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