/*
 * Copyright 1992-2003 by Alan Hourihane, North Wales, UK.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Alan Hourihane not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Alan Hourihane makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * ALAN HOURIHANE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL ALAN HOURIHANE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Alan Hourihane, alanh@fairlite.demon.co.uk
 *          Re-written for XFree86 v4.0
 *
 * Previous driver (pre-XFree86 v4.0) by
 *          Alan Hourihane, alanh@fairlite.demon.co.uk
 *          David Wexelblat (major contributor)
 *          Massimiliano Ghilardi, max@Linuz.sns.it, some fixes to the
 *                                 clockchip programming code.
 */

#include "config.h"

#include "fb.h"

#include "micmap.h"
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Pci.h"
#include "xf86cmap.h"
#include "vgaHW.h"

#include "vbe.h"
#include "dixstruct.h"
#include "compiler.h"

#include "mipointer.h"

#include "shadow.h"
#include "trident.h"
#include "trident_regs.h"

#ifdef XFreeXDGA
#define _XF86DGA_SERVER_
#include <X11/extensions/xf86dgaproto.h>
#endif

#include "globals.h"
#include <X11/extensions/dpmsconst.h>

#include "xf86xv.h"

static const OptionInfoRec * TRIDENTAvailableOptions(int chipid, int busid);
static void     TRIDENTIdentify(int flags);
static Bool     TRIDENTProbe(DriverPtr drv, int flags);
static Bool     TRIDENTPreInit(ScrnInfoPtr pScrn, int flags);
static Bool     TRIDENTScreenInit(ScreenPtr pScreen, int argc, char **argv);

/*
 * This is intentionally screen-independent.  It indicates the binding
 * choice made in the first PreInit.
 */
static int pix24bpp = 0;

#define TRIDENT_VERSION 4000
#define TRIDENT_NAME "TRIDENT"
#define TRIDENT_DRIVER_NAME "trident"
#define TRIDENT_MAJOR_VERSION PACKAGE_VERSION_MAJOR
#define TRIDENT_MINOR_VERSION PACKAGE_VERSION_MINOR
#define TRIDENT_PATCHLEVEL PACKAGE_VERSION_PATCHLEVEL

/* 
 * This contains the functions needed by the server after loading the driver
 * module.  It must be supplied, and gets passed back by the SetupProc
 * function in the dynamic case.  In the static case, a reference to this
 * is compiled in, and this requires that the name of this DriverRec be
 * an upper-case version of the driver name.
 */

_X_EXPORT DriverRec TRIDENT = {
    TRIDENT_VERSION,
    TRIDENT_DRIVER_NAME,
    TRIDENTIdentify,
    TRIDENTProbe,
    TRIDENTAvailableOptions,
    NULL,
    0
};

static SymTabRec TRIDENTChipsets[] = {
    { TVGA9000,         "tvga9000" },
    { TVGA9000i,        "tvga9000i" },
    { TVGA8900C,        "tvga8900c" },
    { TVGA8900D,        "tvga8900d" },
    { TVGA9200CXr,      "tvga9200cxr" },
    { TGUI9400CXi,      "tgui9400cxi" },
    { CYBER9320,        "cyber9320" },
    { CYBER9388,        "cyber9388" },
    { CYBER9397,        "cyber9397" },
    { CYBER9397DVD,     "cyber9397dvd" },
    { CYBER9520,        "cyber9520" },
    { CYBER9525DVD,     "cyber9525dvd" },
    { CYBERBLADEE4,     "cyberblade/e4" },
    { TGUI9420DGi,      "tgui9420dgi" },
    { TGUI9440AGi,      "tgui9440agi" },
    { TGUI9660,         "tgui9660" },
    { TGUI9680,         "tgui9680" },
    { PROVIDIA9682,     "providia9682" },
    { PROVIDIA9685,     "providia9685" },
    { CYBER9382,        "cyber9382" },
    { CYBER9385,        "cyber9385" },
    { IMAGE975,         "3dimage975" },
    { IMAGE985,         "3dimage985" },
    { BLADE3D,          "blade3d" },
    { CYBERBLADEI7,     "cyberbladei7" },
    { CYBERBLADEI7D,    "cyberbladei7d" },
    { CYBERBLADEI1,     "cyberbladei1" },
    { CYBERBLADEI1D,    "cyberbladei1d" },
    { CYBERBLADEAI1,    "cyberbladeAi1" },
    { CYBERBLADEAI1D,   "cyberbladeAi1d" },
    { BLADEXP,          "bladeXP" },
    { CYBERBLADEXPAI1,  "cyberbladeXPAi1" },
    { CYBERBLADEXP4,    "cyberbladeXP4" },
    { XP5,              "XP5" },
    { -1,               NULL }
};

static PciChipsets TRIDENTPciChipsets[] = {
    { CYBER9320,        PCI_CHIP_9320,  RES_SHARED_VGA },
    { CYBER9388,        PCI_CHIP_9388,  RES_SHARED_VGA },
    { CYBER9397,        PCI_CHIP_9397,  RES_SHARED_VGA },
    { CYBER9397DVD,     PCI_CHIP_939A,  RES_SHARED_VGA },
    { CYBER9520,        PCI_CHIP_9520,  RES_SHARED_VGA },
    { CYBER9525DVD,     PCI_CHIP_9525,  RES_SHARED_VGA },
    { CYBERBLADEE4,     PCI_CHIP_9540,  RES_SHARED_VGA },
    { TGUI9420DGi,      PCI_CHIP_9420,  RES_SHARED_VGA },
    { TGUI9440AGi,      PCI_CHIP_9440,  RES_SHARED_VGA },
    { TGUI9660,         PCI_CHIP_9660,  RES_SHARED_VGA },
    { TGUI9680,         PCI_CHIP_9660,  RES_SHARED_VGA },
    { PROVIDIA9682,     PCI_CHIP_9660,  RES_SHARED_VGA },
    { PROVIDIA9685,     PCI_CHIP_9660,  RES_SHARED_VGA },
    { CYBER9382,        PCI_CHIP_9660,  RES_SHARED_VGA },
    { CYBER9385,        PCI_CHIP_9660,  RES_SHARED_VGA },
    { IMAGE975,         PCI_CHIP_9750,  RES_SHARED_VGA },
    { IMAGE985,         PCI_CHIP_9850,  RES_SHARED_VGA },
    { BLADE3D,          PCI_CHIP_9880,  RES_SHARED_VGA },
    { CYBERBLADEI7,     PCI_CHIP_8400,  RES_SHARED_VGA },
    { CYBERBLADEI7D,    PCI_CHIP_8420,  RES_SHARED_VGA },
    { CYBERBLADEI1,     PCI_CHIP_8500,  RES_SHARED_VGA },
    { CYBERBLADEI1D,    PCI_CHIP_8520,  RES_SHARED_VGA },
    { CYBERBLADEAI1,    PCI_CHIP_8600,  RES_SHARED_VGA },
    { CYBERBLADEAI1D,   PCI_CHIP_8620,  RES_SHARED_VGA },
    { BLADEXP,          PCI_CHIP_9910,  RES_SHARED_VGA },
    { CYBERBLADEXPAI1,  PCI_CHIP_8820,  RES_SHARED_VGA },
    { CYBERBLADEXP4,    PCI_CHIP_2100,  RES_SHARED_VGA },
    { XP5,              PCI_CHIP_2200,  RES_SHARED_VGA },
    { -1,               -1,             RES_UNDEFINED }
};

typedef enum {
    OPTION_ACCELMETHOD,
    OPTION_SW_CURSOR,
    OPTION_PCI_RETRY,
    OPTION_RGB_BITS,
    OPTION_NOACCEL,
    OPTION_SETMCLK,
    OPTION_MUX_THRESHOLD,
    OPTION_SHADOW_FB,
    OPTION_ROTATE,
    OPTION_MMIO_ONLY,
    OPTION_VIDEO_KEY,
    OPTION_NOMMIO,
    OPTION_NOPCIBURST,
    OPTION_CYBER_SHADOW,
    OPTION_CYBER_STRETCH,
    OPTION_XV_HSYNC,
    OPTION_XV_VSYNC,
    OPTION_XV_BSKEW,
    OPTION_XV_RSKEW,
    OPTION_FP_DELAY,
    OPTION_1400_DISPLAY,
    OPTION_DISPLAY,
    OPTION_GB,
    OPTION_TV_CHIPSET,
    OPTION_TV_SIGNALMODE
} TRIDENTOpts;

static const OptionInfoRec TRIDENTOptions[] = {
    { OPTION_ACCELMETHOD,       "AccelMethod",      OPTV_ANYSTR,    {0}, FALSE },
    { OPTION_SW_CURSOR,         "SWcursor",         OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_PCI_RETRY,         "PciRetry",         OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_NOACCEL,           "NoAccel",          OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_SETMCLK,           "SetMClk",          OPTV_FREQ,      {0}, FALSE },
    { OPTION_MUX_THRESHOLD,     "MUXThreshold",     OPTV_INTEGER,   {0}, FALSE },
    { OPTION_SHADOW_FB,         "ShadowFB",         OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_ROTATE,            "Rotate",           OPTV_ANYSTR,    {0}, FALSE },
    { OPTION_VIDEO_KEY,         "VideoKey",         OPTV_INTEGER,   {0}, FALSE },
    { OPTION_NOMMIO,            "NoMMIO",           OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_NOPCIBURST,        "NoPciBurst",       OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_MMIO_ONLY,         "MMIOonly",         OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_CYBER_SHADOW,      "CyberShadow",      OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_CYBER_STRETCH,     "CyberStretch",     OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_XV_HSYNC,          "XvHsync",          OPTV_INTEGER,   {0}, FALSE },
    { OPTION_XV_VSYNC,          "XvVsync",          OPTV_INTEGER,   {0}, FALSE },
    { OPTION_XV_BSKEW,          "XvBskew",          OPTV_INTEGER,   {0}, FALSE },
    { OPTION_XV_RSKEW,          "XvRskew",          OPTV_INTEGER,   {0}, FALSE },
    { OPTION_FP_DELAY,          "FpDelay",          OPTV_INTEGER,   {0}, FALSE },
    { OPTION_1400_DISPLAY,      "Display1400",      OPTV_BOOLEAN,   {0}, FALSE },
    { OPTION_DISPLAY,           "Display",          OPTV_ANYSTR,    {0}, FALSE },
    { OPTION_GB,                "GammaBrightness",  OPTV_ANYSTR,    {0}, FALSE },
    { OPTION_TV_CHIPSET,        "TVChipset",        OPTV_ANYSTR,    {0}, FALSE },
    { OPTION_TV_SIGNALMODE,     "TVSignal",         OPTV_INTEGER,   {0}, FALSE },
    { -1,                       NULL,               OPTV_NONE,      {0}, FALSE }
};

/* Clock Limits */
static int ClockLimit[] = {
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    80000,
    90000,
    90000,
    135000,
    135000,
    170000,
    170000,
    170000,
    170000,
    170000,
    170000,
    170000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
};

static int ClockLimit16bpp[] = {
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    40000,
    45000,
    45000,
    90000,
    90000,
    135000,
    135000,
    170000,
    170000,
    170000,
    170000,
    170000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
    230000,
}; 

static int ClockLimit24bpp[] = {
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    40000,
    40000,
    70000,
    70000,
    70000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
};

static int ClockLimit32bpp[] = {
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    25180,
    40000,
    40000,
    70000,
    70000,
    70000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
    115000,
};

/*
 * These are fixed modelines for all physical display dimensions the
 *  chipsets supports on FPs. Most of them are not tested yet.
 */
