#include "accelerant.h"
#include "3dfx.h"
#include <stdlib.h>
#include <unistd.h>
static void
WritePIOReg(uint32 offset, int16 index, uint8 value)
{
PIORegInfo prInfo;
prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
prInfo.offset = offset;
prInfo.index = index;
prInfo.value = value;
status_t result = ioctl(gInfo.deviceFileDesc, TDFX_SET_PIO_REG,
&prInfo, sizeof(prInfo));
if (result != B_OK)
TRACE("WritePIOReg() failed, result = 0x%x\n", result);
}
static uint8
ReadPIOReg(uint32 offset, int16 index)
{
PIORegInfo prInfo;
prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
prInfo.offset = offset;
prInfo.index = index;
status_t result = ioctl(gInfo.deviceFileDesc, TDFX_GET_PIO_REG,
&prInfo, sizeof(prInfo));
if (result != B_OK)
TRACE("ReadPIOReg() failed, result = 0x%x\n", result);
return prInfo.value;
}
static void
WriteMiscOutReg(uint8 value)
{
WritePIOReg(MISC_OUT_W - 0x300, -1, value);
}
static void
WriteCrtcReg(uint8 index, uint8 value)
{
WritePIOReg(CRTC_INDEX - 0x300, index, value);
}
uint8
ReadMiscOutReg()
{
return ReadPIOReg(MISC_OUT_R - 0x300, -1);
}
uint8
ReadCrtcReg(uint8 index)
{
return ReadPIOReg(CRTC_INDEX - 0x300, index);
}
void
TDFX_WaitForFifo(uint32 entries)
{
while ((INREG32(STATUS) & 0x1f) < entries) ;
}
void
TDFX_WaitForIdle()
{
TDFX_WaitForFifo(1);
OUTREG32(CMD_3D, CMD_3D_NOP);
int i = 0;
do {
i = (INREG32(STATUS) & STATUS_BUSY) ? 0 : i + 1;
} while (i < 3);
}
static int
TDFX_CalcPLL(int freq)
{
const int refFreq = 14318;
int best_error = freq;
int best_k = 0;
int best_m = 0;
int best_n = 0;
for (int n = 1; n < 256; n++) {
int freqCur = refFreq * (n + 2);
if (freqCur < freq) {
freqCur = freqCur / 3;
if (freq - freqCur < best_error) {
best_error = freq - freqCur;
best_n = n;
best_m = 1;
best_k = 0;
continue;
}
}
for (int m = 1; m < 64; m++) {
for (int k = 0; k < 4; k++) {
freqCur = refFreq * (n + 2) / (m + 2) / (1 << k);
if (abs(freqCur - freq) < best_error) {
best_error = abs(freqCur - freq);
best_n = n;
best_m = m;
best_k = k;
}
}
}
}
return (best_n << 8) | (best_m << 2) | best_k;
}
bool
TDFX_GetColorSpaceParams(int colorSpace, uint8& bitsPerPixel)
{
switch (colorSpace) {
case B_RGB32:
bitsPerPixel = 32;
break;
case B_RGB16:
bitsPerPixel = 16;
break;
case B_RGB15:
bitsPerPixel = 15;
break;
case B_CMAP8:
bitsPerPixel = 8;
break;
default:
TRACE("Unsupported color space: 0x%X\n", colorSpace);
return false;
}
return true;
}
status_t
TDFX_SetDisplayMode(const DisplayModeEx& mode)
{
SharedInfo& si = *gInfo.sharedInfo;
bool clock2X = mode.timing.pixel_clock > si.maxPixelClock / 2;
int horzDiv = clock2X ? 16 : 8;
int hTotal = mode.timing.h_total / horzDiv - 5;
int hDisp_e = mode.timing.h_display / horzDiv - 1;
int hSync_s = mode.timing.h_sync_start / horzDiv;
int hSync_e = mode.timing.h_sync_end / horzDiv;
int hBlank_s = hDisp_e + 1;
int hBlank_e = hTotal + 3;
int vTotal = mode.timing.v_total - 2;
int vDisp_e = mode.timing.v_display - 1;
int vSync_s = mode.timing.v_sync_start;
int vSync_e = mode.timing.v_sync_end;
int vBlank_s = vDisp_e + 1;
int vBlank_e = vTotal;
uint8 crtc[25];
crtc[0x00] = hTotal;
crtc[0x01] = hDisp_e;
crtc[0x02] = hBlank_s;
crtc[0x03] = (hBlank_e & 0x1f) | 0x80;
crtc[0x04] = hSync_s;
crtc[0x05] = ((hSync_e & 0x1f) | ((hBlank_e & 0x20) << 2));
crtc[0x06] = vTotal;
crtc[0x07] = (((vTotal & 0x100) >> 8)
| ((vDisp_e & 0x100) >> 7)
| ((vSync_s & 0x100) >> 6)
| ((vBlank_s & 0x100) >> 5)
| 0x10
| ((vTotal & 0x200) >> 4)
| ((vDisp_e & 0x200) >> 3)
| ((vSync_s & 0x200) >> 2));
crtc[0x08] = 0x00;
crtc[0x09] = ((vBlank_s & 0x200) >> 4) | 0x40;
crtc[0x0a] = 0x00;
crtc[0x0b] = 0x00;
crtc[0x0c] = 0x00;
crtc[0x0d] = 0x00;
crtc[0x0e] = 0x00;
crtc[0x0f] = 0x00;
crtc[0x10] = vSync_s;
crtc[0x11] = (vSync_e & 0x0f) | 0x20;
crtc[0x12] = vDisp_e;
crtc[0x13] = hDisp_e + 1;
crtc[0x14] = 0x00;
crtc[0x15] = vBlank_s;
crtc[0x16] = vBlank_e;
crtc[0x17] = 0xc3;
crtc[0x18] = 0xff;
uint8 cr1a = (hTotal & 0x100) >> 8
| (hDisp_e & 0x100) >> 6
| (hBlank_s & 0x100) >> 4
| (hBlank_e & 0x40) >> 1
| (hSync_s & 0x100) >> 2
| (hSync_e & 0x20) << 2;
uint8 cr1b = (vTotal & 0x400) >> 10
| (vDisp_e & 0x400) >> 8
| (vBlank_s & 0x400) >> 6
| (vBlank_e & 0x400) >> 4;
uint8 miscOutReg = 0x0f | (mode.timing.v_display < 400 ? 0xa0
: mode.timing.v_display < 480 ? 0x60
: mode.timing.v_display < 768 ? 0xe0 : 0x20);
uint32 vgaInit0 = VGA0_EXTENSIONS
| WAKEUP_3C3
| ENABLE_ALT_READBACK
| CLUT_SELECT_8BIT
| EXT_SHIFT_OUT;
uint32 videoConfig = VIDEO_PROCESSOR_ENABLE | DESKTOP_ENABLE
| (mode.bytesPerPixel - 1) << DESKTOP_PIXEL_FORMAT_SHIFT
| (mode.bytesPerPixel > 1 ? DESKTOP_CLUT_BYPASS : 0);
uint32 dacMode = INREG32(DAC_MODE) & ~DAC_MODE_2X;
if (clock2X) {
dacMode |= DAC_MODE_2X;
videoConfig |= VIDEO_2X_MODE_ENABLE;
}
uint32 pllFreq = TDFX_CalcPLL(mode.timing.pixel_clock);
if (si.chipType == BANSHEE && pllFreq == 45831
&& mode.timing.h_display == 1280 && mode.timing.v_display == 1024)
pllFreq = 45912;
uint32 screenSize = mode.timing.h_display | (mode.timing.v_display << 12);
TDFX_WaitForFifo(2);
OUTREG32(VIDEO_PROC_CONFIG, 0);
OUTREG32(PLL_CTRL0, pllFreq);
WriteMiscOutReg(miscOutReg);
for (uint8 j = 0; j < 25; j++)
WriteCrtcReg(j, crtc[j]);
WriteCrtcReg(0x1a, cr1a);
WriteCrtcReg(0x1b, cr1b);
TDFX_WaitForFifo(6);
OUTREG32(VGA_INIT0, vgaInit0);
OUTREG32(DAC_MODE, dacMode);
OUTREG32(VIDEO_DESKTOP_OVERLAY_STRIDE, mode.bytesPerRow);
OUTREG32(HW_CURSOR_PAT_ADDR, si.cursorOffset);
OUTREG32(VIDEO_SCREEN_SIZE, screenSize);
OUTREG32(VIDEO_DESKTOP_START_ADDR, si.frameBufferOffset);
TDFX_WaitForFifo(7);
OUTREG32(CLIP0_MIN, 0);
OUTREG32(CLIP0_MAX, 0x0fff0fff);
OUTREG32(CLIP1_MIN, 0);
OUTREG32(CLIP1_MAX, 0x0fff0fff);
OUTREG32(VIDEO_PROC_CONFIG, videoConfig);
OUTREG32(SRC_BASE_ADDR, si.frameBufferOffset);
OUTREG32(DST_BASE_ADDR, si.frameBufferOffset);
TDFX_WaitForIdle();
TDFX_AdjustFrame(mode);
return B_OK;
}
void
TDFX_AdjustFrame(const DisplayModeEx& mode)
{
SharedInfo& si = *gInfo.sharedInfo;
int address = (mode.v_display_start * mode.virtual_width
+ mode.h_display_start) * mode.bytesPerPixel;
address &= ~0x07;
address += si.frameBufferOffset;
TDFX_WaitForFifo(1);
OUTREG32(VIDEO_DESKTOP_START_ADDR, address);
return;
}
void
TDFX_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
{
(void)flags;
if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
return ;
uint32 index = first;
while (count--) {
uint32 color = ((colorData[0] << 16) | (colorData[1] << 8) | colorData[2]);
TDFX_WaitForFifo(2);
OUTREG32(DAC_ADDR, index++);
INREG32(DAC_ADDR);
OUTREG32(DAC_DATA, color);
colorData += 3;
}
}