xref: /linux/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c (revision 6c8c1406)
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 #include <linux/of_platform.h>
9 
10 #include "sun8i_dw_hdmi.h"
11 
12 /*
13  * Address can be actually any value. Here is set to same value as
14  * it is set in BSP driver.
15  */
16 #define I2C_ADDR	0x69
17 
18 static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = {
19 	{
20 		30666000, {
21 			{ 0x00b3, 0x0000 },
22 			{ 0x2153, 0x0000 },
23 			{ 0x40f3, 0x0000 },
24 		},
25 	},  {
26 		36800000, {
27 			{ 0x00b3, 0x0000 },
28 			{ 0x2153, 0x0000 },
29 			{ 0x40a2, 0x0001 },
30 		},
31 	},  {
32 		46000000, {
33 			{ 0x00b3, 0x0000 },
34 			{ 0x2142, 0x0001 },
35 			{ 0x40a2, 0x0001 },
36 		},
37 	},  {
38 		61333000, {
39 			{ 0x0072, 0x0001 },
40 			{ 0x2142, 0x0001 },
41 			{ 0x40a2, 0x0001 },
42 		},
43 	},  {
44 		73600000, {
45 			{ 0x0072, 0x0001 },
46 			{ 0x2142, 0x0001 },
47 			{ 0x4061, 0x0002 },
48 		},
49 	},  {
50 		92000000, {
51 			{ 0x0072, 0x0001 },
52 			{ 0x2145, 0x0002 },
53 			{ 0x4061, 0x0002 },
54 		},
55 	},  {
56 		122666000, {
57 			{ 0x0051, 0x0002 },
58 			{ 0x2145, 0x0002 },
59 			{ 0x4061, 0x0002 },
60 		},
61 	},  {
62 		147200000, {
63 			{ 0x0051, 0x0002 },
64 			{ 0x2145, 0x0002 },
65 			{ 0x4064, 0x0003 },
66 		},
67 	},  {
68 		184000000, {
69 			{ 0x0051, 0x0002 },
70 			{ 0x214c, 0x0003 },
71 			{ 0x4064, 0x0003 },
72 		},
73 	},  {
74 		226666000, {
75 			{ 0x0040, 0x0003 },
76 			{ 0x214c, 0x0003 },
77 			{ 0x4064, 0x0003 },
78 		},
79 	},  {
80 		272000000, {
81 			{ 0x0040, 0x0003 },
82 			{ 0x214c, 0x0003 },
83 			{ 0x5a64, 0x0003 },
84 		},
85 	},  {
86 		340000000, {
87 			{ 0x0040, 0x0003 },
88 			{ 0x3b4c, 0x0003 },
89 			{ 0x5a64, 0x0003 },
90 		},
91 	},  {
92 		594000000, {
93 			{ 0x1a40, 0x0003 },
94 			{ 0x3b4c, 0x0003 },
95 			{ 0x5a64, 0x0003 },
96 		},
97 	}, {
98 		~0UL, {
99 			{ 0x0000, 0x0000 },
100 			{ 0x0000, 0x0000 },
101 			{ 0x0000, 0x0000 },
102 		},
103 	}
104 };
105 
106 static const struct dw_hdmi_curr_ctrl sun50i_h6_cur_ctr[] = {
107 	/* pixelclk    bpp8    bpp10   bpp12 */
108 	{ 27000000,  { 0x0012, 0x0000, 0x0000 }, },
109 	{ 74250000,  { 0x0013, 0x001a, 0x001b }, },
110 	{ 148500000, { 0x0019, 0x0033, 0x0034 }, },
111 	{ 297000000, { 0x0019, 0x001b, 0x001b }, },
112 	{ 594000000, { 0x0010, 0x001b, 0x001b }, },
113 	{ ~0UL,      { 0x0000, 0x0000, 0x0000 }, }
114 };
115 
116 static const struct dw_hdmi_phy_config sun50i_h6_phy_config[] = {
117 	/*pixelclk   symbol   term   vlev*/
118 	{ 27000000,  0x8009, 0x0007, 0x02b0 },
119 	{ 74250000,  0x8009, 0x0006, 0x022d },
120 	{ 148500000, 0x8029, 0x0006, 0x0270 },
121 	{ 297000000, 0x8039, 0x0005, 0x01ab },
122 	{ 594000000, 0x8029, 0x0000, 0x008a },
123 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
124 };
125 
126 static void sun8i_hdmi_phy_set_polarity(struct sun8i_hdmi_phy *phy,
127 					const struct drm_display_mode *mode)
128 {
129 	u32 val = 0;
130 
131 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
132 		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
133 
134 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
135 		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
136 
137 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
138 			   SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
139 };
140 
141 static int sun8i_a83t_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
142 				      const struct drm_display_info *display,
143 				      const struct drm_display_mode *mode)
144 {
145 	unsigned int clk_rate = mode->crtc_clock * 1000;
146 	struct sun8i_hdmi_phy *phy = data;
147 
148 	sun8i_hdmi_phy_set_polarity(phy, mode);
149 
150 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
151 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
152 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
153 
154 	/* power down */
155 	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
156 	dw_hdmi_phy_gen2_pddq(hdmi, 1);
157 
158 	dw_hdmi_phy_gen2_reset(hdmi);
159 
160 	dw_hdmi_phy_gen2_pddq(hdmi, 0);
161 
162 	dw_hdmi_phy_i2c_set_addr(hdmi, I2C_ADDR);
163 
164 	/*
165 	 * Values are taken from BSP HDMI driver. Although AW didn't
166 	 * release any documentation, explanation of this values can
167 	 * be found in i.MX 6Dual/6Quad Reference Manual.
168 	 */
169 	if (clk_rate <= 27000000) {
170 		dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
171 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
172 		dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10);
173 		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
174 		dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e);
175 		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
176 	} else if (clk_rate <= 74250000) {
177 		dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06);
178 		dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
179 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
180 		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
181 		dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e);
182 		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
183 	} else if (clk_rate <= 148500000) {
184 		dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06);
185 		dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
186 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
187 		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
188 		dw_hdmi_phy_i2c_write(hdmi, 0x0021, 0x0e);
189 		dw_hdmi_phy_i2c_write(hdmi, 0x8029, 0x09);
190 	} else {
191 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x06);
192 		dw_hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
193 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
194 		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
195 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x0e);
196 		dw_hdmi_phy_i2c_write(hdmi, 0x802b, 0x09);
197 	}
198 
199 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x1e);
200 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);
201 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x17);
202 
203 	dw_hdmi_phy_gen2_txpwron(hdmi, 1);
204 
205 	return 0;
206 }
207 
208 static void sun8i_a83t_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
209 {
210 	struct sun8i_hdmi_phy *phy = data;
211 
212 	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
213 	dw_hdmi_phy_gen2_pddq(hdmi, 1);
214 
215 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
216 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
217 }
218 
219 static const struct dw_hdmi_phy_ops sun8i_a83t_hdmi_phy_ops = {
220 	.init		= sun8i_a83t_hdmi_phy_config,
221 	.disable	= sun8i_a83t_hdmi_phy_disable,
222 	.read_hpd	= dw_hdmi_phy_read_hpd,
223 	.update_hpd	= dw_hdmi_phy_update_hpd,
224 	.setup_hpd	= dw_hdmi_phy_setup_hpd,
225 };
226 
227 static int sun8i_h3_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
228 				    const struct drm_display_info *display,
229 				    const struct drm_display_mode *mode)
230 {
231 	unsigned int clk_rate = mode->crtc_clock * 1000;
232 	struct sun8i_hdmi_phy *phy = data;
233 	u32 pll_cfg1_init;
234 	u32 pll_cfg2_init;
235 	u32 ana_cfg1_end;
236 	u32 ana_cfg2_init;
237 	u32 ana_cfg3_init;
238 	u32 b_offset = 0;
239 	u32 val;
240 
241 	if (phy->variant->has_phy_clk)
242 		clk_set_rate(phy->clk_phy, clk_rate);
243 
244 	sun8i_hdmi_phy_set_polarity(phy, mode);
245 
246 	/* bandwidth / frequency independent settings */
247 
248 	pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
249 			SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN |
250 			SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) |
251 			SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) |
252 			SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN |
253 			SUN8I_HDMI_PHY_PLL_CFG1_CS |
254 			SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) |
255 			SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) |
256 			SUN8I_HDMI_PHY_PLL_CFG1_BWS;
257 
258 	pll_cfg2_init = SUN8I_HDMI_PHY_PLL_CFG2_SV_H |
259 			SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN |
260 			SUN8I_HDMI_PHY_PLL_CFG2_SDIV2;
261 
262 	ana_cfg1_end = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) |
263 		       SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT |
264 		       SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT |
265 		       SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT |
266 		       SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT |
267 		       SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL |
268 		       SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG |
269 		       SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS |
270 		       SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN |
271 		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK |
272 		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL |
273 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK |
274 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
275 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
276 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
277 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 |
278 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
279 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
280 		       SUN8I_HDMI_PHY_ANA_CFG1_CKEN |
281 		       SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
282 		       SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
283 		       SUN8I_HDMI_PHY_ANA_CFG1_ENBI;
284 
285 	ana_cfg2_init = SUN8I_HDMI_PHY_ANA_CFG2_M_EN |
286 			SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK |
287 			SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN |
288 			SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) |
289 			SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1);
290 
291 	ana_cfg3_init = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) |
292 			SUN8I_HDMI_PHY_ANA_CFG3_SDAEN |
293 			SUN8I_HDMI_PHY_ANA_CFG3_SCLEN;
294 
295 	/* bandwidth / frequency dependent settings */
296 	if (clk_rate <= 27000000) {
297 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
298 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
299 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
300 				 SUN8I_HDMI_PHY_PLL_CFG2_S(4);
301 		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
302 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
303 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
304 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) |
305 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5);
306 	} else if (clk_rate <= 74250000) {
307 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
308 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
309 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
310 				 SUN8I_HDMI_PHY_PLL_CFG2_S(5);
311 		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
312 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
313 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
314 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) |
315 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7);
316 	} else if (clk_rate <= 148500000) {
317 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
318 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
319 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
320 				 SUN8I_HDMI_PHY_PLL_CFG2_S(6);
321 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
322 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
323 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2);
324 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) |
325 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9);
326 	} else {
327 		b_offset = 2;
328 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63);
329 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) |
330 				 SUN8I_HDMI_PHY_PLL_CFG2_S(7);
331 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
332 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
333 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4);
334 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) |
335 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13) |
336 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(3);
337 	}
338 
339 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
340 			   SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0);
341 
342 	/*
343 	 * NOTE: We have to be careful not to overwrite PHY parent
344 	 * clock selection bit and clock divider.
345 	 */
346 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
347 			   (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
348 			   pll_cfg1_init);
349 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG,
350 			   (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK,
351 			   pll_cfg2_init);
352 	usleep_range(10000, 15000);
353 	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG,
354 		     SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2);
355 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
356 			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN,
357 			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN);
358 	msleep(100);
359 
360 	/* get B value */
361 	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
362 	val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >>
363 		SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT;
364 	val = min(val + b_offset, (u32)0x3f);
365 
366 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
367 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
368 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD,
369 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
370 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD);
371 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
372 			   SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK,
373 			   val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT);
374 	msleep(100);
375 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end);
376 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init);
377 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init);
378 
379 	return 0;
380 }
381 
382 static void sun8i_h3_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
383 {
384 	struct sun8i_hdmi_phy *phy = data;
385 
386 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
387 		     SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
388 		     SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
389 		     SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
390 	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
391 }
392 
393 static const struct dw_hdmi_phy_ops sun8i_h3_hdmi_phy_ops = {
394 	.init		= sun8i_h3_hdmi_phy_config,
395 	.disable	= sun8i_h3_hdmi_phy_disable,
396 	.read_hpd	= dw_hdmi_phy_read_hpd,
397 	.update_hpd	= dw_hdmi_phy_update_hpd,
398 	.setup_hpd	= dw_hdmi_phy_setup_hpd,
399 };
400 
401 static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy)
402 {
403 	/* enable read access to HDMI controller */
404 	regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
405 		     SUN8I_HDMI_PHY_READ_EN_MAGIC);
406 
407 	/* unscramble register offsets */
408 	regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
409 		     SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
410 }
411 
412 static void sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy *phy)
413 {
414 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
415 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
416 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
417 
418 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
419 			   0xffff0000, 0x80c00000);
420 }
421 
422 static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy)
423 {
424 	sun8i_hdmi_phy_unlock(phy);
425 
426 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
427 			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK,
428 			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK);
429 
430 	/*
431 	 * Set PHY I2C address. It must match to the address set by
432 	 * dw_hdmi_phy_set_slave_addr().
433 	 */
434 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
435 			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK,
436 			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR));
437 }
438 
439 static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
440 {
441 	unsigned int val;
442 
443 	sun8i_hdmi_phy_unlock(phy);
444 
445 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0);
446 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
447 			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI,
448 			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
449 	udelay(5);
450 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
451 			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN,
452 			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN);
453 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
454 			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS,
455 			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS);
456 	usleep_range(10, 20);
457 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
458 			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN,
459 			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN);
460 	udelay(5);
461 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
462 			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN,
463 			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN);
464 	usleep_range(40, 100);
465 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
466 			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL,
467 			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL);
468 	usleep_range(100, 200);
469 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
470 			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG,
471 			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG);
472 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
473 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
474 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
475 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2,
476 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
477 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
478 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2);
479 
480 	/* wait for calibration to finish */
481 	regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val,
482 				 (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D),
483 				 100, 2000);
484 
485 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
486 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK,
487 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK);
488 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
489 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
490 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
491 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
492 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK,
493 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
494 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
495 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
496 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK);
497 
498 	/* enable DDC communication */
499 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG,
500 			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
501 			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN,
502 			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
503 			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN);
504 
505 	/* reset PHY PLL clock parent */
506 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
507 			   SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
508 
509 	/* set HW control of CEC pins */
510 	regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
511 
512 	/* read calibration data */
513 	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
514 	phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2;
515 }
516 
517 int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
518 {
519 	int ret;
520 
521 	ret = reset_control_deassert(phy->rst_phy);
522 	if (ret) {
523 		dev_err(phy->dev, "Cannot deassert phy reset control: %d\n", ret);
524 		return ret;
525 	}
526 
527 	ret = clk_prepare_enable(phy->clk_bus);
528 	if (ret) {
529 		dev_err(phy->dev, "Cannot enable bus clock: %d\n", ret);
530 		goto err_assert_rst_phy;
531 	}
532 
533 	ret = clk_prepare_enable(phy->clk_mod);
534 	if (ret) {
535 		dev_err(phy->dev, "Cannot enable mod clock: %d\n", ret);
536 		goto err_disable_clk_bus;
537 	}
538 
539 	if (phy->variant->has_phy_clk) {
540 		ret = sun8i_phy_clk_create(phy, phy->dev,
541 					   phy->variant->has_second_pll);
542 		if (ret) {
543 			dev_err(phy->dev, "Couldn't create the PHY clock\n");
544 			goto err_disable_clk_mod;
545 		}
546 
547 		clk_prepare_enable(phy->clk_phy);
548 	}
549 
550 	phy->variant->phy_init(phy);
551 
552 	return 0;
553 
554 err_disable_clk_mod:
555 	clk_disable_unprepare(phy->clk_mod);
556 err_disable_clk_bus:
557 	clk_disable_unprepare(phy->clk_bus);
558 err_assert_rst_phy:
559 	reset_control_assert(phy->rst_phy);
560 
561 	return ret;
562 }
563 
564 void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
565 {
566 	clk_disable_unprepare(phy->clk_mod);
567 	clk_disable_unprepare(phy->clk_bus);
568 	clk_disable_unprepare(phy->clk_phy);
569 
570 	reset_control_assert(phy->rst_phy);
571 }
572 
573 void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
574 			    struct dw_hdmi_plat_data *plat_data)
575 {
576 	const struct sun8i_hdmi_phy_variant *variant = phy->variant;
577 
578 	if (variant->phy_ops) {
579 		plat_data->phy_ops = variant->phy_ops;
580 		plat_data->phy_name = "sun8i_dw_hdmi_phy";
581 		plat_data->phy_data = phy;
582 	} else {
583 		plat_data->mpll_cfg = variant->mpll_cfg;
584 		plat_data->cur_ctr = variant->cur_ctr;
585 		plat_data->phy_config = variant->phy_cfg;
586 	}
587 }
588 
589 static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
590 	.reg_bits	= 32,
591 	.val_bits	= 32,
592 	.reg_stride	= 4,
593 	.max_register	= SUN8I_HDMI_PHY_CEC_REG,
594 	.name		= "phy"
595 };
596 
597 static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
598 	.phy_ops = &sun8i_a83t_hdmi_phy_ops,
599 	.phy_init = &sun8i_hdmi_phy_init_a83t,
600 };
601 
602 static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
603 	.has_phy_clk = true,
604 	.phy_ops = &sun8i_h3_hdmi_phy_ops,
605 	.phy_init = &sun8i_hdmi_phy_init_h3,
606 };
607 
608 static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
609 	.has_phy_clk = true,
610 	.has_second_pll = true,
611 	.phy_ops = &sun8i_h3_hdmi_phy_ops,
612 	.phy_init = &sun8i_hdmi_phy_init_h3,
613 };
614 
615 static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
616 	.has_phy_clk = true,
617 	.phy_ops = &sun8i_h3_hdmi_phy_ops,
618 	.phy_init = &sun8i_hdmi_phy_init_h3,
619 };
620 
621 static const struct sun8i_hdmi_phy_variant sun50i_h6_hdmi_phy = {
622 	.cur_ctr  = sun50i_h6_cur_ctr,
623 	.mpll_cfg = sun50i_h6_mpll_cfg,
624 	.phy_cfg  = sun50i_h6_phy_config,
625 	.phy_init = &sun50i_hdmi_phy_init_h6,
626 };
627 
628 static const struct of_device_id sun8i_hdmi_phy_of_table[] = {
629 	{
630 		.compatible = "allwinner,sun8i-a83t-hdmi-phy",
631 		.data = &sun8i_a83t_hdmi_phy,
632 	},
633 	{
634 		.compatible = "allwinner,sun8i-h3-hdmi-phy",
635 		.data = &sun8i_h3_hdmi_phy,
636 	},
637 	{
638 		.compatible = "allwinner,sun8i-r40-hdmi-phy",
639 		.data = &sun8i_r40_hdmi_phy,
640 	},
641 	{
642 		.compatible = "allwinner,sun50i-a64-hdmi-phy",
643 		.data = &sun50i_a64_hdmi_phy,
644 	},
645 	{
646 		.compatible = "allwinner,sun50i-h6-hdmi-phy",
647 		.data = &sun50i_h6_hdmi_phy,
648 	},
649 	{ /* sentinel */ }
650 };
651 
652 int sun8i_hdmi_phy_get(struct sun8i_dw_hdmi *hdmi, struct device_node *node)
653 {
654 	struct platform_device *pdev = of_find_device_by_node(node);
655 	struct sun8i_hdmi_phy *phy;
656 
657 	if (!pdev)
658 		return -EPROBE_DEFER;
659 
660 	phy = platform_get_drvdata(pdev);
661 	if (!phy) {
662 		put_device(&pdev->dev);
663 		return -EPROBE_DEFER;
664 	}
665 
666 	hdmi->phy = phy;
667 
668 	put_device(&pdev->dev);
669 
670 	return 0;
671 }
672 
673 static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
674 {
675 	struct device *dev = &pdev->dev;
676 	struct sun8i_hdmi_phy *phy;
677 	void __iomem *regs;
678 
679 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
680 	if (!phy)
681 		return -ENOMEM;
682 
683 	phy->variant = of_device_get_match_data(dev);
684 	phy->dev = dev;
685 
686 	regs = devm_platform_ioremap_resource(pdev, 0);
687 	if (IS_ERR(regs))
688 		return dev_err_probe(dev, PTR_ERR(regs),
689 				     "Couldn't map the HDMI PHY registers\n");
690 
691 	phy->regs = devm_regmap_init_mmio(dev, regs,
692 					  &sun8i_hdmi_phy_regmap_config);
693 	if (IS_ERR(phy->regs))
694 		return dev_err_probe(dev, PTR_ERR(phy->regs),
695 				     "Couldn't create the HDMI PHY regmap\n");
696 
697 	phy->clk_bus = devm_clk_get(dev, "bus");
698 	if (IS_ERR(phy->clk_bus))
699 		return dev_err_probe(dev, PTR_ERR(phy->clk_bus),
700 				     "Could not get bus clock\n");
701 
702 	phy->clk_mod = devm_clk_get(dev, "mod");
703 	if (IS_ERR(phy->clk_mod))
704 		return dev_err_probe(dev, PTR_ERR(phy->clk_mod),
705 				     "Could not get mod clock\n");
706 
707 	if (phy->variant->has_phy_clk) {
708 		phy->clk_pll0 = devm_clk_get(dev, "pll-0");
709 		if (IS_ERR(phy->clk_pll0))
710 			return dev_err_probe(dev, PTR_ERR(phy->clk_pll0),
711 					     "Could not get pll-0 clock\n");
712 
713 		if (phy->variant->has_second_pll) {
714 			phy->clk_pll1 = devm_clk_get(dev, "pll-1");
715 			if (IS_ERR(phy->clk_pll1))
716 				return dev_err_probe(dev, PTR_ERR(phy->clk_pll1),
717 						     "Could not get pll-1 clock\n");
718 		}
719 	}
720 
721 	phy->rst_phy = devm_reset_control_get_shared(dev, "phy");
722 	if (IS_ERR(phy->rst_phy))
723 		return dev_err_probe(dev, PTR_ERR(phy->rst_phy),
724 				     "Could not get phy reset control\n");
725 
726 	platform_set_drvdata(pdev, phy);
727 
728 	return 0;
729 }
730 
731 struct platform_driver sun8i_hdmi_phy_driver = {
732 	.probe  = sun8i_hdmi_phy_probe,
733 	.driver = {
734 		.name = "sun8i-hdmi-phy",
735 		.of_match_table = sun8i_hdmi_phy_of_table,
736 	},
737 };
738