#if 0
tridentLCD LCD[] = {   /* 0    3    4    5   6    7    10   11  16 */
    { 0,"640x480",25200,0x5f,0x82,0x54,0x80,0xb,0x3e,0xea,0x8c,0xb,0x08},
    { 1,"800x600",40000,0x7f,0x99,0x69,0x99,0x72,0xf0,0x59,0x2d,0x5e,0x08},
    { 2,"1024x768",65000,0xa3,/*0x6*/ 0x98,0x8f,0xa0,0x24,0xf5,0x0f,0x25,0x96,0x08},
    { 3,"1024x768",65000,0xa3,/*0x6*/ 0x98,0x8f,0xa0,0x24,0xf5,0x0f,0x25,0x96,0x08}, /*0x96*/
    { 4,"1280x1024",108000,0xa3,0x6,0x8f,0xa0,0x24,0xf5,0x0f,0x25,0x96,0x08},
    { 5,"1024x600",50500 ,0xa3,0x6,0x8f,0xa0,0xb,0x3e,0xea,0x8c,0xb,0x08},
    { 0xff,"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
#else
#if 0
tridentLCD LCD[] = { 
    { 1,640,480,25200,0x5f,0x82,0x54,0x80,0xb,0x3e,0xea,0x8c,0xb,0x08},
    { 3,800,600,40000,0x7f,0x82,0x6b,0x1b,0x72,0xf8,0x58,0x8c,0x72,0x08},
    { 2,1024,768,65000,0xa3,/*0x6*/0x98,0x8f,0xa0,0x24,0xf5,0x0f,0x24,0x0a,0x08},
    { 0,1280,1024,108000,0xce,0x81,0xa6,0x9a,0x27,0x50,0x00,0x03,0x26,0xa8},
    { 0xff,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
#else
tridentLCD LCD[] = { 
    { 1,640,480,25200,0x5f,0x80,0x52,0x1e,0xb,0x3e,0xea,0x0c,0xb,0x08},
    { 3,800,600,40000,0x7f,0x00,0x69,0x7f,0x72,0xf0,0x59,0x0d,0x00,0x08},
    { 2,1024,768,65000,0xa3,0x00,0x84,0x94,0x24,0xf5,0x03,0x09,0x24,0x08},
    { 0,1280,1024,108000,0xce,0x91,0xa6,0x14,0x28,0x5a,0x01,0x04,0x28,0xa8},
    { 4,1400,1050,122000,0xe6,0x8d,0xba,0x1d,0x38,0x00,0x1c,0x28,0x28,0xf8},
    { 0xff,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
#endif
#endif

static MODULESETUPPROTO(tridentSetup);

static XF86ModuleVersionInfo tridentVersRec =
{
    "trident",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    XORG_VERSION_CURRENT,
    TRIDENT_MAJOR_VERSION, TRIDENT_MINOR_VERSION, TRIDENT_PATCHLEVEL,
    ABI_CLASS_VIDEODRV,			/* This is a video driver */
    ABI_VIDEODRV_VERSION,
    MOD_CLASS_VIDEODRV,
    {0,0,0,0}
};

_X_EXPORT XF86ModuleData tridentModuleData = {
    &tridentVersRec,
    tridentSetup,
    NULL
};

void*
tridentSetup(void *module, void *opts, int *errmaj, int *errmin)
{
    static Bool setupDone = FALSE;

    if (!setupDone) {
        setupDone = TRUE;
        xf86AddDriver(&TRIDENT, module, 0);
        return (void*)TRUE;
    } 

    if (errmaj) *errmaj = LDR_ONCEONLY;
    return NULL;
}

static void
TRIDENTEnableMMIO(ScrnInfoPtr pScrn)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    unsigned long vgaIOBase = pTrident->PIOBase + VGAHWPTR(pScrn)->IOBase;
    CARD8 temp = 0, protect = 0;

    /* Goto New Mode */
    outb(pTrident->PIOBase + 0x3C4, 0x0B);
    inb(pTrident->PIOBase + 0x3C5);

    /* Unprotect registers */
    if (pTrident->Chipset > PROVIDIA9685) {
        outb(pTrident->PIOBase + 0x3C4, Protection);
        protect = inb(pTrident->PIOBase + 0x3C5);
        outb(pTrident->PIOBase + 0x3C5, 0x92);
    }
    outb(pTrident->PIOBase + 0x3C4, NewMode1);
    temp = inb(pTrident->PIOBase + 0x3C5);
    outb(pTrident->PIOBase + 0x3C5, 0x80);

    /* Enable MMIO */
    outb(vgaIOBase + 4, PCIReg);
    pTrident->REGPCIReg = inb(vgaIOBase + 5);
    outb(vgaIOBase + 5, pTrident->REGPCIReg | 0x01); /* Enable it */

    /* Protect registers */
    if (pTrident->Chipset > PROVIDIA9685) {
        OUTB(0x3C4, Protection);
        OUTB(0x3C5, protect);
    }
    OUTB(0x3C4, NewMode1);
    OUTB(0x3C5, temp);
}

static void
TRIDENTDisableMMIO(ScrnInfoPtr pScrn)
{
    int vgaIOBase = VGAHWPTR(pScrn)->IOBase;
    CARD8 temp = 0, protect = 0;
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);

    /* Goto New Mode */
    OUTB(0x3C4, 0x0B); temp = INB(0x3C5);

    /* Unprotect registers */
    OUTB(0x3C4, NewMode1); temp = INB(0x3C5);
    OUTB(0x3C5, 0x80);
    if (pTrident->Chipset > PROVIDIA9685) {
        OUTB(0x3C4, Protection);
        protect = INB(0x3C5);
        OUTB(0x3C5, 0x92);
    }

    /* Disable MMIO access */
    OUTB(vgaIOBase + 4, PCIReg);
    pTrident->REGPCIReg = INB(vgaIOBase + 5);
    OUTB(vgaIOBase + 5, pTrident->REGPCIReg & 0xFE);

    /* Protect registers */
    if (pTrident->Chipset > PROVIDIA9685) {
        outb(pTrident->PIOBase + 0x3C4, Protection);
        outb(pTrident->PIOBase + 0x3C5, protect);
    }
    outb(pTrident->PIOBase + 0x3C4, NewMode1);
    outb(pTrident->PIOBase + 0x3C5, temp);
}

/*
 * This is a terrible hack! If we are on a notebook in a stretched
 * mode and don't want full screen we use the BIOS to set an unstreched
 * mode.
 */
void
tridentSetModeBIOS(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);


#ifdef VBE_INFO
    if (pTrident->vbeModes) {
        vbeSaveRestoreRec vbesr;
        vbesr.stateMode = VBECalcVbeModeIndex(pTrident->vbeModes,
                mode, pScrn->bitsPerPixel);
        vbesr.pstate = NULL;
        if (vbesr.stateMode) {
            if (IsPciCard && UseMMIO)
                TRIDENTDisableMMIO(pScrn);
            VBEVesaSaveRestore(pTrident->pVbe,&vbesr,MODE_RESTORE);
            if (IsPciCard && UseMMIO)
                TRIDENTEnableMMIO(pScrn);
            return;
        } else
            xf86DrvMsg(pScrn->scrnIndex,X_WARNING,"No BIOS Mode matches "
                    "%ix%I@%ibpp\n",mode->HDisplay,mode->VDisplay,
                    pScrn->bitsPerPixel);
    }
#endif
    /* This function is only for LCD screens, and also when we have
     * int10 available */

    if (pTrident->IsCyber && pTrident->lcdMode && pTrident->Int10) {
        int i = pTrident->lcdMode;
        if ((pScrn->currentMode->HDisplay != LCD[i].display_x) /* !fullsize? */
                || (pScrn->currentMode->VDisplay != LCD[i].display_y)) {
            if (pTrident->lcdActive)  { /* LCD Active ?*/
                int h_str, v_str;

                OUTB(0x3CE,HorStretch);  h_str = INB(0x3CF) & 0x01;
                OUTB(0x3CE,VertStretch); v_str = INB(0x3CF) & 0x01;
                if (h_str || v_str) {
                    OUTB(0x3C4, 0x11); OUTB(0x3C5, 0x92);
                    OUTW(0x3CE, BiosReg );
                    pTrident->Int10->ax = 0x3;
                    pTrident->Int10->num = 0x10;
                    if (IsPciCard && UseMMIO)
                        TRIDENTDisableMMIO(pScrn);
                    xf86ExecX86int10(pTrident->Int10);
                    if (IsPciCard && UseMMIO)
                        TRIDENTEnableMMIO(pScrn);
                }
            }
        }
    }
}

/* Currently we only test for 1400 */
static int
TRIDENTLcdDisplaySize (xf86MonPtr pMon)
{
    if (pMon) {
        int i,j;

        for (i = 0; i < STD_TIMINGS; i++) {
            if (pMon->timings2[i].hsize == 1400) {
                return 1400;
            }
        }
        /*
         * If not explicitly set try to find out if the display supports
         * the 1400 mode. For sanity check if DDC comes from a digital
         * display.
         */
        if (DIGITAL(pMon->features.input_type)) {
            for (i = 0; i < DET_TIMINGS; i++) {
                if (pMon->det_mon[i].type == DS_STD_TIMINGS) {
                    for (j = 0; j < 5; j++) {
                        if (pMon->det_mon[i].section.std_t[j].hsize == 1400) {
                            return 1400;
                        }
                    }
                } else if (pMon->det_mon[i].type == DT) {
                    if (pMon->det_mon[i].section.d_timings.h_active == 1400) {
                        return 1400;
                    }
                }
            }
        }
    }
    return 0;
}

/*
 * Initialise a new mode.  This is currently still using the old
 * "initialise struct, restore/write struct to HW" model.  That could
 * be changed.
 */

static Bool
TRIDENTModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    vgaRegPtr vgaReg;
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    TRIDENTRegPtr tridentReg;

    WAITFORVSYNC;

    TridentFindClock(pScrn,mode->Clock);

    switch (pTrident->Chipset) {
    case TGUI9660:
    case TGUI9680:
    case PROVIDIA9682:
    case PROVIDIA9685:
    case IMAGE975:
    case IMAGE985:
    case BLADE3D:
    case CYBERBLADEI7:
    case CYBERBLADEI7D:
    case CYBERBLADEI1:
    case CYBERBLADEI1D:
    case CYBERBLADEAI1:
    case CYBERBLADEAI1D:
    case CYBER9520:
    case CYBER9525DVD:
    case CYBERBLADEE4:
    case CYBER9397:
    case CYBER9397DVD:
    case BLADEXP:
    case CYBERBLADEXPAI1:
    case CYBERBLADEXP4:
    case XP5:
        /* Get ready for MUX mode */
        if (pTrident->MUX &&
                pScrn->bitsPerPixel == 8 &&
                !mode->CrtcHAdjusted) {
            ErrorF("BARF\n");
            mode->CrtcHDisplay >>= 1;
            mode->CrtcHSyncStart >>= 1;
            mode->CrtcHSyncEnd >>= 1;
            mode->CrtcHBlankStart >>= 1;
            mode->CrtcHBlankEnd >>= 1;
            mode->CrtcHTotal >>= 1;
            mode->CrtcHAdjusted = TRUE;
        }
        break;
    default:
        if (pScrn->videoRam < 1024 &&
                !mode->CrtcHAdjusted) {
            mode->CrtcHDisplay <<= 1;
            mode->CrtcHSyncStart <<= 1;
            mode->CrtcHSyncEnd <<= 1;
            mode->CrtcHBlankStart <<= 1;
            mode->CrtcHBlankEnd <<= 1;
            mode->CrtcHTotal <<= 1;
            mode->CrtcHAdjusted = TRUE;
        }
        break;
    }

    vgaHWUnlock(hwp);
    /* Initialise the ModeReg values */
    if (!vgaHWInit(pScrn, mode))
        return FALSE;

    pScrn->vtSema = TRUE;
    /*
     * We used to do this at a later time.
     * Now since READOUT isn't defined any more
     * we do it here.
     * The original NOTE read:
     * TridentInit() has to modify registers
     * that have already been set by vgaHWRestore().
     * So we call it _after_ vgaHWRestore() has
     * programmed these registers.
     */
    if (pScrn->progClock) {
        if (!TridentInit(pScrn, mode))
            return FALSE;
    } else {
        if (!TVGAInit(pScrn, mode))
            return FALSE;
    }

    /* Program the registers */
    vgaHWProtect(pScrn, TRUE);
    vgaReg = &hwp->ModeReg;
    tridentReg = &pTrident->ModeReg;

    vgaHWRestore(pScrn, vgaReg, VGA_SR_MODE);
    if (pScrn->progClock)
        TridentRestore(pScrn, tridentReg);
    else
        TVGARestore(pScrn, tridentReg);

    vgaHWProtect(pScrn, FALSE);

    if (pTrident->TVChipset != 0)
        VIA_TVInit(pScrn);

    return TRUE;
}

static Bool
TRIDENTGetRec(ScrnInfoPtr pScrn)
{
    /*
     * Allocate an TRIDENTRec, and hook it into pScrn->driverPrivate.
     * pScrn->driverPrivate is initialised to NULL, so we can check if
     * the allocation has already been done.
     */
    if (pScrn->driverPrivate != NULL)
        return TRUE;

    pScrn->driverPrivate = XNFcallocarray(sizeof(TRIDENTRec), 1);
    /* Initialise it */

    return TRUE;
}

static void
TRIDENTFreeRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate == NULL)
        return;
    free(pScrn->driverPrivate);
    pScrn->driverPrivate = NULL;
}


