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
7 #include "sti_hdmi_tx3g0c55phy.h"
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
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)
42 #define HDMI_SRZ_CTRL_POWER_DOWN (1 << 0)
43 #define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN (1 << 1)
45 /* sysconf registers */
46 #define HDMI_REJECTION_PLL_CONFIGURATION 0x0858 /* SYSTEM_CONFIG2534 */
47 #define HDMI_REJECTION_PLL_STATUS 0x0948 /* SYSTEM_CONFIG2594 */
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)
58 #define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0)
60 #define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
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.
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
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
}
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} },
101 #define PLL_CHANGE_DELAY 1 /* ms */
104 * Disable the pll rejection
106 * @hdmi: pointer on the hdmi internal structure
108 * return true if the pll has been disabled
110 static bool disable_pll_rejection(struct sti_hdmi
*hdmi
)
114 DRM_DEBUG_DRIVER("\n");
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
);
120 msleep(PLL_CHANGE_DELAY
);
121 val
= readl(hdmi
->syscfg
+ HDMI_REJECTION_PLL_STATUS
);
123 return !(val
& REJECTION_PLL_HDMI_REJ_PLL_LOCK
);
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.
132 * @hdmi: pointer on the hdmi internal structure
134 * return true if pll has been correctly set
136 static bool enable_pll_rejection(struct sti_hdmi
*hdmi
)
138 unsigned int inputclock
;
139 u32 mdiv
, ndiv
, pdiv
, val
;
141 DRM_DEBUG_DRIVER("\n");
143 if (!disable_pll_rejection(hdmi
))
146 inputclock
= hdmi
->mode
.clock
* 1000;
148 DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock
);
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.
155 * The values here have been suggested by validation however they are
156 * still provisional and subject to change.
158 * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv)
160 if (inputclock
< 50000000) {
162 * For slower clocks we need to multiply more to keep the
163 * internal VCO frequency within the physical specification
175 val
= readl(hdmi
->syscfg
+ HDMI_REJECTION_PLL_CONFIGURATION
);
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
);
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
);
187 writel(val
, hdmi
->syscfg
+ HDMI_REJECTION_PLL_CONFIGURATION
);
189 msleep(PLL_CHANGE_DELAY
);
190 val
= readl(hdmi
->syscfg
+ HDMI_REJECTION_PLL_STATUS
);
192 return (val
& REJECTION_PLL_HDMI_REJ_PLL_LOCK
);
196 * Start hdmi phy macro cell tx3g0c55
198 * @hdmi: pointer on the hdmi internal structure
200 * Return false if an error occur
202 static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi
*hdmi
)
204 u32 ckpxpll
= hdmi
->mode
.clock
* 1000;
205 u32 val
, tmdsck
, freqvco
, pllctrl
= 0;
208 if (!enable_pll_rejection(hdmi
))
211 DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll
);
213 /* Assuming no pixel repetition and 24bits color */
215 pllctrl
= 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT
;
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.
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
);
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
);
238 DRM_ERROR("PHY serializer clock out of range\n");
243 * Configure and power up the PHY PLL
245 hdmi
->event_received
= false;
246 DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl
);
247 hdmi_write(hdmi
, pllctrl
, HDMI_SRZ_PLL_CFG
);
249 /* wait PLL interrupt */
250 wait_event_interruptible_timeout(hdmi
->wait_event
,
251 hdmi
->event_received
== true,
253 (HDMI_TIMEOUT_PLL_LOCK
));
255 if ((hdmi_read(hdmi
, HDMI_STA
) & HDMI_STA_DLL_LCK
) == 0) {
256 DRM_ERROR("hdmi phy pll not locked\n");
260 DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
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.
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
);
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]);
291 * Default, power up the serializer with no pre-emphasis or source
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
);
302 disable_pll_rejection(hdmi
);
308 * Stop hdmi phy macro cell tx3g0c55
310 * @hdmi: pointer on the hdmi internal structure
312 static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi
*hdmi
)
314 DRM_DEBUG_DRIVER("\n");
316 hdmi
->event_received
= false;
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
);
321 /* wait PLL interrupt */
322 wait_event_interruptible_timeout(hdmi
->wait_event
,
323 hdmi
->event_received
== true,
325 (HDMI_TIMEOUT_PLL_LOCK
));
327 if (hdmi_read(hdmi
, HDMI_STA
) & HDMI_STA_DLL_LCK
)
328 DRM_ERROR("hdmi phy pll not well disabled\n");
330 disable_pll_rejection(hdmi
);
333 struct hdmi_phy_ops tx3g0c55phy_ops
= {
334 .start
= sti_hdmi_tx3g0c55phy_start
,
335 .stop
= sti_hdmi_tx3g0c55phy_stop
,