#include "tseng.h"
SymTabRec TsengDacTable[] =
{
{NORMAL_DAC, "normal"},
{ATT20C47xA_DAC, "att20c47xa"},
{Sierra1502X_DAC, "sc1502x"},
{ATT20C497_DAC, "att20c497"},
{ATT20C490_DAC, "att20c490"},
{ATT20C493_DAC, "att20c493"},
{ATT20C491_DAC, "att20c491"},
{ATT20C492_DAC, "att20c492"},
{ICS5341_DAC, "ics5341"},
{ICS5301_DAC, "ics5301"},
{STG1700_DAC, "stg1700"},
{STG1702_DAC, "stg1702"},
{STG1703_DAC, "stg1703"},
{ET6000_DAC, "et6000"},
{CH8398_DAC, "ch8398"},
{MUSIC4910_DAC, "music4910"},
{UNKNOWN_DAC, "unknown"},
};
#define RAMDAC_RMR 0x3c6
#define RAMDAC_READ 0x3c7
#define RAMDAC_WRITE 0x3c8
#define RAMDAC_RAM 0x3c9
static unsigned char white_cmap[] =
{0xff, 0xff, 0xff};
void
tseng_dactopel(void)
{
outb(0x3C8, 0);
return;
}
unsigned char
tseng_dactocomm(void)
{
(void)inb(0x3C6);
(void)inb(0x3C6);
(void)inb(0x3C6);
return (inb(0x3C6));
}
unsigned char
tseng_getdaccomm(void)
{
unsigned char ret;
tseng_dactopel();
(void)tseng_dactocomm();
ret = inb(0x3C6);
tseng_dactopel();
return (ret);
}
void
tseng_setdaccomm(unsigned char comm)
{
tseng_dactopel();
(void)tseng_dactocomm();
outb(0x3C6, comm);
tseng_dactopel();
return;
}
static Bool
ProbeSTG1703(TsengPtr pTseng, Bool quiet)
{
unsigned char cid, did, daccomm, readmask;
Bool Found = FALSE;
readmask = inb(RAMDAC_RMR);
tseng_dactopel();
daccomm = tseng_getdaccomm();
tseng_setdaccomm(daccomm | 0x10);
tseng_dactocomm();
inb(0x3C6);
outb(RAMDAC_RMR, 0x00);
outb(RAMDAC_RMR, 0x00);
cid = inb(RAMDAC_RMR);
did = inb(RAMDAC_RMR);
tseng_dactopel();
outb(RAMDAC_RMR, readmask);
tseng_setdaccomm(daccomm);
if (cid == 0x44) {
Found = TRUE;
switch (did) {
case 0x02:
pTseng->DacInfo.DacType = STG1702_DAC;
break;
case 0x03:
pTseng->DacInfo.DacType = STG1703_DAC;
break;
case 0x00:
default:
pTseng->DacInfo.DacType = STG1700_DAC;
}
}
return (Found);
}
static Bool
ProbeGenDAC(TsengPtr pTseng, int scrnIndex, Bool quiet)
{
unsigned char saveCR31, savelut[6];
int i;
long clock01, clock23;
Bool found = FALSE;
unsigned char dbyte = 0;
int mclk = 0;
int iobase = VGAHW_GET_IOBASE();
outb(iobase + 4, 0x31);
saveCR31 = inb(iobase + 5);
outb(iobase + 5, saveCR31 & ~0x40);
outb(RAMDAC_READ, 0);
for (i = 0; i < 2 * 3; i++)
savelut[i] = inb(RAMDAC_RAM);
outb(RAMDAC_WRITE, 0);
for (i = 0; i < 2 * 3; i++)
outb(RAMDAC_RAM, 0);
outb(iobase + 4, 0x31);
outb(iobase + 5, saveCR31 | 0x40);
outb(RAMDAC_READ, 0);
for (i = clock01 = 0; i < 4; i++)
clock01 = (clock01 << 8) | (inb(RAMDAC_RAM) & 0xff);
for (i = clock23 = 0; i < 4; i++)
clock23 = (clock23 << 8) | (inb(RAMDAC_RAM) & 0xff);
outb(RAMDAC_READ, 0x0a);
mclk = (inb(RAMDAC_RAM) + 2) * 14318;
dbyte = inb(RAMDAC_RAM);
mclk /= ((dbyte & 0x1f) + 2) * (1 << ((dbyte >> 5) & 0x03));
pTseng->MClkInfo.MemClk = mclk;
outb(iobase + 4, 0x31);
outb(iobase + 5, saveCR31 & ~0x40);
outb(RAMDAC_WRITE, 0);
for (i = 0; i < 2 * 3; i++)
outb(RAMDAC_RAM, savelut[i]);
outb(iobase + 4, 0x31);
outb(iobase + 5, saveCR31);
if (clock01 == 0x28613d62 ||
(clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) {
found = TRUE;
tseng_dactopel();
inb(RAMDAC_RMR);
inb(RAMDAC_RMR);
inb(RAMDAC_RMR);
dbyte = inb(RAMDAC_RMR);
switch (dbyte & 0xf0) {
case 0xb0:
if (!quiet) {
xf86DrvMsg(scrnIndex, X_PROBED, "Ramdac: ICS 5341 GenDAC and programmable clock (MClk = %d MHz)\n",
mclk/1000);
}
pTseng->DacInfo.DacType = ICS5341_DAC;
break;
case 0xf0:
if (!quiet) {
xf86DrvMsg(scrnIndex, X_PROBED, "Ramdac: ICS 5301 GenDAC and programmable clock (MClk = %d MHz)\n",
mclk/1000);
}
pTseng->DacInfo.DacType = ICS5301_DAC;
break;
default:
if (!quiet) {
xf86DrvMsg(scrnIndex, X_PROBED, "Ramdac: unkown GenDAC and programmable clock (ID code = 0x%02x). Please report. (we'll treat it as a standard ICS5301 for now).\n",
dbyte);
}
pTseng->DacInfo.DacType = ICS5301_DAC;
}
tseng_dactopel();
}
return found;
}
static Bool
ProbeRamdacID(TsengPtr pTseng, Bool quiet)
{
unsigned char cid;
Bool Found = FALSE;
tseng_dactopel();
cid = inb(RAMDAC_RMR);
cid = inb(RAMDAC_RMR);
cid = inb(RAMDAC_RMR);
cid = inb(RAMDAC_RMR);
switch (cid) {
case 0xc0:
Found = TRUE;
pTseng->DacInfo.DacType = CH8398_DAC;
break;
case 0x82:
Found = TRUE;
pTseng->DacInfo.DacType = MUSIC4910_DAC;
break;
default:
Found = FALSE;
}
tseng_dactopel();
return Found;
}
static void
write_cr(int cr)
{
inb(RAMDAC_WRITE);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
outb(RAMDAC_RMR, cr);
xf86IODelay();
inb(RAMDAC_WRITE);
xf86IODelay();
}
static int
read_cr(void)
{
unsigned int cr;
inb(RAMDAC_WRITE);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
cr = inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_WRITE);
return cr;
}
static void
write_color(int entry, unsigned char *cmap)
{
outb(RAMDAC_WRITE, entry);
xf86IODelay();
outb(RAMDAC_RAM, cmap[0]);
xf86IODelay();
outb(RAMDAC_RAM, cmap[1]);
xf86IODelay();
outb(RAMDAC_RAM, cmap[2]);
xf86IODelay();
}
static void
read_color(int entry, unsigned char *cmap)
{
outb(RAMDAC_READ, entry);
xf86IODelay();
cmap[0] = inb(RAMDAC_RAM);
xf86IODelay();
cmap[1] = inb(RAMDAC_RAM);
xf86IODelay();
cmap[2] = inb(RAMDAC_RAM);
xf86IODelay();
}
Bool
Check_Tseng_Ramdac(ScrnInfoPtr pScrn)
{
unsigned char cmap[3], save_cmap[3];
BOOL cr_saved;
int mclk;
int dbyte;
TsengPtr pTseng = TsengPTR(pScrn);
rgb zeros = {0, 0, 0};
PDEBUG(" Check_Tseng_Ramdac\n");
pTseng->dac.rmr = inb(RAMDAC_RMR);
pTseng->dac.saved_cr = read_cr();
cr_saved = TRUE;
if (pScrn->ramdac) {
pTseng->DacInfo.DacType =
(t_ramdactype)xf86StringToToken(TsengDacTable, pScrn->ramdac);
if (pTseng->DacInfo.DacType < 0) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unknown RAMDAC type \"%s\" specified\n", pScrn->ramdac);
return FALSE;
}
} else {
if (Is_ET6K) {
pTseng->DacInfo.DacType = ET6000_DAC;
(void) inb(pTseng->IOAddress + 0x67);
outb(pTseng->IOAddress + 0x67, 10);
mclk = (inb(pTseng->IOAddress + 0x69) + 2) * 14318;
dbyte = inb(pTseng->IOAddress + 0x69);
mclk /= ((dbyte & 0x1f) + 2) * (1 << ((dbyte >> 5) & 0x03));
pTseng->MClkInfo.MemClk = mclk;
} else if (ProbeGenDAC(pTseng, pScrn->scrnIndex, FALSE)) {
} else if (ProbeSTG1703(pTseng, FALSE)) {
} else if (ProbeRamdacID(pTseng, FALSE)) {
} else
{
outb(RAMDAC_RMR, 0xff);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
inb(RAMDAC_RMR);
xf86IODelay();
outb(RAMDAC_RMR, 0x1c);
xf86IODelay();
if (inb(RAMDAC_RMR) != 0xff) {
cr_saved = FALSE;
pTseng->DacInfo.DacType = ATT20C47xA_DAC;
goto dac_found;
}
write_cr(0xe0);
if ((read_cr() >> 5) != 0x7) {
pTseng->DacInfo.DacType = ATT20C497_DAC;
goto dac_found;
}
write_cr(0x60);
if ((read_cr() >> 5) == 0) {
write_cr(0x2);
if ((read_cr() & 0x2) != 0)
pTseng->DacInfo.DacType = ATT20C490_DAC;
else
pTseng->DacInfo.DacType = ATT20C493_DAC;
} else {
write_cr(0x2);
outb(RAMDAC_RMR, 0xff);
read_color(0xff, save_cmap);
write_color(0xff, white_cmap);
read_color(0xff, cmap);
if (cmap[0] == 0xff && cmap[1] == 0xff && cmap[2] == 0xff)
pTseng->DacInfo.DacType = ATT20C491_DAC;
else
pTseng->DacInfo.DacType = ATT20C492_DAC;
write_color(0xff, save_cmap);
}
}
}
dac_found:
pTseng->DacInfo.RamdacShift = 10;
pTseng->DacInfo.RamdacMask = 0x3f;
pTseng->DacInfo.Dac8Bit = FALSE;
pTseng->DacInfo.DacPort16 = FALSE;
pTseng->DacInfo.NotAttCompat = FALSE;
pTseng->DacInfo.rgb24packed = zeros;
pScrn->progClock = FALSE;
pTseng->ClockChip = CLOCKCHIP_DEFAULT;
pTseng->MClkInfo.Programmable = FALSE;
switch (pTseng->DacInfo.DacType) {
case ATT20C490_DAC:
case ATT20C491_DAC:
pTseng->DacInfo.RamdacShift = 8;
pTseng->DacInfo.RamdacMask = 0xff;
pTseng->DacInfo.Dac8Bit = TRUE;
break;
case UNKNOWN_DAC:
case Sierra1502X_DAC:
pTseng->DacInfo.NotAttCompat = TRUE;
break;
case ET6000_DAC:
pScrn->progClock = TRUE;
pTseng->ClockChip = CLOCKCHIP_ET6000;
pTseng->DacInfo.NotAttCompat = TRUE;
pTseng->MClkInfo.Programmable = TRUE;
pTseng->MClkInfo.min = 80000;
pTseng->MClkInfo.max = 110000;
break;
case ICS5341_DAC:
pScrn->progClock = TRUE;
pTseng->ClockChip = CLOCKCHIP_ICS5341;
pTseng->MClkInfo.Programmable = TRUE;
pTseng->MClkInfo.min = 40000;
pTseng->MClkInfo.max = 60000;
pTseng->DacInfo.DacPort16 = TRUE;
pTseng->DacInfo.rgb24packed.red = 0xff;
pTseng->DacInfo.rgb24packed.green = 0xff0000;
pTseng->DacInfo.rgb24packed.blue = 0xff00;
break;
case ICS5301_DAC:
pScrn->progClock = TRUE;
pTseng->ClockChip = CLOCKCHIP_ICS5301;
break;
case STG1702_DAC:
case STG1700_DAC:
pTseng->DacInfo.DacPort16 = TRUE;
break;
case STG1703_DAC:
pScrn->progClock = TRUE;
pTseng->ClockChip = CLOCKCHIP_STG1703;
pTseng->DacInfo.DacPort16 = TRUE;
break;
case CH8398_DAC:
pScrn->progClock = TRUE;
pTseng->ClockChip = CLOCKCHIP_CH8398;
pTseng->DacInfo.DacPort16 = TRUE;
break;
default:
;
}
xf86DrvMsg(pScrn->scrnIndex, (pScrn->ramdac) ? X_CONFIG : X_PROBED, "Ramdac: \"%s\"\n",
xf86TokenToString(TsengDacTable, pTseng->DacInfo.DacType));
if (cr_saved && pTseng->DacInfo.RamdacShift == 10)
write_cr(pTseng->dac.saved_cr);
outb(RAMDAC_RMR, 0xff);
return TRUE;
}
static unsigned char CMD_GENDAC[] =
{0x00, 0x20, 0x60, 0x40, 0xFF,
0x10, 0x30, 0x50, 0x90, 0x70};
static unsigned char CMD_STG170x[] =
{0x00, 0x08, 0xFF, 0xFF, 0xFF,
0x05, 0x02, 0x03, 0x09, 0x04};
static unsigned char CMD_CH8398[] =
{0x04, 0xC4, 0x64, 0x74, 0xFF,
0x24, 0x14, 0x34, 0xB4, 0xFF};
static unsigned char CMD_ATT49x[] =
{0x00, 0xa0, 0xc0, 0xe0, 0xe0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
#if 0
static unsigned char CMD_SC15025[] =
{0x00, 0xa0, 0xe0, 0x60, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
#endif
static unsigned char CMD_MU4910[] =
{0x1C, 0xBC, 0xDC, 0xFC, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
void
tseng_set_ramdac_bpp(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
Bool rgb555;
Bool dac16bit;
unsigned char *cmd_array = NULL;
unsigned char *cmd_dest = NULL;
int index;
TsengPtr pTseng = TsengPTR(pScrn);
rgb555 = (pScrn->weight.red == 5 && pScrn->weight.green == 5
&& pScrn->weight.blue == 5);
dac16bit = (mode->PrivFlags == TSENG_MODE_DACBUS16) ||
(mode->PrivFlags == TSENG_MODE_PIXMUX);
pTseng->ModeReg.ExtATC &= 0xCF;
if (Is_ET6K)
pTseng->ModeReg.ExtATC |= (pTseng->Bytesperpixel - 1) << 4;
else if (dac16bit)
pTseng->ModeReg.ExtATC |= 0x20;
switch (pTseng->DacInfo.DacType) {
case ATT20C490_DAC:
case ATT20C491_DAC:
case ATT20C492_DAC:
case ATT20C493_DAC:
cmd_array = CMD_ATT49x;
cmd_dest = &(pTseng->ModeReg.ATTdac_cmd);
break;
case STG1700_DAC:
case STG1702_DAC:
case STG1703_DAC:
pTseng->ModeReg.pll.cmd_reg &= 0x04;
pTseng->ModeReg.pll.cmd_reg |= 0x18;
switch (pTseng->Bytesperpixel) {
case 2:
if (rgb555)
pTseng->ModeReg.pll.cmd_reg |= 0xA0;
else
pTseng->ModeReg.pll.cmd_reg |= 0xC0;
break;
case 3:
case 4:
pTseng->ModeReg.pll.cmd_reg |= 0xE0;
break;
}
cmd_array = CMD_STG170x;
cmd_dest = &(pTseng->ModeReg.pll.ctrl);
if (mode->SynthClock <= 16000)
pTseng->ModeReg.pll.timingctrl = 0;
else if (mode->SynthClock <= 32000)
pTseng->ModeReg.pll.timingctrl = 1;
else if (mode->SynthClock <= 67500)
pTseng->ModeReg.pll.timingctrl = 2;
else
pTseng->ModeReg.pll.timingctrl = 3;
break;
case ICS5341_DAC:
case ICS5301_DAC:
cmd_array = CMD_GENDAC;
pTseng->ModeReg.pll.ctrl = 0;
cmd_dest = &(pTseng->ModeReg.pll.cmd_reg);
break;
case CH8398_DAC:
cmd_array = CMD_CH8398;
cmd_dest = &(pTseng->ModeReg.pll.cmd_reg);
break;
case ET6000_DAC:
if (pScrn->bitsPerPixel == 16) {
if (rgb555)
pTseng->ModeReg.ExtET6K[0x58] &= ~0x02;
else
pTseng->ModeReg.ExtET6K[0x58] |= 0x02;
}
break;
case MUSIC4910_DAC:
cmd_array = CMD_MU4910;
cmd_dest = &(pTseng->ModeReg.ATTdac_cmd);
break;
default:
break;
}
if (cmd_array != NULL) {
switch (pTseng->Bytesperpixel) {
default:
case 1:
index = 0;
break;
case 2:
index = rgb555 ? 1 : 2;
break;
case 3:
index = 3;
break;
case 4:
index = 4;
break;
}
if (dac16bit)
index += 5;
if (cmd_array[index] != 0xFF) {
if (cmd_dest != NULL) {
*cmd_dest = cmd_array[index];
} else
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " cmd_dest = NULL -- please report\n");
} else {
pTseng->ModeReg.pll.cmd_reg = 0;
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " %dbpp not supported in %d-bit DAC mode on this RAMDAC -- Please report.\n",
pScrn->bitsPerPixel, dac16bit ? 16 : 8);
}
}
#ifdef FIXME
if (mode->PrivFlags == TSENG_MODE_PIXMUX) {
VGAHWPTR(pScrn)->ModeReg.CRTC[0x17] &= 0xFB;
VGAHWPTR(pScrn)->ModeReg.CRTC[0x02] = 0xff;
}
#endif
}