/*
 * This function saves the video state.
 */
static void
TRIDENTSave(ScrnInfoPtr pScrn)
{
    TRIDENTPtr pTrident;
    vgaRegPtr vgaReg;
    TRIDENTRegPtr tridentReg;

    pTrident = TRIDENTPTR(pScrn);
    vgaReg = &VGAHWPTR(pScrn)->SavedReg;
    tridentReg = &pTrident->SavedReg;

    vgaHWSave(pScrn, vgaReg, VGA_SR_MODE | VGA_SR_CMAP |
            (IsPrimaryCard ? VGA_SR_FONTS : 0));

    if (pScrn->progClock)
        TridentSave(pScrn, tridentReg);
    else
        TVGASave(pScrn, tridentReg);

    if (pTrident->TVChipset != 0)
        VIA_SaveTVDepentVGAReg(pScrn);
}


/*
 * Restore the initial (text) mode.
 */
static void
TRIDENTRestore(ScrnInfoPtr pScrn)
{
    vgaHWPtr hwp;
    vgaRegPtr vgaReg;
    TRIDENTPtr pTrident;
    TRIDENTRegPtr tridentReg;

    hwp = VGAHWPTR(pScrn);
    pTrident = TRIDENTPTR(pScrn);
    vgaReg = &hwp->SavedReg;
    tridentReg = &pTrident->SavedReg;

    vgaHWProtect(pScrn, TRUE);

    if (pScrn->progClock)
        TridentRestore(pScrn, tridentReg);
    else
        TVGARestore(pScrn, tridentReg);

    vgaHWRestore(pScrn, vgaReg, VGA_SR_MODE | VGA_SR_CMAP |
            (IsPrimaryCard ? VGA_SR_FONTS : 0));

    if (pTrident->TVChipset != 0)
        VIA_RestoreTVDependVGAReg(pScrn);

    vgaHWProtect(pScrn, FALSE);
}

/* Usually mandatory */
Bool
TRIDENTSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    return TRIDENTModeInit(pScrn, mode);
}


/*
 * This function is used to initialize the Start Address - the first
 * displayed location in the video memory.
 */
/* Usually mandatory */
void
TRIDENTAdjustFrame(ScrnInfoPtr pScrn, int x, int y)
{
    TRIDENTPtr pTrident;
    int base = y * pScrn->displayWidth + x;
    int vgaIOBase;
    CARD8 temp;

    pTrident = TRIDENTPTR(pScrn);
    vgaIOBase = VGAHWPTR(pScrn)->IOBase;

    switch (pScrn->bitsPerPixel) {
    case 8:
        if (pScrn->progClock)
            base = (base & 0xFFFFFFF8) >> 2;
        else
            base = (base & 0xFFFFFFF8) >> 3;
        break;
    case 16:
        base >>= 1;
        break;
    case 24:
        base = (((base + 1) & ~0x03) * 3) >> 2;
        break;
    case 32:
        break;
    }

    /* CRT bits 0-15 */
    OUTW(vgaIOBase + 4, (base & 0x00FF00) | 0x0C);
    OUTW(vgaIOBase + 4, ((base & 0x00FF) << 8) | 0x0D);
    /* CRT bit 16 */
    OUTB(vgaIOBase + 4, CRTCModuleTest); temp = INB(vgaIOBase + 5) & 0xDF;
    OUTB(vgaIOBase + 5, temp | ((base & 0x10000) >> 11));
    /* CRT bit 17-19 */
    OUTB(vgaIOBase + 4, CRTHiOrd); temp = INB(vgaIOBase + 5) & 0xF8;
    OUTB(vgaIOBase + 5, temp | ((base & 0xE0000) >> 17));
}


/*
 * This is called when VT switching back to the X server.  Its job is
 * to reinitialise the video mode.
 */

/* Mandatory */
static Bool
TRIDENTEnterVT(ScrnInfoPtr pScrn)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);

    if (IsPciCard && UseMMIO) TRIDENTEnableMMIO(pScrn);

    /* Should we re-save the text mode on each VT enter? */
    if (!TRIDENTModeInit(pScrn, pScrn->currentMode))
        return FALSE;

    if (pTrident->InitializeAccelerator)
        pTrident->InitializeAccelerator(pScrn);

    return TRUE;
}


/*
 * This is called when VT switching away from the X server.  Its job is
 * to restore the previous (text) mode.
 *
 * We may wish to remap video/MMIO memory too.
 */

/* Mandatory */
static void
TRIDENTLeaveVT(ScrnInfoPtr pScrn)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    vgaHWPtr hwp = VGAHWPTR(pScrn);

        if (!pTrident->NoAccel && pTrident->useEXA)
            pTrident->EXADriverPtr->WaitMarker(pScrn->pScreen, 0);

    TRIDENTRestore(pScrn);
    vgaHWLock(hwp);

    if (IsPciCard && UseMMIO) TRIDENTDisableMMIO(pScrn);
}


/* Free up any per-generation data structures */

/* Optional */
static void
TRIDENTFreeScreen(ScrnInfoPtr pScrn)
{
    if (xf86LoaderCheckSymbol("vgaHWFreeHWRec"))
        vgaHWFreeHWRec(pScrn);
    TRIDENTFreeRec(pScrn);
}


/* Checks if a mode is suitable for the selected chipset. */

/* Optional */
static ModeStatus
TRIDENTValidMode(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool verbose, int flags)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);

    if (pTrident->lcdActive && (pTrident->lcdMode != 0xff)){
        if (((mode->HDisplay > LCD[pTrident->lcdMode].display_x)
                || (mode->VDisplay > LCD[pTrident->lcdMode].display_y))) {
            xf86DrvMsg(pScrn->scrnIndex,X_INFO, "Removing mode (%dx%d) "
                    "larger than the LCD panel (%dx%d)\n",
                    mode->HDisplay,
                    mode->VDisplay,
                    LCD[pTrident->lcdMode].display_x,
                    LCD[pTrident->lcdMode].display_y);
            return(MODE_BAD);
        }
        if (((float)mode->HDisplay/(float)mode->VDisplay) > 2.0) {
            xf86DrvMsg(pScrn->scrnIndex,X_INFO, "Removing mode (%dx%d) "
                    "unusual aspect ratio\n",
                    mode->HDisplay,
                    mode->VDisplay);
            return(MODE_BAD);
        }
    }
    return (MODE_OK);
}

static void
TRIDENTDisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, int flags)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    CARD8 DPMSCont, PMCont, temp;

    if (!pScrn->vtSema)
        return;

    OUTB(0x3C4, 0x0E);
    temp = INB(0x3C5);
    OUTB(0x3C5, 0xC2);
    OUTB(0x83C8, 0x04); /* Read DPMS Control */
    PMCont = INB(0x83C6) & 0xFC;
    OUTB(0x3CE, 0x23);
    DPMSCont = INB(0x3CF) & 0xFC;
    switch (PowerManagementMode)
    {
    case DPMSModeOn:
        /* Screen: On, HSync: On, VSync: On */
        PMCont |= 0x03;
        DPMSCont |= 0x00;
        break;
    case DPMSModeStandby:
        /* Screen: Off, HSync: Off, VSync: On */
        PMCont |= 0x02;
        DPMSCont |= 0x01;
        break;
    case DPMSModeSuspend:
        /* Screen: Off, HSync: On, VSync: Off */
        PMCont |= 0x02;
        DPMSCont |= 0x02;
        break;
    case DPMSModeOff:
        /* Screen: Off, HSync: Off, VSync: Off */
        PMCont |= 0x00;
        DPMSCont |= 0x03;
        break;
    }
    OUTB(0x3CF, DPMSCont);
    OUTB(0x83C8, 0x04);
    OUTB(0x83C6, PMCont);
    OUTW(0x3C4, (temp<<8) | 0x0E);
}

static void
TRIDENTBlockHandler (ScreenPtr pScreen, void *pTimeout)
{
    ScrnInfoPtr    pScrn = xf86ScreenToScrn(pScreen);
    TRIDENTPtr     pTrident = TRIDENTPTR(pScrn);

    pScreen->BlockHandler = pTrident->BlockHandler;
    (*pScreen->BlockHandler) (pScreen, pTimeout);
    pScreen->BlockHandler = TRIDENTBlockHandler;

    if(pTrident->VideoTimerCallback) {
        UpdateCurrentTime();
        (*pTrident->VideoTimerCallback)(pScrn, currentTime.milliseconds);
    }
}

static const OptionInfoRec *
TRIDENTAvailableOptions(int chipid, int busid)
{
    return TRIDENTOptions;
}

/* Mandatory */
static void
TRIDENTIdentify(int flags)
{
    xf86PrintChipsets(TRIDENT_NAME, "driver for Trident chipsets", TRIDENTChipsets);
}

Bool
TRIDENTClockSelect(ScrnInfoPtr pScrn, int no)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    unsigned char temp;

    /*
     * CS0 and CS1 are in MiscOutReg
     *
     * For 8900B, 8900C, 8900CL and 9000, CS2 is bit 0 of
     * New Mode Control Register 2.
     *
     * For 8900CL, CS3 is bit 4 of Old Mode Control Register 1.
     *
     * For 9000, CS3 is bit 6 of New Mode Control Register 2.
     *
     * For TGUI, we don't use the ClockSelect function at all.
     */
    switch(no) {
    case CLK_REG_SAVE:
        pTrident->SaveClock1 = INB(0x3CC);
        if (pTrident->Chipset != TVGA8800CS) {
            if ( (pScrn->numClocks == 16) &&
                    (pTrident->Chipset != TVGA9000) &&
                    (pTrident->Chipset != TVGA9000i) )
            {
                OUTW(0x3C4, 0x000B);	/* Switch to Old Mode */
                OUTB(0x3C4, 0x0E); pTrident->SaveClock3 = INB(0x3C5);
            }
            OUTB(0x3C4, 0x0B);
            INB(0x3C5);		/* Now to New Mode */
            OUTB(0x3C4, 0x0D); pTrident->SaveClock2 = INB(0x3C5);
        }
        break;
    case CLK_REG_RESTORE:
        OUTB(0x3C2, pTrident->SaveClock1);
        if (pTrident->Chipset != TVGA8800CS) {
            if ( (pScrn->numClocks == 16) &&
                    (pTrident->Chipset != TVGA9000) &&
                    (pTrident->Chipset != TVGA9000i) )
            {
                OUTW(0x3C4, 0x000B);	/* Switch to Old Mode */
                OUTW(0x3C4, (pTrident->SaveClock3 << 8) | 0x0E);
            }
            OUTB(0x3C4, 0x0B);
            INB(0x3C5);		/* Now to New Mode */
            OUTW(0x3C4, (pTrident->SaveClock2 << 8) | 0x0D);
        }
        break;
    default:
        /*
         * Do CS0 and CS1
         */
        temp = INB(0x3CC);
        OUTB(0x3C2, (temp & 0xF3) | ((no << 2) & 0x0C));
        if (pTrident->Chipset != TVGA8800CS) {
            if ( (pScrn->numClocks == 16) &&
                    (pTrident->Chipset != TVGA9000) &&
                    (pTrident->Chipset != TVGA9000i) )
            {
                /*
                 * Go to Old Mode for CS3.
                 */
                OUTW(0x3C4, 0x000B);	/* Switch to Old Mode */
                OUTB(0x3C4, 0x0E);
                temp = INB(0x3C5) & 0xEF;
                temp |= (no & 0x08) << 1;
                OUTB(0x3C5, temp);
            }
            /*
             * Go to New Mode for CS2 and TVGA9000 CS3.
             */
            OUTB(0x3C4, 0x0B);
            INB(0x3C5);		/* Now to New Mode */
            OUTB(0x3C4, 0x0D);
            /*
             * Bits 1 & 2 are dividers - set to 0 to get no
             * clock division.
             */
            temp = INB(0x3C5) & 0xF8;
            temp |= (no & 0x04) >> 2;
            if ( (pTrident->Chipset == TVGA9000) ||
                    (pTrident->Chipset == TVGA9000i) )
            {
                temp &= ~0x40;
                temp |= (no & 0x08) << 3;
            }
            OUTB(0x3C5, temp);
        }
    }
    return(TRUE);
}

