Il 19/09/22 18:56, Guillaume Ranquet ha scritto:
Add basic support for the mediatek hdmi phy on MT8195 SoC

Signed-off-by: Guillaume Ranquet <granq...@baylibre.com>

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]

+
        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.o
diff --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..

+
+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...

+       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.

+       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.

+       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.

+               div_ctrl_value = 0x2;
+       else if (posdiv1 == 15)
+               div_ctrl_value = 0x3;
+       else
+               return -EINVAL;
+

..snip..

+
+#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 frequency

Fix comments style.

+       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; //FBDIV

u64 pcw;

+       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)

+               txposdiv = 4;
+       else if (tmds_clk >= 148350000UL && tmds_clk < 296700000UL)

        else if (tmds_clk < 296700000UL)

+               txposdiv = 2;
+       else if (tmds_clk >= 296700000UL && tmds_clk <= 594000000UL)

        else if (tmds_clk <= 594000000UL)

+               txposdiv = 1;
+       else
+               return -EINVAL;
+

..snip..

+
+       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;

+       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;
        }

+               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.

+
+       /* 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.

+
+       /* 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; //24mA

There 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();

+               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.

+               data_channel_bias = 0x14; //10mA
+               clk_channel_bias = 0x14; //10mA
+               impedance_en = 0x0;
+               impedance = 0x0;
+       } else {
+               return -EINVAL;
+       }
+

..snip..

+
+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...

+       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 <can.z...@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek MT8195 HDMI PHY Driver");
+MODULE_LICENSE("GPL v2");

Regards,
Angelo

Reply via email to