xref: /haiku/src/add-ons/accelerants/intel_extreme/pll.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2006-2018, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Alexander von Gluck IV, kallisti5@unixzen.com
8  *		Adrien Destugues, pulkomandy@pulkomandy.tk
9  */
10 
11 
12 #include "pll.h"
13 
14 #include <math.h>
15 #include <stdio.h>
16 #include <string.h>
17 
18 #include <Debug.h>
19 
20 #include <create_display_modes.h>
21 #include <ddc.h>
22 #include <edid.h>
23 #include <validate_display_mode.h>
24 
25 #include "accelerant_protos.h"
26 #include "accelerant.h"
27 #include "utility.h"
28 
29 
30 #undef TRACE
31 #define TRACE_MODE
32 #ifdef TRACE_MODE
33 #	define TRACE(x...) _sPrintf("intel_extreme: " x)
34 #else
35 #	define TRACE(x...)
36 #endif
37 
38 #define ERROR(x...) _sPrintf("intel_extreme: " x)
39 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
40 
41 
42 
43 // Static pll limits taken from Linux kernel KMS
44 
45 static pll_limits kLimitsIlkDac = {
46 	// p, p1, p2, n,   m, m1, m2
47 	{  5,  1,  5, 3,  79, 12,  5}, // min
48 	{ 80,  8, 10, 8, 118, 22,  9}, // max
49 	225000, 1760000, 3510000
50 };
51 
52 static pll_limits kLimitsIlkLvdsSingle = {
53 	// p, p1, p2, n,   m, m1, m2
54 	{ 28,  2, 14, 3,  79, 12,  5}, // min
55 	{112,  8, 14, 8, 118, 22,  9}, // max
56 	225000, 1760000, 3510000
57 };
58 
59 static pll_limits kLimitsIlkLvdsDual = {
60 	// p, p1, p2, n,   m, m1, m2
61 	{ 14,  2,  7, 3,  79, 12,  5}, // min
62 	{ 56,  8,  7, 8, 127, 22,  9}, // max
63 	225000, 1760000, 3510000
64 };
65 
66 // 100Mhz RefClock
67 static pll_limits kLimitsIlkLvdsSingle100 = {
68 	// p, p1, p2, n,   m, m1, m2
69 	{ 28,  2, 14, 3,  79, 12,  5}, // min
70 	{112,  8, 14, 8, 126, 22,  9}, // max
71 	225000, 1760000, 3510000
72 };
73 
74 static pll_limits kLimitsIlkLvdsDual100 = {
75 	// p, p1, p2, n,   m, m1, m2
76 	{ 14,  2,  7, 3,  79, 12,  5}, // min
77 	{ 42,  6,  7, 8, 126, 22,  9}, // max
78 	225000, 1760000, 3510000
79 };
80 
81 #if 0
82 static pll_limits kLimitsChv = {
83 	// p, p1, p2, n,   m, m1, m2
84 	{  0,  2, 14, 1,  79, 2,   24 << 22}, // min
85 	{  0,  4,  1, 1, 127, 2,  175 << 22}, // max
86 	0, 4800000, 6480000
87 };
88 
89 static pll_limits kLimitsVlv = {
90 	// p, p1, p2, n,   m, m1, m2
91 	{  0,  2, 20, 1,  79, 2,   11},	// min
92 	{  0,  3,  2, 7, 127, 3,  156},	// max
93 	0, 4000000, 6000000
94 };
95 
96 static pll_limits kLimitsBxt = {
97 	// p, p1, p2, n,  m, m1, m2
98 	{  0,  2,  1, 1,  0,  2,   2 << 22}, // min
99 	{  0,  4, 20, 1,  0,  2, 255 << 22}, // max
100 	0, 4800000, 6700000
101 };
102 #endif
103 
104 static pll_limits kLimits9xxSdvo = {
105 	// p, p1, p2,  n,   m, m1, m2
106 	{  5,  1, 10,  5,  70, 12,  7},	// min
107 	{ 80,  8,  5, 10, 120, 22, 11},	// max
108 	200000, 1400000, 2800000
109 };
110 
111 static pll_limits kLimits9xxLvds = {
112 	// p, p1, p2,  n,   m, m1, m2
113 	{  7,  1, 14,  1,  70,  8,  3},	// min
114 	{ 98,  8,  7,  6, 120, 18,  7},	// max
115 	112000, 1400000, 2800000
116 };
117 
118 static pll_limits kLimitsG4xSdvo = {
119 	// p, p1, p2, n,   m, m1, m2
120 	{ 10,  1, 10, 1, 104, 17,  5},	// min
121 	{ 30,  3, 10, 4, 138, 23, 11},	// max
122 	270000, 1750000, 3500000
123 };
124 
125 #if 0
126 static pll_limits kLimitsG4xHdmi = {
127 	// p, p1, p2, n,   m, m1, m2
128 	{  5,  1, 10, 1, 104, 16,  5},	// min
129 	{ 80,  8,  5, 4, 138, 23, 11},	// max
130 	165000, 1750000, 3500000
131 };
132 #endif
133 
134 static pll_limits kLimitsG4xLvdsSingle = {
135 	// p,  p1, p2, n,   m, m1, m2
136 	{ 28,   2, 14, 1, 104, 17,  5},	// min
137 	{ 112,  8, 14, 3, 138, 23, 11},	// max
138 	0, 1750000, 3500000
139 };
140 
141 static pll_limits kLimitsG4xLvdsDual = {
142 	// p, p1, p2, n,   m, m1, m2
143 	{ 14,  2,  7, 1, 104, 17,  5},	// min
144 	{ 42,  6,  7, 3, 138, 23, 11},	// max
145 	0, 1750000, 3500000
146 };
147 
148 static pll_limits kLimitsPinSdvo = {
149 	// p, p1, p2, n,   m, m1,  m2
150 	{  5,  1, 10, 3,   2,  0,   0},	// min
151 	{ 80,  8,  5, 6, 256,  0, 254},	// max
152 	200000, 1700000, 3500000
153 };
154 
155 static pll_limits kLimitsPinLvds = {
156 	// p, p1, p2, n,   m, m1,  m2
157 	{  7,  1, 14, 3,   2,  0,   0},	// min
158 	{112,  8, 14, 6, 256,  0, 254},	// max
159 	112000, 1700000, 3500000
160 };
161 
162 static pll_limits kLimits85x = {
163 	// p, p1, p2,  n,   m, m1, m2
164 	{  4,  2,  4,  5,  96, 20,  8},
165 	{128, 33,  2, 18, 140, 28, 18},
166 	165000, 930000, 1400000
167 };
168 
169 
170 static bool
171 lvds_dual_link(display_mode* current)
172 {
173 	float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
174 	if (requestedPixelClock > 112.999)
175 		return true;
176 
177 	// TODO: Force dual link on MacBookPro6,2  MacBookPro8,2  MacBookPro9,1
178 
179 	return ((read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
180 		== LVDS_CLKB_POWER_UP);
181 }
182 
183 
184 bool
185 valid_pll_divisors(pll_divisors* divisors, pll_limits* limits)
186 {
187 	pll_info &info = gInfo->shared_info->pll_info;
188 	uint32 vco = info.reference_frequency * divisors->m / divisors->n;
189 	uint32 frequency = vco / divisors->p;
190 
191 	if (divisors->p < limits->min.p || divisors->p > limits->max.p
192 		|| divisors->m < limits->min.m || divisors->m > limits->max.m
193 		|| vco < limits->min_vco || vco > limits->max_vco
194 		|| frequency < info.min_frequency || frequency > info.max_frequency)
195 		return false;
196 
197 	return true;
198 }
199 
200 
201 static void
202 compute_pll_p2(display_mode* current, pll_divisors* divisors,
203 	pll_limits* limits, bool isLVDS)
204 {
205 	if (isLVDS) {
206 		if (lvds_dual_link(current)) {
207 			// fast DAC timing via 2 channels (dual link LVDS)
208 			divisors->p2 = limits->max.p2;
209 		} else {
210 			// slow DAC timing
211 			divisors->p2 = limits->min.p2;
212 		}
213 	} else {
214 		if (current->timing.pixel_clock < limits->dot_limit) {
215 			// slow DAC timing
216 			divisors->p2 = limits->max.p2;
217 		} else {
218 			// fast DAC timing
219 			divisors->p2 = limits->min.p2;
220 		}
221 	}
222 }
223 
224 
225 static uint32
226 compute_pll_m(pll_divisors* divisors)
227 {
228 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)
229 		|| gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) {
230 		return divisors->m1 * divisors->m2;
231 	}
232 
233 	// Pineview, m1 is reserved
234 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN))
235 		return divisors->m2 + 2;
236 
237 	if (gInfo->shared_info->device_type.Generation() >= 3)
238 		return 5 * (divisors->m1 + 2) + (divisors->m2 + 2);
239 
240 	// TODO: This logic needs validated... PLL's were calculated differently
241 	// on 8xx chipsets
242 
243 	return 5 * divisors->m1 + divisors->m2;
244 }
245 
246 
247 static uint32
248 compute_pll_p(pll_divisors* divisors)
249 {
250 	return divisors->p1 * divisors->p2;
251 }
252 
253 
254 static void
255 compute_dpll_g4x(display_mode* current, pll_divisors* divisors, bool isLVDS)
256 {
257 	float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
258 	float referenceClock
259 		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
260 
261 	TRACE("%s: required MHz: %g, reference clock: %g\n", __func__,
262 		requestedPixelClock, referenceClock);
263 
264 	pll_limits limits;
265 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)) {
266 		// TODO: Pass port type via video_configuration
267 		if (isLVDS) {
268 			if (lvds_dual_link(current))
269 				memcpy(&limits, &kLimitsG4xLvdsDual, sizeof(pll_limits));
270 			else
271 				memcpy(&limits, &kLimitsG4xLvdsSingle, sizeof(pll_limits));
272 		//} else if (type == INTEL_PORT_TYPE_HDMI) {
273 		//	memcpy(&limits, &kLimitsG4xHdmi, sizeof(pll_limits));
274 		} else
275 			memcpy(&limits, &kLimitsG4xSdvo, sizeof(pll_limits));
276 	} else {
277 		// There must be a PCH, so this is ivy bridge or later
278 		if (isLVDS) {
279 			if (lvds_dual_link(current)) {
280 				if (referenceClock == 100.0)
281 					memcpy(&limits, &kLimitsIlkLvdsDual100, sizeof(pll_limits));
282 				else
283 					memcpy(&limits, &kLimitsIlkLvdsDual, sizeof(pll_limits));
284 			} else {
285 				if (referenceClock == 100.0) {
286 					memcpy(&limits, &kLimitsIlkLvdsSingle100,
287 						sizeof(pll_limits));
288 				} else {
289 					memcpy(&limits, &kLimitsIlkLvdsSingle, sizeof(pll_limits));
290 				}
291 			}
292 		} else {
293 			memcpy(&limits, &kLimitsIlkDac, sizeof(pll_limits));
294 		}
295 	}
296 
297 	compute_pll_p2(current, divisors, &limits, isLVDS);
298 
299 	TRACE("PLL limits, min: p %" B_PRId32 " (p1 %" B_PRId32 ", "
300 		"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
301 		"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits.min.p,
302 		limits.min.p1, limits.min.p2, limits.min.n, limits.min.m,
303 		limits.min.m1, limits.min.m2);
304 	TRACE("PLL limits, max: p %" B_PRId32 " (p1 %" B_PRId32 ", "
305 		"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
306 		"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits.max.p,
307 		limits.max.p1, limits.max.p2, limits.max.n, limits.max.m,
308 		limits.max.m1, limits.max.m2);
309 
310 	float best = requestedPixelClock;
311 	pll_divisors bestDivisors;
312 
313 	for (divisors->n = limits.min.n; divisors->n <= limits.max.n;
314 			divisors->n++) {
315 		for (divisors->m1 = limits.max.m1; divisors->m1 >= limits.min.m1;
316 				divisors->m1--) {
317 			for (divisors->m2 = limits.max.m2; divisors->m2 >= limits.min.m2;
318 					divisors->m2--) {
319 				for (divisors->p1 = limits.max.p1;
320 						divisors->p1 >= limits.min.p1; divisors->p1--) {
321 					divisors->m = compute_pll_m(divisors);
322 					divisors->p = compute_pll_p(divisors);
323 
324 					if (!valid_pll_divisors(divisors, &limits))
325 						continue;
326 
327 					float error = fabs(requestedPixelClock
328 						- ((referenceClock * divisors->m) / divisors->n)
329 						/ divisors->p);
330 					if (error < best) {
331 						best = error;
332 						bestDivisors = *divisors;
333 
334 						if (error == 0)
335 							break;
336 					}
337 				}
338 			}
339 		}
340 	}
341 	*divisors = bestDivisors;
342 	TRACE("%s: best MHz: %g (error: %g)\n", __func__,
343 		((referenceClock * divisors->m) / divisors->n) / divisors->p,
344 		best);
345 }
346 
347 
348 static void
349 compute_dpll_9xx(display_mode* current, pll_divisors* divisors, bool isLVDS)
350 {
351 	float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
352 	float referenceClock
353 		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
354 
355 	TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);
356 
357 	pll_limits limits;
358 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN)) {
359 		if (isLVDS)
360 			memcpy(&limits, &kLimitsPinLvds, sizeof(pll_limits));
361 		else
362 			memcpy(&limits, &kLimitsPinSdvo, sizeof(pll_limits));
363 	} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_85x)) {
364 		memcpy(&limits, &kLimits85x, sizeof(pll_limits));
365 	} else {
366 		if (isLVDS)
367 			memcpy(&limits, &kLimits9xxLvds, sizeof(pll_limits));
368 		else
369 			memcpy(&limits, &kLimits9xxSdvo, sizeof(pll_limits));
370 	}
371 
372 	compute_pll_p2(current, divisors, &limits, isLVDS);
373 
374 	TRACE("PLL limits, min: p %" B_PRId32 " (p1 %" B_PRId32 ", "
375 		"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
376 		"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits.min.p,
377 		limits.min.p1, limits.min.p2, limits.min.n, limits.min.m,
378 		limits.min.m1, limits.min.m2);
379 	TRACE("PLL limits, max: p %" B_PRId32 " (p1 %" B_PRId32 ", "
380 		"p2 %" B_PRId32 "), n %" B_PRId32 ", m %" B_PRId32 " "
381 		"(m1 %" B_PRId32 ", m2 %" B_PRId32 ")\n", limits.max.p,
382 		limits.max.p1, limits.max.p2, limits.max.n, limits.max.m,
383 		limits.max.m1, limits.max.m2);
384 
385 	bool is_pine = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN);
386 
387 	float best = requestedPixelClock;
388 	pll_divisors bestDivisors;
389 
390 	for (divisors->m1 = limits.min.m1; divisors->m1 <= limits.max.m1;
391 			divisors->m1++) {
392 		for (divisors->m2 = limits.min.m2; divisors->m2 <= limits.max.m2
393 				&& ((divisors->m2 < divisors->m1) || is_pine); divisors->m2++) {
394 			for (divisors->n = limits.min.n; divisors->n <= limits.max.n;
395 					divisors->n++) {
396 				for (divisors->p1 = limits.min.p1;
397 						divisors->p1 <= limits.max.p1; divisors->p1++) {
398 					divisors->m = compute_pll_m(divisors);
399 					divisors->p = compute_pll_p(divisors);
400 
401 					if (!valid_pll_divisors(divisors, &limits))
402 						continue;
403 
404 					float error = fabs(requestedPixelClock
405 						- ((referenceClock * divisors->m) / divisors->n)
406 						/ divisors->p);
407 					if (error < best) {
408 						best = error;
409 						bestDivisors = *divisors;
410 
411 						if (error == 0)
412 							break;
413 					}
414 				}
415 			}
416 		}
417 	}
418 
419 	*divisors = bestDivisors;
420 
421 	TRACE("%s: best MHz: %g (error: %g)\n", __func__,
422 		((referenceClock * divisors->m) / divisors->n) / divisors->p,
423 		best);
424 }
425 
426 
427 void
428 compute_pll_divisors(display_mode* current, pll_divisors* divisors, bool isLVDS)
429 {
430 	if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_G4x)
431 		|| (gInfo->shared_info->pch_info != INTEL_PCH_NONE)) {
432 		compute_dpll_g4x(current, divisors, isLVDS);
433 	} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_CHV)) {
434 		ERROR("%s: TODO: CherryView\n", __func__);
435 	} else if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_VLV)) {
436 		ERROR("%s: TODO: VallyView\n", __func__);
437 	} else
438 		compute_dpll_9xx(current, divisors, isLVDS);
439 
440 	TRACE("%s: found: p = %" B_PRId32 " (p1 = %" B_PRId32 ", "
441 		"p2 = %" B_PRId32 "), n = %" B_PRId32 ", m = %" B_PRId32 " "
442 		"(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__,
443 		divisors->p, divisors->p1, divisors->p2, divisors->n,
444 		divisors->m, divisors->m1, divisors->m2);
445 }
446 
447 
448 void
449 refclk_activate_ilk(bool hasPanel)
450 {
451 	CALLED();
452 
453 	// aka, our engineers hate you
454 
455 	bool wantsSSC;
456 	bool hasCK505;
457 	if (gInfo->shared_info->pch_info == INTEL_PCH_IBX) {
458 		//XXX: This should be == vbt display_clock_mode
459 		hasCK505 = true;
460 		wantsSSC = hasCK505;
461 	} else {
462 		hasCK505 = false;
463 		wantsSSC = true;
464 	}
465 
466 	uint32 clkRef = read32(PCH_DREF_CONTROL);
467 	uint32 newRef = clkRef;
468 
469 	newRef &= ~DREF_NONSPREAD_SOURCE_MASK;
470 
471 	if (hasCK505)
472 		newRef |= DREF_NONSPREAD_CK505_ENABLE;
473 	else
474 		newRef |= DREF_NONSPREAD_SOURCE_ENABLE;
475 
476 	newRef &= ~DREF_SSC_SOURCE_MASK;
477 	newRef &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
478 	newRef &= ~DREF_SSC1_ENABLE;
479 
480 	if (newRef == clkRef) {
481 		TRACE("%s: No changes to reference clock.\n", __func__);
482 		return;
483 	}
484 
485 	if (hasPanel) {
486 		newRef &= ~DREF_SSC_SOURCE_MASK;
487 		newRef |= DREF_SSC_SOURCE_ENABLE;
488 
489 		if (wantsSSC)
490 			newRef |= DREF_SSC1_ENABLE;
491 		else
492 			newRef &= ~DREF_SSC1_ENABLE;
493 
494 		// Power up SSC before enabling outputs
495 		write32(PCH_DREF_CONTROL, newRef);
496 		read32(PCH_DREF_CONTROL);
497 		spin(200);
498 
499 		newRef &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
500 
501 		bool hasEDP = true;
502 		if (hasEDP) {
503 			if (wantsSSC)
504 				newRef |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
505 			else
506 				newRef |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
507 		} else
508 			newRef |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
509 
510 		write32(PCH_DREF_CONTROL, newRef);
511 		read32(PCH_DREF_CONTROL);
512 		spin(200);
513 	} else {
514 		newRef &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
515 		newRef |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
516 
517 		write32(PCH_DREF_CONTROL, newRef);
518 		read32(PCH_DREF_CONTROL);
519 		spin(200);
520 
521 		if (!wantsSSC) {
522 			newRef &= ~DREF_SSC_SOURCE_MASK;
523 			newRef |= DREF_SSC_SOURCE_DISABLE;
524 			newRef &= ~DREF_SSC1_ENABLE;
525 
526 			write32(PCH_DREF_CONTROL, newRef);
527 			read32(PCH_DREF_CONTROL);
528 			spin(200);
529 		}
530 	}
531 }
532 
533