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