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 38 status_t 39 pll_limit_probe(pll_info* pll) 40 { 41 uint8 tableMajor; 42 uint8 tableMinor; 43 uint16 tableOffset; 44 45 int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); 46 if (atom_parse_data_header(gAtomContext, index, NULL, 47 &tableMajor, &tableMinor, &tableOffset) != B_OK) { 48 ERROR("%s: Couldn't parse data header\n", __func__); 49 return B_ERROR; 50 } 51 52 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, 53 tableMajor, tableMinor); 54 55 union atomFirmwareInfo { 56 ATOM_FIRMWARE_INFO info; 57 ATOM_FIRMWARE_INFO_V1_2 info_12; 58 ATOM_FIRMWARE_INFO_V1_3 info_13; 59 ATOM_FIRMWARE_INFO_V1_4 info_14; 60 ATOM_FIRMWARE_INFO_V2_1 info_21; 61 ATOM_FIRMWARE_INFO_V2_2 info_22; 62 }; 63 union atomFirmwareInfo* firmwareInfo 64 = (union atomFirmwareInfo*)(gAtomContext->bios + tableOffset); 65 66 /* pixel clock limits */ 67 pll->referenceFreq 68 = B_LENDIAN_TO_HOST_INT16(firmwareInfo->info.usReferenceClock) * 10; 69 70 if (tableMinor < 2) { 71 pll->pllOutMin 72 = B_LENDIAN_TO_HOST_INT16( 73 firmwareInfo->info.usMinPixelClockPLL_Output) * 10; 74 } else { 75 pll->pllOutMin 76 = B_LENDIAN_TO_HOST_INT32( 77 firmwareInfo->info_12.ulMinPixelClockPLL_Output) * 10; 78 } 79 80 pll->pllOutMax 81 = B_LENDIAN_TO_HOST_INT32( 82 firmwareInfo->info.ulMaxPixelClockPLL_Output) * 10; 83 84 if (tableMinor >= 4) { 85 pll->lcdPllOutMin 86 = B_LENDIAN_TO_HOST_INT16( 87 firmwareInfo->info_14.usLcdMinPixelClockPLL_Output) * 1000; 88 89 if (pll->lcdPllOutMin == 0) 90 pll->lcdPllOutMin = pll->pllOutMin; 91 92 pll->lcdPllOutMax 93 = B_LENDIAN_TO_HOST_INT16( 94 firmwareInfo->info_14.usLcdMaxPixelClockPLL_Output) * 1000; 95 96 if (pll->lcdPllOutMax == 0) 97 pll->lcdPllOutMax = pll->pllOutMax; 98 99 } else { 100 pll->lcdPllOutMin = pll->pllOutMin; 101 pll->lcdPllOutMax = pll->pllOutMax; 102 } 103 104 if (pll->pllOutMin == 0) { 105 pll->pllOutMin = 64800 * 10; 106 // Avivo+ limit 107 } 108 109 pll->minPostDiv = POST_DIV_MIN; 110 pll->maxPostDiv = POST_DIV_LIMIT; 111 pll->minRefDiv = REF_DIV_MIN; 112 pll->maxRefDiv = REF_DIV_LIMIT; 113 pll->minFeedbackDiv = FB_DIV_MIN; 114 pll->maxFeedbackDiv = FB_DIV_LIMIT; 115 116 pll->pllInMin = B_LENDIAN_TO_HOST_INT16( 117 firmwareInfo->info.usMinPixelClockPLL_Input) * 10; 118 pll->pllInMax = B_LENDIAN_TO_HOST_INT16( 119 firmwareInfo->info.usMaxPixelClockPLL_Input) * 10; 120 121 TRACE("%s: referenceFreq: %" B_PRIu16 "; pllOutMin: %" B_PRIu16 "; " 122 " pllOutMax: %" B_PRIu16 "; pllInMin: %" B_PRIu16 ";" 123 "pllInMax: %" B_PRIu16 "\n", __func__, pll->referenceFreq, 124 pll->pllOutMin, pll->pllOutMax, pll->pllInMin, pll->pllInMax); 125 126 return B_OK; 127 } 128 129 130 status_t 131 pll_ppll_ss_probe(pll_info* pll, uint32 ssID) 132 { 133 uint8 tableMajor; 134 uint8 tableMinor; 135 uint16 headerOffset; 136 uint16 headerSize; 137 138 int index = GetIndexIntoMasterTable(DATA, PPLL_SS_Info); 139 if (atom_parse_data_header(gAtomContext, index, &headerSize, 140 &tableMajor, &tableMinor, &headerOffset) != B_OK) { 141 ERROR("%s: Couldn't parse data header\n", __func__); 142 return B_ERROR; 143 } 144 145 struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info 146 = (struct _ATOM_SPREAD_SPECTRUM_INFO*)((uint16*)gAtomContext->bios 147 + headerOffset); 148 149 int indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER)) 150 / sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); 151 152 int i; 153 for (i = 0; i < indices; i++) { 154 if (ss_info->asSS_Info[i].ucSS_Id == ssID) { 155 pll->ssPercentage = B_LENDIAN_TO_HOST_INT16( 156 ss_info->asSS_Info[i].usSpreadSpectrumPercentage); 157 pll->ssType = ss_info->asSS_Info[i].ucSpreadSpectrumType; 158 pll->ssStep = ss_info->asSS_Info[i].ucSS_Step; 159 pll->ssDelay = ss_info->asSS_Info[i].ucSS_Delay; 160 pll->ssRange = ss_info->asSS_Info[i].ucSS_Range; 161 pll->ssReferenceDiv 162 = ss_info->asSS_Info[i].ucRecommendedRef_Div; 163 return B_OK; 164 } 165 } 166 167 return B_ERROR; 168 } 169 170 171 status_t 172 pll_asic_ss_probe(pll_info* pll, uint32 ssID) 173 { 174 uint8 tableMajor; 175 uint8 tableMinor; 176 uint16 headerOffset; 177 uint16 headerSize; 178 179 int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); 180 if (atom_parse_data_header(gAtomContext, index, &headerSize, 181 &tableMajor, &tableMinor, &headerOffset) != B_OK) { 182 ERROR("%s: Couldn't parse data header\n", __func__); 183 return B_ERROR; 184 } 185 186 union asicSSInfo { 187 struct _ATOM_ASIC_INTERNAL_SS_INFO info; 188 struct _ATOM_ASIC_INTERNAL_SS_INFO_V2 info_2; 189 struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 info_3; 190 }; 191 192 union asicSSInfo *ss_info 193 = (union asicSSInfo*)((uint16*)gAtomContext->bios + headerOffset); 194 195 int i; 196 int indices; 197 switch (tableMajor) { 198 case 1: 199 indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER)) 200 / sizeof(ATOM_ASIC_SS_ASSIGNMENT); 201 202 for (i = 0; i < indices; i++) { 203 if (ss_info->info.asSpreadSpectrum[i].ucClockIndication 204 != ssID) { 205 continue; 206 } 207 TRACE("%s: ss match found\n", __func__); 208 if (pll->pixelClock > B_LENDIAN_TO_HOST_INT32( 209 ss_info->info.asSpreadSpectrum[i].ulTargetClockRange)) { 210 TRACE("%s: pixelClock > targetClockRange!\n", __func__); 211 continue; 212 } 213 214 pll->ssPercentage = B_LENDIAN_TO_HOST_INT16( 215 ss_info->info.asSpreadSpectrum[i].usSpreadSpectrumPercentage 216 ); 217 218 pll->ssType 219 = ss_info->info.asSpreadSpectrum[i].ucSpreadSpectrumMode; 220 pll->ssRate = B_LENDIAN_TO_HOST_INT16( 221 ss_info->info.asSpreadSpectrum[i].usSpreadRateInKhz); 222 return B_OK; 223 } 224 break; 225 case 2: 226 indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER)) 227 / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); 228 229 for (i = 0; i < indices; i++) { 230 if (ss_info->info_2.asSpreadSpectrum[i].ucClockIndication 231 != ssID) { 232 continue; 233 } 234 TRACE("%s: ss match found\n", __func__); 235 if (pll->pixelClock > B_LENDIAN_TO_HOST_INT32( 236 ss_info->info_2.asSpreadSpectrum[i].ulTargetClockRange)) { 237 TRACE("%s: pixelClock > targetClockRange!\n", __func__); 238 continue; 239 } 240 241 pll->ssPercentage = B_LENDIAN_TO_HOST_INT16( 242 ss_info 243 ->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage 244 ); 245 246 pll->ssType 247 = ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode; 248 pll->ssRate = B_LENDIAN_TO_HOST_INT16( 249 ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz); 250 return B_OK; 251 } 252 break; 253 case 3: 254 indices = (headerSize - sizeof(ATOM_COMMON_TABLE_HEADER)) 255 / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); 256 257 for (i = 0; i < indices; i++) { 258 if (ss_info->info_3.asSpreadSpectrum[i].ucClockIndication 259 != ssID) { 260 continue; 261 } 262 TRACE("%s: ss match found\n", __func__); 263 if (pll->pixelClock > B_LENDIAN_TO_HOST_INT32( 264 ss_info->info_3.asSpreadSpectrum[i].ulTargetClockRange)) { 265 TRACE("%s: pixelClock > targetClockRange!\n", __func__); 266 continue; 267 } 268 269 pll->ssPercentage = B_LENDIAN_TO_HOST_INT16( 270 ss_info 271 ->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage 272 ); 273 274 pll->ssType 275 = ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode; 276 pll->ssRate = B_LENDIAN_TO_HOST_INT16( 277 ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz); 278 return B_OK; 279 } 280 break; 281 default: 282 ERROR("%s: Unknown SS table version!\n", __func__); 283 return B_ERROR; 284 } 285 286 ERROR("%s: No potential spread spectrum data found!\n", __func__); 287 return B_ERROR; 288 } 289 290 291 void 292 pll_compute_post_divider(pll_info* pll) 293 { 294 if ((pll->flags & PLL_USE_POST_DIV) != 0) { 295 TRACE("%s: using AtomBIOS post divider\n", __func__); 296 return; 297 } 298 299 uint32 vco; 300 if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) { 301 if ((pll->flags & PLL_IS_LCD) != 0) 302 vco = pll->lcdPllOutMin; 303 else 304 vco = pll->pllOutMax; 305 } else { 306 if ((pll->flags & PLL_IS_LCD) != 0) 307 vco = pll->lcdPllOutMax; 308 else 309 vco = pll->pllOutMin; 310 } 311 312 TRACE("%s: vco = %" B_PRIu32 "\n", __func__, vco); 313 314 uint32 postDivider = vco / pll->adjustedClock; 315 uint32 tmp = vco % pll->adjustedClock; 316 317 if ((pll->flags & PLL_PREFER_MINM_OVER_MAXP) != 0) { 318 if (tmp) 319 postDivider++; 320 } else { 321 if (!tmp) 322 postDivider--; 323 } 324 325 if (postDivider > pll->maxPostDiv) 326 postDivider = pll->maxPostDiv; 327 else if (postDivider < pll->minPostDiv) 328 postDivider = pll->minPostDiv; 329 330 pll->postDiv = postDivider; 331 TRACE("%s: postDiv = %" B_PRIu32 "\n", __func__, postDivider); 332 } 333 334 335 status_t 336 pll_compute(pll_info* pll) 337 { 338 pll_compute_post_divider(pll); 339 340 uint32 targetClock = pll->adjustedClock; 341 342 pll->feedbackDiv = 0; 343 pll->feedbackDivFrac = 0; 344 uint32 referenceFrequency = pll->referenceFreq; 345 346 if ((pll->flags & PLL_USE_REF_DIV) != 0) { 347 TRACE("%s: using AtomBIOS reference divider\n", __func__); 348 } else { 349 TRACE("%s: using minimum reference divider\n", __func__); 350 pll->referenceDiv = pll->minRefDiv; 351 } 352 353 if ((pll->flags & PLL_USE_FRAC_FB_DIV) != 0) { 354 TRACE("%s: using AtomBIOS fractional feedback divider\n", __func__); 355 356 uint32 tmp = pll->postDiv * pll->referenceDiv; 357 tmp *= targetClock; 358 pll->feedbackDiv = tmp / pll->referenceFreq; 359 pll->feedbackDivFrac = tmp % pll->referenceFreq; 360 361 if (pll->feedbackDiv > pll->maxFeedbackDiv) 362 pll->feedbackDiv = pll->maxFeedbackDiv; 363 else if (pll->feedbackDiv < pll->minFeedbackDiv) 364 pll->feedbackDiv = pll->minFeedbackDiv; 365 366 pll->feedbackDivFrac 367 = (1000 * pll->feedbackDivFrac) / pll->referenceFreq; 368 369 if (pll->feedbackDivFrac >= 5) { 370 pll->feedbackDivFrac -= 5; 371 pll->feedbackDivFrac /= 10; 372 pll->feedbackDivFrac++; 373 } 374 if (pll->feedbackDivFrac >= 10) { 375 pll->feedbackDiv++; 376 pll->feedbackDivFrac = 0; 377 } 378 } else { 379 TRACE("%s: performing fractional feedback calculations\n", __func__); 380 381 while (pll->referenceDiv <= pll->maxRefDiv) { 382 // get feedback divider 383 uint32 retroEncabulator = pll->postDiv * pll->referenceDiv; 384 385 retroEncabulator *= targetClock; 386 pll->feedbackDiv = retroEncabulator / referenceFrequency; 387 pll->feedbackDivFrac 388 = retroEncabulator % referenceFrequency; 389 390 if (pll->feedbackDiv > pll->maxFeedbackDiv) 391 pll->feedbackDiv = pll->maxFeedbackDiv; 392 else if (pll->feedbackDiv < pll->minFeedbackDiv) 393 pll->feedbackDiv = pll->minFeedbackDiv; 394 395 if (pll->feedbackDivFrac >= (referenceFrequency / 2)) 396 pll->feedbackDiv++; 397 398 pll->feedbackDivFrac = 0; 399 400 if (pll->referenceDiv == 0 401 || pll->postDiv == 0 402 || targetClock == 0) { 403 TRACE("%s: Caught division by zero!\n", __func__); 404 TRACE("%s: referenceDiv %" B_PRIu32 "\n", 405 __func__, pll->referenceDiv); 406 TRACE("%s: postDiv %" B_PRIu32 "\n", 407 __func__, pll->postDiv); 408 TRACE("%s: targetClock %" B_PRIu32 "\n", 409 __func__, targetClock); 410 return B_ERROR; 411 } 412 uint32 tmp = (referenceFrequency * pll->feedbackDiv) 413 / (pll->postDiv * pll->referenceDiv); 414 tmp = (tmp * 1000) / targetClock; 415 416 if (tmp > (1000 + (MAX_TOLERANCE / 10))) 417 pll->referenceDiv++; 418 else if (tmp >= (1000 - (MAX_TOLERANCE / 10))) 419 break; 420 else 421 pll->referenceDiv++; 422 } 423 } 424 425 if (pll->referenceDiv == 0 || pll->postDiv == 0) { 426 TRACE("%s: Caught division by zero of post or reference divider\n", 427 __func__); 428 return B_ERROR; 429 } 430 431 uint32 calculatedClock 432 = ((referenceFrequency * pll->feedbackDiv * 10) 433 + (referenceFrequency * pll->feedbackDivFrac)) 434 / (pll->referenceDiv * pll->postDiv * 10); 435 436 TRACE("%s: pixel clock: %" B_PRIu32 " gives:" 437 " feedbackDivider = %" B_PRIu32 ".%" B_PRIu32 438 "; referenceDivider = %" B_PRIu32 "; postDivider = %" B_PRIu32 "\n", 439 __func__, pll->adjustedClock, pll->feedbackDiv, pll->feedbackDivFrac, 440 pll->referenceDiv, pll->postDiv); 441 442 if (pll->adjustedClock != calculatedClock) { 443 TRACE("%s: pixel clock %" B_PRIu32 " was changed to %" B_PRIu32 "\n", 444 __func__, pll->adjustedClock, calculatedClock); 445 pll->pixelClock = calculatedClock; 446 } 447 448 return B_OK; 449 } 450 451 452 void 453 pll_setup_flags(pll_info* pll, uint8 crtcID) 454 { 455 radeon_shared_info &info = *gInfo->shared_info; 456 uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; 457 uint32 encoderFlags = gConnector[connectorIndex]->encoder.flags; 458 459 uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor; 460 461 TRACE("%s: CRTC: %" B_PRIu8 ", PLL: %" B_PRIu8 "\n", __func__, 462 crtcID, pll->id); 463 464 if (dceVersion >= 302 && pll->pixelClock > 200000) 465 pll->flags |= PLL_PREFER_HIGH_FB_DIV; 466 else 467 pll->flags |= PLL_PREFER_LOW_REF_DIV; 468 469 if (info.chipsetID < RADEON_RV770) 470 pll->flags |= PLL_PREFER_MINM_OVER_MAXP; 471 472 if ((encoderFlags & ATOM_DEVICE_LCD_SUPPORT) != 0) { 473 pll->flags |= PLL_IS_LCD; 474 475 // use reference divider for spread spectrum 476 TRACE("%s: Spread Spectrum is %" B_PRIu32 "%%\n", __func__, 477 pll->ssPercentage); 478 if (pll->ssPercentage > 0) { 479 if (pll->ssReferenceDiv > 0) { 480 TRACE("%s: using Spread Spectrum reference divider. " 481 "refDiv was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", 482 __func__, pll->referenceDiv, pll->ssReferenceDiv); 483 pll->flags |= PLL_USE_REF_DIV; 484 pll->referenceDiv = pll->ssReferenceDiv; 485 486 // TODO: IS AVIVO+? 487 pll->flags |= PLL_USE_FRAC_FB_DIV; 488 } 489 } 490 } 491 492 if ((encoderFlags & ATOM_DEVICE_TV_SUPPORT) != 0) 493 pll->flags |= PLL_PREFER_CLOSEST_LOWER; 494 495 if ((info.chipsetFlags & CHIP_APU) != 0) { 496 // Use fractional feedback on APU's 497 pll->flags |= PLL_USE_FRAC_FB_DIV; 498 } 499 } 500 501 502 status_t 503 pll_adjust(pll_info* pll, display_mode* mode, uint8 crtcID) 504 { 505 radeon_shared_info &info = *gInfo->shared_info; 506 507 uint32 pixelClock = pll->pixelClock; 508 // original as pixel_clock will be adjusted 509 510 uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; 511 connector_info* connector = gConnector[connectorIndex]; 512 513 uint32 encoderID = connector->encoder.objectID; 514 uint32 encoderMode = display_get_encoder_mode(connectorIndex); 515 uint32 encoderFlags = connector->encoder.flags; 516 517 uint32 externalEncoderID = 0; 518 pll->adjustedClock = pll->pixelClock; 519 if (connector->encoderExternal.isDPBridge) 520 externalEncoderID = connector->encoderExternal.objectID; 521 522 if (info.dceMajor >= 3) { 523 524 uint8 tableMajor; 525 uint8 tableMinor; 526 527 int index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); 528 if (atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor) 529 != B_OK) { 530 ERROR("%s: Couldn't find AtomBIOS PLL adjustment\n", __func__); 531 return B_ERROR; 532 } 533 534 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, 535 tableMajor, tableMinor); 536 537 // Prepare arguments for AtomBIOS call 538 union adjustPixelClock { 539 ADJUST_DISPLAY_PLL_PS_ALLOCATION v1; 540 ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 v3; 541 }; 542 union adjustPixelClock args; 543 memset(&args, 0, sizeof(args)); 544 545 switch (tableMajor) { 546 case 1: 547 switch (tableMinor) { 548 case 1: 549 case 2: 550 args.v1.usPixelClock 551 = B_HOST_TO_LENDIAN_INT16(pixelClock / 10); 552 args.v1.ucTransmitterID = encoderID; 553 args.v1.ucEncodeMode = encoderMode; 554 if (pll->ssPercentage > 0) { 555 args.v1.ucConfig 556 |= ADJUST_DISPLAY_CONFIG_SS_ENABLE; 557 } 558 559 atom_execute_table(gAtomContext, index, (uint32*)&args); 560 // get returned adjusted clock 561 pll->adjustedClock 562 = B_LENDIAN_TO_HOST_INT16(args.v1.usPixelClock); 563 pll->adjustedClock *= 10; 564 break; 565 case 3: 566 args.v3.sInput.usPixelClock 567 = B_HOST_TO_LENDIAN_INT16(pixelClock / 10); 568 args.v3.sInput.ucTransmitterID = encoderID; 569 args.v3.sInput.ucEncodeMode = encoderMode; 570 args.v3.sInput.ucDispPllConfig = 0; 571 if (pll->ssPercentage > 0) { 572 args.v3.sInput.ucDispPllConfig 573 |= DISPPLL_CONFIG_SS_ENABLE; 574 } 575 576 // Handle DP adjustments 577 if (encoderMode == ATOM_ENCODER_MODE_DP 578 || encoderMode == ATOM_ENCODER_MODE_DP_MST) { 579 TRACE("%s: encoderMode is DP\n", __func__); 580 args.v3.sInput.ucDispPllConfig 581 |= DISPPLL_CONFIG_COHERENT_MODE; 582 /* 16200 or 27000 */ 583 uint32 dpLinkSpeed 584 = dp_get_link_rate(connectorIndex, mode); 585 args.v3.sInput.usPixelClock 586 = B_HOST_TO_LENDIAN_INT16(dpLinkSpeed / 10); 587 } else if ((encoderFlags & ATOM_DEVICE_DFP_SUPPORT) 588 != 0) { 589 #if 0 590 if (encoderMode == ATOM_ENCODER_MODE_HDMI) { 591 /* deep color support */ 592 args.v3.sInput.usPixelClock = 593 cpu_to_le16((mode->clock * bpc / 8) / 10); 594 } 595 #endif 596 if (pixelClock > 165000) { 597 args.v3.sInput.ucDispPllConfig 598 |= DISPPLL_CONFIG_DUAL_LINK; 599 } 600 if (1) { // dig coherent mode? 601 args.v3.sInput.ucDispPllConfig 602 |= DISPPLL_CONFIG_COHERENT_MODE; 603 } 604 } 605 606 args.v3.sInput.ucExtTransmitterID = externalEncoderID; 607 608 atom_execute_table(gAtomContext, index, (uint32*)&args); 609 610 // get returned adjusted clock 611 pll->adjustedClock 612 = B_LENDIAN_TO_HOST_INT32( 613 args.v3.sOutput.ulDispPllFreq); 614 pll->adjustedClock *= 10; 615 // convert to kHz for storage 616 617 if (args.v3.sOutput.ucRefDiv) { 618 pll->flags |= PLL_USE_FRAC_FB_DIV; 619 pll->flags |= PLL_USE_REF_DIV; 620 pll->referenceDiv = args.v3.sOutput.ucRefDiv; 621 } 622 if (args.v3.sOutput.ucPostDiv) { 623 pll->flags |= PLL_USE_FRAC_FB_DIV; 624 pll->flags |= PLL_USE_POST_DIV; 625 pll->postDiv = args.v3.sOutput.ucPostDiv; 626 } 627 break; 628 default: 629 TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 630 " unknown\n", __func__, tableMajor, tableMinor); 631 return B_ERROR; 632 } 633 break; 634 default: 635 TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 636 " unknown\n", __func__, tableMajor, tableMinor); 637 return B_ERROR; 638 } 639 } 640 641 TRACE("%s: was: %" B_PRIu32 ", now: %" B_PRIu32 "\n", __func__, 642 pixelClock, pll->adjustedClock); 643 644 return B_OK; 645 } 646 647 648 status_t 649 pll_set(display_mode* mode, uint8 crtcID) 650 { 651 uint32 connectorIndex = gDisplay[crtcID]->connectorIndex; 652 pll_info* pll = &gConnector[connectorIndex]->encoder.pll; 653 uint32 dp_clock = gConnector[connectorIndex]->dpInfo.linkRate / 10; 654 bool ssEnabled = false; 655 656 pll->pixelClock = mode->timing.pixel_clock; 657 658 radeon_shared_info &info = *gInfo->shared_info; 659 660 // Probe for PLL spread spectrum info; 661 pll->ssPercentage = 0; 662 pll->ssType = 0; 663 pll->ssStep = 0; 664 pll->ssDelay = 0; 665 pll->ssRange = 0; 666 pll->ssReferenceDiv = 0; 667 668 switch (display_get_encoder_mode(connectorIndex)) { 669 case ATOM_ENCODER_MODE_DP_MST: 670 case ATOM_ENCODER_MODE_DP: 671 if (info.dceMajor >= 4) 672 pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_DP); 673 else { 674 if (dp_clock == 16200) { 675 ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID2); 676 if (!ssEnabled) 677 // id2 failed, try id1 678 ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1); 679 } else 680 ssEnabled = pll_ppll_ss_probe(pll, ATOM_DP_SS_ID1); 681 } 682 break; 683 case ATOM_ENCODER_MODE_LVDS: 684 if (info.dceMajor >= 4) 685 ssEnabled = pll_asic_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); 686 else 687 ssEnabled = pll_ppll_ss_probe(pll, gInfo->lvdsSpreadSpectrumID); 688 break; 689 case ATOM_ENCODER_MODE_DVI: 690 if (info.dceMajor >= 4) 691 ssEnabled = pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_TMDS); 692 break; 693 case ATOM_ENCODER_MODE_HDMI: 694 if (info.dceMajor >= 4) 695 ssEnabled = pll_asic_ss_probe(pll, ASIC_INTERNAL_SS_ON_HDMI); 696 break; 697 } 698 699 pll_setup_flags(pll, crtcID); 700 // set up any special flags 701 pll_adjust(pll, mode, crtcID); 702 // get any needed clock adjustments, set reference/post dividers 703 pll_compute(pll); 704 // compute dividers 705 706 display_crtc_ss(pll, ATOM_DISABLE); 707 // disable ss 708 709 uint8 tableMajor; 710 uint8 tableMinor; 711 712 int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); 713 atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor); 714 715 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, 716 tableMajor, tableMinor); 717 718 uint32 bitsPerColor = 8; 719 // TODO: Digital Depth, EDID 1.4+ on digital displays 720 // isn't in Haiku edid common code? 721 722 // Prepare arguments for AtomBIOS call 723 union setPixelClock { 724 SET_PIXEL_CLOCK_PS_ALLOCATION base; 725 PIXEL_CLOCK_PARAMETERS v1; 726 PIXEL_CLOCK_PARAMETERS_V2 v2; 727 PIXEL_CLOCK_PARAMETERS_V3 v3; 728 PIXEL_CLOCK_PARAMETERS_V5 v5; 729 PIXEL_CLOCK_PARAMETERS_V6 v6; 730 }; 731 union setPixelClock args; 732 memset(&args, 0, sizeof(args)); 733 734 switch (tableMinor) { 735 case 1: 736 args.v1.usPixelClock 737 = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); 738 args.v1.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); 739 args.v1.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); 740 args.v1.ucFracFbDiv = pll->feedbackDivFrac; 741 args.v1.ucPostDiv = pll->postDiv; 742 args.v1.ucPpll = pll->id; 743 args.v1.ucCRTC = crtcID; 744 args.v1.ucRefDivSrc = 1; 745 break; 746 case 2: 747 args.v2.usPixelClock 748 = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); 749 args.v2.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); 750 args.v2.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); 751 args.v2.ucFracFbDiv = pll->feedbackDivFrac; 752 args.v2.ucPostDiv = pll->postDiv; 753 args.v2.ucPpll = pll->id; 754 args.v2.ucCRTC = crtcID; 755 args.v2.ucRefDivSrc = 1; 756 break; 757 case 3: 758 args.v3.usPixelClock 759 = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); 760 args.v3.usRefDiv = B_HOST_TO_LENDIAN_INT16(pll->referenceDiv); 761 args.v3.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); 762 args.v3.ucFracFbDiv = pll->feedbackDivFrac; 763 args.v3.ucPostDiv = pll->postDiv; 764 args.v3.ucPpll = pll->id; 765 args.v3.ucMiscInfo = (pll->id << 2); 766 if (pll->ssPercentage > 0 767 && (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) { 768 args.v3.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; 769 } 770 args.v3.ucTransmitterId 771 = gConnector[connectorIndex]->encoder.objectID; 772 args.v3.ucEncoderMode = display_get_encoder_mode(connectorIndex); 773 break; 774 case 5: 775 args.v5.ucCRTC = crtcID; 776 args.v5.usPixelClock 777 = B_HOST_TO_LENDIAN_INT16(pll->pixelClock / 10); 778 args.v5.ucRefDiv = pll->referenceDiv; 779 args.v5.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); 780 args.v5.ulFbDivDecFrac 781 = B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000); 782 args.v5.ucPostDiv = pll->postDiv; 783 args.v5.ucMiscInfo = 0; /* HDMI depth, etc. */ 784 if (pll->ssPercentage > 0 785 && (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) { 786 args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_REF_DIV_SRC; 787 } 788 switch (bitsPerColor) { 789 case 8: 790 default: 791 args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_24BPP; 792 break; 793 case 10: 794 args.v5.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; 795 break; 796 } 797 args.v5.ucTransmitterID 798 = gConnector[connectorIndex]->encoder.objectID; 799 args.v5.ucEncoderMode 800 = display_get_encoder_mode(connectorIndex); 801 args.v5.ucPpll = pll->id; 802 break; 803 case 6: 804 args.v6.ulDispEngClkFreq 805 = B_HOST_TO_LENDIAN_INT32(crtcID << 24 | pll->pixelClock / 10); 806 args.v6.ucRefDiv = pll->referenceDiv; 807 args.v6.usFbDiv = B_HOST_TO_LENDIAN_INT16(pll->feedbackDiv); 808 args.v6.ulFbDivDecFrac 809 = B_HOST_TO_LENDIAN_INT32(pll->feedbackDivFrac * 100000); 810 args.v6.ucPostDiv = pll->postDiv; 811 args.v6.ucMiscInfo = 0; /* HDMI depth, etc. */ 812 if (pll->ssPercentage > 0 813 && (pll->ssType & ATOM_EXTERNAL_SS_MASK) != 0) { 814 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; 815 } 816 switch (bitsPerColor) { 817 case 8: 818 default: 819 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_24BPP; 820 break; 821 case 10: 822 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP; 823 break; 824 case 12: 825 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP; 826 break; 827 case 16: 828 args.v6.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; 829 break; 830 } 831 args.v6.ucTransmitterID 832 = gConnector[connectorIndex]->encoder.objectID; 833 args.v6.ucEncoderMode = display_get_encoder_mode(connectorIndex); 834 args.v6.ucPpll = pll->id; 835 break; 836 default: 837 TRACE("%s: ERROR: table version %" B_PRIu8 ".%" B_PRIu8 " TODO\n", 838 __func__, tableMajor, tableMinor); 839 return B_ERROR; 840 } 841 842 TRACE("%s: set adjusted pixel clock %" B_PRIu32 " (was %" B_PRIu32 ")\n", 843 __func__, pll->pixelClock, mode->timing.pixel_clock); 844 845 status_t result = atom_execute_table(gAtomContext, index, (uint32*)&args); 846 847 if (ssEnabled) 848 display_crtc_ss(pll, ATOM_ENABLE); 849 850 return result; 851 } 852 853 854 status_t 855 pll_external_set(uint32 clock) 856 { 857 TRACE("%s: set external pll clock to %" B_PRIu32 "\n", __func__, clock); 858 859 if (clock == 0) 860 ERROR("%s: Warning: default display clock is 0?\n", __func__); 861 862 // also known as PLL display engineering 863 uint8 tableMajor; 864 uint8 tableMinor; 865 866 int index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); 867 atom_parse_cmd_header(gAtomContext, index, &tableMajor, &tableMinor); 868 869 TRACE("%s: table %" B_PRIu8 ".%" B_PRIu8 "\n", __func__, 870 tableMajor, tableMinor); 871 872 union setPixelClock { 873 SET_PIXEL_CLOCK_PS_ALLOCATION base; 874 PIXEL_CLOCK_PARAMETERS v1; 875 PIXEL_CLOCK_PARAMETERS_V2 v2; 876 PIXEL_CLOCK_PARAMETERS_V3 v3; 877 PIXEL_CLOCK_PARAMETERS_V5 v5; 878 PIXEL_CLOCK_PARAMETERS_V6 v6; 879 }; 880 union setPixelClock args; 881 memset(&args, 0, sizeof(args)); 882 883 radeon_shared_info &info = *gInfo->shared_info; 884 uint32 dceVersion = (info.dceMajor * 100) + info.dceMinor; 885 switch (tableMajor) { 886 case 1: 887 switch(tableMinor) { 888 case 5: 889 // If the default DC PLL clock is specified, 890 // SetPixelClock provides the dividers. 891 args.v5.ucCRTC = ATOM_CRTC_INVALID; 892 args.v5.usPixelClock = B_HOST_TO_LENDIAN_INT16(clock); 893 args.v5.ucPpll = ATOM_DCPLL; 894 break; 895 case 6: 896 // If the default DC PLL clock is specified, 897 // SetPixelClock provides the dividers. 898 args.v6.ulDispEngClkFreq = B_HOST_TO_LENDIAN_INT32(clock); 899 if (dceVersion == 601) 900 args.v6.ucPpll = ATOM_EXT_PLL1; 901 else if (dceVersion >= 600) 902 args.v6.ucPpll = ATOM_PPLL0; 903 else 904 args.v6.ucPpll = ATOM_DCPLL; 905 break; 906 default: 907 ERROR("%s: Unknown table version %" B_PRIu8 908 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor); 909 } 910 break; 911 default: 912 ERROR("%s: Unknown table version %" B_PRIu8 913 ".%" B_PRIu8 "\n", __func__, tableMajor, tableMinor); 914 } 915 return B_OK; 916 } 917 918 919 void 920 pll_external_init() 921 { 922 radeon_shared_info &info = *gInfo->shared_info; 923 924 if (info.dceMajor >= 6) { 925 pll_external_set(gInfo->displayClockFrequency); 926 } else if (info.dceMajor >= 4) { 927 // Create our own pseudo pll 928 pll_info pll; 929 bool ssPresent = pll_asic_ss_probe(&pll, ASIC_INTERNAL_SS_ON_DCPLL) 930 == B_OK ? true : false; 931 if (ssPresent) 932 display_crtc_ss(&pll, ATOM_DISABLE); 933 pll_external_set(gInfo->displayClockFrequency); 934 if (ssPresent) 935 display_crtc_ss(&pll, ATOM_ENABLE); 936 } 937 } 938 939 940 status_t 941 pll_pick(uint32 connectorIndex) 942 { 943 pll_info* pll = &gConnector[connectorIndex]->encoder.pll; 944 radeon_shared_info &info = *gInfo->shared_info; 945 946 bool linkB = gConnector[connectorIndex]->encoder.linkEnumeration 947 == GRAPH_OBJECT_ENUM_ID2 ? true : false; 948 949 if (info.dceMajor == 6 && info.dceMinor == 1) { 950 // DCE 6.1 APU 951 if (gConnector[connectorIndex]->encoder.objectID 952 == ENCODER_OBJECT_ID_INTERNAL_UNIPHY && !linkB) { 953 pll->id = ATOM_PPLL2; 954 return B_OK; 955 } 956 // TODO: check for used PLL1 and use PLL2? 957 pll->id = ATOM_PPLL1; 958 return B_OK; 959 } else if (info.dceMajor >= 4) { 960 if (connector_is_dp(connectorIndex)) { 961 if (gInfo->dpExternalClock) { 962 pll->id = ATOM_PPLL_INVALID; 963 return B_OK; 964 } else if (info.dceMajor >= 6) { 965 pll->id = ATOM_PPLL1; 966 return B_OK; 967 } else if (info.dceMajor >= 5) { 968 pll->id = ATOM_DCPLL; 969 return B_OK; 970 } 971 } 972 pll->id = ATOM_PPLL1; 973 return B_OK; 974 } 975 976 // TODO: Should return the CRTCID here. 977 pll->id = ATOM_PPLL1; 978 return B_OK; 979 } 980