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 "accelerant_protos.h" 11 #include "accelerant.h" 12 #include "utility.h" 13 #include "pll.h" 14 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <math.h> 19 20 21 #define TRACE_PLL 22 #ifdef TRACE_PLL 23 extern "C" void _sPrintf(const char *format, ...); 24 # define TRACE(x...) _sPrintf("radeon_hd: " x) 25 #else 26 # define TRACE(x...) ; 27 #endif 28 29 30 /* From hardcoded values. */ 31 static struct PLL_Control RV610PLLControl[] = 32 { 33 { 0x0049, 0x159F8704 }, 34 { 0x006C, 0x159B8704 }, 35 { 0xFFFF, 0x159EC704 } 36 }; 37 38 /* Some tables are provided by atombios, 39 * it's just that they are hidden away deliberately and not exposed */ 40 static struct PLL_Control RV670PLLControl[] = 41 { 42 { 0x004A, 0x159FC704 }, 43 { 0x0067, 0x159BC704 }, 44 { 0x00C4, 0x159EC704 }, 45 { 0x00F4, 0x1593A704 }, 46 { 0x0136, 0x1595A704 }, 47 { 0x01A4, 0x1596A704 }, 48 { 0x022C, 0x159CE504 }, 49 { 0xFFFF, 0x1591E404 } 50 }; 51 52 53 static uint32 54 PLLControlTable(struct PLL_Control *table, uint16 feedbackDivider) 55 { 56 int i; 57 58 for (i = 0; table[i].feedbackDivider < 0xFFFF ; i++) { 59 if (table[i].feedbackDivider >= feedbackDivider) 60 break; 61 } 62 63 return table[i].control; 64 } 65 66 67 status_t 68 PLLCalculate(uint32 pixelClock, uint16 *reference, uint16 *feedback, 69 uint16 *post) 70 { 71 // Freaking phase-locked loops, how do they work? 72 73 float ratio = ((float) pixelClock) 74 / ((float) gInfo->shared_info->pll_info.reference_frequency); 75 76 uint32 bestDiff = 0xFFFFFFFF; 77 uint32 postDiv; 78 uint32 referenceDiv; 79 uint32 feedbackDiv; 80 81 for (postDiv = 2; postDiv < POST_DIV_LIMIT; postDiv++) { 82 uint32 vcoOut = pixelClock * postDiv; 83 84 /* we are conservative and avoid the limits */ 85 if (vcoOut <= gInfo->shared_info->pll_info.min_frequency) 86 continue; 87 if (vcoOut >= gInfo->shared_info->pll_info.max_frequency) 88 break; 89 90 for (referenceDiv = 1; referenceDiv <= REF_DIV_LIMIT; referenceDiv++) { 91 feedbackDiv = (uint32)((ratio * postDiv * referenceDiv) + 0.5); 92 93 if (feedbackDiv >= FB_DIV_LIMIT) 94 break; 95 if (feedbackDiv > (500 + (13 * referenceDiv))) // rv6x0 limit 96 break; 97 98 uint32 diff = abs(pixelClock - (feedbackDiv 99 * gInfo->shared_info->pll_info.reference_frequency) 100 / (postDiv * referenceDiv)); 101 102 if (diff < bestDiff) { 103 *feedback = feedbackDiv; 104 *reference = referenceDiv; 105 *post = postDiv; 106 bestDiff = diff; 107 } 108 109 if (bestDiff == 0) 110 break; 111 } 112 113 if (bestDiff == 0) 114 break; 115 } 116 117 if (bestDiff != 0xFFFFFFFF) { 118 TRACE("%s: Successful PLL Calculation: %dkHz = " 119 "(((%i / 0x%X) * 0x%X) / 0x%X) (%dkHz off)\n", __func__, 120 (int) pixelClock, 121 (unsigned int) gInfo->shared_info->pll_info.reference_frequency, 122 *reference, *feedback, *post, (int) bestDiff); 123 return B_OK; 124 } 125 126 // Shouldn't ever happen 127 TRACE("%s: Failed to get a valid PLL setting for %dkHz\n", 128 __func__, (int) pixelClock); 129 return B_ERROR; 130 } 131 132 133 status_t 134 PLLPower(uint8 pllIndex, int command) 135 { 136 uint16 pllControlReg = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL; 137 138 bool hasDccg = DCCGCLKAvailable(pllIndex); 139 140 TRACE("%s: card has DCCG = %c\n", __func__, hasDccg ? 'y' : 'n'); 141 142 switch (command) { 143 case RHD_POWER_ON: 144 { 145 TRACE("%s: PLL %d Power On\n", __func__, pllIndex); 146 147 if (hasDccg) 148 DCCGCLKSet(pllIndex, RV620_DCCGCLK_RESET); 149 150 Write32Mask(PLL, pllControlReg, 0, 0x02); 151 // Power On 152 snooze(2); 153 PLLCalibrate(pllIndex); 154 155 if (hasDccg) 156 DCCGCLKSet(pllIndex, RV620_DCCGCLK_GRAB); 157 158 return B_OK; 159 } 160 case RHD_POWER_RESET: 161 { 162 TRACE("%s: PLL %d Power Reset\n", __func__, pllIndex); 163 164 if (hasDccg) 165 DCCGCLKSet(pllIndex, RV620_DCCGCLK_RELEASE); 166 167 Write32Mask(PLL, pllControlReg, 0x01, 0x01); 168 // Reset 169 snooze(2); 170 Write32Mask(PLL, pllControlReg, 0, 0x02); 171 // Power On 172 snooze(2); 173 return B_OK; 174 } 175 176 case RHD_POWER_SHUTDOWN: 177 default: 178 TRACE("%s: PLL %d Power Shutdown\n", __func__, pllIndex); 179 180 radeon_shared_info &info = *gInfo->shared_info; 181 182 if (hasDccg) 183 DCCGCLKSet(pllIndex, RV620_DCCGCLK_RELEASE); 184 185 Write32Mask(PLL, pllControlReg, 0x01, 0x01); 186 // Reset 187 snooze(2); 188 189 if (info.device_chipset >= (RADEON_R600 | 0x20)) { 190 uint16 pllDiffPostReg 191 = pllIndex == 1 ? RV620_EXT2_DIFF_POST_DIV_CNTL 192 : RV620_EXT1_DIFF_POST_DIV_CNTL; 193 uint16 pllDiffDriverEnable 194 = pllIndex == 1 ? (uint16)RV62_EXT2_DIFF_DRIVER_ENABLE 195 : (uint16)RV62_EXT1_DIFF_DRIVER_ENABLE; 196 197 // Sometimes we have to keep an unused PLL running. X Bug #18016 198 if ((Read32(PLL, pllDiffPostReg) 199 & pllDiffDriverEnable) == 0) { 200 Write32Mask(PLL, pllControlReg, 0x02, 0x02); 201 // Power Down 202 } else { 203 TRACE("%s: PHYA differential clock driver not disabled\n", 204 __func__); 205 } 206 207 snooze(200); 208 209 Write32Mask(PLL, pllControlReg, 0x2000, 0x2000); 210 // Reset anti-glitch? 211 212 } else { 213 Write32Mask(PLL, pllControlReg, 0x02, 0x02); 214 // Power Down 215 snooze(200); 216 } 217 } 218 219 return B_OK; 220 } 221 222 223 status_t 224 PLLSet(uint8 pllIndex, uint32 pixelClock) 225 { 226 radeon_shared_info &info = *gInfo->shared_info; 227 228 uint16 reference = 0; 229 uint16 feedback = 0; 230 uint16 post = 0; 231 232 PLLCalculate(pixelClock, &reference, &feedback, &post); 233 234 if (info.device_chipset >= (RADEON_R600 | 0x20)) { 235 TRACE("%s : setting pixel clock %d on r620+\n", __func__, 236 (int)pixelClock); 237 PLLSetLowR620(pllIndex, pixelClock, reference, 238 feedback, post); 239 } else if (info.device_chipset < (RADEON_R600 | 0x20)) { 240 TRACE("%s : setting pixel clock %d on r600-r610\n", __func__, 241 (int)pixelClock); 242 PLLSetLowLegacy(pllIndex, pixelClock, reference, 243 feedback, post); 244 } 245 246 return B_OK; 247 } 248 249 250 void 251 PLLSetLowLegacy(uint8 pllIndex, uint32 pixelClock, uint16 reference, 252 uint16 feedback, uint16 post) 253 { 254 uint32 feedbackTemp = feedback << 16; 255 uint32 referenceTemp = reference; 256 257 /* Internal PLL Registers */ 258 uint16 pllCntl = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL; 259 uint16 pllIntSSCntl 260 = pllIndex == 1 ? P2PLL_INT_SS_CNTL : P1PLL_INT_SS_CNTL; 261 262 /* External PLL Registers */ 263 uint16 pllExtCntl 264 = pllIndex == 1 ? EXT2_PPLL_CNTL : EXT1_PPLL_CNTL; 265 uint16 pllExtUpdateCntl 266 = pllIndex == 1 ? EXT2_PPLL_UPDATE_CNTL : EXT1_PPLL_UPDATE_CNTL; 267 uint16 pllExtUpdateLock 268 = pllIndex == 1 ? EXT2_PPLL_UPDATE_LOCK : EXT1_PPLL_UPDATE_LOCK; 269 uint16 pllExtPostDiv 270 = pllIndex == 1 ? EXT2_PPLL_POST_DIV : EXT1_PPLL_POST_DIV; 271 uint16 pllExtPostDivSrc 272 = pllIndex == 1 ? EXT2_PPLL_POST_DIV_SRC : EXT1_PPLL_POST_DIV_SRC; 273 uint16 pllExtFeedbackDiv 274 = pllIndex == 1 ? EXT2_PPLL_FB_DIV : EXT1_PPLL_FB_DIV; 275 uint16 pllExtRefDiv 276 = pllIndex == 1 ? EXT2_PPLL_REF_DIV : EXT1_PPLL_REF_DIV; 277 uint16 pllExtRefDivSrc 278 = pllIndex == 1 ? EXT2_PPLL_REF_DIV_SRC : EXT1_PPLL_REF_DIV_SRC; 279 280 radeon_shared_info &info = *gInfo->shared_info; 281 282 if (info.device_chipset <= RADEON_R600) 283 feedbackTemp |= 0x00000030; 284 else { 285 if (feedback <= 0x24) 286 feedbackTemp |= 0x00000030; 287 else if (feedback <= 0x3F) 288 feedbackTemp |= 0x00000020; 289 } 290 291 uint32 postTemp = Read32(PLL, pllExtPostDiv) & ~0x0000007F; 292 postTemp |= post & 0x0000007F; 293 294 uint32 control; 295 if (info.device_chipset == RADEON_R600) 296 control = 0x01130704; 297 else { 298 control = PLLControlTable(RV610PLLControl, feedback); 299 if (!control) 300 control = Read32(PLL, pllExtCntl); 301 } 302 303 Write32Mask(PLL, pllIntSSCntl, 0, 0x00000001); 304 // Disable Spread Spectrum 305 306 Write32(PLL, pllExtRefDivSrc, 0x01); /* XTAL */ 307 Write32(PLL, pllExtPostDivSrc, 0x00); /* source = reference */ 308 309 Write32(PLL, pllExtUpdateLock, 0x01); /* lock */ 310 311 Write32(PLL, pllExtRefDiv, referenceTemp); 312 Write32(PLL, pllExtFeedbackDiv, feedbackTemp); 313 Write32(PLL, pllExtPostDiv, postTemp); 314 Write32(PLL, pllExtCntl, control); 315 316 Write32Mask(PLL, pllExtUpdateCntl, 0x00010000, 0x00010000); 317 // No autoreset 318 Write32Mask(PLL, pllCntl, 0, 0x04); 319 // Don't bypass calibration 320 321 /* We need to reset the anti glitch logic */ 322 Write32Mask(PLL, pllCntl, 0, 0x00000002); 323 // Power up 324 325 /* reset anti glitch logic */ 326 Write32Mask(PLL, pllCntl, 0x00002000, 0x00002000); 327 snooze(2); 328 Write32Mask(PLL, pllCntl, 0, 0x00002000); 329 330 /* powerdown and reset */ 331 Write32Mask(PLL, pllCntl, 0x00000003, 0x00000003); 332 snooze(2); 333 334 Write32(PLL, pllExtUpdateLock, 0); 335 // Unlock 336 Write32Mask(PLL, pllExtUpdateCntl, 0, 0x01); 337 // Done updating 338 339 Write32Mask(PLL, pllCntl, 0, 0x02); 340 // Power up PLL 341 snooze(2); 342 343 PLLCalibrate(pllIndex); 344 345 Write32(PLL, pllExtPostDivSrc, 0x01); 346 // Set source as PLL 347 348 // TODO : better way to grab crt to work on? 349 PLLCRTCGrab(pllIndex, gRegister->crtid); 350 } 351 352 353 void 354 PLLSetLowR620(uint8 pllIndex, uint32 pixelClock, uint16 reference, 355 uint16 feedback, uint16 post) 356 { 357 radeon_shared_info &info = *gInfo->shared_info; 358 359 bool hasDccg = DCCGCLKAvailable(pllIndex); 360 361 TRACE("%s: card has DCCG = %c\n", __func__, hasDccg ? 'y' : 'n'); 362 363 if (hasDccg) 364 DCCGCLKSet(pllIndex, RV620_DCCGCLK_RESET); 365 366 /* Internal PLL Registers */ 367 uint16 pllCntl = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL; 368 uint16 pllIntSSCntl 369 = pllIndex == 1 ? P2PLL_INT_SS_CNTL : P1PLL_INT_SS_CNTL; 370 371 /* External PLL Registers */ 372 uint16 pllExtCntl 373 = pllIndex == 1 ? EXT2_PPLL_CNTL : EXT1_PPLL_CNTL; 374 //uint16 pllExtUpdateCntl 375 // = pllIndex == 1 ? EXT2_PPLL_UPDATE_CNTL : EXT1_PPLL_UPDATE_CNTL; 376 uint16 pllExtUpdateLock 377 = pllIndex == 1 ? EXT2_PPLL_UPDATE_LOCK : EXT1_PPLL_UPDATE_LOCK; 378 uint16 pllExtPostDiv 379 = pllIndex == 1 ? EXT2_PPLL_POST_DIV : EXT1_PPLL_POST_DIV; 380 uint16 pllExtPostDivSrc 381 = pllIndex == 1 ? EXT2_PPLL_POST_DIV_SRC : EXT1_PPLL_POST_DIV_SRC; 382 uint16 pllExtPostDivSym 383 = pllIndex == 1 ? EXT2_SYM_PPLL_POST_DIV : EXT1_SYM_PPLL_POST_DIV; 384 uint16 pllExtFeedbackDiv 385 = pllIndex == 1 ? EXT2_PPLL_FB_DIV : EXT1_PPLL_FB_DIV; 386 uint16 pllExtRefDiv 387 = pllIndex == 1 ? EXT2_PPLL_REF_DIV : EXT1_PPLL_REF_DIV; 388 //uint16 pllExtRefDivSrc 389 // = pllIndex == 1 ? EXT2_PPLL_REF_DIV_SRC : EXT1_PPLL_REF_DIV_SRC; 390 uint16 pllExtDispClkCntl 391 = pllIndex == 1 ? P2PLL_DISP_CLK_CNTL : P1PLL_DISP_CLK_CNTL; 392 393 Write32Mask(PLL, pllIntSSCntl, 0, 0x00000001); 394 // Disable Spread Spectrum 395 396 uint32 referenceDivider = reference; 397 398 uint32 feedbackDivider = Read32(PLL, pllExtFeedbackDiv) & ~0x07FF003F; 399 feedbackDivider |= ((feedback << 16) | 0x0030) & 0x07FF003F; 400 401 uint32 postDivider = Read32(PLL, pllExtPostDiv) & ~0x0000007F; 402 postDivider |= post & 0x0000007F; 403 404 uint32 control; 405 406 if (info.device_chipset >= (RADEON_R600 | 0x70)) 407 control = PLLControlTable(RV670PLLControl, feedback); 408 else 409 control = PLLControlTable(RV610PLLControl, feedback); 410 411 uint8 symPostDiv = post & 0x0000007F; 412 413 /* switch to external */ 414 Write32(PLL, pllExtPostDivSrc, 0); 415 Write32Mask(PLL, pllExtDispClkCntl, 0x00000200, 0x00000300); 416 Write32Mask(PLL, pllExtPostDiv, 0, 0x00000100); 417 418 Write32Mask(PLL, pllCntl, 0x00000001, 0x00000001); 419 // reset 420 snooze(2); 421 Write32Mask(PLL, pllCntl, 0x00000002, 0x00000002); 422 // power down 423 snooze(10); 424 Write32Mask(PLL, pllCntl, 0x00002000, 0x00002000); 425 // reset antiglitch 426 427 Write32(PLL, pllExtCntl, control); 428 429 Write32Mask(PLL, pllExtDispClkCntl, 2, 0x0000003F); 430 // Scalar Divider 2 431 432 Write32(PLL, pllExtUpdateLock, 1); 433 // Lock PLL 434 435 /* Write PLL clocks */ 436 Write32(PLL, pllExtPostDivSrc, 0x00000001); 437 Write32(PLL, pllExtRefDiv, referenceDivider); 438 Write32(PLL, pllExtFeedbackDiv, feedbackDivider); 439 Write32Mask(PLL, pllExtPostDiv, postDivider, 0x0000007F); 440 Write32Mask(PLL, pllExtPostDivSym, symPostDiv, 0x0000007F); 441 442 snooze(10); 443 444 Write32(PLL, pllExtUpdateLock, 0); 445 // Unlock PLL 446 447 Write32Mask(PLL, pllCntl, 0, 0x00000002); 448 // power up 449 snooze(10); 450 451 Write32Mask(PLL, pllCntl, 0, 0x00002000); 452 // undo reset antiglitch 453 454 PLLCalibrate(pllIndex); 455 456 /* Switch back to PLL */ 457 Write32Mask(PLL, pllExtDispClkCntl, 0, 0x00000300); 458 Write32Mask(PLL, pllExtPostDivSym, 0x00000100, 0x00000100); 459 Write32(PLL, pllExtPostDivSrc, 0x00000001); 460 461 Write32Mask(PLL, pllCntl, 0, 0x80000000); 462 // needed and undocumented 463 464 // TODO : better way to grab crt to work on? 465 PLLCRTCGrab(pllIndex, gRegister->crtid); 466 467 if (hasDccg) 468 DCCGCLKSet(pllIndex, RV620_DCCGCLK_GRAB); 469 470 TRACE("%s: PLLSet exit\n", __func__); 471 } 472 473 474 status_t 475 PLLCalibrate(uint8 pllIndex) 476 { 477 uint16 pllControlReg = pllIndex == 1 ? P2PLL_CNTL : P1PLL_CNTL; 478 479 Write32Mask(PLL, pllControlReg, 1, 0x01); 480 // PLL Reset 481 482 snooze(2); 483 484 Write32Mask(PLL, pllControlReg, 0, 0x01); 485 // PLL Set 486 487 int i; 488 489 for (i = 0; i < PLL_CALIBRATE_WAIT; i++) { 490 if (((Read32(PLL, pllControlReg) >> 20) & 0x03) == 0x03) 491 break; 492 } 493 494 if (i >= PLL_CALIBRATE_WAIT) { 495 if (Read32(PLL, pllControlReg) & 0x00100000) /* Calibration done? */ 496 TRACE("%s: Calibration Failed\n", __func__); 497 if (Read32(PLL, pllControlReg) & 0x00200000) /* PLL locked? */ 498 TRACE("%s: Locking Failed\n", __func__); 499 TRACE("%s: We encountered a problem calibrating the PLL.\n", __func__); 500 return B_ERROR; 501 } else 502 TRACE("%s: pll calibrated and locked in %d loops\n", __func__, i); 503 504 return B_OK; 505 } 506 507 508 void 509 PLLCRTCGrab(uint8 pllIndex, uint8 crtid) 510 { 511 bool pll2IsCurrent; 512 513 if (crtid == 0) { 514 pll2IsCurrent = Read32(PLL, PCLK_CRTC1_CNTL) & 0x00010000; 515 516 Write32Mask(PLL, PCLK_CRTC1_CNTL, pllIndex == 0 ? 0x00010000 : 0, 517 0x00010000); 518 } else { 519 pll2IsCurrent = Read32(PLL, PCLK_CRTC2_CNTL) & 0x00010000; 520 521 Write32Mask(PLL, PCLK_CRTC2_CNTL, pllIndex == 0 ? 0x00010000 : 0, 522 0x00010000); 523 } 524 525 /* if the current pll is not active, then poke it just enough to flip 526 * owners */ 527 if (!pll2IsCurrent) { 528 uint32 stored = Read32(PLL, P1PLL_CNTL); 529 530 if (stored & 0x03) { 531 Write32Mask(PLL, P1PLL_CNTL, 0, 0x03); 532 snooze(10); 533 Write32Mask(PLL, P1PLL_CNTL, stored, 0x03); 534 } 535 536 } else { 537 uint32 stored = Read32(PLL, P2PLL_CNTL); 538 539 if (stored & 0x03) { 540 Write32Mask(PLL, P2PLL_CNTL, 0, 0x03); 541 snooze(10); 542 Write32Mask(PLL, P2PLL_CNTL, stored, 0x03); 543 } 544 } 545 } 546 547 548 // See if card has a DCCG available that we need to lock to 549 // the PLL clock. No one seems really sure what DCCG is. 550 bool 551 DCCGCLKAvailable(uint8 pllIndex) 552 { 553 radeon_shared_info &info = *gInfo->shared_info; 554 555 if (info.device_chipset < (RADEON_R600 | 0x20)) 556 return false; 557 558 uint32 dccg = Read32(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03; 559 560 if (dccg & 0x02) 561 return true; 562 563 if ((pllIndex == 0) && (dccg == 0)) 564 return true; 565 if ((pllIndex == 1) && (dccg == 1)) 566 return true; 567 568 return false; 569 } 570 571 572 void 573 DCCGCLKSet(uint8 pllIndex, int set) 574 { 575 uint32 buffer; 576 577 switch(set) { 578 case RV620_DCCGCLK_GRAB: 579 if (pllIndex == 0) 580 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003); 581 else if (pllIndex == 1) 582 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003); 583 else 584 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); 585 break; 586 case RV620_DCCGCLK_RELEASE: 587 buffer = Read32(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03; 588 589 if ((pllIndex == 0) && (buffer == 0)) { 590 /* set to other PLL or external */ 591 buffer = Read32(PLL, P2PLL_CNTL); 592 // if powered and not in reset, and calibrated and locked 593 if (!(buffer & 0x03) && ((buffer & 0x00300000) == 0x00300000)) 594 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003); 595 else 596 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); 597 598 } else if ((pllIndex == 1) && (buffer == 1)) { 599 /* set to other PLL or external */ 600 buffer = Read32(PLL, P1PLL_CNTL); 601 // if powered and not in reset, and calibrated and locked 602 if (!(buffer & 0x03) && ((buffer & 0x00300000) == 0x00300000)) 603 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003); 604 else 605 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); 606 607 } // no other action needed 608 break; 609 case RV620_DCCGCLK_RESET: 610 buffer = Read32(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03; 611 612 if (((pllIndex == 0) && (buffer == 0)) 613 || ((pllIndex == 1) && (buffer == 1))) 614 Write32Mask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); 615 break; 616 default: 617 break; 618 } 619 } 620 621