tseng_driver.c revision 962c3257
1/*
2 *
3 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Thomas Roell not be used in
10 * advertising or publicity pertaining to distribution of the software without
11 * specific, written prior permission.  Thomas Roell makes no representations
12 * about the suitability of this software for any purpose.  It is provided
13 * "as is" without express or implied warranty.
14 *
15 * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21 * PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author:  Thomas Roell, roell@informatik.tu-muenchen.de
24 *          ET6000 and ET4000W32 16/24/32 bpp and acceleration support by Koen Gadeyne
25 *
26 * Large parts rewritten for XFree86 4.0 by Koen Gadeyne.
27 */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33/*** Generic includes ***/
34
35#include "tseng.h"		       /* this includes most of the generic ones as well */
36
37/* All drivers initialising the SW cursor need this */
38#include "mipointer.h"
39
40/* All drivers implementing backing store need this */
41#include "mibstore.h"
42
43#include "fb.h"
44
45#include "xf86RAC.h"
46#include "xf86Resources.h"
47#include "xf86int10.h"
48
49#include "xf86xv.h"
50#include <X11/extensions/Xv.h>
51
52/*
53 * Forward definitions for the functions that make up the driver.
54 */
55
56/* Mandatory functions */
57static const OptionInfoRec * TsengAvailableOptions(int chipid, int busid);
58static void TsengIdentify(int flags);
59static Bool TsengProbe(DriverPtr drv, int flags);
60static Bool TsengPreInit(ScrnInfoPtr pScrn, int flags);
61static Bool TsengScreenInit(int Index, ScreenPtr pScreen, int argc,
62    char **argv);
63static Bool TsengEnterVT(int scrnIndex, int flags);
64static void TsengLeaveVT(int scrnIndex, int flags);
65static Bool TsengCloseScreen(int scrnIndex, ScreenPtr pScreen);
66static Bool TsengSaveScreen(ScreenPtr pScreen, int mode);
67
68/* Required if the driver supports mode switching */
69static Bool TsengSwitchMode(int scrnIndex, DisplayModePtr mode, int flags);
70
71/* Optional functions */
72static void TsengFreeScreen(int scrnIndex, int flags);
73
74/* If driver-specific config file entries are needed, this must be defined */
75/*static Bool   TsengParseConfig(ParseInfoPtr raw); */
76
77/* Internally used functions (some are defined in tseng.h) */
78static Bool TsengMapMem(ScrnInfoPtr pScrn);
79static Bool TsengUnmapMem(ScrnInfoPtr pScrn);
80static void TsengUnlock(ScrnInfoPtr pScrn);
81static void TsengLock(ScrnInfoPtr pScrn);
82
83/*
84 * This is intentionally screen-independent.  It indicates the binding
85 * choice made in the first PreInit.
86 */
87static int pix24bpp = 0;
88
89#define TSENG_NAME "TSENG"
90#define TSENG_DRIVER_NAME "tseng"
91#define TSENG_MAJOR_VERSION 1
92#define TSENG_MINOR_VERSION 1
93#define TSENG_PATCHLEVEL 0
94#define TSENG_VERSION (TSENG_MAJOR_VERSION << 24) | (TSENG_MINOR_VERSION << 16) | TSENG_PATCHLEVEL
95
96/* CRTC timing limits */
97#define Tseng_HMAX (4096-8)
98#define Tseng_VMAX (2048-1)
99
100/*
101 * This contains the functions needed by the server after loading the
102 * driver module.  It must be supplied, and gets added the driver list by
103 * the Module Setup funtion in the dynamic case.  In the static case a
104 * reference to this is compiled in, and this requires that the name of
105 * this DriverRec be an upper-case version of the driver name.
106 */
107
108_X_EXPORT DriverRec TSENG =
109{
110    TSENG_VERSION,
111    TSENG_DRIVER_NAME,
112    TsengIdentify,
113    TsengProbe,
114    TsengAvailableOptions,
115    NULL,
116    0
117};
118
119/* sub-revisions are now dealt with in the ChipRev variable */
120static SymTabRec TsengChipsets[] =
121{
122    {ET4000, "ET4000W32p"},
123    {ET6000, "ET6000"},
124    {-1, NULL}
125};
126
127/* Convert PCI ID to chipset name */
128static PciChipsets TsengPciChipsets[] =
129{
130    {ET4000, PCI_CHIP_ET4000_W32P_A, RES_SHARED_VGA},
131    {ET4000, PCI_CHIP_ET4000_W32P_B, RES_SHARED_VGA},
132    {ET4000, PCI_CHIP_ET4000_W32P_C, RES_SHARED_VGA},
133    {ET4000, PCI_CHIP_ET4000_W32P_D, RES_SHARED_VGA},
134    {ET6000, PCI_CHIP_ET6000,        RES_SHARED_VGA},
135    {-1,     -1,                     RES_UNDEFINED}
136};
137
138typedef enum {
139    OPTION_HIBIT_HIGH,
140    OPTION_HIBIT_LOW,
141    OPTION_SW_CURSOR,
142    OPTION_HW_CURSOR,
143    OPTION_PCI_BURST,
144    OPTION_SLOW_DRAM,
145    OPTION_MED_DRAM,
146    OPTION_FAST_DRAM,
147    OPTION_W32_INTERLEAVE,
148    OPTION_NOACCEL,
149    OPTION_SHOWCACHE,
150    OPTION_PCI_RETRY
151} TsengOpts;
152
153static const OptionInfoRec TsengOptions[] =
154{
155    {OPTION_HIBIT_HIGH, "hibit_high", OPTV_BOOLEAN,
156	{0}, FALSE},
157    {OPTION_HIBIT_LOW, "hibit_low", OPTV_BOOLEAN,
158	{0}, FALSE},
159    {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN,
160	{0}, FALSE},
161    {OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN,
162	{0}, FALSE},
163    {OPTION_PCI_BURST, "pci_burst", OPTV_BOOLEAN,
164	{0}, FALSE},
165    {OPTION_SLOW_DRAM, "slow_dram", OPTV_BOOLEAN,
166	{0}, FALSE},
167    {OPTION_MED_DRAM, "med_dram", OPTV_BOOLEAN,
168	{0}, FALSE},
169    {OPTION_FAST_DRAM, "fast_dram", OPTV_BOOLEAN,
170	{0}, FALSE},
171    {OPTION_W32_INTERLEAVE, "w32_interleave", OPTV_BOOLEAN,
172	{0}, FALSE},
173    {OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN,
174	{0}, FALSE},
175    {OPTION_SHOWCACHE, "ShowCache", OPTV_BOOLEAN,
176	{0}, FALSE},
177    {OPTION_PCI_RETRY, "PciRetry", OPTV_BOOLEAN,
178	{0}, FALSE},
179    {-1, NULL, OPTV_NONE,
180	{0}, FALSE}
181};
182
183static const char *int10Symbols[] = {
184    "xf86FreeInt10",
185    "xf86InitInt10",
186    NULL
187};
188
189static const char *vgaHWSymbols[] = {
190  "vgaHWFreeHWRec",
191  "vgaHWGetHWRec",
192  "vgaHWGetIOBase",
193  "vgaHWGetIndex",
194  "vgaHWHandleColormaps",
195  "vgaHWInit",
196  "vgaHWLock",
197  "vgaHWMapMem",
198  "vgaHWProtect",
199  "vgaHWRestore",
200  "vgaHWSave",
201  "vgaHWSaveScreen",
202  "vgaHWUnlock",
203  "vgaHWUnmapMem",
204  NULL
205};
206
207#ifdef XFree86LOADER
208static const char* miscfbSymbols[] = {
209  "xf1bppScreenInit",
210  "xf4bppScreenInit",
211  NULL
212};
213#endif
214
215static const char* fbSymbols[] = {
216  "fbPictureInit",
217  "fbScreenInit",
218  NULL
219};
220
221static const char *ramdacSymbols[] = {
222    "xf86CreateCursorInfoRec",
223    "xf86DestroyCursorInfoRec",
224    "xf86InitCursor",
225    NULL
226};
227
228static const char *xaaSymbols[] = {
229    "XAACreateInfoRec",
230    "XAADestroyInfoRec",
231    "XAAInit",
232    NULL
233};
234
235#ifdef XFree86LOADER
236
237static MODULESETUPPROTO(tsengSetup);
238
239static XF86ModuleVersionInfo tsengVersRec =
240{
241    "tseng",
242    MODULEVENDORSTRING,
243    MODINFOSTRING1,
244    MODINFOSTRING2,
245    XORG_VERSION_CURRENT,
246    TSENG_MAJOR_VERSION, TSENG_MINOR_VERSION, TSENG_PATCHLEVEL,
247    ABI_CLASS_VIDEODRV,		       /* This is a video driver */
248    ABI_VIDEODRV_VERSION,
249    MOD_CLASS_VIDEODRV,
250    {0, 0, 0, 0}
251};
252
253/*
254 * This is the module init data for XFree86 modules.
255 *
256 * Its name has to be the driver name followed by ModuleData.
257 */
258_X_EXPORT XF86ModuleData tsengModuleData = { &tsengVersRec, tsengSetup, NULL };
259
260static pointer
261tsengSetup(pointer module, pointer opts, int *errmaj, int *errmin)
262{
263    static Bool setupDone = FALSE;
264
265    if (!setupDone) {
266	setupDone = TRUE;
267	xf86AddDriver(&TSENG, module, 0);
268
269	/*
270	 * Modules that this driver always requires can be loaded here
271	 * by calling LoadSubModule().
272	 */
273	/*
274	 * Tell the loader about symbols from other modules that this module
275	 * might refer to.
276	 */
277	LoaderRefSymLists(vgaHWSymbols, miscfbSymbols, fbSymbols, xaaSymbols,
278			  int10Symbols, ramdacSymbols,  NULL);
279
280	/*
281	 * The return value must be non-NULL on success even though there
282	 * is no TearDownProc.
283	 */
284	return (pointer) 1;
285    } else {
286	if (errmaj)
287	    *errmaj = LDR_ONCEONLY;
288	return NULL;
289    }
290}
291
292#endif /* XFree86LOADER */
293
294static Bool
295TsengGetRec(ScrnInfoPtr pScrn)
296{
297    TsengPtr pTseng;
298
299    PDEBUG("	TsengGetRec\n");
300
301    /*
302     * Allocate an TsengRec, and hook it into pScrn->driverPrivate.
303     * pScrn->driverPrivate is initialised to NULL, so we can check if
304     * the allocation has already been done.
305     */
306    if (pScrn->driverPrivate != NULL)
307	return TRUE;
308
309    pScrn->driverPrivate = xnfcalloc(sizeof(TsengRec), 1);
310
311
312    /* Initialise it here when needed (or possible) */
313    pTseng = TsengPTR(pScrn);
314
315    pTseng->SavedReg.RAMDAC = NULL;
316
317    return TRUE;
318}
319
320static void
321TsengFreeRec(ScrnInfoPtr pScrn)
322{
323    TsengPtr pTseng;
324
325    PDEBUG("	TsengFreeRec\n");
326
327    if (pScrn->driverPrivate == NULL)
328	return;
329
330    pTseng = TsengPTR(pScrn);
331
332    if (pTseng->SavedReg.RAMDAC)
333        xfree(pTseng->SavedReg.RAMDAC);
334
335    xfree(pScrn->driverPrivate);
336    pScrn->driverPrivate = NULL;
337}
338
339static const OptionInfoRec *
340TsengAvailableOptions(int chipid, int busid)
341{
342    return TsengOptions;
343}
344
345static void
346TsengIdentify(int flags)
347{
348    xf86Msg(X_INFO, TSENG_NAME ": driver for TsengLabs ET4000W32p, ET6000 and"
349            " ET6100 chips.\n");
350}
351
352/* unlock ET4000 using KEY register */
353static void
354TsengUnlock(ScrnInfoPtr pScrn)
355{
356    vgaHWPtr hwp = VGAHWPTR(pScrn);
357    CARD8 tmp;
358
359    PDEBUG("	TsengUnlock\n");
360
361    vgaHWHerculesSecondPage(hwp, TRUE);
362    vgaHWWriteModeControl(hwp, 0xA0);
363
364    tmp = hwp->readCrtc(hwp, 0x11);
365    hwp->writeCrtc(hwp, 0x11, tmp & 0x7F);
366}
367
368/* lock ET4000 using KEY register. FIXME: should restore old lock status instead */
369static void
370TsengLock(ScrnInfoPtr pScrn)
371{
372    vgaHWPtr hwp = VGAHWPTR(pScrn);
373    CARD8 tmp;
374
375    PDEBUG("	TsengLock\n");
376
377    tmp = hwp->readCrtc(hwp, 0x11);
378    hwp->writeCrtc(hwp, 0x11, tmp | 0x80);
379
380    vgaHWWriteModeControl(hwp, 0x00);
381    vgaHWWriteModeControl(hwp, 0x29);
382    vgaHWHerculesSecondPage(hwp, FALSE);
383}
384
385static Bool
386TsengProbe(DriverPtr drv, int flags)
387{
388    int i;
389    GDevPtr *devSections;
390    int numDevSections;
391    int numUsed;
392    int *usedChips = NULL;
393    Bool foundScreen = FALSE;
394
395
396    PDEBUG("	TsengProbe\n");
397    /*
398     * The aim here is to find all cards that this driver can handle,
399     * and for the ones not already claimed by another driver, claim the
400     * slot, and allocate a ScrnInfoRec.
401     *
402     * This should be a minimal probe, and it should under no circumstances
403     * change the state of the hardware.  Because a device is found, don't
404     * assume that it will be used.  Don't do any initialisations other than
405     * the required ScrnInfoRec initialisations.  Don't allocate any new
406     * data structures.
407     */
408
409    /*
410     * Find the config file Device sections that match this
411     * driver, and return if there are none.
412     */
413    if ((numDevSections = xf86MatchDevice(TSENG_DRIVER_NAME,
414		&devSections)) <= 0) {
415	return FALSE;
416    }
417
418#ifndef XSERVER_LIBPCIACCESS
419    /* PCI only driver now. */
420    if (!xf86GetPciVideoInfo())
421        return FALSE;
422#endif
423
424    /* XXX maybe this can go some time soon */
425    /*
426     * for the Tseng server, there can only be one matching
427     * device section. So issue a warning if more than one show up.
428     * Multiple Tseng cards in the same machine are not possible.
429     */
430    numUsed = xf86MatchPciInstances(TSENG_NAME, PCI_VENDOR_TSENG,
431                                    TsengChipsets, TsengPciChipsets,
432                                    devSections,numDevSections, drv,
433                                    &usedChips);
434    if (numUsed > 0) {
435        if (flags & PROBE_DETECT)
436            foundScreen = TRUE;
437        else for (i = 0; i < numUsed; i++) {
438            /* Allocate a ScrnInfoRec  */
439            ScrnInfoPtr pScrn = NULL;
440            if ((pScrn = xf86ConfigPciEntity(pScrn,0,usedChips[i],
441                                             TsengPciChipsets,NULL,
442                                             NULL,NULL,NULL,NULL))) {
443                pScrn->driverVersion = TSENG_VERSION;
444                pScrn->driverName = TSENG_DRIVER_NAME;
445                pScrn->name = TSENG_NAME;
446                pScrn->Probe = TsengProbe;
447                pScrn->PreInit = TsengPreInit;
448                pScrn->ScreenInit = TsengScreenInit;
449                pScrn->SwitchMode = TsengSwitchMode;
450                pScrn->AdjustFrame = TsengAdjustFrame;
451                pScrn->EnterVT = TsengEnterVT;
452                pScrn->LeaveVT = TsengLeaveVT;
453                pScrn->FreeScreen = TsengFreeScreen;
454                pScrn->ValidMode = TsengValidMode;
455
456                foundScreen = TRUE;
457            }
458        }
459        xfree(usedChips);
460    }
461
462    xfree(devSections);
463    return foundScreen;
464}
465
466/* The PCI part of TsengPreInit() */
467static Bool
468TsengPreInitPCI(ScrnInfoPtr pScrn)
469{
470    TsengPtr pTseng = TsengPTR(pScrn);
471
472    PDEBUG("	TsengPreInitPCI\n");
473
474    /* This is PCI, we should be able to trust it */
475
476    /* Set up ChipType, ChipRev and pScrn->chipset.
477     * This last one is usually not done manually, but
478     * it's for informative use only anyway. */
479    switch (PCI_DEV_DEVICE_ID(pTseng->PciInfo)) {
480    case PCI_CHIP_ET4000_W32P_A:
481	pTseng->ChipType = ET4000;
482	pTseng->ChipRev = REV_A;
483        pScrn->chipset = "ET4000/W32P (rev A)";
484	break;
485    case PCI_CHIP_ET4000_W32P_B:
486	pTseng->ChipType = ET4000;
487	pTseng->ChipRev = REV_B;
488	pScrn->chipset = "ET4000/W32P (rev B)";
489        break;
490    case PCI_CHIP_ET4000_W32P_C:
491	pTseng->ChipType = ET4000;
492	pTseng->ChipRev = REV_C;
493        pScrn->chipset = "ET4000/W32P (rev C)";
494	break;
495    case PCI_CHIP_ET4000_W32P_D:
496	pTseng->ChipType = ET4000;
497	pTseng->ChipRev = REV_D;
498        pScrn->chipset = "ET4000/W32P (rev D)";
499	break;
500    case PCI_CHIP_ET6000:
501	pTseng->ChipType = ET6000;
502
503        if (PCI_DEV_REVISION(pTseng->PciInfo) < 0x70) {
504            pScrn->chipset = "ET6000";
505            pTseng->ChipRev = REV_ET6000;
506        } else {
507            pScrn->chipset = "ET6100";
508            pTseng->ChipRev = REV_ET6100;
509        }
510	break;
511    default:
512	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unknown Tseng PCI ID: %X\n",
513                   PCI_DEV_DEVICE_ID(pTseng->PciInfo));
514	return FALSE;
515    }
516
517    xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Chipset: \"%s\"\n", pScrn->chipset);
518
519#ifndef XSERVER_LIBPCIACCESS
520    pTseng->PciTag = pciTag(pTseng->PciInfo->bus, pTseng->PciInfo->device,
521	pTseng->PciInfo->func);
522#endif
523
524    /* only the ET6000 implements a PCI IO address */
525    if (pTseng->ChipType == ET6000) {
526        if (!PCI_REGION_BASE(pTseng->PciInfo, 1, REGION_IO)) {
527            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
528                       "No valid PCI I/O address in PCI config space\n");
529            return FALSE;
530        }
531
532        pTseng->ET6000IOAddress = PCI_REGION_BASE(pTseng->PciInfo, 1, REGION_IO);
533
534        xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "ET6000 PCI I/O registers at 0x%lX\n",
535                   (unsigned long)pTseng->ET6000IOAddress);
536    }
537
538    return TRUE;
539}
540
541/*
542 * The 8*32kb ET6000 MDRAM granularity causes the more general probe to
543 * detect too much memory in some configurations, because that code has a
544 * 8-bank (=256k) granularity. E.g. it fails to recognize 2.25 MB of memory
545 * (detects 2.5 instead). This function goes to check if the RAM is actually
546 * there. MDRAM comes in multiples of 4 banks (16, 24, 32, 36, 40, 64, 72,
547 * 80, ... 32kb-banks), so checking each 64k block should be enough granularity.
548 *
549 * No more than the amount of refreshed RAM is checked. Non-refreshed RAM
550 * won't work anyway.
551 *
552 * The same code could be used on other Tseng chips, or even on ANY
553 * VGA board, but probably only in case of trouble.
554 *
555 * FIXME: this should be done using linear memory
556 */
557#define VIDMEM ((volatile CARD32*)check_vgabase)
558#define SEGSIZE (64)		       /* kb */
559
560#define ET6K_SETSEG(seg) \
561    vgaHWWriteBank(hwp, ((seg) & 0x30) | ((seg) >> 4));\
562    vgaHWWriteSegment(hwp, ((seg) & 0x0f) | ((seg) << 4));
563
564static int
565et6000_check_videoram(ScrnInfoPtr pScrn, int ram)
566{
567    vgaHWPtr hwp = VGAHWPTR(pScrn);
568    unsigned char oldSegSel1, oldSegSel2, oldGR5, oldGR6, oldSEQ2, oldSEQ4;
569    int segment, i;
570    int real_ram = 0;
571    pointer check_vgabase;
572    Bool fooled = FALSE;
573    int save_vidmem;
574
575    PDEBUG("	et6000_check_videoram\n");
576    if (ram > 4096) {
577	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
578	    "Detected more than 4096 kb of video RAM. Clipped to 4096kb\n");
579	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
580	    "    (Tseng VGA chips can only use 4096kb).\n");
581	ram = 4096;
582    }
583    if (!vgaHWMapMem(pScrn)) {
584	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
585	    "Could not map VGA memory to check for video memory.\n");
586	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
587	    "    Detected amount may be wrong.\n");
588	return ram;
589    }
590    check_vgabase = (VGAHWPTR(pScrn)->Base);
591
592    /*
593     * We need to set the VGA controller in VGA graphics mode, or else we won't
594     * be able to access the full 4MB memory range. First, we save the
595     * registers we modify, of course.
596     */
597
598    oldSegSel1 = vgaHWReadSegment(hwp);
599    oldSegSel2 = vgaHWReadBank(hwp);
600
601    oldGR5 = hwp->readGr(hwp, 0x05);
602    oldGR6 = hwp->readGr(hwp, 0x06);
603
604    oldSEQ2 = hwp->readSeq(hwp, 0x02);
605    oldSEQ4 = hwp->readSeq(hwp, 0x04);
606
607    /* set graphics mode */
608    hwp->writeGr(hwp, 0x06, 0x05);
609    hwp->writeGr(hwp, 0x05, 0x40);
610    hwp->writeSeq(hwp, 0x02, 0x0F);
611    hwp->writeSeq(hwp, 0x04, 0x0E);
612
613    /*
614     * count down from presumed amount of memory in SEGSIZE steps, and
615     * look at each segment for real RAM.
616     *
617     * To select a segment, we cannot use ET4000W32SetReadWrite(), since
618     * that requires the ScreenPtr, which we don't have here.
619     */
620
621    for (segment = (ram / SEGSIZE) - 1; segment >= 0; segment--) {
622	/* select the segment */
623	ET6K_SETSEG(segment);
624
625	/* save contents of memory probing location */
626	save_vidmem = *(VIDMEM);
627
628	/* test with pattern */
629	*VIDMEM = 0xAAAA5555;
630	if (*VIDMEM != 0xAAAA5555) {
631	    *VIDMEM = save_vidmem;
632	    continue;
633	}
634	/* test with inverted pattern */
635	*VIDMEM = 0x5555AAAA;
636	if (*VIDMEM != 0x5555AAAA) {
637	    *VIDMEM = save_vidmem;
638	    continue;
639	}
640	/*
641	 * If we get here, the memory seems to be writable/readable
642	 * Now check if we aren't fooled by address wrapping (mirroring)
643	 */
644	fooled = FALSE;
645	for (i = segment - 1; i >= 0; i--) {
646	    /* select the segment */
647	    ET6K_SETSEG(i);
648
649            /* again? */
650	    vgaHWWriteBank(hwp, (i & 0x30) | (i >> 4));
651	    vgaHWWriteSegment(hwp, (i & 0x0f) | (i << 4));
652
653	    if (*VIDMEM == 0x5555AAAA) {
654		/*
655		 * Seems like address wrap, but there could of course be
656		 * 0x5555AAAA in here by accident, so we check with another
657		 * pattern again.
658		 */
659		ET6K_SETSEG(segment);
660		/* test with other pattern again */
661		*VIDMEM = 0xAAAA5555;
662		ET6K_SETSEG(i);
663		if (*VIDMEM == 0xAAAA5555) {
664		    /* now we're sure: this is not real memory */
665		    fooled = TRUE;
666		    break;
667		}
668	    }
669	}
670	if (!fooled) {
671	    real_ram = (segment + 1) * SEGSIZE;
672	    break;
673	}
674	/* restore old contents again */
675	ET6K_SETSEG(segment);
676	*VIDMEM = save_vidmem;
677    }
678
679    /* restore original register contents */
680    vgaHWWriteSegment(hwp, oldSegSel1);
681    vgaHWWriteBank(hwp, oldSegSel2);
682
683    hwp->writeGr(hwp, 0x05, oldGR5);
684    hwp->writeGr(hwp, 0x06, oldGR6);
685    hwp->writeSeq(hwp, 0x02, oldSEQ2);
686    hwp->writeSeq(hwp, 0x04, oldSEQ4);
687
688    vgaHWUnmapMem(pScrn);
689    return real_ram;
690}
691
692/*
693 * Handle amount of allowed memory: some combinations can't use all
694 * available memory. Should we still allow the user to override this?
695 *
696 * This must be called AFTER the decision has been made to use linear mode
697 * and/or acceleration, or the memory limit code won't be able to work.
698 */
699
700static int
701TsengDoMemLimit(ScrnInfoPtr pScrn, int ram, int limit, char *reason)
702{
703    if (ram > limit) {
704	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Only %d kb of memory can be used %s.\n",
705	    limit, reason);
706	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Reducing video memory to %d kb.\n", limit);
707	ram = limit;
708    }
709    return ram;
710}
711
712static int
713TsengLimitMem(ScrnInfoPtr pScrn, int ram)
714{
715    TsengPtr pTseng = TsengPTR(pScrn);
716
717    if (pTseng->UseAccel) {
718	if (pTseng->ChipType == ET4000) {
719	    /* <= W32p_ab :
720	     *   2 MB direct access + 2*512kb via apertures MBP0 and MBP1
721	     * == W32p_cd :
722	     *   2*1MB via apertures MBP0 and MBP1
723	     */
724	    if ((pTseng->ChipRev == REV_C) || (pTseng->ChipRev == REV_D))
725		ram = TsengDoMemLimit(pScrn, ram, 2048,
726				      "in linear + accelerated mode "
727				      "on W32p rev c and d");
728
729	    ram = TsengDoMemLimit(pScrn, ram, 2048 + 1024,
730				  "in linear + accelerated mode "
731				  "on W32/W32i/W32p");
732
733	    /*
734	     * upper 516kb of 4MB linear map used for
735	     *  "externally mapped registers"
736	     */
737	    ram = TsengDoMemLimit(pScrn, ram, 4096 - 516,
738				  "in linear + accelerated mode "
739				  "on W32/W32i/W32p");
740	} else {
741	    /*
742	     * upper 8kb used for externally mapped and
743	     * memory mapped registers
744	     */
745	    ram = TsengDoMemLimit(pScrn, ram, 4096 - 8,
746				  "in linear + accelerated mode "
747				  "on ET6000/6100");
748	}
749    }
750    ram = TsengDoMemLimit(pScrn, ram, 4096, "on any Tseng card");
751    return ram;
752}
753
754/*
755 * TsengDetectMem --
756 *      try to find amount of video memory installed.
757 *
758 */
759static int
760TsengDetectMem(ScrnInfoPtr pScrn)
761{
762    vgaHWPtr hwp = VGAHWPTR(pScrn);
763    TsengPtr pTseng = TsengPTR(pScrn);
764    unsigned char config;
765    int ramtype = 0;
766    int ram = 0;
767
768    PDEBUG("	TsengDetectMem\n");
769    if (pTseng->ChipType == ET6000) {
770	ramtype = hwp->readST00(hwp) & 0x03;
771	switch (ramtype) {
772	case 0x03:		       /* MDRAM */
773	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
774		"Video memory type: Multibank DRAM (MDRAM).\n");
775	    ram = ((ET6000IORead(pTseng, 0x47) & 0x07) + 1) * 8 * 32;	/* number of 8 32kb banks  */
776	    if (ET6000IORead(pTseng, 0x45) & 0x04) {
777		ram <<= 1;
778	    }
779	    /*
780	     * 8*32kb MDRAM refresh control granularity in the ET6000 fails to
781	     * recognize 2.25 MB of memory (detects 2.5 instead)
782	     */
783	    ram = et6000_check_videoram(pScrn, ram);
784	    break;
785	case 0x00:		       /* DRAM -- VERY unlikely on ET6000 cards, IMPOSSIBLE on ET6100 */
786	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
787		"Video memory type: Standard DRAM.\n");
788	    ram = 1024 << (ET6000IORead(pTseng, 0x45) & 0x03);
789	    break;
790	default:		       /* unknown RAM type */
791	    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
792		"Unknown ET6000 video memory type %d -- assuming 1 MB (unless specified)\n",
793		ramtype);
794	    ram = 1024;
795	}
796    } else {
797	config = hwp->readCrtc(hwp, 0x37);
798
799	ram = 128 << (config & 0x03);
800
801	if (config & 0x80)
802	    ram <<= 1;
803
804	/* Check for interleaving on W32i/p. */
805        config = hwp->readCrtc(hwp, 0x32);
806        if (config & 0x80) {
807            ram <<= 1;
808            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
809                       "Video memory type: Interleaved DRAM.\n");
810        } else {
811            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
812                       "Video memory type: Standard DRAM.\n");
813        }
814    }
815    return ram;
816}
817
818static Bool
819TsengProcessHibit(ScrnInfoPtr pScrn)
820{
821    TsengPtr pTseng = TsengPTR(pScrn);
822    MessageType from = X_CONFIG;
823    int hibit_mode_width;
824
825    PDEBUG("	TsengProcessHibit\n");
826    if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_HIGH)) {
827	if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_LOW)) {
828	    xf86Msg(X_ERROR, "\nOptions \"hibit_high\" and \"hibit_low\" are incompatible;\n");
829	    xf86Msg(X_ERROR, "    specify only one (not both) in X configuration file\n");
830	    return FALSE;
831	}
832	pTseng->save_divide = 0x40;
833    } else if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_HIGH)) {
834	pTseng->save_divide = 0;
835    } else {
836        vgaHWPtr hwp = VGAHWPTR(pScrn);
837
838	from = X_PROBED;
839
840	/* first check to see if hibit is probed from low-res mode */
841	hibit_mode_width = hwp->readCrtc(hwp, 0x01) + 1;
842
843	if (hibit_mode_width > 82) {
844	    xf86Msg(X_WARNING, "Non-standard VGA text or graphics mode while probing for hibit:\n");
845	    xf86Msg(X_WARNING, "    probed 'hibit' value may be wrong.\n");
846	    xf86Msg(X_WARNING, "    Preferably run probe from 80x25 textmode,\n");
847	    xf86Msg(X_WARNING, "    or specify correct value in X configuration file.\n");
848	}
849
850	/* Check for initial state of divide flag */
851	pTseng->save_divide = hwp->readSeq(hwp, 0x07) & 0x40;
852    }
853    xf86DrvMsg(pScrn->scrnIndex, from, "Initial ET4000 hibit state: %s\n",
854	pTseng->save_divide & 0x40 ? "high" : "low");
855    return TRUE;
856}
857
858static Bool
859TsengProcessOptions(ScrnInfoPtr pScrn)
860{
861    MessageType from;
862    TsengPtr pTseng = TsengPTR(pScrn);
863
864    PDEBUG("	TsengProcessOptions\n");
865
866    /* Collect all of the relevant option flags (fill in pScrn->options) */
867    xf86CollectOptions(pScrn, NULL);
868
869    /* Process the options */
870    if (!(pTseng->Options = xalloc(sizeof(TsengOptions))))
871	return FALSE;
872    memcpy(pTseng->Options, TsengOptions, sizeof(TsengOptions));
873    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pTseng->Options);
874
875    from = X_DEFAULT;
876    pTseng->HWCursor = FALSE;	       /* default */
877    if (xf86GetOptValBool(pTseng->Options, OPTION_HW_CURSOR, &pTseng->HWCursor))
878	from = X_CONFIG;
879    if (xf86ReturnOptValBool(pTseng->Options, OPTION_SW_CURSOR, FALSE)) {
880	from = X_CONFIG;
881	pTseng->HWCursor = FALSE;
882    }
883    if ((pTseng->ChipType == ET4000) && pTseng->HWCursor) {
884	xf86DrvMsg(pScrn->scrnIndex, from,
885		"Hardware Cursor not supported on this chipset\n");
886	pTseng->HWCursor = FALSE;
887    }
888
889    xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
890	pTseng->HWCursor ? "HW" : "SW");
891
892    if (pScrn->bitsPerPixel >= 8) {
893        pTseng->UseAccel = TRUE;
894	if (xf86ReturnOptValBool(pTseng->Options, OPTION_NOACCEL, FALSE)) {
895	    pTseng->UseAccel = FALSE;
896	    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Acceleration disabled\n");
897	}
898    } else
899	pTseng->UseAccel = FALSE;  /* 1bpp and 4bpp are always non-accelerated */
900
901    pTseng->SlowDram = FALSE;
902    if (xf86IsOptionSet(pTseng->Options, OPTION_SLOW_DRAM)) {
903	pTseng->SlowDram = TRUE;
904	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using slow DRAM access\n");
905    }
906    pTseng->MedDram = FALSE;
907    if (xf86IsOptionSet(pTseng->Options, OPTION_MED_DRAM)) {
908	pTseng->MedDram = TRUE;
909	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using Medium-speed DRAM access\n");
910    }
911    pTseng->FastDram = FALSE;
912    if (xf86IsOptionSet(pTseng->Options, OPTION_FAST_DRAM)) {
913	pTseng->FastDram = TRUE;
914	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using fast DRAM access\n");
915    }
916    if ((pTseng->SetW32Interleave =
917	xf86GetOptValBool(pTseng->Options, OPTION_W32_INTERLEAVE, &pTseng->W32Interleave)) )
918	    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forcing W32p memory interleave %s.\n",
919	    pTseng->W32Interleave ? "ON" : "OFF");
920    if ((pTseng->SetPCIBurst =
921	xf86GetOptValBool(pTseng->Options, OPTION_PCI_BURST, &pTseng->PCIBurst)) )
922	    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forcing PCI burst mode %s.\n",
923	    pTseng->PCIBurst ? "ON" : "OFF");
924
925    pTseng->ShowCache = FALSE;
926    if (xf86ReturnOptValBool(pTseng->Options, OPTION_SHOWCACHE, FALSE)) {
927	pTseng->ShowCache = TRUE;
928	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "(for debugging only:) Visible off-screen memory\n");
929    }
930
931    pTseng->UsePCIRetry = FALSE;
932    if (xf86ReturnOptValBool(pTseng->Options, OPTION_PCI_RETRY, FALSE)) {
933	pTseng->UsePCIRetry = TRUE;
934	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry enabled\n");
935    }
936    return TRUE;
937}
938
939static Bool
940TsengGetFbAddress(ScrnInfoPtr pScrn)
941{
942    TsengPtr pTseng = TsengPTR(pScrn);
943
944    PDEBUG("	TsengGetFbAddress\n");
945
946    /* base0 is the framebuffer and base1 is the PCI IO space. */
947    if (PCI_REGION_BASE(pTseng->PciInfo, 0, REGION_MEM)) {
948        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
949                   "No valid Framebuffer address in PCI config space;\n");
950        return FALSE;
951    } else
952        pTseng->FbAddress = PCI_REGION_BASE(pTseng->PciInfo, 0, REGION_MEM);
953
954
955    if (xf86RegisterResources(pTseng->pEnt->index,NULL,ResNone)) {
956        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Cannot register FB memory.\n");
957        return FALSE;
958    }
959
960    /* The W32 linear map address space is always 4Mb (mainly because the
961     * memory-mapped registers are located near the top of the 4MB area).
962     * The ET6000 maps out 16 Meg, but still uses only 4Mb of that.
963     * However, since all mmap()-ed space is also reflected in the "ps"
964     * listing for the Xserver, many users will be worried by a server that
965     * always eats 16MB of memory, even if it's not "real" memory, just
966     * address space. Not mapping all of the 16M may be a potential problem
967     * though: if another board is mapped on top of the remaining part of
968     * the 16M... Boom!
969     */
970    if (pTseng->ChipType == ET6000)
971	pTseng->FbMapSize = 16384 * 1024;
972    else
973	pTseng->FbMapSize = 4096 * 1024;
974
975    xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Framebuffer at 0x%lX\n",
976               (unsigned long)pTseng->FbAddress);
977
978    return TRUE;
979}
980
981static Bool
982TsengPreInit(ScrnInfoPtr pScrn, int flags)
983{
984    TsengPtr pTseng;
985    MessageType from;
986    int i;
987
988    if (flags & PROBE_DETECT) return FALSE;
989
990    PDEBUG("	TsengPreInit\n");
991
992    /*
993     * Note: This function is only called once at server startup, and
994     * not at the start of each server generation.  This means that
995     * only things that are persistent across server generations can
996     * be initialised here.  xf86Screens[] is (pScrn is a pointer to one
997     * of these).  Privates allocated using xf86AllocateScrnInfoPrivateIndex()
998     * are too, and should be used for data that must persist across
999     * server generations.
1000     *
1001     * Per-generation data should be allocated with
1002     * AllocateScreenPrivateIndex() from the ScreenInit() function.
1003     */
1004
1005    /* The vgahw module should be loaded here when needed */
1006
1007    /* This driver doesn't expect more than one entity per screen */
1008    if (pScrn->numEntities > 1)
1009	    return FALSE;
1010
1011    /* Allocate the TsengRec driverPrivate */
1012    if (!TsengGetRec(pScrn)) {
1013	return FALSE;
1014    }
1015    pTseng = TsengPTR(pScrn);
1016
1017    /* This is the general case */
1018    pTseng->pEnt = xf86GetEntityInfo(*pScrn->entityList);
1019
1020#if 1
1021    if (xf86LoadSubModule(pScrn, "int10")) {
1022 	xf86Int10InfoPtr pInt;
1023	xf86LoaderReqSymLists(int10Symbols, NULL);
1024#if 1
1025	xf86DrvMsg(pScrn->scrnIndex,X_INFO,"initializing int10\n");
1026	pInt = xf86InitInt10(pTseng->pEnt->index);
1027	xf86FreeInt10(pInt);
1028#endif
1029    }
1030#endif
1031
1032    if (!xf86LoadSubModule(pScrn, "vgahw"))
1033	return FALSE;
1034    xf86LoaderReqSymLists(vgaHWSymbols, NULL);
1035    /*
1036     * Allocate a vgaHWRec
1037     */
1038    if (!vgaHWGetHWRec(pScrn))
1039	return FALSE;
1040
1041    vgaHWGetIOBase(VGAHWPTR(pScrn));
1042    /*
1043     * Since, the capabilities are determined by the chipset, the very first
1044     * thing to do is to figure out the chipset and its capabilities.
1045     */
1046
1047    TsengUnlock(pScrn);
1048
1049    pTseng->PciInfo = xf86GetPciInfoForEntity(pTseng->pEnt->index);
1050    if (!TsengPreInitPCI(pScrn)) {
1051        TsengFreeRec(pScrn);
1052        return FALSE;
1053    }
1054
1055    if (!TsengRAMDACProbe(pScrn)) {
1056        TsengFreeRec(pScrn);
1057	return FALSE;
1058    }
1059
1060    pScrn->progClock = TRUE;
1061
1062    /*
1063     * Now we can check what depth we support.
1064     */
1065
1066    /* Set pScrn->monitor */
1067    pScrn->monitor = pScrn->confScreen->monitor;
1068
1069    /*
1070     * The first thing we should figure out is the depth, bpp, etc.
1071     * Our default depth is 8, so pass it to the helper function.
1072     * Our preference for depth 24 is 24bpp, so tell it that too.
1073     */
1074    if (!xf86SetDepthBpp(pScrn, 8, 8, 8, Support24bppFb | Support32bppFb |
1075                         SupportConvert32to24 | PreferConvert32to24)) {
1076	return FALSE;
1077    } else {
1078        switch (pScrn->depth) {
1079        case 8:
1080        case 16:
1081        case 24:
1082        case 32:
1083            /* OK */
1084            break;
1085        default:
1086            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1087                       "Given depth (%d) is not supported by this driver\n",
1088                       pScrn->depth);
1089            return FALSE;
1090        }
1091    }
1092    xf86PrintDepthBpp(pScrn);
1093
1094    /* Get the depth24 pixmap format */
1095    if (pScrn->depth == 24 && pix24bpp == 0)
1096	pix24bpp = xf86GetBppFromDepth(pScrn, 24);
1097
1098    if (pScrn->bitsPerPixel > 8)
1099	pTseng->Bytesperpixel = pScrn->bitsPerPixel / 8;
1100    else
1101	pTseng->Bytesperpixel = 1;  /* this is fake for < 8bpp, but simplifies other code */
1102
1103    /* hardware limits */
1104    pScrn->maxHValue = Tseng_HMAX;
1105    pScrn->maxVValue = Tseng_VMAX;
1106
1107    /*
1108     * This must happen after pScrn->display has been set because
1109     * xf86SetWeight references it.
1110     */
1111
1112    /* Set weight/mask/offset for depth > 8 */
1113    if (pScrn->depth > 8) {
1114	/* The defaults are OK for us */
1115	rgb zeros = {0, 0, 0};
1116
1117	if (!xf86SetWeight(pScrn, zeros, zeros)) {
1118	    return FALSE;
1119	} else {
1120	    /* XXX check that weight returned is supported */
1121	    ;
1122	}
1123    }
1124
1125    /* Set the default visual. */
1126    if (!xf86SetDefaultVisual(pScrn, -1))
1127	return FALSE;
1128
1129    /* The gamma fields must be initialised when using the new cmap code */
1130    if (pScrn->depth > 1) {
1131	Gamma zeros = {0.0, 0.0, 0.0};
1132
1133	if (!xf86SetGamma(pScrn, zeros)) {
1134	    return FALSE;
1135	}
1136    }
1137
1138    /* Set the bits per RGB for 8bpp mode */
1139    if (pScrn->depth == 8) {
1140	/* Default to 6, because most Tseng chips/RAMDACs don't support it */
1141	pScrn->rgbBits = 6;
1142    }
1143    if (!TsengProcessOptions(pScrn))   /* must be done _after_ we know what chip this is */
1144	return FALSE;
1145
1146    if (!TsengGetFbAddress(pScrn))
1147        return FALSE;
1148
1149    pScrn->memPhysBase = pTseng->FbAddress;
1150    pScrn->fbOffset = 0;
1151
1152    if (pTseng->UseAccel)
1153	VGAHWPTR(pScrn)->MapSize = 0x20000;  /* accelerator apertures and MMIO */
1154    else
1155	VGAHWPTR(pScrn)->MapSize = 0x10000;
1156
1157    /*
1158     * XXX At least part of this range does appear to be disabled,
1159     * but to play safe, it is marked as "unused" for now.
1160     * Changed this to "disable". Otherwise it might interfere with DGA.
1161     */
1162    xf86SetOperatingState(resVgaMem, pTseng->pEnt->index, ResDisableOpr);
1163
1164    /* hibit processing (TsengProcessOptions() must have been called first) */
1165    pTseng->save_divide = 0x40;	       /* default */
1166    if (pTseng->ChipType == ET4000) {
1167	if (!TsengProcessHibit(pScrn))
1168	    return FALSE;
1169    }
1170    /*
1171     * If the user has specified the amount of memory in the XF86Config
1172     * file, we respect that setting.
1173     */
1174    if (pTseng->pEnt->device->videoRam != 0) {
1175	pScrn->videoRam = pTseng->pEnt->device->videoRam;
1176	from = X_CONFIG;
1177    } else {
1178	from = X_PROBED;
1179	pScrn->videoRam = TsengDetectMem(pScrn);
1180    }
1181    pScrn->videoRam = TsengLimitMem(pScrn, pScrn->videoRam);
1182
1183    xf86DrvMsg(pScrn->scrnIndex, from, "VideoRAM: %d kByte.\n",
1184               pScrn->videoRam);
1185
1186     TsengSetupClockRange(pScrn);
1187
1188    /*
1189     * xf86ValidateModes will check that the mode HTotal and VTotal values
1190     * don't exceed the chipset's limit if pScrn->maxHValue and
1191     * pScrn->maxVValue are set.  Since our TsengValidMode() already takes
1192     * care of this, we don't worry about setting them here.
1193     */
1194
1195    /* Select valid modes from those available */
1196    i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
1197	pScrn->display->modes, &pTseng->clockRange,
1198	NULL, 32, pScrn->maxHValue, 8*pTseng->Bytesperpixel, /* H limits */
1199	0, pScrn->maxVValue,	       /* V limits */
1200	pScrn->display->virtualX,
1201	pScrn->display->virtualY,
1202	pTseng->FbMapSize,
1203	LOOKUP_BEST_REFRESH);	       /* LOOKUP_CLOSEST_CLOCK | LOOKUP_CLKDIV2 when no programmable clock ? */
1204
1205    if (i == -1) {
1206	TsengFreeRec(pScrn);
1207	return FALSE;
1208    }
1209    /* Prune the modes marked as invalid */
1210    xf86PruneDriverModes(pScrn);
1211
1212    if (i == 0 || pScrn->modes == NULL) {
1213	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n");
1214	TsengFreeRec(pScrn);
1215	return FALSE;
1216    }
1217    /*
1218     * Set the CRTC parameters for all of the modes based on the type
1219     * of mode, and the chipset's interlace requirements.
1220     *
1221     * Calling this is required if the mode->Crtc* values are used by the
1222     * driver and if the driver doesn't provide code to set them.  They
1223     * are not pre-initialised at all.
1224     */
1225    xf86SetCrtcForModes(pScrn, 0);
1226
1227    /* Set the current mode to the first in the list */
1228    pScrn->currentMode = pScrn->modes;
1229
1230    /* Print the list of modes being used */
1231    xf86PrintModes(pScrn);
1232
1233    /* Set display resolution */
1234    xf86SetDpi(pScrn, 0, 0);
1235
1236    /* Load bpp-specific modules */
1237    switch (pScrn->bitsPerPixel) {
1238    case 1:
1239	if (xf86LoadSubModule(pScrn, "xf1bpp") == NULL) {
1240	  TsengFreeRec(pScrn);
1241	  return FALSE;
1242	}
1243	xf86LoaderReqSymbols("xf1bppScreenInit", NULL);
1244	break;
1245    case 4:
1246	if (xf86LoadSubModule(pScrn, "xf4bpp") == NULL) {
1247	  TsengFreeRec(pScrn);
1248	  return FALSE;
1249	}
1250	xf86LoaderReqSymbols("xf4bppScreenInit", NULL);
1251	break;
1252    default:
1253	if (xf86LoadSubModule(pScrn, "fb") == NULL) {
1254	  TsengFreeRec(pScrn);
1255	  return FALSE;
1256	}
1257	xf86LoaderReqSymLists(fbSymbols, NULL);
1258	break;
1259    }
1260
1261    /* Load XAA if needed */
1262    if (pTseng->UseAccel) {
1263	if (!xf86LoadSubModule(pScrn, "xaa")) {
1264	    TsengFreeRec(pScrn);
1265	    return FALSE;
1266	}
1267	xf86LoaderReqSymLists(xaaSymbols, NULL);
1268    }
1269    /* Load ramdac if needed */
1270    if (pTseng->HWCursor) {
1271	if (!xf86LoadSubModule(pScrn, "ramdac")) {
1272	    TsengFreeRec(pScrn);
1273	    return FALSE;
1274	}
1275	xf86LoaderReqSymLists(ramdacSymbols, NULL);
1276    }
1277/*    TsengLock(pScrn); */
1278
1279    return TRUE;
1280}
1281
1282static void
1283TsengSetupAccelMemory(int scrnIndex, ScreenPtr pScreen)
1284{
1285    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
1286    TsengPtr pTseng = TsengPTR(pScrn);
1287    int offscreen_videoram, videoram_end, req_videoram;
1288    int i;
1289    int v;
1290
1291    /* XXX Hack to suppress messages in subsequent generations. */
1292    if (serverGeneration == 1)
1293	v = 1;
1294    else
1295	v = 100;
1296    /*
1297     * The accelerator requires free off-screen video memory to operate. The
1298     * more there is, the more it can accelerate.
1299     */
1300
1301    videoram_end = pScrn->videoRam * 1024;
1302    offscreen_videoram = videoram_end -
1303	pScrn->displayWidth * pScrn->virtualY * pTseng->Bytesperpixel;
1304    xf86DrvMsgVerb(scrnIndex, X_INFO, v, "Available off-screen memory: %d bytes.\n",
1305	offscreen_videoram);
1306
1307    /*
1308     * The HW cursor requires 1kb of off-screen memory, aligned to 1kb
1309     * (256 DWORDS). Setting up its memory first ensures the alignment.
1310     */
1311    if (pTseng->HWCursor) {
1312	req_videoram = 1024;
1313	if (offscreen_videoram < req_videoram) {
1314	    xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v,
1315		"Hardware Cursor disabled. It requires %d bytes of free video memory\n",
1316		req_videoram);
1317	    pTseng->HWCursor = FALSE;
1318	    pTseng->HWCursorBufferOffset = 0;
1319	} else {
1320	    offscreen_videoram -= req_videoram;
1321	    videoram_end -= req_videoram;
1322	    pTseng->HWCursorBufferOffset = videoram_end;
1323	}
1324    } else {
1325	pTseng->HWCursorBufferOffset = 0;
1326    }
1327
1328    /*
1329     * Acceleration memory setup. Do this only if acceleration is enabled.
1330     */
1331    if (!pTseng->UseAccel) return;
1332
1333    /*
1334     * Basic acceleration needs storage for FG, BG and PAT colors in
1335     * off-screen memory. Each color requires 2(ping-pong)*8 bytes.
1336     */
1337    req_videoram = 2 * 8 * 3;
1338    if (offscreen_videoram < req_videoram) {
1339	xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v,
1340	    "Acceleration disabled. It requires AT LEAST %d bytes of free video memory\n",
1341	    req_videoram);
1342	pTseng->UseAccel = FALSE;
1343	pTseng->AccelColorBufferOffset = 0;
1344	goto end_memsetup;	      /* no basic acceleration means none at all */
1345    } else {
1346	offscreen_videoram -= req_videoram;
1347	videoram_end -= req_videoram;
1348	pTseng->AccelColorBufferOffset = videoram_end;
1349    }
1350
1351    /*
1352     * Color expansion (using triple buffering) requires 3 non-expanded
1353     * scanlines, DWORD padded.
1354     */
1355    req_videoram = 3 * ((pScrn->virtualX + 31) / 32) * 4;
1356    if (offscreen_videoram < req_videoram) {
1357	xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v,
1358	    "Accelerated color expansion disabled (%d more bytes of free video memory required)\n",
1359	    req_videoram - offscreen_videoram);
1360	pTseng->AccelColorExpandBufferOffsets[0] = 0;
1361    } else {
1362	offscreen_videoram -= req_videoram;
1363	for (i = 0; i < 3; i++) {
1364	    videoram_end -= req_videoram / 3;
1365	    pTseng->AccelColorExpandBufferOffsets[i] = videoram_end;
1366	}
1367    }
1368
1369    /*
1370     * XAA ImageWrite support needs two entire line buffers. The
1371     * current code assumes buffer 1 lies in the same 8kb aperture as
1372     * buffer 0.
1373     *
1374     * [ FIXME: aren't we forgetting the DWORD padding here ? ]
1375     * [ FIXME: why here double-buffering and in colexp triple-buffering? ]
1376     */
1377    req_videoram = 2 * (pScrn->virtualX * pTseng->Bytesperpixel);
1378
1379    if (offscreen_videoram < req_videoram) {
1380	xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v,
1381	    "Accelerated ImageWrites disabled (%d more bytes of free video memory required)\n",
1382	    req_videoram - offscreen_videoram);
1383	pTseng->AccelImageWriteBufferOffsets[0] = 0;
1384    } else {
1385	offscreen_videoram -= req_videoram;
1386	for (i = 0; i < 2; i++) {
1387	    videoram_end -= req_videoram / 2;
1388	    pTseng->AccelImageWriteBufferOffsets[i] = videoram_end;
1389	}
1390    }
1391
1392    xf86DrvMsgVerb(scrnIndex, X_INFO, v,
1393	"Remaining off-screen memory available for pixmap cache: %d bytes.\n",
1394	offscreen_videoram);
1395
1396end_memsetup:
1397    pScrn->videoRam = videoram_end / 1024;
1398}
1399
1400static Bool
1401TsengScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
1402{
1403    ScrnInfoPtr pScrn;
1404    TsengPtr pTseng;
1405    int ret;
1406    VisualPtr visual;
1407
1408    PDEBUG("	TsengScreenInit\n");
1409
1410    /*
1411     * First get the ScrnInfoRec
1412     */
1413    pScrn = xf86Screens[pScreen->myNum];
1414
1415    pTseng = TsengPTR(pScrn);
1416    /* Map the Tseng memory areas */
1417    if (!TsengMapMem(pScrn))
1418	return FALSE;
1419
1420    /* Save the current state */
1421    TsengSave(pScrn);
1422
1423    /* Initialise the first mode */
1424    TsengModeInit(pScrn, pScrn->currentMode);
1425
1426    /* Darken the screen for aesthetic reasons and set the viewport */
1427    TsengSaveScreen(pScreen, SCREEN_SAVER_ON);
1428
1429    TsengAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
1430    /* XXX Fill the screen with black */
1431
1432    /*
1433     * Reset visual list.
1434     */
1435    miClearVisualTypes();
1436
1437    /* Setup the visuals we support. */
1438
1439    /*
1440     * For bpp > 8, the default visuals are not acceptable because we only
1441     * support TrueColor and not DirectColor.  To deal with this, call
1442     * miSetVisualTypes for each visual supported.
1443     */
1444    if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth),
1445			  pScrn->rgbBits, pScrn->defaultVisual))
1446      return FALSE;
1447
1448    miSetPixmapDepths ();
1449
1450    /*
1451     * Call the framebuffer layer's ScreenInit function, and fill in other
1452     * pScreen fields.
1453     */
1454
1455    switch (pScrn->bitsPerPixel) {
1456#if HAVE_XF1BPP
1457    case 1:
1458	ret = xf1bppScreenInit(pScreen, pTseng->FbBase,
1459			pScrn->virtualX, pScrn->virtualY,
1460			pScrn->xDpi, pScrn->yDpi,
1461			pScrn->displayWidth);
1462	break;
1463#endif
1464#if HAVE_XF4BPP
1465    case 4:
1466	ret = xf4bppScreenInit(pScreen, pTseng->FbBase,
1467			pScrn->virtualX, pScrn->virtualY,
1468			pScrn->xDpi, pScrn->yDpi,
1469			pScrn->displayWidth);
1470	break;
1471#endif
1472    default:
1473        ret  = fbScreenInit(pScreen, pTseng->FbBase,
1474			pScrn->virtualX, pScrn->virtualY,
1475			pScrn->xDpi, pScrn->yDpi,
1476			pScrn->displayWidth, pScrn->bitsPerPixel);
1477	break;
1478    }
1479
1480    if (!ret)
1481	return FALSE;
1482
1483    xf86SetBlackWhitePixels(pScreen);
1484
1485    if (pScrn->bitsPerPixel > 8) {
1486	/* Fixup RGB ordering */
1487	visual = pScreen->visuals + pScreen->numVisuals;
1488	while (--visual >= pScreen->visuals) {
1489	    if ((visual->class | DynamicClass) == DirectColor) {
1490		visual->offsetRed = pScrn->offset.red;
1491		visual->offsetGreen = pScrn->offset.green;
1492		visual->offsetBlue = pScrn->offset.blue;
1493		visual->redMask = pScrn->mask.red;
1494		visual->greenMask = pScrn->mask.green;
1495		visual->blueMask = pScrn->mask.blue;
1496	    }
1497	}
1498    }
1499
1500#if HAVE_XF1BPP
1501    /* must be after RGB ordering fixed */
1502    if (pScrn->bitsPerPixel > 4)
1503#endif
1504	fbPictureInit(pScreen, 0, 0);
1505
1506    if (pScrn->depth >= 8)
1507        TsengDGAInit(pScreen);
1508
1509    /*
1510     * Initialize the acceleration interface.
1511     */
1512    TsengSetupAccelMemory(scrnIndex, pScreen);
1513    if (pTseng->UseAccel) {
1514	tseng_init_acl(pScrn);	/* set up accelerator */
1515	if (!TsengXAAInit(pScreen)) {	/* set up XAA interface */
1516	    return FALSE;
1517	}
1518    }
1519
1520    miInitializeBackingStore(pScreen);
1521    xf86SetSilkenMouse(pScreen);
1522    /* Initialise cursor functions */
1523    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());
1524
1525    /* Hardware Cursor layer */
1526    if (pTseng->HWCursor) {
1527	if (!TsengHWCursorInit(pScreen))
1528	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1529		"Hardware cursor initialization failed\n");
1530    }
1531
1532    /* Initialise default colourmap */
1533    if (!miCreateDefColormap(pScreen))
1534	return FALSE;
1535
1536    if (pScrn->depth == 4 || pScrn->depth == 8) { /* fb and xf4bpp */
1537	vgaHWHandleColormaps(pScreen);
1538    }
1539    pScrn->racIoFlags = RAC_FB | RAC_COLORMAP | RAC_CURSOR | RAC_VIEWPORT;
1540    pScrn->racMemFlags = pScrn->racIoFlags;
1541
1542    /* Wrap the current CloseScreen and SaveScreen functions */
1543    pScreen->SaveScreen = TsengSaveScreen;
1544
1545    /* Support for DPMS, the ET4000W32Pc and newer uses a different and
1546     * simpler method than the older cards.
1547     */
1548    if ((pTseng->ChipType == ET4000) &&
1549        ((pTseng->ChipRev == REV_A) || (pTseng->ChipRev == REV_B)))
1550        xf86DPMSInit(pScreen, (DPMSSetProcPtr)TsengHVSyncDPMSSet, 0);
1551    else
1552	xf86DPMSInit(pScreen, (DPMSSetProcPtr)TsengCrtcDPMSSet, 0);
1553
1554    pTseng->CloseScreen = pScreen->CloseScreen;
1555    pScreen->CloseScreen = TsengCloseScreen;
1556
1557    /* Report any unused options (only for the first generation) */
1558    if (serverGeneration == 1) {
1559	xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
1560    }
1561    /* Done */
1562    return TRUE;
1563}
1564
1565static Bool
1566TsengEnterVT(int scrnIndex, int flags)
1567{
1568    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
1569    TsengPtr pTseng = TsengPTR(pScrn);
1570
1571    PDEBUG("	TsengEnterVT\n");
1572
1573    vgaHWUnlock(VGAHWPTR(pScrn));
1574    TsengUnlock(pScrn);
1575
1576    if (!TsengModeInit(pScrn, pScrn->currentMode))
1577        return FALSE;
1578    if (pTseng->UseAccel) {
1579	tseng_init_acl(pScrn);	/* set up accelerator */
1580    }
1581    return TRUE;
1582}
1583
1584static void
1585TsengLeaveVT(int scrnIndex, int flags)
1586{
1587    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
1588    TsengPtr pTseng = TsengPTR(pScrn);
1589
1590    PDEBUG("	TsengLeaveVT\n");
1591    TsengRestore(pScrn, &(VGAHWPTR(pScrn)->SavedReg),
1592		 &pTseng->SavedReg,VGA_SR_ALL);
1593
1594    TsengLock(pScrn);
1595    vgaHWLock(VGAHWPTR(pScrn));
1596}
1597
1598static Bool
1599TsengCloseScreen(int scrnIndex, ScreenPtr pScreen)
1600{
1601    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
1602    TsengPtr pTseng = TsengPTR(pScrn);
1603
1604    PDEBUG("	TsengCloseScreen\n");
1605
1606    if (pScrn->vtSema) {
1607    TsengRestore(pScrn, &(VGAHWPTR(pScrn)->SavedReg),
1608		 &(pTseng->SavedReg),VGA_SR_ALL);
1609    TsengUnmapMem(pScrn);
1610    }
1611    if (pTseng->AccelInfoRec)
1612	XAADestroyInfoRec(pTseng->AccelInfoRec);
1613    if (pTseng->CursorInfoRec)
1614	xf86DestroyCursorInfoRec(pTseng->CursorInfoRec);
1615
1616    pScrn->vtSema = FALSE;
1617
1618    pScreen->CloseScreen = pTseng->CloseScreen;
1619    return (*pScreen->CloseScreen) (scrnIndex, pScreen);
1620}
1621
1622/*
1623 * SaveScreen --
1624 *
1625 *   perform a sequencer reset.
1626 *
1627 * The ET4000 "Video System Configuration 1" register (CRTC index 0x36),
1628 * which is used to set linear memory mode and MMU-related stuff, is
1629 * partially reset to "0" when TS register index 0 bit 1 is set (synchronous
1630 * reset): bits 3..5 are reset during a sync. reset.
1631 *
1632 * We therefor do _not_ call vgaHWSaveScreen here, since it does a sequencer
1633 * reset. Instead, we do the same as in vgaHWSaveScreen except for the seq. reset.
1634 *
1635 * If this is not done, the higher level code will not be able to access the
1636 * framebuffer (because it is temporarily in banked mode instead of linear
1637 * mode) as long as SaveScreen is active (=in between a
1638 * SaveScreen(FALSE)/SaveScreen(TRUE) pair)
1639 */
1640
1641static Bool
1642TsengSaveScreen(ScreenPtr pScreen, int mode)
1643{
1644    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
1645    vgaHWPtr hwp = VGAHWPTR(pScrn);
1646    TsengPtr pTseng = TsengPTR(pScrn);
1647    Bool unblank;
1648
1649    PDEBUG("	TsengSaveScreen\n");
1650
1651    unblank = xf86IsUnblank(mode);
1652
1653    if (pTseng->ChipType == ET6000) {
1654	return vgaHWSaveScreen(pScreen, unblank);
1655    } else {
1656       if (unblank)
1657	  SetTimeSinceLastInputEvent();
1658
1659       if (pScrn->vtSema) {
1660           /* vgaHWBlankScreen without seq reset */
1661           CARD8 scrn;
1662
1663           scrn = hwp->readSeq(hwp, 0x01);
1664
1665           if (unblank)
1666               scrn &= 0xDF; /* enable screen */
1667           else
1668               scrn |= 0x20; /* blank screen */
1669
1670           hwp->writeSeq(hwp, 0x01, scrn); /* change mode */
1671       }
1672       return (TRUE);
1673    }
1674}
1675
1676static Bool
1677TsengMapMem(ScrnInfoPtr pScrn)
1678{
1679    TsengPtr pTseng = TsengPTR(pScrn);
1680
1681    PDEBUG("	TsengMapMem\n");
1682
1683    /* Map the VGA memory */
1684
1685    if (!vgaHWMapMem(pScrn)) {
1686	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1687	    "Could not mmap standard VGA memory aperture.\n");
1688	return FALSE;
1689    }
1690
1691#ifndef XSERVER_LIBPCIACCESS
1692    pTseng->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
1693                                   pTseng->PciTag,
1694                                   (unsigned long)pTseng->FbAddress,
1695                                   pTseng->FbMapSize);
1696#else
1697    {
1698      void** result = (void**)&pTseng->FbBase;
1699      int err = pci_device_map_range(pTseng->PciInfo,
1700				     pTseng->FbAddress,
1701				     pTseng->FbMapSize,
1702				     PCI_DEV_MAP_FLAG_WRITABLE |
1703				     PCI_DEV_MAP_FLAG_WRITE_COMBINE,
1704				     result);
1705
1706      if (err)
1707	return FALSE;
1708    }
1709#endif
1710    if (pTseng->FbBase == NULL) {
1711        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1712                   "Could not mmap linear video memory.\n");
1713        return FALSE;
1714    }
1715
1716    /* need some sanity here */
1717    if (pTseng->UseAccel) {
1718#ifndef XSERVER_LIBPCIACCESS
1719        pTseng->MMioBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO,
1720                                         pTseng->PciTag,
1721                                         (unsigned long)pTseng->FbAddress,
1722                                         pTseng->FbMapSize);
1723#else
1724	pTseng->MMioBase = pTseng->FbBase;
1725#endif
1726        if (!pTseng->MMioBase) {
1727	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
1728		       "Could not mmap mmio memory.\n");
1729	    return FALSE;
1730        }
1731        pTseng->MMioBase += 0x3FFF00L;
1732    }
1733
1734    if (pTseng->FbBase == NULL)
1735	return FALSE;
1736
1737    return TRUE;
1738}
1739
1740static Bool
1741TsengUnmapMem(ScrnInfoPtr pScrn)
1742{
1743    TsengPtr pTseng = TsengPTR(pScrn);
1744
1745    PDEBUG("	TsengUnmapMem\n");
1746
1747#ifndef XSERVER_LIBPCIACCESS
1748    xf86UnMapVidMem(pScrn->scrnIndex, (pointer) pTseng->FbBase, pTseng->FbMapSize);
1749#else
1750    pci_device_unmap_range(pTseng->PciInfo, pTseng->FbBase, pTseng->FbMapSize);
1751#endif
1752
1753    vgaHWUnmapMem(pScrn);
1754
1755    pTseng->FbBase = NULL;
1756
1757    return TRUE;
1758}
1759
1760static void
1761TsengFreeScreen(int scrnIndex, int flags)
1762{
1763    PDEBUG("	TsengFreeScreen\n");
1764    if (xf86LoaderCheckSymbol("vgaHWFreeHWRec"))
1765	vgaHWFreeHWRec(xf86Screens[scrnIndex]);
1766    TsengFreeRec(xf86Screens[scrnIndex]);
1767}
1768
1769static Bool
1770TsengSwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
1771{
1772    PDEBUG("	TsengSwitchMode\n");
1773    return TsengModeInit(xf86Screens[scrnIndex], mode);
1774}
1775