1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2018 Jernej Skrabec <jernej.skrabec@siol.net> 4 */ 5 6 #include <linux/delay.h> 7 #include <linux/of_address.h> 8 9 #include "sun8i_dw_hdmi.h" 10 11 /* 12 * Address can be actually any value. Here is set to same value as 13 * it is set in BSP driver. 14 */ 15 #define I2C_ADDR 0x69 16 17 static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi, 18 struct sun8i_hdmi_phy *phy, 19 unsigned int clk_rate) 20 { 21 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, 22 SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 23 SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN); 24 25 /* power down */ 26 dw_hdmi_phy_gen2_txpwron(hdmi, 0); 27 dw_hdmi_phy_gen2_pddq(hdmi, 1); 28 29 dw_hdmi_phy_reset(hdmi); 30 31 dw_hdmi_phy_gen2_pddq(hdmi, 0); 32 33 dw_hdmi_phy_i2c_set_addr(hdmi, I2C_ADDR); 34 35 /* 36 * Values are taken from BSP HDMI driver. Although AW didn't 37 * release any documentation, explanation of this values can 38 * be found in i.MX 6Dual/6Quad Reference Manual. 39 */ 40 if (clk_rate <= 27000000) { 41 dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06); 42 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); 43 dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10); 44 dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19); 45 dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e); 46 dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09); 47 } else if (clk_rate <= 74250000) { 48 dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06); 49 dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); 50 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10); 51 dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19); 52 dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e); 53 dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09); 54 } else if (clk_rate <= 148500000) { 55 dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06); 56 dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); 57 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10); 58 dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19); 59 dw_hdmi_phy_i2c_write(hdmi, 0x0021, 0x0e); 60 dw_hdmi_phy_i2c_write(hdmi, 0x8029, 0x09); 61 } else { 62 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x06); 63 dw_hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); 64 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10); 65 dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19); 66 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x0e); 67 dw_hdmi_phy_i2c_write(hdmi, 0x802b, 0x09); 68 } 69 70 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x1e); 71 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); 72 dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x17); 73 74 dw_hdmi_phy_gen2_txpwron(hdmi, 1); 75 76 return 0; 77 } 78 79 static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi, 80 struct sun8i_hdmi_phy *phy, 81 unsigned int clk_rate) 82 { 83 u32 pll_cfg1_init; 84 u32 pll_cfg2_init; 85 u32 ana_cfg1_end; 86 u32 ana_cfg2_init; 87 u32 ana_cfg3_init; 88 u32 b_offset = 0; 89 u32 val; 90 91 /* bandwidth / frequency independent settings */ 92 93 pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN | 94 SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN | 95 SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) | 96 SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) | 97 SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN | 98 SUN8I_HDMI_PHY_PLL_CFG1_CS | 99 SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) | 100 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) | 101 SUN8I_HDMI_PHY_PLL_CFG1_BWS; 102 103 pll_cfg2_init = SUN8I_HDMI_PHY_PLL_CFG2_SV_H | 104 SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN | 105 SUN8I_HDMI_PHY_PLL_CFG2_SDIV2; 106 107 ana_cfg1_end = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) | 108 SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT | 109 SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT | 110 SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT | 111 SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT | 112 SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL | 113 SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG | 114 SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS | 115 SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN | 116 SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK | 117 SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL | 118 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK | 119 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 | 120 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 | 121 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 | 122 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 | 123 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 | 124 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 | 125 SUN8I_HDMI_PHY_ANA_CFG1_CKEN | 126 SUN8I_HDMI_PHY_ANA_CFG1_LDOEN | 127 SUN8I_HDMI_PHY_ANA_CFG1_ENVBS | 128 SUN8I_HDMI_PHY_ANA_CFG1_ENBI; 129 130 ana_cfg2_init = SUN8I_HDMI_PHY_ANA_CFG2_M_EN | 131 SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK | 132 SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN | 133 SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) | 134 SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1); 135 136 ana_cfg3_init = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) | 137 SUN8I_HDMI_PHY_ANA_CFG3_SDAEN | 138 SUN8I_HDMI_PHY_ANA_CFG3_SCLEN; 139 140 /* bandwidth / frequency dependent settings */ 141 if (clk_rate <= 27000000) { 142 pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 | 143 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32); 144 pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) | 145 SUN8I_HDMI_PHY_PLL_CFG2_S(4); 146 ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW; 147 ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) | 148 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal); 149 ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) | 150 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5); 151 } else if (clk_rate <= 74250000) { 152 pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 | 153 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32); 154 pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) | 155 SUN8I_HDMI_PHY_PLL_CFG2_S(5); 156 ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW; 157 ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) | 158 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal); 159 ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) | 160 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7); 161 } else if (clk_rate <= 148500000) { 162 pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 | 163 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32); 164 pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) | 165 SUN8I_HDMI_PHY_PLL_CFG2_S(6); 166 ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK | 167 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW | 168 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2); 169 ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) | 170 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9); 171 } else { 172 b_offset = 2; 173 pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63); 174 pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) | 175 SUN8I_HDMI_PHY_PLL_CFG2_S(7); 176 ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK | 177 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW | 178 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4); 179 ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) | 180 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13); 181 } 182 183 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 184 SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0); 185 186 /* 187 * NOTE: We have to be careful not to overwrite PHY parent 188 * clock selection bit and clock divider. 189 */ 190 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 191 (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 192 pll_cfg1_init); 193 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG, 194 (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK, 195 pll_cfg2_init); 196 usleep_range(10000, 15000); 197 regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG, 198 SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2); 199 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 200 SUN8I_HDMI_PHY_PLL_CFG1_PLLEN, 201 SUN8I_HDMI_PHY_PLL_CFG1_PLLEN); 202 msleep(100); 203 204 /* get B value */ 205 regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val); 206 val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >> 207 SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT; 208 val = min(val + b_offset, (u32)0x3f); 209 210 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 211 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 | 212 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD, 213 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 | 214 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD); 215 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 216 SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK, 217 val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT); 218 msleep(100); 219 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end); 220 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init); 221 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init); 222 223 return 0; 224 } 225 226 static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, 227 struct drm_display_mode *mode) 228 { 229 struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data; 230 u32 val = 0; 231 232 if (mode->flags & DRM_MODE_FLAG_NHSYNC) 233 val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC; 234 235 if (mode->flags & DRM_MODE_FLAG_NVSYNC) 236 val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC; 237 238 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, 239 SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val); 240 241 if (phy->variant->has_phy_clk) 242 clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000); 243 244 return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000); 245 }; 246 247 static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi, 248 struct sun8i_hdmi_phy *phy) 249 { 250 dw_hdmi_phy_gen2_txpwron(hdmi, 0); 251 dw_hdmi_phy_gen2_pddq(hdmi, 1); 252 253 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, 254 SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0); 255 } 256 257 static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi, 258 struct sun8i_hdmi_phy *phy) 259 { 260 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 261 SUN8I_HDMI_PHY_ANA_CFG1_LDOEN | 262 SUN8I_HDMI_PHY_ANA_CFG1_ENVBS | 263 SUN8I_HDMI_PHY_ANA_CFG1_ENBI); 264 regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0); 265 } 266 267 static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) 268 { 269 struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data; 270 271 phy->variant->phy_disable(hdmi, phy); 272 } 273 274 static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = { 275 .init = &sun8i_hdmi_phy_config, 276 .disable = &sun8i_hdmi_phy_disable, 277 .read_hpd = &dw_hdmi_phy_read_hpd, 278 .update_hpd = &dw_hdmi_phy_update_hpd, 279 .setup_hpd = &dw_hdmi_phy_setup_hpd, 280 }; 281 282 static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy) 283 { 284 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, 285 SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK, 286 SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK); 287 288 /* 289 * Set PHY I2C address. It must match to the address set by 290 * dw_hdmi_phy_set_slave_addr(). 291 */ 292 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, 293 SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK, 294 SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR)); 295 } 296 297 static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy) 298 { 299 unsigned int val; 300 301 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0); 302 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 303 SUN8I_HDMI_PHY_ANA_CFG1_ENBI, 304 SUN8I_HDMI_PHY_ANA_CFG1_ENBI); 305 udelay(5); 306 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 307 SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN, 308 SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN); 309 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 310 SUN8I_HDMI_PHY_ANA_CFG1_ENVBS, 311 SUN8I_HDMI_PHY_ANA_CFG1_ENVBS); 312 usleep_range(10, 20); 313 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 314 SUN8I_HDMI_PHY_ANA_CFG1_LDOEN, 315 SUN8I_HDMI_PHY_ANA_CFG1_LDOEN); 316 udelay(5); 317 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 318 SUN8I_HDMI_PHY_ANA_CFG1_CKEN, 319 SUN8I_HDMI_PHY_ANA_CFG1_CKEN); 320 usleep_range(40, 100); 321 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 322 SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL, 323 SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL); 324 usleep_range(100, 200); 325 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 326 SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG, 327 SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG); 328 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 329 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 | 330 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 | 331 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2, 332 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 | 333 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 | 334 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2); 335 336 /* wait for calibration to finish */ 337 regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val, 338 (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D), 339 100, 2000); 340 341 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 342 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK, 343 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK); 344 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 345 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 | 346 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 | 347 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 | 348 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK, 349 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 | 350 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 | 351 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 | 352 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK); 353 354 /* enable DDC communication */ 355 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, 356 SUN8I_HDMI_PHY_ANA_CFG3_SCLEN | 357 SUN8I_HDMI_PHY_ANA_CFG3_SDAEN, 358 SUN8I_HDMI_PHY_ANA_CFG3_SCLEN | 359 SUN8I_HDMI_PHY_ANA_CFG3_SDAEN); 360 361 /* reset PHY PLL clock parent */ 362 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 363 SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0); 364 365 /* set HW control of CEC pins */ 366 regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0); 367 368 /* read calibration data */ 369 regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val); 370 phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2; 371 } 372 373 void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy) 374 { 375 /* enable read access to HDMI controller */ 376 regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG, 377 SUN8I_HDMI_PHY_READ_EN_MAGIC); 378 379 /* unscramble register offsets */ 380 regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG, 381 SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC); 382 383 phy->variant->phy_init(phy); 384 } 385 386 const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void) 387 { 388 return &sun8i_hdmi_phy_ops; 389 } 390 391 static struct regmap_config sun8i_hdmi_phy_regmap_config = { 392 .reg_bits = 32, 393 .val_bits = 32, 394 .reg_stride = 4, 395 .max_register = SUN8I_HDMI_PHY_CEC_REG, 396 .name = "phy" 397 }; 398 399 static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = { 400 .has_phy_clk = true, 401 .phy_init = &sun8i_hdmi_phy_init_h3, 402 .phy_disable = &sun8i_hdmi_phy_disable_h3, 403 .phy_config = &sun8i_hdmi_phy_config_h3, 404 }; 405 406 static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = { 407 .phy_init = &sun8i_hdmi_phy_init_a83t, 408 .phy_disable = &sun8i_hdmi_phy_disable_a83t, 409 .phy_config = &sun8i_hdmi_phy_config_a83t, 410 }; 411 412 static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { 413 .has_phy_clk = true, 414 .phy_init = &sun8i_hdmi_phy_init_h3, 415 .phy_disable = &sun8i_hdmi_phy_disable_h3, 416 .phy_config = &sun8i_hdmi_phy_config_h3, 417 }; 418 419 static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = { 420 .has_phy_clk = true, 421 .has_second_pll = true, 422 .phy_init = &sun8i_hdmi_phy_init_h3, 423 .phy_disable = &sun8i_hdmi_phy_disable_h3, 424 .phy_config = &sun8i_hdmi_phy_config_h3, 425 }; 426 427 static const struct of_device_id sun8i_hdmi_phy_of_table[] = { 428 { 429 .compatible = "allwinner,sun50i-a64-hdmi-phy", 430 .data = &sun50i_a64_hdmi_phy, 431 }, 432 { 433 .compatible = "allwinner,sun8i-a83t-hdmi-phy", 434 .data = &sun8i_a83t_hdmi_phy, 435 }, 436 { 437 .compatible = "allwinner,sun8i-h3-hdmi-phy", 438 .data = &sun8i_h3_hdmi_phy, 439 }, 440 { 441 .compatible = "allwinner,sun8i-r40-hdmi-phy", 442 .data = &sun8i_r40_hdmi_phy, 443 }, 444 { /* sentinel */ } 445 }; 446 447 int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node) 448 { 449 const struct of_device_id *match; 450 struct device *dev = hdmi->dev; 451 struct sun8i_hdmi_phy *phy; 452 struct resource res; 453 void __iomem *regs; 454 int ret; 455 456 match = of_match_node(sun8i_hdmi_phy_of_table, node); 457 if (!match) { 458 dev_err(dev, "Incompatible HDMI PHY\n"); 459 return -EINVAL; 460 } 461 462 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 463 if (!phy) 464 return -ENOMEM; 465 466 phy->variant = (struct sun8i_hdmi_phy_variant *)match->data; 467 468 ret = of_address_to_resource(node, 0, &res); 469 if (ret) { 470 dev_err(dev, "phy: Couldn't get our resources\n"); 471 return ret; 472 } 473 474 regs = devm_ioremap_resource(dev, &res); 475 if (IS_ERR(regs)) { 476 dev_err(dev, "Couldn't map the HDMI PHY registers\n"); 477 return PTR_ERR(regs); 478 } 479 480 phy->regs = devm_regmap_init_mmio(dev, regs, 481 &sun8i_hdmi_phy_regmap_config); 482 if (IS_ERR(phy->regs)) { 483 dev_err(dev, "Couldn't create the HDMI PHY regmap\n"); 484 return PTR_ERR(phy->regs); 485 } 486 487 phy->clk_bus = of_clk_get_by_name(node, "bus"); 488 if (IS_ERR(phy->clk_bus)) { 489 dev_err(dev, "Could not get bus clock\n"); 490 return PTR_ERR(phy->clk_bus); 491 } 492 493 phy->clk_mod = of_clk_get_by_name(node, "mod"); 494 if (IS_ERR(phy->clk_mod)) { 495 dev_err(dev, "Could not get mod clock\n"); 496 ret = PTR_ERR(phy->clk_mod); 497 goto err_put_clk_bus; 498 } 499 500 if (phy->variant->has_phy_clk) { 501 phy->clk_pll0 = of_clk_get_by_name(node, "pll-0"); 502 if (IS_ERR(phy->clk_pll0)) { 503 dev_err(dev, "Could not get pll-0 clock\n"); 504 ret = PTR_ERR(phy->clk_pll0); 505 goto err_put_clk_mod; 506 } 507 508 if (phy->variant->has_second_pll) { 509 phy->clk_pll1 = of_clk_get_by_name(node, "pll-1"); 510 if (IS_ERR(phy->clk_pll1)) { 511 dev_err(dev, "Could not get pll-1 clock\n"); 512 ret = PTR_ERR(phy->clk_pll1); 513 goto err_put_clk_pll0; 514 } 515 } 516 517 ret = sun8i_phy_clk_create(phy, dev, 518 phy->variant->has_second_pll); 519 if (ret) { 520 dev_err(dev, "Couldn't create the PHY clock\n"); 521 goto err_put_clk_pll1; 522 } 523 524 clk_prepare_enable(phy->clk_phy); 525 } 526 527 phy->rst_phy = of_reset_control_get_shared(node, "phy"); 528 if (IS_ERR(phy->rst_phy)) { 529 dev_err(dev, "Could not get phy reset control\n"); 530 ret = PTR_ERR(phy->rst_phy); 531 goto err_disable_clk_phy; 532 } 533 534 ret = reset_control_deassert(phy->rst_phy); 535 if (ret) { 536 dev_err(dev, "Cannot deassert phy reset control: %d\n", ret); 537 goto err_put_rst_phy; 538 } 539 540 ret = clk_prepare_enable(phy->clk_bus); 541 if (ret) { 542 dev_err(dev, "Cannot enable bus clock: %d\n", ret); 543 goto err_deassert_rst_phy; 544 } 545 546 ret = clk_prepare_enable(phy->clk_mod); 547 if (ret) { 548 dev_err(dev, "Cannot enable mod clock: %d\n", ret); 549 goto err_disable_clk_bus; 550 } 551 552 hdmi->phy = phy; 553 554 return 0; 555 556 err_disable_clk_bus: 557 clk_disable_unprepare(phy->clk_bus); 558 err_deassert_rst_phy: 559 reset_control_assert(phy->rst_phy); 560 err_put_rst_phy: 561 reset_control_put(phy->rst_phy); 562 err_disable_clk_phy: 563 clk_disable_unprepare(phy->clk_phy); 564 err_put_clk_pll1: 565 clk_put(phy->clk_pll1); 566 err_put_clk_pll0: 567 clk_put(phy->clk_pll0); 568 err_put_clk_mod: 569 clk_put(phy->clk_mod); 570 err_put_clk_bus: 571 clk_put(phy->clk_bus); 572 573 return ret; 574 } 575 576 void sun8i_hdmi_phy_remove(struct sun8i_dw_hdmi *hdmi) 577 { 578 struct sun8i_hdmi_phy *phy = hdmi->phy; 579 580 clk_disable_unprepare(phy->clk_mod); 581 clk_disable_unprepare(phy->clk_bus); 582 clk_disable_unprepare(phy->clk_phy); 583 584 reset_control_assert(phy->rst_phy); 585 586 reset_control_put(phy->rst_phy); 587 588 clk_put(phy->clk_pll0); 589 clk_put(phy->clk_pll1); 590 clk_put(phy->clk_mod); 591 clk_put(phy->clk_bus); 592 } 593