/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_ramdac.c,v 1.25 2001/02/15 17:54:56 eich Exp $ */ /* * * Copyright 1993-1997 The XFree86 Project, Inc. * */ /* * tseng_ramdac.c. * * Much of this code was taken from the XF86_W32 (3.2) server [kmg] */ #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"}, }; /*** private data ***/ #define RAMDAC_RMR 0x3c6 #define RAMDAC_READ 0x3c7 #define RAMDAC_WRITE 0x3c8 #define RAMDAC_RAM 0x3c9 static unsigned char black_cmap[] = {0x0, 0x0, 0x0}; 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); /* company ID */ did = inb(RAMDAC_RMR); /* device ID */ tseng_dactopel(); outb(RAMDAC_RMR, readmask); tseng_setdaccomm(daccomm); if (cid == 0x44) { /* STG170x RAMDAC found */ 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; /* treat an unknown STG170x as a 1700 */ } } return (Found); } static Bool ProbeGenDAC(TsengPtr pTseng, int scrnIndex, Bool quiet) { /* probe for ICS GENDAC (ICS5341) */ /* * GENDAC and SDAC have two fixed read only PLL clocks * CLK0 f0: 25.255MHz M-byte 0x28 N-byte 0x61 * CLK0 f1: 28.311MHz M-byte 0x3d N-byte 0x62 * which can be used to detect GENDAC and SDAC since there is no chip-id * for the GENDAC. * * code was taken from S3 XFree86 driver. * NOTE: for the GENDAC on a ET4000W32p, reading PLL values * for CLK0 f0 and f1 always returns 0x7f (but is documented "read only") * In fact, all "read only" registers return 0x7f */ 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++) /* save first two LUT entries */ savelut[i] = inb(RAMDAC_RAM); outb(RAMDAC_WRITE, 0); for (i = 0; i < 2 * 3; i++) /* set first two LUT entries to zero */ 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); /* get MClk value */ 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++) /* restore first two LUT entries */ 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); /* the fourth read will show the GenDAC/SDAC chip ID and revision */ 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; } /* probe for RAMDAC using the chip-ID method */ 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); /* this returns chip ID */ 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; } /* * For a description of the following, see AT&T's data sheet for ATT20C490/491 * and ATT20C492/493--GGL */ 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 temp; 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; /* first see if ramdac type was given in XF86Config. If so, assume that is * correct, and don't probe for it. */ if (pScrn->ramdac) { pTseng->DacInfo.DacType = 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 { /* autoprobe for the RAMDAC */ if (Is_ET6K) { pTseng->DacInfo.DacType = ET6000_DAC; temp = 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)) { /* It is. Nothing to do here */ } else if (ProbeSTG1703(pTseng, FALSE)) { /* it's a STG170x */ } else if (ProbeRamdacID(pTseng, FALSE)) { /* found one using RAMDAC ID code */ } else /* if none of the above: start probing for other DACs */ { 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: /* defaults: 8-bit wide DAC port, 6-bit color lookup-tables */ pTseng->DacInfo.RamdacShift = 10; pTseng->DacInfo.RamdacMask = 0x3f; pTseng->DacInfo.Dac8Bit = FALSE; pTseng->DacInfo.DacPort16 = FALSE; pTseng->DacInfo.NotAttCompat = FALSE; /* default: treat as ATT compatible DAC */ pTseng->DacInfo.rgb24packed = zeros; pScrn->progClock = FALSE; pTseng->ClockChip = -1; pTseng->MClkInfo.Programmable = FALSE; /* now override defaults with appropriate values for each RAMDAC */ 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; /* avoids treatment as ATT compatible DAC */ break; case ET6000_DAC: pScrn->progClock = TRUE; pTseng->ClockChip = CLOCKCHIP_ET6000; pTseng->DacInfo.NotAttCompat = TRUE; /* avoids treatment as ATT compatible DAC */ 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: /* defaults already set */ ; } 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; } /* * The following arrays hold command register values for all possible * modes of the 16-bit DACs used on ET4000W32p cards (bpp/pixel_bus_width): * * { 8bpp/8, 15bpp/8, 16bpp/8, 24bpp/8, 32bpp/8, * 8bpp/16, 15bpp/16, 16bpp/16, 24bpp/16, 32bpp/16 * } * * "0xFF" is used as a "not-supported" flag. Assuming no RAMDAC uses this * value for some real configuration... */ 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}; static unsigned char CMD_SC15025[] = {0x00, 0xa0, 0xe0, 0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static unsigned char CMD_MU4910[] = {0x1C, 0xBC, 0xDC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* * This sets up the RAMDAC registers for the correct BPP and pixmux values. * (also set VGA controller registers for pixmux and BPP) */ void tseng_set_ramdac_bpp(ScrnInfoPtr pScrn, DisplayModePtr mode) { Bool rgb555; Bool dac16bit; /* use DAC in 16-bit mode if set (W32p only) */ 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); /* rgb565 otherwise */ /* This is not the good way to find out if we're in 8- or 16-bit RAMDAC * mode It should rather be passed on from the TsengValidMode() code. * Right now it'd better agree with what TsengValidMode() proposed. FIXME */ dac16bit = (mode->PrivFlags == TSENG_MODE_DACBUS16) || (mode->PrivFlags == TSENG_MODE_PIXMUX); pTseng->ModeReg.ExtATC &= 0xCF; /* ATC index 0x16 -- bits-per-PCLK */ 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; /* keep 7.5 IRE setup setting */ pTseng->ModeReg.pll.cmd_reg |= 0x18; /* enable ext regs and pixel modes */ 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); /* set PLL (input) range */ 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; /* 5-5-5 RGB mode */ else pTseng->ModeReg.ExtET6K[0x58] |= 0x02; /* 5-6-5 RGB mode */ } 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 /* still needed? */ if (mode->PrivFlags == TSENG_MODE_PIXMUX) { VGAHWPTR(pScrn)->ModeReg.CRTC[0x17] &= 0xFB; /* to avoid blurred vertical line during flyback, disable H-blanking * (better solution needed !!!) */ VGAHWPTR(pScrn)->ModeReg.CRTC[0x02] = 0xff; } #endif }