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