/*
 * Map the framebuffer and MMIO memory.
 */

static Bool
TRIDENTMapMem(ScrnInfoPtr pScrn)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    int mapsize = 0x10000;

    if (Is3Dchip) mapsize = 0x20000;

    if (IsPciCard && UseMMIO)
    {
        void **result = (void **)&pTrident->IOBase;
        int err = pci_device_map_range(pTrident->PciInfo,
                pTrident->IOAddress,
                mapsize,
                PCI_DEV_MAP_FLAG_WRITABLE,
                result);
        if (err) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                    "Unable to map IO aperture. %s (%d)\n",
                    strerror(err), err);
        }
    }
    else {
        return FALSE;
    }

    if (pTrident->IOBase == NULL)
        return FALSE;

    if (LINEAR()) {
        if (pTrident->FbMapSize != 0) {
            {
                void **result = (void **)&pTrident->FbBase;
                int err = pci_device_map_range(pTrident->PciInfo,
                        pTrident->FbAddress,
                        pTrident->FbMapSize,
                        PCI_DEV_MAP_FLAG_WRITABLE |
                        PCI_DEV_MAP_FLAG_WRITE_COMBINE,
                        result);
                if (err) {
                    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                            "Unable to map VRAM aperture. %s (%d)\n",
                            strerror(err), err);
                }
            }
            if (pTrident->FbBase == NULL)
                return FALSE;
        }
    }
    else
        pTrident->FbBase = hwp->Base;

    return TRUE;
}

/*
 * Unmap the framebuffer and MMIO memory.
 */

static Bool
TRIDENTUnmapMem(ScrnInfoPtr pScrn)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    int mapsize = 0x10000;

    if (Is3Dchip) mapsize = 0x20000;

    /*
     * Unmap IO registers to virtual address space
     */
    pci_device_unmap_range(pTrident->PciInfo, pTrident->IOBase, mapsize);
    pTrident->IOBase = NULL;

    if (LINEAR()) {
        if (pTrident->FbMapSize != 0) {
            pci_device_unmap_range(pTrident->PciInfo, pTrident->FbBase, pTrident->FbMapSize);
            pTrident->FbBase = NULL;
        }
    }

    return TRUE;
}

/*
 * GetAccelPitchValues -
 *
 * This function returns a list of display width (pitch) values that can
 * be used in accelerated mode.
 */
static int *
GetAccelPitchValues(ScrnInfoPtr pScrn)
{
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    int *linePitches = NULL;
    int lines[4] = { 512, 1024, 2048, 4096 }; /* 9440AGi */
    int i, n = 0;

    if (pTrident->Chipset >= BLADEXP) {
        lines[0] = 1024;
        lines[1] = 2048;
        lines[2] = 4096;
        lines[3] = 8192;
    }

    for (i = 0; i < 4; i++) {
        n++;
        linePitches = XNFrealloc(linePitches, n * sizeof(int));
        linePitches[n - 1] = lines[i];
    }

    /* Mark the end of the list */
    if (n > 0) {
        linePitches = XNFrealloc(linePitches, (n + 1) * sizeof(int));
        linePitches[n] = 0;
    }
    return linePitches;
}

static void
TRIDENTProbeDDC(ScrnInfoPtr pScrn, int index)
{
    vbeInfoPtr pVbe;
    if (xf86LoadSubModule(pScrn, "vbe")) {
        pVbe = VBEInit(NULL,index);
        ConfiguredMonitor = vbeDoEDID(pVbe, NULL);
        vbeFree(pVbe);
    }
}

static void
TRIDENTProtect(ScrnInfoPtr pScrn, Bool on)
{
    vgaHWProtect(pScrn, on);
}

static void
TRIDENTBlankScreen(ScrnInfoPtr pScrn, Bool on)
{
    vgaHWBlankScreen(pScrn, on);
}

/* Mandatory */
static Bool
TRIDENTProbe(DriverPtr drv, int flags)
{
    int i;
    GDevPtr *devSections;
    int *usedChips = NULL;
    int numDevSections;
    int numUsed;
    Bool foundScreen = FALSE;

    if ((numDevSections = xf86MatchDevice(TRIDENT_DRIVER_NAME,
            &devSections)) <= 0) {
        /*
         * There's no matching device section in the config file, so quit
         * now.
         */
        return FALSE;
    }

    /*
     * While we're VGA-dependent, can really only have one such instance, but
     * we'll ignore that.
     */

    /*
     * We need to probe the hardware first.  We then need to see how this
     * fits in with what is given in the config file, and allow the config
     * file info to override any contradictions.
     */

    /*
     * All of the cards this driver supports are PCI, so the "probing" just
     * amounts to checking the PCI data that the server has already collected.
     */
    {
        numUsed = xf86MatchPciInstances(TRIDENT_NAME, PCI_VENDOR_TRIDENT,
                TRIDENTChipsets, TRIDENTPciChipsets, devSections,
                numDevSections, drv, &usedChips);

        if (numUsed > 0) {
            if (flags & PROBE_DETECT)
                foundScreen = TRUE;
            else for (i = 0; i < numUsed; i++) {
                ScrnInfoPtr pScrn = NULL;

                if ((pScrn = xf86ConfigPciEntity(pScrn, 0,usedChips[i],
                        TRIDENTPciChipsets, NULL,
                        NULL, NULL, NULL, NULL))) {
                    /* Fill in what we can of the ScrnInfoRec */
                    pScrn->driverVersion = TRIDENT_VERSION;
                    pScrn->driverName	 = TRIDENT_DRIVER_NAME;
                    pScrn->name		 = TRIDENT_NAME;
                    pScrn->Probe	 = TRIDENTProbe;
                    pScrn->PreInit	 = TRIDENTPreInit;
                    pScrn->ScreenInit	 = TRIDENTScreenInit;
                    pScrn->SwitchMode	 = TRIDENTSwitchMode;
                    pScrn->AdjustFrame	 = TRIDENTAdjustFrame;
                    pScrn->EnterVT	 = TRIDENTEnterVT;
                    pScrn->LeaveVT	 = TRIDENTLeaveVT;
                    pScrn->FreeScreen	 = TRIDENTFreeScreen;
                    pScrn->ValidMode	 = TRIDENTValidMode;
                    foundScreen = TRUE;
                }
            }
            free(usedChips);
        }
    }

    free(devSections);
    return foundScreen;
}

/* Mandatory */
static Bool
TRIDENTPreInit(ScrnInfoPtr pScrn, int flags)
{
    TRIDENTPtr pTrident;
    vgaHWPtr hwp;
    MessageType from;
    CARD8 videoram, videorammask;
    const char *ramtype = NULL, *chipset = NULL;
    Bool Support24bpp;
    int vgaIOBase;
    float mclk;
    double real;
    int i, NoClocks = 16;
    CARD8 revision;
    ClockRangePtr clockRanges;
    Bool ddcLoaded = FALSE;
    xf86MonPtr pMon = NULL;
    const char *s;
    Bool tmp_bool;

    /* Allocate the TRIDENTRec driverPrivate */
    if (!TRIDENTGetRec(pScrn)) {
        return FALSE;
    }
    pTrident = TRIDENTPTR(pScrn);
    pTrident->pScrn = pScrn;

    if (pScrn->numEntities > 1)
        return FALSE;
    /* This is the general case */
    for (i = 0; i<pScrn->numEntities; i++) {
        pTrident->pEnt = xf86GetEntityInfo(pScrn->entityList[i]);
        pTrident->Chipset = pTrident->pEnt->chipset;
        pScrn->chipset = (char *)xf86TokenToString(TRIDENTChipsets,
                pTrident->pEnt->chipset);
        /* This driver can handle ISA and PCI buses */
        if (pTrident->pEnt->location.type == BUS_PCI) {
            pTrident->PciInfo = xf86GetPciInfoForEntity(pTrident->pEnt->index);
            pTrident->Linear = TRUE;
        } else {
            pTrident->Linear = FALSE;
        }
    }

    if (flags & PROBE_DETECT) {
        TRIDENTProbeDDC(pScrn, pTrident->pEnt->index);
        return TRUE;
    }

    /* Set pScrn->monitor */
    pScrn->monitor = pScrn->confScreen->monitor;

    /*
     * The first thing we should figure out is the depth, bpp, etc.
     * Our preference for depth 24 is 24bpp, so tell it that too.
     */
    if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support24bppFb | Support32bppFb |
            SupportConvert32to24 /*| PreferConvert32to24*/)) {
        return FALSE;
    } else {
        /* Check that the returned depth is one we support */
        switch (pScrn->depth) {
        case 8:
            if (pScrn->bitsPerPixel != pScrn->depth) {
                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                        "Given depth (%d)/ fbbpp (%d) is not supported by this driver\n",
                        pScrn->depth, pScrn->bitsPerPixel);
                return FALSE;
            }
            break;
        case 15:
        case 16:
            if (pScrn->bitsPerPixel != 16) {
                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                        "Given depth (%d)/ fbbpp (%d) is not supported by this driver\n",
                        pScrn->depth, pScrn->bitsPerPixel);
                return FALSE;
            }
            break;
        case 24:
            if ((pScrn->bitsPerPixel != 24) && (pScrn->bitsPerPixel != 32)) {
                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                        "Given depth (%d)/ fbbpp (%d) is not supported by this driver\n",
                        pScrn->depth, pScrn->bitsPerPixel);
                return FALSE;
            }
            /* OK */
            break;
        default:
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                    "Given depth (%d) is not supported by this driver\n",
                    pScrn->depth);
            return FALSE;
        }
    }

    xf86PrintDepthBpp(pScrn);

    /* Get the depth24 pixmap format */
    if (pScrn->depth == 24 && pix24bpp == 0)
        pix24bpp = xf86GetBppFromDepth(pScrn, 24);

    /* The vgahw module should be loaded here when needed */
    if (!xf86LoadSubModule(pScrn, "vgahw"))
        return FALSE;

    /*
     * Allocate a vgaHWRec
     */
    if (!vgaHWGetHWRec(pScrn))
        return FALSE;

    hwp = VGAHWPTR(pScrn);
    vgaHWSetStdFuncs(hwp);
    vgaHWGetIOBase(hwp);
    vgaIOBase = hwp->IOBase;

    pTrident->PIOBase = 0;

    /* The ramdac module should be loaded here when needed */
    if (!xf86LoadSubModule(pScrn, "ramdac"))
        return FALSE;

    /*
     * This must happen after pScrn->display has been set because
     * xf86SetWeight references it.
     */
    if (pScrn->depth > 8) {
        /* The defaults are OK for us */
        rgb zeros = {0, 0, 0};

        if (!xf86SetWeight(pScrn, zeros, zeros)) {
            return FALSE;
        } else {
            /* XXX check that weight returned is supported */
            ;
        }
    }

    if (!xf86SetDefaultVisual(pScrn, -1)) {
        return FALSE;
    } else {
        /* We don't currently support DirectColor at > 8bpp */
        if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Given default visual"
                    " (%s) is not supported at depth %d\n",
                    xf86GetVisualName(pScrn->defaultVisual), pScrn->depth);
            return FALSE;
        }
    }

    /*
     * The new cmap layer needs this to be initialised.
     */

    {
        Gamma zeros = {0.0, 0.0, 0.0};

        if (!xf86SetGamma(pScrn, zeros)) {
            return FALSE;
        }
    }

    /* Collect all of the relevant option flags (fill in pScrn->options) */
    xf86CollectOptions(pScrn, NULL);

    /* Process the options */
    if (!(pTrident->Options = malloc(sizeof(TRIDENTOptions))))
        return FALSE;
    memcpy(pTrident->Options, TRIDENTOptions, sizeof(TRIDENTOptions));
    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pTrident->Options);

    /* Set the bits per RGB for 8bpp mode */
    if (pScrn->depth == 8) {
        /* XXX This is here just to test options. */
        /* Default to 8 */
        pScrn->rgbBits = 6;
#if 0
        if (xf86GetOptValInteger(pTrident->Options, OPTION_RGB_BITS,
                &pScrn->rgbBits)) {
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Bits per RGB set to %d\n",
                    pScrn->rgbBits);
        }
