* 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:
parent
7c865cf94d
commit
e7e76b29e8
@ -12,6 +12,7 @@ Addon radeon_hd.accelerant :
|
||||
accelerant.cpp
|
||||
engine.cpp
|
||||
hooks.cpp
|
||||
pll.cpp
|
||||
mode.cpp
|
||||
bios.cpp
|
||||
create_display_modes.cpp
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
495
src/add-ons/accelerants/radeon_hd/pll.cpp
Normal file
495
src/add-ons/accelerants/radeon_hd/pll.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
52
src/add-ons/accelerants/radeon_hd/pll.h
Normal file
52
src/add-ons/accelerants/radeon_hd/pll.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user