Re: [PATCH v1 14/17] phy: mediatek: add support for phy-mtk-hdmi-mt8195
From: Guillaume Ranquet <hidden>
Date: 2022-09-27 13:23:57
Also in:
dri-devel, linux-clk, linux-devicetree, linux-mediatek, linux-phy, lkml
On Tue, 20 Sep 2022 14:17, AngeloGioacchino Del Regno [off-list ref] wrote:
Il 19/09/22 18:56, Guillaume Ranquet ha scritto:quoted
Add basic support for the mediatek hdmi phy on MT8195 SoC Signed-off-by: Guillaume Ranquet <redacted>diff --git a/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c index bb7593ea4c86..0157acdce56c 100644 --- a/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_mt8195_hdmi.c@@ -1344,6 +1344,8 @@ static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge, mtk_hdmi_disable_hdcp_encrypt(hdmi); usleep_range(50000, 50050); + phy_power_off(hdmi->phy);This one belongs to patch [11/17]
Sorry, bad rebase :-/
quoted
+ hdmi->enabled = false; }diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index fb1f8edaffa7..c9a50395533e 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile@@ -12,6 +12,7 @@ obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt8173.o +phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt8195.o obj-$(CONFIG_PHY_MTK_HDMI) += phy-mtk-hdmi-drv.o phy-mtk-mipi-dsi-drv-y := phy-mtk-mipi-dsi.odiff --git a/drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c b/drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c new file mode 100644 index 000000000000..149015b64c02 --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt8195.c@@ -0,0 +1,673 @@..snip..quoted
+ +static int mtk_hdmi_pll_set_hw(struct clk_hw *hw, unsigned char prediv, + unsigned char fbkdiv_high, + unsigned long fbkdiv_low, + unsigned char fbkdiv_hs3, unsigned char posdiv1, + unsigned char posdiv2, unsigned char txprediv, + unsigned char txposdiv, + unsigned char digital_div) +{ + unsigned char txposdiv_value = 0; + unsigned char div3_ctrl_value = 0; + unsigned char posdiv_vallue = 0; + unsigned char div_ctrl_value = 0; + unsigned char reserve_3_2_value = 0; + unsigned char prediv_value = 0; + unsigned char reserve13_value = 0; + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + + mtk_hdmi_pll_select_source(hw); + + mtk_hdmi_pll_performance_setting(hw); + + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_10, + 0x2 << RG_HDMITX21_BIAS_PE_BG_VREF_SEL_SHIFT, + RG_HDMITX21_BIAS_PE_BG_VREF_SEL); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_10, + 0x0 << RG_HDMITX21_VREF_SEL_SHIFT, + RG_HDMITX21_VREF_SEL); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_9, + 0x2 << RG_HDMITX21_SLDO_VREF_SEL_SHIFT, + RG_HDMITX21_SLDO_VREF_SEL); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_10, + 0x0 << RG_HDMITX21_BIAS_PE_VREF_SELB_SHIFT, + RG_HDMITX21_BIAS_PE_VREF_SELB); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_3, + 0x1 << RG_HDMITX21_SLDOLPF_EN_SHIFT, + RG_HDMITX21_SLDOLPF_EN); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_6, + 0x11 << RG_HDMITX21_INTR_CAL_SHIFT, + RG_HDMITX21_INTR_CAL); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_PLL_CFG_2, + 0x1 << RG_HDMITXPLL_PWD_SHIFT, RG_HDMITXPLL_PWD); + + /* TXPOSDIV */Either ilog2() or use a switch...quoted
+ if (txposdiv == 1) + txposdiv_value = 0x0; + else if (txposdiv == 2) + txposdiv_value = 0x1; + else if (txposdiv == 4) + txposdiv_value = 0x2; + else if (txposdiv == 8) + txposdiv_value = 0x3; + else + return -EINVAL; + + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_6, + txposdiv_value << RG_HDMITX21_TX_POSDIV_SHIFT, + RG_HDMITX21_TX_POSDIV); + + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_6, + 0x1 << RG_HDMITX21_TX_POSDIV_EN_SHIFT, + RG_HDMITX21_TX_POSDIV_EN); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_CFG_6, + 0x0 << RG_HDMITX21_FRL_EN_SHIFT, RG_HDMITX21_FRL_EN); + + /* TXPREDIV */Use a switch.quoted
+ if (txprediv == 2) { + div3_ctrl_value = 0x0; + posdiv_vallue = 0x0; + } else if (txprediv == 4) { + div3_ctrl_value = 0x0; + posdiv_vallue = 0x1; + } else if (txprediv == 6) { + div3_ctrl_value = 0x1; + posdiv_vallue = 0x0; + } else if (txprediv == 12) { + div3_ctrl_value = 0x1; + posdiv_vallue = 0x1; + } else { + return -EINVAL; + } + + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_PLL_CFG_4, + div3_ctrl_value + << RG_HDMITXPLL_POSDIV_DIV3_CTRL_SHIFT, + RG_HDMITXPLL_POSDIV_DIV3_CTRL); + mtk_hdmi_phy_mask(hdmi_phy, HDMI_1_PLL_CFG_4, + posdiv_vallue << RG_HDMITXPLL_POSDIV_SHIFT, + RG_HDMITXPLL_POSDIV); + + /* POSDIV1 */same here.
Will do.
quoted
+ if (posdiv1 == 5) + div_ctrl_value = 0x0; + else if (posdiv1 == 10) + div_ctrl_value = 0x1; + else if (posdiv1 == (125 / 10))/* This is 12.5 in reality, but we get only the integer part */ case 12: ...or you'll have to use a "deci-divider", which would complicate human readability by ... quite a bit, in some cases.quoted
+ div_ctrl_value = 0x2; + else if (posdiv1 == 15) + div_ctrl_value = 0x3; + else + return -EINVAL; +..snip..quoted
+ +#define PCW_DECIMAL_WIDTH 24 + +static int mtk_hdmi_pll_calculate_params(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret; + unsigned long long tmds_clk = 0; + unsigned long long pixel_clk = 0; + //pll input source frequencyFix comments style.quoted
+ unsigned long long da_hdmitx21_ref_ck = 0; + unsigned long long ns_hdmipll_ck = 0; //ICO output clk + //source clk for Display digital + unsigned long long ad_hdmipll_pixel_ck = 0; + unsigned char digital_div = 0; + unsigned long long pcw = 0; //FBDIVu64 pcw;quoted
+ unsigned char txprediv = 0; + unsigned char txposdiv = 0; + unsigned char fbkdiv_high = 0; + unsigned long fbkdiv_low = 0; + unsigned char posdiv1 = 0; + unsigned char posdiv2 = 0; + unsigned char prediv = 1; //prediv is always 1 + unsigned char fbkdiv_hs3 = 1; //fbkdiv_hs3 is always 1 + int i = 0; + unsigned char txpredivs[4] = { 2, 4, 6, 12 }; + + pixel_clk = rate; + tmds_clk = pixel_clk; + + if (tmds_clk < 25000000 || tmds_clk > 594000000) + return -EINVAL; + + da_hdmitx21_ref_ck = 26000000UL; //in HZ + + /* TXPOSDIV stage treatment: + * 0M < TMDS clk < 54M /8 + * 54M <= TMDS clk < 148.35M /4 + * 148.35M <=TMDS clk < 296.7M /2 + * 296.7 <=TMDS clk <= 594M /1 + */ + if (tmds_clk < 54000000UL) + txposdiv = 8; + else if (tmds_clk >= 54000000UL && tmds_clk < 148350000UL)else if (tmds_clk < 148350000UL)quoted
+ txposdiv = 4; + else if (tmds_clk >= 148350000UL && tmds_clk < 296700000UL)else if (tmds_clk < 296700000UL)quoted
+ txposdiv = 2; + else if (tmds_clk >= 296700000UL && tmds_clk <= 594000000UL)else if (tmds_clk <= 594000000UL)quoted
+ txposdiv = 1; + else + return -EINVAL; +..snip..quoted
+ + txprediv = txpredivs[i]; + + /* PCW calculation: FBKDIV + * formula: pcw=(frequency_out*2^pcw_bit) / frequency_in / FBKDIV_HS3; + * RG_HDMITXPLL_FBKDIV[32:0]: + * [32,24] 9bit integer, [23,0]:24bit fraction + */ + pcw = ns_hdmipll_ck;pcw = ns_hdmipll_ck << PCW_DECIMAL_WIDTH; pcw /= da_hdmitx21_ref_ck; pcw /= fbkdiv_hs3;quoted
+ pcw = pcw << PCW_DECIMAL_WIDTH; + pcw = pcw / da_hdmitx21_ref_ck; + pcw = pcw / fbkdiv_hs3; + + if ((pcw / BIT(32)) > 1) {pcw_nbits = fls64(pcw); if (pcw_nbits > 33) return -EINVAL; if (pcw_nbits == 33) { fbkdiv_high = 1; fkbdiv_low = pcw % BIT(32); } else { fbkdiv_high = 0; fbkdiv_low = pcw; }quoted
+ return -EINVAL; + } else if ((pcw / BIT(32)) == 1) { + fbkdiv_high = 1; + fbkdiv_low = pcw % BIT(32); + } else { + fbkdiv_high = 0; + fbkdiv_low = pcw; + } + + /* posdiv1: + * posdiv1 stage treatment according to color_depth: + * 24bit -> posdiv1 /10, 30bit -> posdiv1 /12.5, + * 36bit -> posdiv1 /15, 48bit -> posdiv1 /10 + */ + posdiv1 = 10; // div 10 + posdiv2 = 1; + ad_hdmipll_pixel_ck = (ns_hdmipll_ck / 10) / 1;I understand this as ad_hdmipll_pixel_ck = (ns_hdmipll_ck / posdiv1) / posdiv2; ..if that's true, please fix.
I'll try to make everything more readable.
quoted
+ + /* Digital clk divider, max /32 */ + digital_div = ad_hdmipll_pixel_ck / pixel_clk; + if (!(digital_div <= 32 && digital_div >= 1)) + return -EINVAL; + + ret = mtk_hdmi_pll_set_hw(hw, prediv, fbkdiv_high, fbkdiv_low, + fbkdiv_hs3, posdiv1, posdiv2, txprediv, + txposdiv, digital_div); + if (ret) + return -EINVAL; + + return 0; +} + +static int mtk_hdmi_pll_drv_setting(struct clk_hw *hw) +{ + unsigned char data_channel_bias, clk_channel_bias; + unsigned char impedance, impedance_en; + struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); + unsigned long tmds_clk; + unsigned long pixel_clk = hdmi_phy->pll_rate; + + tmds_clk = pixel_clk;tmds_rate = pixel_rate; ...better describes that we're talking about clock rates, not clock per-se.quoted
+ + /* bias & impedance setting: + * 3G < data rate <= 6G: enable impedance 100ohm, + * data channel bias 24mA, clock channel bias 20mA + * pixel clk >= HD, 74.175MHZ <= pixel clk <= 300MHZ: + * enalbe impedance 100ohm + * data channel 20mA, clock channel 16mA + * 27M =< pixel clk < 74.175: disable impedance + * data channel & clock channel bias 10mA + */ + + /* 3G < data rate <= 6G, 300M < tmds rate <= 594M */ + if (tmds_clk > 300000000UL && tmds_clk <= 594000000UL) { + data_channel_bias = 0x3c; //24mAThere must be an equation to calculate the bias value from milliamps to HW values, in which case we would see here... data_channel_bias = MTK_HDMI_BIAS_MA(24); clk_channel_bias = MTK_HDMI_BIAS_MA(20); impedance = MTK_HDMI_IMPEDANCE_OHMS(100); impedance_en = SOMETHING();
That would improve readability by quite a lot. I'll try to find that equation.
quoted
+ clk_channel_bias = 0x34; //20mA + impedance_en = 0xf; + impedance = 0x36; //100ohm + } else if (pixel_clk >= 74175000UL && pixel_clk <= 300000000UL) { + data_channel_bias = 0x34; //20mA + clk_channel_bias = 0x2c; //16mA + impedance_en = 0xf; + impedance = 0x36; //100ohm + } else if (pixel_clk >= 27000000UL && pixel_clk < 74175000UL) {By the way, if you invert all the checks here (check from highest pixclk to lowest) you can simplify it.
I'll give it a try, thx for the suggestion.
quoted
+ data_channel_bias = 0x14; //10mA + clk_channel_bias = 0x14; //10mA + impedance_en = 0x0; + impedance = 0x0; + } else { + return -EINVAL; + } +..snip..quoted
+ +static int mtk_hdmi_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct phy_configure_opts_dp *dp_opts = &opts->dp; + struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); + int ret = 0; + bool enable = 0; + + ret = clk_set_rate(hdmi_phy->pll, dp_opts->link_rate); +Please remove this blank line...quoted
+ if (ret) + goto out; + + mtk_mt8195_phy_tmds_high_bit_clk_ratio(hdmi_phy, enable); + +out: + return ret; +} + +struct mtk_hdmi_phy_conf mtk_hdmi_phy_8195_conf = { + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, + .hdmi_phy_clk_ops = &mtk_hdmi_pll_ops, + .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds, + .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds, + .hdmi_phy_configure = mtk_hdmi_phy_configure, +}; + +MODULE_AUTHOR("Can Zeng [off-list ref]"); +MODULE_DESCRIPTION("MediaTek MT8195 HDMI PHY Driver"); +MODULE_LICENSE("GPL v2");Regards, Angelo
_______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel