xref: /haiku/src/add-ons/accelerants/radeon_hd/pll.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *	  Alexander von Gluck, kallisti5@unixzen.com
7  */
8 
9 
10 #include "pll.h"
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <math.h>
16 
17 #include "accelerant_protos.h"
18 #include "accelerant.h"
19 #include "bios.h"
20 #include "connector.h"
21 #include "display.h"
22 #include "displayport.h"
23 #include "encoder.h"
24 #include "utility.h"
25 
26 
27 #define TRACE_PLL
28 #ifdef TRACE_PLL
29 extern "C" void _sPrintf(const char* format, ...);
30 #   define TRACE(x...) _sPrintf("radeon_hd: " x)
31 #else
32 #   define TRACE(x...) ;
33 #endif
34 
35 #define ERROR(x...) _sPrintf("radeon_hd: " x)
36 
37 // Pixel Clock Storage
38 // kHz			Value			Result
39 //	Haiku:		104000 khz		104 Mhz
40 //	Linux:		104000 khz		104 Mhz
41 //	AtomBIOS:	10400 * 10 khz	104 Mhz
42 // Ghz
43 //	Haiku:		162000 * 10 khz	1.62 Ghz
44 //	Linux:		162000 * 10 khz	1.62 Ghz
45 //	AtomBIOS:	16200  * 10 Khz	0.162 * 10 Ghz
46 
47 
48 /* The PLL allows to synthesize a clock signal with a range of frequencies
49  * based on a single input reference clock signal. It uses several dividers
50  * to create a rational factor multiple of the input frequency.
51  *
52  * The reference clock signal frequency is pll_info::referenceFreq (in kHz).
53  * It is then, one after another...
54  *   (1) divided by the (integer) reference divider (pll_info::referenceDiv).
55  *   (2) multiplied by the fractional feedback divider, which sits in the
56  *       PLL's feedback loop and thus multiplies the frequency. It allows
57  *       using a rational number factor of the form "x.y", with
58  *       x = pll_info::feedbackDiv and y = pll_info::feedbackDivFrac.
59  *   (3) divided by the (integer) post divider (pll_info::postDiv).
60  *   Allowed ranges are given in the pll_info min/max values.
61  *
62  *   The resulting output pixel clock frequency is then:
63  *
64  *                            feedbackDiv + (feedbackDivFrac/10)
65  *   f_out = referenceFreq * ------------------------------------
66  *                                  referenceDiv * postDiv
67  */
68 
69 
70 status_t
71 pll_limit_probe(pll_info* pll)
72 {
73 	uint8 tableMajor;
74 	uint8 tableMinor;
75 	uint16 tableOffset;
76 
77 	int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
78 	if (atom_parse_data_header(gAtomContext, index, NULL,
79 		&tableMajor, &tableMinor, &tableOffset) != B_OK) {
80 		ERROR("%s: Couldn't parse data header\n", __func__);
81 		return B_ERROR;
82 	}
83 
84 	TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
85 		tableMajor, tableMinor);
86 
87 	union atomFirmwareInfo {
88 		ATOM_FIRMWARE_INFO info;
89 		ATOM_FIRMWARE_INFO_V1_2 info_12;
90 		ATOM_FIRMWARE_INFO_V1_3 info_13;
91 		ATOM_FIRMWARE_INFO_V1_4 info_14;
92 		ATOM_FIRMWARE_INFO_V2_1 info_21;
93 		ATOM_FIRMWARE_INFO_V2_2 info_22;
94 	};
95 	union atomFirmwareInfo* firmwareInfo
96 		= (union atomFirmwareInfo*)(gAtomContext->bios + tableOffset);
97 
98 	/* pixel clock limits */
99 	pll->referenceFreq
100 		= B_LENDIAN_TO_HOST_INT16(firmwareInfo->info.usReferenceClock) * 10;
101 
102 	if (tableMinor < 2) {
103 		pll->pllOutMin
104 			= B_LENDIAN_TO_HOST_INT16(
105 				firmwareInfo->info.usMinPixelClockPLL_Output) * 10;
106 	} else {
107 		pll->pllOutMin
108 			= B_LENDIAN_TO_HOST_INT32(
109 				firmwareInfo->info_12.ulMinPixelClockPLL_Output) * 10;
110 	}
111 
112 	pll->pllOutMax
113 		= B_LENDIAN_TO_HOST_INT32(
114 			firmwareInfo->info.ulMaxPixelClockPLL_Output) * 10;
115 
116 	if (tableMinor >= 4) {
117 		pll->lcdPllOutMin
118 			= B_LENDIAN_TO_HOST_INT16(
119 				firmwareInfo->info_14.usLcdMinPixelClockPLL_Output) * 1000;
120 
121 		if (pll->lcdPllOutMin == 0)
122 			pll->lcdPllOutMin = pll->pllOutMin;
123 
124 		pll->lcdPllOutMax
125 			= B_LENDIAN_TO_HOST_INT16(
126 				firmwareInfo->info_14.usLcdMaxPixelClockPLL_Output) * 1000;
127 
128 		if (pll->lcdPllOutMax == 0)
129 			pll->lcdPllOutMax = pll->pllOutMax;
130 
131 	} else {
132 		pll->lcdPllOutMin = pll->pllOutMin;
133 		pll->lcdPllOutMax = pll->pllOutMax;
134 	}
135 
136 	if (pll->pllOutMin == 0) {
137 		pll->pllOutMin = 64800 * 10;
138 			// Avivo+ limit
139 	}
140 
141 	pll->minPostDiv = POST_DIV_MIN;
142 	pll->maxPostDiv = POST_DIV_LIMIT;
143 	pll->minRefDiv = REF_DIV_MIN;
144 	pll->maxRefDiv = REF_DIV_LIMIT;
145 	pll->minFeedbackDiv = FB_DIV_MIN;
146 	pll->maxFeedbackDiv = FB_DIV_LIMIT;
147 
148 	pll->pllInMin = B_LENDIAN_TO_HOST_INT16(
149 		firmwareInfo->info.usMinPixelClockPLL_Input) * 10;
150 	pll->pllInMax = B_LENDIAN_TO_HOST_INT16(
151 		firmwareInfo->info.usMaxPixelClockPLL_Input) * 10;
152 
153 	TRACE("%s: referenceFreq: %" B_PRIu32 "; pllOutMin: %" B_PRIu32 "; "
154 		" pllOutMax: %" B_PRIu32 "; pllInMin: %" B_PRIu32 ";"
155 		"pllInMax: %" B_PRIu32 "\n", __func__, pll->referenceFreq,
156 		pll->pllOutMin, pll->pllOutMax, pll->pllInMin, pll->pllInMax);
157 
158 	return B_OK;
159 }
160 
161 
162 status_t
163 pll_ppll_ss_probe(pll_info* pll, uint32 ssID)
164 {
165 	uint8 tableMajor;
166 	uint8 tableMinor;
167 	uint16 headerOffset;
168 	uint16 headerSize;
169 
170 	int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info);
171 	if (atom_parse_data_header(gAtomContext, index, &headerSize,
172 		&tableMajor, &tableMinor, &headerOffset) != B_OK) {
173 		ERROR("%s: Couldn't parse data header\n", __func__);
174 		pll->ssEnabled = false;
175 		return B_ERROR;
176 	}
177 
178 	struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info
179 		= (struct _ATOM_SPREAD_SPECTRUM_INFO*)((uint16*)gAtomContext->bios
180 		+ headerOffset);
181 
182 	int indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
183 		/ sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT);
184 
185 	int i;
186 	for (i = 0; i < indices; i++) {
187 		if (ss_info->asSS_Info[i].ucSS_Id == ssID) {
188 			pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
189 				ss_info->asSS_Info[i].usSpreadSpectrumPercentage);
190 			pll->ssType = ss_info->asSS_Info[i].ucSpreadSpectrumType;
191 			pll->ssStep = ss_info->asSS_Info[i].ucSS_Step;
192 			pll->ssDelay = ss_info->asSS_Info[i].ucSS_Delay;
193 			pll->ssRange = ss_info->asSS_Info[i].ucSS_Range;
194 			pll->ssReferenceDiv
195 				= ss_info->asSS_Info[i].ucRecommendedRef_Div;
196 			pll->ssEnabled = true;
197 			return B_OK;
198 		}
199 	}
200 
201 	pll->ssEnabled = false;
202 	return B_ERROR;
203 }
204 
205 
206 status_t
207 pll_asic_ss_probe(pll_info* pll, uint32 ssID)
208 {
209 	uint8 tableMajor;
210 	uint8 tableMinor;
211 	uint16 headerOffset;
212 	uint16 headerSize;
213 
214 	int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
215 	if (atom_parse_data_header(gAtomContext, index, &headerSize,
216 		&tableMajor, &tableMinor, &headerOffset) != B_OK) {
217 		ERROR("%s: Couldn't parse data header\n", __func__);
218 		pll->ssEnabled = false;
219 		return B_ERROR;
220 	}
221 
222 	union asicSSInfo {
223 		struct _ATOM_ASIC_INTERNAL_SS_INFO info;
224 		struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 info_2;
225 		struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3;
226 	};
227 
228 	union asicSSInfo *ss_info
229 		= (union asicSSInfo*)((uint16*)gAtomContext->bios + headerOffset);
230 
231 	int i;
232 	int indices;
233 	switch (tableMajor) {
234 		case 1:
235 			indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
236 				/ sizeof(ATOM_ASIC_SS_ASSIGNMENT);
237 
238 			for (i = 0; i < indices; i++) {
239 				if (ss_info->info.asSpreadSpectrum[i].ucClockIndication
240 					!= ssID) {
241 					continue;
242 				}
243 				TRACE("%s: ss match found\n", __func__);
244 				if (pll->pixelClock / 10 > B_LENDIAN_TO_HOST_INT32(
245 					ss_info->info.asSpreadSpectrum[i].ulTargetClockRange)) {
246 					TRACE("%s: pixelClock > targetClockRange!\n", __func__);
247 					continue;
248 				}
249 
250 				pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
251 					ss_info->info.asSpreadSpectrum[i].usSpreadSpectrumPercentage
252 					);
253 
254 				pll->ssType
255 					= ss_info->info.asSpreadSpectrum[i].ucSpreadSpectrumMode;
256 				pll->ssRate = B_LENDIAN_TO_HOST_INT16(
257 					ss_info->info.asSpreadSpectrum[i].usSpreadRateInKhz);
258 				pll->ssPercentageDiv = 100;
259 				pll->ssEnabled = true;
260 				return B_OK;
261 			}
262 			break;
263 		case 2:
264 			indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
265 				/ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2);
266 
267 			for (i = 0; i < indices; i++) {
268 				if (ss_info->info_2.asSpreadSpectrum[i].ucClockIndication
269 					!= ssID) {
270 					continue;
271 				}
272 				TRACE("%s: ss match found\n", __func__);
273 				if (pll->pixelClock / 10 > B_LENDIAN_TO_HOST_INT32(
274 					ss_info->info_2.asSpreadSpectrum[i].ulTargetClockRange)) {
275 					TRACE("%s: pixelClock > targetClockRange!\n", __func__);
276 					continue;
277 				}
278 
279 				pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
280 					ss_info
281 						->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage
282 					);
283 
284 				pll->ssType
285 					= ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode;
286 				pll->ssRate = B_LENDIAN_TO_HOST_INT16(
287 					ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz);
288 				pll->ssPercentageDiv = 100;
289 				pll->ssEnabled = true;
290 				return B_OK;
291 			}
292 			break;
293 		case 3:
294 			indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
295 				/ sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3);
296 
297 			for (i = 0; i < indices; i++) {
298 				if (ss_info->info_3.asSpreadSpectrum[i].ucClockIndication
299 					!= ssID) {
300 					continue;
301 				}
302 				TRACE("%s: ss match found\n", __func__);
303 				if (pll->pixelClock / 10 > B_LENDIAN_TO_HOST_INT32(
304 					ss_info->info_3.asSpreadSpectrum[i].ulTargetClockRange)) {
305 					TRACE("%s: pixelClock > targetClockRange!\n", __func__);
306 					continue;
307 				}
308 
309 				pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
310 					ss_info
311 						->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage
312 					);
313 				pll->ssType
314 					= ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode;
315 				pll->ssRate = B_LENDIAN_TO_HOST_INT16(
316 					ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz);
317 
318 				if ((ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode
319 					& SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK) != 0)
320 					pll->ssPercentageDiv = 1000;
321 				else
322 					pll->ssPercentageDiv = 100;
323 
324 				if (ssID == ASIC_INTERNAL_ENGINE_SS
325 					|| ssID == ASIC_INTERNAL_MEMORY_SS)
326 					pll->ssRate /= 100;
327 
328 				pll->ssEnabled = true;
329 				return B_OK;
330 			}
331 			break;
332 		default:
333 			ERROR("%s: Unknown SS table version!\n", __func__);
334 			pll->ssEnabled = false;
335 			return B_ERROR;
336 	}
337 
338 	ERROR("%s: No potential spread spectrum data found!\n", __func__);
339 	pll->ssEnabled = false;
340 	return B_ERROR;
341 }
342 
343 
344 void
345 pll_compute_post_divider(pll_info* pll)
346 {
347 	if ((pll->flags & PLL_USE_POST_DIV) != 0) {
348 		TRACE("%s: using AtomBIOS post divider\n", __func__);
349 		return;
350 	}
351 
352 	uint32 vco;
353 	if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) {
354 		if ((pll->flags & PLL_IS_LCD) != 0)
355 			vco = pll->lcdPllOutMin;
356 		else
357 			vco = pll->pllOutMax;
358 	} else {
359 		if ((pll->flags & PLL_IS_LCD) != 0)
360 			vco = pll->lcdPllOutMax;
361 		else
362 			vco = pll->pllOutMin;
363 	}
364 
365 	TRACE("%s: vco = %" B_PRIu32 "\n", __func__, vco);
366 
367 	uint32 postDivider = vco / pll->adjustedClock;
368 	uint32 tmp = vco % pll->adjustedClock;
369 
370 	if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) {
371 		if (tmp)
372 			postDivider++;
373 	} else {
374 		if (!tmp)
375 			postDivider--;
376 	}
377 
378 	if (postDivider > pll->maxPostDiv)
379 		postDivider = pll->maxPostDiv;
380 	else if (postDivider < pll->minPostDiv)
381 		postDivider = pll->minPostDiv;
382 
383 	pll->postDiv = postDivider;
384 	TRACE("%s: postDiv = %" B_PRIu32 "\n", __func__, postDivider);
385 }
386 
387 
388 /*! Compute values for the fractional feedback divider to match the desired
389  *  pixel clock frequency as closely as possible. Reference and post divider
390  *  values are already filled in (if used).
391  */
392 status_t
393 pll_compute(pll_info* pll)
394 {
395 	radeon_shared_info &info = *gInfo->shared_info;
396 
397 	pll_compute_post_divider(pll);
398 
399 	const uint32 targetClock = pll->adjustedClock;
400 
401 	pll->feedbackDiv = 0;
402 	pll->feedbackDivFrac = 0;
403 
404 	if ((pll->flags & PLL_USE_REF_DIV) != 0) {
405 		TRACE("%s: using AtomBIOS reference divider\n", __func__);
406 	} else {
407 		TRACE("%s: using minimum reference divider\n", __func__);
408 		pll->referenceDiv = pll->minRefDiv;
409 	}
410 
411 	if ((pll->flags & PLL_USE_FRAC_FB_DIV) != 0) {
412 		TRACE("%s: using AtomBIOS fractional feedback divider\n", __func__);
413 
414 		const uint32 numerator = pll->postDiv * pll->referenceDiv
415 			* targetClock;
416 		pll->feedbackDiv = numerator / pll->referenceFreq;
417 		pll->feedbackDivFrac = numerator % pll->referenceFreq;
418 
419 		if (pll->feedbackDiv > pll->maxFeedbackDiv)
420 			pll->feedbackDiv = pll->maxFeedbackDiv;
421 		else if (pll->feedbackDiv < pll->minFeedbackDiv)
422 			pll->feedbackDiv = pll->minFeedbackDiv;
423 
424 		// Put first 2 digits after the decimal point into feedbackDivFrac
425 		pll->feedbackDivFrac
426 			= (100 * pll->feedbackDivFrac) / pll->referenceFreq;
427 
428 		// Now round it to one digit
429 		if (pll->feedbackDivFrac >= 5) {
430 			pll->feedbackDivFrac -= 5;
431 			pll->feedbackDivFrac /= 10;
432 			pll->feedbackDivFrac++;
433 		}
434 		if (pll->feedbackDivFrac >= 10) {
435 			pll->feedbackDiv++;
436 			pll->feedbackDivFrac = 0;
437 		}
438 	} else {
439 		TRACE("%s: performing fractional feedback calculations\n", __func__);
440 
441 		while (pll->referenceDiv <= pll->maxRefDiv) {
442 			// get feedback divider
443 			uint32 retroEncabulator = pll->postDiv * pll->referenceDiv;
444 
445 			retroEncabulator *= targetClock;
446 			pll->feedbackDiv = retroEncabulator / pll->referenceFreq;
447 			pll->feedbackDivFrac
448 				= retroEncabulator % pll->referenceFreq;
449 
450 			if (pll->feedbackDiv > pll->maxFeedbackDiv)
451 				pll->feedbackDiv = pll->maxFeedbackDiv;
452 			else if (pll->feedbackDiv < pll->minFeedbackDiv)
453 				pll->feedbackDiv = pll->minFeedbackDiv;
454 
455 			if (pll->feedbackDivFrac >= (pll->referenceFreq / 2))
456 				pll->feedbackDiv++;
457 
458 			pll->feedbackDivFrac = 0;
459 
460 			if (pll->referenceDiv == 0
461 				|| pll->postDiv == 0
462 				|| targetClock == 0) {
463 				TRACE("%s: Caught division by zero!\n", __func__);
464 				TRACE("%s: referenceDiv %" B_PRIu32 "\n",
465 					__func__, pll->referenceDiv);
466 				TRACE("%s: postDiv      %" B_PRIu32 "\n",
467 					__func__, pll->postDiv);
468 				TRACE("%s: targetClock  %" B_PRIu32 "\n",
469 					__func__, targetClock);
470 				return B_ERROR;
471 			}
472 			uint32 tmp = (pll->referenceFreq * pll->feedbackDiv)
473 				/ (pll->postDiv * pll->referenceDiv);
474 			tmp = (tmp * 1000) / targetClock;
475 
476 			if (tmp > (1000 + (MAX_TOLERANCE / 10)))
477 				pll->referenceDiv++;
478 			else if (tmp >= (1000 - (MAX_TOLERANCE / 10)))
479 				break;
480 			else
481 				pll->referenceDiv++;
482 		}
483 	}
484 
485 	if (pll->referenceDiv == 0 || pll->postDiv == 0) {
486 		TRACE("%s: Caught division by zero of post or reference divider\n",
487 			__func__);
488 		return B_ERROR;
489 	}
490 
491 	uint32 calculatedClock
492 		= ((pll->referenceFreq * pll->feedbackDiv * 10)
493 		+ (pll->referenceFreq * pll->feedbackDivFrac))
494 		/ (pll->referenceDiv * pll->postDiv * 10);
495 
496 	TRACE("%s: Calculated pixel clock of %" B_PRIu32 " based on:\n", __func__,
497 		calculatedClock);
498 	TRACE("%s:   referenceFrequency: %" B_PRIu32 "; "
499 		"referenceDivider: %" B_PRIu32 "\n", __func__, pll->referenceFreq,
500 		pll->referenceDiv);
501 	TRACE("%s:   feedbackDivider: %" B_PRIu32 "; "
502 		"feedbackDividerFrac: %" B_PRIu32 "\n", __func__, pll->feedbackDiv,
503 		pll->feedbackDivFrac);
504 	TRACE("%s:   postDivider: %" B_PRIu32 "\n", __func__, pll->postDiv);
505 
506 	if (pll->adjustedClock != calculatedClock) {
507 		TRACE("%s: pixel clock %" B_PRIu32 " was changed to %" B_PRIu32 "\n",
508 			__func__, pll->adjustedClock, calculatedClock);
509 		pll->pixelClock = calculatedClock;
510 	}
511 
512 	// Calcuate needed SS data on DCE4
513 	if (info.dceMajor >= 4 && pll->ssEnabled) {
514 		if (pll->ssPercentageDiv == 0) {
515 			// Avoid div by 0, shouldn't happen but be mindful of it
516 			TRACE("%s: ssPercentageDiv is less than 0, aborting SS calcualation",
517 				__func__);
518 			pll->ssEnabled = false;
519 			return B_OK;
520 		}
521 		uint32 amount = ((pll->feedbackDiv * 10) + pll->feedbackDivFrac);
522 		amount *= pll->ssPercentage;
523 		amount /= pll->ssPercentageDiv * 100;
524 		pll->ssAmount = (amount / 10) & ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK;
525 		pll->ssAmount |= ((amount - (amount / 10))
526 			<< ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) & ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK;
527 
528 		uint32 centerSpreadMultiplier = 2;
529 		if ((pll->ssType & ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD) != 0)
530 			centerSpreadMultiplier = 4;
531 		pll->ssStep = (centerSpreadMultiplier * amount * pll->referenceDiv
532 			* (pll->ssRate * 2048)) / (125 * 25 * pll->referenceFreq / 100);
533 	}
534 
535 	return B_OK;
536 }
537 
538 
539 void
540 pll_setup_flags(pll_info* pll, uint8 crtcID)
541 {
542 	radeon_shared_info &info = *gInfo->shared_info;
543 	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
544 	uint32 connectorFlags = gConnector[connectorIndex]->flags;
545 
546 	uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
547 
548 	TRACE("%s: CRTC: %" B_PRIu8 ", PLL: %" B_PRIu8 "\n", __func__,
549 		crtcID, pll->id);
550 
551 	if (dceVersion >= 302 && pll->pixelClock > 200000)
552 		pll->flags |= PLL_PREFER_HIGH_FB_DIV;
553 	else
554 		pll->flags |= PLL_PREFER_LOW_REF_DIV;
555 
556 	if (info.chipsetID < RADEON_RV770)
557 		pll->flags |= PLL_PREFER_MINM_OVER_MAXP;
558 
559 	if ((connectorFlags & ATOM_DEVICE_LCD_SUPPORT) != 0) {
560 		pll->flags |= PLL_IS_LCD;
561 
562 		// use reference divider for spread spectrum
563 		TRACE("%s: Spread Spectrum is %" B_PRIu32 "%%\n", __func__,
564 			pll->ssPercentage);
565 		if (pll->ssPercentage > 0) {
566 			if (pll->ssReferenceDiv > 0) {
567 				TRACE("%s: using Spread Spectrum reference divider. "
568 					"refDiv was: %" B_PRIu32 ", now: %" B_PRIu32 "\n",
569 					__func__, pll->referenceDiv, pll->ssReferenceDiv);
570 				pll->flags |= PLL_USE_REF_DIV;
571 				pll->referenceDiv = pll->ssReferenceDiv;
572 
573 				// TODO: IS AVIVO+?
574 				pll->flags |= PLL_USE_FRAC_FB_DIV;
575 			}
576 		}
577 	}
578 
579 	if ((connectorFlags & ATOM_DEVICE_TV_SUPPORT) != 0)
580 		pll->flags |= PLL_PREFER_CLOSEST_LOWER;
581 
582 	if ((info.chipsetFlags & CHIP_APU) != 0) {
583 		// Use fractional feedback on APU's
584 		pll->flags |= PLL_USE_FRAC_FB_DIV;
585 	}
586 }
587 
588 
589 /**
590  * pll_adjust - Ask AtomBIOS if it wants to make adjustments to our pll
591  *
592  * Returns B_OK on successful execution.
593  */
594 status_t
595 pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID)
596 {
597 	radeon_shared_info &info = *gInfo->shared_info;
598 
599 	uint32 pixelClock = pll->pixelClock;
600 		// original as pixel_clock will be adjusted
601 
602 	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
603 	connector_info* connector = gConnector[connectorIndex];
604 
605 	uint32 encoderID = connector->encoder.objectID;
606 	uint32 encoderMode = display_get_encoder_mode(connectorIndex);
607 	uint32 connectorFlags = connector->flags;
608 
609 	uint32 externalEncoderID = 0;
610 	pll->adjustedClock = pll->pixelClock;
611 	if (connector->encoderExternal.isDPBridge)
612 		externalEncoderID = connector->encoderExternal.objectID;
613 
614 	if (info.dceMajor >= 3) {
615 
616 		uint8 tableMajor;
617 		uint8 tableMinor;
618 
619 		int index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll);
620 		if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor)
621 			!= B_OK) {
622 			ERROR("%s: Couldn't find AtomBIOS PLL adjustment\n", __func__);
623 			return B_ERROR;
624 		}
625 
626 		TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
627 			tableMajor, tableMinor);
628 
629 		// Prepare arguments for AtomBIOS call
630 		union adjustPixelClock {
631 			ADJUST_DISPLAY_PLL_PS_ALLOCATION v1;
632 			ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3;
633 		};
634 		union adjustPixelClock args;
635 		memset(&args, 0, sizeof(args));
636 
637 		switch (tableMajor) {
638 			case 1:
639 				switch (tableMinor) {
640 					case 1:
641 					case 2:
642 						args.v1.usPixelClock
643 							= B_HOST_TO_LENDIAN_INT16(pixelClock / 10);
644 						args.v1.ucTransmitterID = encoderID;
645 						args.v1.ucEncodeMode = encoderMode;
646 						if (pll->ssPercentage > 0) {
647 							args.v1.ucConfig
648 								|= ADJUST_DISPLAY_CONFIG_SS_ENABLE;
649 						}
650 
651 						atom_execute_table(gAtomContext, index, (uint32*)&args);
652 						// get returned adjusted clock
653 						pll->adjustedClock
654 							= B_LENDIAN_TO_HOST_INT16(args.v1.usPixelClock);
655 						pll->adjustedClock *= 10;
656 						break;
657 					case 3:
658 						args.v3.sInput.usPixelClock
659 							= B_HOST_TO_LENDIAN_INT16(pixelClock / 10);
660 						args.v3.sInput.ucTransmitterID = encoderID;
661 						args.v3.sInput.ucEncodeMode = encoderMode;
662 						args.v3.sInput.ucDispPllConfig = 0;
663 						if (pll->ssPercentage > 0) {
664 							args.v3.sInput.ucDispPllConfig
665 								|= DISPPLL_CONFIG_SS_ENABLE;
666 						}
667 
668 						// Handle DP adjustments
669 						if (encoderMode == ATOM_ENCODER_MODE_DP
670 							|| encoderMode == ATOM_ENCODER_MODE_DP_MST) {
671 							TRACE("%s: encoderMode is DP\n", __func__);
672 							args.v3.sInput.ucDispPllConfig
673 								|= DISPPLL_CONFIG_COHERENT_MODE;
674 							/* 162000 or 270000 */
675 							uint32 dpLinkSpeed
676 								= dp_get_link_rate(connectorIndex, mode);
677 							/* 16200 or 27000 */
678 							args.v3.sInput.usPixelClock
679 								= B_HOST_TO_LENDIAN_INT16(dpLinkSpeed / 10);
680 						} else if ((connectorFlags & ATOM_DEVICE_DFP_SUPPORT)
681 							!= 0) {
682 							#if 0
683 							if (encoderMode == ATOM_ENCODER_MODE_HDMI) {
684 								/* deep color support */
685 								args.v3.sInput.usPixelClock =
686 									cpu_to_le16((mode->clock * bpc / 8) / 10);
687 							}
688 							#endif
689 							if (pixelClock > 165000) {
690 								args.v3.sInput.ucDispPllConfig
691 									|= DISPPLL_CONFIG_DUAL_LINK;
692 							}
693 							if (1) {	// dig coherent mode?
694 								args.v3.sInput.ucDispPllConfig
695 									|= DISPPLL_CONFIG_COHERENT_MODE;
696 							}
697 						}
698 
699 						args.v3.sInput.ucExtTransmitterID = externalEncoderID;
700 
701 						atom_execute_table(gAtomContext, index, (uint32*)&args);
702 
703 						// get returned adjusted clock
704 						pll->adjustedClock = B_LENDIAN_TO_HOST_INT32(
705 								args.v3.sOutput.ulDispPllFreq);
706 						pll->adjustedClock *= 10;
707 							// convert to kHz for storage
708 
709 						if (args.v3.sOutput.ucRefDiv) {
710 							pll->flags |= PLL_USE_FRAC_FB_DIV;
711 							pll->flags |= PLL_USE_REF_DIV;
712 							pll->referenceDiv = args.v3.sOutput.ucRefDiv;
713 						}
714 						if (args.v3.sOutput.ucPostDiv) {
715 							pll->flags |= PLL_USE_FRAC_FB_DIV;
716 							pll->flags |= PLL_USE_POST_DIV;
717 							pll->postDiv = args.v3.sOutput.ucPostDiv;
718 						}
719 						break;
720 					default:
721 						TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8
722 							" unknown\n", __func__, tableMajor, tableMinor);
723 						return B_ERROR;
724 				}
725 				break;
726 			default:
727 				TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8
728 					" unknown\n", __func__, tableMajor, tableMinor);
729 				return B_ERROR;
730 		}
731 	}
732 
733 	TRACE("%s: was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", __func__,
734 		pixelClock, pll->adjustedClock);
735 
736 	return B_OK;
737 }
738 
739 
740 /*
741  * pll_set - Calculate and set a pll on the crtc provided based on the mode.
742  *
743  * Returns B_OK on successful execution
744  */
745 status_t
746 pll_set(display_mode* mode, uint8 crtcID)
747 {
748 	uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
749 	uint32 encoderMode = display_get_encoder_mode(connectorIndex);
750 	pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
751 	uint32 dp_clock = gConnector[connectorIndex]->dpInfo.linkRate;
752 
753 	pll->ssEnabled = false;
754 
755 	pll->pixelClock = mode->timing.pixel_clock;
756 
757 	radeon_shared_info &info = *gInfo->shared_info;
758 
759 	// Probe for PLL spread spectrum info;
760 	pll->ssPercentage = 0;
761 	pll->ssType = 0;
762 	pll->ssStep = 0;
763 	pll->ssDelay = 0;
764 	pll->ssRange = 0;
765 	pll->ssReferenceDiv = 0;
766 
767 	switch (encoderMode) {
768 		case ATOM_ENCODER_MODE_DP_MST:
769 		case ATOM_ENCODER_MODE_DP:
770 			if (info.dceMajor >= 4)
771 				pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_DP);
772 			else {
773 				if (dp_clock == 162000) {
774 					pll_ppll_ss_probe(pll, ATOM_DP_SS_ID2);
775 					if (!pll->ssEnabled)
776 						pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1);
777 				} else
778 					pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1);
779 			}
780 			break;
781 		case ATOM_ENCODER_MODE_LVDS:
782 			if (info.dceMajor >= 4)
783 				pll_asic_ss_probe(pll, gInfo->lvdsSpreadSpectrumID);
784 			else
785 				pll_ppll_ss_probe(pll, gInfo->lvdsSpreadSpectrumID);
786 			break;
787 		case ATOM_ENCODER_MODE_DVI:
788 			if (info.dceMajor >= 4)
789 				pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_TMDS);
790 			break;
791 		case ATOM_ENCODER_MODE_HDMI:
792 			if (info.dceMajor >= 4)
793 				pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_HDMI);
794 			break;
795 	}
796 
797 	pll_setup_flags(pll, crtcID);
798 		// set up any special flags
799 	pll_adjust(pll, mode, crtcID);
800 		// get any needed clock adjustments, set reference/post dividers
801 	pll_compute(pll);
802 		// compute dividers and spread spectrum
803 
804 	uint8 tableMajor;
805 	uint8 tableMinor;
806 
807 	int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
808 	atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor);
809 
810 	TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
811 		tableMajor, tableMinor);
812 
813 	uint32 bitsPerColor = 8;
814 		// TODO: Digital Depth, EDID 1.4+ on digital displays
815 		// isn't in Haiku edid common code?
816 
817 	// Prepare arguments for AtomBIOS call
818 	union setPixelClock {
819 		SET_PIXEL_CLOCK_PS_ALLOCATION base;
820 		PIXEL_CLOCK_PARAMETERS v1;
821 		PIXEL_CLOCK_PARAMETERS_V2 v2;
822 		PIXEL_CLOCK_PARAMETERS_V3 v3;
823 		PIXEL_CLOCK_PARAMETERS_V5 v5;
824 		PIXEL_CLOCK_PARAMETERS_V6 v6;
825 		PIXEL_CLOCK_PARAMETERS_V7 v7;
826 	};
827 	union setPixelClock args;
828 	memset(&args, 0, sizeof(args));
829 
830 	switch (tableMinor) {
831 		case 1:
832 			args.v1.usPixelClock
833 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
834 			args.v1.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv);
835 			args.v1.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
836 			args.v1.ucFracFbDiv = pll->feedbackDivFrac;
837 			args.v1.ucPostDiv = pll->postDiv;
838 			args.v1.ucPpll = pll->id;
839 			args.v1.ucCRTC = crtcID;
840 			args.v1.ucRefDivSrc = 1;
841 			break;
842 		case 2:
843 			args.v2.usPixelClock
844 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
845 			args.v2.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv);
846 			args.v2.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
847 			args.v2.ucFracFbDiv = pll->feedbackDivFrac;
848 			args.v2.ucPostDiv = pll->postDiv;
849 			args.v2.ucPpll = pll->id;
850 			args.v2.ucCRTC = crtcID;
851 			args.v2.ucRefDivSrc = 1;
852 			break;
853 		case 3:
854 			args.v3.usPixelClock
855 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
856 			args.v3.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv);
857 			args.v3.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
858 			args.v3.ucFracFbDiv = pll->feedbackDivFrac;
859 			args.v3.ucPostDiv = pll->postDiv;
860 			args.v3.ucPpll = pll->id;
861 			args.v3.ucMiscInfo = (pll->id << 2);
862 			if (pll->ssPercentage > 0
863 				&& (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) {
864 				args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC;
865 			}
866 			args.v3.ucTransmitterId
867 				= gConnector[connectorIndex]->encoder.objectID;
868 			args.v3.ucEncoderMode = encoderMode;
869 			break;
870 		case 5:
871 			args.v5.ucCRTC = crtcID;
872 			args.v5.usPixelClock
873 				= B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10);
874 			args.v5.ucRefDiv = pll->referenceDiv;
875 			args.v5.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
876 			args.v5.ulFbDivDecFrac
877 				= B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000);
878 			args.v5.ucPostDiv = pll->postDiv;
879 			args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */
880 			if (pll->ssPercentage > 0
881 				&& (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) {
882 				args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC;
883 			}
884 			if (encoderMode == ATOM_ENCODER_MODE_HDMI) {
885 				switch (bitsPerColor) {
886 					case 8:
887 					default:
888 						args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
889 						break;
890 					case 10:
891 						// AMD notes the atombios define is incorrect here
892 						args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_32BPP;
893 						break;
894 					case 12:
895 						// AMD notes the atombios define is incorrect here
896 						args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
897 						break;
898 				}
899 			}
900 			args.v5.ucTransmitterID
901 				= gConnector[connectorIndex]->encoder.objectID;
902 			args.v5.ucEncoderMode = encoderMode;
903 			args.v5.ucPpll = pll->id;
904 			break;
905 		case 6:
906 			args.v6.ulDispEngClkFreq
907 				= B_HOST_TO_LENDIAN_INT32(crtcID << 24 | pll->pixelClock / 10);
908 			args.v6.ucRefDiv = pll->referenceDiv;
909 			args.v6.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
910 			args.v6.ulFbDivDecFrac
911 				= B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000);
912 			args.v6.ucPostDiv = pll->postDiv;
913 			args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */
914 			if (pll->ssPercentage > 0
915 				&& (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) {
916 				args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC;
917 			}
918 			if (encoderMode == ATOM_ENCODER_MODE_HDMI) {
919 				switch (bitsPerColor) {
920 					case 8:
921 					default:
922 						args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
923 						break;
924 					case 10:
925 						args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP_V6;
926 						break;
927 					case 12:
928 						args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP_V6;
929 						break;
930 					case 16:
931 						args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
932 						break;
933 				}
934 			}
935 			args.v6.ucTransmitterID
936 				= gConnector[connectorIndex]->encoder.objectID;
937 			args.v6.ucEncoderMode = encoderMode;
938 			args.v6.ucPpll = pll->id;
939 			break;
940 		case 7:
941 			args.v7.ulPixelClock
942 				= B_HOST_TO_LENDIAN_INT32(pll->pixelClock / 10);
943 			args.v7.ucMiscInfo = 0;
944 			if (gConnector[connectorIndex]->type == VIDEO_CONNECTOR_DVID
945 				&& pll->pixelClock > 165000) {
946 				args.v7.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_DVI_DUALLINK_EN;
947 			}
948 			args.v7.ucCRTC = crtcID;
949 			if (encoderMode == ATOM_ENCODER_MODE_HDMI) {
950 				switch (bitsPerColor) {
951 					case 8:
952 					default:
953 						args.v7.ucDeepColorRatio
954 							= PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_DIS;
955 						break;
956 					case 10:
957 						args.v7.ucDeepColorRatio
958 							= PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_5_4;
959 						break;
960 					case 12:
961 						args.v7.ucDeepColorRatio
962 							= PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_3_2;
963 						break;
964 					case 16:
965 						args.v7.ucDeepColorRatio
966 							= PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_2_1;
967 						break;
968 				}
969 			}
970 			args.v7.ucTransmitterID
971 				= gConnector[connectorIndex]->encoder.objectID;
972 			args.v7.ucEncoderMode = encoderMode;
973 			args.v7.ucPpll = pll->id;
974 			break;
975 		default:
976 			TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " TODO\n",
977 				__func__, tableMajor, tableMinor);
978 			return B_ERROR;
979 	}
980 
981 	TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n",
982 		__func__, pll->pixelClock, mode->timing.pixel_clock);
983 
984 	status_t result = atom_execute_table(gAtomContext, index, (uint32*)&args);
985 
986 	if (pll->ssEnabled)
987 		display_crtc_ss(pll, ATOM_ENABLE);
988 	else
989 		display_crtc_ss(pll, ATOM_DISABLE);
990 
991 	return result;
992 }
993 
994 
995 /**
996  * pll_set_external - Sets external default pll via SetPixelClock
997  *
998  * Applies a clock frequency to card's external PLL clock.
999  */
1000 status_t
1001 pll_set_external(uint32 clock)
1002 {
1003 	TRACE("%s: set external pll clock to %" B_PRIu32 "\n", __func__, clock);
1004 
1005 	if (clock == 0)
1006 		ERROR("%s: Warning: default display clock is 0?\n", __func__);
1007 
1008 	// also known as PLL display engineering
1009 	uint8 tableMajor;
1010 	uint8 tableMinor;
1011 
1012 	int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
1013 	atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor);
1014 
1015 	TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
1016 		tableMajor, tableMinor);
1017 
1018 	union setPixelClock {
1019 		SET_PIXEL_CLOCK_PS_ALLOCATION base;
1020 		PIXEL_CLOCK_PARAMETERS v1;
1021 		PIXEL_CLOCK_PARAMETERS_V2 v2;
1022 		PIXEL_CLOCK_PARAMETERS_V3 v3;
1023 		PIXEL_CLOCK_PARAMETERS_V5 v5;
1024 		PIXEL_CLOCK_PARAMETERS_V6 v6;
1025 		PIXEL_CLOCK_PARAMETERS_V7 v7;
1026 	};
1027 	union setPixelClock args;
1028 	memset(&args, 0, sizeof(args));
1029 
1030 	radeon_shared_info &info = *gInfo->shared_info;
1031 	uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
1032 	switch (tableMajor) {
1033 		case 1:
1034 			switch (tableMinor) {
1035 				case 5:
1036 					// If the default DC PLL clock is specified,
1037 					// SetPixelClock provides the dividers.
1038 					args.v5.ucCRTC = ATOM_CRTC_INVALID;
1039 					args.v5.usPixelClock = B_HOST_TO_LENDIAN_INT16(clock / 10);
1040 					args.v5.ucPpll = ATOM_DCPLL;
1041 					break;
1042 				case 6:
1043 					// If the default DC PLL clock is specified,
1044 					// SetPixelClock provides the dividers.
1045 					args.v6.ulDispEngClkFreq
1046 						= B_HOST_TO_LENDIAN_INT32(clock / 10);
1047 					if (dceVersion == 601)
1048 						args.v6.ucPpll = ATOM_EXT_PLL1;
1049 					else if (dceVersion >= 600)
1050 						args.v6.ucPpll = ATOM_PPLL0;
1051 					else
1052 						args.v6.ucPpll = ATOM_DCPLL;
1053 					break;
1054 				default:
1055 					ERROR("%s: Unknown table version %" B_PRIu8
1056 						".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1057 			}
1058 			break;
1059 		default:
1060 			ERROR("%s: Unknown table version %" B_PRIu8
1061 						".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1062 	}
1063 	return atom_execute_table(gAtomContext, index, (uint32*)&args);
1064 }
1065 
1066 
1067 /**
1068  * pll_set_dce - Sets external default pll via DCE Clock Allocation
1069  *
1070  * Applies a clock frequency to card's external PLL clock via SetDCEClock
1071  * Used on Polaris.
1072  */
1073 status_t
1074 pll_set_dce(uint32 clock, uint8 clockType, uint8 clockSource)
1075 {
1076 	TRACE("%s: set external pll clock to %" B_PRIu32 "\n", __func__, clock);
1077 
1078 	if (clock == 0)
1079 		ERROR("%s: Warning: default display clock is 0?\n", __func__);
1080 
1081 	uint8 tableMajor;
1082 	uint8 tableMinor;
1083 
1084 	int index = GetIndexIntoMasterTable(COMMAND, SetDCEClock);
1085 	atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor);
1086 
1087 	TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
1088 		tableMajor, tableMinor);
1089 
1090 	union setDCEClock {
1091 	    SET_DCE_CLOCK_PS_ALLOCATION_V1_1 v1;
1092 	    SET_DCE_CLOCK_PS_ALLOCATION_V2_1 v2;
1093 	};
1094 	union setDCEClock args;
1095 	memset(&args, 0, sizeof(args));
1096 
1097 	switch (tableMajor) {
1098 		case 2:
1099 			switch (tableMinor) {
1100 				case 1:
1101 					args.v2.asParam.ulDCEClkFreq
1102 						= B_HOST_TO_LENDIAN_INT32(clock / 10);
1103 					args.v2.asParam.ucDCEClkType = clockType;
1104 					args.v2.asParam.ucDCEClkSrc = clockSource;
1105 					break;
1106 				default:
1107 					ERROR("%s: Unknown table version %" B_PRIu8
1108 						".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1109 					return B_ERROR;
1110 			}
1111 			break;
1112 		default:
1113 			ERROR("%s: Unknown table version %" B_PRIu8
1114 				".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1115 			return B_ERROR;
1116 	}
1117 	return atom_execute_table(gAtomContext, index, (uint32*)&args);
1118 }
1119 
1120 
1121 /**
1122  * pll_external_init - Sets external default pll to sane value
1123  *
1124  * Takes the AtomBIOS ulDefaultDispEngineClkFreq and applies it
1125  * back to the card's external PLL clock via SetPixelClock
1126  */
1127 void
1128 pll_external_init()
1129 {
1130 	radeon_shared_info &info = *gInfo->shared_info;
1131 
1132 	if (info.dceMajor >= 12) {
1133 		pll_set_dce(gInfo->displayClockFrequency,
1134 			DCE_CLOCK_TYPE_DISPCLK, ATOM_GCK_DFS);
1135 		pll_set_dce(gInfo->displayClockFrequency,
1136 			DCE_CLOCK_TYPE_DPREFCLK, ATOM_GCK_DFS);
1137 	} else if (info.dceMajor >= 6) {
1138 		pll_set_external(gInfo->displayClockFrequency);
1139 	} else if (info.dceMajor >= 4) {
1140 		// Create our own pseudo pll
1141 		pll_info pll;
1142 		pll.pixelClock = gInfo->displayClockFrequency;
1143 
1144 		pll_asic_ss_probe(&pll, ASIC_INTERNAL_SS_ON_DCPLL);
1145 		if (pll.ssEnabled)
1146 			display_crtc_ss(&pll, ATOM_DISABLE);
1147 		pll_set_external(pll.pixelClock);
1148 		if (pll.ssEnabled)
1149 			display_crtc_ss(&pll, ATOM_ENABLE);
1150 	}
1151 }
1152 
1153 
1154 /**
1155  * pll_usage_mask - Calculate which PLL's are in use
1156  *
1157  * Returns the mask of which PLL's are in use
1158  */
1159 uint32
1160 pll_usage_mask()
1161 {
1162 	uint32 pllMask = 0;
1163 	for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
1164 		if (gConnector[id]->valid == true) {
1165 			pll_info* pll = &gConnector[id]->encoder.pll;
1166 			if (pll->id != ATOM_PPLL_INVALID)
1167 				pllMask |= (1 << pll->id);
1168 		}
1169 	}
1170 	return pllMask;
1171 }
1172 
1173 
1174 /**
1175  * pll_usage_count - Find number of connectors attached to a PLL
1176  *
1177  * Returns the count of connectors using specified PLL
1178  */
1179 uint32
1180 pll_usage_count(uint32 pllID)
1181 {
1182 	uint32 pllCount = 0;
1183 	for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
1184 		if (gConnector[id]->valid == true) {
1185 			pll_info* pll = &gConnector[id]->encoder.pll;
1186 			if (pll->id == pllID)
1187 				pllCount++;
1188 		}
1189 	}
1190 
1191 	return pllCount;
1192 }
1193 
1194 
1195 /**
1196  * pll_shared_dp - Find any existing PLL's used for DP connectors
1197  *
1198  * Returns the PLL shared by other DP connectors
1199  */
1200 uint32
1201 pll_shared_dp()
1202 {
1203 	for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
1204 		if (gConnector[id]->valid == true) {
1205 			if (connector_is_dp(id)) {
1206 				pll_info* pll = &gConnector[id]->encoder.pll;
1207 				return pll->id;
1208 			}
1209 		}
1210 	}
1211 	return ATOM_PPLL_INVALID;
1212 }
1213 
1214 
1215 /**
1216  * pll_next_available - Find the next available PLL
1217  *
1218  * Returns the next available PLL
1219  */
1220 uint32
1221 pll_next_available()
1222 {
1223 	radeon_shared_info &info = *gInfo->shared_info;
1224 	uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
1225 
1226 	uint32 pllMask = pll_usage_mask();
1227 
1228 	if (dceVersion == 802 || dceVersion == 601) {
1229 		if (!(pllMask & (1 << ATOM_PPLL0)))
1230 			return ATOM_PPLL0;
1231 	}
1232 
1233 	if (!(pllMask & (1 << ATOM_PPLL1)))
1234 		return ATOM_PPLL1;
1235 	if (dceVersion != 601) {
1236 		if (!(pllMask & (1 << ATOM_PPLL2)))
1237 			return ATOM_PPLL2;
1238 	}
1239 	// TODO: If this starts happening, we likely need to
1240 	// add the sharing of PLL's with identical clock rates
1241 	// (see radeon_atom_pick_pll in drm)
1242 	ERROR("%s: Unable to find a PLL! (0x%" B_PRIX32 ")\n", __func__, pllMask);
1243 	return ATOM_PPLL_INVALID;
1244 }
1245 
1246 
1247 status_t
1248 pll_pick(uint32 connectorIndex)
1249 {
1250 	pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
1251 	radeon_shared_info &info = *gInfo->shared_info;
1252 	uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
1253 
1254 	bool linkB = gConnector[connectorIndex]->encoder.linkEnumeration
1255 		== GRAPH_OBJECT_ENUM_ID2 ? true : false;
1256 
1257 	pll->id = ATOM_PPLL_INVALID;
1258 
1259 	// DCE 6.1 APU, UNIPHYA requires PLL2
1260 	if (gConnector[connectorIndex]->encoder.objectID
1261 		== ENCODER_OBJECT_ID_INTERNAL_UNIPHY && !linkB) {
1262 		pll->id = ATOM_PPLL2;
1263 		return B_OK;
1264 	}
1265 
1266 	if (connector_is_dp(connectorIndex)) {
1267 		// If DP external clock, set to invalid except on DCE 6.1
1268 		if (gInfo->dpExternalClock && !(dceVersion == 601)) {
1269 			pll->id = ATOM_PPLL_INVALID;
1270 			return B_OK;
1271 		}
1272 
1273 		// DCE 6.1+, we can share DP PLLs. See if any other DP connectors
1274 		// have been assigned a PLL yet.
1275 		if (dceVersion >= 601) {
1276 			pll->id = pll_shared_dp();
1277 			if (pll->id != ATOM_PPLL_INVALID)
1278 				return B_OK;
1279 			// Continue through to pll_next_available
1280 		} else if (dceVersion == 600) {
1281 			pll->id = ATOM_PPLL0;
1282 			return B_OK;
1283 		} else if (info.dceMajor >= 5) {
1284 			pll->id = ATOM_DCPLL;
1285 			return B_OK;
1286 		}
1287 	}
1288 
1289 	if (info.dceMajor >= 4) {
1290 		pll->id = pll_next_available();
1291 		return B_OK;
1292 	}
1293 
1294 	// TODO: Should return the CRTCID here.
1295 	pll->id = ATOM_PPLL1;
1296 	return B_OK;
1297 }
1298