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
pll_limit_probe(pll_info * pll)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
pll_ppll_ss_probe(pll_info * pll,uint32 ssID)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
pll_asic_ss_probe(pll_info * pll,uint32 ssID)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
pll_compute_post_divider(pll_info * pll)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
pll_compute(pll_info * pll)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
pll_setup_flags(pll_info * pll,uint8 crtcID)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
pll_adjust(pll_info * pll,display_mode * mode,uint8 crtcID)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
pll_set(display_mode * mode,uint8 crtcID)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
pll_set_external(uint32 clock)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
pll_set_dce(uint32 clock,uint8 clockType,uint8 clockSource)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
pll_external_init()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
pll_usage_mask()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
pll_usage_count(uint32 pllID)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
pll_shared_dp()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
pll_next_available()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
pll_pick(uint32 connectorIndex)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