#include "accel.h"
#include "trio64.h"
#define BASE_FREQ 14.31818
static void
Trio64_CalcClock(long freq, int min_m, int min_n1, int max_n1,
int min_n2, int max_n2, long freq_min, long freq_max,
uint8* mdiv, uint8* ndiv)
{
uint8 best_n1 = 18, best_n2 = 2, best_m = 127;
double ffreq = freq / 1000.0 / BASE_FREQ;
double ffreq_min = freq_min / 1000.0 / BASE_FREQ;
double ffreq_max = freq_max / 1000.0 / BASE_FREQ;
if (ffreq < ffreq_min / (1 << max_n2)) {
TRACE("Trio64_CalcClock() invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n",
ffreq*BASE_FREQ, ffreq_min*BASE_FREQ / (1 << max_n2));
ffreq = ffreq_min / (1 << max_n2);
}
if (ffreq > ffreq_max / (1 << min_n2)) {
TRACE("Trio64_CalcClock() invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n",
ffreq*BASE_FREQ, ffreq_max*BASE_FREQ / (1 << min_n2));
ffreq = ffreq_max / (1 << min_n2);
}
double best_diff = ffreq;
for (uint8 n2 = min_n2; n2 <= max_n2; n2++) {
for (uint8 n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) {
int m = (int)(ffreq * n1 * (1 << n2) + 0.5);
if (m < min_m + 2 || m > 127 + 2)
continue;
double div = (double)(m) / (double)(n1);
if ((div >= ffreq_min) && (div <= ffreq_max)) {
double diff = ffreq - div / (1 << n2);
if (diff < 0.0)
diff = -diff;
if (diff < best_diff) {
best_diff = diff;
best_m = m;
best_n1 = n1;
best_n2 = n2;
}
}
}
}
if (max_n1 == 63)
*ndiv = (best_n1 - 2) | (best_n2 << 6);
else
*ndiv = (best_n1 - 2) | (best_n2 << 5);
*mdiv = best_m - 2;
}
static bool
Trio64_ModeInit(const DisplayModeEx& mode)
{
SharedInfo& si = *gInfo.sharedInfo;
TRACE("Trio64_ModeInit(%d x %d, %d KHz)\n",
mode.timing.h_display, mode.timing.v_display, mode.timing.pixel_clock);
uint32 videoRamMB = si.videoMemSize / (1024 * 1024);
WriteCrtcReg(0x38, 0x48);
WriteCrtcReg(0x39, 0xa5);
WriteSeqReg(0x08, 0x06);
WriteCrtcReg(0x45, 0x00, 0x01);
uint8 sr12, sr13;
Trio64_CalcClock(mode.timing.pixel_clock, 1, 1, 31, 0, 3, 135000, 270000,
&sr13, &sr12);
WriteSeqReg(0x12, sr12);
WriteSeqReg(0x13, sr13);
uint8 tmp = ReadSeqReg(0x15) & ~0x21;
WriteSeqReg(0x15, tmp | 0x02);
WriteSeqReg(0x15, tmp | 0x22);
WriteSeqReg(0x15, tmp | 0x02);
uint8 cr33 = ReadCrtcReg(0x33) & ~0x28;
uint8 cr50 = 0;
uint8 pixmux = 0;
if (si.chipType == S3_TRIO64_V2)
cr33 |= 0x20;
switch (mode.bpp) {
case 8:
break;
case 15:
cr33 |= 0x08;
cr50 = 0x10;
pixmux = 0x30;
break;
case 16:
cr33 |= 0x08;
cr50 = 0x10;
pixmux = 0x50;
break;
case 32:
cr50 = 0x30;
pixmux = 0xd0;
break;
}
bool bDisableAccelFuncs = false;
switch (mode.timing.h_display) {
case 640:
cr50 |= 0x40;
break;
case 800:
cr50 |= 0x80;
break;
case 1024:
cr50 |= 0x00;
break;
case 1152:
cr50 |= 0x01;
break;
case 1280:
cr50 |= 0xc0;
break;
case 1600:
cr50 |= 0x81;
break;
default:
bDisableAccelFuncs = true;
break;
}
WriteCrtcReg(0x33, cr33);
WriteCrtcReg(0x50, cr50);
WriteCrtcReg(0x67, pixmux);
WriteSeqReg(0x15, 0x00, 0x10);
WriteSeqReg(0x18, 0x00, 0x80);
si.bDisableAccelDraw = bDisableAccelFuncs;
uint8 crtc[25], cr3b, cr3c, cr5d, cr5e;
InitCrtcTimingValues(mode, (mode.bpp > 8) ? 2 : 1, crtc, cr3b, cr3c, cr5d, cr5e);
crtc[0x17] = 0xe3;
WriteCrtcReg(0x11, 0x00, 0x80);
for (int k = 0; k < (int)B_COUNT_OF(crtc); k++) {
WriteCrtcReg(k, crtc[k]);
}
WriteCrtcReg(0x3b, cr3b);
WriteCrtcReg(0x3c, cr3c);
WriteCrtcReg(0x5d, cr5d);
WriteCrtcReg(0x5e, cr5e);
uint8 miscOutReg = 0x2f;
if ( ! (mode.timing.flags & B_POSITIVE_HSYNC))
miscOutReg |= 0x40;
if ( ! (mode.timing.flags & B_POSITIVE_VSYNC))
miscOutReg |= 0x80;
WriteMiscOutReg(miscOutReg);
uint8 cr58;
if (videoRamMB <= 1)
cr58 = 0x01;
else if (videoRamMB <= 2)
cr58 = 0x02;
else
cr58 = 0x03;
WriteCrtcReg(0x58, cr58 | 0x10, 0x13);
WriteCrtcReg(0x31, 0x08);
WriteCrtcReg(0x32, 0x00);
WriteCrtcReg(0x34, 0x10);
WriteCrtcReg(0x3a, 0x15);
WriteCrtcReg(0x51, mode.bytesPerRow >> 7, 0x30);
WriteCrtcReg(0x53, 0x18, 0x18);
int n = 255;
int clock2 = mode.timing.pixel_clock * (mode.bpp / 8);
if (videoRamMB < 2)
clock2 *= 2;
int m = (int)((gInfo.sharedInfo->mclk / 1000.0 * .72 + 16.867) * 89.736
/ (clock2 / 1000.0 + 39) - 21.1543);
if (videoRamMB < 2)
m /= 2;
if (m > 31)
m = 31;
else if (m < 0) {
m = 0;
n = 16;
}
if (n < 0)
n = 0;
else if (n > 255)
n = 255;
WriteCrtcReg(0x54, m << 3);
WriteCrtcReg(0x60, n);
WriteCrtcReg(0x42, 0x00, 0x20);
WriteCrtcReg(0x66, 0x89, 0x8f);
WriteReg16(ADVFUNC_CNTL, 0x0001);
WriteReg16(SUBSYS_CNTL, 0x8000);
WriteReg16(SUBSYS_CNTL, 0x4000);
ReadReg16(SUBSYS_STAT);
WriteReg16(MULTIFUNC_CNTL, 0x5000 | 0x0004 | 0x000c);
gInfo.WaitQueue(5);
WriteReg16(MULTIFUNC_CNTL, SCISSORS_L | 0);
WriteReg16(MULTIFUNC_CNTL, SCISSORS_T | 0);
WriteReg16(MULTIFUNC_CNTL, SCISSORS_R | (mode.timing.h_display - 1));
WriteReg16(MULTIFUNC_CNTL, SCISSORS_B | ((si.maxFrameBufferSize / mode.bytesPerRow) - 1));
WriteReg32(WRT_MASK, ~0);
TRACE("Trio64_ModeInit() exit\n");
return true;
}
bool
Trio64_SetDisplayMode(const DisplayModeEx& mode)
{
WriteSeqReg(0x01, 0x20, 0x20);
if ( ! Trio64_ModeInit(mode)) {
TRACE("Trio64_ModeInit() failed\n");
return false;
}
Trio64_AdjustFrame(mode);
WriteSeqReg(0x01, 0x00, 0x20);
return true;
}
void
Trio64_AdjustFrame(const DisplayModeEx& mode)
{
int base = (((mode.v_display_start * mode.virtual_width + mode.h_display_start)
* (mode.bpp / 8)) >> 2) & ~1;
base += gInfo.sharedInfo->frameBufferOffset;
WriteCrtcReg(0x0c, (base >> 8) & 0xff);
WriteCrtcReg(0x0d, base & 0xff);
WriteCrtcReg(0x31, base >> 12, 0x30);
WriteCrtcReg(0x51, base >> 18, 0x03);
}
void
Trio64_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
{
(void)flags;
if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
return ;
while (count--) {
WriteIndexedColor(first++,
colorData[0] >> 2,
colorData[1] >> 2,
colorData[2] >> 2);
colorData += 3;
}
}