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