/* * $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_driver.c,v 1.87 2001/10/01 13:44:11 eich Exp $ * * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. * * 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 Thomas Roell not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Thomas Roell makes no representations * about the suitability of this software for any purpose. It is provided * "as is" without express or implied warranty. * * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THOMAS ROELL 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. * * Author: Thomas Roell, roell@informatik.tu-muenchen.de * ET6000 and ET4000W32 16/24/32 bpp and acceleration support by Koen Gadeyne * * Large parts rewritten for XFree86 4.0 by Koen Gadeyne. */ /* $XConsortium: et4_driver.c /main/27 1996/10/28 04:48:15 kaleb $ */ /*** Generic includes ***/ #include "tseng.h" /* this includes most of the generic ones as well */ #include "tseng_acl.h" /* All drivers initialising the SW cursor need this */ #include "mipointer.h" /* All drivers implementing backing store need this */ #include "mibstore.h" #include "fb.h" #include "xf86RAC.h" #include "xf86Resources.h" #include "xf86int10.h" #ifdef XvExtension #include "xf86xv.h" #include "Xv.h" #endif /*** Chip-specific includes ***/ /* #include "tseng_acl.h" */ /* * Forward definitions for the functions that make up the driver. */ /* Mandatory functions */ static const OptionInfoRec * TsengAvailableOptions(int chipid, int busid); static void TsengIdentify(int flags); static Bool TsengProbe(DriverPtr drv, int flags); static Bool TsengPreInit(ScrnInfoPtr pScrn, int flags); static Bool TsengScreenInit(int Index, ScreenPtr pScreen, int argc, char **argv); static Bool TsengEnterVT(int scrnIndex, int flags); static void TsengLeaveVT(int scrnIndex, int flags); static Bool TsengCloseScreen(int scrnIndex, ScreenPtr pScreen); static Bool TsengSaveScreen(ScreenPtr pScreen, int mode); /* Required if the driver supports mode switching */ static Bool TsengSwitchMode(int scrnIndex, DisplayModePtr mode, int flags); /* Optional functions */ static void TsengFreeScreen(int scrnIndex, int flags); static ModeStatus TsengValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags); /* If driver-specific config file entries are needed, this must be defined */ /*static Bool TsengParseConfig(ParseInfoPtr raw); */ /* Internally used functions (some are defined in tseng.h) */ static Bool TsengMapMem(ScrnInfoPtr pScrn); static Bool TsengUnmapMem(ScrnInfoPtr pScrn); static void TsengSave(ScrnInfoPtr pScrn); static void TsengRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, TsengRegPtr tsengReg, int flags); static void TsengUnlock(void); static void TsengLock(void); static Bool ET4000DetailedProbe(t_tseng_type * chiptype, t_w32_revid * rev); /* * This is intentionally screen-independent. It indicates the binding * choice made in the first PreInit. */ static int pix24bpp = 0; #define VERSION 4000 #define TSENG_NAME "TSENG" #define TSENG_DRIVER_NAME "tseng" #define TSENG_MAJOR_VERSION 1 #define TSENG_MINOR_VERSION 0 #define TSENG_PATCHLEVEL 0 /* CRTC timing limits */ #define Tseng_HMAX (4096-8) #define Tseng_VMAX (2048-1) /* * This contains the functions needed by the server after loading the * driver module. It must be supplied, and gets added the driver list by * the Module Setup funtion in the dynamic case. In the static case a * reference to this is compiled in, and this requires that the name of * this DriverRec be an upper-case version of the driver name. */ DriverRec TSENG = { VERSION, TSENG_DRIVER_NAME, TsengIdentify, TsengProbe, TsengAvailableOptions, NULL, 0 }; /* sub-revisions are now dealt with in the ChipRev variable */ static SymTabRec TsengChipsets[] = { {TYPE_ET4000, "ET4000"}, {TYPE_ET4000W32, "ET4000W32"}, {TYPE_ET4000W32I, "ET4000W32i"}, {TYPE_ET4000W32P, "ET4000W32p"}, {TYPE_ET6000, "ET6000"}, {TYPE_ET6100, "ET6100"}, {-1, NULL} }; /* Convert PCI ID to chipset name */ static PciChipsets TsengPciChipsets[] = { {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_A, RES_SHARED_VGA}, {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_B, RES_SHARED_VGA}, {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_C, RES_SHARED_VGA}, {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_D, RES_SHARED_VGA}, {TYPE_ET6000, PCI_CHIP_ET6000, RES_SHARED_VGA}, {-1, -1, RES_UNDEFINED} }; static IsaChipsets TsengIsaChipsets[] = { {TYPE_ET4000, RES_EXCLUSIVE_VGA}, {TYPE_ET4000W32, RES_EXCLUSIVE_VGA}, {TYPE_ET4000W32I, RES_EXCLUSIVE_VGA}, {-1, RES_UNDEFINED} }; typedef enum { OPTION_HIBIT_HIGH, OPTION_HIBIT_LOW, OPTION_SW_CURSOR, OPTION_HW_CURSOR, OPTION_PCI_BURST, OPTION_SLOW_DRAM, OPTION_MED_DRAM, OPTION_FAST_DRAM, OPTION_W32_INTERLEAVE, OPTION_NOACCEL, OPTION_NOCLOCKCHIP, OPTION_LINEAR, OPTION_SHOWCACHE, OPTION_LEGEND, OPTION_PCI_RETRY, OPTION_SET_MCLK } TsengOpts; static const OptionInfoRec TsengOptions[] = { {OPTION_HIBIT_HIGH, "hibit_high", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_HIBIT_LOW, "hibit_low", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_PCI_BURST, "pci_burst", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_SLOW_DRAM, "slow_dram", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_MED_DRAM, "med_dram", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_FAST_DRAM, "fast_dram", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_W32_INTERLEAVE, "w32_interleave", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_NOCLOCKCHIP, "NoClockchip", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_LINEAR, "Linear", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_SHOWCACHE, "ShowCache", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_LEGEND, "Legend", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_PCI_RETRY, "PciRetry", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_SET_MCLK, "SetMClk", OPTV_FREQ, {0}, FALSE}, {-1, NULL, OPTV_NONE, {0}, FALSE} }; static const char *int10Symbols[] = { "xf86FreeInt10", "xf86InitInt10", NULL }; static const char *vgaHWSymbols[] = { "vgaHWFreeHWRec", "vgaHWGetHWRec", "vgaHWGetIOBase", "vgaHWGetIndex", "vgaHWHandleColormaps", "vgaHWInit", "vgaHWLock", "vgaHWMapMem", "vgaHWProtect", "vgaHWRestore", "vgaHWSave", "vgaHWSaveScreen", "vgaHWUnlock", "vgaHWUnmapMem", NULL }; static const char* miscfbSymbols[] = { "xf1bppScreenInit", "xf4bppScreenInit", NULL }; static const char* fbSymbols[] = { "fbPictureInit", "fbScreenInit", NULL }; static const char *ramdacSymbols[] = { "xf86CreateCursorInfoRec", "xf86DestroyCursorInfoRec", "xf86InitCursor", NULL }; static const char *xaaSymbols[] = { "XAACreateInfoRec", "XAADestroyInfoRec", "XAAInit", NULL }; #ifdef XFree86LOADER static MODULESETUPPROTO(tsengSetup); static XF86ModuleVersionInfo tsengVersRec = { "tseng", MODULEVENDORSTRING, MODINFOSTRING1, MODINFOSTRING2, XF86_VERSION_CURRENT, TSENG_MAJOR_VERSION, TSENG_MINOR_VERSION, TSENG_PATCHLEVEL, ABI_CLASS_VIDEODRV, /* This is a video driver */ ABI_VIDEODRV_VERSION, MOD_CLASS_VIDEODRV, {0, 0, 0, 0} }; /* * This is the module init data for XFree86 modules. * * Its name has to be the driver name followed by ModuleData. */ XF86ModuleData tsengModuleData = { &tsengVersRec, tsengSetup, NULL }; static pointer tsengSetup(pointer module, pointer opts, int *errmaj, int *errmin) { static Bool setupDone = FALSE; if (!setupDone) { setupDone = TRUE; xf86AddDriver(&TSENG, module, 0); /* * Modules that this driver always requires can be loaded here * by calling LoadSubModule(). */ /* * Tell the loader about symbols from other modules that this module * might refer to. */ LoaderRefSymLists(vgaHWSymbols, miscfbSymbols, fbSymbols, xaaSymbols, int10Symbols, ramdacSymbols, NULL); /* * The return value must be non-NULL on success even though there * is no TearDownProc. */ return (pointer) 1; } else { if (errmaj) *errmaj = LDR_ONCEONLY; return NULL; } } #endif /* XFree86LOADER */ static Bool TsengGetRec(ScrnInfoPtr pScrn) { PDEBUG(" TsengGetRec\n"); /* * Allocate an TsengRec, and hook it into pScrn->driverPrivate. * pScrn->driverPrivate is initialised to NULL, so we can check if * the allocation has already been done. */ if (pScrn->driverPrivate != NULL) return TRUE; pScrn->driverPrivate = xnfcalloc(sizeof(TsengRec), 1); /* Initialise it here when needed (or possible) */ return TRUE; } static void TsengFreeRec(ScrnInfoPtr pScrn) { PDEBUG(" TsengFreeRec\n"); if (pScrn->driverPrivate == NULL) return; xfree(pScrn->driverPrivate); pScrn->driverPrivate = NULL; } static t_tseng_type TsengPCI2Type(ScrnInfoPtr pScrn, int ChipID) { TsengPtr pTseng = TsengPTR(pScrn); switch (ChipID) { case PCI_CHIP_ET4000_W32P_A: pTseng->ChipType = TYPE_ET4000W32P; pTseng->ChipRev = W32REVID_A; break; case PCI_CHIP_ET4000_W32P_B: pTseng->ChipType = TYPE_ET4000W32P; pTseng->ChipRev = W32REVID_B; break; case PCI_CHIP_ET4000_W32P_C: pTseng->ChipType = TYPE_ET4000W32P; pTseng->ChipRev = W32REVID_C; break; case PCI_CHIP_ET4000_W32P_D: pTseng->ChipType = TYPE_ET4000W32P; pTseng->ChipRev = W32REVID_D; break; case PCI_CHIP_ET6000: pTseng->ChipType = TYPE_ET6000; pTseng->ChipRev = pTseng->PciInfo->chipRev; /* ET6000 != ET6100 */ if (pTseng->ChipRev >= ET6100REVID) pTseng->ChipType = TYPE_ET6100; break; default: xf86Msg(X_ERROR, "%s: Unknown Tseng PCI ID: %X\n", TSENG_NAME, ChipID); return FALSE; } return TRUE; } static const OptionInfoRec * TsengAvailableOptions(int chipid, int busid) { return TsengOptions; } static void TsengIdentify(int flags) { PDEBUG(" TsengIdentify\n"); xf86PrintChipsets(TSENG_NAME, "driver for Tseng Labs chipsets", TsengChipsets); } static void TsengAssignFPtr(ScrnInfoPtr pScrn) { pScrn->driverVersion = VERSION; pScrn->driverName = TSENG_DRIVER_NAME; pScrn->name = TSENG_NAME; pScrn->Probe = TsengProbe; pScrn->PreInit = TsengPreInit; pScrn->ScreenInit = TsengScreenInit; pScrn->SwitchMode = TsengSwitchMode; pScrn->AdjustFrame = TsengAdjustFrame; pScrn->EnterVT = TsengEnterVT; pScrn->LeaveVT = TsengLeaveVT; pScrn->FreeScreen = TsengFreeScreen; pScrn->ValidMode = TsengValidMode; } /* unlock ET4000 using KEY register */ static void TsengUnlock(void) { unsigned char temp; int iobase = VGAHW_GET_IOBASE(); PDEBUG(" TsengUnlock\n"); outb(0x3BF, 0x03); outb(iobase + 8, 0xA0); outb(iobase + 4, 0x11); temp = inb(iobase + 5); outb(iobase + 5, temp & 0x7F); } /* lock ET4000 using KEY register. FIXME: should restore old lock status instead */ static void TsengLock(void) { unsigned char temp; int iobase = VGAHW_GET_IOBASE(); PDEBUG(" TsengLock\n"); outb(iobase + 4, 0x11); temp = inb(iobase + 5); outb(iobase + 5, temp | 0x80); outb(iobase + 8, 0x00); outb(0x3D8, 0x29); outb(0x3BF, 0x01); } /* * ET4000AutoDetect -- Old-style autodetection code (by register probing) * * This code is only called when the chipset is not given beforehand, * and if the PCI code hasn't detected one previously. */ #if 0 static Bool ET4000MinimalProbe(void) { unsigned char temp, origVal, newVal; int iobase; PDEBUG(" ET4000MinimalProbe\n"); /* * Note, the vgaHW module cannot be used here, but there are * some macros in vgaHW.h that can be used. */ iobase = VGAHW_GET_IOBASE(); /* * Check first that there is a ATC[16] register and then look at * CRTC[33]. If both are R/W correctly it's a ET4000 ! */ temp = inb(iobase + 0x0A); TsengUnlock(); /* only ATC 0x16 is protected by KEY */ outb(0x3C0, 0x16 | 0x20); origVal = inb(0x3C1); outb(0x3C0, origVal ^ 0x10); outb(0x3C0, 0x16 | 0x20); newVal = inb(0x3C1); outb(0x3C0, origVal); /* TsengLock(); FIXME: RESTORE OLD CONTENTS INSTEAD ! */ if (newVal != (origVal ^ 0x10)) { return (FALSE); } outb(iobase + 0x04, 0x33); origVal = inb(iobase + 0x05); outb(iobase + 0x05, origVal ^ 0x0F); newVal = inb(iobase + 0x05); outb(iobase + 0x05, origVal); if (newVal != (origVal ^ 0x0F)) { return (FALSE); } return TRUE; } #endif static int TsengFindIsaDevice(GDevPtr dev) { /* XXX Need to implement this */ return -1; } static Bool TsengProbe(DriverPtr drv, int flags) { int i; GDevPtr *devSections; int numDevSections; int numUsed; int *usedChips = NULL; Bool foundScreen = FALSE; PDEBUG(" TsengProbe\n"); /* * The aim here is to find all cards that this driver can handle, * and for the ones not already claimed by another driver, claim the * slot, and allocate a ScrnInfoRec. * * This should be a minimal probe, and it should under no circumstances * change the state of the hardware. Because a device is found, don't * assume that it will be used. Don't do any initialisations other than * the required ScrnInfoRec initialisations. Don't allocate any new * data structures. */ /* * Find the config file Device sections that match this * driver, and return if there are none. */ if ((numDevSections = xf86MatchDevice(TSENG_DRIVER_NAME, &devSections)) <= 0) { return FALSE; } /* XXX maybe this can go some time soon */ /* * for the Tseng server, there can only be one matching * device section. So issue a warning if more than one show up. * Multiple Tseng cards in the same machine are not possible. */ /* * If this is a PCI card, "probing" just amounts to checking the PCI * data that the server has already collected. If there is none, * check for non-PCI boards. * * The provided xf86MatchPciInstances() helper takes care of * the details. */ numUsed = 0; if (xf86GetPciVideoInfo() != NULL) { numUsed = xf86MatchPciInstances(TSENG_NAME, PCI_VENDOR_TSENG, TsengChipsets, TsengPciChipsets, devSections,numDevSections, drv, &usedChips); if (numUsed > 0) { if (flags & PROBE_DETECT) foundScreen = TRUE; else for (i = 0; i < numUsed; i++) { /* Allocate a ScrnInfoRec */ ScrnInfoPtr pScrn = NULL; if ((pScrn = xf86ConfigPciEntity(pScrn,0,usedChips[i], TsengPciChipsets,NULL, NULL,NULL,NULL,NULL))) { TsengAssignFPtr(pScrn); foundScreen = TRUE; } } xfree(usedChips); } } /* Check for non-PCI cards */ numUsed = xf86MatchIsaInstances(TSENG_NAME, TsengChipsets, TsengIsaChipsets,drv, TsengFindIsaDevice, devSections, numDevSections, &usedChips); if (numUsed > 0) { if (flags & PROBE_DETECT) foundScreen = TRUE; else for (i = 0; i < numUsed; i++) { ScrnInfoPtr pScrn = NULL; if ((pScrn = xf86ConfigIsaEntity(pScrn,0,usedChips[i], TsengIsaChipsets,NULL, NULL,NULL,NULL,NULL))) { TsengAssignFPtr(pScrn); foundScreen = TRUE; } } xfree(usedChips); } xfree(devSections); return foundScreen; } /* The PCI part of TsengPreInit() */ static Bool TsengPreInitPCI(ScrnInfoPtr pScrn) { MessageType from; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengPreInitPCI\n"); /* * * Set the ChipType and ChipRev, allowing config file entries to * * override. */ if (pTseng->pEnt->device->chipset && *pTseng->pEnt->device->chipset) { /* chipset given as a string in the config file */ pScrn->chipset = pTseng->pEnt->device->chipset; pTseng->ChipType = xf86StringToToken(TsengChipsets, pScrn->chipset); /* FIXME: still need to probe for W32p revision here */ from = X_CONFIG; } else if (pTseng->pEnt->device->chipID >= 0) { /* chipset given as a PCI ID in the config file */ if (!TsengPCI2Type(pScrn, pTseng->pEnt->device->chipID)) return FALSE; pScrn->chipset = (char *)xf86TokenToString(TsengChipsets, pTseng->ChipType); from = X_CONFIG; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipID override: 0x%04X\n", pTseng->ChipType); } else { /* probe for chipset type */ from = X_PROBED; if (!TsengPCI2Type(pScrn, pTseng->PciInfo->chipType)) return FALSE; pScrn->chipset = (char *)xf86TokenToString(TsengChipsets, pTseng->ChipType); } if (pTseng->pEnt->device->chipRev >= 0) { pTseng->ChipRev = pTseng->pEnt->device->chipRev; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n", pTseng->ChipRev); if ((pTseng->ChipType == TYPE_ET6000) && (pTseng->ChipRev >= ET6100REVID)) pTseng->ChipType = TYPE_ET6100; } else { t_tseng_type dum_chiptype; t_w32_revid rev; if (Is_ET6K) { pTseng->ChipRev = pTseng->PciInfo->chipRev; } else { /* * on W32p cards, the PCI ChipRev field is always 0 (it is not * implemented). We will use the ChipRev field to distinguish * between revisions A through D, so we set it up with the * standard register-probing "Detailed Probe" instead. */ ET4000DetailedProbe(&dum_chiptype, &rev); pTseng->ChipRev = rev; } } if (Is_ET6K) { xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\"\n", pScrn->chipset); } else { char ch_rev; switch (pTseng->ChipRev) { case W32REVID_A: ch_rev = 'A'; break; case W32REVID_B: ch_rev = 'B'; break; case W32REVID_C: ch_rev = 'C'; break; case W32REVID_D: ch_rev = 'D'; break; default: ch_rev = 'X'; } xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\" (rev %c)\n", pScrn->chipset, ch_rev); } pTseng->PciTag = pciTag(pTseng->PciInfo->bus, pTseng->PciInfo->device, pTseng->PciInfo->func); /* only the ET6000 implements a PCI IO address */ if (Is_ET6K) { if (pTseng->pEnt->device->IOBase != 0) { pTseng->IOAddress = pTseng->pEnt->device->IOBase; from = X_CONFIG; } else { if ((pTseng->PciInfo->ioBase[1]) != 0) { pTseng->IOAddress = pTseng->PciInfo->ioBase[1]; from = X_PROBED; } else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid PCI I/O address in PCI config space\n"); return FALSE; } } xf86DrvMsg(pScrn->scrnIndex, from, "PCI I/O registers at 0x%lX\n", (unsigned long)pTseng->IOAddress); } pTseng->LinFbAddressMask = 0xFF000000; return TRUE; } /* * This is a more detailed probe. We already know it's a Tseng chip from * TsengPreInit() and ET4000MinimalProbe(). */ static Bool ET4000DetailedProbe(t_tseng_type * chiptype, t_w32_revid * rev) { unsigned char temp, origVal, newVal; PDEBUG(" ET4000DetailedProbe\n"); /* * We know it's an ET4000, now check for an ET4000/W32. * Test for writability of 0x3cb. */ origVal = inb(0x3cb); outb(0x3cb, 0x33); /* Arbitrary value */ newVal = inb(0x3cb); outb(0x3cb, origVal); if (newVal != 0x33) { /* not a W32; so it's a standard ET4000 */ *chiptype = TYPE_ET4000; *rev = TSENGNOREV; return TRUE; } /* We have an ET4000/W32. Now determine the type. */ outb(0x217a, 0xec); temp = inb(0x217b) >> 4; switch (temp) { case 0: /* ET4000/W32 */ *chiptype = TYPE_ET4000W32; break; case 1: /* ET4000/W32i */ *chiptype = TYPE_ET4000W32I; *rev = W32REVID_A; break; case 3: /* ET4000/W32i rev b */ *chiptype = TYPE_ET4000W32I; *rev = W32REVID_B; break; case 11: /* ET4000/W32i rev c */ *chiptype = TYPE_ET4000W32I; *rev = W32REVID_C; break; case 2: /* ET4000/W32p rev a */ *chiptype = TYPE_ET4000W32P; *rev = W32REVID_A; break; case 5: /* ET4000/W32p rev b */ *chiptype = TYPE_ET4000W32P; *rev = W32REVID_B; break; case 6: /* ET4000/W32p rev d */ *chiptype = TYPE_ET4000W32P; *rev = W32REVID_D; break; case 7: /* ET4000/W32p rev c */ *chiptype = TYPE_ET4000W32P; *rev = W32REVID_C; break; default: return (FALSE); } return (TRUE); } /* * TsengFindBusType -- * determine bus interface type * (also determines Lin Mem address mask, because that depends on bustype) * * We don't need to bother with PCI busses here: TsengPreInitPCI() took care * of that. This code isn't called if it's a PCI bus anyway. */ static void TsengFindNonPciBusType(ScrnInfoPtr pScrn) { unsigned char bus; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengFindNonPciBusType\n"); pTseng->Bustype = T_BUS_ISA; pTseng->Linmem_1meg = FALSE; pTseng->LinFbAddressMask = 0; switch (pTseng->ChipType) { case TYPE_ET4000: break; case TYPE_ET4000W32: case TYPE_ET4000W32I: /* * Notation: SMx = bit x of Segment Map Comparator (CRTC index 0x30) * * We assume the driver code disables the image port (which it does) * * ISA: [ A23==SEGE, A22, A21, A20 ] == [ SM1, SM0, 0, 0 ] * MCA: [ A24, A23, A22, A21, A20 ] == [ SM2, SM1, SM0, 0, 0 ] * VLB: [ /A26, /A25, /A24, A23, A22, A21, A20 ] == ("/" means inverted!) * [ SM4, SM3, SM2, SM1, SM0, 0 , 0 ] */ outb(0x217A, 0xEF); bus = inb(0x217B) & 0x60; /* Determine bus type */ switch (bus) { case 0x40: xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32/W32i bus type: MCA\n"); pTseng->Bustype = T_BUS_MCA; pTseng->LinFbAddressMask = 0x01C00000; /* MADE24, A23 and A22 are decoded */ break; case 0x60: xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32/W32i bus type: Local Bus\n"); pTseng->Bustype = T_BUS_VLB; pTseng->LinFbAddressMask = 0x07C00000; /* A26..A22 are decoded */ break; case 0x00: case 0x20: default: xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32/W32i bus type (0x%02X): ISA\n", bus); pTseng->Bustype = T_BUS_ISA; pTseng->LinFbAddressMask = 0x00C00000; /* SEGE and A22 are decoded */ break; } break; case TYPE_ET4000W32P: outb(0x217A, 0xEF); bus = inb(0x217B) >> 3; /* Determine bus type */ switch (bus) { case 0x1C: /* PCI case already handled in TsengPreInitPCI() */ pTseng->Bustype = T_BUS_VLB; pTseng->LinFbAddressMask = 0x3FC00000; /* A29..A22 */ xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Buffered Bus\n"); pTseng->Linmem_1meg = TRUE; /* IMA bus support allows for only 1M linear memory */ break; case 0x13: xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Bus (option 1a)\n"); pTseng->Bustype = T_BUS_VLB; if (pTseng->ChipRev == W32REVID_A) pTseng->LinFbAddressMask = 0x07C00000; else pTseng->LinFbAddressMask = 0x1FC00000; /* SEGI,A27..A22 */ break; case 0x11: xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Bus (option 1b)\n"); pTseng->Bustype = T_BUS_VLB; pTseng->LinFbAddressMask = 0x00C00000; /* SEGI,A22 */ pTseng->Linmem_1meg = TRUE; /* IMA bus support allows for only 1M linear memory */ break; case 0x08: case 0x0B: default: xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Bus (option 2)\n"); pTseng->Bustype = T_BUS_VLB; pTseng->LinFbAddressMask = 0x3FC00000; /* A29..A22 */ break; } if (Is_W32p_cd && (pTseng->LinFbAddressMask = 0x3FC00000)) pTseng->LinFbAddressMask |= 0xC0000000; /* A31,A30 decoded from PCI config space */ break; case TYPE_ET6000: case TYPE_ET6100: pTseng->Bustype = T_BUS_PCI; pTseng->LinFbAddressMask = 0xFF000000; break; } } /* The TsengPreInit() part for non-PCI busses */ static Bool TsengPreInitNoPCI(ScrnInfoPtr pScrn) { MessageType from; t_w32_revid rev = TSENGNOREV; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengPreInitNoPCI\n"); /* * Set the ChipType and ChipRev, allowing config file entries to * override. */ if (pTseng->pEnt->device->chipset && *pTseng->pEnt->device->chipset) { /* chipset given as a string in the config file */ pScrn->chipset = pTseng->pEnt->device->chipset; pTseng->ChipType = xf86StringToToken(TsengChipsets, pScrn->chipset); from = X_CONFIG; } else if (pTseng->pEnt->device->chipID > 0) { /* chipset given as a PCI ID in the config file */ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "ChipID override only possible for PCI cards\n"); return FALSE; } else { from = X_PROBED; if (!ET4000DetailedProbe(&(pTseng->ChipType), &rev)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unknown Tseng chip detected. Try chipset override.\n"); return FALSE; } pScrn->chipset = (char *)xf86TokenToString(TsengChipsets, pTseng->ChipType); } if (pTseng->pEnt->device->chipRev >= 0) { pTseng->ChipRev = pTseng->pEnt->device->chipRev; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n", pTseng->ChipRev); } else { pTseng->ChipRev = rev; } xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\"\n", pScrn->chipset); TsengFindNonPciBusType(pScrn); return TRUE; } /* * The 8*32kb ET6000 MDRAM granularity causes the more general probe to * detect too much memory in some configurations, because that code has a * 8-bank (=256k) granularity. E.g. it fails to recognize 2.25 MB of memory * (detects 2.5 instead). This function goes to check if the RAM is actually * there. MDRAM comes in multiples of 4 banks (16, 24, 32, 36, 40, 64, 72, * 80, ... 32kb-banks), so checking each 64k block should be enough granularity. * * No more than the amount of refreshed RAM is checked. Non-refreshed RAM * won't work anyway. * * The same code could be used on other Tseng chips, or even on ANY * VGA board, but probably only in case of trouble. * * FIXME: this should be done using linear memory */ #define VIDMEM ((volatile CARD32*)check_vgabase) #define SEGSIZE (64) /* kb */ #define ET6K_SETSEG(seg) \ outb(0x3CB, ((seg) & 0x30) | ((seg) >> 4)); \ outb(0x3CD, ((seg) & 0x0f) | ((seg) << 4)); static int et6000_check_videoram(ScrnInfoPtr pScrn, int ram) { unsigned char oldSegSel1, oldSegSel2, oldGR5, oldGR6, oldSEQ2, oldSEQ4; int segment, i; int real_ram = 0; pointer check_vgabase; Bool fooled = FALSE; int save_vidmem; PDEBUG(" et6000_check_videoram\n"); if (ram > 4096) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Detected more than 4096 kb of video RAM. Clipped to 4096kb\n"); xf86DrvMsg(pScrn->scrnIndex, X_INFO, " (Tseng VGA chips can only use 4096kb).\n"); ram = 4096; } if (!vgaHWMapMem(pScrn)) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Could not map VGA memory to check for video memory.\n"); xf86DrvMsg(pScrn->scrnIndex, X_WARNING, " Detected amount may be wrong.\n"); return ram; } check_vgabase = (VGAHWPTR(pScrn)->Base); /* * We need to set the VGA controller in VGA graphics mode, or else we won't * be able to access the full 4MB memory range. First, we save the * registers we modify, of course. */ oldSegSel1 = inb(0x3CD); oldSegSel2 = inb(0x3CB); outb(0x3CE, 5); oldGR5 = inb(0x3CF); outb(0x3CE, 6); oldGR6 = inb(0x3CF); outb(0x3C4, 2); oldSEQ2 = inb(0x3C5); outb(0x3C4, 4); oldSEQ4 = inb(0x3C5); /* set graphics mode */ outb(0x3CE, 6); outb(0x3CF, 5); outb(0x3CE, 5); outb(0x3CF, 0x40); outb(0x3C4, 2); outb(0x3C5, 0x0f); outb(0x3C4, 4); outb(0x3C5, 0x0e); /* * count down from presumed amount of memory in SEGSIZE steps, and * look at each segment for real RAM. * * To select a segment, we cannot use ET4000W32SetReadWrite(), since * that requires the ScreenPtr, which we don't have here. */ for (segment = (ram / SEGSIZE) - 1; segment >= 0; segment--) { /* select the segment */ ET6K_SETSEG(segment); /* save contents of memory probing location */ save_vidmem = *(VIDMEM); /* test with pattern */ *VIDMEM = 0xAAAA5555; if (*VIDMEM != 0xAAAA5555) { *VIDMEM = save_vidmem; continue; } /* test with inverted pattern */ *VIDMEM = 0x5555AAAA; if (*VIDMEM != 0x5555AAAA) { *VIDMEM = save_vidmem; continue; } /* * If we get here, the memory seems to be writable/readable * Now check if we aren't fooled by address wrapping (mirroring) */ fooled = FALSE; for (i = segment - 1; i >= 0; i--) { /* select the segment */ ET6K_SETSEG(i); outb(0x3CB, (i & 0x30) | (i >> 4)); outb(0x3CD, (i & 0x0f) | (i << 4)); if (*VIDMEM == 0x5555AAAA) { /* * Seems like address wrap, but there could of course be * 0x5555AAAA in here by accident, so we check with another * pattern again. */ ET6K_SETSEG(segment); /* test with other pattern again */ *VIDMEM = 0xAAAA5555; ET6K_SETSEG(i); if (*VIDMEM == 0xAAAA5555) { /* now we're sure: this is not real memory */ fooled = TRUE; break; } } } if (!fooled) { real_ram = (segment + 1) * SEGSIZE; break; } /* restore old contents again */ ET6K_SETSEG(segment); *VIDMEM = save_vidmem; } /* restore original register contents */ outb(0x3CD, oldSegSel1); outb(0x3CB, oldSegSel2); outb(0x3CE, 5); outb(0x3CF, oldGR5); outb(0x3CE, 6); outb(0x3CF, oldGR6); outb(0x3C4, 2); outb(0x3C5, oldSEQ2); outb(0x3C4, 4); outb(0x3C5, oldSEQ4); vgaHWUnmapMem(pScrn); return real_ram; } /* * Handle amount of allowed memory: some combinations can't use all * available memory. Should we still allow the user to override this? * * This must be called AFTER the decision has been made to use linear mode * and/or acceleration, or the memory limit code won't be able to work. */ static int TsengDoMemLimit(ScrnInfoPtr pScrn, int ram, int limit, char *reason) { if (ram > limit) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Only %d kb of memory can be used %s.\n", limit, reason); xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Reducing video memory to %d kb.\n", limit); ram = limit; } return ram; } static int TsengLimitMem(ScrnInfoPtr pScrn, int ram) { TsengPtr pTseng = TsengPTR(pScrn); if (pTseng->UseLinMem && pTseng->Linmem_1meg) { ram = TsengDoMemLimit(pScrn, ram, 1024, "in linear mode on " "this VGA board/bus configuration"); } if (pTseng->UseAccel && pTseng->UseLinMem) { if (Is_W32_any) { /* <= W32p_ab : * 2 MB direct access + 2*512kb via apertures MBP0 and MBP1 * == W32p_cd : * 2*1MB via apertures MBP0 and MBP1 */ if (Is_W32p_cd) ram = TsengDoMemLimit(pScrn, ram, 2048, "in linear + accelerated mode " "on W32p rev c and d"); ram = TsengDoMemLimit(pScrn, ram, 2048 + 1024, "in linear + accelerated mode " "on W32/W32i/W32p"); /* * upper 516kb of 4MB linear map used for * "externally mapped registers" */ ram = TsengDoMemLimit(pScrn, ram, 4096 - 516, "in linear + accelerated mode " "on W32/W32i/W32p"); } if (Is_ET6K) { /* * upper 8kb used for externally mapped and * memory mapped registers */ ram = TsengDoMemLimit(pScrn, ram, 4096 - 8, "in linear + accelerated mode " "on ET6000/6100"); } } ram = TsengDoMemLimit(pScrn, ram, 4096, "on any Tseng card"); return ram; } /* * TsengDetectMem -- * try to find amount of video memory installed. * */ static int TsengDetectMem(ScrnInfoPtr pScrn) { unsigned char config; int ramtype = 0; int ram = 0; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengDetectMem\n"); if (Is_ET6K) { ramtype = inb(0x3C2) & 0x03; switch (ramtype) { case 0x03: /* MDRAM */ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Video memory type: Multibank DRAM (MDRAM).\n"); ram = ((inb(pTseng->IOAddress + 0x47) & 0x07) + 1) * 8 * 32; /* number of 8 32kb banks */ if (inb(pTseng->IOAddress + 0x45) & 0x04) { ram <<= 1; } /* * 8*32kb MDRAM refresh control granularity in the ET6000 fails to * recognize 2.25 MB of memory (detects 2.5 instead) */ ram = et6000_check_videoram(pScrn, ram); break; case 0x00: /* DRAM -- VERY unlikely on ET6000 cards, IMPOSSIBLE on ET6100 */ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Video memory type: Standard DRAM.\n"); ram = 1024 << (inb(pTseng->IOAddress + 0x45) & 0x03); break; default: /* unknown RAM type */ xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Unknown ET6000 video memory type %d -- assuming 1 MB (unless specified)\n", ramtype); ram = 1024; } } else { /* pre-ET6000 devices */ int iobase = VGAHWPTR(pScrn)->IOBase; outb(iobase + 0x04, 0x37); config = inb(iobase + 0x05); ram = 128 << (config & 0x03); if (config & 0x80) ram <<= 1; /* Check for interleaving on W32i/p. */ if (Is_W32i || Is_W32p) { outb(iobase + 0x04, 0x32); config = inb(iobase + 0x05); if (config & 0x80) { ram <<= 1; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Video memory type: Interleaved DRAM.\n"); } else { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Video memory type: Standard DRAM.\n"); } } } return ram; } static Bool TsengProcessHibit(ScrnInfoPtr pScrn) { MessageType from = X_CONFIG; int hibit_mode_width; int iobase; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengProcessHibit\n"); if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_HIGH)) { if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_LOW)) { xf86Msg(X_ERROR, "\nOptions \"hibit_high\" and \"hibit_low\" are incompatible;\n"); xf86Msg(X_ERROR, " specify only one (not both) in XFree86 configuration file\n"); return FALSE; } pTseng->save_divide = 0x40; } else if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_HIGH)) { pTseng->save_divide = 0; } else { from = X_PROBED; /* first check to see if hibit is probed from low-res mode */ iobase = VGAHWPTR(pScrn)->IOBase; outb(iobase + 4, 1); hibit_mode_width = inb(iobase + 5) + 1; if (hibit_mode_width > 82) { xf86Msg(X_WARNING, "Non-standard VGA text or graphics mode while probing for hibit:\n"); xf86Msg(X_WARNING, " probed 'hibit' value may be wrong.\n"); xf86Msg(X_WARNING, " Preferably run probe from 80x25 textmode,\n"); xf86Msg(X_WARNING, " or specify correct value in XFree86 configuration file.\n"); } /* Check for initial state of divide flag */ outb(0x3C4, 7); pTseng->save_divide = inb(0x3C5) & 0x40; } xf86DrvMsg(pScrn->scrnIndex, from, "Initial ET4000 hibit state: %s\n", pTseng->save_divide & 0x40 ? "high" : "low"); return TRUE; } static Bool TsengProcessOptions(ScrnInfoPtr pScrn) { MessageType from; double real; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengProcessOptions\n"); /* Collect all of the relevant option flags (fill in pScrn->options) */ xf86CollectOptions(pScrn, NULL); /* Process the options */ if (!(pTseng->Options = xalloc(sizeof(TsengOptions)))) return FALSE; memcpy(pTseng->Options, TsengOptions, sizeof(TsengOptions)); xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pTseng->Options); from = X_DEFAULT; pTseng->HWCursor = FALSE; /* default */ if (xf86GetOptValBool(pTseng->Options, OPTION_HW_CURSOR, &pTseng->HWCursor)) from = X_CONFIG; if (xf86ReturnOptValBool(pTseng->Options, OPTION_SW_CURSOR, FALSE)) { from = X_CONFIG; pTseng->HWCursor = FALSE; } if (pTseng->HWCursor && !Is_ET6K) { xf86DrvMsg(pScrn->scrnIndex, from, "Hardware Cursor not supported on this chipset\n"); pTseng->HWCursor = FALSE; } xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n", pTseng->HWCursor ? "HW" : "SW"); if (pScrn->bitsPerPixel >= 8) { if (pTseng->ChipType != TYPE_ET4000) pTseng->UseAccel = TRUE; if (xf86ReturnOptValBool(pTseng->Options, OPTION_NOACCEL, FALSE)) { pTseng->UseAccel = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Acceleration disabled\n"); } } else pTseng->UseAccel = FALSE; /* 1bpp and 4bpp are always non-accelerated */ pTseng->SlowDram = FALSE; if (xf86IsOptionSet(pTseng->Options, OPTION_SLOW_DRAM)) { pTseng->SlowDram = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using slow DRAM access\n"); } pTseng->MedDram = FALSE; if (xf86IsOptionSet(pTseng->Options, OPTION_MED_DRAM)) { pTseng->MedDram = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using Medium-speed DRAM access\n"); } pTseng->FastDram = FALSE; if (xf86IsOptionSet(pTseng->Options, OPTION_FAST_DRAM)) { pTseng->FastDram = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using fast DRAM access\n"); } if ((pTseng->SetW32Interleave = xf86GetOptValBool(pTseng->Options, OPTION_W32_INTERLEAVE, &pTseng->W32Interleave)) ) xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forcing W32p memory interleave %s.\n", pTseng->W32Interleave ? "ON" : "OFF"); if ((pTseng->SetPCIBurst = xf86GetOptValBool(pTseng->Options, OPTION_PCI_BURST, &pTseng->PCIBurst)) ) xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forcing PCI burst mode %s.\n", pTseng->PCIBurst ? "ON" : "OFF"); from = X_CONFIG; if (xf86GetOptValBool(pTseng->Options, OPTION_LINEAR, &pTseng->UseLinMem)) { /* check if linear mode is allowed */ if (pTseng->UseLinMem) { if (!CHIP_SUPPORTS_LINEAR) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Linear memory not supported on chipset \"%s\".\n", pScrn->chipset); pTseng->UseLinMem = FALSE; } else if (!xf86LinearVidMem()) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "This operating system does not support a linear framebuffer.\n"); pTseng->UseLinMem = FALSE; } } } else { /* option not specified: use defaults */ from = X_DEFAULT; if (pTseng->PciTag) pTseng->UseLinMem = TRUE; /* use linear memory by default on PCI systems */ else pTseng->UseLinMem = FALSE; /* ... and banked on non-PCI systems */ } xf86DrvMsg(pScrn->scrnIndex, from, "Using %s Memory.\n", (pTseng->UseLinMem) ? "linear" : "banked"); pTseng->ShowCache = FALSE; if (xf86ReturnOptValBool(pTseng->Options, OPTION_SHOWCACHE, FALSE)) { pTseng->ShowCache = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "(for debugging only:) Visible off-screen memory\n"); } pTseng->Legend = FALSE; if (xf86ReturnOptValBool(pTseng->Options, OPTION_LEGEND, FALSE)) { pTseng->Legend = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using Legend pixel clock selection.\n"); } pTseng->NoClockchip = FALSE; if (xf86ReturnOptValBool(pTseng->Options, OPTION_NOCLOCKCHIP, FALSE)) { pTseng->NoClockchip = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Disabling clockchip programming.\n"); } /* * Check_Tseng_ramdac() already set pScrn->progClock according to probed * values. Override it if requested. */ if (pTseng->NoClockchip) pScrn->progClock = FALSE; pTseng->UsePCIRetry = FALSE; if (xf86ReturnOptValBool(pTseng->Options, OPTION_PCI_RETRY, FALSE)) { pTseng->UsePCIRetry = TRUE; xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry enabled\n"); } pTseng->MemClk = 0; if (xf86GetOptValFreq(pTseng->Options, OPTION_SET_MCLK, OPTUNITS_MHZ, &real)) pTseng->MemClk = (int)(real * 1000.0); return TRUE; } static Bool TsengGetLinFbAddress(ScrnInfoPtr pScrn) { MessageType from; TsengPtr pTseng = TsengPTR(pScrn); resRange range[] = { {ResExcMemBlock,0,0},_END }; PDEBUG(" TsengGetLinFbAddress\n"); from = X_PROBED; /* let config file override Base address */ if (pTseng->pEnt->device->MemBase != 0) { pTseng->LinFbAddress = pTseng->pEnt->device->MemBase; from = X_CONFIG; /* check for possible errors in given linear base address */ if ((pTseng->LinFbAddress & (~pTseng->LinFbAddressMask)) != 0) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "MemBase out of range. Must be <= 0x%x on 0x%x boundary.\n", pTseng->LinFbAddressMask, ~(pTseng->LinFbAddressMask | 0xFF000000) + 1); pTseng->LinFbAddress &= ~pTseng->LinFbAddressMask; xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Clipping MemBase to: 0x%x.\n", pTseng->LinFbAddress); range[0].rBegin = pTseng->LinFbAddress; range[0].rEnd = pTseng->LinFbAddress + 16 * 1024 * 1024; if (xf86RegisterResources(pTseng->pEnt->index,range,ResNone)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Cannot register linear memory." " Using banked mode instead.\n"); pTseng->UseLinMem = FALSE; return TRUE; } } } else { from = X_PROBED; if (pTseng->PciTag) { /* * base0 is the framebuffer and base1 is the PCI IO space. */ if ((pTseng->PciInfo->memBase[0]) != 0) { pTseng->LinFbAddress = pTseng->PciInfo->memBase[0]; } else { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid Framebuffer address in PCI config space;\n"); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Falling back to banked mode.\n"); pTseng->UseLinMem = FALSE; return TRUE; } if (xf86RegisterResources(pTseng->pEnt->index,range,ResNone)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Cannot register linear memory." " Using banked mode instead.\n"); pTseng->UseLinMem = FALSE; return TRUE; } } else { /* non-PCI boards */ /* W32p cards can give us their Lin. memory address through the PCI * configuration. For W32i, this is not possible (VL-bus, MCA or ISA). W32i * cards have three extra external "address" lines, SEG2..SEG0 which _can_ * be connected to any set of address lines in addition to the already * connected A23..A0. SEG2..SEG0 are either for three extra address lines * or to connect an external address decoder (mostly an 74F27). It is NOT * possible to know how SEG2..SEG0 are connected. We _assume_ they are * connected to A26..A24 (most likely case). This means linear memory can * be decoded into any 4MB block in the address range 0..128MB. * * For non-PCI cards (especially VLB), most motherboards don't decode all * 32 address bits. The weird default memory base below will always end up * at the end of the decoded address space -- independent of the number of * address bits that are decoded. */ #define DEFAULT_LIN_MEMBASE ( (256 + 128 + 64 + 32 + 16 + 8 + 4) * 1024*1024 ) #define DEFAULT_LIN_MEMBASE_PCI (DEFAULT_LIN_MEMBASE & 0xFF000000) switch (pTseng->ChipType) { case TYPE_ET4000W32: case TYPE_ET4000W32I: case TYPE_ET4000W32P: /* A31,A30 are decoded as 00 (=always mapped below 512 MB) */ pTseng->LinFbAddress = DEFAULT_LIN_MEMBASE; if (pTseng->LinFbAddress > pTseng->LinFbAddressMask) /* ... except if we can't decode that much */ pTseng->LinFbAddress = pTseng->LinFbAddressMask - 4 * 1024 * 1024; /* top of decodable memory */ break; default: xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "TsengNonPciLinMem(): Internal error. This should not happen: please report to XFree86@XFree86.Org\n"); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Falling back to banked mode.\n"); pTseng->UseLinMem = FALSE; return TRUE; } pTseng->LinFbAddress &= pTseng->LinFbAddressMask; /* One final check for a valid MemBase */ if (pTseng->LinFbAddress < 4096 * 1024) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid MemBase for linear mode:\n"); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " please define a non-zero MemBase in XF86Config.\n"); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " See README.tseng or tseng.sgml for more information.\n"); xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Using banked mode instead.\n"); pTseng->UseLinMem = FALSE; return TRUE; } range[0].type |= ResBios; range[0].rBegin = pTseng->LinFbAddress; range[0].rEnd = pTseng->LinFbAddress + 16 * 1024 * 1024; if (xf86RegisterResources(pTseng->pEnt->index,range,ResNone)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Cannot register linear memory." " Using banked mode instead.\n"); pTseng->UseLinMem = FALSE; return TRUE; } } } /* The W32 linear map address space is always 4Mb (mainly because the * memory-mapped registers are located near the top of the 4MB area). * The ET6000 maps out 16 Meg, but still uses only 4Mb of that. * However, since all mmap()-ed space is also reflected in the "ps" * listing for the Xserver, many users will be worried by a server that * always eats 16MB of memory, even if it's not "real" memory, just * address space. Not mapping all of the 16M may be a potential problem * though: if another board is mapped on top of the remaining part of * the 16M... Boom! */ if (Is_ET6K) pTseng->FbMapSize = 16384 * 1024; else pTseng->FbMapSize = 4096 * 1024; xf86DrvMsg(pScrn->scrnIndex, from, "Linear framebuffer at 0x%lX\n", (unsigned long)pTseng->LinFbAddress); return TRUE; } static Bool TsengPreInit(ScrnInfoPtr pScrn, int flags) { TsengPtr pTseng; MessageType from; int i; if (flags & PROBE_DETECT) return FALSE; PDEBUG(" TsengPreInit\n"); /* * Note: This function is only called once at server startup, and * not at the start of each server generation. This means that * only things that are persistent across server generations can * be initialised here. xf86Screens[] is (pScrn is a pointer to one * of these). Privates allocated using xf86AllocateScrnInfoPrivateIndex() * are too, and should be used for data that must persist across * server generations. * * Per-generation data should be allocated with * AllocateScreenPrivateIndex() from the ScreenInit() function. */ /* The vgahw module should be loaded here when needed */ /* This driver doesn't expect more than one entity per screen */ if (pScrn->numEntities > 1) return FALSE; /* Allocate the TsengRec driverPrivate */ if (!TsengGetRec(pScrn)) { return FALSE; } pTseng = TsengPTR(pScrn); /* This is the general case */ pTseng->pEnt = xf86GetEntityInfo(*pScrn->entityList); #if 1 if (xf86LoadSubModule(pScrn, "int10")) { xf86Int10InfoPtr pInt; xf86LoaderReqSymLists(int10Symbols, NULL); #if 1 xf86DrvMsg(pScrn->scrnIndex,X_INFO,"initializing int10\n"); pInt = xf86InitInt10(pTseng->pEnt->index); xf86FreeInt10(pInt); #endif } #endif if (!xf86LoadSubModule(pScrn, "vgahw")) return FALSE; xf86LoaderReqSymLists(vgaHWSymbols, NULL); /* * Allocate a vgaHWRec */ if (!vgaHWGetHWRec(pScrn)) return FALSE; vgaHWGetIOBase(VGAHWPTR(pScrn)); /* * Since, the capabilities are determined by the chipset, the very first * thing to do is to figure out the chipset and its capabilities. */ TsengUnlock(); /* * Find the bus slot for this screen (PCI or other). This also finds the * exact chipset type. */ /* This driver can handle ISA and PCI buses */ if (pTseng->pEnt->location.type == BUS_PCI) { pTseng->PciInfo = xf86GetPciInfoForEntity(pTseng->pEnt->index); if (!TsengPreInitPCI(pScrn)) { TsengFreeRec(pScrn); return FALSE; } } else if (!TsengPreInitNoPCI(pScrn)) { TsengFreeRec(pScrn); return FALSE; } /* * Find RAMDAC and CLOCKCHIP type and fill Tsengdac struct */ if (!Check_Tseng_Ramdac(pScrn)) return FALSE; /* check for clockchip */ if (!Tseng_check_clockchip(pScrn)) { return FALSE; } /* * Now we can check what depth we support. */ /* Set pScrn->monitor */ pScrn->monitor = pScrn->confScreen->monitor; /* * The first thing we should figure out is the depth, bpp, etc. * Our default depth is 8, so pass it to the helper function. * Our preference for depth 24 is 24bpp, so tell it that too. */ if (!xf86SetDepthBpp(pScrn, 8, 8, 8, Support24bppFb | Support32bppFb | SupportConvert32to24 | PreferConvert32to24)) { return FALSE; } else { /* Check that the returned depth is one we support */ Bool CanDo16bpp = FALSE, CanDo24bpp = FALSE, CanDo32bpp = FALSE; Bool CanDoThis = FALSE; switch (pTseng->DacInfo.DacType) { case ET6000_DAC: case ICS5341_DAC: case STG1703_DAC: case STG1702_DAC: CanDo16bpp = TRUE; CanDo24bpp = TRUE; CanDo32bpp = TRUE; break; case ATT20C490_DAC: case ATT20C491_DAC: case ATT20C492_DAC: case ATT20C493_DAC: case ICS5301_DAC: case MUSIC4910_DAC: CanDo16bpp = TRUE; CanDo24bpp = TRUE; break; case CH8398_DAC: CanDo16bpp = TRUE; CanDo24bpp = TRUE; break; case STG1700_DAC: /* can't do packed 24bpp over a 16-bit bus */ CanDo16bpp = TRUE; /* FIXME: can do it over 8 bit bus */ CanDo32bpp = TRUE; break; default: /* default: only 1, 4, 8 bpp */ break; } switch (pScrn->depth) { case 1: case 4: case 8: CanDoThis = TRUE; break; case 16: CanDoThis = CanDo16bpp; break; case 24: CanDoThis = (CanDo24bpp || CanDo32bpp); break; } if (!CanDoThis) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Given depth (%d) is not supported by this chipset/RAMDAC\n", pScrn->depth); return FALSE; } if ((pScrn->bitsPerPixel == 32) && (!CanDo32bpp)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Given bpp (%d) is not supported by this chipset/RAMDAC\n", pScrn->bitsPerPixel); return FALSE; } } xf86PrintDepthBpp(pScrn); /* Get the depth24 pixmap format */ if (pScrn->depth == 24 && pix24bpp == 0) pix24bpp = xf86GetBppFromDepth(pScrn, 24); if (pScrn->bitsPerPixel > 8) pTseng->Bytesperpixel = pScrn->bitsPerPixel / 8; else pTseng->Bytesperpixel = 1; /* this is fake for < 8bpp, but simplifies other code */ /* hardware limits */ pScrn->maxHValue = Tseng_HMAX; pScrn->maxVValue = Tseng_VMAX; /* * This must happen after pScrn->display has been set because * xf86SetWeight references it. */ /* Set weight/mask/offset for depth > 8 */ if (pScrn->depth > 8) { /* The defaults are OK for us */ rgb zeros = {0, 0, 0}; rgb mask; /* * Initialize mask here. * Currently we only treat the ICS5341 RAMDAC special */ if ((pScrn->depth == 24) && (pScrn->bitsPerPixel == 24)) mask = pTseng->DacInfo.rgb24packed; else mask = zeros; if (!xf86SetWeight(pScrn, zeros, mask)) { return FALSE; } else { /* XXX check that weight returned is supported */ ; } } /* Set the default visual. */ if (!xf86SetDefaultVisual(pScrn, -1)) return FALSE; /* The gamma fields must be initialised when using the new cmap code */ if (pScrn->depth > 1) { Gamma zeros = {0.0, 0.0, 0.0}; if (!xf86SetGamma(pScrn, zeros)) { return FALSE; } } /* Set the bits per RGB for 8bpp mode */ if (pScrn->depth == 8) { /* Default to 6, because most Tseng chips/RAMDACs don't support it */ pScrn->rgbBits = 6; } if (!TsengProcessOptions(pScrn)) /* must be done _after_ we know what chip this is */ return FALSE; if (pTseng->UseLinMem) { if (!TsengGetLinFbAddress(pScrn)) return FALSE; } if (pTseng->UseAccel) VGAHWPTR(pScrn)->MapSize = 0x20000; /* accelerator apertures and MMIO */ else VGAHWPTR(pScrn)->MapSize = 0x10000; if (pTseng->UseLinMem) { /* * XXX At least part of this range does appear to be disabled, * but to play safe, it is marked as "unused" for now. * Changed this to "disable". Otherwise it might interfere with DGA. */ xf86SetOperatingState(resVgaMemShared, pTseng->pEnt->index, ResDisableOpr); } /* hibit processing (TsengProcessOptions() must have been called first) */ pTseng->save_divide = 0x40; /* default */ if (!Is_ET6K) { if (!TsengProcessHibit(pScrn)) return FALSE; } /* * If the user has specified the amount of memory in the XF86Config * file, we respect that setting. */ if (pTseng->pEnt->device->videoRam != 0) { pScrn->videoRam = pTseng->pEnt->device->videoRam; from = X_CONFIG; } else { from = X_PROBED; pScrn->videoRam = TsengDetectMem(pScrn); } pScrn->videoRam = TsengLimitMem(pScrn, pScrn->videoRam); xf86DrvMsg(pScrn->scrnIndex, from, "VideoRAM: %d kByte.\n", pScrn->videoRam); /* do all clock-related setup */ tseng_clock_setup(pScrn); /* * xf86ValidateModes will check that the mode HTotal and VTotal values * don't exceed the chipset's limit if pScrn->maxHValue and * pScrn->maxVValue are set. Since our TsengValidMode() already takes * care of this, we don't worry about setting them here. */ /* Select valid modes from those available */ i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, pScrn->display->modes, pTseng->clockRange[0], NULL, 32, pScrn->maxHValue, 8*pTseng->Bytesperpixel, /* H limits */ 0, pScrn->maxVValue, /* V limits */ pScrn->display->virtualX, pScrn->display->virtualY, pTseng->FbMapSize, LOOKUP_BEST_REFRESH); /* LOOKUP_CLOSEST_CLOCK | LOOKUP_CLKDIV2 when no programable clock ? */ if (i == -1) { TsengFreeRec(pScrn); return FALSE; } /* Prune the modes marked as invalid */ xf86PruneDriverModes(pScrn); if (i == 0 || pScrn->modes == NULL) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n"); TsengFreeRec(pScrn); return FALSE; } /* * Set the CRTC parameters for all of the modes based on the type * of mode, and the chipset's interlace requirements. * * Calling this is required if the mode->Crtc* values are used by the * driver and if the driver doesn't provide code to set them. They * are not pre-initialised at all. */ xf86SetCrtcForModes(pScrn, 0); /* Set the current mode to the first in the list */ pScrn->currentMode = pScrn->modes; /* Print the list of modes being used */ xf86PrintModes(pScrn); /* Set display resolution */ xf86SetDpi(pScrn, 0, 0); /* Load bpp-specific modules */ switch (pScrn->bitsPerPixel) { case 1: if (xf86LoadSubModule(pScrn, "xf1bpp") == NULL) { TsengFreeRec(pScrn); return FALSE; } xf86LoaderReqSymbols("xf1bppScreenInit", NULL); break; case 4: if (xf86LoadSubModule(pScrn, "xf4bpp") == NULL) { TsengFreeRec(pScrn); return FALSE; } xf86LoaderReqSymbols("xf4bppScreenInit", NULL); break; default: if (xf86LoadSubModule(pScrn, "fb") == NULL) { TsengFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(fbSymbols, NULL); break; } /* Load XAA if needed */ if (pTseng->UseAccel) { if (!xf86LoadSubModule(pScrn, "xaa")) { TsengFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(xaaSymbols, NULL); } /* Load ramdac if needed */ if (pTseng->HWCursor) { if (!xf86LoadSubModule(pScrn, "ramdac")) { TsengFreeRec(pScrn); return FALSE; } xf86LoaderReqSymLists(ramdacSymbols, NULL); } /* TsengLock(); */ return TRUE; } static void TsengSetupAccelMemory(int scrnIndex, ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; TsengPtr pTseng = TsengPTR(pScrn); int offscreen_videoram, videoram_end, req_videoram; int i; int v; /* XXX Hack to suppress messages in subsequent generations. */ if (serverGeneration == 1) v = 1; else v = 100; /* * The accelerator requires free off-screen video memory to operate. The * more there is, the more it can accelerate. */ videoram_end = pScrn->videoRam * 1024; offscreen_videoram = videoram_end - pScrn->displayWidth * pScrn->virtualY * pTseng->Bytesperpixel; xf86DrvMsgVerb(scrnIndex, X_INFO, v, "Available off-screen memory: %d bytes.\n", offscreen_videoram); /* * The HW cursor requires 1kb of off-screen memory, aligned to 1kb * (256 DWORDS). Setting up its memory first ensures the alignment. */ if (pTseng->HWCursor) { req_videoram = 1024; if (offscreen_videoram < req_videoram) { xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, "Hardware Cursor disabled. It requires %d bytes of free video memory\n", req_videoram); pTseng->HWCursor = FALSE; pTseng->HWCursorBufferOffset = 0; } else { offscreen_videoram -= req_videoram; videoram_end -= req_videoram; pTseng->HWCursorBufferOffset = videoram_end; } } else { pTseng->HWCursorBufferOffset = 0; } /* * Acceleration memory setup. Do this only if acceleration is enabled. */ if (!pTseng->UseAccel) return; /* * Basic acceleration needs storage for FG, BG and PAT colors in * off-screen memory. Each color requires 2(ping-pong)*8 bytes. */ req_videoram = 2 * 8 * 3; if (offscreen_videoram < req_videoram) { xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, "Acceleration disabled. It requires AT LEAST %d bytes of free video memory\n", req_videoram); pTseng->UseAccel = FALSE; pTseng->AccelColorBufferOffset = 0; goto end_memsetup; /* no basic acceleration means none at all */ } else { offscreen_videoram -= req_videoram; videoram_end -= req_videoram; pTseng->AccelColorBufferOffset = videoram_end; } /* * Color expansion (using triple buffering) requires 3 non-expanded * scanlines, DWORD padded. */ req_videoram = 3 * ((pScrn->virtualX + 31) / 32) * 4; if (offscreen_videoram < req_videoram) { xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, "Accelerated color expansion disabled (%d more bytes of free video memory required)\n", req_videoram - offscreen_videoram); pTseng->AccelColorExpandBufferOffsets[0] = 0; } else { offscreen_videoram -= req_videoram; for (i = 0; i < 3; i++) { videoram_end -= req_videoram / 3; pTseng->AccelColorExpandBufferOffsets[i] = videoram_end; } } /* * XAA ImageWrite support needs two entire line buffers. The * current code assumes buffer 1 lies in the same 8kb aperture as * buffer 0. * * [ FIXME: aren't we forgetting the DWORD padding here ? ] * [ FIXME: why here double-buffering and in colexp triple-buffering? ] */ req_videoram = 2 * (pScrn->virtualX * pTseng->Bytesperpixel); /* banked mode uses an 8kb aperture for imagewrite */ if ((req_videoram > 8192) && (!pTseng->UseLinMem)) { xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, "Accelerated ImageWrites disabled (banked %dbpp virtual width must be <= %d)\n", pScrn->bitsPerPixel, 8192 / (2 * pTseng->Bytesperpixel)); pTseng->AccelImageWriteBufferOffsets[0] = 0; } else if (offscreen_videoram < req_videoram) { xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, "Accelerated ImageWrites disabled (%d more bytes of free video memory required)\n", req_videoram - offscreen_videoram); pTseng->AccelImageWriteBufferOffsets[0] = 0; } else { offscreen_videoram -= req_videoram; for (i = 0; i < 2; i++) { videoram_end -= req_videoram / 2; pTseng->AccelImageWriteBufferOffsets[i] = videoram_end; } } xf86DrvMsgVerb(scrnIndex, X_INFO, v, "Remaining off-screen memory available for pixmap cache: %d bytes.\n", offscreen_videoram); end_memsetup: pScrn->videoRam = videoram_end / 1024; } static Bool TsengScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) { ScrnInfoPtr pScrn; TsengPtr pTseng; int ret; VisualPtr visual; PDEBUG(" TsengScreenInit\n"); /* * First get the ScrnInfoRec */ pScrn = xf86Screens[pScreen->myNum]; pTseng = TsengPTR(pScrn); /* Map the Tseng memory areas */ if (!TsengMapMem(pScrn)) return FALSE; /* Save the current state */ TsengSave(pScrn); /* Initialise the first mode */ TsengModeInit(pScrn, pScrn->currentMode); /* Darken the screen for aesthetic reasons and set the viewport */ TsengSaveScreen(pScreen, SCREEN_SAVER_ON); TsengAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0); /* XXX Fill the screen with black */ /* * Reset visual list. */ miClearVisualTypes(); /* Setup the visuals we support. */ /* * For bpp > 8, the default visuals are not acceptable because we only * support TrueColor and not DirectColor. To deal with this, call * miSetVisualTypes for each visual supported. */ if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth), pScrn->rgbBits, pScrn->defaultVisual)) return FALSE; miSetPixmapDepths (); /* * Call the framebuffer layer's ScreenInit function, and fill in other * pScreen fields. */ switch (pScrn->bitsPerPixel) { case 1: ret = xf1bppScreenInit(pScreen, pTseng->FbBase, pScrn->virtualX, pScrn->virtualY, pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth); break; case 4: ret = xf4bppScreenInit(pScreen, pTseng->FbBase, pScrn->virtualX, pScrn->virtualY, pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth); break; default: ret = fbScreenInit(pScreen, pTseng->FbBase, pScrn->virtualX, pScrn->virtualY, pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth, pScrn->bitsPerPixel); break; } if (!ret) return FALSE; xf86SetBlackWhitePixels(pScreen); if (pScrn->bitsPerPixel > 8) { /* Fixup RGB ordering */ visual = pScreen->visuals + pScreen->numVisuals; while (--visual >= pScreen->visuals) { if ((visual->class | DynamicClass) == DirectColor) { visual->offsetRed = pScrn->offset.red; visual->offsetGreen = pScrn->offset.green; visual->offsetBlue = pScrn->offset.blue; visual->redMask = pScrn->mask.red; visual->greenMask = pScrn->mask.green; visual->blueMask = pScrn->mask.blue; } } } /* must be after RGB ordering fixed */ if (pScrn->bitsPerPixel > 4) fbPictureInit(pScreen, 0, 0); if (pScrn->depth >= 8) TsengDGAInit(pScreen); /* * If banking is needed, initialise an miBankInfoRec (defined in * "mibank.h"), and call miInitializeBanking(). */ if (!pTseng->UseLinMem) { if (!Is_stdET4K && (pScrn->videoRam > 1024)) { pTseng->BankInfo.SetSourceBank = ET4000W32SetRead; pTseng->BankInfo.SetDestinationBank = ET4000W32SetWrite; pTseng->BankInfo.SetSourceAndDestinationBanks = ET4000W32SetReadWrite; } else { pTseng->BankInfo.SetSourceBank = ET4000SetRead; pTseng->BankInfo.SetDestinationBank = ET4000SetWrite; pTseng->BankInfo.SetSourceAndDestinationBanks = ET4000SetReadWrite; } pTseng->BankInfo.pBankA = pTseng->FbBase; pTseng->BankInfo.pBankB = pTseng->FbBase; pTseng->BankInfo.BankSize = 0x10000; pTseng->BankInfo.nBankDepth = (pScrn->depth == 4) ? 1 : pScrn->depth; if (!miInitializeBanking(pScreen, pScrn->virtualX, pScrn->virtualY, pScrn->displayWidth, &pTseng->BankInfo)) { return FALSE; } } /* * Initialize the acceleration interface. */ TsengSetupAccelMemory(scrnIndex, pScreen); if (pTseng->UseAccel) { tseng_init_acl(pScrn); /* set up accelerator */ if (!TsengXAAInit(pScreen)) { /* set up XAA interface */ return FALSE; } } miInitializeBackingStore(pScreen); xf86SetSilkenMouse(pScreen); /* Initialise cursor functions */ miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); /* Hardware Cursor layer */ if (pTseng->HWCursor) { if (!TsengHWCursorInit(pScreen)) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Hardware cursor initialization failed\n"); } /* Initialise default colourmap */ if (!miCreateDefColormap(pScreen)) return FALSE; if (pScrn->depth == 4 || pScrn->depth == 8) { /* fb and xf4bpp */ vgaHWHandleColormaps(pScreen); } pScrn->racIoFlags = RAC_FB | RAC_COLORMAP | RAC_CURSOR | RAC_VIEWPORT; pScrn->racMemFlags = pScrn->racIoFlags; /* Wrap the current CloseScreen and SaveScreen functions */ pScreen->SaveScreen = TsengSaveScreen; /* Support for DPMS, the ET4000W32Pc and newer uses a different and * simpler method than the older cards. */ if (Is_W32p_cd || Is_ET6K) { xf86DPMSInit(pScreen, (DPMSSetProcPtr)TsengCrtcDPMSSet, 0); } else { xf86DPMSInit(pScreen, (DPMSSetProcPtr)TsengHVSyncDPMSSet, 0); } pTseng->CloseScreen = pScreen->CloseScreen; pScreen->CloseScreen = TsengCloseScreen; /* Report any unused options (only for the first generation) */ if (serverGeneration == 1) { xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); } /* Done */ return TRUE; } static Bool TsengEnterVT(int scrnIndex, int flags) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengEnterVT\n"); vgaHWUnlock(VGAHWPTR(pScrn)); TsengUnlock(); if (!TsengModeInit(pScrn, pScrn->currentMode)) return FALSE; if (pTseng->UseAccel) { tseng_init_acl(pScrn); /* set up accelerator */ } return TRUE; } static void TsengLeaveVT(int scrnIndex, int flags) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengLeaveVT\n"); TsengRestore(pScrn, &(VGAHWPTR(pScrn)->SavedReg), &pTseng->SavedReg,VGA_SR_ALL); TsengLock(); vgaHWLock(VGAHWPTR(pScrn)); } static Bool TsengCloseScreen(int scrnIndex, ScreenPtr pScreen) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengCloseScreen\n"); if (pScrn->vtSema) { TsengRestore(pScrn, &(VGAHWPTR(pScrn)->SavedReg), &(pTseng->SavedReg),VGA_SR_ALL); TsengUnmapMem(pScrn); } if (pTseng->AccelInfoRec) XAADestroyInfoRec(pTseng->AccelInfoRec); if (pTseng->CursorInfoRec) xf86DestroyCursorInfoRec(pTseng->CursorInfoRec); pScrn->vtSema = FALSE; pScreen->CloseScreen = pTseng->CloseScreen; return (*pScreen->CloseScreen) (scrnIndex, pScreen); } /* * SaveScreen -- * * perform a sequencer reset. * * The ET4000 "Video System Configuration 1" register (CRTC index 0x36), * which is used to set linear memory mode and MMU-related stuff, is * partially reset to "0" when TS register index 0 bit 1 is set (synchronous * reset): bits 3..5 are reset during a sync. reset. * * We therefor do _not_ call vgaHWSaveScreen here, since it does a sequencer * reset. Instead, we do the same as in vgaHWSaveScreen except for the seq. reset. * * If this is not done, the higher level code will not be able to access the * framebuffer (because it is temporarily in banked mode instead of linear * mode) as long as SaveScreen is active (=in between a * SaveScreen(FALSE)/SaveScreen(TRUE) pair) */ static Bool TsengSaveScreen(ScreenPtr pScreen, int mode) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; TsengPtr pTseng = TsengPTR(pScrn); Bool unblank; PDEBUG(" TsengSaveScreen\n"); unblank = xf86IsUnblank(mode); if (Is_ET6K) { return vgaHWSaveScreen(pScreen, unblank); } else { if (unblank) SetTimeSinceLastInputEvent(); if (pScrn->vtSema) { TsengBlankScreen(pScrn, unblank); } return (TRUE); } } static Bool TsengMapMem(ScrnInfoPtr pScrn) { TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengMapMem\n"); /* Map the VGA memory */ if (!vgaHWMapMem(pScrn)) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not mmap standard VGA memory aperture.\n"); return FALSE; } if (pTseng->UseLinMem) { pTseng->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER, pTseng->PciTag, (unsigned long)pTseng->LinFbAddress, pTseng->FbMapSize); if (pTseng->FbBase == NULL) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not mmap linear video memory.\n"); return FALSE; } if (pTseng->UseAccel) { pTseng->MMioBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO, pTseng->PciTag, (unsigned long)pTseng->LinFbAddress, pTseng->FbMapSize); if (!pTseng->MMioBase) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not mmap mmio memory.\n"); return FALSE; } pTseng->MMioBase += 0x3FFF00L; } } else { vgaHWPtr hwp = VGAHWPTR(pScrn); pTseng->FbBase = hwp->Base; if (pTseng->UseAccel) { pTseng->MMioBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO, pTseng->PciTag, (unsigned long)hwp->MapPhys, hwp->MapSize); if (!pTseng->MMioBase) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not mmap mmio memory.\n"); return FALSE; } pTseng->MMioBase += 0x1FF00L; } } if (pTseng->FbBase == NULL) return FALSE; return TRUE; } static Bool TsengUnmapMem(ScrnInfoPtr pScrn) { TsengPtr pTseng = TsengPTR(pScrn); PDEBUG(" TsengUnmapMem\n"); if (pTseng->UseLinMem) { xf86UnMapVidMem(pScrn->scrnIndex, (pointer) pTseng->FbBase, pTseng->FbMapSize); } vgaHWUnmapMem(pScrn); pTseng->FbBase = NULL; return TRUE; } static void TsengFreeScreen(int scrnIndex, int flags) { PDEBUG(" TsengFreeScreen\n"); if (xf86LoaderCheckSymbol("vgaHWFreeHWRec")) vgaHWFreeHWRec(xf86Screens[scrnIndex]); TsengFreeRec(xf86Screens[scrnIndex]); } Bool TsengModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode) { vgaHWPtr hwp; TsengPtr pTseng = TsengPTR(pScrn); TsengRegPtr new = &(pTseng->ModeReg); TsengRegPtr initial = &(pTseng->SavedReg); int row_offset; int min_n2; int hdiv = 1, hmul = 1; PDEBUG(" TsengModeInit\n"); switch (mode->PrivFlags) { case TSENG_MODE_PIXMUX: case TSENG_MODE_DACBUS16: hdiv = pTseng->clockRange[1]->ClockDivFactor; hmul = pTseng->clockRange[1]->ClockMulFactor; break; default: hdiv = pTseng->clockRange[0]->ClockDivFactor; hmul = pTseng->clockRange[0]->ClockMulFactor; } /* * Modify mode timings accordingly */ if (!mode->CrtcHAdjusted) { /* now divide and multiply the horizontal timing parameters as required */ mode->CrtcHTotal = (mode->CrtcHTotal * hmul) / hdiv; mode->CrtcHDisplay = (mode->CrtcHDisplay * hmul) / hdiv; mode->CrtcHSyncStart = (mode->CrtcHSyncStart * hmul) / hdiv; mode->CrtcHSyncEnd = (mode->CrtcHSyncEnd * hmul) / hdiv; mode->CrtcHBlankStart = (mode->CrtcHBlankStart * hmul) / hdiv; mode->CrtcHBlankEnd = (mode->CrtcHBlankEnd * hmul) / hdiv; mode->CrtcHSkew = (mode->CrtcHSkew * hmul) / hdiv; if (pScrn->bitsPerPixel == 24) { int rgb_skew; /* * in 24bpp, the position of the BLANK signal determines the * phase of the R,G and B values. XFree86 sets blanking equal to * the Sync, so setting the Sync correctly will also set the * BLANK corectly, and thus also the RGB phase */ rgb_skew = (mode->CrtcHTotal / 8 - mode->CrtcHBlankEnd / 8 - 1) % 3; mode->CrtcHBlankEnd += rgb_skew * 8 + 24; /* HBlankEnd must come BEFORE HTotal */ if (mode->CrtcHBlankEnd > mode->CrtcHTotal) mode->CrtcHBlankEnd -= 24; } mode->CrtcHAdjusted = TRUE; } /* set clockIndex to "2" for programmable clocks */ if (pScrn->progClock) mode->ClockIndex = 2; /* prepare standard VGA register contents */ hwp = VGAHWPTR(pScrn); if (!vgaHWInit(pScrn, mode)) return (FALSE); pScrn->vtSema = TRUE; /* prepare extended (Tseng) register contents */ /* * Start by copying all the saved registers in the "new" data, so we * only have to modify those that need to change. */ memcpy(new, initial, sizeof(TsengRegRec)); if (pScrn->bitsPerPixel < 8) { /* Don't ask me why this is needed on the ET6000 and not on the others */ if (Is_ET6K) hwp->ModeReg.Sequencer[1] |= 0x04; row_offset = hwp->ModeReg.CRTC[19]; } else { hwp->ModeReg.Attribute[16] = 0x01; /* use the FAST 256 Color Mode */ row_offset = pScrn->displayWidth >> 3; /* overruled by 16/24/32 bpp code */ } hwp->ModeReg.CRTC[20] = 0x60; hwp->ModeReg.CRTC[23] = 0xAB; new->ExtTS[6] = 0x00; new->ExtTS[7] = 0xBC; new->ExtCRTC[0x33] = 0x00; new->ExtCRTC[0x35] = (mode->Flags & V_INTERLACE ? 0x80 : 0x00) | 0x10 | ((mode->CrtcVSyncStart & 0x400) >> 7) | (((mode->CrtcVDisplay - 1) & 0x400) >> 8) | (((mode->CrtcVTotal - 2) & 0x400) >> 9) | (((mode->CrtcVBlankStart - 1) & 0x400) >> 10); if (pScrn->bitsPerPixel < 8) new->ExtATC = 0x00; else new->ExtATC = 0x80; if (pScrn->bitsPerPixel >= 8) { if (pTseng->FastDram && !Is_ET6K) { /* * make sure Trsp is no more than 75ns * Tcsw is 25ns * Tcsp is 25ns * Trcd is no more than 50ns * Timings assume SCLK = 40MHz * * Note, this is experimental, but works for me (DHD) */ /* Tcsw, Tcsp, Trsp */ new->ExtCRTC[0x32] &= ~0x1F; if (new->ExtCRTC[0x32] & 0x18) new->ExtCRTC[0x32] |= 0x08; /* Trcd */ new->ExtCRTC[0x32] &= ~0x20; } } /* * Here we make sure that CRTC regs 0x34 and 0x37 are untouched, except for * some bits we want to change. * Notably bit 7 of CRTC 0x34, which changes RAS setup time from 4 to 0 ns * (performance), * and bit 7 of CRTC 0x37, which changes the CRTC FIFO low treshold control. * At really high pixel clocks, this will avoid lots of garble on the screen * when something is being drawn. This only happens WAY beyond 80 MHz * (those 135 MHz ramdac's...) */ if (Is_W32i || Is_W32p) { if (!pTseng->SlowDram) new->ExtCRTC[0x34] = (new->ExtCRTC[0x34] & 0x7F) | 0x80; if ((mode->Clock * pTseng->Bytesperpixel) > 80000) new->ExtCRTC[0x37] = (new->ExtCRTC[0x37] & 0x7f) | 0x80; /* * now on to the memory interleave setting (CR32 bit 7) */ if (pTseng->SetW32Interleave) { if (pTseng->W32Interleave) new->ExtCRTC[0x32] |= 0x80; else new->ExtCRTC[0x32] &= 0x7F; } } if (Is_W32p) { /* * CR34 bit 4 controls the PCI Burst option */ if (pTseng->SetPCIBurst) { if (pTseng->PCIBurst) new->ExtCRTC[0x34] |= 0x10; else new->ExtCRTC[0x34] &= 0xEF; } } /* prepare clock-related registers when not Legend. * cannot really SET the clock here yet, since the ET4000Save() * is called LATER, so it would save the wrong state... * ET4000Restore() is used to actually SET vga regs. */ if (STG170x_programmable_clock || Gendac_programmable_clock) { if (mode->PrivFlags == TSENG_MODE_PIXMUX) /* pixmux requires a post-div of 4 on ICS GenDAC clock generator */ min_n2 = 2; else min_n2 = 0; TsengcommonCalcClock(mode->SynthClock, 1, 1, 31, min_n2, 3, 100000, pTseng->max_vco_freq, &(new->pll.f2_M), &(new->pll.f2_N)); new->pll.w_idx = 0; new->pll.r_idx = 0; /* memory clock */ if (Gendac_programmable_clock && pTseng->MClkInfo.Set) { TsengcommonCalcClock(pTseng->MClkInfo.MemClk, 1, 1, 31, 1, 3, 100000, pTseng->MaxClock * 2 + 1, &(new->pll.MClkM), &(new->pll.MClkN)); } } else if (ICD2061a_programmable_clock) { #ifdef TODO /* FIXME: icd2061_dwv not used anywhere ... */ pTseng->icd2061_dwv = AltICD2061CalcClock(mode->SynthClock * 1000); /* Tseng_ICD2061AClockSelect(mode->SynthClock); */ #endif } else if (CH8398_programmable_clock) { #ifdef TODO Chrontel8391CalcClock(mode->SynthClock, &temp1, &temp2, &temp3); new->pll.f2_N = (unsigned char)(temp2); new->pll.f2_M = (unsigned char)(temp1 | (temp3 << 6)); /* ok LSB=f2_N and MSB=f2_M */ /* now set the Clock Select Register(CSR) */ new->pll.ctrl = (new->pll.ctrl | 0x90) & 0xF0; new->pll.timingctrl &= 0x1F; new->pll.r_idx = 0; new->pll.w_idx = 0; #endif } else if (Is_ET6K) { /* setting min_n2 to "1" will ensure a more stable clock ("0" is allowed though) */ TsengcommonCalcClock(mode->SynthClock, 1, 1, 31, 1, 3, 100000, pTseng->max_vco_freq, &(new->pll.f2_M), &(new->pll.f2_N)); /* above 130MB/sec, we enable the "LOW FIFO threshold" */ if (mode->Clock * pTseng->Bytesperpixel > 130000) { new->ExtET6K[0x41] |= 0x10; if (Is_ET6100) new->ExtET6K[0x46] |= 0x04; } else { new->ExtET6K[0x41] &= ~0x10; if (Is_ET6100) new->ExtET6K[0x46] &= ~0x04; } if (pTseng->MClkInfo.Set) { /* according to Tseng Labs, N1 must be <= 4, and N2 should always be 1 for MClk */ TsengcommonCalcClock(pTseng->MClkInfo.MemClk, 1, 1, 4, 1, 1, 100000, pTseng->MaxClock * 2, &(new->pll.MClkM), &(new->pll.MClkN)); } /* * Even when we don't allow setting the MClk value as described * above, we can use the FAST/MED/SLOW DRAM options to set up * the RAS/CAS delays as decided by the value of ExtET6K[0x44]. * This is also a more correct use of the flags, as it describes * how fast the RAM works. [HNH]. */ if (pTseng->FastDram) new->ExtET6K[0x44] = 0x04; /* Fastest speed(?) */ else if (pTseng->MedDram) new->ExtET6K[0x44] = 0x15; /* Medium speed */ else if (pTseng->SlowDram) new->ExtET6K[0x44] = 0x35; /* Slow speed */ else ; /* keep current value */ } /* * Set the clock selection bits. Because of the odd mapping between * Tseng clock select bits and what XFree86 does, "CSx" refers to a * register bit with the same name in the Tseng data books. * * XFree86 uses the following mapping: * * Tseng register bit name XFree86 clock select bit * CS0 0 * CS1 1 * CS2 2 * MCLK/2 3 * CS3 4 * CS4 not used */ if (mode->ClockIndex >= 0) { /* CS0 and CS1 are set by standard VGA code (vgaHW) */ /* CS2 = CRTC 0x34 bit 1 */ new->ExtCRTC[0x34] = (new->ExtCRTC[0x34] & 0xFD) | ((mode->ClockIndex & 0x04) >> 1); /* for programmable clocks: disable MCLK/2 and MCLK/4 independent of hibit */ new->ExtTS[7] = (new->ExtTS[7] & 0xBE); if (!pScrn->progClock) { /* clock select bit 3 = MCLK/2 disable/enable */ new->ExtTS[7] |= (pTseng->save_divide ^ ((mode->ClockIndex & 0x08) << 3)); } /* clock select bit 4 = CS3 , clear CS4 */ new->ExtCRTC[0x31] = ((mode->ClockIndex & 0x10) << 2) | (new->ExtCRTC[0x31] & 0x3F); } /* * linear mode handling */ if (Is_ET6K) { if (pTseng->UseLinMem) { new->ExtET6K[0x13] = pTseng->LinFbAddress >> 24; new->ExtET6K[0x40] |= 0x09; } else { new->ExtET6K[0x40] &= ~0x09; } } else { /* et4000 style linear memory */ if (pTseng->UseLinMem) { new->ExtCRTC[0x36] |= 0x10; if (Is_W32p || Is_ET6K) new->ExtCRTC[0x30] = (pTseng->LinFbAddress >> 22) & 0xFF; else new->ExtCRTC[0x30] = ((pTseng->LinFbAddress >> 22) & 0x1F) ^ 0x1c; /* invert bits 4..2 */ hwp->ModeReg.Graphics[6] &= ~0x0C; new->ExtIMACtrl &= ~0x01; /* disable IMA port (to get >1MB lin mem) */ } else { new->ExtCRTC[0x36] &= ~0x10; if (pTseng->ChipType < TYPE_ET4000W32P) new->ExtCRTC[0x30] = 0x1C; /* default value */ else new->ExtCRTC[0x30] = 0x00; } } /* * 16/24/32 bpp handling. */ if (pScrn->bitsPerPixel >= 8) { tseng_set_ramdac_bpp(pScrn, mode); row_offset *= pTseng->Bytesperpixel; } /* * Horizontal overflow settings: for modes with > 2048 pixels per line */ hwp->ModeReg.CRTC[19] = row_offset; new->ExtCRTC[0x3F] = ((((mode->CrtcHTotal >> 3) - 5) & 0x100) >> 8) | ((((mode->CrtcHDisplay >> 3) - 1) & 0x100) >> 7) | ((((mode->CrtcHBlankStart >> 3) - 1) & 0x100) >> 6) | (((mode->CrtcHSyncStart >> 3) & 0x100) >> 4) | ((row_offset & 0x200) >> 3) | ((row_offset & 0x100) >> 1); /* * Enable memory mapped IO registers when acceleration is needed. */ if (pTseng->UseAccel) { if (Is_ET6K) { if (pTseng->UseLinMem) new->ExtET6K[0x40] |= 0x02; /* MMU can't be used here (causes system hang...) */ else new->ExtET6K[0x40] |= 0x06; /* MMU is needed in banked accelerated mode */ } else { new->ExtCRTC[0x36] |= 0x28; } } vgaHWUnlock(hwp); /* TODO: is this needed (tsengEnterVT does this) */ /* Program the registers */ TsengRestore(pScrn, &hwp->ModeReg, new, VGA_SR_MODE); return TRUE; } static Bool TsengSwitchMode(int scrnIndex, DisplayModePtr mode, int flags) { PDEBUG(" TsengSwitchMode\n"); return TsengModeInit(xf86Screens[scrnIndex], mode); } /* * adjust the current video frame (viewport) to display the mousecursor. */ void TsengAdjustFrame(int scrnIndex, int x, int y, int flags) { ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; TsengPtr pTseng = TsengPTR(pScrn); int iobase = VGAHWPTR(pScrn)->IOBase; int Base; PDEBUG(" TsengAdjustFrame\n"); if (pTseng->ShowCache) { if (y) y += 256; } if (pScrn->bitsPerPixel < 8) Base = (y * pScrn->displayWidth + x + 3) >> 3; else { Base = ((y * pScrn->displayWidth + x + 1) * pTseng->Bytesperpixel) >> 2; /* adjust Base address so it is a non-fractional multiple of pTseng->Bytesperpixel */ Base -= (Base % pTseng->Bytesperpixel); } outw(iobase + 4, (Base & 0x00FF00) | 0x0C); outw(iobase + 4, ((Base & 0x00FF) << 8) | 0x0D); outw(iobase + 4, ((Base & 0x0F0000) >> 8) | 0x33); } ModeStatus TsengValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags) { PDEBUG(" TsengValidMode\n"); #ifdef FIXME is this needed? xf86ValidMode gets HMAX and VMAX variables, so it could deal with this. need to recheck hsize with mode->Htotal*mulFactor/divFactor /* Check for CRTC timing bits overflow. */ if (mode->HTotal > Tseng_HMAX) { return MODE_BAD_HVALUE; } if (mode->VTotal > Tseng_VMAX) { return MODE_BAD_VVALUE; } #endif return MODE_OK; } /* * TsengSave -- * save the current video mode */ static void TsengSave(ScrnInfoPtr pScrn) { unsigned char temp, saveseg1 = 0, saveseg2 = 0; TsengPtr pTseng = TsengPTR(pScrn); vgaRegPtr vgaReg; TsengRegPtr tsengReg; int iobase = VGAHWPTR(pScrn)->IOBase; PDEBUG(" TsengSave\n"); vgaReg = &VGAHWPTR(pScrn)->SavedReg; tsengReg = &pTseng->SavedReg; /* * This function will handle creating the data structure and filling * in the generic VGA portion. */ vgaHWSave(pScrn, vgaReg, VGA_SR_ALL); /* * we need this here , cause we MUST disable the ROM SYNC feature * this bit changed with W32p_rev_c... */ outb(iobase + 4, 0x34); temp = inb(iobase + 5); tsengReg->ExtCRTC[0x34] = temp; if (Is_stdET4K || Is_W32_W32i || Is_W32p_ab) { #ifdef OLD_CODE outb(iobase + 5, temp & 0x1F); #else /* data books say translation ROM is controlled by bits 4 and 5 */ outb(iobase + 5, temp & 0xCF); #endif } saveseg1 = inb(0x3CD); outb(0x3CD, 0x00); /* segment select 1 */ if (!Is_stdET4K) { saveseg2 = inb(0x3CB); outb(0x3CB, 0x00); /* segment select 2 */ } tsengReg->ExtSegSel[0] = saveseg1; tsengReg->ExtSegSel[1] = saveseg2; outb(iobase + 4, 0x33); tsengReg->ExtCRTC[0x33] = inb(iobase + 5); outb(iobase + 4, 0x35); tsengReg->ExtCRTC[0x35] = inb(iobase + 5); if (Is_W32_any) { outb(iobase + 4, 0x36); tsengReg->ExtCRTC[0x36] = inb(iobase + 5); outb(iobase + 4, 0x37); tsengReg->ExtCRTC[0x37] = inb(iobase + 5); outb(0x217a, 0xF7); tsengReg->ExtIMACtrl = inb(0x217b); } if (!Is_ET6K) { outb(iobase + 4, 0x32); tsengReg->ExtCRTC[0x32] = inb(iobase + 5); } outb(0x3C4, 6); tsengReg->ExtTS[6] = inb(0x3C5); outb(0x3C4, 7); tsengReg->ExtTS[7] = inb(0x3C5); tsengReg->ExtTS[7] |= 0x14; temp = inb(iobase + 0x0A); /* reset flip-flop */ outb(0x3C0, 0x36); tsengReg->ExtATC = inb(0x3C1); outb(0x3C0, tsengReg->ExtATC); if (DAC_is_GenDAC) { /* Save GenDAC Command and PLL registers */ outb(iobase + 4, 0x31); temp = inb(iobase + 5); outb(iobase + 5, temp | 0x40); tsengReg->pll.cmd_reg = inb(0x3c6); /* Enhanced command register */ tsengReg->pll.w_idx = inb(0x3c8); /* PLL write index */ tsengReg->pll.r_idx = inb(0x3c7); /* PLL read index */ if (Gendac_programmable_clock) { outb(0x3c7, 2); /* index to f2 reg */ tsengReg->pll.f2_M = inb(0x3c9); /* f2 PLL M divider */ tsengReg->pll.f2_N = inb(0x3c9); /* f2 PLL N1/N2 divider */ outb(0x3c7, 10); /* index to Mclk reg */ #ifdef TODO tsengReg->MClkInfo.MClkM = inb(0x3c9); /* MClk PLL M divider */ tsengReg->MClkInfo.MClkN = inb(0x3c9); /* MClk PLL N1/N2 divider */ #endif } outb(0x3c7, 0x0e); /* index to PLL control */ tsengReg->pll.ctrl = inb(0x3c9); /* PLL control */ outb(iobase + 4, 0x31); outb(iobase + 5, temp & ~0x40); } if ((pTseng->DacInfo.DacType == STG1702_DAC) || (pTseng->DacInfo.DacType == STG1703_DAC) || (pTseng->DacInfo.DacType == STG1700_DAC)) { #ifdef TODO /* Save STG 1703 GenDAC Command and PLL registers * unfortunately we reuse the gendac data structure, so the * field names are not really good. */ tseng_dactopel(); tsengReg->pll.cmd_reg = tseng_getdaccomm(); /* Enhanced command register */ if (STG170x_programmable_clock) { tsengReg->pll.f2_M = STG1703getIndex(0x24); /* f2 PLL M divider */ tsengReg->pll.f2_N = inb(0x3c6); /* f2 PLL N1/N2 divider */ } tsengReg->pll.ctrl = STG1703getIndex(0x03); /* pixel mode select control */ tsengReg->pll.timingctrl = STG1703getIndex(0x05); /* pll timing control */ #endif } if (DAC_IS_CHRONTEL) { tseng_dactopel(); tsengReg->pll.cmd_reg = tseng_getdaccomm(); if (CH8398_programmable_clock) { inb(0x3c8); inb(0x3c6); inb(0x3c6); inb(0x3c6); inb(0x3c6); inb(0x3c6); tsengReg->pll.timingctrl = inb(0x3c6); /* Save PLL */ outb(iobase + 4, 0x31); temp = inb(iobase + 5); outb(iobase + 5, temp | (1 << 6)); /* set RS2 through CS3 */ /* We are in ClockRAM mode 0x3c7 = CRA, 0x3c8 = CWA, 0x3c9 = CDR */ tsengReg->pll.r_idx = inb(0x3c7); tsengReg->pll.w_idx = inb(0x3c8); outb(0x3c7, 10); tsengReg->pll.f2_N = inb(0x3c9); tsengReg->pll.f2_M = inb(0x3c9); outb(0x3c7, tsengReg->pll.r_idx); inb(0x3c8); /* loop to Clock Select Register */ inb(0x3c8); inb(0x3c8); inb(0x3c8); tsengReg->pll.ctrl = inb(0x3c8); outb(iobase + 4, 0x31); outb(iobase + 5, temp); } } if (ET6000_programmable_clock) { /* Save ET6000 CLKDAC PLL registers */ temp = inb(pTseng->IOAddress + 0x67); /* remember old CLKDAC index register pointer */ outb(pTseng->IOAddress + 0x67, 2); tsengReg->pll.f2_M = inb(pTseng->IOAddress + 0x69); tsengReg->pll.f2_N = inb(pTseng->IOAddress + 0x69); /* save MClk values */ outb(pTseng->IOAddress + 0x67, 10); tsengReg->pll.MClkM = inb(pTseng->IOAddress + 0x69); tsengReg->pll.MClkN = inb(pTseng->IOAddress + 0x69); /* restore old index register */ outb(pTseng->IOAddress + 0x67, temp); } if (DAC_IS_ATT49x) tsengReg->ATTdac_cmd = tseng_getdaccomm(); if (Is_ET6K) { tsengReg->ExtET6K[0x13] = inb(pTseng->IOAddress + 0x13); tsengReg->ExtET6K[0x40] = inb(pTseng->IOAddress + 0x40); tsengReg->ExtET6K[0x58] = inb(pTseng->IOAddress + 0x58); tsengReg->ExtET6K[0x41] = inb(pTseng->IOAddress + 0x41); tsengReg->ExtET6K[0x44] = inb(pTseng->IOAddress + 0x44); tsengReg->ExtET6K[0x46] = inb(pTseng->IOAddress + 0x46); } outb(iobase + 4, 0x30); tsengReg->ExtCRTC[0x30] = inb(iobase + 5); outb(iobase + 4, 0x31); tsengReg->ExtCRTC[0x31] = inb(iobase + 5); outb(iobase + 4, 0x3F); tsengReg->ExtCRTC[0x3F] = inb(iobase + 5); } /* * TsengRestore -- * restore a video mode */ static void TsengRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, TsengRegPtr tsengReg, int flags) { vgaHWPtr hwp; TsengPtr pTseng; unsigned char tmp; int iobase = VGAHWPTR(pScrn)->IOBase; PDEBUG(" TsengRestore\n"); hwp = VGAHWPTR(pScrn); pTseng = TsengPTR(pScrn); TsengProtect(pScrn, TRUE); outb(0x3CD, 0x00); /* segment select bits 0..3 */ if (!Is_stdET4K) outb(0x3CB, 0x00); /* segment select bits 4,5 */ if (DAC_is_GenDAC) { /* Restore GenDAC Command and PLL registers */ outb(iobase + 4, 0x31); tmp = inb(iobase + 5); outb(iobase + 5, tmp | 0x40); outb(0x3c6, tsengReg->pll.cmd_reg); /* Enhanced command register */ if (Gendac_programmable_clock) { outb(0x3c8, 2); /* index to f2 reg */ outb(0x3c9, tsengReg->pll.f2_M); /* f2 PLL M divider */ outb(0x3c9, tsengReg->pll.f2_N); /* f2 PLL N1/N2 divider */ #ifdef TODO if (pTseng->MClkInfo.Set) { outb(0x3c7, 10); /* index to Mclk reg */ outb(0x3c9, tsengReg->MClkM); /* MClk PLL M divider */ outb(0x3c9, tsengReg->MClkN); /* MClk PLL N1/N2 divider */ } #endif } outb(0x3c8, 0x0e); /* index to PLL control */ outb(0x3c9, tsengReg->pll.ctrl); /* PLL control */ outb(0x3c8, tsengReg->pll.w_idx); /* PLL write index */ outb(0x3c7, tsengReg->pll.r_idx); /* PLL read index */ outb(iobase + 4, 0x31); outb(iobase + 5, tmp & ~0x40); } if (DAC_is_STG170x) { #ifdef TODO /* Restore STG 170x GenDAC Command and PLL registers * we share one data structure with the gendac code, so the names * are not too good. */ if (STG170x_programmable_clock) { STG1703setIndex(0x24, tsengReg->pll.f2_M); outb(0x3c6, tsengReg->pll.f2_N); /* use autoincrement */ } STG1703setIndex(0x03, tsengReg->pll.ctrl); /* primary pixel mode */ outb(0x3c6, tsengReg->pll.ctrl); /* secondary pixel mode */ outb(0x3c6, tsengReg->pll.timingctrl); /* pipeline timing control */ usleep(500); /* 500 usec PLL settling time required */ STG1703magic(0); tseng_dactopel(); tseng_setdaccomm(tsengReg->pll.cmd_reg); /* write enh command reg */ #endif } if (DAC_IS_CHRONTEL) { tseng_dactopel(); tseng_setdaccomm(tsengReg->pll.cmd_reg); inb(0x3c8); inb(0x3c6); inb(0x3c6); inb(0x3c6); inb(0x3c6); inb(0x3c6); outb(0x3c6, tsengReg->pll.timingctrl); if (CH8398_programmable_clock) { outb(iobase + 4, 0x31); tmp = inb(iobase + 5); outb(iobase + 5, tmp | (1 << 6)); /* Set RS2 through CS3 */ /* We are in ClockRAM mode 0x3c7 = CRA, 0x3c8 = CWA, 0x3c9 = CDR */ outb(0x3c7, tsengReg->pll.r_idx); outb(0x3c8, 10); outb(0x3c9, tsengReg->pll.f2_N); outb(0x3c9, tsengReg->pll.f2_M); outb(0x3c8, tsengReg->pll.w_idx); usleep(500); inb(0x3c7); /* reset sequence */ inb(0x3c8); /* loop to Clock Select Register */ inb(0x3c8); inb(0x3c8); inb(0x3c8); outb(0x3c8, tsengReg->pll.ctrl); outb(iobase + 4, 0x31); outb(iobase + 5, (tmp & 0x3F)); } } if (ET6000_programmable_clock) { /* Restore ET6000 CLKDAC PLL registers */ tmp = inb(pTseng->IOAddress + 0x67); /* remember old CLKDAC index register pointer */ outb(pTseng->IOAddress + 0x67, 2); outb(pTseng->IOAddress + 0x69, tsengReg->pll.f2_M); outb(pTseng->IOAddress + 0x69, tsengReg->pll.f2_N); /* set MClk values if needed, but don't touch them if not needed */ if (pTseng->MClkInfo.Set) { /* * Since setting the MClk to highly illegal value results in a * total system crash, we'd better play it safe here. * N1 must be <= 4, and N2 should always be 1 */ if ((tsengReg->pll.MClkN & 0xf8) != 0x20) { xf86Msg(X_ERROR, "Internal Error in MClk registers: MClkM=0x%x, MClkN=0x%x\n", tsengReg->pll.MClkM, tsengReg->pll.MClkN); } else { outb(pTseng->IOAddress + 0x67, 10); outb(pTseng->IOAddress + 0x69, tsengReg->pll.MClkM); outb(pTseng->IOAddress + 0x69, tsengReg->pll.MClkN); } } /* restore old index register */ outb(pTseng->IOAddress + 0x67, tmp); } if (DAC_IS_ATT49x) tseng_setdaccomm(tsengReg->ATTdac_cmd); if (Is_ET6K) { outb(pTseng->IOAddress + 0x13, tsengReg->ExtET6K[0x13]); outb(pTseng->IOAddress + 0x40, tsengReg->ExtET6K[0x40]); outb(pTseng->IOAddress + 0x58, tsengReg->ExtET6K[0x58]); outb(pTseng->IOAddress + 0x41, tsengReg->ExtET6K[0x41]); outb(pTseng->IOAddress + 0x44, tsengReg->ExtET6K[0x44]); outb(pTseng->IOAddress + 0x46, tsengReg->ExtET6K[0x46]); } outw(iobase + 4, (tsengReg->ExtCRTC[0x3F] << 8) | 0x3F); outw(iobase + 4, (tsengReg->ExtCRTC[0x30] << 8) | 0x30); outw(iobase + 4, (tsengReg->ExtCRTC[0x31] << 8) | 0x31); vgaHWRestore(pScrn, vgaReg, flags); /* TODO: does this belong HERE, in the middle? */ outw(0x3C4, (tsengReg->ExtTS[6] << 8) | 0x06); outw(0x3C4, (tsengReg->ExtTS[7] << 8) | 0x07); tmp = inb(iobase + 0x0A); /* reset flip-flop */ outb(0x3C0, 0x36); outb(0x3C0, tsengReg->ExtATC); outw(iobase + 4, (tsengReg->ExtCRTC[0x33] << 8) | 0x33); outw(iobase + 4, (tsengReg->ExtCRTC[0x34] << 8) | 0x34); outw(iobase + 4, (tsengReg->ExtCRTC[0x35] << 8) | 0x35); if (Is_W32_any) { outw(iobase + 4, (tsengReg->ExtCRTC[0x37] << 8) | 0x37); outw(0x217a, (tsengReg->ExtIMACtrl << 8) | 0xF7); } if (!Is_ET6K) { outw(iobase + 4, (tsengReg->ExtCRTC[0x32] << 8) | 0x32); } outb(0x3CD, tsengReg->ExtSegSel[0]); if (pTseng->ChipType > TYPE_ET4000) outb(0x3CB, tsengReg->ExtSegSel[1]); #ifdef TODO /* * This might be required for the Legend clock setting method, but * should not be used for the "normal" case because the high order * bits are not set in ClockIndex when returning to text mode. */ if (pTseng->Legend) { if (tsengReg->ClockIndex >= 0) { vgaProtect(TRUE); (ClockSelect) (tsengReg->ClockIndex); } #endif TsengProtect(pScrn, FALSE); /* * We must change CRTC 0x36 only OUTSIDE the TsengProtect(pScrn, * TRUE)/TsengProtect(pScrn, FALSE) pair, because the sequencer reset * also resets the linear mode bits in CRTC 0x36. */ if (Is_W32_any) { outw(iobase + 4, (tsengReg->ExtCRTC[0x36] << 8) | 0x36); } } /* replacement of vgaHWBlankScreen(pScrn, unblank) without seq reset */ void TsengBlankScreen(ScrnInfoPtr pScrn, Bool unblank) { unsigned char scrn; PDEBUG(" TsengBlankScreen\n"); outb(0x3C4,1); scrn = inb(0x3C5); if(unblank) { scrn &= 0xDF; /* enable screen */ }else { scrn |= 0x20; /* blank screen */ } /* vgaHWSeqReset(hwp, TRUE);*/ outw(0x3C4, (scrn << 8) | 0x01); /* change mode */ /* vgaHWSeqReset(hwp, FALSE);*/ } void TsengProtect(ScrnInfoPtr pScrn, Bool on) { PDEBUG(" TsengProtect\n"); vgaHWProtect(pScrn, on); } /* * The rest below is stuff from the old driver, which still needs to be * checked and integrated in the ND */ #ifdef OLD_DRIVER #include "tseng_cursor.h" extern vgaHWCursorRec vgaHWCursor; /* * ET4000Probe -- * check whether a Et4000 based board is installed */ static Bool ET4000Probe() { int numClocks; Bool autodetect = TRUE; ... if (pScrn->bitsPerPixel >= 8) { ... /* * Acceleration is only supported on W32 or newer chips. * * Also, some bus configurations only allow for a 1MB linear memory * aperture instead of the default 4M aperture used on all Tseng devices. * If acceleration is also enabled, you only get 512k + (with some aperture * tweaking) 2*128k for a total of max 768 kb of memory. This just isn't * worth having a lot of conditionals in the accelerator code (the * memory-mapped registers move to the top of the 1M aperture), so we * simply don't allow acceleration and linear mode combined on these cards. * */ if ((pTseng->ChipType < TYPE_ET4000W32) || (pTseng->Linmem_1meg && pTseng->UseLinMem)) { tseng_use_ACL = FALSE; } else { /* enable acceleration-related options */ OFLG_SET(OPTION_NOACCEL, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_PCI_RETRY, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_SHOWCACHE, &TSENG.ChipOptionFlags); tseng_use_ACL = !OFLG_ISSET(OPTION_NOACCEL, &vga256InfoRec.options); } ... /* Hardware Cursor support */ #ifdef W32_HW_CURSOR_FIXED if (pTseng->ChipType >= TYPE_ET4000W32P) #else if (Is_ET6K) #endif { /* Set HW Cursor option valid */ OFLG_SET(OPTION_HW_CURSOR, &TSENG.ChipOptionFlags); } } /* if (pScrn->bitsPerPixel >= 8) */ else { OFLG_CLR(OPTION_HW_CURSOR, &vga256InfoRec.options); pTseng->UseLinMem = FALSE; tseng_use_ACL = FALSE; } if (!Is_ET6K) { /* Initialize option flags allowed for this driver */ OFLG_SET(OPTION_LEGEND, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_HIBIT_HIGH, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_HIBIT_LOW, &TSENG.ChipOptionFlags); if (pScrn->bitsPerPixel >= 8) { OFLG_SET(OPTION_PCI_BURST_ON, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_PCI_BURST_OFF, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_W32_INTERLEAVE_ON, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_W32_INTERLEAVE_OFF, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_SLOW_DRAM, &TSENG.ChipOptionFlags); OFLG_SET(OPTION_FAST_DRAM, &TSENG.ChipOptionFlags); } /* * because of some problems with W32 cards, SLOW_DRAM is _always_ enabled * for those cards */ if (pTseng->ChipType <= TYPE_ET4000W32) { ErrorF("%s %s: option \"slow_dram\" is enabled by default on this card.\n", XCONFIG_PROBED, vga256InfoRec.name); OFLG_SET(OPTION_SLOW_DRAM, &vga256InfoRec.options); } ... vga256InfoRec.bankedMono = TRUE; ... return (TRUE); } #endif