Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (c) 2018 Jernej Skrabec <jernej.skrabec@siol.net>
0004  */
0005 
0006 #include <linux/delay.h>
0007 #include <linux/of_address.h>
0008 #include <linux/of_platform.h>
0009 
0010 #include "sun8i_dw_hdmi.h"
0011 
0012 /*
0013  * Address can be actually any value. Here is set to same value as
0014  * it is set in BSP driver.
0015  */
0016 #define I2C_ADDR    0x69
0017 
0018 static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
0019     {
0020         30666000, {
0021             { 0x00b3, 0x0000 },
0022             { 0x2153, 0x0000 },
0023             { 0x40f3, 0x0000 },
0024         },
0025     },  {
0026         36800000, {
0027             { 0x00b3, 0x0000 },
0028             { 0x2153, 0x0000 },
0029             { 0x40a2, 0x0001 },
0030         },
0031     },  {
0032         46000000, {
0033             { 0x00b3, 0x0000 },
0034             { 0x2142, 0x0001 },
0035             { 0x40a2, 0x0001 },
0036         },
0037     },  {
0038         61333000, {
0039             { 0x0072, 0x0001 },
0040             { 0x2142, 0x0001 },
0041             { 0x40a2, 0x0001 },
0042         },
0043     },  {
0044         73600000, {
0045             { 0x0072, 0x0001 },
0046             { 0x2142, 0x0001 },
0047             { 0x4061, 0x0002 },
0048         },
0049     },  {
0050         92000000, {
0051             { 0x0072, 0x0001 },
0052             { 0x2145, 0x0002 },
0053             { 0x4061, 0x0002 },
0054         },
0055     },  {
0056         122666000, {
0057             { 0x0051, 0x0002 },
0058             { 0x2145, 0x0002 },
0059             { 0x4061, 0x0002 },
0060         },
0061     },  {
0062         147200000, {
0063             { 0x0051, 0x0002 },
0064             { 0x2145, 0x0002 },
0065             { 0x4064, 0x0003 },
0066         },
0067     },  {
0068         184000000, {
0069             { 0x0051, 0x0002 },
0070             { 0x214c, 0x0003 },
0071             { 0x4064, 0x0003 },
0072         },
0073     },  {
0074         226666000, {
0075             { 0x0040, 0x0003 },
0076             { 0x214c, 0x0003 },
0077             { 0x4064, 0x0003 },
0078         },
0079     },  {
0080         272000000, {
0081             { 0x0040, 0x0003 },
0082             { 0x214c, 0x0003 },
0083             { 0x5a64, 0x0003 },
0084         },
0085     },  {
0086         340000000, {
0087             { 0x0040, 0x0003 },
0088             { 0x3b4c, 0x0003 },
0089             { 0x5a64, 0x0003 },
0090         },
0091     },  {
0092         594000000, {
0093             { 0x1a40, 0x0003 },
0094             { 0x3b4c, 0x0003 },
0095             { 0x5a64, 0x0003 },
0096         },
0097     }, {
0098         ~0UL, {
0099             { 0x0000, 0x0000 },
0100             { 0x0000, 0x0000 },
0101             { 0x0000, 0x0000 },
0102         },
0103     }
0104 };
0105 
0106 static const struct dw_hdmi_curr_ctrl sun50i_h6_cur_ctr[] = {
0107     /* pixelclk    bpp8    bpp10   bpp12 */
0108     { 27000000,  { 0x0012, 0x0000, 0x0000 }, },
0109     { 74250000,  { 0x0013, 0x001a, 0x001b }, },
0110     { 148500000, { 0x0019, 0x0033, 0x0034 }, },
0111     { 297000000, { 0x0019, 0x001b, 0x001b }, },
0112     { 594000000, { 0x0010, 0x001b, 0x001b }, },
0113     { ~0UL,      { 0x0000, 0x0000, 0x0000 }, }
0114 };
0115 
0116 static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = {
0117     /*pixelclk   symbol   term   vlev*/
0118     { 27000000,  0x8009, 0x0007, 0x02b0 },
0119     { 74250000,  0x8009, 0x0006, 0x022d },
0120     { 148500000, 0x8029, 0x0006, 0x0270 },
0121     { 297000000, 0x8039, 0x0005, 0x01ab },
0122     { 594000000, 0x8029, 0x0000, 0x008a },
0123     { ~0UL,      0x0000, 0x0000, 0x0000}
0124 };
0125 
0126 static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
0127                     const struct drm_display_mode *mode)
0128 {
0129     u32 val = 0;
0130 
0131     if (mode->flags & DRM_MODE_FLAG_NHSYNC)
0132         val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
0133 
0134     if (mode->flags & DRM_MODE_FLAG_NVSYNC)
0135         val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
0136 
0137     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
0138                SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
0139 };
0140 
0141 static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
0142                       const struct drm_display_info *display,
0143                       const struct drm_display_mode *mode)
0144 {
0145     unsigned int clk_rate = mode->crtc_clock * 1000;
0146     struct sun8i_hdmi_phy *phy = data;
0147 
0148     sun8i_hdmi_phy_set_polarity(phy, mode);
0149 
0150     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
0151                SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
0152                SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
0153 
0154     /* power down */
0155     dw_hdmi_phy_gen2_txpwron(hdmi, 0);
0156     dw_hdmi_phy_gen2_pddq(hdmi, 1);
0157 
0158     dw_hdmi_phy_gen2_reset(hdmi);
0159 
0160     dw_hdmi_phy_gen2_pddq(hdmi, 0);
0161 
0162     dw_hdmi_phy_i2c_set_addr(hdmi, I2C_ADDR);
0163 
0164     /*
0165      * Values are taken from BSP HDMI driver. Although AW didn't
0166      * release any documentation, explanation of this values can
0167      * be found in i.MX 6Dual/6Quad Reference Manual.
0168      */
0169     if (clk_rate <= 27000000) {
0170         dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
0171         dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
0172         dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10);
0173         dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
0174         dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e);
0175         dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
0176     } else if (clk_rate <= 74250000) {
0177         dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06);
0178         dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
0179         dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
0180         dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
0181         dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e);
0182         dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
0183     } else if (clk_rate <= 148500000) {
0184         dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06);
0185         dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
0186         dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
0187         dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
0188         dw_hdmi_phy_i2c_write(hdmi, 0x0021, 0x0e);
0189         dw_hdmi_phy_i2c_write(hdmi, 0x8029, 0x09);
0190     } else {
0191         dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x06);
0192         dw_hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
0193         dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
0194         dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
0195         dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x0e);
0196         dw_hdmi_phy_i2c_write(hdmi, 0x802b, 0x09);
0197     }
0198 
0199     dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x1e);
0200     dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);
0201     dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x17);
0202 
0203     dw_hdmi_phy_gen2_txpwron(hdmi, 1);
0204 
0205     return 0;
0206 }
0207 
0208 static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
0209 {
0210     struct sun8i_hdmi_phy *phy = data;
0211 
0212     dw_hdmi_phy_gen2_txpwron(hdmi, 0);
0213     dw_hdmi_phy_gen2_pddq(hdmi, 1);
0214 
0215     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
0216                SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
0217 }
0218 
0219 static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
0220     .init       = sun8i_a83t_hdmi_phy_config,
0221     .disable    = sun8i_a83t_hdmi_phy_disable,
0222     .read_hpd   = dw_hdmi_phy_read_hpd,
0223     .update_hpd = dw_hdmi_phy_update_hpd,
0224     .setup_hpd  = dw_hdmi_phy_setup_hpd,
0225 };
0226 
0227 static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
0228                     const struct drm_display_info *display,
0229                     const struct drm_display_mode *mode)
0230 {
0231     unsigned int clk_rate = mode->crtc_clock * 1000;
0232     struct sun8i_hdmi_phy *phy = data;
0233     u32 pll_cfg1_init;
0234     u32 pll_cfg2_init;
0235     u32 ana_cfg1_end;
0236     u32 ana_cfg2_init;
0237     u32 ana_cfg3_init;
0238     u32 b_offset = 0;
0239     u32 val;
0240 
0241     if (phy->variant->has_phy_clk)
0242         clk_set_rate(phy->clk_phy, clk_rate);
0243 
0244     sun8i_hdmi_phy_set_polarity(phy, mode);
0245 
0246     /* bandwidth / frequency independent settings */
0247 
0248     pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
0249             SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN |
0250             SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) |
0251             SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) |
0252             SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN |
0253             SUN8I_HDMI_PHY_PLL_CFG1_CS |
0254             SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) |
0255             SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) |
0256             SUN8I_HDMI_PHY_PLL_CFG1_BWS;
0257 
0258     pll_cfg2_init = SUN8I_HDMI_PHY_PLL_CFG2_SV_H |
0259             SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN |
0260             SUN8I_HDMI_PHY_PLL_CFG2_SDIV2;
0261 
0262     ana_cfg1_end = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) |
0263                SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT |
0264                SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT |
0265                SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT |
0266                SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT |
0267                SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL |
0268                SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG |
0269                SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS |
0270                SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN |
0271                SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK |
0272                SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL |
0273                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK |
0274                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
0275                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
0276                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
0277                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 |
0278                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
0279                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
0280                SUN8I_HDMI_PHY_ANA_CFG1_CKEN |
0281                SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
0282                SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
0283                SUN8I_HDMI_PHY_ANA_CFG1_ENBI;
0284 
0285     ana_cfg2_init = SUN8I_HDMI_PHY_ANA_CFG2_M_EN |
0286             SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK |
0287             SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN |
0288             SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) |
0289             SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1);
0290 
0291     ana_cfg3_init = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) |
0292             SUN8I_HDMI_PHY_ANA_CFG3_SDAEN |
0293             SUN8I_HDMI_PHY_ANA_CFG3_SCLEN;
0294 
0295     /* bandwidth / frequency dependent settings */
0296     if (clk_rate <= 27000000) {
0297         pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
0298                  SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
0299         pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
0300                  SUN8I_HDMI_PHY_PLL_CFG2_S(4);
0301         ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
0302         ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
0303                  SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
0304         ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) |
0305                  SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5);
0306     } else if (clk_rate <= 74250000) {
0307         pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
0308                  SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
0309         pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
0310                  SUN8I_HDMI_PHY_PLL_CFG2_S(5);
0311         ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
0312         ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
0313                  SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
0314         ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) |
0315                  SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7);
0316     } else if (clk_rate <= 148500000) {
0317         pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
0318                  SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
0319         pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
0320                  SUN8I_HDMI_PHY_PLL_CFG2_S(6);
0321         ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
0322                  SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
0323                  SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2);
0324         ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) |
0325                  SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9);
0326     } else {
0327         b_offset = 2;
0328         pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63);
0329         pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) |
0330                  SUN8I_HDMI_PHY_PLL_CFG2_S(7);
0331         ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
0332                  SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
0333                  SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4);
0334         ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) |
0335                  SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13) |
0336                  SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(3);
0337     }
0338 
0339     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0340                SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0);
0341 
0342     /*
0343      * NOTE: We have to be careful not to overwrite PHY parent
0344      * clock selection bit and clock divider.
0345      */
0346     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
0347                (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
0348                pll_cfg1_init);
0349     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG,
0350                (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK,
0351                pll_cfg2_init);
0352     usleep_range(10000, 15000);
0353     regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG,
0354              SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2);
0355     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
0356                SUN8I_HDMI_PHY_PLL_CFG1_PLLEN,
0357                SUN8I_HDMI_PHY_PLL_CFG1_PLLEN);
0358     msleep(100);
0359 
0360     /* get B value */
0361     regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
0362     val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >>
0363         SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT;
0364     val = min(val + b_offset, (u32)0x3f);
0365 
0366     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
0367                SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
0368                SUN8I_HDMI_PHY_PLL_CFG1_REG_OD,
0369                SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
0370                SUN8I_HDMI_PHY_PLL_CFG1_REG_OD);
0371     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
0372                SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK,
0373                val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT);
0374     msleep(100);
0375     regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end);
0376     regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init);
0377     regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init);
0378 
0379     return 0;
0380 }
0381 
0382 static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
0383 {
0384     struct sun8i_hdmi_phy *phy = data;
0385 
0386     regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0387              SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
0388              SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
0389              SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
0390     regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
0391 }
0392 
0393 static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = {
0394     .init       = sun8i_h3_hdmi_phy_config,
0395     .disable    = sun8i_h3_hdmi_phy_disable,
0396     .read_hpd   = dw_hdmi_phy_read_hpd,
0397     .update_hpd = dw_hdmi_phy_update_hpd,
0398     .setup_hpd  = dw_hdmi_phy_setup_hpd,
0399 };
0400 
0401 static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy)
0402 {
0403     /* enable read access to HDMI controller */
0404     regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
0405              SUN8I_HDMI_PHY_READ_EN_MAGIC);
0406 
0407     /* unscramble register offsets */
0408     regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
0409              SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
0410 }
0411 
0412 static void sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy *phy)
0413 {
0414     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
0415                SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
0416                SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
0417 
0418     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
0419                0xffff0000, 0x80c00000);
0420 }
0421 
0422 static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy)
0423 {
0424     sun8i_hdmi_phy_unlock(phy);
0425 
0426     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
0427                SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK,
0428                SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK);
0429 
0430     /*
0431      * Set PHY I2C address. It must match to the address set by
0432      * dw_hdmi_phy_set_slave_addr().
0433      */
0434     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
0435                SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK,
0436                SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR));
0437 }
0438 
0439 static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
0440 {
0441     unsigned int val;
0442 
0443     sun8i_hdmi_phy_unlock(phy);
0444 
0445     regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0);
0446     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0447                SUN8I_HDMI_PHY_ANA_CFG1_ENBI,
0448                SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
0449     udelay(5);
0450     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0451                SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN,
0452                SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN);
0453     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0454                SUN8I_HDMI_PHY_ANA_CFG1_ENVBS,
0455                SUN8I_HDMI_PHY_ANA_CFG1_ENVBS);
0456     usleep_range(10, 20);
0457     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0458                SUN8I_HDMI_PHY_ANA_CFG1_LDOEN,
0459                SUN8I_HDMI_PHY_ANA_CFG1_LDOEN);
0460     udelay(5);
0461     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0462                SUN8I_HDMI_PHY_ANA_CFG1_CKEN,
0463                SUN8I_HDMI_PHY_ANA_CFG1_CKEN);
0464     usleep_range(40, 100);
0465     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0466                SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL,
0467                SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL);
0468     usleep_range(100, 200);
0469     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0470                SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG,
0471                SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG);
0472     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0473                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
0474                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
0475                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2,
0476                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
0477                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
0478                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2);
0479 
0480     /* wait for calibration to finish */
0481     regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val,
0482                  (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D),
0483                  100, 2000);
0484 
0485     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0486                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK,
0487                SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK);
0488     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
0489                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
0490                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
0491                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
0492                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK,
0493                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
0494                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
0495                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
0496                SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK);
0497 
0498     /* enable DDC communication */
0499     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG,
0500                SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
0501                SUN8I_HDMI_PHY_ANA_CFG3_SDAEN,
0502                SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
0503                SUN8I_HDMI_PHY_ANA_CFG3_SDAEN);
0504 
0505     /* reset PHY PLL clock parent */
0506     regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
0507                SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
0508 
0509     /* set HW control of CEC pins */
0510     regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
0511 
0512     /* read calibration data */
0513     regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
0514     phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2;
0515 }
0516 
0517 int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
0518 {
0519     int ret;
0520 
0521     ret = reset_control_deassert(phy->rst_phy);
0522     if (ret) {
0523         dev_err(phy->dev, "Cannot deassert phy reset control: %d\n", ret);
0524         return ret;
0525     }
0526 
0527     ret = clk_prepare_enable(phy->clk_bus);
0528     if (ret) {
0529         dev_err(phy->dev, "Cannot enable bus clock: %d\n", ret);
0530         goto err_assert_rst_phy;
0531     }
0532 
0533     ret = clk_prepare_enable(phy->clk_mod);
0534     if (ret) {
0535         dev_err(phy->dev, "Cannot enable mod clock: %d\n", ret);
0536         goto err_disable_clk_bus;
0537     }
0538 
0539     if (phy->variant->has_phy_clk) {
0540         ret = sun8i_phy_clk_create(phy, phy->dev,
0541                        phy->variant->has_second_pll);
0542         if (ret) {
0543             dev_err(phy->dev, "Couldn't create the PHY clock\n");
0544             goto err_disable_clk_mod;
0545         }
0546 
0547         clk_prepare_enable(phy->clk_phy);
0548     }
0549 
0550     phy->variant->phy_init(phy);
0551 
0552     return 0;
0553 
0554 err_disable_clk_mod:
0555     clk_disable_unprepare(phy->clk_mod);
0556 err_disable_clk_bus:
0557     clk_disable_unprepare(phy->clk_bus);
0558 err_assert_rst_phy:
0559     reset_control_assert(phy->rst_phy);
0560 
0561     return ret;
0562 }
0563 
0564 void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
0565 {
0566     clk_disable_unprepare(phy->clk_mod);
0567     clk_disable_unprepare(phy->clk_bus);
0568     clk_disable_unprepare(phy->clk_phy);
0569 
0570     reset_control_assert(phy->rst_phy);
0571 }
0572 
0573 void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
0574                 struct dw_hdmi_plat_data *plat_data)
0575 {
0576     const struct sun8i_hdmi_phy_variant *variant = phy->variant;
0577 
0578     if (variant->phy_ops) {
0579         plat_data->phy_ops = variant->phy_ops;
0580         plat_data->phy_name = "sun8i_dw_hdmi_phy";
0581         plat_data->phy_data = phy;
0582     } else {
0583         plat_data->mpll_cfg = variant->mpll_cfg;
0584         plat_data->cur_ctr = variant->cur_ctr;
0585         plat_data->phy_config = variant->phy_cfg;
0586     }
0587 }
0588 
0589 static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
0590     .reg_bits   = 32,
0591     .val_bits   = 32,
0592     .reg_stride = 4,
0593     .max_register   = SUN8I_HDMI_PHY_CEC_REG,
0594     .name       = "phy"
0595 };
0596 
0597 static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
0598     .phy_ops = &sun8i_a83t_hdmi_phy_ops,
0599     .phy_init = &sun8i_hdmi_phy_init_a83t,
0600 };
0601 
0602 static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
0603     .has_phy_clk = true,
0604     .phy_ops = &sun8i_h3_hdmi_phy_ops,
0605     .phy_init = &sun8i_hdmi_phy_init_h3,
0606 };
0607 
0608 static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
0609     .has_phy_clk = true,
0610     .has_second_pll = true,
0611     .phy_ops = &sun8i_h3_hdmi_phy_ops,
0612     .phy_init = &sun8i_hdmi_phy_init_h3,
0613 };
0614 
0615 static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
0616     .has_phy_clk = true,
0617     .phy_ops = &sun8i_h3_hdmi_phy_ops,
0618     .phy_init = &sun8i_hdmi_phy_init_h3,
0619 };
0620 
0621 static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = {
0622     .cur_ctr  = sun50i_h6_cur_ctr,
0623     .mpll_cfg = sun50i_h6_mpll_cfg,
0624     .phy_cfg  = sun50i_h6_phy_config,
0625     .phy_init = &sun50i_hdmi_phy_init_h6,
0626 };
0627 
0628 static const struct of_device_id sun8i_hdmi_phy_of_table[] = {
0629     {
0630         .compatible = "allwinner,sun8i-a83t-hdmi-phy",
0631         .data = &sun8i_a83t_hdmi_phy,
0632     },
0633     {
0634         .compatible = "allwinner,sun8i-h3-hdmi-phy",
0635         .data = &sun8i_h3_hdmi_phy,
0636     },
0637     {
0638         .compatible = "allwinner,sun8i-r40-hdmi-phy",
0639         .data = &sun8i_r40_hdmi_phy,
0640     },
0641     {
0642         .compatible = "allwinner,sun50i-a64-hdmi-phy",
0643         .data = &sun50i_a64_hdmi_phy,
0644     },
0645     {
0646         .compatible = "allwinner,sun50i-h6-hdmi-phy",
0647         .data = &sun50i_h6_hdmi_phy,
0648     },
0649     { /* sentinel */ }
0650 };
0651 
0652 int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
0653 {
0654     struct platform_device *pdev = of_find_device_by_node(node);
0655     struct sun8i_hdmi_phy *phy;
0656 
0657     if (!pdev)
0658         return -EPROBE_DEFER;
0659 
0660     phy = platform_get_drvdata(pdev);
0661     if (!phy) {
0662         put_device(&pdev->dev);
0663         return -EPROBE_DEFER;
0664     }
0665 
0666     hdmi->phy = phy;
0667 
0668     put_device(&pdev->dev);
0669 
0670     return 0;
0671 }
0672 
0673 static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
0674 {
0675     struct device *dev = &pdev->dev;
0676     struct sun8i_hdmi_phy *phy;
0677     void __iomem *regs;
0678 
0679     phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
0680     if (!phy)
0681         return -ENOMEM;
0682 
0683     phy->variant = of_device_get_match_data(dev);
0684     phy->dev = dev;
0685 
0686     regs = devm_platform_ioremap_resource(pdev, 0);
0687     if (IS_ERR(regs))
0688         return dev_err_probe(dev, PTR_ERR(regs),
0689                      "Couldn't map the HDMI PHY registers\n");
0690 
0691     phy->regs = devm_regmap_init_mmio(dev, regs,
0692                       &sun8i_hdmi_phy_regmap_config);
0693     if (IS_ERR(phy->regs))
0694         return dev_err_probe(dev, PTR_ERR(phy->regs),
0695                      "Couldn't create the HDMI PHY regmap\n");
0696 
0697     phy->clk_bus = devm_clk_get(dev, "bus");
0698     if (IS_ERR(phy->clk_bus))
0699         return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
0700                      "Could not get bus clock\n");
0701 
0702     phy->clk_mod = devm_clk_get(dev, "mod");
0703     if (IS_ERR(phy->clk_mod))
0704         return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
0705                      "Could not get mod clock\n");
0706 
0707     if (phy->variant->has_phy_clk) {
0708         phy->clk_pll0 = devm_clk_get(dev, "pll-0");
0709         if (IS_ERR(phy->clk_pll0))
0710             return dev_err_probe(dev, PTR_ERR(phy->clk_pll0),
0711                          "Could not get pll-0 clock\n");
0712 
0713         if (phy->variant->has_second_pll) {
0714             phy->clk_pll1 = devm_clk_get(dev, "pll-1");
0715             if (IS_ERR(phy->clk_pll1))
0716                 return dev_err_probe(dev, PTR_ERR(phy->clk_pll1),
0717                              "Could not get pll-1 clock\n");
0718         }
0719     }
0720 
0721     phy->rst_phy = devm_reset_control_get_shared(dev, "phy");
0722     if (IS_ERR(phy->rst_phy))
0723         return dev_err_probe(dev, PTR_ERR(phy->rst_phy),
0724                      "Could not get phy reset control\n");
0725 
0726     platform_set_drvdata(pdev, phy);
0727 
0728     return 0;
0729 }
0730 
0731 struct platform_driver sun8i_hdmi_phy_driver = {
0732     .probe  = sun8i_hdmi_phy_probe,
0733     .driver = {
0734         .name = "sun8i-hdmi-phy",
0735         .of_match_table = sun8i_hdmi_phy_of_table,
0736     },
0737 };