From e7e76b29e839204be635b9f10dd52a41837e20bd Mon Sep 17 00:00:00 2001 From: Alexander von Gluck IV Date: Mon, 13 Jun 2011 04:07:42 +0000 Subject: [PATCH] * Define default PLL ranges * Add crtid to register struct * Disable VGA mode on FrameBuffer set (enables extended mode setting) * Disable blanking calculations and setting more gracefully via if 0 * Add a *large* amount of code to Set/Calculate/Calibrate PLL * Disable PLL on removal of accel. * Remove junk comments on overscan * Enable pixel clock limit pulling * write32AtMask style cleanup * Rename ReadMC to be more consistant (I need to adjust naming for these MMIO calls) * Implement read/write MC. (so many hardcoded oneoffs AMD) * Implement write32PLLAtMask MMIO call git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42146 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/accelerants/radeon_hd/Jamfile | 1 + .../accelerants/radeon_hd/accelerant.cpp | 12 + .../accelerants/radeon_hd/accelerant.h | 70 ++- src/add-ons/accelerants/radeon_hd/bios.cpp | 29 +- src/add-ons/accelerants/radeon_hd/mode.cpp | 58 +- src/add-ons/accelerants/radeon_hd/pll.cpp | 495 ++++++++++++++++++ src/add-ons/accelerants/radeon_hd/pll.h | 52 ++ 7 files changed, 665 insertions(+), 52 deletions(-) create mode 100644 src/add-ons/accelerants/radeon_hd/pll.cpp create mode 100644 src/add-ons/accelerants/radeon_hd/pll.h diff --git a/src/add-ons/accelerants/radeon_hd/Jamfile b/src/add-ons/accelerants/radeon_hd/Jamfile index 871733a016..c6613d21e5 100644 --- a/src/add-ons/accelerants/radeon_hd/Jamfile +++ b/src/add-ons/accelerants/radeon_hd/Jamfile @@ -12,6 +12,7 @@ Addon radeon_hd.accelerant : accelerant.cpp engine.cpp hooks.cpp + pll.cpp mode.cpp bios.cpp create_display_modes.cpp diff --git a/src/add-ons/accelerants/radeon_hd/accelerant.cpp b/src/add-ons/accelerants/radeon_hd/accelerant.cpp index 60bc77b532..e60b888c95 100644 --- a/src/add-ons/accelerants/radeon_hd/accelerant.cpp +++ b/src/add-ons/accelerants/radeon_hd/accelerant.cpp @@ -12,6 +12,7 @@ #include "accelerant.h" #include "utility.h" +#include "pll.h" #include #include @@ -141,6 +142,12 @@ init_common(int device, bool isClone) sharedCloner.Keep(); regsCloner.Keep(); + // Define Radeon PLL default ranges + gInfo->shared_info->pll_info.reference_frequency + = RHD_PLL_REFERENCE_DEFAULT; + gInfo->shared_info->pll_info.min_frequency = RHD_PLL_MIN_DEFAULT; + gInfo->shared_info->pll_info.max_frequency = RHD_PLL_MAX_DEFAULT; + return B_OK; } @@ -270,6 +277,8 @@ init_registers(uint8 crtid) // Populate common registers // TODO : Wait.. this doesn't work with Eyefinity > crt 1. + gRegister->crtid = crtid; + gRegister->modeCenter = (crtid == 1) ? D2MODE_CENTER : D1MODE_CENTER; gRegister->crtHPolarity @@ -351,6 +360,9 @@ radeon_uninit_accelerant(void) gInfo->mode_list = NULL; + PLLPower(1, RHD_POWER_SHUTDOWN); + // Power down PLL + radeon_shared_info &info = *gInfo->shared_info; uninit_lock(&info.accelerant_lock); diff --git a/src/add-ons/accelerants/radeon_hd/accelerant.h b/src/add-ons/accelerants/radeon_hd/accelerant.h index f602cbe6f0..646becdd86 100644 --- a/src/add-ons/accelerants/radeon_hd/accelerant.h +++ b/src/add-ons/accelerants/radeon_hd/accelerant.h @@ -12,6 +12,7 @@ #include "mode.h" #include "radeon_hd.h" +#include "pll.h" #include @@ -39,6 +40,7 @@ struct accelerant_info { struct register_info { + uint16_t crtid; uint16_t grphEnable; uint16_t grphControl; uint16_t grphSwapControl; @@ -103,33 +105,65 @@ write32(uint32 offset, uint32 value) inline void -write32AtMask(uint32 adress, uint32 value, uint32 mask) +write32AtMask(uint32 offset, uint32 value, uint32 mask) { - uint32 temp; - temp = read32(adress); + uint32 temp = read32(offset); temp &= ~mask; temp |= value & mask; - write32(adress, temp); + write32(offset, temp); } -inline uint32_t -ReadMC(int screenIndex, uint32_t addr) +inline uint32 +read32MC(uint32 offset) { - // TODO : readMC for R5XX - return 0; + radeon_shared_info &info = *gInfo->shared_info; + + if (info.device_chipset == RADEON_R600) { + write32(RS600_MC_INDEX, ((offset & RS600_MC_INDEX_ADDR_MASK) + | RS600_MC_INDEX_CITF_ARB0)); + return read32(RS600_MC_DATA); + } else if (info.device_chipset == (RADEON_R600 & 0x90) + || info.device_chipset == (RADEON_R700 & 0x40)) { + write32(RS690_MC_INDEX, (offset & RS690_MC_INDEX_ADDR_MASK)); + return read32(RS690_MC_DATA); + } else if (info.device_chipset == (RADEON_R700 & 0x80) + || info.device_chipset == (RADEON_R800 & 0x80)) { + write32(RS780_MC_INDEX, offset & RS780_MC_INDEX_ADDR_MASK); + return read32(RS780_MC_DATA); + } + + // eh. + return read32(offset); } inline void -WriteMC(int screenIndex, uint32_t addr, uint32_t data) +write32MC(uint32 offset, uint32 data) { - // TODO : writeMC for R5XX + radeon_shared_info &info = *gInfo->shared_info; + + if (info.device_chipset == RADEON_R600) { + write32(RS600_MC_INDEX, ((offset & RS600_MC_INDEX_ADDR_MASK) + | RS600_MC_INDEX_CITF_ARB0 | RS600_MC_INDEX_WR_EN)); + write32(RS600_MC_DATA, data); + } else if (info.device_chipset == (RADEON_R600 & 0x90) + || info.device_chipset == (RADEON_R700 & 0x40)) { + write32(RS690_MC_INDEX, ((offset & RS690_MC_INDEX_ADDR_MASK) + | RS690_MC_INDEX_WR_EN)); + write32(RS690_MC_DATA, data); + write32(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK); + } else if (info.device_chipset == (RADEON_R700 & 0x80) + || info.device_chipset == (RADEON_R800 & 0x80)) { + write32(RS780_MC_INDEX, ((offset & RS780_MC_INDEX_ADDR_MASK) + | RS780_MC_INDEX_WR_EN)); + write32(RS780_MC_DATA, data); + } } -inline uint32_t -ReadPLL(int screenIndex, uint16_t offset) +inline uint32 +read32PLL(uint16 offset) { write32(CLOCK_CNTL_INDEX, offset & PLL_ADDR); return read32(CLOCK_CNTL_DATA); @@ -137,11 +171,21 @@ ReadPLL(int screenIndex, uint16_t offset) inline void -WritePLL(int screenIndex, uint16_t offset, uint32_t data) +write32PLL(uint16 offset, uint32 data) { write32(CLOCK_CNTL_INDEX, (offset & PLL_ADDR) | PLL_WR_EN); write32(CLOCK_CNTL_DATA, data); } +inline void +write32PLLAtMask(uint16 offset, uint32 value, uint32 mask) +{ + uint32 temp = read32PLL(offset); + temp &= ~mask; + temp |= value & mask; + write32PLL(offset, temp); +} + + #endif /* RADEON_HD_ACCELERANT_H */ diff --git a/src/add-ons/accelerants/radeon_hd/bios.cpp b/src/add-ons/accelerants/radeon_hd/bios.cpp index c35bff6e68..8da17c7a5b 100644 --- a/src/add-ons/accelerants/radeon_hd/bios.cpp +++ b/src/add-ons/accelerants/radeon_hd/bios.cpp @@ -128,48 +128,47 @@ CailWritePCIConfigData(VOID *CAIL, VOID *src, UINT32 idx, UINT16 size) ULONG -CailReadPLL(VOID *CAIL, ULONG Address) +CailReadPLL(VOID *CAIL, ULONG address) { - TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, Address); - // TODO : Assumed screen index 0 - return ReadPLL(0, Address); + TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, address); + return read32PLL(address); } VOID -CailWritePLL(VOID *CAIL, ULONG Address, ULONG Data) +CailWritePLL(VOID *CAIL, ULONG address, ULONG data) { - TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, Address); + TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, address); // TODO : save PLL registers - // atomSaveRegisters((atomBiosHandlePtr)CAIL, atomRegisterPLL, Address); + // atomSaveRegisters((atomBiosHandlePtr)CAIL, atomRegisterPLL, address); // TODO : Assumed screen index 0 - WritePLL(0, Address, Data); + write32PLL(address, data); } ULONG -CailReadMC(VOID *CAIL, ULONG Address) +CailReadMC(VOID *CAIL, ULONG address) { - TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, Address); + TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, address); // TODO : CailReadMC ULONG ret = 0; - // ret = RHDReadMC(((atomBiosHandlePtr)CAIL), Address | MC_IND_ALL); + // ret = RHDReadMC(((atomBiosHandlePtr)CAIL), address | MC_IND_ALL); return ret; } VOID -CailWriteMC(VOID *CAIL, ULONG Address, ULONG data) +CailWriteMC(VOID *CAIL, ULONG address, ULONG data) { - TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, Address); + TRACE("AtomBios callback %s, addr (0x%X)\n", __func__, address); // TODO : CailWriteMC - // atomSaveRegisters((atomBiosHandlePtr)CAIL, atomRegisterMC, Address); + // atomSaveRegisters((atomBiosHandlePtr)CAIL, atomRegisterMC, address); // RHDWriteMC(((atomBiosHandlePtr)CAIL), - // Address | MC_IND_ALL | MC_IND_WR_EN, data); + // address | MC_IND_ALL | MC_IND_WR_EN, data); } diff --git a/src/add-ons/accelerants/radeon_hd/mode.cpp b/src/add-ons/accelerants/radeon_hd/mode.cpp index 637ac2c64f..4b30e1efd4 100644 --- a/src/add-ons/accelerants/radeon_hd/mode.cpp +++ b/src/add-ons/accelerants/radeon_hd/mode.cpp @@ -148,6 +148,15 @@ CardFBSet(display_mode *mode) get_color_space_format(*mode, colorMode, bytesPerRow, bitsPerPixel); + // Disable VGA mode to enable Radeon extended registers + write32AtMask(VGA_RENDER_CONTROL, 0, 0x00030000); + write32AtMask(VGA_MODE_CONTROL, 0, 0x00000030); + write32AtMask(VGA_HDP_CONTROL, 0x00010010, 0x00010010); + write32AtMask(D1VGA_CONTROL, 0, D1VGA_MODE_ENABLE + | D1VGA_TIMING_SELECT | D1VGA_SYNC_POLARITY_SELECT); + write32AtMask(D2VGA_CONTROL, 0, D2VGA_MODE_ENABLE + | D2VGA_TIMING_SELECT | D1VGA_SYNC_POLARITY_SELECT); + // disable R/B swap, disable tiling, disable 16bit alpha, etc. write32AtMask(gRegister->grphEnable, 1, 0x00000001); write32(gRegister->grphControl, 0); @@ -217,8 +226,6 @@ CardFBSet(display_mode *mode) static void CardModeSet(display_mode *mode) { - CardBlankSet(true); - display_timing& displayTiming = mode->timing; TRACE("%s called to do %dx%d\n", @@ -231,12 +238,14 @@ CardModeSet(display_mode *mode) write32(gRegister->crtHTotal, displayTiming.h_total - 1); + #if 0 // determine blanking based on passed modeline - //uint16 blankStart = displayTiming.h_display + 1; - //uint16 blankEnd = displayTiming.h_total - 1; + uint16 blankStart = displayTiming.h_display; + uint16 blankEnd = displayTiming.h_total; - //write32(gRegister->crtHBlank, - // blankStart | (blankEnd << 16)); + write32(gRegister->crtHBlank, + blankStart | (blankEnd << 16)); + #endif write32(gRegister->crtHSync, (displayTiming.h_sync_end - displayTiming.h_sync_start) << 16); @@ -249,11 +258,13 @@ CardModeSet(display_mode *mode) write32(gRegister->crtVTotal, displayTiming.v_total - 1); - //blankStart = displayTiming.v_display + 1; - //blankEnd = displayTiming.v_total - 1; + #if 0 + blankStart = displayTiming.v_display; + blankEnd = displayTiming.v_total; - //write32(gRegister->crtVBlank, - // blankStart | (blankEnd << 16)); + write32(gRegister->crtVBlank, + blankStart | (blankEnd << 16)); + #endif // Set Interlace if specified within mode line if (displayTiming.flags & B_TIMING_INTERLACED) { @@ -276,8 +287,6 @@ CardModeSet(display_mode *mode) should only be set to 1 on 30bpp DVI modes */ write32AtMask(gRegister->crtCountControl, 0x0, 0x1); - - CardBlankSet(false); } @@ -290,15 +299,9 @@ CardModeScale(display_mode *mode) // For now, no overscan support write32(D1MODE_EXT_OVERSCAN_LEFT_RIGHT, - (0 << 16) | 0); + (0 << 16) | 0); // LEFT | RIGHT write32(D1MODE_EXT_OVERSCAN_TOP_BOTTOM, - (0 << 16) | 0); - -/* write32(regOffset + D1MODE_EXT_OVERSCAN_LEFT_RIGHT, - (Overscan.OverscanLeft << 16) | Overscan.OverscanRight); - write32(regOffset + D1MODE_EXT_OVERSCAN_TOP_BOTTOM, - (Overscan.OverscanTop << 16) | Overscan.OverscanBottom); -*/ + (0 << 16) | 0); // TOP | BOTTOM // No scaling write32(gRegister->sclUpdate, (1<<16)); // Lock @@ -333,9 +336,15 @@ radeon_set_display_mode(display_mode *mode) int crtNumber = 0; init_registers(crtNumber); + + CardBlankSet(true); CardFBSet(mode); CardModeSet(mode); CardModeScale(mode); + PLLSet(1, mode->timing.pixel_clock); + PLLPower(1, RHD_POWER_ON); + DACPower(1, RHD_POWER_ON); + CardBlankSet(false); int32 crtstatus = read32(D1CRTC_STATUS); TRACE("CRT0 Status: 0x%X\n", crtstatus); @@ -372,12 +381,13 @@ status_t radeon_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high) { TRACE("%s\n", __func__); -/* + if (_low != NULL) { // lower limit of about 48Hz vertical refresh uint32 totalClocks = (uint32)mode->timing.h_total *(uint32)mode->timing.v_total; uint32 low = (totalClocks * 48L) / 1000L; + if (low < gInfo->shared_info->pll_info.min_frequency) low = gInfo->shared_info->pll_info.min_frequency; else if (low > gInfo->shared_info->pll_info.max_frequency) @@ -388,9 +398,9 @@ radeon_get_pixel_clock_limits(display_mode *mode, uint32 *_low, uint32 *_high) if (_high != NULL) *_high = gInfo->shared_info->pll_info.max_frequency; -*/ - *_low = 48L; - *_high = 100 * 1000000L; + + //*_low = 48L; + //*_high = 100 * 1000000L; return B_OK; } diff --git a/src/add-ons/accelerants/radeon_hd/pll.cpp b/src/add-ons/accelerants/radeon_hd/pll.cpp new file mode 100644 index 0000000000..85cdc67a30 --- /dev/null +++ b/src/add-ons/accelerants/radeon_hd/pll.cpp @@ -0,0 +1,495 @@ +/* + * Copyright 2006-2011, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Alexander von Gluck, kallisti5@unixzen.com + */ + + +#include "accelerant_protos.h" +#include "accelerant.h" +#include "utility.h" +#include "pll.h" + +#include +#include +#include +#include + + +#define TRACE_PLL +#ifdef TRACE_PLL +extern "C" void _sPrintf(const char *format, ...); +# define TRACE(x...) _sPrintf("radeon_hd: " x) +#else +# define TRACE(x...) ; +#endif + + +/* From hardcoded values. */ +static struct PLL_Control RV610PLLControl[] = +{ + { 0x0049, 0x159F8704 }, + { 0x006C, 0x159B8704 }, + { 0xFFFF, 0x159EC704 } +}; + +/* Some tables are provided by atombios, + * it's just that they are hidden away deliberately and not exposed */ +static struct PLL_Control RV670PLLControl[] = +{ + { 0x004A, 0x159FC704 }, + { 0x0067, 0x159BC704 }, + { 0x00C4, 0x159EC704 }, + { 0x00F4, 0x1593A704 }, + { 0x0136, 0x1595A704 }, + { 0x01A4, 0x1596A704 }, + { 0x022C, 0x159CE504 }, + { 0xFFFF, 0x1591E404 } +}; + + +static uint32 +PLLControlTable(struct PLL_Control *table, uint16 feedbackDivider) +{ + int i; + + for (i = 0; table[i].feedbackDivider < 0xFFFF ; i++) + if (table[i].feedbackDivider >= feedbackDivider) + break; + + return table[i].control; +} + + +status_t +PLLCalculate(uint32 pixelClock, uint16 *reference, uint16 *feedback, + uint16 *post) +{ + + // Freaking phase-locked loops, how do they work? + + float ratio = ((float) pixelClock) + / ((float) gInfo->shared_info->pll_info.reference_frequency); + + uint32 bestDiff = 0xFFFFFFFF; + uint32 postDiv; + uint32 referenceDiv; + uint32 feedbackDiv; + + for (postDiv = 2; postDiv < POST_DIV_LIMIT; postDiv++) { + uint32 vcoOut = pixelClock * postDiv; + + /* we are conservative and avoid the limits */ + if (vcoOut <= gInfo->shared_info->pll_info.min_frequency) + continue; + if (vcoOut >= gInfo->shared_info->pll_info.max_frequency) + break; + + + for (referenceDiv = 1; referenceDiv <= REF_DIV_LIMIT; referenceDiv++) { + uint32 diff; + + feedbackDiv = (uint32)((ratio * postDiv * referenceDiv) + 0.5); + + if (feedbackDiv >= FB_DIV_LIMIT) + break; + if (feedbackDiv > (500 + (13 * referenceDiv))) // rv6x0 limit + break; + + diff = abs(pixelClock - (feedbackDiv + * gInfo->shared_info->pll_info.reference_frequency) + / (postDiv * referenceDiv)); + + if (diff < bestDiff) { + *feedback = feedbackDiv; + *reference = referenceDiv; + *post = postDiv; + bestDiff = diff; + } + + if (bestDiff == 0) + break; + } + + if (bestDiff == 0) + break; + } + + if (bestDiff != 0xFFFFFFFF) { + TRACE("%s: Successful PLL Calculation: %dkHz = " + "(((%i / 0x%X) * 0x%X) / 0x%X) (%dkHz off)\n", __func__, + (int) pixelClock, + (unsigned int) gInfo->shared_info->pll_info.reference_frequency, + *reference, *feedback, *post, (int) bestDiff); + return B_OK; + } + + // Shouldn't ever happen + TRACE("%s: Failed to get a valid PLL setting for %dkHz\n", + __func__, (int) pixelClock); + return B_ERROR; +} + + +status_t +PLLPower(uint8 pllIndex, int command) +{ + + uint16 pllControlReg = (pllIndex == 2) ? P2PLL_CNTL : P1PLL_CNTL; + + bool hasDccg = DCCGCLKAvailable(pllIndex); + + TRACE("%s: card has DCCG = %c\n", __func__, hasDccg ? 'y' : 'n'); + + switch (command) { + case RHD_POWER_ON: + { + TRACE("%s: PLL Power On\n", __func__); + + if (hasDccg) + DCCGCLKSet(pllIndex, RV620_DCCGCLK_RESET); + + write32PLLAtMask(pllControlReg, 0, 0x02); + // Power On + snooze(2); + PLLCalibrate(pllIndex); + + if (hasDccg) + DCCGCLKSet(pllIndex, RV620_DCCGCLK_GRAB); + + return B_OK; + } + case RHD_POWER_RESET: + { + TRACE("%s: PLL Power Reset\n", __func__); + + if (hasDccg) + DCCGCLKSet(pllIndex, RV620_DCCGCLK_RELEASE); + + write32PLLAtMask(pllControlReg, 0x01, 0x01); + // Reset + snooze(2); + write32PLLAtMask(pllControlReg, 0, 0x02); + // Power On + snooze(2); + return B_OK; + } + + case RHD_POWER_SHUTDOWN: + default: + TRACE("%s: PLL Power Shutdown\n", __func__); + + if (hasDccg) + DCCGCLKSet(pllIndex, RV620_DCCGCLK_RELEASE); + + write32PLLAtMask(pllControlReg, 0x01, 0x01); + // Reset + snooze(2); + + // Sometimes we have to keep an unused PLL running. Xorg Bug #18016 + if ((read32PLL(RV620_EXT1_DIFF_POST_DIV_CNTL) + & RV62_EXT1_DIFF_DRIVER_ENABLE) == 0) { + write32PLLAtMask(pllControlReg, 0x02, 0x02); + // Power Down + } else { + TRACE("%s: PHYA differential clock driver not disabled\n", + __func__); + } + + snooze(200); + + write32PLLAtMask(pllControlReg, 0x2000, 0x2000); + // Reset anti-glitch? + } + + return B_OK; +} + + +status_t +PLLSet(uint8 pllIndex, uint32 pixelClock) +{ + + TRACE("%s: enter to set pixel clock %d\n", __func__, + (int)pixelClock); + + radeon_shared_info &info = *gInfo->shared_info; + + bool hasDccg = DCCGCLKAvailable(pllIndex); + + TRACE("%s: card has DCCG = %c\n", __func__, hasDccg ? 'y' : 'n'); + + uint16 reference = 0; + uint16 feedback = 0; + uint16 post = 0; + + PLLCalculate(pixelClock, &reference, &feedback, &post); + + if (hasDccg) + DCCGCLKSet(pllIndex, RV620_DCCGCLK_RESET); + + uint16 pllLockReg + = (pllIndex == 2) ? EXT2_PPLL_UPDATE_LOCK : EXT1_PPLL_UPDATE_LOCK; + uint16 pllControlReg = (pllIndex == 2) ? P2PLL_CNTL : P1PLL_CNTL; + uint16 pllExtControlReg = (pllIndex == 2) ? EXT2_PPLL_CNTL : EXT1_PPLL_CNTL; + uint16 pllDisplayClockControlReg + = (pllIndex == 2) ? P2PLL_DISP_CLK_CNTL : P1PLL_DISP_CLK_CNTL; + uint16 pllIntSSControlReg + = (pllIndex == 2) ? P2PLL_INT_SS_CNTL : P1PLL_INT_SS_CNTL; + uint16 pllReferenceDividerReg + = (pllIndex == 2) ? EXT2_PPLL_REF_DIV : EXT1_PPLL_REF_DIV; + uint16 pllFeedbackDividerReg + = (pllIndex == 2) ? EXT2_PPLL_FB_DIV : EXT1_PPLL_FB_DIV; + uint16 pllPostDividerReg + = (pllIndex == 2) ? EXT2_PPLL_POST_DIV : EXT1_PPLL_POST_DIV; + uint16 pplPostDividerSymReg + = (pllIndex == 2) ? EXT2_SYM_PPLL_POST_DIV : EXT1_SYM_PPLL_POST_DIV; + uint16 pllPostDividerSrcReg + = (pllIndex == 2) ? EXT2_PPLL_POST_DIV_SRC : EXT1_PPLL_POST_DIV_SRC; + + write32PLLAtMask(pllIntSSControlReg, 0, 0x00000001); + // Disable Spread Spectrum + + uint32 referenceDivider = reference; + + uint32 feedbackDivider = read32PLL(pllFeedbackDividerReg) & ~0x07FF003F; + feedbackDivider |= ((feedback << 16) | 0x0030) & 0x07FF003F; + + uint32 postDivider = read32PLL(pllPostDividerReg) & ~0x0000007F; + postDivider |= post & 0x0000007F; + + uint32 control; + if (info.device_chipset >= (RADEON_R600 & 0x70)) + control = PLLControlTable(RV670PLLControl, feedback); + else + control = PLLControlTable(RV610PLLControl, feedback); + + uint8 symPostDiv = post & 0x0000007F; + + /* switch to external */ + write32PLL(pllPostDividerSrcReg, 0); + write32PLLAtMask(pllDisplayClockControlReg, 0x00000200, 0x00000300); + write32PLLAtMask(pllPostDividerReg, 0, 0x00000100); + + write32PLLAtMask(pllControlReg, 0x00000001, 0x00000001); + // reset + snooze(2); + write32PLLAtMask(pllControlReg, 0x00000002, 0x00000002); + // power down + snooze(10); + write32PLLAtMask(pllControlReg, 0x00002000, 0x00002000); + // reset antiglitch + + write32PLL(pllExtControlReg, control); + + write32PLLAtMask(pllDisplayClockControlReg, 2, 0x0000003F); + // Scalar Divider 2 + + write32PLL(pllLockReg, 1); + // Lock PLL + + /* Write PLL clocks */ + write32PLL(pllPostDividerSrcReg, 0x00000001); + write32PLL(pllReferenceDividerReg, referenceDivider); + write32PLL(pllFeedbackDividerReg, feedbackDivider); + write32PLLAtMask(pllPostDividerReg, postDivider, 0x0000007F); + write32PLLAtMask(pplPostDividerSymReg, symPostDiv, 0x0000007F); + + snooze(10); + + write32PLL(pllLockReg, 0); + // Unlock PLL + + write32PLLAtMask(pllControlReg, 0, 0x00000002); + // power up + snooze(10); + + write32PLLAtMask(pllControlReg, 0, 0x00002000); + // undo reset antiglitch + + PLLCalibrate(pllIndex); + + /* Switch back to PLL */ + write32PLLAtMask(pllDisplayClockControlReg, 0, 0x00000300); + write32PLLAtMask(pplPostDividerSymReg, 0x00000100, 0x00000100); + write32PLL(pllPostDividerSrcReg, 0x00000001); + + write32PLLAtMask(pllControlReg, 0, 0x80000000); + // needed and undocumented + + // TODO : If CRT2 ah-la R500PLLCRTCGrab + PLLCRTCGrab(pllIndex, false); + + if (hasDccg) + DCCGCLKSet(pllIndex, RV620_DCCGCLK_RELEASE); + + TRACE("%s: PLLSet exit\n", __func__); + return B_OK; +} + + +status_t +PLLCalibrate(uint8 pllIndex) +{ + + uint16 pllControlReg = (pllIndex == 2) ? P2PLL_CNTL : P1PLL_CNTL; + + write32PLLAtMask(pllControlReg, 1, 0x01); + // PLL Reset + + snooze(2); + + write32PLLAtMask(pllControlReg, 0, 0x01); + // PLL Set + + int i; + + for (i = 0; i < PLL_CALIBRATE_WAIT; i++) + if (((read32PLL(pllControlReg) >> 20) & 0x03) == 0x03) + break; + + if (i == PLL_CALIBRATE_WAIT) { + if (read32PLL(pllControlReg) & 0x00100000) /* Calibration done? */ + TRACE("%s: Calibration Failed\n"); + if (read32PLL(pllControlReg) & 0x00200000) /* PLL locked? */ + TRACE("%s: Locking Failed\n"); + } else + TRACE("%s: pll calibrated and locked in %d loops\n", __func__, i); + + return B_OK; +} + + +void +PLLCRTCGrab(uint8 pllIndex, bool crt2) +{ + + uint32 stored; + bool pll2IsCurrent; + + if (!crt2) { + pll2IsCurrent = read32PLL(PCLK_CRTC1_CNTL) & 0x00010000; + + write32PLLAtMask(PCLK_CRTC1_CNTL, (pllIndex == 2) ? 0x00010000 : 0, + 0x00010000); + } else { + pll2IsCurrent = read32PLL(PCLK_CRTC2_CNTL) & 0x00010000; + + write32PLLAtMask(PCLK_CRTC2_CNTL, (pllIndex == 2) ? 0x00010000 : 0, + 0x00010000); + } + + /* if the current pll is not active, then poke it just enough to flip + * owners */ + if (!pll2IsCurrent) { + stored = read32PLL(P1PLL_CNTL); + + if (stored & 0x03) { + write32PLLAtMask(P1PLL_CNTL, 0, 0x03); + snooze(10); + write32PLLAtMask(P1PLL_CNTL, stored, 0x03); + } + + } else { + stored = read32PLL(P2PLL_CNTL); + + if (stored & 0x03) { + write32PLLAtMask(P2PLL_CNTL, 0, 0x03); + snooze(10); + write32PLLAtMask(P2PLL_CNTL, stored, 0x03); + } + } +} + + +bool +DCCGCLKAvailable(uint8 pllIndex) +{ + uint32 dccg = read32PLL(DCCG_DISP_CLK_SRCSEL) & 0x03; + + if (dccg & 0x02) + return true; + + if ((pllIndex == 1) && (dccg == 0)) + return true; + if ((pllIndex == 2) && (dccg == 1)) + return true; + + return false; +} + + +void +DCCGCLKSet(uint8 pllIndex, int set) +{ + uint32 buffer; + + switch(set) { + case RV620_DCCGCLK_GRAB: + if (pllIndex == 1) + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 0, 0x00000003); + else if (pllIndex == 2) + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 1, 0x00000003); + else + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); + break; + case RV620_DCCGCLK_RELEASE: + buffer = read32PLL(DCCG_DISP_CLK_SRCSEL) & 0x03; + + if ((pllIndex == 1) && (buffer == 0)) { + /* set to other PLL or external */ + buffer = read32PLL(P2PLL_CNTL); + // if powered and not in reset, and calibrated and locked + if (!(buffer & 0x03) && ((buffer & 0x00300000) == 0x00300000)) + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 1, 0x00000003); + else + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); + + } else if ((pllIndex == 2) && (buffer == 1)) { + /* set to other PLL or external */ + buffer = read32PLL(P1PLL_CNTL); + // if powered and not in reset, and calibrated and locked + if (!(buffer & 0x03) && ((buffer & 0x00300000) == 0x00300000)) + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 0, 0x00000003); + else + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); + + } // no other action needed + break; + case RV620_DCCGCLK_RESET: + buffer = read32PLL(DCCG_DISP_CLK_SRCSEL) & 0x03; + + if (((pllIndex == 1) && (buffer == 0)) + || ((pllIndex == 2) && (buffer == 1))) + write32PLLAtMask(DCCG_DISP_CLK_SRCSEL, 3, 0x00000003); + break; + default: + break; + } +} + + +void +DACPower(uint8 dacIndex, int mode) +{ + uint32 dacOffset = (dacIndex == 2) ? REG_DACB_OFFSET : REG_DACA_OFFSET; + uint32 powerdown; + + switch (mode) { + case RHD_POWER_ON: + // TODO : SensedType Detection? + powerdown = 0; + write32(dacOffset + DACA_ENABLE, 1); + write32(dacOffset + DACA_POWERDOWN, 0); + snooze(14); + write32AtMask(dacOffset + DACA_POWERDOWN, powerdown, 0xFFFFFF00); + snooze(2); + write32(dacOffset + DACA_FORCE_OUTPUT_CNTL, 0); + write32AtMask(dacOffset + DACA_SYNC_SELECT, 0, 0x00000101); + write32(dacOffset + DACA_SYNC_TRISTATE_CONTROL, 0); + return; + } +} + diff --git a/src/add-ons/accelerants/radeon_hd/pll.h b/src/add-ons/accelerants/radeon_hd/pll.h new file mode 100644 index 0000000000..3f35ddc3d0 --- /dev/null +++ b/src/add-ons/accelerants/radeon_hd/pll.h @@ -0,0 +1,52 @@ +/* + * Copyright 2006-2011, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Alexander von Gluck, kallisti5@unixzen.com + */ +#ifndef RADEON_HD_PLL_H +#define RADEON_HD_PLL_H + + +#define RHD_PLL_MIN_DEFAULT 16000 +#define RHD_PLL_MAX_DEFAULT 400000 +#define RHD_PLL_REFERENCE_DEFAULT 27000 + +#define PLL_CALIBRATE_WAIT 0x100000 + +/* limited by the number of bits available */ +#define FB_DIV_LIMIT 2048 +#define REF_DIV_LIMIT 1024 +#define POST_DIV_LIMIT 128 + +// DCCGClk Operation Modes +#define RV620_DCCGCLK_RESET 0 +#define RV620_DCCGCLK_GRAB 1 +#define RV620_DCCGCLK_RELEASE 2 + +// DAC Offsets +#define REG_DACA_OFFSET 0 +#define REG_DACB_OFFSET 0x200 +#define RV620_REG_DACA_OFFSET 0 +#define RV620_REG_DACB_OFFSET 0x100 + + +struct PLL_Control { + uint16 feedbackDivider; // 0xFFFF is the endmarker + uint32 control; +}; + + +status_t PLLCalculate(uint32 pixelClock, uint16 *reference, uint16 *feedback, + uint16 *post); +status_t PLLSet(uint8 pllIndex, uint32 pixelClock); +status_t PLLPower(uint8 pllIndex, int command); +status_t PLLCalibrate(uint8 pllIndex); +void PLLCRTCGrab(uint8 pllIndex, bool crt2); +bool DCCGCLKAvailable(uint8 pllIndex); +void DCCGCLKSet(uint8 pllIndex, int set); +void DACPower(uint8 dacIndex, int mode); + + +#endif /* RADEON_HD_PLL_H */