#endif
    }
    from = X_DEFAULT;

    pTrident->useEXA = FALSE;
    if ((s = (char *)xf86GetOptValString(pTrident->Options,
            OPTION_ACCELMETHOD))) {
        if (!xf86NameCmp(s, "EXA")) {
            pTrident->useEXA = TRUE;
            from = X_CONFIG;
        }
    }
    xf86DrvMsg(pScrn->scrnIndex, from, "Using %s for acceleration\n",
            pTrident->useEXA ? "EXA" : "none");

    pTrident->HWCursor = TRUE;
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_SW_CURSOR, FALSE)) {
        from = X_CONFIG;
        pTrident->HWCursor = FALSE;
    }
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_NOACCEL, FALSE)) {
        pTrident->NoAccel = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Acceleration disabled\n");
    }
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_PCI_RETRY, FALSE)) {
        pTrident->UsePCIRetry = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry enabled\n");
    }
    pTrident->UsePCIBurst = TRUE;
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_NOPCIBURST, FALSE)) {
        pTrident->UsePCIBurst = FALSE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI Burst disabled\n");
    }
    /* Display Size override moved to DDC section */
    if(xf86GetOptValInteger(pTrident->Options, OPTION_VIDEO_KEY,
            &(pTrident->videoKey))) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "video key set to 0x%x\n",
                pTrident->videoKey);
    } else {
        pTrident->videoKey =  (1 << pScrn->offset.red) |
                (1 << pScrn->offset.green) |
                (((pScrn->mask.blue >> pScrn->offset.blue) - 1)
                        << pScrn->offset.blue);
    }
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_NOMMIO, FALSE)) {
        pTrident->NoMMIO = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "MMIO Disabled\n");
    }
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_MMIO_ONLY, FALSE)) {
        if (pTrident->NoMMIO)
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "MMIO only cannot be set"
                    " with NoMMIO\n");
        else {
            pTrident->MMIOonly = TRUE;
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "MMIO only enabled\n");
        }
    }

    pTrident->dspOverride = 0;
    if ((s = xf86GetOptValString(pTrident->Options, OPTION_DISPLAY))) {
        if(!xf86NameCmp(s, "CRT")) {
            pTrident->dspOverride = CRT_ACTIVE;
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"LCD off CRT on\n");
        } else if (!xf86NameCmp(s, "LCD")) {
            pTrident->dspOverride = LCD_ACTIVE;
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"LCD on CRT off\n");
        } else if (!xf86NameCmp(s, "Dual")) {
            pTrident->dspOverride = LCD_ACTIVE | CRT_ACTIVE;
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"LCD on CRT on\n");
        } else
            xf86DrvMsg(pScrn->scrnIndex,X_ERROR,
                    "%s is an unknown display option\n",s);
    }
    if ((s = xf86GetOptValString(pTrident->Options, OPTION_GB))) {
        int brightness = -1;
        double gamma = -1.0;
        Bool error = FALSE;
        int i;

        i = sscanf(s,"%lf %i",&gamma,&brightness);

        if (i != 2 || brightness == -1 || gamma == -1.0) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                    "Invalid Gamma/Brightness argument: %s\n",s);
            error = TRUE;
        } else {
            if (brightness < 0 || brightness > 128) {
                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                        "brightness out of range [0,128]: %i\n",brightness);
                error = TRUE;
            }
            if (gamma <= 0.0 || gamma > 10.0) {
                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                        "gamma out of range (0,10.0]: %f\n",gamma);
                error = TRUE;
            }
        }

        if (!error) {
            pTrident->GammaBrightnessOn = TRUE;
            pTrident->gamma = gamma;
            pTrident->brightness = brightness;
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"Gamma: %f Brightness: %i\n",
                    gamma,brightness);
        }
    }

    /* The following is a temporary hack */
    pTrident->FPDelay = 7; /* invalid value */
    if (xf86GetOptValInteger(pTrident->Options, OPTION_FP_DELAY,
            &pTrident->FPDelay)) {
        if (pTrident->FPDelay < -2 || pTrident->FPDelay > 5) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "FPDelay %i out if range "
                    "(-2 < FPDelay < 5)\n",pTrident->FPDelay);
            pTrident->FPDelay = 7;
        } else
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "FP Delay set to %i\n",
                    pTrident->FPDelay);
    }
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_CYBER_SHADOW, FALSE)) {
        pTrident->CyberShadow = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Cyber Shadow enabled\n");
    }
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_CYBER_STRETCH, FALSE)) {
        pTrident->CyberStretch = TRUE;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Cyber Stretch enabled\n");
    }

    pTrident->MUXThreshold = 90000; /* 90MHz */
    if (xf86GetOptValInteger(pTrident->Options, OPTION_MUX_THRESHOLD,
            &pTrident->MUXThreshold)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "MUX Threshold set to %d\n",
                pTrident->MUXThreshold);
    }
    pTrident->OverrideHsync = 0;
    if (xf86GetOptValInteger(pTrident->Options, OPTION_XV_HSYNC,
            &pTrident->OverrideHsync)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Xv Hsync set to %d\n",
                pTrident->OverrideHsync);
    }
    pTrident->OverrideVsync = 0;
    if (xf86GetOptValInteger(pTrident->Options, OPTION_XV_VSYNC,
            &pTrident->OverrideVsync)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Xv Vsync set to %d\n",
                pTrident->OverrideVsync);
    }
    pTrident->OverrideHsync = 0;
    if (xf86GetOptValInteger(pTrident->Options, OPTION_XV_HSYNC,
            &pTrident->OverrideHsync)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Xv Hsync set to %d\n",
                pTrident->OverrideHsync);
    }
    pTrident->OverrideVsync = 0;
    if (xf86GetOptValInteger(pTrident->Options, OPTION_XV_VSYNC,
            &pTrident->OverrideVsync)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Xv Vsync set to %d\n",
                pTrident->OverrideVsync);
    }
    pTrident->OverrideRskew = 0;
    if (xf86GetOptValInteger(pTrident->Options, OPTION_XV_RSKEW,
            &pTrident->OverrideRskew)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Xv Rskew set to %d\n",
                pTrident->OverrideRskew);
    }
    pTrident->OverrideBskew = 0;
    if (xf86GetOptValInteger(pTrident->Options, OPTION_XV_BSKEW,
            &pTrident->OverrideBskew)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Xv Bskew set to %d\n",
                pTrident->OverrideBskew);
    }
    if (xf86ReturnOptValBool(pTrident->Options, OPTION_SHADOW_FB, FALSE)) {
        if (!LINEAR())
            xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Ignoring Option SHADOW_FB"
                    " in non-Linear Mode\n");
        else {
            pTrident->ShadowFB = TRUE;
            pTrident->NoAccel = TRUE;
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "Using \"Shadow Framebuffer\" - acceleration disabled\n");
        }
    }
    pTrident->Rotate = 0;
    if ((s = xf86GetOptValString(pTrident->Options, OPTION_ROTATE))) {
        if (!LINEAR())
            xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Ignoring Option ROTATE "
                    "in non-Linear Mode\n");
        else {
            if(!xf86NameCmp(s, "CW")) {
                /* accel is disabled below for shadowFB */
                pTrident->ShadowFB = TRUE;
                pTrident->NoAccel = TRUE;
                pTrident->HWCursor = FALSE;
                pTrident->Rotate = 1;
                xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                        "Rotating screen clockwise - acceleration disabled\n");
            } else if(!xf86NameCmp(s, "CCW")) {
                pTrident->ShadowFB = TRUE;
                pTrident->NoAccel = TRUE;
                pTrident->HWCursor = FALSE;
                pTrident->Rotate = -1;
                xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,  "Rotating screen"
                        "counter clockwise - acceleration disabled\n");
            } else {
                xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "\"%s\" is not a valid"
                        "value for Option \"Rotate\"\n", s);
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Valid options are \"CW\" or \"CCW\"\n");
            }
        }
    }

    pTrident->TVChipset = 0;
    if ((s = xf86GetOptValString(pTrident->Options, OPTION_TV_CHIPSET))) {
        if(!xf86NameCmp(s, "VT1621")) {
            pTrident->TVChipset = 1;
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"Using VIA VT1621 TV chip\n");
        } else if (!xf86NameCmp(s, "CH7005")) {
            pTrident->TVChipset = 2;
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"Using Chrontel CH7005 TV chip\n");
        } else
            xf86DrvMsg(pScrn->scrnIndex,X_ERROR,
                    "%s is an unknown TV chipset option\n",s);
    }
    /* Default : NTSC */
    pTrident->TVSignalMode=0;
    if (xf86GetOptValInteger(pTrident->Options, OPTION_TV_SIGNALMODE,
            &pTrident->TVSignalMode)) {
        ErrorF("TV SignalMode set to %d\n",pTrident->TVSignalMode);
    }

    /* FIXME ACCELERATION */
    if (!UseMMIO) pTrident->NoAccel = TRUE;

    if (LINEAR()) {
        if (pTrident->pEnt->device->MemBase != 0) {
            /*
             * XXX Should check that the config file value matches one of the
             * PCI base address values.
             */
            pTrident->FbAddress = pTrident->pEnt->device->MemBase;
            from = X_CONFIG;
        } else {
            if (IsPciCard)
                pTrident->FbAddress = PCI_REGION_BASE(pTrident->PciInfo, 0, REGION_MEM) & 0xFFFFFFF0;
            else
                pTrident->FbAddress = 0xA0000;
        }

        xf86DrvMsg(pScrn->scrnIndex, from, "Linear framebuffer at 0x%lX\n",
                (unsigned long)pTrident->FbAddress);
    }

    if (UseMMIO) {
        if (pTrident->pEnt->device->IOBase != 0) {
            /*
             * XXX Should check that the config file value matches one of the
             * PCI base address values.
             */
            pTrident->IOAddress = pTrident->pEnt->device->IOBase;
            from = X_CONFIG;
        } else {
            if (IsPciCard)
                pTrident->IOAddress = PCI_REGION_BASE(pTrident->PciInfo, 1, REGION_MEM) & 0xFFFFC000;
            else
                /* FIXME - Multihead UNAWARE */
                pTrident->IOAddress = 0xBF000;
        }

        xf86DrvMsg(pScrn->scrnIndex,X_PROBED,"IO registers at 0x%lX\n",
                (unsigned long)pTrident->IOAddress);
    }

    /* Initialize VBE if possible
     * Don't move this past MMIO enable!!
     * PIO access will be blocked
     * when MMIO is turned on!
     */

    if (xf86LoadSubModule(pScrn, "vbe")) {
        vbeInfoPtr pVbe;

        pVbe =  VBEInit(NULL,pTrident->pEnt->index);
        pMon = vbeDoEDID(pVbe, NULL);
#ifdef VBE_INFO
        {
            VbeInfoBlock* vbeInfoBlockPtr;
            if ((vbeInfoBlockPtr = VBEGetVBEInfo(pVbe))) {
                pTrident->vbeModes = VBEBuildVbeModeList(pVbe,vbeInfoBlockPtr);
                VBEFreeVBEInfo(vbeInfoBlockPtr);
            }
        }
#endif
        vbeFree(pVbe);
        if (pMon) {
            if (!xf86LoadSubModule(pScrn, "ddc")) {
                TRIDENTFreeRec(pScrn);
                return FALSE;
            } else {
                xf86SetDDCproperties(pScrn,xf86PrintEDID(pMon));
                ddcLoaded = TRUE;
            }
        }

    }

    if (xf86GetOptValBool(pTrident->Options, OPTION_1400_DISPLAY, &tmp_bool)) {
        if (tmp_bool)
            pTrident->displaySize = 1400;
    } else
        pTrident->displaySize = TRIDENTLcdDisplaySize(pMon);

    if (IsPciCard && UseMMIO) {
        if (!TRIDENTMapMem(pScrn))
            return FALSE;

        TRIDENTEnableMMIO(pScrn);
    }

    OUTB(0x3C4, RevisionID); revision = INB(0x3C5);
    xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Revision is %d\n",revision);

    pScrn->progClock = TRUE;
    pTrident->EngineOperation = 0x00;
    pTrident->IsCyber = FALSE;
    pTrident->HasSGRAM = FALSE;
    pTrident->NewClockCode = FALSE;
    pTrident->MUX = FALSE;
    Support24bpp = FALSE;

    OUTB(vgaIOBase + 4, InterfaceSel);

    switch (pTrident->Chipset) {
    case TVGA9000:
    case TVGA9000i:
        pScrn->progClock = FALSE;
        NoClocks = 16;
        pTrident->NoMMIO = TRUE;
        pTrident->NoAccel = TRUE;
        pTrident->HWCursor = FALSE;
        chipset = "TVGA9000/9000i";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case TVGA9100B:
        pScrn->progClock = FALSE;
        NoClocks = 8;
        pTrident->NoMMIO = TRUE;
        pTrident->NoAccel = TRUE;
        pTrident->HWCursor = FALSE;
        chipset = "TVGA9100B";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case TVGA8900B:
        pScrn->progClock = FALSE;
        NoClocks = 8;
        pTrident->NoMMIO = TRUE;
        pTrident->NoAccel = TRUE;
        pTrident->HWCursor = FALSE;
        chipset = "TVGA8900B";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case TVGA8900C:
        pScrn->progClock = FALSE;
        NoClocks = 16;
        pTrident->NoMMIO = TRUE;
        pTrident->NoAccel = TRUE;
        pTrident->HWCursor = FALSE;
        chipset = "TVGA8900C";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case TVGA8900D:
        pScrn->progClock = FALSE;
        NoClocks = 16;
        pTrident->NoMMIO = TRUE;
        pTrident->NoAccel = TRUE;
        pTrident->HWCursor = FALSE;
        chipset = "TVGA8900D";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case TVGA9200CXr:
        pScrn->progClock = FALSE;
        NoClocks = 16;
        pTrident->NoMMIO = TRUE;
        pTrident->NoAccel = TRUE;
        pTrident->HWCursor = FALSE;
        chipset = "TVGA9200CXr";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case TGUI9400CXi:
        pScrn->progClock = FALSE;
        NoClocks = 16;
        pTrident->NoMMIO = TRUE;
        pTrident->NoAccel = TRUE;
        pTrident->HWCursor = FALSE;
        chipset = "TVGA9200CXr";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case TGUI9440AGi:
        pTrident->ddc1Read = Tridentddc1Read;
        pTrident->HWCursor = FALSE;
        chipset = "TGUI9440AGi";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        pTrident->frequency = NTSC;
        break;
    case CYBER9320:
        pTrident->ddc1Read = Tridentddc1Read;
        chipset = "Cyber9320";
        ramtype = "Standard DRAM";
        if (pTrident->UsePCIRetry)
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
        pTrident->UsePCIRetry = FALSE; /* Not Supported */
        break;
        /* Trident didn't update the PCI ID's and so we have to determine
         * which chips are right ! Then override pTrident->Chipset to
         * correct values */
    case TGUI9660:
        pTrident->ddc1Read = Tridentddc1Read;
        if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
            ramtype = "EDO Ram";
        if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C)
            ramtype = "Standard DRAM";
        switch (revision) {
        case 0x00:
            chipset = "TGUI9660";
            pTrident->Chipset = TGUI9660;
            if (pTrident->UsePCIRetry)
                xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
            pTrident->UsePCIRetry = FALSE; /* Not Supported */
            break;
        case 0x01:
            chipset = "TGUI9680";
            pTrident->Chipset = TGUI9680;
            if (pTrident->UsePCIRetry)
                xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry not supported, disabling\n");
            pTrident->UsePCIRetry = FALSE; /* Not Supported */
            break;
        case 0x10:
            chipset = "ProVidia 9682";
            Support24bpp = TRUE;
            pTrident->Chipset = PROVIDIA9682;
            break;
        case 0x21:
            chipset = "ProVidia 9685";
            Support24bpp = TRUE;
            pTrident->NewClockCode = TRUE;
            pTrident->Chipset = PROVIDIA9685;
            break;
        case 0x22:
        case 0x23:
            chipset = "Cyber 9397";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->NewClockCode = TRUE;
            pTrident->Chipset = CYBER9397;
            pTrident->IsCyber = TRUE;
            break;
        case 0x2a:
            chipset = "Cyber 9397/DVD";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->NewClockCode = TRUE;
            pTrident->Chipset = CYBER9397DVD;
            pTrident->IsCyber = TRUE;
            break;
        case 0x30:
        case 0x33:
        case 0x34:
        case 0x35:
        case 0xB3:
            chipset = "Cyber 9385";
            pTrident->NewClockCode = TRUE;
            pTrident->Chipset = CYBER9385;
            pTrident->IsCyber = TRUE;
            break;
        case 0x38:
        case 0x3A:
            chipset = "Cyber 9385-1";
            pTrident->NewClockCode = TRUE;
            pTrident->Chipset = CYBER9385;
            pTrident->IsCyber = TRUE;
            break;
        case 0x40:
        case 0x41:
        case 0x42:
        case 0x43:
            chipset = "Cyber 9382";
            pTrident->NewClockCode = TRUE;
            pTrident->Chipset = CYBER9382;
            pTrident->IsCyber = TRUE;
            break;
        case 0x4A:
            chipset = "Cyber 9388";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->NewClockCode = TRUE;
            pTrident->Chipset = CYBER9388;
            pTrident->IsCyber = TRUE;
            break;
        default:
            chipset = "Unknown";
            pTrident->Chipset = TGUI9660;
            break;
        }
        break;
        case CYBER9388:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "Cyber 9388";
            pTrident->NewClockCode = TRUE;
            break;
        case CYBER9397:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "Cyber 9397";
            pTrident->NewClockCode = TRUE;
            break;
        case CYBER9397DVD:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "Cyber 9397/DVD";
            pTrident->NewClockCode = TRUE;
            break;
        case CYBER9520:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "Cyber 9520";
            pTrident->NewClockCode = TRUE;
            break;
        case CYBER9525DVD:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "Cyber 9525/DVD";
            pTrident->NewClockCode = TRUE;
            break;
        case CYBERBLADEE4:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SDRAM";
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBlade e4/128";
            pTrident->NewClockCode = TRUE;
            break;
        case IMAGE975:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            Support24bpp = TRUE;
            chipset = "3DImage975";
            pTrident->NewClockCode = TRUE;
            break;
        case IMAGE985:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x04)
                ramtype = "EDO Ram";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            Support24bpp = TRUE;
            chipset = "3DImage985";
            pTrident->NewClockCode = TRUE;
            break;
        case BLADE3D:
            pTrident->ddc1Read = Tridentddc1Read;
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x08)
                ramtype = "SDRAM";
            if ((INB(vgaIOBase + 5) & 0x0C) == 0x0C) {
                pTrident->HasSGRAM = TRUE;
                ramtype = "SGRAM";
            }
            Support24bpp = TRUE;
            chipset = "Blade3D";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            pTrident->UsePCIRetry = TRUE; /* To avoid lockups */
            break;
        case CYBERBLADEI7:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SDRAM";
            /* pTrident->IsCyber = TRUE; VIA MVP4 integrated Desktop version */
            Support24bpp = TRUE;
            chipset = "CyberBlade/i7/VIA MVP4";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case CYBERBLADEI7D:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SDRAM";
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBlade/DSTN/i7";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case CYBERBLADEI1:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SDRAM";
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBlade/i1";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case CYBERBLADEI1D:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SDRAM";
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBlade/DSTN/i1";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case CYBERBLADEAI1:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SDRAM";
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBlade/Ai1";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case CYBERBLADEAI1D:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SDRAM";
            pTrident->IsCyber = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBlade/DSTN/Ai1";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case BLADEXP: /* 0x9910 */
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SGRAM";
            pTrident->HasSGRAM = TRUE;
            Support24bpp = TRUE;
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            OUTB(0x3C4, 0x5D);
            if (PCI_SUB_VENDOR_ID(pTrident->PciInfo) != 0x1023) {
                chipset = "CyberBladeXP";
                pTrident->IsCyber = TRUE;
            } else
                if (!(INB(0x3C5) & 0x01)) {
                    chipset = "BladeXP";
                } else {
                    CARD8 mem1, mem2;
                    OUTB(vgaIOBase + 0x04, SPR);
                    mem1 = INB(vgaIOBase + 5);
                    OUTB(vgaIOBase + 0x04, 0xC1);
                    mem2 = INB(vgaIOBase + 5);
                    if ((mem1 & 0x0e) && (mem2 == 0x11)) {
                        chipset = "BladeT64";
                    } else {
                        chipset = "BladeT16";
                    }
                }
            break;
        case CYBERBLADEXPAI1:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SGRAM";
            pTrident->HasSGRAM = TRUE;
            pTrident->IsCyber = TRUE;
            pTrident->shadowNew = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBladeXPAi1";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case CYBERBLADEXP4:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SGRAM";
            pTrident->HasSGRAM = TRUE;
            pTrident->IsCyber = TRUE;
            pTrident->shadowNew = TRUE;
            Support24bpp = TRUE;
            chipset = "CyberBladeXP4";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
        case XP5:
            pTrident->ddc1Read = Tridentddc1Read;
            ramtype = "SGRAM";
            pTrident->HasSGRAM = TRUE;
            pTrident->IsCyber = TRUE;
            pTrident->shadowNew = TRUE;
            Support24bpp = TRUE;
            chipset = "XP5";
            pTrident->NewClockCode = TRUE;
            pTrident->frequency = NTSC;
            break;
    }

    if (!pScrn->progClock) {
        pScrn->numClocks = NoClocks;
        xf86GetClocks(pScrn, NoClocks, TRIDENTClockSelect,
                TRIDENTProtect, TRIDENTBlankScreen,
                vgaIOBase + 0x0A, 0x08, 1, 28322);
        from = X_PROBED;
        xf86ShowClocks(pScrn, from);
    }

    if (!chipset) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No support for \"%s\"\n",
                pScrn->chipset);
        if (IsPciCard && UseMMIO) {
            TRIDENTDisableMMIO(pScrn);
            TRIDENTUnmapMem(pScrn);
        }
        return FALSE;
    }
    xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Found %s chip\n", chipset);
    if (ramtype)
        xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "RAM type is %s\n", ramtype);

    if (pScrn->bitsPerPixel == 24 && !Support24bpp) {
        xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "No support for 24bpp on this chipset, use -pixmap32.\n");
        if (IsPciCard && UseMMIO) {
            TRIDENTDisableMMIO(pScrn);
            TRIDENTUnmapMem(pScrn);
        }
        return FALSE;
    }

    /* HW bpp matches reported bpp */
    pTrident->HwBpp = pScrn->bitsPerPixel;

    /* Due to bugs in the chip, turn it off */
    if (pTrident->Chipset >= CYBERBLADEI7 && pTrident->Chipset <= CYBERBLADEAI1D)
        pTrident->HWCursor = FALSE;

    from = X_PROBED;
    if (pTrident->pEnt->device->videoRam != 0) {
        pScrn->videoRam = pTrident->pEnt->device->videoRam;
        from = X_CONFIG;
    } else {
        if (pTrident->Chipset == XP5) {
            OUTB(vgaIOBase + 4, 0x60);
            videoram = INB(vgaIOBase + 5);
            switch (videoram & 0x7) {
            case 0x00:
                pScrn->videoRam = 65536 /* 131072 */;
                break;
            case 0x01:
                pScrn->videoRam = 65536;
                break;
            case 0x02:
                pScrn->videoRam = 32768;
                break;
            case 0x03:
                pScrn->videoRam = 16384;
                break;
            case 0x04:
                pScrn->videoRam = 8192;
                break;
            }
        } else
            if (pTrident->Chipset == CYBER9525DVD) {
                pScrn->videoRam = 2560;
            } else
            {
                OUTB(vgaIOBase + 4, SPR);
                videoram = INB(vgaIOBase + 5);
                if (pTrident->Chipset < TGUI9440AGi)
                    videorammask = 0x07;
                else
                    videorammask = 0x0F;
                switch (videoram & videorammask) {
                case 0x01:
                    pScrn->videoRam = 512;
                    break;
                case 0x02: /* XP */
                    pScrn->videoRam = 6144;
                    break;
                case 0x03:
                    pScrn->videoRam = 1024;
                    break;
                case 0x04:
                    pScrn->videoRam = 8192;
                    break;
                case 0x06: /* XP */
                    pScrn->videoRam = 10240;
                    break;
                case 0x07:
                    pScrn->videoRam = 2048;
                    break;
                case 0x08: /* XP */
                    pScrn->videoRam = 12288;
                    break;
                case 0x0A: /* XP */
                    pScrn->videoRam = 14336;
                    break;
                case 0x0C: /* XP */
                    pScrn->videoRam = 16384;
                    break;
                case 0x0E: /* XP */
                    OUTB(vgaIOBase + 4, 0xC1);
                    switch (INB(vgaIOBase + 5) & 0x11) {
                    case 0x00:
                        pScrn->videoRam = 20480;
                        break;
                    case 0x01:
                        pScrn->videoRam = 24576;
                        break;
                    case 0x10:
                        pScrn->videoRam = 28672;
                        break;
                    case 0x11:
                        pScrn->videoRam = 32768;
                        break;
                    }
                    break;
                    case 0x0F:
                        pScrn->videoRam = 4096;
                        break;
                    default:
                        pScrn->videoRam = 1024;
                        xf86DrvMsg(pScrn->scrnIndex, from,
                                "Unable to determine VideoRam, defaulting to 1MB\n");
                        break;
                }
            }
    }

    xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
            pTrident->HWCursor ? "HW" : "SW");

    xf86DrvMsg(pScrn->scrnIndex, from, "VideoRAM: %d kByte\n",
            pScrn->videoRam);

    if (pTrident->IsCyber) {
        unsigned char mod, dsp, dsp1;

        pTrident->lcdMode = 0xff;

        OUTB(0x3CE,0x42);
        dsp = INB(0x3CF);
        OUTB(0x3CE,0x43);
        dsp1 = INB(0x3CF);
        OUTB(0x3CE,0x52);
        mod = INB(0x3CF);
        /*
         * Only allow display size override if 1280x1024 is detected
         * Currently 1400x1050 is supported - which is detected as
         * 1280x1024
         */
        if (pTrident->displaySize) {
            if (((mod >> 4) & 3) == 0) {
                for (i = 0; LCD[i].mode != 0xff; i++) {
                    if (pTrident->displaySize == LCD[i].display_x)
                        pTrident->lcdMode = LCD[i].mode;
                }
                xf86DrvMsg(pScrn->scrnIndex,
                        X_INFO,"%s Panel %ix%i found\n",
                        (dsp & 0x80) ? "TFT" :
                                ((dsp1 & 0x20) ? "DSTN" : "STN"),
                                LCD[i].display_x,LCD[i].display_y);
            } else {
                xf86DrvMsg(pScrn->scrnIndex,X_WARNING,
                        "Display size override only for 1280x1024\n");
                pTrident->displaySize = 0;
            }
        }

        if (!pTrident->displaySize) {
            for (i = 0; LCD[i].mode != 0xff; i++) {
                if (LCD[i].mode == ((mod >> 4) & 3)) {
                    pTrident->lcdMode = i;
                    xf86DrvMsg(pScrn->scrnIndex,
                            X_PROBED,"%s Panel %ix%i found\n",
                            (dsp & 0x80) ? "TFT" :
                                    ((dsp1 & 0x20) ? "DSTN" : "STN"),
                                    LCD[i].display_x,LCD[i].display_y);
                }
            }
        }
        if (pTrident->dspOverride) {
            if (pTrident->dspOverride & LCD_ACTIVE)
                pTrident->lcdActive = TRUE;
            else
                pTrident->lcdActive = FALSE;
        } else {
            OUTB(0x3CE, FPConfig);
            pTrident->lcdActive = (INB(0x3CF) & 0x10);
        }
    }

    pTrident->MCLK = 0;
    mclk = CalculateMCLK(pScrn);
    xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Memory Clock is %3.2f MHz\n", mclk);
    if (xf86GetOptValFreq(pTrident->Options, OPTION_SETMCLK, OPTUNITS_MHZ,
            &real)) {
        pTrident->MCLK = (int)(real * 1000.0);
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Setting new Memory Clock to %3.2f MHz\n",
                (float)(pTrident->MCLK / 1000));
    }

    /* Set the min pixel clock */
    pTrident->MinClock = 12000;	/* XXX Guess, need to check this */
    xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "Min pixel clock is %d MHz\n",
            pTrident->MinClock / 1000);

    /*
     * If the user has specified ramdac speed in the XF86Config
     * file, we respect that setting.
     */
    if (pTrident->pEnt->device->dacSpeeds[0]) {
        int speed = 0;

        switch (pScrn->bitsPerPixel) {
        case 8:
            speed = pTrident->pEnt->device->dacSpeeds[DAC_BPP8];
            break;
        case 16:
            speed = pTrident->pEnt->device->dacSpeeds[DAC_BPP16];
            break;
        case 24:
            speed = pTrident->pEnt->device->dacSpeeds[DAC_BPP24];
            break;
        case 32:
            speed = pTrident->pEnt->device->dacSpeeds[DAC_BPP32];
            break;
        }
        if (speed == 0)
            pTrident->MaxClock = pTrident->pEnt->device->dacSpeeds[0];
        else
            pTrident->MaxClock = speed;
        from = X_CONFIG;
    } else {
        switch (pScrn->bitsPerPixel) {
        case 16:
            pTrident->MaxClock = ClockLimit16bpp[pTrident->Chipset];
            break;
        case 24:
            pTrident->MaxClock = ClockLimit24bpp[pTrident->Chipset];
            break;
        case 32:
            pTrident->MaxClock = ClockLimit32bpp[pTrident->Chipset];
            break;
        default:
            pTrident->MaxClock = ClockLimit[pTrident->Chipset];
            break;
        }
    }
    xf86DrvMsg(pScrn->scrnIndex, from, "Max pixel clock is %d MHz\n",
            pTrident->MaxClock / 1000);

    /*
     * Setup the ClockRanges, which describe what clock ranges are available,
     * and what sort of modes they can be used for.
     */
    clockRanges = XNFcallocarray(sizeof(ClockRange), 1);
    clockRanges->next = NULL;
    if (!pScrn->progClock) {
        if (pScrn->videoRam < 1024)
            clockRanges->ClockMulFactor = 2;
        if (pScrn->bitsPerPixel == 16)
            clockRanges->ClockMulFactor = 2;
    }
    clockRanges->minClock = pTrident->MinClock;
    clockRanges->maxClock = pTrident->MaxClock;
    clockRanges->clockIndex = -1;		/* programmable */
    clockRanges->interlaceAllowed = TRUE;
    clockRanges->doubleScanAllowed = TRUE;

    /*
     * xf86ValidateModes will check that the mode HTotal and VTotal values
     * don't exceed the chipset's limit if pScrn->maxHValue and
     * pScrn->maxVValue are set.  Since our TRIDENTValidMode() already takes
     * care of this, we don't worry about setting them here.
     */

    if (pScrn->bitsPerPixel == 24) {
        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
                "Disabling Engine due to 24bpp.\n");
        pTrident->NoAccel = TRUE;
    }

    /* Select valid modes from those available */
    if (pTrident->NoAccel || Is3Dchip) {
        /*
         * XXX Assuming min pitch 256, max 4096
         * XXX Assuming min height 128, max 4096
         */
        i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
                pScrn->display->modes, clockRanges,
                NULL, 256, 4096,
                pScrn->bitsPerPixel, 128, 4096,
                pScrn->display->virtualX,
                pScrn->display->virtualY,
                pTrident->FbMapSize,
                LOOKUP_BEST_REFRESH);
    } else {
        /*
         * XXX Assuming min height 128, max 2048
         */
        i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
                pScrn->display->modes, clockRanges,
                GetAccelPitchValues(pScrn), 0, 0,
                pScrn->bitsPerPixel, 128, 2048,
                pScrn->display->virtualX,
                pScrn->display->virtualY,
                pTrident->FbMapSize,
                LOOKUP_BEST_REFRESH);
    }

    if (i == -1) {
        if (IsPciCard && UseMMIO) {
            TRIDENTDisableMMIO(pScrn);
            TRIDENTUnmapMem(pScrn);
        }
        TRIDENTFreeRec(pScrn);
        return FALSE;
    }

    /* Prune the modes marked as invalid */
    xf86PruneDriverModes(pScrn);

    if (i == 0 || pScrn->modes == NULL) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n");
        if (IsPciCard && UseMMIO) {
            TRIDENTDisableMMIO(pScrn);
            TRIDENTUnmapMem(pScrn);
        }
        TRIDENTFreeRec(pScrn);
        return FALSE;
    }

    xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V);

    /* Set the current mode to the first in the list */
    pScrn->currentMode = pScrn->modes;

    /* Print the list of modes being used */
    xf86PrintModes(pScrn);

    /* Set display resolution */
    xf86SetDpi(pScrn, 0, 0);

    /* Load bpp-specific modules */
    switch (pScrn->bitsPerPixel) {
    case 8:
        pTrident->EngineOperation |= 0x00;
        break;
    case 16:
        pTrident->EngineOperation |= 0x01;
        break;
    case 24:
        pTrident->EngineOperation |= 0x03;
        break;
    case 32:
        pTrident->EngineOperation |= 0x02;
        break;
    }

    if (xf86LoadSubModule(pScrn, "fb") == NULL) {
        if (IsPciCard && UseMMIO) {
            TRIDENTDisableMMIO(pScrn);
            TRIDENTUnmapMem(pScrn);
        }
        TRIDENTFreeRec(pScrn);
        return FALSE;
    }

    if (!xf86LoadSubModule(pScrn, "i2c")) {
        if (IsPciCard && UseMMIO) {
            TRIDENTDisableMMIO(pScrn);
            TRIDENTUnmapMem(pScrn);
        }
        TRIDENTFreeRec(pScrn);
        return FALSE;
    }

    /* Load EXA if needed */
    if (!pTrident->NoAccel) {
        if (!pTrident->useEXA) {
            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                       "Falling back to shadowfb\n");
            pTrident->NoAccel = 1;
            pTrident->ShadowFB = 1;
        }

        if (pTrident->useEXA) {
            XF86ModReqInfo req;
            int errmaj, errmin;

            memset(&req, 0, sizeof(req));

            req.majorversion = 2;
            if (!LoadSubModule(pScrn->module, "exa", NULL, NULL, NULL, &req,
                    &errmaj, &errmin))
            {
                LoaderErrorMsg(NULL, "exa", errmaj, errmin);
                if (IsPciCard && UseMMIO) {
                    TRIDENTDisableMMIO(pScrn);
                    TRIDENTUnmapMem(pScrn);
                }
                TRIDENTFreeRec(pScrn);
                return FALSE;
            }
        }

        switch (pScrn->displayWidth * pScrn->bitsPerPixel / 8) {
        case 512:
        case 8192:
            pTrident->EngineOperation |= 0x00;
            break;
        case 1024:
            pTrident->EngineOperation |= 0x04;
            break;
        case 2048:
            pTrident->EngineOperation |= 0x08;
            break;
        case 4096:
            pTrident->EngineOperation |= 0x0C;
            break;
        }
    }

    /* Load shadow if needed */
    if (pTrident->ShadowFB) {
        if (!xf86LoadSubModule(pScrn, "shadow")) {
            TRIDENTFreeRec(pScrn);
            return FALSE;
        }
    }

    /* Load DDC if needed */
    /* This gives us DDC1 - we should be able to get DDC2B using i2c */

    if (! ddcLoaded)
        if (!xf86LoadSubModule(pScrn, "ddc")) {
            if (IsPciCard && UseMMIO) {
                TRIDENTDisableMMIO(pScrn);
                TRIDENTUnmapMem(pScrn);
            }
            TRIDENTFreeRec(pScrn);
            return FALSE;
        }

    if (IsPciCard && UseMMIO) {
        TRIDENTDisableMMIO(pScrn);
        TRIDENTUnmapMem(pScrn);
    }

    pTrident->FbMapSize = pScrn->videoRam * 1024;

    return TRUE;
}


