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