Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
e17afdce AT |
2 | /* |
3 | * Copyright (c) 2016, The Linux Foundation. All rights reserved. | |
e17afdce AT |
4 | */ |
5 | ||
6 | #include <linux/clk-provider.h> | |
7 | ||
8 | #include "hdmi.h" | |
9 | ||
10 | #define HDMI_VCO_MAX_FREQ 12000000000UL | |
11 | #define HDMI_VCO_MIN_FREQ 8000000000UL | |
12 | ||
13 | #define HDMI_PCLK_MAX_FREQ 600000000 | |
14 | #define HDMI_PCLK_MIN_FREQ 25000000 | |
15 | ||
16 | #define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL | |
17 | #define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL | |
18 | #define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL | |
19 | #define HDMI_CORECLK_DIV 5 | |
20 | #define HDMI_DEFAULT_REF_CLOCK 19200000 | |
21 | #define HDMI_PLL_CMP_CNT 1024 | |
22 | ||
23 | #define HDMI_PLL_POLL_MAX_READS 100 | |
24 | #define HDMI_PLL_POLL_TIMEOUT_US 150 | |
25 | ||
26 | #define HDMI_NUM_TX_CHANNEL 4 | |
27 | ||
28 | struct hdmi_pll_8996 { | |
29 | struct platform_device *pdev; | |
30 | struct clk_hw clk_hw; | |
31 | ||
32 | /* pll mmio base */ | |
33 | void __iomem *mmio_qserdes_com; | |
34 | /* tx channel base */ | |
35 | void __iomem *mmio_qserdes_tx[HDMI_NUM_TX_CHANNEL]; | |
36 | }; | |
37 | ||
38 | #define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8996, clk_hw) | |
39 | ||
40 | struct hdmi_8996_phy_pll_reg_cfg { | |
41 | u32 tx_lx_lane_mode[HDMI_NUM_TX_CHANNEL]; | |
42 | u32 tx_lx_tx_band[HDMI_NUM_TX_CHANNEL]; | |
43 | u32 com_svs_mode_clk_sel; | |
44 | u32 com_hsclk_sel; | |
45 | u32 com_pll_cctrl_mode0; | |
46 | u32 com_pll_rctrl_mode0; | |
47 | u32 com_cp_ctrl_mode0; | |
48 | u32 com_dec_start_mode0; | |
49 | u32 com_div_frac_start1_mode0; | |
50 | u32 com_div_frac_start2_mode0; | |
51 | u32 com_div_frac_start3_mode0; | |
52 | u32 com_integloop_gain0_mode0; | |
53 | u32 com_integloop_gain1_mode0; | |
54 | u32 com_lock_cmp_en; | |
55 | u32 com_lock_cmp1_mode0; | |
56 | u32 com_lock_cmp2_mode0; | |
57 | u32 com_lock_cmp3_mode0; | |
58 | u32 com_core_clk_en; | |
59 | u32 com_coreclk_div; | |
60 | u32 com_vco_tune_ctrl; | |
61 | ||
62 | u32 tx_lx_tx_drv_lvl[HDMI_NUM_TX_CHANNEL]; | |
63 | u32 tx_lx_tx_emp_post1_lvl[HDMI_NUM_TX_CHANNEL]; | |
64 | u32 tx_lx_vmode_ctrl1[HDMI_NUM_TX_CHANNEL]; | |
65 | u32 tx_lx_vmode_ctrl2[HDMI_NUM_TX_CHANNEL]; | |
66 | u32 tx_lx_res_code_lane_tx[HDMI_NUM_TX_CHANNEL]; | |
67 | u32 tx_lx_hp_pd_enables[HDMI_NUM_TX_CHANNEL]; | |
68 | ||
69 | u32 phy_mode; | |
70 | }; | |
71 | ||
72 | struct hdmi_8996_post_divider { | |
73 | u64 vco_freq; | |
74 | int hsclk_divsel; | |
75 | int vco_ratio; | |
76 | int tx_band_sel; | |
77 | int half_rate_mode; | |
78 | }; | |
79 | ||
80 | static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8996 *pll) | |
81 | { | |
82 | return platform_get_drvdata(pll->pdev); | |
83 | } | |
84 | ||
85 | static inline void hdmi_pll_write(struct hdmi_pll_8996 *pll, int offset, | |
86 | u32 data) | |
87 | { | |
88 | msm_writel(data, pll->mmio_qserdes_com + offset); | |
89 | } | |
90 | ||
91 | static inline u32 hdmi_pll_read(struct hdmi_pll_8996 *pll, int offset) | |
92 | { | |
93 | return msm_readl(pll->mmio_qserdes_com + offset); | |
94 | } | |
95 | ||
96 | static inline void hdmi_tx_chan_write(struct hdmi_pll_8996 *pll, int channel, | |
97 | int offset, int data) | |
98 | { | |
99 | msm_writel(data, pll->mmio_qserdes_tx[channel] + offset); | |
100 | } | |
101 | ||
102 | static inline u32 pll_get_cpctrl(u64 frac_start, unsigned long ref_clk, | |
103 | bool gen_ssc) | |
104 | { | |
105 | if ((frac_start != 0) || gen_ssc) | |
106 | return (11000000 / (ref_clk / 20)); | |
107 | ||
108 | return 0x23; | |
109 | } | |
110 | ||
111 | static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc) | |
112 | { | |
113 | if ((frac_start != 0) || gen_ssc) | |
114 | return 0x16; | |
115 | ||
116 | return 0x10; | |
117 | } | |
118 | ||
119 | static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc) | |
120 | { | |
121 | if ((frac_start != 0) || gen_ssc) | |
122 | return 0x28; | |
123 | ||
124 | return 0x1; | |
125 | } | |
126 | ||
127 | static inline u32 pll_get_integloop_gain(u64 frac_start, u64 bclk, u32 ref_clk, | |
128 | bool gen_ssc) | |
129 | { | |
130 | int digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2; | |
131 | u64 base; | |
132 | ||
133 | if ((frac_start != 0) || gen_ssc) | |
134 | base = (64 * ref_clk) / HDMI_DEFAULT_REF_CLOCK; | |
135 | else | |
136 | base = (1022 * ref_clk) / 100; | |
137 | ||
138 | base <<= digclk_divsel; | |
139 | ||
140 | return (base <= 2046 ? base : 2046); | |
141 | } | |
142 | ||
143 | static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk) | |
144 | { | |
145 | u64 dividend = HDMI_PLL_CMP_CNT * fdata; | |
146 | u32 divisor = ref_clk * 10; | |
147 | u32 rem; | |
148 | ||
149 | rem = do_div(dividend, divisor); | |
150 | if (rem > (divisor >> 1)) | |
151 | dividend++; | |
152 | ||
153 | return dividend - 1; | |
154 | } | |
155 | ||
156 | static inline u64 pll_cmp_to_fdata(u32 pll_cmp, unsigned long ref_clk) | |
157 | { | |
158 | u64 fdata = ((u64)pll_cmp) * ref_clk * 10; | |
159 | ||
160 | do_div(fdata, HDMI_PLL_CMP_CNT); | |
161 | ||
162 | return fdata; | |
163 | } | |
164 | ||
165 | static int pll_get_post_div(struct hdmi_8996_post_divider *pd, u64 bclk) | |
166 | { | |
167 | int ratio[] = { 2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35 }; | |
168 | int hs_divsel[] = { 0, 4, 8, 12, 1, 5, 2, 9, 3, 13, 10, 7, 14, 11, 15 }; | |
169 | int tx_band_sel[] = { 0, 1, 2, 3 }; | |
170 | u64 vco_freq[60]; | |
171 | u64 vco, vco_optimal; | |
172 | int half_rate_mode = 0; | |
173 | int vco_optimal_index, vco_freq_index; | |
174 | int i, j; | |
175 | ||
176 | retry: | |
177 | vco_optimal = HDMI_VCO_MAX_FREQ; | |
178 | vco_optimal_index = -1; | |
179 | vco_freq_index = 0; | |
180 | for (i = 0; i < 15; i++) { | |
181 | for (j = 0; j < 4; j++) { | |
182 | u32 ratio_mult = ratio[i] << tx_band_sel[j]; | |
183 | ||
184 | vco = bclk >> half_rate_mode; | |
185 | vco *= ratio_mult; | |
186 | vco_freq[vco_freq_index++] = vco; | |
187 | } | |
188 | } | |
189 | ||
190 | for (i = 0; i < 60; i++) { | |
191 | u64 vco_tmp = vco_freq[i]; | |
192 | ||
193 | if ((vco_tmp >= HDMI_VCO_MIN_FREQ) && | |
194 | (vco_tmp <= vco_optimal)) { | |
195 | vco_optimal = vco_tmp; | |
196 | vco_optimal_index = i; | |
197 | } | |
198 | } | |
199 | ||
200 | if (vco_optimal_index == -1) { | |
201 | if (!half_rate_mode) { | |
202 | half_rate_mode = 1; | |
203 | goto retry; | |
204 | } | |
205 | } else { | |
206 | pd->vco_freq = vco_optimal; | |
207 | pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4]; | |
208 | pd->vco_ratio = ratio[vco_optimal_index / 4]; | |
209 | pd->hsclk_divsel = hs_divsel[vco_optimal_index / 4]; | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
214 | return -EINVAL; | |
215 | } | |
216 | ||
217 | static int pll_calculate(unsigned long pix_clk, unsigned long ref_clk, | |
218 | struct hdmi_8996_phy_pll_reg_cfg *cfg) | |
219 | { | |
220 | struct hdmi_8996_post_divider pd; | |
221 | u64 bclk; | |
222 | u64 tmds_clk; | |
223 | u64 dec_start; | |
224 | u64 frac_start; | |
225 | u64 fdata; | |
226 | u32 pll_divisor; | |
227 | u32 rem; | |
228 | u32 cpctrl; | |
229 | u32 rctrl; | |
230 | u32 cctrl; | |
231 | u32 integloop_gain; | |
232 | u32 pll_cmp; | |
233 | int i, ret; | |
234 | ||
235 | /* bit clk = 10 * pix_clk */ | |
236 | bclk = ((u64)pix_clk) * 10; | |
237 | ||
238 | if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) | |
239 | tmds_clk = pix_clk >> 2; | |
240 | else | |
241 | tmds_clk = pix_clk; | |
242 | ||
243 | ret = pll_get_post_div(&pd, bclk); | |
244 | if (ret) | |
245 | return ret; | |
246 | ||
247 | dec_start = pd.vco_freq; | |
248 | pll_divisor = 4 * ref_clk; | |
249 | do_div(dec_start, pll_divisor); | |
250 | ||
251 | frac_start = pd.vco_freq * (1 << 20); | |
252 | ||
253 | rem = do_div(frac_start, pll_divisor); | |
254 | frac_start -= dec_start * (1 << 20); | |
255 | if (rem > (pll_divisor >> 1)) | |
256 | frac_start++; | |
257 | ||
258 | cpctrl = pll_get_cpctrl(frac_start, ref_clk, false); | |
259 | rctrl = pll_get_rctrl(frac_start, false); | |
260 | cctrl = pll_get_cctrl(frac_start, false); | |
261 | integloop_gain = pll_get_integloop_gain(frac_start, bclk, | |
262 | ref_clk, false); | |
263 | ||
264 | fdata = pd.vco_freq; | |
265 | do_div(fdata, pd.vco_ratio); | |
266 | ||
267 | pll_cmp = pll_get_pll_cmp(fdata, ref_clk); | |
268 | ||
269 | DBG("VCO freq: %llu", pd.vco_freq); | |
270 | DBG("fdata: %llu", fdata); | |
271 | DBG("pix_clk: %lu", pix_clk); | |
272 | DBG("tmds clk: %llu", tmds_clk); | |
273 | DBG("HSCLK_SEL: %d", pd.hsclk_divsel); | |
274 | DBG("DEC_START: %llu", dec_start); | |
275 | DBG("DIV_FRAC_START: %llu", frac_start); | |
276 | DBG("PLL_CPCTRL: %u", cpctrl); | |
277 | DBG("PLL_RCTRL: %u", rctrl); | |
278 | DBG("PLL_CCTRL: %u", cctrl); | |
279 | DBG("INTEGLOOP_GAIN: %u", integloop_gain); | |
280 | DBG("TX_BAND: %d", pd.tx_band_sel); | |
281 | DBG("PLL_CMP: %u", pll_cmp); | |
282 | ||
283 | /* Convert these values to register specific values */ | |
284 | if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) | |
285 | cfg->com_svs_mode_clk_sel = 1; | |
286 | else | |
287 | cfg->com_svs_mode_clk_sel = 2; | |
288 | ||
289 | cfg->com_hsclk_sel = (0x20 | pd.hsclk_divsel); | |
290 | cfg->com_pll_cctrl_mode0 = cctrl; | |
291 | cfg->com_pll_rctrl_mode0 = rctrl; | |
292 | cfg->com_cp_ctrl_mode0 = cpctrl; | |
293 | cfg->com_dec_start_mode0 = dec_start; | |
294 | cfg->com_div_frac_start1_mode0 = (frac_start & 0xff); | |
295 | cfg->com_div_frac_start2_mode0 = ((frac_start & 0xff00) >> 8); | |
296 | cfg->com_div_frac_start3_mode0 = ((frac_start & 0xf0000) >> 16); | |
297 | cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xff); | |
298 | cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xf00) >> 8); | |
299 | cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xff); | |
300 | cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xff00) >> 8); | |
301 | cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); | |
302 | cfg->com_lock_cmp_en = 0x0; | |
303 | cfg->com_core_clk_en = 0x2c; | |
304 | cfg->com_coreclk_div = HDMI_CORECLK_DIV; | |
305 | cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; | |
306 | cfg->com_vco_tune_ctrl = 0x0; | |
307 | ||
308 | cfg->tx_lx_lane_mode[0] = | |
309 | cfg->tx_lx_lane_mode[2] = 0x43; | |
310 | ||
311 | cfg->tx_lx_hp_pd_enables[0] = | |
312 | cfg->tx_lx_hp_pd_enables[1] = | |
313 | cfg->tx_lx_hp_pd_enables[2] = 0x0c; | |
314 | cfg->tx_lx_hp_pd_enables[3] = 0x3; | |
315 | ||
316 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) | |
317 | cfg->tx_lx_tx_band[i] = pd.tx_band_sel + 4; | |
318 | ||
319 | if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { | |
320 | cfg->tx_lx_tx_drv_lvl[0] = | |
321 | cfg->tx_lx_tx_drv_lvl[1] = | |
322 | cfg->tx_lx_tx_drv_lvl[2] = 0x25; | |
323 | cfg->tx_lx_tx_drv_lvl[3] = 0x22; | |
324 | ||
325 | cfg->tx_lx_tx_emp_post1_lvl[0] = | |
326 | cfg->tx_lx_tx_emp_post1_lvl[1] = | |
327 | cfg->tx_lx_tx_emp_post1_lvl[2] = 0x23; | |
328 | cfg->tx_lx_tx_emp_post1_lvl[3] = 0x27; | |
329 | ||
330 | cfg->tx_lx_vmode_ctrl1[0] = | |
331 | cfg->tx_lx_vmode_ctrl1[1] = | |
332 | cfg->tx_lx_vmode_ctrl1[2] = | |
333 | cfg->tx_lx_vmode_ctrl1[3] = 0x00; | |
334 | ||
335 | cfg->tx_lx_vmode_ctrl2[0] = | |
336 | cfg->tx_lx_vmode_ctrl2[1] = | |
337 | cfg->tx_lx_vmode_ctrl2[2] = 0x0D; | |
338 | ||
339 | cfg->tx_lx_vmode_ctrl2[3] = 0x00; | |
340 | } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { | |
341 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { | |
342 | cfg->tx_lx_tx_drv_lvl[i] = 0x25; | |
343 | cfg->tx_lx_tx_emp_post1_lvl[i] = 0x23; | |
344 | cfg->tx_lx_vmode_ctrl1[i] = 0x00; | |
345 | } | |
346 | ||
347 | cfg->tx_lx_vmode_ctrl2[0] = | |
348 | cfg->tx_lx_vmode_ctrl2[1] = | |
349 | cfg->tx_lx_vmode_ctrl2[2] = 0x0D; | |
350 | cfg->tx_lx_vmode_ctrl2[3] = 0x00; | |
351 | } else { | |
352 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { | |
353 | cfg->tx_lx_tx_drv_lvl[i] = 0x20; | |
354 | cfg->tx_lx_tx_emp_post1_lvl[i] = 0x20; | |
355 | cfg->tx_lx_vmode_ctrl1[i] = 0x00; | |
356 | cfg->tx_lx_vmode_ctrl2[i] = 0x0E; | |
357 | } | |
358 | } | |
359 | ||
360 | DBG("com_svs_mode_clk_sel = 0x%x", cfg->com_svs_mode_clk_sel); | |
361 | DBG("com_hsclk_sel = 0x%x", cfg->com_hsclk_sel); | |
362 | DBG("com_lock_cmp_en = 0x%x", cfg->com_lock_cmp_en); | |
363 | DBG("com_pll_cctrl_mode0 = 0x%x", cfg->com_pll_cctrl_mode0); | |
364 | DBG("com_pll_rctrl_mode0 = 0x%x", cfg->com_pll_rctrl_mode0); | |
365 | DBG("com_cp_ctrl_mode0 = 0x%x", cfg->com_cp_ctrl_mode0); | |
366 | DBG("com_dec_start_mode0 = 0x%x", cfg->com_dec_start_mode0); | |
367 | DBG("com_div_frac_start1_mode0 = 0x%x", cfg->com_div_frac_start1_mode0); | |
368 | DBG("com_div_frac_start2_mode0 = 0x%x", cfg->com_div_frac_start2_mode0); | |
369 | DBG("com_div_frac_start3_mode0 = 0x%x", cfg->com_div_frac_start3_mode0); | |
370 | DBG("com_integloop_gain0_mode0 = 0x%x", cfg->com_integloop_gain0_mode0); | |
371 | DBG("com_integloop_gain1_mode0 = 0x%x", cfg->com_integloop_gain1_mode0); | |
372 | DBG("com_lock_cmp1_mode0 = 0x%x", cfg->com_lock_cmp1_mode0); | |
373 | DBG("com_lock_cmp2_mode0 = 0x%x", cfg->com_lock_cmp2_mode0); | |
374 | DBG("com_lock_cmp3_mode0 = 0x%x", cfg->com_lock_cmp3_mode0); | |
375 | DBG("com_core_clk_en = 0x%x", cfg->com_core_clk_en); | |
376 | DBG("com_coreclk_div = 0x%x", cfg->com_coreclk_div); | |
377 | DBG("phy_mode = 0x%x", cfg->phy_mode); | |
378 | ||
379 | DBG("tx_l0_lane_mode = 0x%x", cfg->tx_lx_lane_mode[0]); | |
380 | DBG("tx_l2_lane_mode = 0x%x", cfg->tx_lx_lane_mode[2]); | |
381 | ||
382 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { | |
383 | DBG("tx_l%d_tx_band = 0x%x", i, cfg->tx_lx_tx_band[i]); | |
384 | DBG("tx_l%d_tx_drv_lvl = 0x%x", i, cfg->tx_lx_tx_drv_lvl[i]); | |
385 | DBG("tx_l%d_tx_emp_post1_lvl = 0x%x", i, | |
386 | cfg->tx_lx_tx_emp_post1_lvl[i]); | |
387 | DBG("tx_l%d_vmode_ctrl1 = 0x%x", i, cfg->tx_lx_vmode_ctrl1[i]); | |
388 | DBG("tx_l%d_vmode_ctrl2 = 0x%x", i, cfg->tx_lx_vmode_ctrl2[i]); | |
389 | } | |
390 | ||
391 | return 0; | |
392 | } | |
393 | ||
394 | static int hdmi_8996_pll_set_clk_rate(struct clk_hw *hw, unsigned long rate, | |
395 | unsigned long parent_rate) | |
396 | { | |
397 | struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); | |
398 | struct hdmi_phy *phy = pll_get_phy(pll); | |
399 | struct hdmi_8996_phy_pll_reg_cfg cfg; | |
400 | int i, ret; | |
401 | ||
402 | memset(&cfg, 0x00, sizeof(cfg)); | |
403 | ||
404 | ret = pll_calculate(rate, parent_rate, &cfg); | |
405 | if (ret) { | |
406 | DRM_ERROR("PLL calculation failed\n"); | |
407 | return ret; | |
408 | } | |
409 | ||
410 | /* Initially shut down PHY */ | |
411 | DBG("Disabling PHY"); | |
412 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x0); | |
413 | udelay(500); | |
414 | ||
415 | /* Power up sequence */ | |
416 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x04); | |
417 | ||
418 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1); | |
419 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESETSM_CNTRL, 0x20); | |
420 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX0_TX1_LANE_CTL, 0x0F); | |
421 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX2_TX3_LANE_CTL, 0x0F); | |
422 | ||
423 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { | |
424 | hdmi_tx_chan_write(pll, i, | |
425 | REG_HDMI_PHY_QSERDES_TX_LX_CLKBUF_ENABLE, | |
426 | 0x03); | |
427 | hdmi_tx_chan_write(pll, i, | |
428 | REG_HDMI_PHY_QSERDES_TX_LX_TX_BAND, | |
429 | cfg.tx_lx_tx_band[i]); | |
430 | hdmi_tx_chan_write(pll, i, | |
431 | REG_HDMI_PHY_QSERDES_TX_LX_RESET_TSYNC_EN, | |
432 | 0x03); | |
433 | } | |
434 | ||
435 | hdmi_tx_chan_write(pll, 0, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, | |
436 | cfg.tx_lx_lane_mode[0]); | |
437 | hdmi_tx_chan_write(pll, 2, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, | |
438 | cfg.tx_lx_lane_mode[2]); | |
439 | ||
440 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E); | |
441 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07); | |
442 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_EN_SEL, 0x37); | |
443 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYS_CLK_CTRL, 0x02); | |
444 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_ENABLE1, 0x0E); | |
445 | ||
446 | /* Bypass VCO calibration */ | |
447 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SVS_MODE_CLK_SEL, | |
448 | cfg.com_svs_mode_clk_sel); | |
449 | ||
450 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_TRIM, 0x0F); | |
451 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_IVCO, 0x0F); | |
452 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_CTRL, | |
453 | cfg.com_vco_tune_ctrl); | |
454 | ||
455 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x06); | |
456 | ||
457 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_SELECT, 0x30); | |
458 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_HSCLK_SEL, | |
459 | cfg.com_hsclk_sel); | |
460 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP_EN, | |
461 | cfg.com_lock_cmp_en); | |
462 | ||
463 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_CCTRL_MODE0, | |
464 | cfg.com_pll_cctrl_mode0); | |
465 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_RCTRL_MODE0, | |
466 | cfg.com_pll_rctrl_mode0); | |
467 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CP_CTRL_MODE0, | |
468 | cfg.com_cp_ctrl_mode0); | |
469 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DEC_START_MODE0, | |
470 | cfg.com_dec_start_mode0); | |
471 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START1_MODE0, | |
472 | cfg.com_div_frac_start1_mode0); | |
473 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START2_MODE0, | |
474 | cfg.com_div_frac_start2_mode0); | |
475 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START3_MODE0, | |
476 | cfg.com_div_frac_start3_mode0); | |
477 | ||
478 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE0, | |
479 | cfg.com_integloop_gain0_mode0); | |
480 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE0, | |
481 | cfg.com_integloop_gain1_mode0); | |
482 | ||
483 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0, | |
484 | cfg.com_lock_cmp1_mode0); | |
485 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0, | |
486 | cfg.com_lock_cmp2_mode0); | |
487 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0, | |
488 | cfg.com_lock_cmp3_mode0); | |
489 | ||
490 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MAP, 0x00); | |
491 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORE_CLK_EN, | |
492 | cfg.com_core_clk_en); | |
493 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORECLK_DIV, | |
494 | cfg.com_coreclk_div); | |
495 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CMN_CONFIG, 0x02); | |
496 | ||
497 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESCODE_DIV_NUM, 0x15); | |
498 | ||
499 | /* TX lanes setup (TX 0/1/2/3) */ | |
500 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { | |
501 | hdmi_tx_chan_write(pll, i, | |
502 | REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL, | |
503 | cfg.tx_lx_tx_drv_lvl[i]); | |
504 | hdmi_tx_chan_write(pll, i, | |
505 | REG_HDMI_PHY_QSERDES_TX_LX_TX_EMP_POST1_LVL, | |
506 | cfg.tx_lx_tx_emp_post1_lvl[i]); | |
507 | hdmi_tx_chan_write(pll, i, | |
508 | REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL1, | |
509 | cfg.tx_lx_vmode_ctrl1[i]); | |
510 | hdmi_tx_chan_write(pll, i, | |
511 | REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL2, | |
512 | cfg.tx_lx_vmode_ctrl2[i]); | |
513 | hdmi_tx_chan_write(pll, i, | |
514 | REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL_OFFSET, | |
515 | 0x00); | |
516 | hdmi_tx_chan_write(pll, i, | |
517 | REG_HDMI_PHY_QSERDES_TX_LX_RES_CODE_LANE_OFFSET, | |
518 | 0x00); | |
519 | hdmi_tx_chan_write(pll, i, | |
520 | REG_HDMI_PHY_QSERDES_TX_LX_TRAN_DRVR_EMP_EN, | |
521 | 0x03); | |
522 | hdmi_tx_chan_write(pll, i, | |
523 | REG_HDMI_PHY_QSERDES_TX_LX_PARRATE_REC_DETECT_IDLE_EN, | |
524 | 0x40); | |
525 | hdmi_tx_chan_write(pll, i, | |
526 | REG_HDMI_PHY_QSERDES_TX_LX_HP_PD_ENABLES, | |
527 | cfg.tx_lx_hp_pd_enables[i]); | |
528 | } | |
529 | ||
530 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_MODE, cfg.phy_mode); | |
531 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1F); | |
532 | ||
533 | /* | |
534 | * Ensure that vco configuration gets flushed to hardware before | |
535 | * enabling the PLL | |
536 | */ | |
537 | wmb(); | |
538 | ||
539 | return 0; | |
540 | } | |
541 | ||
542 | static int hdmi_8996_phy_ready_status(struct hdmi_phy *phy) | |
543 | { | |
544 | u32 nb_tries = HDMI_PLL_POLL_MAX_READS; | |
545 | unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US; | |
546 | u32 status; | |
547 | int phy_ready = 0; | |
548 | ||
549 | DBG("Waiting for PHY ready"); | |
550 | ||
551 | while (nb_tries--) { | |
552 | status = hdmi_phy_read(phy, REG_HDMI_8996_PHY_STATUS); | |
553 | phy_ready = status & BIT(0); | |
554 | ||
555 | if (phy_ready) | |
556 | break; | |
557 | ||
558 | udelay(timeout); | |
559 | } | |
560 | ||
561 | DBG("PHY is %sready", phy_ready ? "" : "*not* "); | |
562 | ||
563 | return phy_ready; | |
564 | } | |
565 | ||
566 | static int hdmi_8996_pll_lock_status(struct hdmi_pll_8996 *pll) | |
567 | { | |
568 | u32 status; | |
569 | int nb_tries = HDMI_PLL_POLL_MAX_READS; | |
570 | unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US; | |
571 | int pll_locked = 0; | |
572 | ||
573 | DBG("Waiting for PLL lock"); | |
574 | ||
575 | while (nb_tries--) { | |
576 | status = hdmi_pll_read(pll, | |
577 | REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); | |
578 | pll_locked = status & BIT(0); | |
579 | ||
580 | if (pll_locked) | |
581 | break; | |
582 | ||
583 | udelay(timeout); | |
584 | } | |
585 | ||
586 | DBG("HDMI PLL is %slocked", pll_locked ? "" : "*not* "); | |
587 | ||
588 | return pll_locked; | |
589 | } | |
590 | ||
591 | static int hdmi_8996_pll_prepare(struct clk_hw *hw) | |
592 | { | |
593 | struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); | |
594 | struct hdmi_phy *phy = pll_get_phy(pll); | |
595 | int i, ret = 0; | |
596 | ||
597 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x1); | |
598 | udelay(100); | |
599 | ||
600 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); | |
601 | udelay(100); | |
602 | ||
603 | ret = hdmi_8996_pll_lock_status(pll); | |
604 | if (!ret) | |
605 | return ret; | |
606 | ||
607 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) | |
608 | hdmi_tx_chan_write(pll, i, | |
609 | REG_HDMI_PHY_QSERDES_TX_LX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, | |
610 | 0x6F); | |
611 | ||
612 | /* Disable SSC */ | |
613 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER1, 0x0); | |
614 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER2, 0x0); | |
615 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE1, 0x0); | |
616 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE2, 0x0); | |
617 | hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_EN_CENTER, 0x2); | |
618 | ||
619 | ret = hdmi_8996_phy_ready_status(phy); | |
620 | if (!ret) | |
621 | return ret; | |
622 | ||
623 | /* Restart the retiming buffer */ | |
624 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x18); | |
625 | udelay(1); | |
626 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); | |
627 | ||
628 | return 0; | |
629 | } | |
630 | ||
631 | static long hdmi_8996_pll_round_rate(struct clk_hw *hw, | |
632 | unsigned long rate, | |
633 | unsigned long *parent_rate) | |
634 | { | |
635 | if (rate < HDMI_PCLK_MIN_FREQ) | |
636 | return HDMI_PCLK_MIN_FREQ; | |
637 | else if (rate > HDMI_PCLK_MAX_FREQ) | |
638 | return HDMI_PCLK_MAX_FREQ; | |
639 | else | |
640 | return rate; | |
641 | } | |
642 | ||
643 | static unsigned long hdmi_8996_pll_recalc_rate(struct clk_hw *hw, | |
644 | unsigned long parent_rate) | |
645 | { | |
646 | struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); | |
647 | u64 fdata; | |
648 | u32 cmp1, cmp2, cmp3, pll_cmp; | |
649 | ||
650 | cmp1 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0); | |
651 | cmp2 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0); | |
652 | cmp3 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0); | |
653 | ||
654 | pll_cmp = cmp1 | (cmp2 << 8) | (cmp3 << 16); | |
655 | ||
656 | fdata = pll_cmp_to_fdata(pll_cmp + 1, parent_rate); | |
657 | ||
658 | do_div(fdata, 10); | |
659 | ||
660 | return fdata; | |
661 | } | |
662 | ||
663 | static void hdmi_8996_pll_unprepare(struct clk_hw *hw) | |
664 | { | |
b474cbbb AT |
665 | struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); |
666 | struct hdmi_phy *phy = pll_get_phy(pll); | |
667 | ||
668 | hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x6); | |
669 | usleep_range(100, 150); | |
e17afdce AT |
670 | } |
671 | ||
672 | static int hdmi_8996_pll_is_enabled(struct clk_hw *hw) | |
673 | { | |
674 | struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); | |
675 | u32 status; | |
676 | int pll_locked; | |
677 | ||
678 | status = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); | |
679 | pll_locked = status & BIT(0); | |
680 | ||
681 | return pll_locked; | |
682 | } | |
683 | ||
684 | static struct clk_ops hdmi_8996_pll_ops = { | |
685 | .set_rate = hdmi_8996_pll_set_clk_rate, | |
686 | .round_rate = hdmi_8996_pll_round_rate, | |
687 | .recalc_rate = hdmi_8996_pll_recalc_rate, | |
688 | .prepare = hdmi_8996_pll_prepare, | |
689 | .unprepare = hdmi_8996_pll_unprepare, | |
690 | .is_enabled = hdmi_8996_pll_is_enabled, | |
691 | }; | |
692 | ||
693 | static const char * const hdmi_pll_parents[] = { | |
694 | "xo", | |
695 | }; | |
696 | ||
697 | static struct clk_init_data pll_init = { | |
698 | .name = "hdmipll", | |
699 | .ops = &hdmi_8996_pll_ops, | |
700 | .parent_names = hdmi_pll_parents, | |
701 | .num_parents = ARRAY_SIZE(hdmi_pll_parents), | |
73b65b19 | 702 | .flags = CLK_IGNORE_UNUSED, |
e17afdce AT |
703 | }; |
704 | ||
fcda50c8 | 705 | int msm_hdmi_pll_8996_init(struct platform_device *pdev) |
e17afdce AT |
706 | { |
707 | struct device *dev = &pdev->dev; | |
708 | struct hdmi_pll_8996 *pll; | |
709 | struct clk *clk; | |
710 | int i; | |
711 | ||
712 | pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); | |
713 | if (!pll) | |
714 | return -ENOMEM; | |
715 | ||
716 | pll->pdev = pdev; | |
717 | ||
718 | pll->mmio_qserdes_com = msm_ioremap(pdev, "hdmi_pll", "HDMI_PLL"); | |
719 | if (IS_ERR(pll->mmio_qserdes_com)) { | |
6a41da17 | 720 | DRM_DEV_ERROR(dev, "failed to map pll base\n"); |
e17afdce AT |
721 | return -ENOMEM; |
722 | } | |
723 | ||
724 | for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { | |
725 | char name[32], label[32]; | |
726 | ||
727 | snprintf(name, sizeof(name), "hdmi_tx_l%d", i); | |
728 | snprintf(label, sizeof(label), "HDMI_TX_L%d", i); | |
729 | ||
730 | pll->mmio_qserdes_tx[i] = msm_ioremap(pdev, name, label); | |
731 | if (IS_ERR(pll->mmio_qserdes_tx[i])) { | |
6a41da17 | 732 | DRM_DEV_ERROR(dev, "failed to map pll base\n"); |
e17afdce AT |
733 | return -ENOMEM; |
734 | } | |
735 | } | |
736 | pll->clk_hw.init = &pll_init; | |
737 | ||
738 | clk = devm_clk_register(dev, &pll->clk_hw); | |
739 | if (IS_ERR(clk)) { | |
6a41da17 | 740 | DRM_DEV_ERROR(dev, "failed to register pll clock\n"); |
e17afdce AT |
741 | return -EINVAL; |
742 | } | |
743 | ||
744 | return 0; | |
745 | } | |
746 | ||
747 | static const char * const hdmi_phy_8996_reg_names[] = { | |
748 | "vddio", | |
749 | "vcca", | |
750 | }; | |
751 | ||
752 | static const char * const hdmi_phy_8996_clk_names[] = { | |
aede1e9e | 753 | "iface", "ref", |
e17afdce AT |
754 | }; |
755 | ||
fcda50c8 | 756 | const struct hdmi_phy_cfg msm_hdmi_phy_8996_cfg = { |
e17afdce AT |
757 | .type = MSM_HDMI_PHY_8996, |
758 | .reg_names = hdmi_phy_8996_reg_names, | |
759 | .num_regs = ARRAY_SIZE(hdmi_phy_8996_reg_names), | |
760 | .clk_names = hdmi_phy_8996_clk_names, | |
761 | .num_clks = ARRAY_SIZE(hdmi_phy_8996_clk_names), | |
762 | }; |