* 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
This commit is contained in:
Alexander von Gluck IV 2011-06-13 04:07:42 +00:00
parent 7c865cf94d
commit e7e76b29e8
7 changed files with 665 additions and 52 deletions

View File

@ -12,6 +12,7 @@ Addon radeon_hd.accelerant :
accelerant.cpp
engine.cpp
hooks.cpp
pll.cpp
mode.cpp
bios.cpp
create_display_modes.cpp

View File

@ -12,6 +12,7 @@
#include "accelerant.h"
#include "utility.h"
#include "pll.h"
#include <errno.h>
#include <stdlib.h>
@ -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);

View File

@ -12,6 +12,7 @@
#include "mode.h"
#include "radeon_hd.h"
#include "pll.h"
#include <edid.h>
@ -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 */

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#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;
}
}

View File

@ -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 */