vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / radeon_hd / pll.cpp
blob0949d9a5bf730c0b57dc7250f8a317afe7f3ebb1
1 /*
2 * Copyright 2006-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Alexander von Gluck, kallisti5@unixzen.com
7 */
10 #include "pll.h"
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <math.h>
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"
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
35 #define ERROR(x...) _sPrintf("radeon_hd: " x)
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
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.
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.
62 * The resulting output pixel clock frequency is then:
64 * feedbackDiv + (feedbackDivFrac/10)
65 * f_out = referenceFreq * ------------------------------------
66 * referenceDiv * postDiv
70 status_t
71 pll_limit_probe(pll_info* pll)
73 uint8 tableMajor;
74 uint8 tableMinor;
75 uint16 tableOffset;
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;
84 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
85 tableMajor, tableMinor);
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;
95 union atomFirmwareInfo* firmwareInfo
96 = (union atomFirmwareInfo*)(gAtomContext->bios + tableOffset);
98 /* pixel clock limits */
99 pll->referenceFreq
100 = B_LENDIAN_TO_HOST_INT16(firmwareInfo->info.usReferenceClock) * 10;
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;
112 pll->pllOutMax
113 = B_LENDIAN_TO_HOST_INT32(
114 firmwareInfo->info.ulMaxPixelClockPLL_Output) * 10;
116 if (tableMinor >= 4) {
117 pll->lcdPllOutMin
118 = B_LENDIAN_TO_HOST_INT16(
119 firmwareInfo->info_14.usLcdMinPixelClockPLL_Output) * 1000;
121 if (pll->lcdPllOutMin == 0)
122 pll->lcdPllOutMin = pll->pllOutMin;
124 pll->lcdPllOutMax
125 = B_LENDIAN_TO_HOST_INT16(
126 firmwareInfo->info_14.usLcdMaxPixelClockPLL_Output) * 1000;
128 if (pll->lcdPllOutMax == 0)
129 pll->lcdPllOutMax = pll->pllOutMax;
131 } else {
132 pll->lcdPllOutMin = pll->pllOutMin;
133 pll->lcdPllOutMax = pll->pllOutMax;
136 if (pll->pllOutMin == 0) {
137 pll->pllOutMin = 64800 * 10;
138 // Avivo+ limit
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;
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;
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);
158 return B_OK;
162 status_t
163 pll_ppll_ss_probe(pll_info* pll, uint32 ssID)
165 uint8 tableMajor;
166 uint8 tableMinor;
167 uint16 headerOffset;
168 uint16 headerSize;
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;
178 struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info
179 = (struct _ATOM_SPREAD_SPECTRUM_INFO*)((uint16*)gAtomContext->bios
180 + headerOffset);
182 int indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
183 / sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT);
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;
201 pll->ssEnabled = false;
202 return B_ERROR;
206 status_t
207 pll_asic_ss_probe(pll_info* pll, uint32 ssID)
209 uint8 tableMajor;
210 uint8 tableMinor;
211 uint16 headerOffset;
212 uint16 headerSize;
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;
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;
228 union asicSSInfo *ss_info
229 = (union asicSSInfo*)((uint16*)gAtomContext->bios + headerOffset);
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);
238 for (i = 0; i < indices; i++) {
239 if (ss_info->info.asSpreadSpectrum[i].ucClockIndication
240 != ssID) {
241 continue;
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;
250 pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
251 ss_info->info.asSpreadSpectrum[i].usSpreadSpectrumPercentage
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;
262 break;
263 case 2:
264 indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
265 / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2);
267 for (i = 0; i < indices; i++) {
268 if (ss_info->info_2.asSpreadSpectrum[i].ucClockIndication
269 != ssID) {
270 continue;
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;
279 pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
280 ss_info
281 ->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage
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;
292 break;
293 case 3:
294 indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER))
295 / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3);
297 for (i = 0; i < indices; i++) {
298 if (ss_info->info_3.asSpreadSpectrum[i].ucClockIndication
299 != ssID) {
300 continue;
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;
309 pll->ssPercentage = B_LENDIAN_TO_HOST_INT16(
310 ss_info
311 ->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage
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);
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;
324 if (ssID == ASIC_INTERNAL_ENGINE_SS
325 || ssID == ASIC_INTERNAL_MEMORY_SS)
326 pll->ssRate /= 100;
328 pll->ssEnabled = true;
329 return B_OK;
331 break;
332 default:
333 ERROR("%s: Unknown SS table version!\n", __func__);
334 pll->ssEnabled = false;
335 return B_ERROR;
338 ERROR("%s: No potential spread spectrum data found!\n", __func__);
339 pll->ssEnabled = false;
340 return B_ERROR;
344 void
345 pll_compute_post_divider(pll_info* pll)
347 if ((pll->flags & PLL_USE_POST_DIV) != 0) {
348 TRACE("%s: using AtomBIOS post divider\n", __func__);
349 return;
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;
365 TRACE("%s: vco = %" B_PRIu32 "\n", __func__, vco);
367 uint32 postDivider = vco / pll->adjustedClock;
368 uint32 tmp = vco % pll->adjustedClock;
370 if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) {
371 if (tmp)
372 postDivider++;
373 } else {
374 if (!tmp)
375 postDivider--;
378 if (postDivider > pll->maxPostDiv)
379 postDivider = pll->maxPostDiv;
380 else if (postDivider < pll->minPostDiv)
381 postDivider = pll->minPostDiv;
383 pll->postDiv = postDivider;
384 TRACE("%s: postDiv = %" B_PRIu32 "\n", __func__, postDivider);
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).
392 status_t
393 pll_compute(pll_info* pll)
395 radeon_shared_info &info = *gInfo->shared_info;
397 pll_compute_post_divider(pll);
399 const uint32 targetClock = pll->adjustedClock;
401 pll->feedbackDiv = 0;
402 pll->feedbackDivFrac = 0;
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;
411 if ((pll->flags & PLL_USE_FRAC_FB_DIV) != 0) {
412 TRACE("%s: using AtomBIOS fractional feedback divider\n", __func__);
414 const uint32 numerator = pll->postDiv * pll->referenceDiv
415 * targetClock;
416 pll->feedbackDiv = numerator / pll->referenceFreq;
417 pll->feedbackDivFrac = numerator % pll->referenceFreq;
419 if (pll->feedbackDiv > pll->maxFeedbackDiv)
420 pll->feedbackDiv = pll->maxFeedbackDiv;
421 else if (pll->feedbackDiv < pll->minFeedbackDiv)
422 pll->feedbackDiv = pll->minFeedbackDiv;
424 // Put first 2 digits after the decimal point into feedbackDivFrac
425 pll->feedbackDivFrac
426 = (100 * pll->feedbackDivFrac) / pll->referenceFreq;
428 // Now round it to one digit
429 if (pll->feedbackDivFrac >= 5) {
430 pll->feedbackDivFrac -= 5;
431 pll->feedbackDivFrac /= 10;
432 pll->feedbackDivFrac++;
434 if (pll->feedbackDivFrac >= 10) {
435 pll->feedbackDiv++;
436 pll->feedbackDivFrac = 0;
438 } else {
439 TRACE("%s: performing fractional feedback calculations\n", __func__);
441 while (pll->referenceDiv <= pll->maxRefDiv) {
442 // get feedback divider
443 uint32 retroEncabulator = pll->postDiv * pll->referenceDiv;
445 retroEncabulator *= targetClock;
446 pll->feedbackDiv = retroEncabulator / pll->referenceFreq;
447 pll->feedbackDivFrac
448 = retroEncabulator % pll->referenceFreq;
450 if (pll->feedbackDiv > pll->maxFeedbackDiv)
451 pll->feedbackDiv = pll->maxFeedbackDiv;
452 else if (pll->feedbackDiv < pll->minFeedbackDiv)
453 pll->feedbackDiv = pll->minFeedbackDiv;
455 if (pll->feedbackDivFrac >= (pll->referenceFreq / 2))
456 pll->feedbackDiv++;
458 pll->feedbackDivFrac = 0;
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;
472 uint32 tmp = (pll->referenceFreq * pll->feedbackDiv)
473 / (pll->postDiv * pll->referenceDiv);
474 tmp = (tmp * 1000) / targetClock;
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++;
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;
491 uint32 calculatedClock
492 = ((pll->referenceFreq * pll->feedbackDiv * 10)
493 + (pll->referenceFreq * pll->feedbackDivFrac))
494 / (pll->referenceDiv * pll->postDiv * 10);
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);
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;
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;
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;
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);
535 return B_OK;
539 void
540 pll_setup_flags(pll_info* pll, uint8 crtcID)
542 radeon_shared_info &info = *gInfo->shared_info;
543 uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
544 uint32 connectorFlags = gConnector[connectorIndex]->flags;
546 uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
548 TRACE("%s: CRTC: %" B_PRIu8 ", PLL: %" B_PRIu8 "\n", __func__,
549 crtcID, pll->id);
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;
556 if (info.chipsetID < RADEON_RV770)
557 pll->flags |= PLL_PREFER_MINM_OVER_MAXP;
559 if ((connectorFlags & ATOM_DEVICE_LCD_SUPPORT) != 0) {
560 pll->flags |= PLL_IS_LCD;
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;
573 // TODO: IS AVIVO+?
574 pll->flags |= PLL_USE_FRAC_FB_DIV;
579 if ((connectorFlags & ATOM_DEVICE_TV_SUPPORT) != 0)
580 pll->flags |= PLL_PREFER_CLOSEST_LOWER;
582 if ((info.chipsetFlags & CHIP_APU) != 0) {
583 // Use fractional feedback on APU's
584 pll->flags |= PLL_USE_FRAC_FB_DIV;
590 * pll_adjust - Ask AtomBIOS if it wants to make adjustments to our pll
592 * Returns B_OK on successful execution.
594 status_t
595 pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID)
597 radeon_shared_info &info = *gInfo->shared_info;
599 uint32 pixelClock = pll->pixelClock;
600 // original as pixel_clock will be adjusted
602 uint32 connectorIndex = gDisplay[crtcID]->connectorIndex;
603 connector_info* connector = gConnector[connectorIndex];
605 uint32 encoderID = connector->encoder.objectID;
606 uint32 encoderMode = display_get_encoder_mode(connectorIndex);
607 uint32 connectorFlags = connector->flags;
609 uint32 externalEncoderID = 0;
610 pll->adjustedClock = pll->pixelClock;
611 if (connector->encoderExternal.isDPBridge)
612 externalEncoderID = connector->encoderExternal.objectID;
614 if (info.dceMajor >= 3) {
616 uint8 tableMajor;
617 uint8 tableMinor;
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;
626 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
627 tableMajor, tableMinor);
629 // Prepare arguments for AtomBIOS call
630 union adjustPixelClock {
631 ADJUST_DISPLAY_PLL_PS_ALLOCATION v1;
632 ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3;
634 union adjustPixelClock args;
635 memset(&args, 0, sizeof(args));
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;
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;
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);
688 #endif
689 if (pixelClock > 165000) {
690 args.v3.sInput.ucDispPllConfig
691 |= DISPPLL_CONFIG_DUAL_LINK;
693 if (1) { // dig coherent mode?
694 args.v3.sInput.ucDispPllConfig
695 |= DISPPLL_CONFIG_COHERENT_MODE;
699 args.v3.sInput.ucExtTransmitterID = externalEncoderID;
701 atom_execute_table(gAtomContext, index, (uint32*)&args);
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
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;
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;
719 break;
720 default:
721 TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8
722 " unknown\n", __func__, tableMajor, tableMinor);
723 return B_ERROR;
725 break;
726 default:
727 TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8
728 " unknown\n", __func__, tableMajor, tableMinor);
729 return B_ERROR;
733 TRACE("%s: was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", __func__,
734 pixelClock, pll->adjustedClock);
736 return B_OK;
741 * pll_set - Calculate and set a pll on the crtc provided based on the mode.
743 * Returns B_OK on successful execution
745 status_t
746 pll_set(display_mode* mode, uint8 crtcID)
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;
753 pll->ssEnabled = false;
755 pll->pixelClock = mode->timing.pixel_clock;
757 radeon_shared_info &info = *gInfo->shared_info;
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;
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);
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;
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
804 uint8 tableMajor;
805 uint8 tableMinor;
807 int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
808 atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor);
810 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
811 tableMajor, tableMinor);
813 uint32 bitsPerColor = 8;
814 // TODO: Digital Depth, EDID 1.4+ on digital displays
815 // isn't in Haiku edid common code?
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;
827 union setPixelClock args;
828 memset(&args, 0, sizeof(args));
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;
866 args.v3.ucTransmitterId
867 = gConnector[connectorIndex]->encoder.objectID;
868 args.v3.ucEncoderMode = display_get_encoder_mode(connectorIndex);
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;
884 switch (bitsPerColor) {
885 case 8:
886 default:
887 args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP;
888 break;
889 case 10:
890 args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP;
891 break;
893 args.v5.ucTransmitterID
894 = gConnector[connectorIndex]->encoder.objectID;
895 args.v5.ucEncoderMode
896 = display_get_encoder_mode(connectorIndex);
897 args.v5.ucPpll = pll->id;
898 break;
899 case 6:
900 args.v6.ulDispEngClkFreq
901 = B_HOST_TO_LENDIAN_INT32(crtcID << 24 | pll->pixelClock / 10);
902 args.v6.ucRefDiv = pll->referenceDiv;
903 args.v6.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv);
904 args.v6.ulFbDivDecFrac
905 = B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000);
906 args.v6.ucPostDiv = pll->postDiv;
907 args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */
908 if (pll->ssPercentage > 0
909 && (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) {
910 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC;
912 switch (bitsPerColor) {
913 case 8:
914 default:
915 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP;
916 break;
917 case 10:
918 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP;
919 break;
920 case 12:
921 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP;
922 break;
923 case 16:
924 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP;
925 break;
927 args.v6.ucTransmitterID
928 = gConnector[connectorIndex]->encoder.objectID;
929 args.v6.ucEncoderMode = display_get_encoder_mode(connectorIndex);
930 args.v6.ucPpll = pll->id;
931 break;
932 case 7:
933 args.v7.ulPixelClock
934 = B_HOST_TO_LENDIAN_INT32(pll->pixelClock / 10);
935 args.v7.ucMiscInfo = 0;
936 if (gConnector[connectorIndex]->type == VIDEO_CONNECTOR_DVID
937 && pll->pixelClock > 165000) {
938 args.v7.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_DVI_DUALLINK_EN;
940 args.v7.ucCRTC = crtcID;
941 if (encoderMode == ATOM_ENCODER_MODE_HDMI) {
942 switch (bitsPerColor) {
943 case 8:
944 default:
945 args.v7.ucDeepColorRatio
946 = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_DIS;
947 break;
948 case 10:
949 args.v7.ucDeepColorRatio
950 = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_5_4;
951 break;
952 case 12:
953 args.v7.ucDeepColorRatio
954 = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_3_2;
955 break;
956 case 16:
957 args.v7.ucDeepColorRatio
958 = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_2_1;
959 break;
962 args.v7.ucTransmitterID
963 = gConnector[connectorIndex]->encoder.objectID;
964 args.v7.ucEncoderMode = display_get_encoder_mode(connectorIndex);
965 args.v7.ucPpll = pll->id;
966 break;
967 default:
968 TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " TODO\n",
969 __func__, tableMajor, tableMinor);
970 return B_ERROR;
973 TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n",
974 __func__, pll->pixelClock, mode->timing.pixel_clock);
976 status_t result = atom_execute_table(gAtomContext, index, (uint32*)&args);
978 if (pll->ssEnabled)
979 display_crtc_ss(pll, ATOM_ENABLE);
980 else
981 display_crtc_ss(pll, ATOM_DISABLE);
983 return result;
988 * pll_set_external - Sets external default pll via SetPixelClock
990 * Applies a clock frequency to card's external PLL clock.
992 status_t
993 pll_set_external(uint32 clock)
995 TRACE("%s: set external pll clock to %" B_PRIu32 "\n", __func__, clock);
997 if (clock == 0)
998 ERROR("%s: Warning: default display clock is 0?\n", __func__);
1000 // also known as PLL display engineering
1001 uint8 tableMajor;
1002 uint8 tableMinor;
1004 int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
1005 atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor);
1007 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
1008 tableMajor, tableMinor);
1010 union setPixelClock {
1011 SET_PIXEL_CLOCK_PS_ALLOCATION base;
1012 PIXEL_CLOCK_PARAMETERS v1;
1013 PIXEL_CLOCK_PARAMETERS_V2 v2;
1014 PIXEL_CLOCK_PARAMETERS_V3 v3;
1015 PIXEL_CLOCK_PARAMETERS_V5 v5;
1016 PIXEL_CLOCK_PARAMETERS_V6 v6;
1017 PIXEL_CLOCK_PARAMETERS_V7 v7;
1019 union setPixelClock args;
1020 memset(&args, 0, sizeof(args));
1022 radeon_shared_info &info = *gInfo->shared_info;
1023 uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
1024 switch (tableMajor) {
1025 case 1:
1026 switch (tableMinor) {
1027 case 5:
1028 // If the default DC PLL clock is specified,
1029 // SetPixelClock provides the dividers.
1030 args.v5.ucCRTC = ATOM_CRTC_INVALID;
1031 args.v5.usPixelClock = B_HOST_TO_LENDIAN_INT16(clock / 10);
1032 args.v5.ucPpll = ATOM_DCPLL;
1033 break;
1034 case 6:
1035 // If the default DC PLL clock is specified,
1036 // SetPixelClock provides the dividers.
1037 args.v6.ulDispEngClkFreq
1038 = B_HOST_TO_LENDIAN_INT32(clock / 10);
1039 if (dceVersion == 601)
1040 args.v6.ucPpll = ATOM_EXT_PLL1;
1041 else if (dceVersion >= 600)
1042 args.v6.ucPpll = ATOM_PPLL0;
1043 else
1044 args.v6.ucPpll = ATOM_DCPLL;
1045 break;
1046 default:
1047 ERROR("%s: Unknown table version %" B_PRIu8
1048 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1050 break;
1051 default:
1052 ERROR("%s: Unknown table version %" B_PRIu8
1053 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1055 return atom_execute_table(gAtomContext, index, (uint32*)&args);
1060 * pll_set_dce - Sets external default pll via DCE Clock Allocation
1062 * Applies a clock frequency to card's external PLL clock via SetDCEClock
1063 * Used on Polaris.
1065 status_t
1066 pll_set_dce(uint32 clock, uint8 clockType, uint8 clockSource)
1068 TRACE("%s: set external pll clock to %" B_PRIu32 "\n", __func__, clock);
1070 if (clock == 0)
1071 ERROR("%s: Warning: default display clock is 0?\n", __func__);
1073 uint8 tableMajor;
1074 uint8 tableMinor;
1076 int index = GetIndexIntoMasterTable(COMMAND, SetDCEClock);
1077 atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor);
1079 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__,
1080 tableMajor, tableMinor);
1082 union setDCEClock {
1083 SET_DCE_CLOCK_PS_ALLOCATION_V1_1 v1;
1084 SET_DCE_CLOCK_PS_ALLOCATION_V2_1 v2;
1086 union setDCEClock args;
1087 memset(&args, 0, sizeof(args));
1089 switch (tableMajor) {
1090 case 2:
1091 switch (tableMinor) {
1092 case 1:
1093 args.v2.asParam.ulDCEClkFreq
1094 = B_HOST_TO_LENDIAN_INT32(clock / 10);
1095 args.v2.asParam.ucDCEClkType = clockType;
1096 args.v2.asParam.ucDCEClkSrc = clockSource;
1097 break;
1098 default:
1099 ERROR("%s: Unknown table version %" B_PRIu8
1100 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1101 return B_ERROR;
1103 break;
1104 default:
1105 ERROR("%s: Unknown table version %" B_PRIu8
1106 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor);
1107 return B_ERROR;
1109 return atom_execute_table(gAtomContext, index, (uint32*)&args);
1114 * pll_external_init - Sets external default pll to sane value
1116 * Takes the AtomBIOS ulDefaultDispEngineClkFreq and applies it
1117 * back to the card's external PLL clock via SetPixelClock
1119 void
1120 pll_external_init()
1122 radeon_shared_info &info = *gInfo->shared_info;
1124 if (info.dceMajor >= 12) {
1125 pll_set_dce(gInfo->displayClockFrequency,
1126 DCE_CLOCK_TYPE_DISPCLK, ATOM_GCK_DFS);
1127 pll_set_dce(gInfo->displayClockFrequency,
1128 DCE_CLOCK_TYPE_DPREFCLK, ATOM_GCK_DFS);
1129 } else if (info.dceMajor >= 6) {
1130 pll_set_external(gInfo->displayClockFrequency);
1131 } else if (info.dceMajor >= 4) {
1132 // Create our own pseudo pll
1133 pll_info pll;
1134 pll.pixelClock = gInfo->displayClockFrequency;
1136 pll_asic_ss_probe(&pll, ASIC_INTERNAL_SS_ON_DCPLL);
1137 if (pll.ssEnabled)
1138 display_crtc_ss(&pll, ATOM_DISABLE);
1139 pll_set_external(pll.pixelClock);
1140 if (pll.ssEnabled)
1141 display_crtc_ss(&pll, ATOM_ENABLE);
1147 * pll_usage_mask - Calculate which PLL's are in use
1149 * Returns the mask of which PLL's are in use
1151 uint32
1152 pll_usage_mask()
1154 uint32 pllMask = 0;
1155 for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
1156 if (gConnector[id]->valid == true) {
1157 pll_info* pll = &gConnector[id]->encoder.pll;
1158 if (pll->id != ATOM_PPLL_INVALID)
1159 pllMask |= (1 << pll->id);
1162 return pllMask;
1167 * pll_usage_count - Find number of connectors attached to a PLL
1169 * Returns the count of connectors using specified PLL
1171 uint32
1172 pll_usage_count(uint32 pllID)
1174 uint32 pllCount = 0;
1175 for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
1176 if (gConnector[id]->valid == true) {
1177 pll_info* pll = &gConnector[id]->encoder.pll;
1178 if (pll->id == pllID)
1179 pllCount++;
1183 return pllCount;
1188 * pll_shared_dp - Find any existing PLL's used for DP connectors
1190 * Returns the PLL shared by other DP connectors
1192 uint32
1193 pll_shared_dp()
1195 for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) {
1196 if (gConnector[id]->valid == true) {
1197 if (connector_is_dp(id)) {
1198 pll_info* pll = &gConnector[id]->encoder.pll;
1199 return pll->id;
1203 return ATOM_PPLL_INVALID;
1208 * pll_next_available - Find the next available PLL
1210 * Returns the next available PLL
1212 uint32
1213 pll_next_available()
1215 radeon_shared_info &info = *gInfo->shared_info;
1216 uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
1218 uint32 pllMask = pll_usage_mask();
1220 if (dceVersion == 802 || dceVersion == 601) {
1221 if (!(pllMask & (1 << ATOM_PPLL0)))
1222 return ATOM_PPLL0;
1225 if (!(pllMask & (1 << ATOM_PPLL1)))
1226 return ATOM_PPLL1;
1227 if (dceVersion != 601) {
1228 if (!(pllMask & (1 << ATOM_PPLL2)))
1229 return ATOM_PPLL2;
1231 // TODO: If this starts happening, we likely need to
1232 // add the sharing of PLL's with identical clock rates
1233 // (see radeon_atom_pick_pll in drm)
1234 ERROR("%s: Unable to find a PLL! (0x%" B_PRIX32 ")\n", __func__, pllMask);
1235 return ATOM_PPLL_INVALID;
1239 status_t
1240 pll_pick(uint32 connectorIndex)
1242 pll_info* pll = &gConnector[connectorIndex]->encoder.pll;
1243 radeon_shared_info &info = *gInfo->shared_info;
1244 uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor;
1246 bool linkB = gConnector[connectorIndex]->encoder.linkEnumeration
1247 == GRAPH_OBJECT_ENUM_ID2 ? true : false;
1249 pll->id = ATOM_PPLL_INVALID;
1251 // DCE 6.1 APU, UNIPHYA requires PLL2
1252 if (gConnector[connectorIndex]->encoder.objectID
1253 == ENCODER_OBJECT_ID_INTERNAL_UNIPHY && !linkB) {
1254 pll->id = ATOM_PPLL2;
1255 return B_OK;
1258 if (connector_is_dp(connectorIndex)) {
1259 // If DP external clock, set to invalid except on DCE 6.1
1260 if (gInfo->dpExternalClock && !(dceVersion == 601)) {
1261 pll->id = ATOM_PPLL_INVALID;
1262 return B_OK;
1265 // DCE 6.1+, we can share DP PLLs. See if any other DP connectors
1266 // have been assigned a PLL yet.
1267 if (dceVersion >= 601) {
1268 pll->id = pll_shared_dp();
1269 if (pll->id != ATOM_PPLL_INVALID)
1270 return B_OK;
1271 // Continue through to pll_next_available
1272 } else if (dceVersion == 600) {
1273 pll->id = ATOM_PPLL0;
1274 return B_OK;
1275 } else if (info.dceMajor >= 5) {
1276 pll->id = ATOM_DCPLL;
1277 return B_OK;
1281 if (info.dceMajor >= 4) {
1282 pll->id = pll_next_available();
1283 return B_OK;
1286 // TODO: Should return the CRTCID here.
1287 pll->id = ATOM_PPLL1;
1288 return B_OK;