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