xref: /aosp_15_r20/external/igt-gpu-tools/tools/cnl_compute_wrpll.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2017 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <assert.h>
25 #include <inttypes.h>
26 #include <stdio.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32 
33 #define U32_MAX         ((uint32_t)~0ULL)
34 #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
35 
div_u64(uint64_t dividend,uint32_t divisor)36 static inline uint64_t div_u64(uint64_t dividend, uint32_t divisor)
37 {
38 	return dividend / divisor;
39 }
40 
41 struct skl_wrpll_params {
42 	uint32_t dco_fraction;
43 	uint32_t dco_integer;
44 	uint32_t qdiv_ratio;
45 	uint32_t qdiv_mode;
46 	uint32_t kdiv;
47 	uint32_t pdiv;
48 
49 	/* for this test code only */
50 	unsigned int ref_clock;
51 } __attribute__((packed));
52 
dump_params(const char * name,struct skl_wrpll_params * params)53 static void dump_params(const char *name, struct skl_wrpll_params *params)
54 {
55 	printf("%s:\n", name);
56 	printf("Pdiv: %d\n", params->pdiv);
57 	printf("Qdiv: %d\n", params->qdiv_ratio);
58 	printf("Kdiv: %d\n", params->kdiv);
59 	printf("qdiv mode: %d\n", params->qdiv_mode);
60 	printf("dco integer: %d\n", params->dco_integer);
61 	printf("dco fraction: %d\n", params->dco_fraction);
62 }
63 
compare_params(unsigned int clock,const char * name1,struct skl_wrpll_params * p1,const char * name2,struct skl_wrpll_params * p2)64 static void compare_params(unsigned int clock,
65 			   const char *name1, struct skl_wrpll_params *p1,
66 			   const char *name2, struct skl_wrpll_params *p2)
67 {
68 	if (memcmp(p1, p2, sizeof(struct skl_wrpll_params)) == 0)
69 		return;
70 
71 	printf("=======================================\n");
72 	printf("Difference with clock: %10.6f MHz\n", clock/1000000.0);
73 	printf("Reference clock:       %10.6f MHz\n\n", p1->ref_clock/1000.0);
74 	dump_params(name1, p1);
75 	printf("\n");
76 	dump_params(name2, p2);
77 	printf("=======================================\n");
78 }
79 
cnl_wrpll_params_populate(struct skl_wrpll_params * params,uint32_t dco_freq,uint32_t ref_freq,uint32_t pdiv,uint32_t qdiv,uint32_t kdiv)80 static void cnl_wrpll_params_populate(struct skl_wrpll_params *params,
81 				      uint32_t dco_freq, uint32_t ref_freq,
82 				      uint32_t pdiv, uint32_t qdiv,
83 				      uint32_t kdiv)
84 {
85 	uint32_t dco;
86 
87 	params->qdiv_ratio = qdiv;
88 	params->qdiv_mode = (qdiv == 1) ? 0 : 1;
89 	params->pdiv = pdiv;
90 	params->kdiv = kdiv;
91 
92 	if (kdiv != 2 && qdiv != 1)
93 		printf("kdiv != 2 and qdiv != 1\n");
94 
95 	dco = div_u64((uint64_t)dco_freq << 15, ref_freq);
96 
97 	params->dco_integer = dco >> 15;
98 	params->dco_fraction = dco & 0x7fff;
99 }
100 
cnl_wrpll_get_multipliers(int bestdiv,int * pdiv,int * qdiv,int * kdiv)101 static void cnl_wrpll_get_multipliers(int bestdiv,
102 				      int *pdiv,
103 				      int *qdiv,
104 				      int *kdiv)
105 {
106 	/* even dividers */
107 	if (bestdiv % 2 == 0) {
108 		if (bestdiv == 2) {
109 			*pdiv = 2;
110 			*qdiv = 1;
111 			*kdiv = 1;
112 		} else if (bestdiv % 4 == 0) {
113 			*pdiv = 2;
114 			*qdiv = bestdiv / 4;
115 			*kdiv = 2;
116 		} else if (bestdiv % 6 == 0) {
117 			*pdiv = 3;
118 			*qdiv = bestdiv / 6;
119 			*kdiv = 2;
120 		} else if (bestdiv % 5 == 0) {
121 			*pdiv = 5;
122 			*qdiv = bestdiv / 10;
123 			*kdiv = 2;
124 		} else if (bestdiv % 14 == 0) {
125 			*pdiv = 7;
126 			*qdiv = bestdiv / 14;
127 			*kdiv = 2;
128 		}
129 	} else {
130 		if (bestdiv == 3 || bestdiv == 5 || bestdiv == 7) {
131 			*pdiv = bestdiv;
132 			*qdiv = 1;
133 			*kdiv = 1;
134 		} else { /* 9, 15, 21 */
135 			*pdiv = bestdiv / 3;
136 			*qdiv = 1;
137 			*kdiv = 3;
138 		}
139 	}
140 }
141 
142 static bool
cnl_ddi_calculate_wrpll1(int clock,struct skl_wrpll_params * params)143 cnl_ddi_calculate_wrpll1(int clock /* in Hz */,
144 			 struct skl_wrpll_params *params)
145 {
146 	double afe_clock = (clock/1000000.0) * 5; /* clocks in MHz */
147 	double dco_min = 7998;
148 	double dco_max = 10000;
149 	double dco_mid = (dco_min + dco_max) / 2;
150 	static const int dividers[] = {  2,  4,  6,  8, 10, 12,  14,  16,
151 					 18, 20, 24, 28, 30, 32,  36,  40,
152 					 42, 44, 48, 50, 52, 54,  56,  60,
153 					 64, 66, 68, 70, 72, 76,  78,  80,
154 					 84, 88, 90, 92, 96, 98, 100, 102,
155 					 3,  5,  7,  9, 15, 21 };
156 	double dco, dco_centrality = 0;
157 	double best_dco_centrality = 999999;
158 	int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0;
159 	double ref_clock = params->ref_clock/1000.0; /* MHz */
160 	uint32_t dco_int, dco_frac;
161 
162 	for (d = 0; d < ARRAY_SIZE(dividers); d++) {
163 		dco = afe_clock * dividers[d];
164 
165 		if ((dco <= dco_max) && (dco >= dco_min)) {
166 			dco_centrality = fabs(dco - dco_mid);
167 
168 			if (dco_centrality < best_dco_centrality) {
169 				best_dco_centrality = dco_centrality;
170 				best_div = dividers[d];
171 				dco_int = (uint32_t)(dco/ref_clock);
172 				dco_frac = round((dco/ref_clock - dco_int) * (1<<15));
173 			}
174 		}
175 	}
176 
177 	if (best_div != 0) {
178 		cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv);
179 
180 		params->qdiv_ratio = qdiv;
181 		params->qdiv_mode = (qdiv == 1) ? 0 : 1;
182 		params->pdiv = pdiv;
183 		params->kdiv = kdiv;
184 		params->dco_integer = dco_int;
185 		params->dco_fraction = dco_frac;
186 	} else {
187 		return false;
188 	}
189 
190 	return true;
191 }
192 
193 static bool
cnl_ddi_calculate_wrpll2(int clock,struct skl_wrpll_params * params)194 cnl_ddi_calculate_wrpll2(int clock,
195 			 struct skl_wrpll_params *params)
196 {
197 	uint32_t afe_clock = clock * 5 / 1000; /* clock in kHz */
198 	uint32_t dco_min = 7998000;
199 	uint32_t dco_max = 10000000;
200 	uint32_t dco_mid = (dco_min + dco_max) / 2;
201 	static const int dividers[] = {  2,  4,  6,  8, 10, 12,  14,  16,
202 					 18, 20, 24, 28, 30, 32,  36,  40,
203 					 42, 44, 48, 50, 52, 54,  56,  60,
204 					 64, 66, 68, 70, 72, 76,  78,  80,
205 					 84, 88, 90, 92, 96, 98, 100, 102,
206 					  3,  5,  7,  9, 15, 21 };
207 	uint32_t dco, best_dco = 0, dco_centrality = 0;
208 	uint32_t best_dco_centrality = U32_MAX; /* Spec meaning of 999999 MHz */
209 	int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0;
210 	uint32_t ref_clock = params->ref_clock;
211 
212 	for (d = 0; d < ARRAY_SIZE(dividers); d++) {
213 		dco = afe_clock * dividers[d];
214 
215 		if ((dco <= dco_max) && (dco >= dco_min)) {
216 			dco_centrality = abs(dco - dco_mid);
217 
218 			if (dco_centrality < best_dco_centrality) {
219 				best_dco_centrality = dco_centrality;
220 				best_div = dividers[d];
221 				best_dco = dco;
222 			}
223 		}
224 	}
225 
226 	if (best_div == 0)
227 		return false;
228 
229 	cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv);
230 
231 	cnl_wrpll_params_populate(params, best_dco, ref_clock,
232 				  pdiv, qdiv, kdiv);
233 
234 	return true;
235 }
236 
test_multipliers(unsigned int clock)237 static void test_multipliers(unsigned int clock)
238 {
239 	uint64_t afe_clock = clock * 5 / 1000; /* clocks in kHz */
240 	unsigned int dco_min = 7998000;
241 	unsigned int dco_max = 10000000;
242 	unsigned int dco_mid = (dco_min + dco_max) / 2;
243 
244 	static const int dividerlist[] = {  2,  4,  6,  8, 10, 12,  14,  16,
245 					   18, 20, 24, 28, 30, 32,  36,  40,
246 					   42, 44, 48, 50, 52, 54,  56,  60,
247 					   64, 66, 68, 70, 72, 76,  78,  80,
248 					   84, 88, 90, 92, 96, 98, 100, 102,
249 					    3,  5,  7,  9, 15, 21 };
250 	unsigned int dco, dco_centrality = 0;
251 	unsigned int best_dco_centrality = U32_MAX;
252 	int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0;
253 
254 	for (d = 0; d < ARRAY_SIZE(dividerlist); d++) {
255 		dco = afe_clock * dividerlist[d];
256 
257 		if ((dco <= dco_max) && (dco >= dco_min)) {
258 			dco_centrality = abs(dco - dco_mid);
259 
260 			if (dco_centrality < best_dco_centrality) {
261 				best_dco_centrality = dco_centrality;
262 				best_div = dividerlist[d];
263 			}
264 		}
265 
266 		if (best_div != 0) {
267 			cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv);
268 
269 			if ((kdiv != 2) && (qdiv == 1))
270 				continue;
271 			else
272 				break;
273 		}
274 	}
275 
276 	assert(pdiv);
277 	assert(qdiv);
278 	assert(kdiv);
279 
280 	if (kdiv != 2)
281 		assert(qdiv == 1);
282 }
283 
284 static const struct {
285 	uint32_t clock; /* in Hz */
286 } modes[] = {
287 	{19750000},
288 	{23500000},
289 	{23750000},
290 	{25175000},
291 	{25200000},
292 	{26000000},
293 	{27000000},
294 	{27027000},
295 	{27500000},
296 	{28750000},
297 	{29750000},
298 	{30750000},
299 	{31500000},
300 	{35000000},
301 	{35500000},
302 	{36750000},
303 	{37000000},
304 	{37088000},
305 	{37125000},
306 	{37762500},
307 	{37800000},
308 	{38250000},
309 	{40500000},
310 	{40541000},
311 	{40750000},
312 	{41000000},
313 	{41500000},
314 	{42500000},
315 	{45250000},
316 	{46360000},
317 	{46406000},
318 	{46750000},
319 	{49000000},
320 	{50500000},
321 	{52000000},
322 	{54000000},
323 	{54054000},
324 	{54500000},
325 	{55632000},
326 	{55688000},
327 	{56000000},
328 	{56750000},
329 	{58250000},
330 	{58750000},
331 	{59341000},
332 	{59400000},
333 	{60500000},
334 	{62250000},
335 	{63500000},
336 	{64000000},
337 	{65250000},
338 	{65500000},
339 	{66750000},
340 	{67750000},
341 	{68250000},
342 	{69000000},
343 	{72000000},
344 	{74176000},
345 	{74250000},
346 	{74500000},
347 	{75250000},
348 	{76000000},
349 	{79500000},
350 	{81000000},
351 	{81081000},
352 	{82000000},
353 	{83000000},
354 	{84750000},
355 	{85250000},
356 	{85750000},
357 	{88500000},
358 	{89012000},
359 	{89100000},
360 	{91000000},
361 	{92719800},
362 	{92812500},
363 	{94500000},
364 	{95750000},
365 	{97750000},
366 	{99000000},
367 	{99750000},
368 	{100000000},
369 	{100500000},
370 	{101000000},
371 	{101250000},
372 	{102250000},
373 	{107892000},
374 	{108000000},
375 	{108108000},
376 	{109000000},
377 	{110250000},
378 	{110500000},
379 	{111264000},
380 	{111375000},
381 	{112500000},
382 	{117500000},
383 	{119000000},
384 	{119500000},
385 	{121250000},
386 	{121750000},
387 	{125250000},
388 	{125750000},
389 	{127250000},
390 	{130000000},
391 	{130250000},
392 	{131000000},
393 	{131500000},
394 	{132750000},
395 	{135250000},
396 	{138500000},
397 	{138750000},
398 	{141500000},
399 	{146250000},
400 	{148250000},
401 	{148352000},
402 	{148500000},
403 	{154000000},
404 	{155250000},
405 	{155750000},
406 	{156000000},
407 	{158250000},
408 	{159500000},
409 	{161000000},
410 	{162000000},
411 	{162162000},
412 	{162500000},
413 	{169500000},
414 	{172750000},
415 	{173000000},
416 	{175000000},
417 	{178500000},
418 	{179500000},
419 	{184750000},
420 	{185440000},
421 	{185625000},
422 	{187000000},
423 	{192250000},
424 	{193250000},
425 	{197750000},
426 	{198500000},
427 	{204750000},
428 	{207500000},
429 	{209250000},
430 	{213750000},
431 	{214750000},
432 	{216000000},
433 	{218750000},
434 	{219000000},
435 	{220750000},
436 	{222525000},
437 	{222750000},
438 	{227000000},
439 	{230250000},
440 	{233500000},
441 	{235000000},
442 	{238000000},
443 	{241500000},
444 	{243000000},
445 	{245250000},
446 	{247750000},
447 	{253250000},
448 	{256250000},
449 	{262500000},
450 	{267250000},
451 	{268500000},
452 	{270000000},
453 	{272500000},
454 	{273750000},
455 	{280750000},
456 	{281250000},
457 	{286000000},
458 	{291750000},
459 	{296703000},
460 	{297000000},
461 	{298000000},
462 	{303750000},
463 	{322250000},
464 	{324000000},
465 	{337750000},
466 	{370878750},
467 	{371250000},
468 	{373250000},
469 	{414500000},
470 	{432000000},
471 	{445054500},
472 	{445500000},
473 	{497750000},
474 	{533250000},
475 	{540000000},
476 	{592500000},
477 	{594000000},
478 	{648000000},
479 	{810000000},
480 };
481 
test_run(unsigned int ref_clock)482 static void test_run(unsigned int ref_clock)
483 {
484 	unsigned int m;
485 	struct skl_wrpll_params params[2];
486 
487 	for (m = 0; m < ARRAY_SIZE(modes); m++) {
488 		int clock = modes[m].clock;
489 		bool skip = false;
490 
491 		params[0].ref_clock = params[1].ref_clock = ref_clock;
492 
493 		if (!cnl_ddi_calculate_wrpll1(clock, &params[0])) {
494 			fprintf(stderr, "Reference: Couldn't compute divider for %dHz, reference %dHz\n",
495 				clock, params[0].ref_clock*1000);
496 			skip = true;
497 		}
498 
499 		if (!skip) {
500 			if (!cnl_ddi_calculate_wrpll2(clock, &params[1])) {
501 				fprintf(stderr, "i915 implementation: Couldn't compute divider for %dHz, reference %dHz\n",
502 					clock, params[1].ref_clock*1000);
503 			}
504 
505 			compare_params(clock, "Reference", &params[0],
506 				       "i915 implementation", &params[1]);
507 		}
508 	}
509 }
510 
main(int argc,char ** argv)511 int main(int argc, char **argv)
512 {
513 	unsigned int m;
514 	unsigned int f;
515 	unsigned int ref_clocks[] = {19200, 24000}; /* in kHz */
516 
517 	for (m = 0; m < ARRAY_SIZE(modes); m++)
518 		test_multipliers(modes[m].clock);
519 
520 	for (f = 0; f < ARRAY_SIZE(ref_clocks); f++) {
521 		printf("=== Testing with ref clock %d kHz\n", ref_clocks[f]);
522 		test_run(ref_clocks[f]);
523 	}
524 
525 	return 0;
526 }
527