/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/atidac.c,v 1.18 2003/02/25 17:58:13 tsi Exp $ */ /* * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of Marc Aurele La France not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Marc Aurele La France makes no representations * about the suitability of this software for any purpose. It is provided * "as-is" without express or implied warranty. * * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "ati.h" #include "atidac.h" #include "atimach64io.h" #include "atimono.h" /* * RAMDAC-related definitions. */ const SymTabRec ATIDACDescriptors[] = { /* Keep this table in ascending DACType order */ {ATI_DAC_ATI68830, "ATI 68830 or similar"}, {ATI_DAC_SC11483, "Sierra 11483 or similar"}, {ATI_DAC_ATI68875, "ATI 68875 or similar"}, {ATI_DAC_TVP3026_A, "TI ViewPoint3026 or similar"}, {ATI_DAC_GENERIC, "Brooktree 476 or similar"}, {ATI_DAC_BT481, "Brooktree 481 or similar"}, {ATI_DAC_ATT20C491, "AT&T 20C491 or similar"}, {ATI_DAC_SC15026, "Sierra 15026 or similar"}, {ATI_DAC_MU9C1880, "Music 9C1880 or similar"}, {ATI_DAC_IMSG174, "Inmos G174 or similar"}, {ATI_DAC_ATI68860_B, "ATI 68860 (Revision B) or similar"}, {ATI_DAC_ATI68860_C, "ATI 68860 (Revision C) or similar"}, {ATI_DAC_TVP3026_B, "TI ViewPoint3026 or similar"}, {ATI_DAC_STG1700, "SGS-Thompson 1700 or similar"}, {ATI_DAC_ATT20C498, "AT&T 20C498 or similar"}, {ATI_DAC_STG1702, "SGS-Thompson 1702 or similar"}, {ATI_DAC_SC15021, "Sierra 15021 or similar"}, {ATI_DAC_ATT21C498, "AT&T 21C498 or similar"}, {ATI_DAC_STG1703, "SGS-Thompson 1703 or similar"}, {ATI_DAC_CH8398, "Chrontel 8398 or similar"}, {ATI_DAC_ATT20C408, "AT&T 20C408 or similar"}, {ATI_DAC_INTERNAL, "Internal"}, {ATI_DAC_IBMRGB514, "IBM RGB 514 or similar"}, {ATI_DAC_UNKNOWN, "Unknown"} /* Must be last */ }; #ifndef AVOID_CPIO /* * ATISetDACIOPorts -- * * This function sets up DAC access I/O port numbers. */ void ATISetDACIOPorts ( ATIPtr pATI, ATICRTCType crtc ) { switch (crtc) { case ATI_CRTC_VGA: pATI->CPIO_DAC_DATA = VGA_DAC_DATA; pATI->CPIO_DAC_MASK = VGA_DAC_MASK; pATI->CPIO_DAC_READ = VGA_DAC_READ; pATI->CPIO_DAC_WRITE = VGA_DAC_WRITE; pATI->CPIO_DAC_WAIT = GENS1(pATI->CPIO_VGABase); break; case ATI_CRTC_8514: pATI->CPIO_DAC_DATA = IBM_DAC_DATA; pATI->CPIO_DAC_MASK = IBM_DAC_MASK; pATI->CPIO_DAC_READ = IBM_DAC_READ; pATI->CPIO_DAC_WRITE = IBM_DAC_WRITE; pATI->CPIO_DAC_WAIT = pATI->CPIO_DAC_MASK; break; case ATI_CRTC_MACH64: pATI->CPIO_DAC_DATA = ATIIOPort(DAC_REGS) + 1; pATI->CPIO_DAC_MASK = ATIIOPort(DAC_REGS) + 2; pATI->CPIO_DAC_READ = ATIIOPort(DAC_REGS) + 3; pATI->CPIO_DAC_WRITE = ATIIOPort(DAC_REGS) + 0; pATI->CPIO_DAC_WAIT = pATI->CPIOBase; break; default: break; } } #endif /* AVOID_CPIO */ /* * ATIGetDACCmdReg -- * * Setup to access a RAMDAC's command register. */ CARD8 ATIGetDACCmdReg ( ATIPtr pATI ) { #ifdef AVOID_CPIO (void)in8(M64_DAC_WRITE); /* Reset to PEL mode */ (void)in8(M64_DAC_MASK); (void)in8(M64_DAC_MASK); (void)in8(M64_DAC_MASK); return in8(M64_DAC_MASK); #else /* AVOID_CPIO */ (void)inb(pATI->CPIO_DAC_WRITE); /* Reset to PEL mode */ (void)inb(pATI->CPIO_DAC_MASK); (void)inb(pATI->CPIO_DAC_MASK); (void)inb(pATI->CPIO_DAC_MASK); return inb(pATI->CPIO_DAC_MASK); #endif /* AVOID_CPIO */ } /* * ATIDACPreInit -- * * This function initialises the fields in an ATIHWRec that relate to DACs. */ void ATIDACPreInit ( ScrnInfoPtr pScreenInfo, ATIPtr pATI, ATIHWPtr pATIHW ) { int Index, Index2; CARD8 maxColour = (1 << pATI->rgbBits) - 1; pATIHW->dac_read = pATIHW->dac_write = 0x00U; pATIHW->dac_mask = 0xFFU; /* * Set colour lookup table. The first entry has already been zeroed out. */ if (pATI->depth > 8) for (Index = 1; Index < (NumberOf(pATIHW->lut) / 3); Index++) { Index2 = Index * 3; pATIHW->lut[Index2 + 0] = pATIHW->lut[Index2 + 1] = pATIHW->lut[Index2 + 2] = Index; } else { /* * Initialise hardware colour map so that use of uninitialised * software colour map entries can easily be seen. For 256-colour * modes, this doesn't remain effective for very long... */ pATIHW->lut[3] = pATIHW->lut[4] = pATIHW->lut[5] = 0xFFU; for (Index = 2; Index < (NumberOf(pATIHW->lut) / 3); Index++) { Index2 = Index * 3; pATIHW->lut[Index2 + 0] = maxColour; pATIHW->lut[Index2 + 1] = 0x00U; pATIHW->lut[Index2 + 2] = maxColour; } #ifndef AVOID_CPIO if (pATI->depth == 1) { rgb blackColour = pScreenInfo->display->blackColour, whiteColour = pScreenInfo->display->whiteColour; if (blackColour.red > maxColour) blackColour.red = maxColour; if (blackColour.green > maxColour) blackColour.green = maxColour; if (blackColour.blue > maxColour) blackColour.blue = maxColour; if (whiteColour.red > maxColour) whiteColour.red = maxColour; if (whiteColour.green > maxColour) whiteColour.green = maxColour; if (whiteColour.blue > maxColour) whiteColour.blue = maxColour; if ((blackColour.red == whiteColour.red) && (blackColour.green == whiteColour.green) && (blackColour.blue == whiteColour.blue)) { blackColour.red ^= maxColour; blackColour.green ^= maxColour; blackColour.blue ^= maxColour; } pATIHW->lut[(MONO_BLACK * 3) + 0] = blackColour.red; pATIHW->lut[(MONO_BLACK * 3) + 1] = blackColour.green; pATIHW->lut[(MONO_BLACK * 3) + 2] = blackColour.blue; pATIHW->lut[(MONO_WHITE * 3) + 0] = whiteColour.red; pATIHW->lut[(MONO_WHITE * 3) + 1] = whiteColour.green; pATIHW->lut[(MONO_WHITE * 3) + 2] = whiteColour.blue; } if (pATIHW->crtc == ATI_CRTC_VGA) { /* Initialise overscan to black */ Index = pATIHW->attr[17] * 3; pATIHW->lut[Index + 0] = pATIHW->lut[Index + 1] = pATIHW->lut[Index + 2] = 0x00U; } #endif /* AVOID_CPIO */ } } /* * ATIDACSave -- * * This function is called to save the current RAMDAC state into an ATIHWRec * structure occurrence. */ void ATIDACSave ( ATIPtr pATI, ATIHWPtr pATIHW ) { int Index; #ifdef AVOID_CPIO pATIHW->dac_read = in8(M64_DAC_READ); DACDelay; pATIHW->dac_write = in8(M64_DAC_WRITE); DACDelay; pATIHW->dac_mask = in8(M64_DAC_MASK); DACDelay; /* Save DAC's colour lookup table */ out8(M64_DAC_MASK, 0xFFU); DACDelay; out8(M64_DAC_READ, 0x00U); DACDelay; for (Index = 0; Index < NumberOf(pATIHW->lut); Index++) { pATIHW->lut[Index] = in8(M64_DAC_DATA); DACDelay; } out8(M64_DAC_MASK, pATIHW->dac_mask); DACDelay; out8(M64_DAC_READ, pATIHW->dac_read); DACDelay; #else /* AVOID_CPIO */ ATISetDACIOPorts(pATI, pATIHW->crtc); pATIHW->dac_read = inb(pATI->CPIO_DAC_READ); DACDelay; pATIHW->dac_write = inb(pATI->CPIO_DAC_WRITE); DACDelay; pATIHW->dac_mask = inb(pATI->CPIO_DAC_MASK); DACDelay; /* Save DAC's colour lookup table */ outb(pATI->CPIO_DAC_MASK, 0xFFU); DACDelay; outb(pATI->CPIO_DAC_READ, 0x00U); DACDelay; for (Index = 0; Index < NumberOf(pATIHW->lut); Index++) { pATIHW->lut[Index] = inb(pATI->CPIO_DAC_DATA); DACDelay; } outb(pATI->CPIO_DAC_MASK, pATIHW->dac_mask); DACDelay; outb(pATI->CPIO_DAC_READ, pATIHW->dac_read); DACDelay; #endif /* AVOID_CPIO */ } /* * ATIDACSet -- * * This function loads RAMDAC data from an ATIHWRec structure occurrence. */ void ATIDACSet ( ATIPtr pATI, ATIHWPtr pATIHW ) { int Index; #ifdef AVOID_CPIO /* Load DAC's colour lookup table */ out8(M64_DAC_MASK, 0xFFU); DACDelay; out8(M64_DAC_WRITE, 0x00U); DACDelay; for (Index = 0; Index < NumberOf(pATIHW->lut); Index++) { out8(M64_DAC_DATA, pATIHW->lut[Index]); DACDelay; } out8(M64_DAC_MASK, pATIHW->dac_mask); DACDelay; out8(M64_DAC_READ, pATIHW->dac_read); DACDelay; out8(M64_DAC_WRITE, pATIHW->dac_write); DACDelay; #else /* AVOID_CPIO */ ATISetDACIOPorts(pATI, pATIHW->crtc); /* Load DAC's colour lookup table */ outb(pATI->CPIO_DAC_MASK, 0xFFU); DACDelay; outb(pATI->CPIO_DAC_WRITE, 0x00U); DACDelay; for (Index = 0; Index < NumberOf(pATIHW->lut); Index++) { outb(pATI->CPIO_DAC_DATA, pATIHW->lut[Index]); DACDelay; } outb(pATI->CPIO_DAC_MASK, pATIHW->dac_mask); DACDelay; outb(pATI->CPIO_DAC_READ, pATIHW->dac_read); DACDelay; outb(pATI->CPIO_DAC_WRITE, pATIHW->dac_write); DACDelay; #endif /* AVOID_CPIO */ } /* * ATILoadPalette -- * * This function updates the RAMDAC's LUT and the in-memory copy of it in * NewHW. */ void ATILoadPalette ( ScrnInfoPtr pScreenInfo, int nColours, int *Indices, LOCO *Colours, VisualPtr pVisual ) { ATIPtr pATI = ATIPTR(pScreenInfo); CARD8 *LUTEntry; int i, j, Index; if (((pVisual->class | DynamicClass) == DirectColor) && ((1 << pVisual->nplanes) > (SizeOf(pATI->NewHW.lut) / 3))) { int reds = pVisual->redMask >> pVisual->offsetRed; int greens = pVisual->greenMask >> pVisual->offsetGreen; int blues = pVisual->blueMask >> pVisual->offsetBlue; int redShift = 8 - pATI->weight.red; int greenShift = 8 - pATI->weight.green; int blueShift = 8 - pATI->weight.blue; int redMult = 3 << redShift; int greenMult = 3 << greenShift; int blueMult = 3 << blueShift; int minShift; CARD8 fChanged[SizeOf(pATI->NewHW.lut) / 3]; (void)memset(fChanged, 0, SizeOf(fChanged)); minShift = redShift; if (minShift > greenShift) minShift = greenShift; if (minShift > blueShift) minShift = blueShift; for (i = 0; i < nColours; i++) { if((Index = Indices[i]) < 0) continue; if (Index <= reds) { j = Index * redMult; pATI->NewHW.lut[j + 0] = Colours[Index].red; fChanged[j / 3] = TRUE; } if (Index <= greens) { j = Index * greenMult; pATI->NewHW.lut[j + 1] = Colours[Index].green; fChanged[j / 3] = TRUE; } if (Index <= blues) { j = Index * blueMult; pATI->NewHW.lut[j + 2] = Colours[Index].blue; fChanged[j / 3] = TRUE; } } if (pScreenInfo->vtSema || pATI->currentMode) { /* Rewrite LUT entries that could have been changed */ i = 1 << minShift; LUTEntry = pATI->NewHW.lut; for (Index = 0; Index < (SizeOf(pATI->NewHW.lut) / 3); Index += i, LUTEntry += i * 3) { if (!fChanged[Index]) continue; #ifdef AVOID_CPIO out8(M64_DAC_WRITE, Index); DACDelay; out8(M64_DAC_DATA, LUTEntry[0]); DACDelay; out8(M64_DAC_DATA, LUTEntry[1]); DACDelay; out8(M64_DAC_DATA, LUTEntry[2]); DACDelay; #else /* AVOID_CPIO */ outb(pATI->CPIO_DAC_WRITE, Index); DACDelay; outb(pATI->CPIO_DAC_DATA, LUTEntry[0]); DACDelay; outb(pATI->CPIO_DAC_DATA, LUTEntry[1]); DACDelay; outb(pATI->CPIO_DAC_DATA, LUTEntry[2]); DACDelay; #endif /* AVOID_CPIO */ } } } else { for (i = 0; i < nColours; i++) { Index = Indices[i]; if ((Index < 0) || (Index >= (SizeOf(pATI->NewHW.lut) / 3))) continue; LUTEntry = &pATI->NewHW.lut[Index * 3]; LUTEntry[0] = Colours[Index].red; LUTEntry[1] = Colours[Index].green; LUTEntry[2] = Colours[Index].blue; if (pScreenInfo->vtSema || pATI->currentMode) { #ifdef AVOID_CPIO out8(M64_DAC_WRITE, Index); DACDelay; out8(M64_DAC_DATA, LUTEntry[0]); DACDelay; out8(M64_DAC_DATA, LUTEntry[1]); DACDelay; out8(M64_DAC_DATA, LUTEntry[2]); DACDelay; #else /* AVOID_CPIO */ outb(pATI->CPIO_DAC_WRITE, Index); DACDelay; outb(pATI->CPIO_DAC_DATA, LUTEntry[0]); DACDelay; outb(pATI->CPIO_DAC_DATA, LUTEntry[1]); DACDelay; outb(pATI->CPIO_DAC_DATA, LUTEntry[2]); DACDelay; #endif /* AVOID_CPIO */ } } } }