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 regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, pll_cfg1_init); 187 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG, 188 (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK, 189 pll_cfg2_init); 190 usleep_range(10000, 15000); 191 regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG, 192 SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2); 193 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 194 SUN8I_HDMI_PHY_PLL_CFG1_PLLEN, 195 SUN8I_HDMI_PHY_PLL_CFG1_PLLEN); 196 msleep(100); 197 198 /* get B value */ 199 regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val); 200 val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >> 201 SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT; 202 val = min(val + b_offset, (u32)0x3f); 203 204 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 205 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 | 206 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD, 207 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 | 208 SUN8I_HDMI_PHY_PLL_CFG1_REG_OD); 209 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 210 SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK, 211 val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT); 212 msleep(100); 213 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end); 214 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init); 215 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init); 216 217 return 0; 218 } 219 220 static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data, 221 struct drm_display_mode *mode) 222 { 223 struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data; 224 u32 val = 0; 225 226 if (mode->flags & DRM_MODE_FLAG_NHSYNC) 227 val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC; 228 229 if (mode->flags & DRM_MODE_FLAG_NVSYNC) 230 val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC; 231 232 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, 233 SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val); 234 235 if (phy->variant->has_phy_clk) 236 clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000); 237 238 return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000); 239 }; 240 241 static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi, 242 struct sun8i_hdmi_phy *phy) 243 { 244 dw_hdmi_phy_gen2_txpwron(hdmi, 0); 245 dw_hdmi_phy_gen2_pddq(hdmi, 1); 246 247 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG, 248 SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0); 249 } 250 251 static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi, 252 struct sun8i_hdmi_phy *phy) 253 { 254 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 255 SUN8I_HDMI_PHY_ANA_CFG1_LDOEN | 256 SUN8I_HDMI_PHY_ANA_CFG1_ENVBS | 257 SUN8I_HDMI_PHY_ANA_CFG1_ENBI); 258 regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0); 259 } 260 261 static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data) 262 { 263 struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data; 264 265 phy->variant->phy_disable(hdmi, phy); 266 } 267 268 static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = { 269 .init = &sun8i_hdmi_phy_config, 270 .disable = &sun8i_hdmi_phy_disable, 271 .read_hpd = &dw_hdmi_phy_read_hpd, 272 .update_hpd = &dw_hdmi_phy_update_hpd, 273 .setup_hpd = &dw_hdmi_phy_setup_hpd, 274 }; 275 276 static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy) 277 { 278 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, 279 SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK, 280 SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK); 281 282 /* 283 * Set PHY I2C address. It must match to the address set by 284 * dw_hdmi_phy_set_slave_addr(). 285 */ 286 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG, 287 SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK, 288 SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR)); 289 } 290 291 static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy) 292 { 293 unsigned int val; 294 295 regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0); 296 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 297 SUN8I_HDMI_PHY_ANA_CFG1_ENBI, 298 SUN8I_HDMI_PHY_ANA_CFG1_ENBI); 299 udelay(5); 300 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 301 SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN, 302 SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN); 303 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 304 SUN8I_HDMI_PHY_ANA_CFG1_ENVBS, 305 SUN8I_HDMI_PHY_ANA_CFG1_ENVBS); 306 usleep_range(10, 20); 307 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 308 SUN8I_HDMI_PHY_ANA_CFG1_LDOEN, 309 SUN8I_HDMI_PHY_ANA_CFG1_LDOEN); 310 udelay(5); 311 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 312 SUN8I_HDMI_PHY_ANA_CFG1_CKEN, 313 SUN8I_HDMI_PHY_ANA_CFG1_CKEN); 314 usleep_range(40, 100); 315 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 316 SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL, 317 SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL); 318 usleep_range(100, 200); 319 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 320 SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG, 321 SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG); 322 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 323 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 | 324 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 | 325 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2, 326 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 | 327 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 | 328 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2); 329 330 /* wait for calibration to finish */ 331 regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val, 332 (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D), 333 100, 2000); 334 335 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 336 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK, 337 SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK); 338 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 339 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 | 340 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 | 341 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 | 342 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK, 343 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 | 344 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 | 345 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 | 346 SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK); 347 348 /* enable DDC communication */ 349 regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, 350 SUN8I_HDMI_PHY_ANA_CFG3_SCLEN | 351 SUN8I_HDMI_PHY_ANA_CFG3_SDAEN, 352 SUN8I_HDMI_PHY_ANA_CFG3_SCLEN | 353 SUN8I_HDMI_PHY_ANA_CFG3_SDAEN); 354 355 /* set HW control of CEC pins */ 356 regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0); 357 358 /* read calibration data */ 359 regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val); 360 phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2; 361 } 362 363 void sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy) 364 { 365 /* enable read access to HDMI controller */ 366 regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG, 367 SUN8I_HDMI_PHY_READ_EN_MAGIC); 368 369 /* unscramble register offsets */ 370 regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG, 371 SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC); 372 373 phy->variant->phy_init(phy); 374 } 375 376 const struct dw_hdmi_phy_ops *sun8i_hdmi_phy_get_ops(void) 377 { 378 return &sun8i_hdmi_phy_ops; 379 } 380 381 static struct regmap_config sun8i_hdmi_phy_regmap_config = { 382 .reg_bits = 32, 383 .val_bits = 32, 384 .reg_stride = 4, 385 .max_register = SUN8I_HDMI_PHY_CEC_REG, 386 .name = "phy" 387 }; 388 389 static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = { 390 .phy_init = &sun8i_hdmi_phy_init_a83t, 391 .phy_disable = &sun8i_hdmi_phy_disable_a83t, 392 .phy_config = &sun8i_hdmi_phy_config_a83t, 393 }; 394 395 static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = { 396 .has_phy_clk = true, 397 .phy_init = &sun8i_hdmi_phy_init_h3, 398 .phy_disable = &sun8i_hdmi_phy_disable_h3, 399 .phy_config = &sun8i_hdmi_phy_config_h3, 400 }; 401 402 static const struct of_device_id sun8i_hdmi_phy_of_table[] = { 403 { 404 .compatible = "allwinner,sun8i-a83t-hdmi-phy", 405 .data = &sun8i_a83t_hdmi_phy, 406 }, 407 { 408 .compatible = "allwinner,sun8i-h3-hdmi-phy", 409 .data = &sun8i_h3_hdmi_phy, 410 }, 411 { /* sentinel */ } 412 }; 413 414 int sun8i_hdmi_phy_probe(struct sun8i_dw_hdmi *hdmi, struct device_node *node) 415 { 416 const struct of_device_id *match; 417 struct device *dev = hdmi->dev; 418 struct sun8i_hdmi_phy *phy; 419 struct resource res; 420 void __iomem *regs; 421 int ret; 422 423 match = of_match_node(sun8i_hdmi_phy_of_table, node); 424 if (!match) { 425 dev_err(dev, "Incompatible HDMI PHY\n"); 426 return -EINVAL; 427 } 428 429 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); 430 if (!phy) 431 return -ENOMEM; 432 433 phy->variant = (struct sun8i_hdmi_phy_variant *)match->data; 434 435 ret = of_address_to_resource(node, 0, &res); 436 if (ret) { 437 dev_err(dev, "phy: Couldn't get our resources\n"); 438 return ret; 439 } 440 441 regs = devm_ioremap_resource(dev, &res); 442 if (IS_ERR(regs)) { 443 dev_err(dev, "Couldn't map the HDMI PHY registers\n"); 444 return PTR_ERR(regs); 445 } 446 447 phy->regs = devm_regmap_init_mmio(dev, regs, 448 &sun8i_hdmi_phy_regmap_config); 449 if (IS_ERR(phy->regs)) { 450 dev_err(dev, "Couldn't create the HDMI PHY regmap\n"); 451 return PTR_ERR(phy->regs); 452 } 453 454 phy->clk_bus = of_clk_get_by_name(node, "bus"); 455 if (IS_ERR(phy->clk_bus)) { 456 dev_err(dev, "Could not get bus clock\n"); 457 return PTR_ERR(phy->clk_bus); 458 } 459 460 phy->clk_mod = of_clk_get_by_name(node, "mod"); 461 if (IS_ERR(phy->clk_mod)) { 462 dev_err(dev, "Could not get mod clock\n"); 463 ret = PTR_ERR(phy->clk_mod); 464 goto err_put_clk_bus; 465 } 466 467 if (phy->variant->has_phy_clk) { 468 phy->clk_pll0 = of_clk_get_by_name(node, "pll-0"); 469 if (IS_ERR(phy->clk_pll0)) { 470 dev_err(dev, "Could not get pll-0 clock\n"); 471 ret = PTR_ERR(phy->clk_pll0); 472 goto err_put_clk_mod; 473 } 474 475 ret = sun8i_phy_clk_create(phy, dev); 476 if (ret) { 477 dev_err(dev, "Couldn't create the PHY clock\n"); 478 goto err_put_clk_pll0; 479 } 480 } 481 482 phy->rst_phy = of_reset_control_get_shared(node, "phy"); 483 if (IS_ERR(phy->rst_phy)) { 484 dev_err(dev, "Could not get phy reset control\n"); 485 ret = PTR_ERR(phy->rst_phy); 486 goto err_put_clk_pll0; 487 } 488 489 ret = reset_control_deassert(phy->rst_phy); 490 if (ret) { 491 dev_err(dev, "Cannot deassert phy reset control: %d\n", ret); 492 goto err_put_rst_phy; 493 } 494 495 ret = clk_prepare_enable(phy->clk_bus); 496 if (ret) { 497 dev_err(dev, "Cannot enable bus clock: %d\n", ret); 498 goto err_deassert_rst_phy; 499 } 500 501 ret = clk_prepare_enable(phy->clk_mod); 502 if (ret) { 503 dev_err(dev, "Cannot enable mod clock: %d\n", ret); 504 goto err_disable_clk_bus; 505 } 506 507 hdmi->phy = phy; 508 509 return 0; 510 511 err_disable_clk_bus: 512 clk_disable_unprepare(phy->clk_bus); 513 err_deassert_rst_phy: 514 reset_control_assert(phy->rst_phy); 515 err_put_rst_phy: 516 reset_control_put(phy->rst_phy); 517 err_put_clk_pll0: 518 if (phy->variant->has_phy_clk) 519 clk_put(phy->clk_pll0); 520 err_put_clk_mod: 521 clk_put(phy->clk_mod); 522 err_put_clk_bus: 523 clk_put(phy->clk_bus); 524 525 return ret; 526 } 527 528 void sun8i_hdmi_phy_remove(struct sun8i_dw_hdmi *hdmi) 529 { 530 struct sun8i_hdmi_phy *phy = hdmi->phy; 531 532 clk_disable_unprepare(phy->clk_mod); 533 clk_disable_unprepare(phy->clk_bus); 534 535 reset_control_assert(phy->rst_phy); 536 537 reset_control_put(phy->rst_phy); 538 539 if (phy->variant->has_phy_clk) 540 clk_put(phy->clk_pll0); 541 clk_put(phy->clk_mod); 542 clk_put(phy->clk_bus); 543 } 544