• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
sun8i_hdmi_phy_config_a83t(struct dw_hdmi * hdmi,struct sun8i_hdmi_phy * phy,unsigned int clk_rate)126 static int sun8i_hdmi_phy_config_a83t(struct dw_hdmi *hdmi,
127 				      struct sun8i_hdmi_phy *phy,
128 				      unsigned int clk_rate)
129 {
130 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
131 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
132 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
133 
134 	/* power down */
135 	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
136 	dw_hdmi_phy_gen2_pddq(hdmi, 1);
137 
138 	dw_hdmi_phy_reset(hdmi);
139 
140 	dw_hdmi_phy_gen2_pddq(hdmi, 0);
141 
142 	dw_hdmi_phy_i2c_set_addr(hdmi, I2C_ADDR);
143 
144 	/*
145 	 * Values are taken from BSP HDMI driver. Although AW didn't
146 	 * release any documentation, explanation of this values can
147 	 * be found in i.MX 6Dual/6Quad Reference Manual.
148 	 */
149 	if (clk_rate <= 27000000) {
150 		dw_hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06);
151 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x15);
152 		dw_hdmi_phy_i2c_write(hdmi, 0x08da, 0x10);
153 		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
154 		dw_hdmi_phy_i2c_write(hdmi, 0x0318, 0x0e);
155 		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
156 	} else if (clk_rate <= 74250000) {
157 		dw_hdmi_phy_i2c_write(hdmi, 0x0540, 0x06);
158 		dw_hdmi_phy_i2c_write(hdmi, 0x0005, 0x15);
159 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
160 		dw_hdmi_phy_i2c_write(hdmi, 0x0007, 0x19);
161 		dw_hdmi_phy_i2c_write(hdmi, 0x02b5, 0x0e);
162 		dw_hdmi_phy_i2c_write(hdmi, 0x8009, 0x09);
163 	} else if (clk_rate <= 148500000) {
164 		dw_hdmi_phy_i2c_write(hdmi, 0x04a0, 0x06);
165 		dw_hdmi_phy_i2c_write(hdmi, 0x000a, 0x15);
166 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
167 		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
168 		dw_hdmi_phy_i2c_write(hdmi, 0x0021, 0x0e);
169 		dw_hdmi_phy_i2c_write(hdmi, 0x8029, 0x09);
170 	} else {
171 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x06);
172 		dw_hdmi_phy_i2c_write(hdmi, 0x000f, 0x15);
173 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x10);
174 		dw_hdmi_phy_i2c_write(hdmi, 0x0002, 0x19);
175 		dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x0e);
176 		dw_hdmi_phy_i2c_write(hdmi, 0x802b, 0x09);
177 	}
178 
179 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x1e);
180 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);
181 	dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x17);
182 
183 	dw_hdmi_phy_gen2_txpwron(hdmi, 1);
184 
185 	return 0;
186 }
187 
sun8i_hdmi_phy_config_h3(struct dw_hdmi * hdmi,struct sun8i_hdmi_phy * phy,unsigned int clk_rate)188 static int sun8i_hdmi_phy_config_h3(struct dw_hdmi *hdmi,
189 				    struct sun8i_hdmi_phy *phy,
190 				    unsigned int clk_rate)
191 {
192 	u32 pll_cfg1_init;
193 	u32 pll_cfg2_init;
194 	u32 ana_cfg1_end;
195 	u32 ana_cfg2_init;
196 	u32 ana_cfg3_init;
197 	u32 b_offset = 0;
198 	u32 val;
199 
200 	/* bandwidth / frequency independent settings */
201 
202 	pll_cfg1_init = SUN8I_HDMI_PHY_PLL_CFG1_LDO2_EN |
203 			SUN8I_HDMI_PHY_PLL_CFG1_LDO1_EN |
204 			SUN8I_HDMI_PHY_PLL_CFG1_LDO_VSET(7) |
205 			SUN8I_HDMI_PHY_PLL_CFG1_UNKNOWN(1) |
206 			SUN8I_HDMI_PHY_PLL_CFG1_PLLDBEN |
207 			SUN8I_HDMI_PHY_PLL_CFG1_CS |
208 			SUN8I_HDMI_PHY_PLL_CFG1_CP_S(2) |
209 			SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63) |
210 			SUN8I_HDMI_PHY_PLL_CFG1_BWS;
211 
212 	pll_cfg2_init = SUN8I_HDMI_PHY_PLL_CFG2_SV_H |
213 			SUN8I_HDMI_PHY_PLL_CFG2_VCOGAIN_EN |
214 			SUN8I_HDMI_PHY_PLL_CFG2_SDIV2;
215 
216 	ana_cfg1_end = SUN8I_HDMI_PHY_ANA_CFG1_REG_SVBH(1) |
217 		       SUN8I_HDMI_PHY_ANA_CFG1_AMP_OPT |
218 		       SUN8I_HDMI_PHY_ANA_CFG1_EMP_OPT |
219 		       SUN8I_HDMI_PHY_ANA_CFG1_AMPCK_OPT |
220 		       SUN8I_HDMI_PHY_ANA_CFG1_EMPCK_OPT |
221 		       SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL |
222 		       SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG |
223 		       SUN8I_HDMI_PHY_ANA_CFG1_REG_SCKTMDS |
224 		       SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN |
225 		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK |
226 		       SUN8I_HDMI_PHY_ANA_CFG1_TXEN_ALL |
227 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK |
228 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
229 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
230 		       SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
231 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2 |
232 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
233 		       SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
234 		       SUN8I_HDMI_PHY_ANA_CFG1_CKEN |
235 		       SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
236 		       SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
237 		       SUN8I_HDMI_PHY_ANA_CFG1_ENBI;
238 
239 	ana_cfg2_init = SUN8I_HDMI_PHY_ANA_CFG2_M_EN |
240 			SUN8I_HDMI_PHY_ANA_CFG2_REG_DENCK |
241 			SUN8I_HDMI_PHY_ANA_CFG2_REG_DEN |
242 			SUN8I_HDMI_PHY_ANA_CFG2_REG_CKSS(1) |
243 			SUN8I_HDMI_PHY_ANA_CFG2_REG_CSMPS(1);
244 
245 	ana_cfg3_init = SUN8I_HDMI_PHY_ANA_CFG3_REG_WIRE(0x3e0) |
246 			SUN8I_HDMI_PHY_ANA_CFG3_SDAEN |
247 			SUN8I_HDMI_PHY_ANA_CFG3_SCLEN;
248 
249 	/* bandwidth / frequency dependent settings */
250 	if (clk_rate <= 27000000) {
251 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
252 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
253 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
254 				 SUN8I_HDMI_PHY_PLL_CFG2_S(4);
255 		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
256 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
257 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
258 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(3) |
259 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(5);
260 	} else if (clk_rate <= 74250000) {
261 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
262 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
263 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
264 				 SUN8I_HDMI_PHY_PLL_CFG2_S(5);
265 		ana_cfg1_end |= SUN8I_HDMI_PHY_ANA_CFG1_REG_CALSW;
266 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4) |
267 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_RESDI(phy->rcal);
268 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(5) |
269 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(7);
270 	} else if (clk_rate <= 148500000) {
271 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_HV_IS_33 |
272 				 SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(32);
273 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(4) |
274 				 SUN8I_HDMI_PHY_PLL_CFG2_S(6);
275 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
276 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
277 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(2);
278 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(7) |
279 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(9);
280 	} else {
281 		b_offset = 2;
282 		pll_cfg1_init |= SUN8I_HDMI_PHY_PLL_CFG1_CNT_INT(63);
283 		pll_cfg2_init |= SUN8I_HDMI_PHY_PLL_CFG2_VCO_S(6) |
284 				 SUN8I_HDMI_PHY_PLL_CFG2_S(7);
285 		ana_cfg2_init |= SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSWCK |
286 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_BIGSW |
287 				 SUN8I_HDMI_PHY_ANA_CFG2_REG_SLV(4);
288 		ana_cfg3_init |= SUN8I_HDMI_PHY_ANA_CFG3_REG_AMPCK(9) |
289 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_AMP(13) |
290 				 SUN8I_HDMI_PHY_ANA_CFG3_REG_EMP(3);
291 	}
292 
293 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
294 			   SUN8I_HDMI_PHY_ANA_CFG1_TXEN_MASK, 0);
295 
296 	/*
297 	 * NOTE: We have to be careful not to overwrite PHY parent
298 	 * clock selection bit and clock divider.
299 	 */
300 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
301 			   (u32)~SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK,
302 			   pll_cfg1_init);
303 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG2_REG,
304 			   (u32)~SUN8I_HDMI_PHY_PLL_CFG2_PREDIV_MSK,
305 			   pll_cfg2_init);
306 	usleep_range(10000, 15000);
307 	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG3_REG,
308 		     SUN8I_HDMI_PHY_PLL_CFG3_SOUT_DIV2);
309 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
310 			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN,
311 			   SUN8I_HDMI_PHY_PLL_CFG1_PLLEN);
312 	msleep(100);
313 
314 	/* get B value */
315 	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
316 	val = (val & SUN8I_HDMI_PHY_ANA_STS_B_OUT_MSK) >>
317 		SUN8I_HDMI_PHY_ANA_STS_B_OUT_SHIFT;
318 	val = min(val + b_offset, (u32)0x3f);
319 
320 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
321 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
322 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD,
323 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD1 |
324 			   SUN8I_HDMI_PHY_PLL_CFG1_REG_OD);
325 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
326 			   SUN8I_HDMI_PHY_PLL_CFG1_B_IN_MSK,
327 			   val << SUN8I_HDMI_PHY_PLL_CFG1_B_IN_SHIFT);
328 	msleep(100);
329 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, ana_cfg1_end);
330 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG2_REG, ana_cfg2_init);
331 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG, ana_cfg3_init);
332 
333 	return 0;
334 }
335 
sun8i_hdmi_phy_config(struct dw_hdmi * hdmi,void * data,const struct drm_display_info * display,const struct drm_display_mode * mode)336 static int sun8i_hdmi_phy_config(struct dw_hdmi *hdmi, void *data,
337 				 const struct drm_display_info *display,
338 				 const struct drm_display_mode *mode)
339 {
340 	struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
341 	u32 val = 0;
342 
343 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
344 		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NHSYNC;
345 
346 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
347 		val |= SUN8I_HDMI_PHY_DBG_CTRL_POL_NVSYNC;
348 
349 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
350 			   SUN8I_HDMI_PHY_DBG_CTRL_POL_MASK, val);
351 
352 	if (phy->variant->has_phy_clk)
353 		clk_set_rate(phy->clk_phy, mode->crtc_clock * 1000);
354 
355 	return phy->variant->phy_config(hdmi, phy, mode->crtc_clock * 1000);
356 };
357 
sun8i_hdmi_phy_disable_a83t(struct dw_hdmi * hdmi,struct sun8i_hdmi_phy * phy)358 static void sun8i_hdmi_phy_disable_a83t(struct dw_hdmi *hdmi,
359 					struct sun8i_hdmi_phy *phy)
360 {
361 	dw_hdmi_phy_gen2_txpwron(hdmi, 0);
362 	dw_hdmi_phy_gen2_pddq(hdmi, 1);
363 
364 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
365 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN, 0);
366 }
367 
sun8i_hdmi_phy_disable_h3(struct dw_hdmi * hdmi,struct sun8i_hdmi_phy * phy)368 static void sun8i_hdmi_phy_disable_h3(struct dw_hdmi *hdmi,
369 				      struct sun8i_hdmi_phy *phy)
370 {
371 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
372 		     SUN8I_HDMI_PHY_ANA_CFG1_LDOEN |
373 		     SUN8I_HDMI_PHY_ANA_CFG1_ENVBS |
374 		     SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
375 	regmap_write(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG, 0);
376 }
377 
sun8i_hdmi_phy_disable(struct dw_hdmi * hdmi,void * data)378 static void sun8i_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
379 {
380 	struct sun8i_hdmi_phy *phy = (struct sun8i_hdmi_phy *)data;
381 
382 	phy->variant->phy_disable(hdmi, phy);
383 }
384 
385 static const struct dw_hdmi_phy_ops sun8i_hdmi_phy_ops = {
386 	.init = &sun8i_hdmi_phy_config,
387 	.disable = &sun8i_hdmi_phy_disable,
388 	.read_hpd = &dw_hdmi_phy_read_hpd,
389 	.update_hpd = &dw_hdmi_phy_update_hpd,
390 	.setup_hpd = &dw_hdmi_phy_setup_hpd,
391 };
392 
sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy * phy)393 static void sun8i_hdmi_phy_unlock(struct sun8i_hdmi_phy *phy)
394 {
395 	/* enable read access to HDMI controller */
396 	regmap_write(phy->regs, SUN8I_HDMI_PHY_READ_EN_REG,
397 		     SUN8I_HDMI_PHY_READ_EN_MAGIC);
398 
399 	/* unscramble register offsets */
400 	regmap_write(phy->regs, SUN8I_HDMI_PHY_UNSCRAMBLE_REG,
401 		     SUN8I_HDMI_PHY_UNSCRAMBLE_MAGIC);
402 }
403 
sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy * phy)404 static void sun50i_hdmi_phy_init_h6(struct sun8i_hdmi_phy *phy)
405 {
406 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
407 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN,
408 			   SUN8I_HDMI_PHY_REXT_CTRL_REXT_EN);
409 
410 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_REXT_CTRL_REG,
411 			   0xffff0000, 0x80c00000);
412 }
413 
sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy * phy)414 static void sun8i_hdmi_phy_init_a83t(struct sun8i_hdmi_phy *phy)
415 {
416 	sun8i_hdmi_phy_unlock(phy);
417 
418 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
419 			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK,
420 			   SUN8I_HDMI_PHY_DBG_CTRL_PX_LOCK);
421 
422 	/*
423 	 * Set PHY I2C address. It must match to the address set by
424 	 * dw_hdmi_phy_set_slave_addr().
425 	 */
426 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_DBG_CTRL_REG,
427 			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR_MASK,
428 			   SUN8I_HDMI_PHY_DBG_CTRL_ADDR(I2C_ADDR));
429 }
430 
sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy * phy)431 static void sun8i_hdmi_phy_init_h3(struct sun8i_hdmi_phy *phy)
432 {
433 	unsigned int val;
434 
435 	sun8i_hdmi_phy_unlock(phy);
436 
437 	regmap_write(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG, 0);
438 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
439 			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI,
440 			   SUN8I_HDMI_PHY_ANA_CFG1_ENBI);
441 	udelay(5);
442 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
443 			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN,
444 			   SUN8I_HDMI_PHY_ANA_CFG1_TMDSCLK_EN);
445 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
446 			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS,
447 			   SUN8I_HDMI_PHY_ANA_CFG1_ENVBS);
448 	usleep_range(10, 20);
449 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
450 			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN,
451 			   SUN8I_HDMI_PHY_ANA_CFG1_LDOEN);
452 	udelay(5);
453 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
454 			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN,
455 			   SUN8I_HDMI_PHY_ANA_CFG1_CKEN);
456 	usleep_range(40, 100);
457 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
458 			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL,
459 			   SUN8I_HDMI_PHY_ANA_CFG1_ENRCAL);
460 	usleep_range(100, 200);
461 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
462 			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG,
463 			   SUN8I_HDMI_PHY_ANA_CFG1_ENCALOG);
464 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
465 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
466 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
467 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2,
468 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS0 |
469 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS1 |
470 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDS2);
471 
472 	/* wait for calibration to finish */
473 	regmap_read_poll_timeout(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, val,
474 				 (val & SUN8I_HDMI_PHY_ANA_STS_RCALEND2D),
475 				 100, 2000);
476 
477 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
478 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK,
479 			   SUN8I_HDMI_PHY_ANA_CFG1_ENP2S_TMDSCLK);
480 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG1_REG,
481 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
482 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
483 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
484 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK,
485 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS0 |
486 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS1 |
487 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDS2 |
488 			   SUN8I_HDMI_PHY_ANA_CFG1_BIASEN_TMDSCLK);
489 
490 	/* enable DDC communication */
491 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_ANA_CFG3_REG,
492 			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
493 			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN,
494 			   SUN8I_HDMI_PHY_ANA_CFG3_SCLEN |
495 			   SUN8I_HDMI_PHY_ANA_CFG3_SDAEN);
496 
497 	/* reset PHY PLL clock parent */
498 	regmap_update_bits(phy->regs, SUN8I_HDMI_PHY_PLL_CFG1_REG,
499 			   SUN8I_HDMI_PHY_PLL_CFG1_CKIN_SEL_MSK, 0);
500 
501 	/* set HW control of CEC pins */
502 	regmap_write(phy->regs, SUN8I_HDMI_PHY_CEC_REG, 0);
503 
504 	/* read calibration data */
505 	regmap_read(phy->regs, SUN8I_HDMI_PHY_ANA_STS_REG, &val);
506 	phy->rcal = (val & SUN8I_HDMI_PHY_ANA_STS_RCAL_MASK) >> 2;
507 }
508 
sun8i_hdmi_phy_init(struct sun8i_hdmi_phy * phy)509 int sun8i_hdmi_phy_init(struct sun8i_hdmi_phy *phy)
510 {
511 	int ret;
512 
513 	ret = reset_control_deassert(phy->rst_phy);
514 	if (ret) {
515 		dev_err(phy->dev, "Cannot deassert phy reset control: %d\n", ret);
516 		return ret;
517 	}
518 
519 	ret = clk_prepare_enable(phy->clk_bus);
520 	if (ret) {
521 		dev_err(phy->dev, "Cannot enable bus clock: %d\n", ret);
522 		goto err_assert_rst_phy;
523 	}
524 
525 	ret = clk_prepare_enable(phy->clk_mod);
526 	if (ret) {
527 		dev_err(phy->dev, "Cannot enable mod clock: %d\n", ret);
528 		goto err_disable_clk_bus;
529 	}
530 
531 	if (phy->variant->has_phy_clk) {
532 		ret = sun8i_phy_clk_create(phy, phy->dev,
533 					   phy->variant->has_second_pll);
534 		if (ret) {
535 			dev_err(phy->dev, "Couldn't create the PHY clock\n");
536 			goto err_disable_clk_mod;
537 		}
538 
539 		clk_prepare_enable(phy->clk_phy);
540 	}
541 
542 	phy->variant->phy_init(phy);
543 
544 	return 0;
545 
546 err_disable_clk_mod:
547 	clk_disable_unprepare(phy->clk_mod);
548 err_disable_clk_bus:
549 	clk_disable_unprepare(phy->clk_bus);
550 err_assert_rst_phy:
551 	reset_control_assert(phy->rst_phy);
552 
553 	return ret;
554 }
555 
sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy * phy)556 void sun8i_hdmi_phy_deinit(struct sun8i_hdmi_phy *phy)
557 {
558 	clk_disable_unprepare(phy->clk_mod);
559 	clk_disable_unprepare(phy->clk_bus);
560 	clk_disable_unprepare(phy->clk_phy);
561 
562 	reset_control_assert(phy->rst_phy);
563 }
564 
sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy * phy,struct dw_hdmi_plat_data * plat_data)565 void sun8i_hdmi_phy_set_ops(struct sun8i_hdmi_phy *phy,
566 			    struct dw_hdmi_plat_data *plat_data)
567 {
568 	struct sun8i_hdmi_phy_variant *variant = phy->variant;
569 
570 	if (variant->is_custom_phy) {
571 		plat_data->phy_ops = &sun8i_hdmi_phy_ops;
572 		plat_data->phy_name = "sun8i_dw_hdmi_phy";
573 		plat_data->phy_data = phy;
574 	} else {
575 		plat_data->mpll_cfg = variant->mpll_cfg;
576 		plat_data->cur_ctr = variant->cur_ctr;
577 		plat_data->phy_config = variant->phy_cfg;
578 	}
579 }
580 
581 static const struct regmap_config sun8i_hdmi_phy_regmap_config = {
582 	.reg_bits	= 32,
583 	.val_bits	= 32,
584 	.reg_stride	= 4,
585 	.max_register	= SUN8I_HDMI_PHY_CEC_REG,
586 	.name		= "phy"
587 };
588 
589 static const struct sun8i_hdmi_phy_variant sun8i_a83t_hdmi_phy = {
590 	.is_custom_phy = true,
591 	.phy_init = &sun8i_hdmi_phy_init_a83t,
592 	.phy_disable = &sun8i_hdmi_phy_disable_a83t,
593 	.phy_config = &sun8i_hdmi_phy_config_a83t,
594 };
595 
596 static const struct sun8i_hdmi_phy_variant sun8i_h3_hdmi_phy = {
597 	.has_phy_clk = true,
598 	.is_custom_phy = true,
599 	.phy_init = &sun8i_hdmi_phy_init_h3,
600 	.phy_disable = &sun8i_hdmi_phy_disable_h3,
601 	.phy_config = &sun8i_hdmi_phy_config_h3,
602 };
603 
604 static const struct sun8i_hdmi_phy_variant sun8i_r40_hdmi_phy = {
605 	.has_phy_clk = true,
606 	.has_second_pll = true,
607 	.is_custom_phy = true,
608 	.phy_init = &sun8i_hdmi_phy_init_h3,
609 	.phy_disable = &sun8i_hdmi_phy_disable_h3,
610 	.phy_config = &sun8i_hdmi_phy_config_h3,
611 };
612 
613 static const struct sun8i_hdmi_phy_variant sun50i_a64_hdmi_phy = {
614 	.has_phy_clk = true,
615 	.is_custom_phy = true,
616 	.phy_init = &sun8i_hdmi_phy_init_h3,
617 	.phy_disable = &sun8i_hdmi_phy_disable_h3,
618 	.phy_config = &sun8i_hdmi_phy_config_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 
sun8i_hdmi_phy_get(struct sun8i_dw_hdmi * hdmi,struct device_node * node)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 
sun8i_hdmi_phy_probe(struct platform_device * pdev)673 static int sun8i_hdmi_phy_probe(struct platform_device *pdev)
674 {
675 	const struct of_device_id *match;
676 	struct device *dev = &pdev->dev;
677 	struct device_node *node = dev->of_node;
678 	struct sun8i_hdmi_phy *phy;
679 	struct resource res;
680 	void __iomem *regs;
681 	int ret;
682 
683 	match = of_match_node(sun8i_hdmi_phy_of_table, node);
684 	if (!match) {
685 		dev_err(dev, "Incompatible HDMI PHY\n");
686 		return -EINVAL;
687 	}
688 
689 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
690 	if (!phy)
691 		return -ENOMEM;
692 
693 	phy->variant = (struct sun8i_hdmi_phy_variant *)match->data;
694 	phy->dev = dev;
695 
696 	ret = of_address_to_resource(node, 0, &res);
697 	if (ret) {
698 		dev_err(dev, "phy: Couldn't get our resources\n");
699 		return ret;
700 	}
701 
702 	regs = devm_ioremap_resource(dev, &res);
703 	if (IS_ERR(regs)) {
704 		dev_err(dev, "Couldn't map the HDMI PHY registers\n");
705 		return PTR_ERR(regs);
706 	}
707 
708 	phy->regs = devm_regmap_init_mmio(dev, regs,
709 					  &sun8i_hdmi_phy_regmap_config);
710 	if (IS_ERR(phy->regs)) {
711 		dev_err(dev, "Couldn't create the HDMI PHY regmap\n");
712 		return PTR_ERR(phy->regs);
713 	}
714 
715 	phy->clk_bus = of_clk_get_by_name(node, "bus");
716 	if (IS_ERR(phy->clk_bus)) {
717 		dev_err(dev, "Could not get bus clock\n");
718 		return PTR_ERR(phy->clk_bus);
719 	}
720 
721 	phy->clk_mod = of_clk_get_by_name(node, "mod");
722 	if (IS_ERR(phy->clk_mod)) {
723 		dev_err(dev, "Could not get mod clock\n");
724 		ret = PTR_ERR(phy->clk_mod);
725 		goto err_put_clk_bus;
726 	}
727 
728 	if (phy->variant->has_phy_clk) {
729 		phy->clk_pll0 = of_clk_get_by_name(node, "pll-0");
730 		if (IS_ERR(phy->clk_pll0)) {
731 			dev_err(dev, "Could not get pll-0 clock\n");
732 			ret = PTR_ERR(phy->clk_pll0);
733 			goto err_put_clk_mod;
734 		}
735 
736 		if (phy->variant->has_second_pll) {
737 			phy->clk_pll1 = of_clk_get_by_name(node, "pll-1");
738 			if (IS_ERR(phy->clk_pll1)) {
739 				dev_err(dev, "Could not get pll-1 clock\n");
740 				ret = PTR_ERR(phy->clk_pll1);
741 				goto err_put_clk_pll0;
742 			}
743 		}
744 	}
745 
746 	phy->rst_phy = of_reset_control_get_shared(node, "phy");
747 	if (IS_ERR(phy->rst_phy)) {
748 		dev_err(dev, "Could not get phy reset control\n");
749 		ret = PTR_ERR(phy->rst_phy);
750 		goto err_put_clk_pll1;
751 	}
752 
753 	platform_set_drvdata(pdev, phy);
754 
755 	return 0;
756 
757 err_put_clk_pll1:
758 	clk_put(phy->clk_pll1);
759 err_put_clk_pll0:
760 	clk_put(phy->clk_pll0);
761 err_put_clk_mod:
762 	clk_put(phy->clk_mod);
763 err_put_clk_bus:
764 	clk_put(phy->clk_bus);
765 
766 	return ret;
767 }
768 
sun8i_hdmi_phy_remove(struct platform_device * pdev)769 static int sun8i_hdmi_phy_remove(struct platform_device *pdev)
770 {
771 	struct sun8i_hdmi_phy *phy = platform_get_drvdata(pdev);
772 
773 	reset_control_put(phy->rst_phy);
774 
775 	clk_put(phy->clk_pll0);
776 	clk_put(phy->clk_pll1);
777 	clk_put(phy->clk_mod);
778 	clk_put(phy->clk_bus);
779 	return 0;
780 }
781 
782 struct platform_driver sun8i_hdmi_phy_driver = {
783 	.probe  = sun8i_hdmi_phy_probe,
784 	.remove = sun8i_hdmi_phy_remove,
785 	.driver = {
786 		.name = "sun8i-hdmi-phy",
787 		.of_match_table = sun8i_hdmi_phy_of_table,
788 	},
789 };
790