#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86_ansic.h"
#include "compiler.h"
#include "IBM.h"
#include "s3.h"
#define IBMRGB_WRITE_ADDR 0x3C8
#define IBMRGB_RAMDAC_DATA 0x3C9
#define IBMRGB_PIXEL_MASK 0x3C6
#define IBMRGB_READ_ADDR 0x3C7
#define IBMRGB_INDEX_LOW 0x3C8
#define IBMRGB_INDEX_HIGH 0x3C9
#define IBMRGB_INDEX_DATA 0x3C6
#define IBMRGB_INDEX_CONTROL 0x3C7
static void S3OutIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg,
unsigned char mask, unsigned char data)
{
S3Ptr pS3 = S3PTR(pScrn);
unsigned char tmp, tmp2 = 0x00;
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
outb(vgaCRIndex, 0x55);
tmp = inb(vgaCRReg) & 0xfc;
outb(vgaCRReg, tmp | 0x01);
outb(IBMRGB_INDEX_LOW, reg);
if (mask != 0x00)
tmp2 = inb(IBMRGB_INDEX_DATA) & mask;
outb(IBMRGB_INDEX_DATA, tmp2 | data);
outb(vgaCRIndex, 0x55);
outb(vgaCRReg, tmp);
}
static unsigned char S3InIBMRGBIndReg(ScrnInfoPtr pScrn, CARD32 reg)
{
S3Ptr pS3 = S3PTR(pScrn);
unsigned char tmp, ret;
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
outb(vgaCRIndex, 0x55);
tmp = inb(vgaCRReg) & 0xfc;
outb(vgaCRReg, tmp | 0x01);
outb(IBMRGB_INDEX_LOW, reg);
ret = inb(IBMRGB_INDEX_DATA);
outb(vgaCRIndex, 0x55);
outb(vgaCRReg, tmp);
return ret;
}
static void S3IBMWriteAddress(ScrnInfoPtr pScrn, CARD32 index)
{
outb(IBMRGB_WRITE_ADDR, index);
}
static void S3IBMWriteData(ScrnInfoPtr pScrn, unsigned char data)
{
outb(IBMRGB_INDEX_DATA, data);
}
static void S3IBMReadAddress(ScrnInfoPtr pScrn, CARD32 index)
{
outb(IBMRGB_READ_ADDR, index);
}
static unsigned char S3IBMReadData(ScrnInfoPtr pScrn)
{
return inb(IBMRGB_RAMDAC_DATA);
}
Bool S3ProbeIBMramdac(ScrnInfoPtr pScrn)
{
S3Ptr pS3 = S3PTR(pScrn);
if (pS3->Chipset != PCI_CHIP_968)
return FALSE;
pS3->RamDacRec = RamDacCreateInfoRec();
pS3->RamDacRec->ReadDAC = S3InIBMRGBIndReg;
pS3->RamDacRec->WriteDAC = S3OutIBMRGBIndReg;
pS3->RamDacRec->ReadAddress = S3IBMReadAddress;
pS3->RamDacRec->WriteAddress = S3IBMWriteAddress;
pS3->RamDacRec->ReadData = S3IBMReadData;
pS3->RamDacRec->WriteData = S3IBMWriteData;
pS3->RamDacRec->LoadPalette = NULL;
if (!RamDacInit(pScrn, pS3->RamDacRec)) {
RamDacDestroyInfoRec(pS3->RamDacRec);
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "RamDacInit failed\n");
return FALSE;
}
pS3->RamDac = IBMramdacProbe(pScrn, S3IBMRamdacs);
if (pS3->RamDac)
return TRUE;
return FALSE;
}
static void S3ProgramIBMRGBClock(ScrnInfoPtr pScrn, int clk, unsigned char m,
unsigned char n, unsigned char df)
{
S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, ~1, 1);
S3OutIBMRGBIndReg(pScrn, IBMRGB_m0+2*clk, 0, (df<<6)|(m&0x3f));
S3OutIBMRGBIndReg(pScrn, IBMRGB_n0+2*clk, 0, n);
S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl2, 0xf0, clk);
S3OutIBMRGBIndReg(pScrn, IBMRGB_pll_ctrl1, 0xf8, 3);
}
static void S3IBMRGBSetClock(ScrnInfoPtr pScrn, long freq, int clk, long dacspeed,
long fref)
{
volatile double ffreq, fdacspeed, ffref;
volatile int df, n, m, max_n, min_df;
volatile int best_m=69, best_n=17, best_df=0;
volatile double diff, mindiff;
#define FREQ_MIN 16250
#define FREQ_MAX dacspeed
if (freq < FREQ_MIN)
ffreq = FREQ_MIN / 1000.0;
else if (freq > FREQ_MAX)
ffreq = FREQ_MAX / 1000.0;
else
ffreq = freq / 1000.0;
fdacspeed = dacspeed / 1e3;
ffref = fref / 1e3;
ffreq /= ffref;
ffreq *= 16;
mindiff = ffreq;
if (freq <= dacspeed/4)
min_df = 0;
else if (freq <= dacspeed/2)
min_df = 1;
else
min_df = 2;
for (df=0; df<4; df++) {
ffreq /= 2;
mindiff /= 2;
if (df < min_df)
continue;
if (df < 3)
max_n = fref / 1000 / 2;
else
max_n = fref / 1000;
if (max_n > 31)
max_n = 31;
for (n=2; n <= max_n; n++) {
m = (int)(ffreq * n + 0.5) - 65;
if (m < 0)
m = 0;
else if (m > 63)
m = 63;
diff = (m+65.0)/n-ffreq;
if (diff < 0)
diff = -diff;
if (diff < mindiff) {
mindiff = diff;
best_n = n;
best_m = m;
best_df = df;
}
}
}
S3ProgramIBMRGBClock(pScrn, clk, best_m, best_n, best_df);
}
void S3IBMRGB_Restore(ScrnInfoPtr pScrn)
{
S3Ptr pS3 = S3PTR(pScrn);
S3RegPtr restore = &pS3->SavedRegs;
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
int i;
for(i=0; i<0x100; i++)
S3OutIBMRGBIndReg(pScrn, i, 0, restore->dacregs[i]);
outb(vgaCRIndex, 0x22);
outb(vgaCRReg, restore->dacregs[0x100]);
}
void S3IBMRGB_Save(ScrnInfoPtr pScrn)
{
S3Ptr pS3 = S3PTR(pScrn);
S3RegPtr save = &pS3->SavedRegs;
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
int i;
for (i=0; i<0x100; i++)
save->dacregs[i] = S3InIBMRGBIndReg(pScrn, i);
outb(vgaCRIndex, 0x22);
save->dacregs[0x100] = inb(vgaCRReg);
}
void S3IBMRGB_PreInit(ScrnInfoPtr pScrn)
{
S3Ptr pS3 = S3PTR(pScrn);
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
unsigned char cr55, tmp;
outb(vgaCRIndex, 0x43);
tmp = inb(vgaCRReg);
outb(vgaCRReg, tmp & ~0x02);
outb(vgaCRIndex, 0x55);
cr55 = inb(vgaCRReg);
outb(vgaCRReg, (cr55 & ~0x03) | 0x01);
tmp = inb(IBMRGB_INDEX_CONTROL);
outb(IBMRGB_INDEX_CONTROL, tmp & ~1);
outb(IBMRGB_INDEX_HIGH, 0);
outb(vgaCRIndex, 0x55);
outb(vgaCRReg, cr55 & ~0x03);
{
int m, n, df, mclk=0;
m = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_vco_div);
n = S3InIBMRGBIndReg(pScrn, IBMRGB_sysclk_ref_div) & 0x1f;
df = m >> 6;
m &= 0x3f;
if (!n) {
m = 0;
n = 1;
}
mclk = ((pS3->RefClock*100 * (m+65)) / n / (8 >> df) + 50) / 100;
pS3->mclk = mclk;
xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n",
mclk / 1000.0);
}
}
void S3IBMRGB_Init(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
S3Ptr pS3 = S3PTR(pScrn);
unsigned char tmp, blank;
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
S3IBMRGBSetClock(pScrn, mode->Clock, 2, pS3->MaxClock,
pS3->RefClock);
outb(0x3c4, 1);
blank = inb(0x3c5);
outb(0x3c5, blank | 0x20);
S3OutIBMRGBIndReg(pScrn, IBMRGB_misc_clock, 0xf0, 0x03);
S3OutIBMRGBIndReg(pScrn, IBMRGB_sync, 0, 0);
S3OutIBMRGBIndReg(pScrn, IBMRGB_hsync_pos, 0, 0);
S3OutIBMRGBIndReg(pScrn, IBMRGB_pwr_mgmt, 0, 0);
S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~8, 0);
S3OutIBMRGBIndReg(pScrn, IBMRGB_dac_op, ~2, 2);
S3OutIBMRGBIndReg(pScrn, IBMRGB_pal_ctrl, 0, 0);
S3OutIBMRGBIndReg(pScrn, IBMRGB_misc1, ~0x43, 1);
S3OutIBMRGBIndReg(pScrn, IBMRGB_misc2, 0, 0x47);
outb(vgaCRIndex, 0x22);
tmp = inb(vgaCRReg);
if (pS3->s3Bpp == 1)
outb(vgaCRReg, tmp | 8);
else
outb(vgaCRReg, tmp & ~8);
outb(vgaCRIndex, 0x65);
outb(vgaCRReg, 0x00);
outb(vgaCRIndex, 0x40);
outb(vgaCRReg, 0x11);
outb(vgaCRIndex, 0x55);
outb(vgaCRReg, 0x00);
switch (pScrn->depth) {
case 8:
S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 3);
S3OutIBMRGBIndReg(pScrn, IBMRGB_8bpp, 0, 0);
break;
case 15:
S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc0);
break;
case 16:
S3OutIBMRGBIndReg(pScrn, IBMRGB_pix_fmt, 0xf8, 4);
S3OutIBMRGBIndReg(pScrn, IBMRGB_16bpp, 0, 0xc2);
break;
}
outb(vgaCRIndex, 0x66);
tmp = inb(vgaCRReg) & 0xf8;
outb(vgaCRReg, tmp);
outb(vgaCRIndex, 0x58);
tmp = (inb(vgaCRReg) & 0xbf) | 0x40;
outb(vgaCRReg, tmp);
outb(vgaCRIndex, 0x67);
outb(vgaCRReg, 0x11);
switch (pScrn->bitsPerPixel) {
case 8:
tmp = 0x21;
break;
case 16:
tmp = 0x10;
break;
}
outb(vgaCRIndex, 0x6d);
outb(vgaCRReg, tmp);
outb(0x3c4, 1);
outb(0x3c5, blank);
}
static void S3IBMRGBSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
{
S3Ptr pS3 = S3PTR(pScrn);
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
unsigned char tmp;
outb(vgaCRIndex, 0x39);
outb(vgaCRReg, 0xa5);
outb(vgaCRIndex, 0x55);
tmp = inb(vgaCRReg) & 0xfc;
outb(vgaCRReg, tmp | 0x01);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_r);
outb(IBMRGB_INDEX_DATA, (bg & 0x00ff0000) >> 16);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_g);
outb(IBMRGB_INDEX_DATA, (bg & 0x0000ff00) >> 8);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col1_b);
outb(IBMRGB_INDEX_DATA, (bg & 0x000000ff));
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_r);
outb(IBMRGB_INDEX_DATA, (fg & 0x00ff0000) >> 16);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_g);
outb(IBMRGB_INDEX_DATA, (fg & 0x0000ff00) >> 8);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_col2_b);
outb(IBMRGB_INDEX_DATA, (fg & 0x000000ff));
outb(vgaCRReg, tmp);
}
static void S3IBMRGBSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
{
S3Ptr pS3 = S3PTR(pScrn);
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
unsigned char tmp;
outb(vgaCRIndex, 0x39);
outb(vgaCRReg, 0xa5);
outb(vgaCRIndex, 0x55);
tmp = inb(vgaCRReg) & 0xfc;
outb(vgaCRReg, tmp | 0x01);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
outb(IBMRGB_INDEX_DATA, x);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
outb(IBMRGB_INDEX_DATA, x >> 8);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
outb(IBMRGB_INDEX_DATA, y);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
outb(IBMRGB_INDEX_DATA, y >> 8);
outb(vgaCRReg, tmp);
}
static void S3IBMRGBHideCursor(ScrnInfoPtr pScrn)
{
S3Ptr pS3 = S3PTR(pScrn);
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
outb(vgaCRIndex, 0x39);
outb(vgaCRReg, 0xa5);
S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, ~3, 0x00);
}
static void S3IBMRGBShowCursor(ScrnInfoPtr pScrn)
{
S3Ptr pS3 = S3PTR(pScrn);
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
unsigned char tmp;
outb(vgaCRIndex, 0x39);
outb(vgaCRReg, 0xa5);
outb(vgaCRIndex, 0x55);
tmp = (inb(vgaCRReg) & 0xdf) | 0x20;
outb(vgaCRReg, tmp);
outb(vgaCRIndex, 0x45);
tmp = inb(vgaCRReg) & ~0x20;
outb(vgaCRReg, tmp);
S3OutIBMRGBIndReg(pScrn, IBMRGB_curs, 0, 0x27);
}
static void S3IBMRGBLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *image)
{
S3Ptr pS3 = S3PTR(pScrn);
int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg;
unsigned char tmp, tmp2;
register int i;
outb(vgaCRIndex, 0x39);
outb(vgaCRReg, 0xa5);
outb(vgaCRIndex, 0x55);
tmp = inb(vgaCRReg) & 0xfc;
outb(vgaCRReg, tmp | 0x01);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_x);
outb(IBMRGB_INDEX_DATA, 0);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_hot_y);
outb(IBMRGB_INDEX_DATA, 0);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xl);
outb(IBMRGB_INDEX_DATA, 0xff);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_xh);
outb(IBMRGB_INDEX_DATA, 0x7f);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yl);
outb(IBMRGB_INDEX_DATA, 0xff);
outb(IBMRGB_INDEX_LOW, IBMRGB_curs_yh);
outb(IBMRGB_INDEX_DATA, 0x7f);
tmp2 = inb(IBMRGB_INDEX_CONTROL) & 0xfe;
outb(IBMRGB_INDEX_CONTROL, tmp2 | 1);
outb(IBMRGB_INDEX_HIGH, (unsigned char) (IBMRGB_curs_array >> 8));
outb(IBMRGB_INDEX_LOW, (unsigned char) (IBMRGB_curs_array));
for (i=0; i<1024; i++)
outb(IBMRGB_INDEX_DATA, *image++);
outb(IBMRGB_INDEX_HIGH, 0);
outb(IBMRGB_INDEX_CONTROL, tmp2);
outb(vgaCRIndex, 0x55);
outb(vgaCRReg, tmp);
}
static Bool S3IBMRGBUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
S3Ptr pS3 = S3PTR(pScrn);
return (pS3->hwCursor);
}
Bool S3IBMRGB_CursorInit(ScreenPtr pScreen)
{
ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
S3Ptr pS3 = S3PTR(pScrn);
xf86CursorInfoPtr pCurs;
if (!(pCurs = pS3->pCurs = xf86CreateCursorInfoRec()))
return FALSE;
pCurs->MaxWidth = 64;
pCurs->MaxHeight = 64;
pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 |
HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
HARDWARE_CURSOR_NIBBLE_SWAPPED |
HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
pCurs->SetCursorColors = S3IBMRGBSetCursorColors;
pCurs->SetCursorPosition = S3IBMRGBSetCursorPosition;
pCurs->LoadCursorImage = S3IBMRGBLoadCursorImage;
pCurs->HideCursor = S3IBMRGBHideCursor;
pCurs->ShowCursor = S3IBMRGBShowCursor;
pCurs->UseHWCursor = S3IBMRGBUseHWCursor;
return xf86InitCursor(pScreen, pCurs);
}