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