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
29extern "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
70status_t
71pll_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
162status_t
163pll_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
206status_t
207pll_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
344void
345pll_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 */
392status_t
393pll_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
539void
540pll_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 */
594status_t
595pll_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 */
745status_t
746pll_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 */
1000status_t
1001pll_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 */
1073status_t
1074pll_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 */
1127void
1128pll_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 */
1159uint32
1160pll_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 */
1179uint32
1180pll_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 */
1200uint32
1201pll_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 */
1220uint32
1221pll_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
1247status_t
1248pll_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