/*
 * This is called at the end of each server generation.  It restores the
 * original (text) mode.  It should really also unmap the video memory too.
 */

/* Mandatory */
static Bool
TRIDENTCloseScreen(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);

    if (pScrn->vtSema) {
            if (!pTrident->NoAccel && pTrident->useEXA)
                pTrident->EXADriverPtr->WaitMarker(pScreen, 0);

        TRIDENTRestore(pScrn);
        vgaHWLock(hwp);
        if (IsPciCard && UseMMIO) TRIDENTDisableMMIO(pScrn);
        TRIDENTUnmapMem(pScrn);
    }

    if (pTrident->EXADriverPtr) {
        exaDriverFini(pScreen);
        free(pTrident->EXADriverPtr);
        pTrident->EXADriverPtr = NULL;
    }
    if (pTrident->CursorInfoRec)
        xf86DestroyCursorInfoRec(pTrident->CursorInfoRec);
    if (pTrident->ShadowPtr) {
        shadowRemove(pScreen, pScreen->GetScreenPixmap(pScreen));
        free(pTrident->ShadowPtr);
        pScreen->CreateScreenResources = pTrident->CreateScreenResources;
    }
    if (pTrident->DGAModes)
        free(pTrident->DGAModes);
    pScrn->vtSema = FALSE;

    if(pTrident->BlockHandler)
        pScreen->BlockHandler = pTrident->BlockHandler;

    if (pTrident->pVbe)
        vbeFree(pTrident->pVbe);
    else
        xf86FreeInt10(pTrident->Int10);
    pScreen->CloseScreen = pTrident->CloseScreen;
    return (*pScreen->CloseScreen)(pScreen);
}

