#include "accelerant.h"
#include "rage128.h"
#include <unistd.h>
struct DisplayParams {
uint32 crtc_gen_cntl;
uint32 crtc_h_total_disp;
uint32 crtc_h_sync_strt_wid;
uint32 crtc_v_total_disp;
uint32 crtc_v_sync_strt_wid;
uint32 crtc_pitch;
uint32 dda_config;
uint32 dda_on_off;
int feedback_div;
int post_div;
uint32 ppll_ref_div;
uint32 ppll_div_3;
};
static inline int
DivideWithRounding(int n, int d)
{
return (n + (d / 2)) / d;
}
static int
MinimumBits(uint32 value)
{
if (value == 0)
return 1;
int numBits = 0;
while (value != 0) {
value >>= 1;
numBits++;
}
return numBits;
}
static bool
CalculateCrtcRegisters(const DisplayModeEx& mode, DisplayParams& params)
{
const uint8 hSyncFudge[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
uint32 format;
switch (mode.bitsPerPixel) {
case 8:
format = 2;
break;
case 15:
format = 3;
break;
case 16:
format = 4;
break;
case 32:
format = 6;
break;
default:
TRACE("Unsupported color depth: %d bits/pixel\n", mode.bitsPerPixel);
return false;
}
params.crtc_gen_cntl = (R128_CRTC_EXT_DISP_EN
| R128_CRTC_EN
| (format << 8));
params.crtc_h_total_disp = (((mode.timing.h_total / 8) - 1) & 0xffff)
| (((mode.timing.h_display / 8) - 1) << 16);
int hSyncWidth = (mode.timing.h_sync_end - mode.timing.h_sync_start) / 8;
if (hSyncWidth <= 0)
hSyncWidth = 1;
if (hSyncWidth > 0x3f)
hSyncWidth = 0x3f;
int hSyncStart = mode.timing.h_sync_start - 8 + hSyncFudge[format - 1];
params.crtc_h_sync_strt_wid = (hSyncStart & 0xfff) | (hSyncWidth << 16)
| ((mode.timing.flags & B_POSITIVE_HSYNC) ? 0 : R128_CRTC_H_SYNC_POL);
params.crtc_v_total_disp = (((mode.timing.v_total - 1) & 0xffff)
| ((mode.timing.v_display - 1) << 16));
int vSyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
if (vSyncWidth <= 0)
vSyncWidth = 1;
if (vSyncWidth > 0x1f)
vSyncWidth = 0x1f;
params.crtc_v_sync_strt_wid = ((mode.timing.v_sync_start - 1) & 0xfff)
| (vSyncWidth << 16)
| ((mode.timing.flags & B_POSITIVE_VSYNC) ? 0 : R128_CRTC_V_SYNC_POL);
params.crtc_pitch = mode.timing.h_display / 8;
return true;
}
static bool
CalculateDDARegisters(const DisplayModeEx& mode, DisplayParams& params)
{
SharedInfo& si = *gInfo.sharedInfo;
R128_RAMSpec& memSpec = si.r128MemSpec;
R128_PLLParams& pll = si.r128PLLParams;
int displayFifoWidth = 128;
int displayFifoDepth = 32;
int xClkFreq = pll.xclk;
int vClkFreq = DivideWithRounding(pll.reference_freq * params.feedback_div,
pll.reference_div * params.post_div);
int bytesPerPixel = (mode.bitsPerPixel + 7) / 8;
int xClksPerTransfer = DivideWithRounding(xClkFreq * displayFifoWidth,
vClkFreq * bytesPerPixel * 8);
int useablePrecision = MinimumBits(xClksPerTransfer) + 1;
int xClksPerTransferPrecise = DivideWithRounding(
(xClkFreq * displayFifoWidth) << (11 - useablePrecision),
vClkFreq * bytesPerPixel * 8);
int rOff = xClksPerTransferPrecise * (displayFifoDepth - 4);
int rOn = (4 * memSpec.memBurstLen
+ 3 * MAX(memSpec.rasToCasDelay - 2, 0)
+ 2 * memSpec.rasPercentage
+ memSpec.writeRecovery
+ memSpec.casLatency
+ memSpec.readToWriteDelay
+ xClksPerTransfer) << (11 - useablePrecision);
if (rOn + memSpec.loopLatency >= rOff) {
TRACE("Error: (rOn = %d) + (loopLatency = %d) >= (rOff = %d)\n",
rOn, memSpec.loopLatency, rOff);
return false;
}
params.dda_config = xClksPerTransferPrecise | (useablePrecision << 16)
| (memSpec.loopLatency << 20);
params.dda_on_off = (rOn << 16) | rOff;
return true;
}
static bool
CalculatePLLRegisters(const DisplayModeEx& mode, DisplayParams& params)
{
struct Divider {
int divider;
int bitValue;
};
const Divider postDividers[] = {
{ 1, 0 },
{ 2, 1 },
{ 4, 2 },
{ 8, 3 },
{ 3, 4 },
{ 6, 6 },
{ 12, 7 }
};
R128_PLLParams& pll = gInfo.sharedInfo->r128PLLParams;
uint32 freq = mode.timing.pixel_clock / 10;
if (freq > pll.max_pll_freq)
freq = pll.max_pll_freq;
if (freq * 12 < pll.min_pll_freq)
freq = pll.min_pll_freq / 12;
int bitValue = -1;
uint32 output_freq;
for (int j = 0; j < (int)B_COUNT_OF(postDividers); j++) {
output_freq = postDividers[j].divider * freq;
if (output_freq >= pll.min_pll_freq && output_freq <= pll.max_pll_freq) {
params.feedback_div = DivideWithRounding(pll.reference_div * output_freq,
pll.reference_freq);
params.post_div = postDividers[j].divider;
bitValue = postDividers[j].bitValue;
break;
}
}
if (bitValue < 0) {
TRACE("CalculatePLLRegisters(), acceptable divider not found\n");
return false;
}
params.ppll_ref_div = pll.reference_div;
params.ppll_div_3 = (params.feedback_div | (bitValue << 16));
return true;
}
static void
PLLWaitForReadUpdateComplete()
{
while (GetPLLReg(R128_PPLL_REF_DIV) & R128_PPLL_ATOMIC_UPDATE_R)
;
}
static void
PLLWriteUpdate()
{
PLLWaitForReadUpdateComplete();
SetPLLReg(R128_PPLL_REF_DIV, R128_PPLL_ATOMIC_UPDATE_W, R128_PPLL_ATOMIC_UPDATE_W);
}
static void
SetRegisters(DisplayParams& params)
{
OUTREGM(R128_FP_GEN_CNTL, R128_FP_BLANK_DIS, R128_FP_BLANK_DIS);
OUTREG(R128_OVR_CLR, 0);
OUTREG(R128_OVR_WID_LEFT_RIGHT, 0);
OUTREG(R128_OVR_WID_TOP_BOTTOM, 0);
OUTREG(R128_OV0_SCALE_CNTL, 0);
OUTREG(R128_MPP_TB_CONFIG, 0);
OUTREG(R128_MPP_GP_CONFIG, 0);
OUTREG(R128_SUBPIC_CNTL, 0);
OUTREG(R128_VIPH_CONTROL, 0);
OUTREG(R128_I2C_CNTL_1, 0);
OUTREG(R128_GEN_INT_CNTL, 0);
OUTREG(R128_CAP0_TRIG_CNTL, 0);
OUTREG(R128_CAP1_TRIG_CNTL, 0);
uint32 busCntl = INREG(R128_BUS_CNTL);
if (busCntl & (R128_BUS_WRT_BURST | R128_BUS_READ_BURST)) {
busCntl |= R128_BUS_RD_DISCARD_EN | R128_BUS_RD_ABORT_EN;
OUTREG(R128_BUS_CNTL, busCntl);
}
OUTREG(R128_DDA_CONFIG, params.dda_config);
OUTREG(R128_DDA_ON_OFF, params.dda_on_off);
OUTREG(R128_CRTC_GEN_CNTL, params.crtc_gen_cntl);
OUTREGM(R128_DAC_CNTL, R128_DAC_MASK_ALL | R128_DAC_8BIT_EN,
~(R128_DAC_RANGE_CNTL | R128_DAC_BLANKING));
OUTREG(R128_CRTC_H_TOTAL_DISP, params.crtc_h_total_disp);
OUTREG(R128_CRTC_H_SYNC_STRT_WID, params.crtc_h_sync_strt_wid);
OUTREG(R128_CRTC_V_TOTAL_DISP, params.crtc_v_total_disp);
OUTREG(R128_CRTC_V_SYNC_STRT_WID, params.crtc_v_sync_strt_wid);
OUTREG(R128_CRTC_OFFSET, 0);
OUTREG(R128_CRTC_OFFSET_CNTL, 0);
OUTREG(R128_CRTC_PITCH, params.crtc_pitch);
OUTREGM(R128_CLOCK_CNTL_INDEX, R128_PLL_DIV_SEL, R128_PLL_DIV_SEL);
SetPLLReg(R128_VCLK_ECP_CNTL, R128_VCLK_SRC_SEL_CPUCLK, R128_VCLK_SRC_SEL_MASK);
SetPLLReg(R128_PPLL_CNTL, 0xffffffff,
R128_PPLL_RESET | R128_PPLL_ATOMIC_UPDATE_EN | R128_PPLL_VGA_ATOMIC_UPDATE_EN);
PLLWaitForReadUpdateComplete();
SetPLLReg(R128_PPLL_REF_DIV, params.ppll_ref_div, R128_PPLL_REF_DIV_MASK);
PLLWriteUpdate();
PLLWaitForReadUpdateComplete();
SetPLLReg(R128_PPLL_DIV_3, params.ppll_div_3,
R128_PPLL_FB3_DIV_MASK | R128_PPLL_POST3_DIV_MASK);
PLLWriteUpdate();
PLLWaitForReadUpdateComplete();
SetPLLReg(R128_HTOTAL_CNTL, 0);
PLLWriteUpdate();
SetPLLReg(R128_PPLL_CNTL, 0, R128_PPLL_RESET
| R128_PPLL_SLEEP
| R128_PPLL_ATOMIC_UPDATE_EN
| R128_PPLL_VGA_ATOMIC_UPDATE_EN);
snooze(5000);
SetPLLReg(R128_VCLK_ECP_CNTL, R128_VCLK_SRC_SEL_PPLLCLK,
R128_VCLK_SRC_SEL_MASK);
}
status_t
Rage128_SetDisplayMode(const DisplayModeEx& mode)
{
DisplayParams params;
if (gInfo.sharedInfo->displayType == MT_VGA) {
if ( ! CalculateCrtcRegisters(mode, params))
return B_BAD_VALUE;
if ( ! CalculatePLLRegisters(mode, params))
return B_BAD_VALUE;
if ( ! CalculateDDARegisters(mode, params))
return B_BAD_VALUE;
SetRegisters(params);
} else {
uint16 vesaMode = GetVesaModeNumber(display_mode(mode), mode.bitsPerPixel);
if (vesaMode == 0)
return B_BAD_VALUE;
if (ioctl(gInfo.deviceFileDesc, ATI_SET_VESA_DISPLAY_MODE,
&vesaMode, sizeof(vesaMode)) != B_OK)
return B_ERROR;
}
Rage128_AdjustFrame(mode);
OUTREGM(R128_DAC_CNTL, R128_DAC_8BIT_EN,
R128_DAC_PALETTE_ACC_CTL | R128_DAC_8BIT_EN);
OUTREG8(R128_PALETTE_INDEX, 0);
for (int i = 0; i < 256; i++)
OUTREG(R128_PALETTE_DATA, (i << 16) | (i << 8) | i );
Rage128_EngineInit(mode);
return B_OK;
}
void
Rage128_AdjustFrame(const DisplayModeEx& mode)
{
SharedInfo& si = *gInfo.sharedInfo;
int address = (mode.v_display_start * mode.virtual_width
+ mode.h_display_start) * ((mode.bitsPerPixel + 1) / 8);
address &= ~0x07;
address += si.frameBufferOffset;
OUTREG(R128_CRTC_OFFSET, address);
return;
}
void
Rage128_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
{
(void)flags;
if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
return ;
OUTREGM(R128_DAC_CNTL, R128_DAC_8BIT_EN,
R128_DAC_PALETTE_ACC_CTL | R128_DAC_8BIT_EN);
OUTREG8(R128_PALETTE_INDEX, first);
while (count--) {
OUTREG(R128_PALETTE_DATA, ((colorData[0] << 16)
| (colorData[1] << 8)
| colorData[2]));
colorData += 3;
}
}