• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) STMicroelectronics SA 2014
3  * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
4  * License terms:  GNU General Public License (GPL), version 2
5  */
6 
7 #include "sti_hdmi_tx3g0c55phy.h"
8 
9 #define HDMI_SRZ_PLL_CFG                0x0504
10 #define HDMI_SRZ_TAP_1                  0x0508
11 #define HDMI_SRZ_TAP_2                  0x050C
12 #define HDMI_SRZ_TAP_3                  0x0510
13 #define HDMI_SRZ_CTRL                   0x0514
14 
15 #define HDMI_SRZ_PLL_CFG_POWER_DOWN     BIT(0)
16 #define HDMI_SRZ_PLL_CFG_VCOR_SHIFT     1
17 #define HDMI_SRZ_PLL_CFG_VCOR_425MHZ    0
18 #define HDMI_SRZ_PLL_CFG_VCOR_850MHZ    1
19 #define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ   2
20 #define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ   3
21 #define HDMI_SRZ_PLL_CFG_VCOR_MASK      3
22 #define HDMI_SRZ_PLL_CFG_VCOR(x)        (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT)
23 #define HDMI_SRZ_PLL_CFG_NDIV_SHIFT     8
24 #define HDMI_SRZ_PLL_CFG_NDIV_MASK      (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT)
25 #define HDMI_SRZ_PLL_CFG_MODE_SHIFT     16
26 #define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ  0x1
27 #define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ  0x4
28 #define HDMI_SRZ_PLL_CFG_MODE_27_MHZ    0x5
29 #define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6
30 #define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ  0x7
31 #define HDMI_SRZ_PLL_CFG_MODE_54_MHZ    0x8
32 #define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ  0x9
33 #define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA
34 #define HDMI_SRZ_PLL_CFG_MODE_81_MHZ    0xB
35 #define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ  0xC
36 #define HDMI_SRZ_PLL_CFG_MODE_108_MHZ   0xD
37 #define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE
38 #define HDMI_SRZ_PLL_CFG_MODE_165_MHZ   0xF
39 #define HDMI_SRZ_PLL_CFG_MODE_MASK      0xF
40 #define HDMI_SRZ_PLL_CFG_MODE(x)        (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT)
41 
42 #define HDMI_SRZ_CTRL_POWER_DOWN        (1 << 0)
43 #define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN  (1 << 1)
44 
45 /* sysconf registers */
46 #define HDMI_REJECTION_PLL_CONFIGURATION 0x0858	/* SYSTEM_CONFIG2534 */
47 #define HDMI_REJECTION_PLL_STATUS        0x0948	/* SYSTEM_CONFIG2594 */
48 
49 #define REJECTION_PLL_HDMI_ENABLE_SHIFT 0
50 #define REJECTION_PLL_HDMI_ENABLE_MASK  (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT)
51 #define REJECTION_PLL_HDMI_PDIV_SHIFT   24
52 #define REJECTION_PLL_HDMI_PDIV_MASK    (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT)
53 #define REJECTION_PLL_HDMI_NDIV_SHIFT   16
54 #define REJECTION_PLL_HDMI_NDIV_MASK    (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT)
55 #define REJECTION_PLL_HDMI_MDIV_SHIFT   8
56 #define REJECTION_PLL_HDMI_MDIV_MASK    (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT)
57 
58 #define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0)
59 
60 #define HDMI_TIMEOUT_PLL_LOCK  50   /*milliseconds */
61 
62 /**
63  * pll mode structure
64  *
65  * A pointer to an array of these structures is passed to a TMDS (HDMI) output
66  * via the control interface to provide board and SoC specific
67  * configurations of the HDMI PHY. Each entry in the array specifies a hardware
68  * specific configuration for a given TMDS clock frequency range. The array
69  * should be terminated with an entry that has all fields set to zero.
70  *
71  * @min: Lower bound of TMDS clock frequency this entry applies to
72  * @max: Upper bound of TMDS clock frequency this entry applies to
73  * @mode: SoC specific register configuration
74  */
75 struct pllmode {
76 	u32 min;
77 	u32 max;
78 	u32 mode;
79 };
80 
81 #define NB_PLL_MODE 7
82 static struct pllmode pllmodes[NB_PLL_MODE] = {
83 	{13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ},
84 	{25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ},
85 	{27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ},
86 	{54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ},
87 	{72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ},
88 	{108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ},
89 	{148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ}
90 };
91 
92 #define NB_HDMI_PHY_CONFIG 5
93 static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
94 	{0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} },
95 	{40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} },
96 	{140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} },
97 	{160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} },
98 	{250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} },
99 };
100 
101 #define PLL_CHANGE_DELAY	1 /* ms */
102 
103 /**
104  * Disable the pll rejection
105  *
106  * @hdmi: pointer on the hdmi internal structure
107  *
108  * return true if the pll has been disabled
109  */
disable_pll_rejection(struct sti_hdmi * hdmi)110 static bool disable_pll_rejection(struct sti_hdmi *hdmi)
111 {
112 	u32 val;
113 
114 	DRM_DEBUG_DRIVER("\n");
115 
116 	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
117 	val &= ~REJECTION_PLL_HDMI_ENABLE_MASK;
118 	writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
119 
120 	msleep(PLL_CHANGE_DELAY);
121 	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
122 
123 	return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
124 }
125 
126 /**
127  * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL
128  * clock input to the new PHY PLL that generates the serializer clock
129  * (TMDS*10) and the TMDS clock which is now fed back into the HDMI
130  * formatter instead of the TMDS clock line from ClockGenB.
131  *
132  * @hdmi: pointer on the hdmi internal structure
133  *
134  * return true if pll has been correctly set
135  */
enable_pll_rejection(struct sti_hdmi * hdmi)136 static bool enable_pll_rejection(struct sti_hdmi *hdmi)
137 {
138 	unsigned int inputclock;
139 	u32 mdiv, ndiv, pdiv, val;
140 
141 	DRM_DEBUG_DRIVER("\n");
142 
143 	if (!disable_pll_rejection(hdmi))
144 		return false;
145 
146 	inputclock = hdmi->mode.clock * 1000;
147 
148 	DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock);
149 
150 
151 	/* Power up the HDMI rejection PLL
152 	 * Note: On this SoC (stiH416) we are forced to have the input clock
153 	 * be equal to the HDMI pixel clock.
154 	 *
155 	 * The values here have been suggested by validation however they are
156 	 * still provisional and subject to change.
157 	 *
158 	 * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv)
159 	 */
160 	if (inputclock < 50000000) {
161 		/*
162 		 * For slower clocks we need to multiply more to keep the
163 		 * internal VCO frequency within the physical specification
164 		 * of the PLL.
165 		 */
166 		pdiv = 4;
167 		ndiv = 240;
168 		mdiv = 30;
169 	} else {
170 		pdiv = 2;
171 		ndiv = 60;
172 		mdiv = 30;
173 	}
174 
175 	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
176 
177 	val &= ~(REJECTION_PLL_HDMI_PDIV_MASK |
178 		REJECTION_PLL_HDMI_NDIV_MASK |
179 		REJECTION_PLL_HDMI_MDIV_MASK |
180 		REJECTION_PLL_HDMI_ENABLE_MASK);
181 
182 	val |=	(pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) |
183 		(ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) |
184 		(mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) |
185 		(0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT);
186 
187 	writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
188 
189 	msleep(PLL_CHANGE_DELAY);
190 	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
191 
192 	return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
193 }
194 
195 /**
196  * Start hdmi phy macro cell tx3g0c55
197  *
198  * @hdmi: pointer on the hdmi internal structure
199  *
200  * Return false if an error occur
201  */
sti_hdmi_tx3g0c55phy_start(struct sti_hdmi * hdmi)202 static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi)
203 {
204 	u32 ckpxpll = hdmi->mode.clock * 1000;
205 	u32 val, tmdsck, freqvco, pllctrl = 0;
206 	unsigned int i;
207 
208 	if (!enable_pll_rejection(hdmi))
209 		return false;
210 
211 	DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
212 
213 	/* Assuming no pixel repetition and 24bits color */
214 	tmdsck = ckpxpll;
215 	pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT;
216 
217 	/*
218 	 * Setup the PLL mode parameter based on the ckpxpll. If we haven't got
219 	 * a clock frequency supported by one of the specific PLL modes then we
220 	 * will end up using the generic mode (0) which only supports a 10x
221 	 * multiplier, hence only 24bit color.
222 	 */
223 	for (i = 0; i < NB_PLL_MODE; i++) {
224 		if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max)
225 			pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode);
226 	}
227 
228 	freqvco = tmdsck * 10;
229 	if (freqvco <= 425000000UL)
230 		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ);
231 	else if (freqvco <= 850000000UL)
232 		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ);
233 	else if (freqvco <= 1700000000UL)
234 		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ);
235 	else if (freqvco <= 2970000000UL)
236 		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ);
237 	else {
238 		DRM_ERROR("PHY serializer clock out of range\n");
239 		goto err;
240 	}
241 
242 	/*
243 	 * Configure and power up the PHY PLL
244 	 */
245 	hdmi->event_received = false;
246 	DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
247 	hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG);
248 
249 	/* wait PLL interrupt */
250 	wait_event_interruptible_timeout(hdmi->wait_event,
251 					 hdmi->event_received == true,
252 					 msecs_to_jiffies
253 					 (HDMI_TIMEOUT_PLL_LOCK));
254 
255 	if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
256 		DRM_ERROR("hdmi phy pll not locked\n");
257 		goto err;
258 	}
259 
260 	DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
261 
262 	/*
263 	 * To configure the source termination and pre-emphasis appropriately
264 	 * for different high speed TMDS clock frequencies a phy configuration
265 	 * table must be provided, tailored to the SoC and board combination.
266 	 */
267 	for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
268 		if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
269 		    (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
270 			val = hdmiphy_config[i].config[0];
271 			hdmi_write(hdmi, val, HDMI_SRZ_TAP_1);
272 			val = hdmiphy_config[i].config[1];
273 			hdmi_write(hdmi, val, HDMI_SRZ_TAP_2);
274 			val = hdmiphy_config[i].config[2];
275 			hdmi_write(hdmi, val, HDMI_SRZ_TAP_3);
276 			val = hdmiphy_config[i].config[3];
277 			val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN;
278 			val &= ~HDMI_SRZ_CTRL_POWER_DOWN;
279 			hdmi_write(hdmi, val, HDMI_SRZ_CTRL);
280 
281 			DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n",
282 					 hdmiphy_config[i].config[0],
283 					 hdmiphy_config[i].config[1],
284 					 hdmiphy_config[i].config[2],
285 					 hdmiphy_config[i].config[3]);
286 			return true;
287 		}
288 	}
289 
290 	/*
291 	 * Default, power up the serializer with no pre-emphasis or source
292 	 * termination.
293 	 */
294 	hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1);
295 	hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2);
296 	hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3);
297 	hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL);
298 
299 	return true;
300 
301 err:
302 	disable_pll_rejection(hdmi);
303 
304 	return false;
305 }
306 
307 /**
308  * Stop hdmi phy macro cell tx3g0c55
309  *
310  * @hdmi: pointer on the hdmi internal structure
311  */
sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi * hdmi)312 static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi)
313 {
314 	DRM_DEBUG_DRIVER("\n");
315 
316 	hdmi->event_received = false;
317 
318 	hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL);
319 	hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG);
320 
321 	/* wait PLL interrupt */
322 	wait_event_interruptible_timeout(hdmi->wait_event,
323 					 hdmi->event_received == true,
324 					 msecs_to_jiffies
325 					 (HDMI_TIMEOUT_PLL_LOCK));
326 
327 	if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
328 		DRM_ERROR("hdmi phy pll not well disabled\n");
329 
330 	disable_pll_rejection(hdmi);
331 }
332 
333 struct hdmi_phy_ops tx3g0c55phy_ops = {
334 	.start = sti_hdmi_tx3g0c55phy_start,
335 	.stop = sti_hdmi_tx3g0c55phy_stop,
336 };
337