/* Do screen blanking */

/* Mandatory */
static Bool
TRIDENTSaveScreen(ScreenPtr pScreen, int mode)
{
    return vgaHWSaveScreen(pScreen, mode);
}


static Bool
TRIDENTCreateScreenResources(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    Bool ret;

    pScreen->CreateScreenResources = pTrident->CreateScreenResources;
    ret = pScreen->CreateScreenResources(pScreen);
    pTrident->CreateScreenResources = pScreen->CreateScreenResources;
    pScreen->CreateScreenResources = TRIDENTCreateScreenResources;

    if (ret)
        ret = shadowAdd(pScreen, pScreen->GetScreenPixmap(pScreen),
                TRIDENTShadowUpdate, NULL, 0, 0);

    return ret;
}

/* Mandatory */

/* This gets called at the start of each server generation */

static Bool
TRIDENTScreenInit(ScreenPtr pScreen, int argc, char **argv)
{
    /* The vgaHW references will disappear one day */
    ScrnInfoPtr pScrn;
    vgaHWPtr hwp;
    TRIDENTPtr pTrident;
    int ret;
    VisualPtr visual;
    unsigned char *FBStart;
    int width, height, displayWidth;

    /*
     * First get the ScrnInfoRec
     */
    pScrn = xf86ScreenToScrn(pScreen);
    pTrident = TRIDENTPTR(pScrn);

    if (IsPrimaryCard) {
        if (!vgaHWMapMem(pScrn))
            return FALSE;
    }

    /* Map the TRIDENT memory and MMIO areas */
    if (!TRIDENTMapMem(pScrn))
        return FALSE;

#ifdef VBE_INFO
    if (pTrident->vbeModes) {
        pTrident->pVbe = VBEInit(NULL,pTrident->pEnt->index);
        pTrident->Int10 = pTrident->pVbe->pInt10;
    } else
#endif
    {
        if (xf86LoadSubModule(pScrn, "int10")) {
            xf86DrvMsg(pScrn->scrnIndex,X_INFO,"Initializing int10\n");
            pTrident->Int10 = xf86InitInt10(pTrident->pEnt->index);
        }
    }

    hwp = VGAHWPTR(pScrn);

    if (IsPciCard && UseMMIO) {
        TRIDENTEnableMMIO(pScrn);

        /* Initialize the MMIO vgahw functions */
        vgaHWSetMmioFuncs(hwp, pTrident->IOBase, 0);
    }

    /* Save the current state */
    TRIDENTSave(pScrn);

    tridentSetModeBIOS(pScrn,pScrn->currentMode);

    /* Initialise the first mode */
    if (!TRIDENTModeInit(pScrn, pScrn->currentMode))
        return FALSE;

    /* Darken the screen for aesthetic reasons and set the viewport */
    TRIDENTSaveScreen(pScreen, SCREEN_SAVER_ON);
    TRIDENTAdjustFrame(pScrn, pScrn->frameX0, pScrn->frameY0);

    /*
     * The next step is to setup the screen's visuals, and initialise the
     * framebuffer code.  In cases where the framebuffer's default
     * choices for things like visual layouts and bits per RGB are OK,
     * this may be as simple as calling the framebuffer's ScreenInit()
     * function.  If not, the visuals will need to be setup before calling
     * a fb ScreenInit() function and fixed up after.
     *
     * For most PC hardware at depths >= 8, the defaults that fb uses
     * are not appropriate.  In this driver, we fixup the visuals after.
     */

    /*
     * Reset visual list.
     */
    miClearVisualTypes();

    /* Setup the visuals we support. */

    if (!miSetVisualTypes(pScrn->depth,
            miGetDefaultVisualMask(pScrn->depth),
            pScrn->rgbBits, pScrn->defaultVisual)) {
        if (pTrident->pVbe)
            vbeFree(pTrident->pVbe);
        else
            xf86FreeInt10(pTrident->Int10);
        return FALSE;
    }

    miSetPixmapDepths ();

    /* FIXME - we don't do shadowfb for < 4 */
    displayWidth = pScrn->displayWidth;
    if (pTrident->Rotate) {
        height = pScrn->virtualX;
        width = pScrn->virtualY;
    } else {
        width = pScrn->virtualX;
        height = pScrn->virtualY;
    }

    if(pTrident->ShadowFB) {
        pTrident->ShadowPitch = BitmapBytePad(pScrn->bitsPerPixel * width);
        pTrident->ShadowPtr = malloc(pTrident->ShadowPitch * height);
        displayWidth = pTrident->ShadowPitch / (pScrn->bitsPerPixel >> 3);
        FBStart = pTrident->ShadowPtr;
    } else {
        pTrident->ShadowFB = FALSE;
        pTrident->ShadowPtr = NULL;
        FBStart = pTrident->FbBase;
    }

    /*
     * Call the framebuffer layer's ScreenInit function, and fill in other
     * pScreen fields.
     */

    switch (pScrn->bitsPerPixel) {
    case 8:
    case 16:
    case 24:
    case 32:
        ret = fbScreenInit(pScreen, FBStart, width,
                height, pScrn->xDpi, pScrn->yDpi,
                displayWidth, pScrn->bitsPerPixel);

        break;
    default:
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                "Internal error: invalid bpp (%d) in TRIDENTScrnInit\n",
                pScrn->bitsPerPixel);
        ret = FALSE;
        break;
    }
    if (!ret) {
        if (pTrident->pVbe)
            vbeFree(pTrident->pVbe);
        else
            xf86FreeInt10(pTrident->Int10);
        return FALSE;
    }
    if (pScrn->bitsPerPixel > 8) {
        /* Fixup RGB ordering */
        visual = pScreen->visuals + pScreen->numVisuals;
        while (--visual >= pScreen->visuals) {
            if ((visual->class | DynamicClass) == DirectColor) {
                visual->offsetRed = pScrn->offset.red;
                visual->offsetGreen = pScrn->offset.green;
                visual->offsetBlue = pScrn->offset.blue;
                visual->redMask = pScrn->mask.red;
                visual->greenMask = pScrn->mask.green;
                visual->blueMask = pScrn->mask.blue;
            }
        }
    }

    /* must be after RGB ordering fixed */
    fbPictureInit (pScreen, 0, 0);

    xf86SetBlackWhitePixels(pScreen);

    pTrident->BlockHandler = pScreen->BlockHandler;
    pScreen->BlockHandler = TRIDENTBlockHandler;

    if (!pTrident->ShadowFB)
        TRIDENTDGAInit(pScreen);

    {
        BoxRec AvailFBArea;

        AvailFBArea.x1 = 0;
        AvailFBArea.y1 = 0;
        AvailFBArea.x2 = pScrn->displayWidth;
        AvailFBArea.y2 = pTrident->FbMapSize / (pScrn->displayWidth *
                pScrn->bitsPerPixel / 8);

        if (AvailFBArea.y2 > 2047) AvailFBArea.y2 = 2047;

        if (xf86InitFBManager(pScreen, &AvailFBArea)) {
            int cpp = pScrn->bitsPerPixel / 8;
            int area = AvailFBArea.y2 * pScrn->displayWidth;
            int areaoffset = area * cpp;

            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                    "Using %i scanlines of offscreen memory for area's \n",
                    AvailFBArea.y2 - pScrn->virtualY);

            if (xf86InitFBManagerLinear(pScreen, area, ((pTrident->FbMapSize/cpp) - area))) {
                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                        "Using %ld bytes of offscreen memory for linear (offset=0x%x)\n", (pTrident->FbMapSize - areaoffset), areaoffset);
            }
        }
    }

    if (Is3Dchip) {
        if ((pTrident->Chipset == CYBERBLADEI7) ||
                (pTrident->Chipset == CYBERBLADEI7D) ||
                (pTrident->Chipset == CYBERBLADEI1) ||
                (pTrident->Chipset == CYBERBLADEI1D) ||
                (pTrident->Chipset == CYBERBLADEAI1) ||
                (pTrident->Chipset == CYBERBLADEAI1D) ||
                (pTrident->Chipset == CYBERBLADEE4) ||
                (pTrident->Chipset == BLADE3D)) {
            if (pTrident->useEXA)
                BladeExaInit(pScreen);
        } else if ((pTrident->Chipset == CYBERBLADEXP4) ||
                   (pTrident->Chipset == XP5)) {
            if (pTrident->useEXA)
                XP4ExaInit(pScreen);
        }
    }

    xf86SetBackingStore(pScreen);

    /* Initialise cursor functions */
    miDCInitialize (pScreen, xf86GetPointerScreenFuncs());

    if (pTrident->HWCursor) {
        xf86SetSilkenMouse(pScreen);
        TridentHWCursorInit(pScreen);
    }

    /* Initialise default colourmap */
    if (!miCreateDefColormap(pScreen)) {
        if (pTrident->pVbe)
            vbeFree(pTrident->pVbe);
        else
            xf86FreeInt10(pTrident->Int10);
        return FALSE;
    }
    if(!xf86HandleColormaps(pScreen, 256, 6, TridentLoadPalette,
            TridentSetOverscan, CMAP_RELOAD_ON_MODE_SWITCH|CMAP_PALETTED_TRUECOLOR)) {
        if (pTrident->pVbe)
            vbeFree(pTrident->pVbe);
        else
            xf86FreeInt10(pTrident->Int10);
        return FALSE;
    }
    if(pTrident->ShadowFB) {
        if(pTrident->Rotate) {
            if (!pTrident->PointerMoved) {
                pTrident->PointerMoved = pScrn->PointerMoved;
                pScrn->PointerMoved = TRIDENTPointerMoved;
            }
            switch (pScrn->bitsPerPixel) {
            case 8:    pTrident->RefreshArea = TRIDENTRefreshArea8; break;
            case 16:   pTrident->RefreshArea = TRIDENTRefreshArea16; break;
            case 24:   pTrident->RefreshArea = TRIDENTRefreshArea24; break;
            case 32:   pTrident->RefreshArea = TRIDENTRefreshArea32; break;
            }
        } else {
            pTrident->RefreshArea = TRIDENTRefreshArea;
        }
        if (!shadowSetup(pScreen))
            return FALSE;
        pTrident->CreateScreenResources = pScreen->CreateScreenResources;
        pScreen->CreateScreenResources = TRIDENTCreateScreenResources;
    }

    xf86DPMSInit(pScreen, (DPMSSetProcPtr)TRIDENTDisplayPowerManagementSet, 0);

    pScrn->memPhysBase = pTrident->FbAddress;
    pScrn->fbOffset = 0;

    if (pTrident->Chipset >= TGUI9660)
        TRIDENTInitVideo(pScreen);

    pTrident->CloseScreen = pScreen->CloseScreen;
    pScreen->CloseScreen = TRIDENTCloseScreen;
    pScreen->SaveScreen = TRIDENTSaveScreen;

    /* Report any unused options (only for the first generation) */
    if (serverGeneration == 1) {
        xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
    }

#if 0
    TRIDENTI2CInit(pScreen);

    xf86PrintEDID(xf86DoEDID_DDC2(pScrn->scrnIndex,pTrident->DDC));
#endif

    return TRUE;
}

