Merge branch 'drm-next-4.7' of git://people.freedesktop.org/~agd5f/linux into drm...
[deliverable/linux.git] / drivers / gpu / drm / sti / sti_hdmi_tx3g0c55phy.c
CommitLineData
5402626c
BG
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 */
75struct pllmode {
76 u32 min;
77 u32 max;
78 u32 mode;
79};
80
81#define NB_PLL_MODE 7
82static 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
93static 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 */
110static 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 */
136static 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 */
202static 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
301err:
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 */
312static 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
333struct hdmi_phy_ops tx3g0c55phy_ops = {
334 .start = sti_hdmi_tx3g0c55phy_start,
335 .stop = sti_hdmi_tx3g0c55phy_stop,
336};
This page took 0.143527 seconds and 5 git commands to generate.