File Index
Files and directories
Sign in
FILE

intel_snps_hdmi_pll.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c C
  1// SPDX-License-Identifier: MIT
  2/*
  3 * Copyright © 2025 Synopsys, Inc., Intel Corporation
  4 */
  5
  6#include <linux/math.h>
  7
  8#include "intel_cx0_phy_regs.h"
  9#include "intel_display_types.h"
 10#include "intel_snps_phy.h"
 11#include "intel_snps_phy_regs.h"
 12#include "intel_snps_hdmi_pll.h"
 13
 14#define INTEL_SNPS_PHY_HDMI_4999MHZ 4999999900ULL
 15#define INTEL_SNPS_PHY_HDMI_16GHZ 16000000000ULL
 16#define INTEL_SNPS_PHY_HDMI_9999MHZ (2 * INTEL_SNPS_PHY_HDMI_4999MHZ)
 17
 18#define CURVE0_MULTIPLIER 1000000000
 19#define CURVE1_MULTIPLIER 100
 20#define CURVE2_MULTIPLIER 1000000000000ULL
 21
 22struct pll_output_params {
 23	u32 ssc_up_spread;
 24	u32 mpll_div5_en;
 25	u32 hdmi_div;
 26	u32 ana_cp_int;
 27	u32 ana_cp_prop;
 28	u32 refclk_postscalar;
 29	u32 tx_clk_div;
 30	u32 fracn_quot;
 31	u32 fracn_rem;
 32	u32 fracn_den;
 33	u32 fracn_en;
 34	u32 pmix_en;
 35	u32 multiplier;
 36	int mpll_ana_v2i;
 37	int ana_freq_vco;
 38};
 39
 40static s64 interp(s64 x, s64 x1, s64 x2, s64 y1, s64 y2)
 41{
 42	s64 dydx;
 43
 44	dydx = DIV64_U64_ROUND_UP((y2 - y1) * 100000, (x2 - x1));
 45
 46	return (y1 + DIV64_U64_ROUND_UP(dydx * (x - x1), 100000));
 47}
 48
 49static void get_ana_cp_int_prop(u64 vco_clk,
 50				u32 refclk_postscalar,
 51				int mpll_ana_v2i,
 52				int c, int a,
 53				const u64 curve_freq_hz[2][8],
 54				const u64 curve_0[2][8],
 55				const u64 curve_1[2][8],
 56				const u64 curve_2[2][8],
 57				u32 *ana_cp_int,
 58				u32 *ana_cp_prop)
 59{
 60	u64 vco_div_refclk_float;
 61	u64 curve_0_interpolated;
 62	u64 curve_2_interpolated;
 63	u64 curve_1_interpolated;
 64	u64 curve_2_scaled1;
 65	u64 curve_2_scaled2;
 66	u64 adjusted_vco_clk1;
 67	u64 adjusted_vco_clk2;
 68	u64 curve_2_scaled_int;
 69	u64 interpolated_product;
 70	u64 scaled_interpolated_sqrt;
 71	u64 scaled_vco_div_refclk1;
 72	u64 scaled_vco_div_refclk2;
 73	u64 ana_cp_int_temp;
 74	u64 temp;
 75
 76	vco_div_refclk_float = vco_clk * DIV_ROUND_DOWN_ULL(1000000000000ULL, refclk_postscalar);
 77
 78	/* Interpolate curve values at the target vco_clk frequency */
 79	curve_0_interpolated = interp(vco_clk, curve_freq_hz[c][a], curve_freq_hz[c][a + 1],
 80				      curve_0[c][a], curve_0[c][a + 1]);
 81
 82	curve_2_interpolated = interp(vco_clk, curve_freq_hz[c][a], curve_freq_hz[c][a + 1],
 83				      curve_2[c][a], curve_2[c][a + 1]);
 84
 85	curve_1_interpolated = interp(vco_clk, curve_freq_hz[c][a], curve_freq_hz[c][a + 1],
 86				      curve_1[c][a], curve_1[c][a + 1]);
 87
 88	curve_1_interpolated = DIV_ROUND_DOWN_ULL(curve_1_interpolated, CURVE1_MULTIPLIER);
 89
 90	/*
 91	 * Scale curve_2_interpolated based on mpll_ana_v2i, for integer part
 92	 * ana_cp_int and for the proportional part ana_cp_prop
 93	 */
 94	temp = curve_2_interpolated * (4 - mpll_ana_v2i);
 95	curve_2_scaled1 = DIV_ROUND_DOWN_ULL(temp, 16000);
 96	curve_2_scaled2 = DIV_ROUND_DOWN_ULL(temp, 160);
 97
 98	/* Scale vco_div_refclk for ana_cp_int */
 99	scaled_vco_div_refclk1 = 112008301 * DIV_ROUND_DOWN_ULL(vco_div_refclk_float, 100000);
100
101	adjusted_vco_clk1 = CURVE2_MULTIPLIER *
102			    DIV_ROUND_DOWN_ULL(scaled_vco_div_refclk1, (curve_0_interpolated *
103			    DIV_ROUND_DOWN_ULL(curve_1_interpolated, CURVE0_MULTIPLIER)));
104
105	ana_cp_int_temp =
106		DIV64_U64_ROUND_CLOSEST(DIV_ROUND_DOWN_ULL(adjusted_vco_clk1, curve_2_scaled1),
107					CURVE2_MULTIPLIER);
108
109	*ana_cp_int = clamp(ana_cp_int_temp, 1, 127);
110
111	curve_2_scaled_int = curve_2_scaled1 * (*ana_cp_int);
112
113	interpolated_product = curve_1_interpolated *
114			       (curve_2_scaled_int * DIV_ROUND_DOWN_ULL(curve_0_interpolated,
115								      CURVE0_MULTIPLIER));
116
117	scaled_interpolated_sqrt =
118			int_sqrt(DIV64_U64_ROUND_UP(interpolated_product, vco_div_refclk_float) *
119			DIV_ROUND_DOWN_ULL(1000000000000ULL, 55));
120
121	/* Scale vco_div_refclk for ana_cp_int */
122	scaled_vco_div_refclk2 = DIV_ROUND_UP_ULL(vco_div_refclk_float, 1000000);
123	adjusted_vco_clk2 = 1460281 * DIV64_U64_ROUND_UP(scaled_interpolated_sqrt *
124						       scaled_vco_div_refclk2,
125						       curve_1_interpolated);
126
127	*ana_cp_prop = DIV64_U64_ROUND_UP(adjusted_vco_clk2, curve_2_scaled2);
128	*ana_cp_prop = clamp(*ana_cp_prop, 1, 127);
129}
130
131static void compute_hdmi_tmds_pll(u64 pixel_clock, u32 refclk,
132				  u32 ref_range,
133				  u32 ana_cp_int_gs,
134				  u32 ana_cp_prop_gs,
135				  const u64 curve_freq_hz[2][8],
136				  const u64 curve_0[2][8],
137				  const u64 curve_1[2][8],
138				  const u64 curve_2[2][8],
139				  u32 prescaler_divider,
140				  struct pll_output_params *pll_params)
141{
142	u64 datarate = pixel_clock * 10000;
143	u32 ssc_up_spread = 1;
144	u32 mpll_div5_en = 1;
145	u32 hdmi_div = 1;
146	u32 ana_cp_int;
147	u32 ana_cp_prop;
148	u32 refclk_postscalar = refclk >> prescaler_divider;
149	u32 tx_clk_div;
150	u64 vco_clk;
151	u64 vco_clk_do_div;
152	u32 vco_div_refclk_integer;
153	u32 vco_div_refclk_fracn;
154	u32 fracn_quot;
155	u32 fracn_rem;
156	u32 fracn_den;
157	u32 fracn_en;
158	u32 pmix_en;
159	u32 multiplier;
160	int mpll_ana_v2i;
161	int ana_freq_vco = 0;
162	int c, a = 0;
163	int i;
164
165	/* Select appropriate v2i point */
166	if (datarate <= INTEL_SNPS_PHY_HDMI_9999MHZ) {
167		mpll_ana_v2i = 2;
168		tx_clk_div = ilog2(div64_u64(INTEL_SNPS_PHY_HDMI_9999MHZ, datarate));
169	} else {
170		mpll_ana_v2i = 3;
171		tx_clk_div = ilog2(div64_u64(INTEL_SNPS_PHY_HDMI_16GHZ, datarate));
172	}
173	vco_clk = (datarate << tx_clk_div) >> 1;
174
175	vco_div_refclk_integer = DIV_ROUND_DOWN_ULL(vco_clk, refclk_postscalar);
176	vco_clk_do_div = do_div(vco_clk, refclk_postscalar);
177	vco_div_refclk_fracn = DIV_ROUND_DOWN_ULL(vco_clk_do_div << 32, refclk_postscalar);
178
179	fracn_quot = vco_div_refclk_fracn >> 16;
180	fracn_rem = vco_div_refclk_fracn & 0xffff;
181	fracn_rem = fracn_rem - (fracn_rem >> 15);
182	fracn_den = 0xffff;
183	fracn_en = (fracn_quot != 0 || fracn_rem != 0) ? 1 : 0;
184	pmix_en = fracn_en;
185	multiplier = (vco_div_refclk_integer - 16) * 2;
186	/* Curve selection for ana_cp_* calculations. One curve hardcoded per v2i range */
187	c = mpll_ana_v2i - 2;
188
189	/* Find the right segment of the table */
190	for (i = 0; i < 8; i += 2) {
191		if (vco_clk <= curve_freq_hz[c][i + 1]) {
192			a = i;
193			ana_freq_vco = 3 - (a >> 1);
194			break;
195		}
196	}
197
198	get_ana_cp_int_prop(vco_clk, refclk_postscalar, mpll_ana_v2i, c, a,
199			    curve_freq_hz, curve_0, curve_1, curve_2,
200			    &ana_cp_int, &ana_cp_prop);
201
202	pll_params->ssc_up_spread = ssc_up_spread;
203	pll_params->mpll_div5_en = mpll_div5_en;
204	pll_params->hdmi_div = hdmi_div;
205	pll_params->ana_cp_int = ana_cp_int;
206	pll_params->refclk_postscalar = refclk_postscalar;
207	pll_params->tx_clk_div = tx_clk_div;
208	pll_params->fracn_quot = fracn_quot;
209	pll_params->fracn_rem = fracn_rem;
210	pll_params->fracn_den = fracn_den;
211	pll_params->fracn_en = fracn_en;
212	pll_params->pmix_en = pmix_en;
213	pll_params->multiplier = multiplier;
214	pll_params->ana_cp_prop = ana_cp_prop;
215	pll_params->mpll_ana_v2i = mpll_ana_v2i;
216	pll_params->ana_freq_vco = ana_freq_vco;
217}
218
219void intel_snps_hdmi_pll_compute_mpllb(struct intel_mpllb_state *pll_state, u64 pixel_clock)
220{
221	/* x axis frequencies. One curve in each array per v2i point */
222	static const u64 dg2_curve_freq_hz[2][8] = {
223		{ 2500000000ULL, 3000000000ULL, 3000000000ULL, 3500000000ULL, 3500000000ULL,
224		  4000000000ULL, 4000000000ULL, 5000000000ULL },
225		{ 4000000000ULL, 4600000000ULL, 4601000000ULL, 5400000000ULL, 5401000000ULL,
226		  6600000000ULL, 6601000000ULL, 8001000000ULL }
227	};
228
229	/* y axis heights multiplied with 1000000000 */
230	static const u64 dg2_curve_0[2][8] = {
231		{ 34149871, 39803269, 36034544, 40601014, 35646940, 40016109, 35127987, 41889522 },
232		{ 70000000, 78770454, 70451838, 80427119, 70991400, 84230173, 72945921, 87064218 }
233	};
234
235	/* Multiplied with 100 */
236	static const u64 dg2_curve_1[2][8] = {
237		{ 85177000000000ULL, 79385227160000ULL, 95672603580000ULL, 88857207160000ULL,
238		  109379790900000ULL, 103528193900000ULL, 131941242400000ULL, 117279000000000ULL },
239		{ 60255000000000ULL, 55569000000000ULL, 72036000000000ULL, 69509000000000ULL,
240		  81785000000000ULL, 731030000000000ULL, 96591000000000ULL, 69077000000000ULL }
241	};
242
243	/* Multiplied with 1000000000000 */
244	static const u64 dg2_curve_2[2][8] = {
245		{ 2186930000ULL, 2835287134ULL, 2395395343ULL, 2932270687ULL, 2351887545ULL,
246		  2861031697ULL, 2294149152ULL, 3091730000ULL },
247		{ 4560000000ULL, 5570000000ULL, 4610000000ULL, 5770000000ULL, 4670000000ULL,
248		  6240000000ULL, 4890000000ULL, 6600000000ULL }
249	};
250
251	struct pll_output_params pll_params;
252	u32 refclk = 100000000;
253	u32 prescaler_divider = 1;
254	u32 ref_range = 3;
255	u32 ana_cp_int_gs = 64;
256	u32 ana_cp_prop_gs = 124;
257
258	compute_hdmi_tmds_pll(pixel_clock, refclk, ref_range, ana_cp_int_gs, ana_cp_prop_gs,
259			      dg2_curve_freq_hz, dg2_curve_0, dg2_curve_1, dg2_curve_2,
260			      prescaler_divider, &pll_params);
261
262	pll_state->clock = pixel_clock;
263	pll_state->ref_control =
264		REG_FIELD_PREP(SNPS_PHY_REF_CONTROL_REF_RANGE, ref_range);
265	pll_state->mpllb_cp =
266		REG_FIELD_PREP(SNPS_PHY_MPLLB_CP_INT, pll_params.ana_cp_int) |
267		REG_FIELD_PREP(SNPS_PHY_MPLLB_CP_PROP, pll_params.ana_cp_prop) |
268		REG_FIELD_PREP(SNPS_PHY_MPLLB_CP_INT_GS, ana_cp_int_gs) |
269		REG_FIELD_PREP(SNPS_PHY_MPLLB_CP_PROP_GS, ana_cp_prop_gs);
270	pll_state->mpllb_div =
271		REG_FIELD_PREP(SNPS_PHY_MPLLB_DIV5_CLK_EN, pll_params.mpll_div5_en) |
272		REG_FIELD_PREP(SNPS_PHY_MPLLB_TX_CLK_DIV, pll_params.tx_clk_div) |
273		REG_FIELD_PREP(SNPS_PHY_MPLLB_PMIX_EN, pll_params.pmix_en) |
274		REG_FIELD_PREP(SNPS_PHY_MPLLB_V2I, pll_params.mpll_ana_v2i) |
275		REG_FIELD_PREP(SNPS_PHY_MPLLB_FREQ_VCO, pll_params.ana_freq_vco);
276	pll_state->mpllb_div2 =
277		REG_FIELD_PREP(SNPS_PHY_MPLLB_REF_CLK_DIV, prescaler_divider) |
278		REG_FIELD_PREP(SNPS_PHY_MPLLB_MULTIPLIER, pll_params.multiplier) |
279		REG_FIELD_PREP(SNPS_PHY_MPLLB_HDMI_DIV, pll_params.hdmi_div);
280	pll_state->mpllb_fracn1 =
281		REG_FIELD_PREP(SNPS_PHY_MPLLB_FRACN_CGG_UPDATE_EN, 1) |
282		REG_FIELD_PREP(SNPS_PHY_MPLLB_FRACN_EN, pll_params.fracn_en) |
283		REG_FIELD_PREP(SNPS_PHY_MPLLB_FRACN_DEN, pll_params.fracn_den);
284	pll_state->mpllb_fracn2 =
285		REG_FIELD_PREP(SNPS_PHY_MPLLB_FRACN_QUOT, pll_params.fracn_quot) |
286		REG_FIELD_PREP(SNPS_PHY_MPLLB_FRACN_REM, pll_params.fracn_rem);
287	pll_state->mpllb_sscen =
288		REG_FIELD_PREP(SNPS_PHY_MPLLB_SSC_UP_SPREAD, pll_params.ssc_up_spread);
289}
290
291void intel_snps_hdmi_pll_compute_c10pll(struct intel_c10pll_state *pll_state, u64 pixel_clock)
292{
293	/* x axis frequencies. One curve in each array per v2i point */
294	static const u64 c10_curve_freq_hz[2][8] = {
295		{ 2500000000ULL, 3000000000ULL, 3000000000ULL, 3500000000ULL, 3500000000ULL,
296		  4000000000ULL, 4000000000ULL, 5000000000ULL },
297		{ 4000000000ULL, 4600000000ULL, 4601000000ULL, 5400000000ULL, 5401000000ULL,
298		  6600000000ULL, 6601000000ULL, 8001000000ULL }
299	};
300
301	/* y axis heights multiplied with 1000000000 */
302	static const u64 c10_curve_0[2][8] = {
303		{ 41174500, 48605500, 42973700, 49433100, 42408600, 47681900, 40297400, 49131400 },
304		{ 82056800, 94420700, 82323400, 96370600, 81273300, 98630100, 81728700, 99105700}
305	};
306
307	static const u64 c10_curve_1[2][8] = {
308		{ 73300000000000ULL, 66000000000000ULL, 83100000000000ULL, 75300000000000ULL,
309		  99700000000000ULL, 92300000000000ULL, 125000000000000ULL, 110000000000000ULL },
310		{ 53700000000000ULL, 47700000000000ULL, 62200000000000ULL, 54400000000000ULL,
311		  75100000000000ULL, 63400000000000ULL, 90600000000000ULL, 76300000000000ULL }
312	};
313
314	/* Multiplied with 1000000000000 */
315	static const u64 c10_curve_2[2][8] = {
316		{ 2415790000ULL, 3136460000ULL, 2581990000ULL, 3222670000ULL, 2529330000ULL,
317		  3042020000ULL, 2336970000ULL, 3191460000ULL},
318		{ 4808390000ULL, 5994250000ULL, 4832730000ULL, 6193730000ULL, 4737700000ULL,
319		  6428750000ULL, 4779200000ULL, 6479340000ULL }
320	};
321
322	struct pll_output_params pll_params;
323	u32 refclk = 38400000;
324	u32 prescaler_divider = 0;
325	u32 ref_range = 1;
326	u32 ana_cp_int_gs = 30;
327	u32 ana_cp_prop_gs = 28;
328
329	compute_hdmi_tmds_pll(pixel_clock, refclk, ref_range,
330			      ana_cp_int_gs, ana_cp_prop_gs,
331			      c10_curve_freq_hz, c10_curve_0,
332			      c10_curve_1, c10_curve_2, prescaler_divider,
333			      &pll_params);
334
335	pll_state->clock = pixel_clock;
336
337	pll_state->tx = 0x10;
338	pll_state->cmn = 0x1;
339	pll_state->pll[0] = REG_FIELD_PREP(C10_PLL0_DIV5CLK_EN, pll_params.mpll_div5_en) |
340			    REG_FIELD_PREP(C10_PLL0_FRACEN, pll_params.fracn_en) |
341			    REG_FIELD_PREP(C10_PLL0_PMIX_EN, pll_params.pmix_en) |
342			    REG_FIELD_PREP(C10_PLL0_ANA_FREQ_VCO_MASK, pll_params.ana_freq_vco);
343	pll_state->pll[2] = REG_FIELD_PREP(C10_PLL2_MULTIPLIERL_MASK, pll_params.multiplier);
344	pll_state->pll[3] = REG_FIELD_PREP(C10_PLL3_MULTIPLIERH_MASK, pll_params.multiplier >> 8);
345	pll_state->pll[8] = REG_FIELD_PREP(C10_PLL8_SSC_UP_SPREAD, pll_params.ssc_up_spread);
346	pll_state->pll[9] = REG_FIELD_PREP(C10_PLL9_FRACN_DENL_MASK, pll_params.fracn_den);
347	pll_state->pll[10] = REG_FIELD_PREP(C10_PLL10_FRACN_DENH_MASK, pll_params.fracn_den >> 8);
348	pll_state->pll[11] = REG_FIELD_PREP(C10_PLL11_FRACN_QUOT_L_MASK, pll_params.fracn_quot);
349	pll_state->pll[12] = REG_FIELD_PREP(C10_PLL12_FRACN_QUOT_H_MASK,
350					    pll_params.fracn_quot >> 8);
351
352	pll_state->pll[13] = REG_FIELD_PREP(C10_PLL13_FRACN_REM_L_MASK, pll_params.fracn_rem);
353	pll_state->pll[14] = REG_FIELD_PREP(C10_PLL14_FRACN_REM_H_MASK, pll_params.fracn_rem >> 8);
354	pll_state->pll[15] = REG_FIELD_PREP(C10_PLL15_TXCLKDIV_MASK, pll_params.tx_clk_div) |
355			     REG_FIELD_PREP(C10_PLL15_HDMIDIV_MASK, pll_params.hdmi_div);
356	pll_state->pll[16] = REG_FIELD_PREP(C10_PLL16_ANA_CPINT, pll_params.ana_cp_int) |
357			     REG_FIELD_PREP(C10_PLL16_ANA_CPINTGS_L, ana_cp_int_gs);
358	pll_state->pll[17] = REG_FIELD_PREP(C10_PLL17_ANA_CPINTGS_H_MASK, ana_cp_int_gs >> 1) |
359			     REG_FIELD_PREP(C10_PLL17_ANA_CPPROP_L_MASK, pll_params.ana_cp_prop);
360	pll_state->pll[18] =
361			REG_FIELD_PREP(C10_PLL18_ANA_CPPROP_H_MASK, pll_params.ana_cp_prop >> 2) |
362			REG_FIELD_PREP(C10_PLL18_ANA_CPPROPGS_L_MASK, ana_cp_prop_gs);
363
364	pll_state->pll[19] = REG_FIELD_PREP(C10_PLL19_ANA_CPPROPGS_H_MASK, ana_cp_prop_gs >> 3) |
365			     REG_FIELD_PREP(C10_PLL19_ANA_V2I_MASK, pll_params.mpll_ana_v2i);
366}
~/ Read-only file preview.