/*
 * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
 * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sub license,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include "xf86RAC.h"
#include "shadowfb.h"

#include "globals.h"
#define DPMS_SERVER
#include "extensions/dpms.h"

#ifdef XvExtension
#include "xf86xv.h"
#endif

#include "savage_driver.h"
#include "savage_bci.h"
#include "savage_util.h"

#ifdef XF86DRI
#include "dri.h"
#endif



#define DRIVER_NAME	"savage"
#define DRIVER_VERSION	"1.1.18 S3"
#define VERSION_MAJOR	1
#define VERSION_MINOR	1
#define PATCHLEVEL	18
#define SAVAGE_VERSION	((VERSION_MAJOR << 24)  \
             			 | (VERSION_MINOR << 16)  \
               			 | PATCHLEVEL)

#ifdef TRACEON
#define TRACE(prms)	ErrorF prms
#else
#define TRACE(prms)
#endif

/* Supported chipsets */
static SymTabRec SavageChips[] = {
    { PCI_CHIP_SAVAGE4,         	"Savage4" },
    { PCI_CHIP_SAVAGE3D,        	"Savage3D" },
    { PCI_CHIP_SAVAGE3D_MV,     	"Savage3D-MV" },
    { PCI_CHIP_SAVAGE2000,          "Savage2000" },
    { PCI_CHIP_SAVAGE_MX_MV,        "Savage/MX-MV" },
    { PCI_CHIP_SAVAGE_MX,       	"Savage/MX" },
    { PCI_CHIP_SAVAGE_IX_MV,        "Savage/IX-MV" },
    { PCI_CHIP_SAVAGE_IX,           "Savage/IX" },
    { PCI_CHIP_PROSAVAGE_PM,        "ProSavage PM133" },
    { PCI_CHIP_PROSAVAGE_KM,        "ProSavage KM133" },
    { PCI_CHIP_S3TWISTER_P,         "ProSavage PN133" },
    { PCI_CHIP_S3TWISTER_K,         "ProSavage KN133" },
    { PCI_CHIP_S3PROSAVAGEDDR,  	"ProSavageDDR" },
    { PCI_CHIP_PM128,           	"SuperSavage/PM128"},
    { PCI_CHIP_PM64,            	"SuperSavage/PM64"},
    { PCI_CHIP_PM64C,           	"SuperSavage/PM64C"},
    { PCI_CHIP_PM128IX_SDR,         "SuperSavage/PM128IX_SDR"},
    { PCI_CHIP_PM128IX_DDR,         "SuperSavage/PM128IX_DDR"},
    { PCI_CHIP_PM64IX_SDR,          "SuperSavage/PM64IX_SDR"},
    { PCI_CHIP_PM64IX_DDR,          "SuperSavage/PM64IX_DDR"},
    { PCI_CHIP_PM64IXC_SDR,         "SuperSavage/PM64IXC_SDR"},
    { PCI_CHIP_PM64IXC_DDR,         "SuperSavage/PM64IXC_DDR"},
    { -1,                       	NULL }
};

static SymTabRec SavageChipsets[] = {
    { S3_SAVAGE3D,              "Savage3D" },
    { S3_SAVAGE4,               "Savage4" },
    { S3_SAVAGE2000,            "Savage2000" },
    { S3_SAVAGE_MX,             "MobileSavage" },
    { S3_PROSAVAGE,             "ProSavage" },
    { S3_TWISTER,               "Twister"},
    { S3_PROSAVAGEDDR,          "PROSAVAGEDDR"},
    { S3_SUPERSAVAGE,           "SuperSavage" },
    { -1,                       NULL }
};

/* This table maps a PCI device ID to a chipset family identifier. */
static PciChipsets SavagePciChipsets[] = {
    { S3_SAVAGE3D,	PCI_CHIP_SAVAGE3D,	RES_SHARED_VGA },
    { S3_SAVAGE3D,	PCI_CHIP_SAVAGE3D_MV, 	RES_SHARED_VGA },
    { S3_SAVAGE4,	PCI_CHIP_SAVAGE4,	RES_SHARED_VGA },
    { S3_SAVAGE2000,	PCI_CHIP_SAVAGE2000,	RES_SHARED_VGA },
    { S3_SAVAGE_MX,	PCI_CHIP_SAVAGE_MX_MV,	RES_SHARED_VGA },
    { S3_SAVAGE_MX,	PCI_CHIP_SAVAGE_MX,	RES_SHARED_VGA },
    { S3_SAVAGE_MX,	PCI_CHIP_SAVAGE_IX_MV,	RES_SHARED_VGA },
    { S3_SAVAGE_MX,	PCI_CHIP_SAVAGE_IX,	RES_SHARED_VGA },
    { S3_PROSAVAGE,	PCI_CHIP_PROSAVAGE_PM,	RES_SHARED_VGA },
    { S3_PROSAVAGE,	PCI_CHIP_PROSAVAGE_KM,	RES_SHARED_VGA },
    { S3_TWISTER,	PCI_CHIP_S3TWISTER_P,	RES_SHARED_VGA },
    { S3_TWISTER,	PCI_CHIP_S3TWISTER_K,	RES_SHARED_VGA },
    { S3_PROSAVAGEDDR,	PCI_CHIP_S3PROSAVAGEDDR,	RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM128,		RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM64,		RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM64C,		RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM128IX_SDR,	RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM128IX_DDR,	RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM64IX_SDR,	RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM64IX_DDR,	RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM64IXC_SDR,	RES_SHARED_VGA },
    { S3_SUPERSAVAGE,	PCI_CHIP_PM64IXC_DDR,	RES_SHARED_VGA },
    { -1,		-1,			RES_UNDEFINED }
};

typedef enum {
    OPTION_PCI_BURST,
    OPTION_PCI_RETRY,
    OPTION_NOACCEL,
    OPTION_LCD_CENTER,
    OPTION_LCDCLOCK,
    OPTION_MCLK,
    OPTION_REFCLK,
    OPTION_SHOWCACHE,
    OPTION_SWCURSOR,
    OPTION_HWCURSOR,
    OPTION_SHADOW_FB,
    OPTION_ROTATE,
    OPTION_USEBIOS,
    OPTION_SHADOW_STATUS,
    OPTION_VIDEORAM,
    OPTION_CRT_ONLY,
    OPTION_DISABLE_XVMC,
    OPTION_TV_ON,
    OPTION_TV_PAL,
    OPTION_SAA7111,
    /* add by peterzhu*/
    OPTION_V4l_VIDEOIN,
    OPTION_V4l_DEVNUM,
    OPTION_DOUBLE_BUFFER,
    OPTION_DISABLE_TILE
    /*OPTION_MAXXFBMEM*/
} SavageOpts;


static OptionInfoRec SavageOptions[] = {
    { OPTION_NOACCEL,	"NoAccel",	OPTV_BOOLEAN, {0}, FALSE  },
    { OPTION_HWCURSOR,	"HWCursor",	OPTV_BOOLEAN, {0}, FALSE },
    { OPTION_SWCURSOR,	"SWCursor",	OPTV_BOOLEAN, {0}, FALSE },
    { OPTION_SHADOW_FB,	"ShadowFB",	OPTV_BOOLEAN, {0}, FALSE },
    { OPTION_ROTATE,	"Rotate",	OPTV_ANYSTR, {0}, FALSE },
    { OPTION_LCDCLOCK,	"LCDClock",	OPTV_FREQ,    {0}, FALSE },
    { OPTION_USEBIOS,	"UseBIOS",	OPTV_BOOLEAN, {0}, FALSE },
    { OPTION_SHADOW_STATUS, "ShadowStatus", OPTV_BOOLEAN, {0}, FALSE },
    { OPTION_VIDEORAM,  "VideoRAM",     OPTV_INTEGER, {0}, FALSE },
    { OPTION_CRT_ONLY,  "CrtOnly",      OPTV_BOOLEAN, {0}, FALSE },
    { OPTION_DISABLE_XVMC,	"DisableXVMC",  OPTV_BOOLEAN,   {0}, FALSE  },
    { OPTION_TV_ON,     "TvOn",         OPTV_BOOLEAN, {0}, FALSE },
    { OPTION_SAA7111,	"SAA7111",	OPTV_BOOLEAN, {0}, FALSE  },
    /* add by peterzhu*/
    { OPTION_V4l_VIDEOIN, 	"V4lVideoIn", 	OPTV_BOOLEAN, 	{0}, FALSE  },
    { OPTION_V4l_DEVNUM, 	"V4lDevNum", 	OPTV_INTEGER, 	{0}, FALSE  },
    { OPTION_DOUBLE_BUFFER,	"Double Buffer",OPTV_BOOLEAN,   {0}, FALSE  },
    { OPTION_DISABLE_TILE,	"DisableTile",  OPTV_BOOLEAN,   {0}, FALSE  },
    /*{ OPTION_MAXXFBMEM, 	"MaxXFBMem",    OPTV_INTEGER,   {0}, -1 },*/
    { -1,		NULL,		OPTV_NONE,    {0}, FALSE }
};

/*
 * do we need VGA module? I think we should do all of the VGA
 * operations by ourself,then we can know what we do
 */
static const char *vgaHWSymbols[] = {
    "vgaHWBlankScreen",
    "vgaHWCopyReg",
    "vgaHWGetHWRec",
    "vgaHWGetIOBase",
    "vgaHWGetIndex",
    "vgaHWInit",
    "vgaHWLock",
    "vgaHWProtect",
    "vgaHWRestore",
    "vgaHWSave",
    "vgaHWSaveScreen",
    "vgaHWSetMmioFuncs",
    "vgaHWSetStdFuncs",
    "vgaHWUnmapMem",
    "vgaHWddc1SetSpeed",
    "vgaHWFreeHWRec",
#if 0
    "vgaHWMapMem",
    "vgaHWUnlock",
#endif
    NULL
};

static const char *ramdacSymbols[] = {
    "xf86CreateCursorInfoRec",
#if 0
    "xf86DestroyCursorInfoRec",
#endif
    "xf86InitCursor",
    NULL
};

static const char *vbeSymbols[] = {
    "VBEInit",
    "vbeDoEDID",
    "vbeFree",
    NULL
};

static const char *ddcSymbols[] = {
    "xf86DoEDID_DDC1",
    "xf86DoEDID_DDC2",
    "xf86PrintEDID",
    "xf86SetDDCproperties",
    NULL
};

static const char *i2cSymbols[] = {
    "xf86CreateI2CBusRec",
    "xf86I2CBusInit",
    "xf86CreateI2CDevRec",
    "xf86I2CDevInit",
    "xf86I2CWriteByte",
    "xf86I2CWriteBytes",
    "xf86I2CReadByte",
    "xf86I2CReadBytes",
    "xf86I2CWriteRead",
    "xf86DestroyI2CDevRec",
    NULL
};

static const char *xaaSymbols[] = {
    "XAACopyROP",
    "XAACopyROP_PM",
    "XAACreateInfoRec",
    "XAADestroyInfoRec",
    "XAAFillSolidRects",
    "XAAHelpPatternROP",
#if 0
    "XAAHelpSolidROP",
#endif
    "XAAInit",
    "XAAScreenIndex",
    NULL
};

static const char *shadowSymbols[] = {
    "ShadowFBInit",
    NULL
};

static const char *fbSymbols[] = {
    "fbPictureInit",
    "fbScreenInit",
    NULL
};

#ifdef XF86DRI
static const char *drmSymbols[] = {
    "drmAvailable",
    "drmAddBufs",
    "drmAddMap",
    "drmCtlInstHandler",
    "drmGetInterruptFromBusID",
    "drmFreeVersion",
    "drmGetVersion",
    "drmMap",
    "drmUnmap",
    "drmMapBufs",
    "drmUnmapBufs",
    "drmAgpAcquire",
    "drmAgpRelease",
    "drmAgpEnable",
    "drmAgpAlloc",
    "drmAgpFree",
    "drmAgpBind",
    "drmAgpUnbind",
    "drmAgpGetMode",
    "drmAgpBase",
    "drmAgpSize",
    "drmAgpVendorId",
    "drmAgpDeviceId",
    "drmMGAInitDMA",
    "drmMGACleanupDMA",
    "drmMGAFlushDMA",
    "drmMGAEngineReset",
    NULL
};

static const char *driSymbols[] = {
    "DRIGetDrawableIndex",
    "DRIFinishScreenInit",
    "DRIDestroyInfoRec",
    "DRICloseScreen",
    "DRIDestroyInfoRec",
    "DRIScreenInit",
    "DRIDestroyInfoRec",
    "DRICreateInfoRec",
    "DRILock",
    "DRIUnlock",
    "DRIGetSAREAPrivate",
    "DRIGetContext",
    "DRIQueryVersion",
    "DRIAdjustFrame",
    "DRIOpenFullScreen",
    "DRICloseFullScreen",
    "GlxSetVisualConfigs",
    NULL
};
#endif

/* prototypes */
static void SavageIdentify(int flags);
static Bool SavageProbe(DriverPtr drv, int flags);
static const OptionInfoRec *SavageAvailableOptions(int chipid, int busid);
DriverRec SAVAGE = {
    SAVAGE_VERSION,
    DRIVER_NAME,
    SavageIdentify,
    SavageProbe,
    SavageAvailableOptions,
    NULL,
    0
};

static Bool SavagePreInit(ScrnInfoPtr pScrn, int flags);
static Bool SavageScreenInit(int scrnIndex, ScreenPtr pScreen, int argc,
                             char **argv);
static Bool SavageCloseScreen(int scrnIndex, ScreenPtr pScreen);
static Bool SavageSaveScreen(ScreenPtr pScreen, int mode);
void SavageAdjustFrame(int scrnIndex, int x, int y, int flags);
Bool SavageSwitchMode(int scrnIndex, DisplayModePtr mode, int flags);
static ModeStatus SavageValidMode(int index, DisplayModePtr pMode,
                                  Bool verbose, int flags);
static Bool SavageEnterVT(int scrnIndex, int flags);
static void SavageLeaveVT(int scrnIndex, int flags);

static void SavageDPMS(ScrnInfoPtr pScrn, int mode, int flags);

static void SavageProcessConfig(ScrnInfoPtr pScrn);
static Bool SavageBuildModeTable(ScrnInfoPtr pScrn);
static Bool SavageInitHardware(ScrnInfoPtr pScrn);
static int  SavageSubScreenInit(int scrnIndex, ScreenPtr pScreen);

static void SavageProbeDDC(ScrnInfoPtr pScrn, int index);
static Bool SavageDDC1(int scrnIndex);
static unsigned int SavageDDC1Read(ScrnInfoPtr pScrn);

static Bool SavageAssertMode(ScrnInfoPtr pScrn,DisplayModePtr mode);
static void SavageEnableMode(ScrnInfoPtr pScrn,BOOL bEnable);

void SavageEnableMode_M7(ScrnInfoPtr pScrn,BOOL bEnable);
void SavageEnableMode_Twister(ScrnInfoPtr pScrn,BOOL bEnable);
void SavageEnableMode_Savage4(ScrnInfoPtr pScrn,BOOL bEnable);
void SavageEnableMode_PM(ScrnInfoPtr pScrn,BOOL bEnable);


static Bool SavageEnableMMIO(ScrnInfoPtr pScrn);
static void SavageDisableMMIO(ScrnInfoPtr pScrn);
static Bool SavageMapFB(ScrnInfoPtr pScrn);
static void SavageUnmapFB(ScrnInfoPtr pScrn, int All);

static void SavageDetectVidRam(ScrnInfoPtr pScrn);

static void SavageLoadPalette(ScrnInfoPtr pScrn, int numColors,
                              int *indicies, LOCO *colors,
                              VisualPtr pVisual);
static void SavageLoadPaletteSavage4(ScrnInfoPtr pScrn,
                                     int numColors, int *indicies,
                                     LOCO *colors, VisualPtr pVisual);

static void SavagePanningCheck(ScrnInfoPtr pScrn);

static  void ResetBCI2K(SavagePtr psav);

static  Bool ShadowWait(SavagePtr psav);

static  int WaitQueue3D(SavagePtr psav, int v);
static  int WaitQueue4(SavagePtr psav, int v);
static  int WaitQueue2K(SavagePtr psav, int v);

static  int WaitIdleEmpty3D(SavagePtr psav);
static  int WaitIdleEmpty4(SavagePtr psav);
static  int WaitIdleEmpty2K(SavagePtr psav);

static  int WaitIdle3D(SavagePtr psav);
static  int WaitIdle4(SavagePtr psav);
static  int WaitIdle2K(SavagePtr psav);

static int LookupChipID(PciChipsets* pset, int ChipID);
static Bool SavageGetRec(ScrnInfoPtr pScrn);
static void SavageFreeRec(ScrnInfoPtr pScrn);
static int GetTileAperturePitch(ulong dwWidth, ulong dwBpp);

void SavagePrintRegs(ScrnInfoPtr pScrn);

static void SavageSave(ScrnInfoPtr pScrn);
static Bool SavageNoBiosModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode);
static void SavageNoBiosWriteMode(ScrnInfoPtr pScrn, vgaRegPtr vgaSavePtr,
			    SavageRegPtr restore);
static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
			   int min_n2, int max_n2, long freq_min,
			   long freq_max, unsigned int *mdiv,
			   unsigned int *ndiv, unsigned int *r);

/* extern functions and variables */

#ifdef XFree86LOADER

static MODULESETUPPROTO(SavageSetup);

static XF86ModuleVersionInfo SavageVersRec = {
    "savage",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    XF86_VERSION_CURRENT,
    VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL,
    ABI_CLASS_VIDEODRV,
    ABI_VIDEODRV_VERSION,
    MOD_CLASS_VIDEODRV,
    {0, 0, 0, 0}
};

XF86ModuleData savageModuleData = { &SavageVersRec, SavageSetup, NULL };

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

    if (!setupDone) {
        setupDone = TRUE;
        xf86AddDriver(&SAVAGE, module, 0);
        LoaderRefSymLists(vgaHWSymbols, fbSymbols, ramdacSymbols,
                          xaaSymbols, shadowSymbols, vbeSymbols,
#ifdef XF86DRI
                          drmSymbols, driSymbols,
#endif
                          i2cSymbols, ddcSymbols, NULL);
        return (pointer) 1;
    } else {
        if (errmaj)
            *errmaj = LDR_ONCEONLY;
        return NULL;
    }
}
#endif /* XFree86LOADER */


static void SavageIdentify(int flags)
{
    xf86PrintChipsets("SAVAGE", 
                      "driver (version " DRIVER_VERSION ") for S3 Savage chipsets",
                      SavageChips);
}


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

    /* sanity checks */
    if ((numDevSections = xf86MatchDevice("savage", &devSections)) <= 0) {
        return FALSE;
    }
    
    if (xf86GetPciVideoInfo() == NULL) {
        return FALSE;
    }

    numUsed = xf86MatchPciInstances("SAVAGE", PCI_VENDOR_S3,
                                    SavageChipsets, SavagePciChipsets,
                                    devSections, numDevSections, drv,
                                    &usedChips);
    /* Free it since we don't need that list after this */
    if (devSections) {
        xfree(devSections);
        devSections = NULL;
    }
    if (numUsed <= 0) {
        return FALSE;
    }

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

            pScrn->driverVersion = (int)DRIVER_VERSION;
            pScrn->driverName = DRIVER_NAME;
            pScrn->name = "SAVAGE";
            pScrn->Probe = SavageProbe;
            pScrn->PreInit = SavagePreInit;
            pScrn->ScreenInit = SavageScreenInit;
            pScrn->SwitchMode = SavageSwitchMode;
            pScrn->AdjustFrame = SavageAdjustFrame;
            pScrn->EnterVT = SavageEnterVT;
            pScrn->LeaveVT = SavageLeaveVT;
            pScrn->FreeScreen = NULL;
            pScrn->ValidMode = SavageValidMode;
            foundScreen = TRUE;
            xf86ConfigActivePciEntity(pScrn, usedChips[i], SavagePciChipsets,
                                      NULL, NULL, NULL, NULL, NULL);
        }
    }
    
    xfree(usedChips);
    
    return foundScreen;
}

static Bool SavagePreInit(ScrnInfoPtr pScrn, int flags)
{
    EntityInfoPtr pEnt = NULL;
    SavagePtr psav;
    vgaHWPtr hwp;
    MessageType from = X_DEFAULT;
    
    TRACE(("SavagePreInit(%d)\n", flags));

    gpScrn = pScrn;

    if (flags & PROBE_DETECT) {
        SavageProbeDDC( pScrn, xf86GetEntityInfo(pScrn->entityList[0])->index );
        return TRUE;
    }

    if (!xf86LoadSubModule(pScrn, "vgahw"))
        return FALSE;
    xf86LoaderReqSymLists(vgaHWSymbols, NULL);
    
    if (!vgaHWGetHWRec(pScrn))
        return FALSE;

#if 0
    /*
     * Here we can alter the number of registers saved and restored by the
     * standard vgaHWSave and Restore routines.
     */
    vgaHWSetRegCounts( pScrn, VGA_NUM_CRTC, VGA_NUM_SEQ, VGA_NUM_GFX, VGA_NUM_ATTR );
#endif

    pScrn->monitor = pScrn->confScreen->monitor;

    /*
     * We support depths of 8, 15, 16 and 24.
     * We support bpp of 8, 16, and 32.
     */
    if (!xf86SetDepthBpp(pScrn, 8, 8, 8, Support32bppFb))
        return FALSE;
    else {
        int requiredBpp;
        int altBpp = 0;

        switch (pScrn->depth) {
            case 8:
            case 16:
                requiredBpp = pScrn->depth;
                break;
            case 15:
                requiredBpp = 16;
                break;
            case 24:
                requiredBpp = 32;
                altBpp = 24;
                break;
                
            default:
                xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                           "Given depth (%d) is not supported by this driver\n",
                           pScrn->depth);
                return FALSE;
        }

        if ((pScrn->bitsPerPixel != requiredBpp) &&
            (pScrn->bitsPerPixel != altBpp)) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Depth %d must specify %d bpp; %d was given\n",
                       pScrn->depth, requiredBpp, pScrn->bitsPerPixel );
            return FALSE;
        }
    }

    xf86PrintDepthBpp(pScrn);

    if (pScrn->depth > 8)
    {
        rgb zeros = {0, 0, 0};

        if (!xf86SetWeight(pScrn, zeros, zeros))
            return FALSE;
        else {
            /* TODO check 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;
        }
    }

    pScrn->progClock = TRUE;

    if (!SavageGetRec(pScrn))
        return FALSE;
    psav = SAVPTR(pScrn);

    psav->DrvVersion = SAVAGE_VERSION;
    
    SavageProcessConfig(pScrn);
    
    if (pScrn->numEntities > 1) {
        goto FreeRec;        
    }

    pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
    if (pEnt->resources) {
        xfree(pEnt);
        goto FreeRec;
    }
    psav->EntityIndex = pEnt->index;

    psav->UseBIOS = FALSE;
    if (xf86LoadSubModule(pScrn, "vbe")) {
        xf86LoaderReqSymLists(vbeSymbols, NULL);
        psav->pVbe = VBEInit(psav->pInt10, pEnt->index);
        if (psav->pVbe) {
            psav->pInt10 = psav->pVbe->pInt10;
            psav->UseBIOS = TRUE;
        }
    }

    psav->PciInfo = xf86GetPciInfoForEntity(pEnt->index);
    xf86RegisterResources(pEnt->index, NULL, ResNone);
    xf86SetOperatingState(resVgaIo, pEnt->index, ResUnusedOpr);
    xf86SetOperatingState(resVgaMem, pEnt->index, ResDisableOpr);

    from = X_DEFAULT;
    if (pEnt->device->chipset && *pEnt->device->chipset) {
        pScrn->chipset = pEnt->device->chipset;
        psav->ChipId = pEnt->device->chipID;
        psav->Chipset = xf86StringToToken(SavageChipsets, pScrn->chipset);
        from = X_CONFIG;
    } else if (pEnt->device->chipID >= 0) {
        psav->ChipId = pEnt->device->chipID;
        psav->Chipset = LookupChipID(SavagePciChipsets, psav->ChipId);
        pScrn->chipset = (char *)xf86TokenToString(SavageChipsets,
                                                   psav->Chipset);
        from = X_CONFIG;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipID override: 0x%04X\n",
                   pEnt->device->chipID);
    } else {
        from = X_PROBED;
        psav->ChipId = psav->PciInfo->chipType;
        psav->Chipset = LookupChipID(SavagePciChipsets, psav->ChipId);
        pScrn->chipset = (char *)xf86TokenToString(SavageChipsets,
                                                   psav->Chipset);
    }

    psav->ChipName = xf86TokenToString(SavageChips, psav->ChipId);
    xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Chip: id %04x, \"%s\"\n",
               psav->ChipId, psav->ChipName);

    if (pEnt->device->chipRev >= 0) {
        psav->ChipRev = pEnt->device->chipRev;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n",
                   psav->ChipRev);
    } else
        psav->ChipRev = psav->PciInfo->chipRev;

    xfree(pEnt);

    /* maybe throw in some more sanity checks here */
    xf86DrvMsg(pScrn->scrnIndex, from, "Engine: \"%s\"\n", pScrn->chipset);

    psav->PciTag = pciTag(psav->PciInfo->bus, psav->PciInfo->device,
                          psav->PciInfo->func);

    hwp = VGAHWPTR(pScrn);

    if (!SavageEnableMMIO(pScrn)) {
        goto FreeAll;                    
	}
    
    if (!SavageInitHardware(pScrn)) {
        goto FreeAll;            
    }
    
    if (!SavageBuildModeTable(pScrn)) {
        goto FreeAll;            
    }

    if (xf86LoadSubModule(pScrn, "fb") == NULL) {
        goto FreeAll;            
    }

    xf86LoaderReqSymLists(fbSymbols, NULL);

    if (!psav->NoAccel) {
        if (!xf86LoadSubModule(pScrn, "xaa")) {
            goto FreeAll;            
        }
        xf86LoaderReqSymLists(xaaSymbols, NULL );
    }

    if (psav->hwcursor) {
        if (!xf86LoadSubModule(pScrn, "ramdac")) {
            goto FreeAll;
        }
        xf86LoaderReqSymLists(ramdacSymbols, NULL);
    }

    if (psav->shadowFB) {
        if (!xf86LoadSubModule(pScrn, "shadowfb")) {
            goto FreeAll;
        }
        xf86LoaderReqSymLists(shadowSymbols, NULL);
    }
	  
    return TRUE;
    
  FreeAll:
    vgaHWFreeHWRec(pScrn);
    if (psav->pVbe)
        vbeFree(psav->pVbe);
  FreeRec:
    if (psav)
        SavageFreeRec(pScrn);
    return FALSE;
} /* SavagePreInit */


static Bool SavageScreenInit(int scrnIndex, ScreenPtr pScreen,
                             int argc, char **argv)
{
    ScrnInfoPtr pScrn;
    SavagePtr psav;
    int ret;

    TRACE(("SavageScreenInit()\n"));

    pScrn = xf86Screens[pScreen->myNum];
    psav = SAVPTR(pScrn);

    /* save current console state */
    SavageSaveRestoreState(psav,1);
    
    SavageEnableMMIO(pScrn);
    
    if (!SavageMapFB(pScrn)) {
        goto FreeAll;
    }
    
    /* set the mode,enable the hardware according to the mode */
    if (!SavageAssertMode(pScrn, pScrn->currentMode)) {
        goto FreeAll;
    }
    
#ifdef XF86DRI
    if ((psav->Chipset == S3_TWISTER)
        || (psav->Chipset == S3_PROSAVAGE)
        || (psav->Chipset == S3_SAVAGE4)
        || (psav->Chipset == S3_SAVAGE_MX)
	|| (psav->Chipset == S3_SAVAGE3D)
	|| (psav->Chipset == S3_SUPERSAVAGE)
        || (psav->Chipset == S3_PROSAVAGEDDR)) {
        /* Setup DRI after visuals have been established */
        psav->directRenderingEnabled = SAVAGEDRIScreenInit(pScreen);
    } else
        psav->directRenderingEnabled = FALSE;
    
    if(psav->directRenderingEnabled) {
        xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"DRI is enabled\n");
    }
    else {
        xf86DrvMsg(pScrn->scrnIndex,X_ERROR,"DRI isn't enabled\n");
    }
#endif

    miClearVisualTypes();

    if (pScrn->bitsPerPixel == 16) {
        if (!miSetVisualTypes(pScrn->depth, TrueColorMask,
                              pScrn->rgbBits, pScrn->defaultVisual))
            return FALSE;
        if (!miSetPixmapDepths())
            return FALSE;
    } else {
        if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth),
                              pScrn->rgbBits, pScrn->defaultVisual)) {
            goto FreeAll;
        }
        if (!miSetPixmapDepths()){
            goto FreeAll;
        }
    }
       
    ret = SavageSubScreenInit(scrnIndex, pScreen);
    if (!ret) {
        goto FreeAll;
    }
       
    xf86SetBlackWhitePixels(pScreen);
       
    if (pScrn->bitsPerPixel > 8) {
        VisualPtr visual;
        
        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);

    if (!psav->NoAccel) {
        SavageInitAccel(pScreen);
    }

    miInitializeBackingStore(pScreen);
    xf86SetBackingStore(pScreen);

    if (!psav->shadowFB) {
        SavageDGAInit(pScreen);
    }

    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

    if (psav->hwcursor) {
        if (!SavageHWCursorInit(pScreen))
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Hardware cursor initialization failed\n");
    }

    if (psav->shadowFB) {
        RefreshAreaFuncPtr refreshArea = SavageRefreshArea;

        if(psav->rotate) {
            if (!psav->PointerMoved) {
                psav->PointerMoved = pScrn->PointerMoved;
                pScrn->PointerMoved = SavagePointerMoved;
            }

            switch(pScrn->bitsPerPixel) {
                case 8:	refreshArea = SavageRefreshArea8;	break;
                case 16:refreshArea = SavageRefreshArea16;	break;
                case 24:refreshArea = SavageRefreshArea24;	break;
                case 32:refreshArea = SavageRefreshArea32;	break;
            }
        }

        ShadowFBInit(pScreen, refreshArea);
    }

    if (!miCreateDefColormap(pScreen))
	    goto FreeAll;

    if (psav->Chipset == S3_SAVAGE4) {
        if (!xf86HandleColormaps(pScreen, 256, 6, SavageLoadPaletteSavage4,NULL,
                                 CMAP_RELOAD_ON_MODE_SWITCH | CMAP_PALETTED_TRUECOLOR))
            goto FreeAll;
    }
    else {
        if (!xf86HandleColormaps(pScreen, 256, 6, SavageLoadPalette,NULL,
                                 CMAP_RELOAD_ON_MODE_SWITCH | CMAP_PALETTED_TRUECOLOR))
            goto FreeAll;
    }

    psav->CloseScreen = pScreen->CloseScreen;
    pScreen->SaveScreen = SavageSaveScreen;
    pScreen->CloseScreen = SavageCloseScreen;

    if (xf86DPMSInit(pScreen, SavageDPMS, 0) == FALSE)
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DPMS initialization failed\n");

#ifdef XF86DRI
    if (psav->directRenderingEnabled) {
        /* complete the DRI setup.*/
        psav->directRenderingEnabled = SAVAGEDRIFinishScreenInit(pScreen);
    }
    if (psav->directRenderingEnabled) {
        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct rendering enabled\n");
    } else {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Direct rendering disabled\n");
    }
#endif

#ifdef XvExtension
    SavagePanningCheck(pScrn);
    if (psav->NoAccel == FALSE) {
        SavageInitVideo(pScreen); 
    }
#endif

    if ((psav->directRenderingEnabled) && (!psav->bDisableXvMC)) {
        if (SAVAGEInitMC(pScreen))
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"XvMC is enabled\n");
        else
            xf86DrvMsg(pScrn->scrnIndex,X_CONFIG,"XvMC is not enabled\n");
    }

    if (serverGeneration == 1)
        xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);

    return TRUE;
    
  FreeAll:
    vgaHWFreeHWRec(pScrn);
    if (psav->pVbe)
        vbeFree(psav->pVbe);
    SavageFreeRec(pScrn);
    
    return FALSE;
    
} /* SavageScreenInit */


static Bool SavageCloseScreen(int scrnIndex, ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    SavagePtr psav = SAVPTR(pScrn);

    TRACE(("SavageCloseScreen\n"));

#ifdef XF86DRI
    if (psav->directRenderingEnabled) {
        SAVAGEDRICloseScreen(pScreen);
        psav->directRenderingEnabled=FALSE;
    }
#endif

    if (psav->CursorInfoRec)
        xfree(psav->CursorInfoRec);
    psav->CursorInfoRec = NULL;

    if (psav->adaptor)
        xfree(psav->adaptor);
    psav->adaptor = NULL;

    if (psav->AccelInfoRec) {
        XAADestroyInfoRec(psav->AccelInfoRec);
        psav->AccelInfoRec = NULL;
    }

    if (psav->DGAModes) {
        xfree(psav->DGAModes);
        psav->DGAModes = NULL;
        psav->numDGAModes = 0;
    }

    if (pScrn->vtSema) {
        SavageEnableMode(pScrn,FALSE);
        SavageUnmapFB(pScrn, 0);        
        SavageDisableMMIO(pScrn);
        /* restore the console state */
        SavageSaveRestoreState(psav,0);
    }

    pScrn->vtSema = FALSE;
    pScreen->CloseScreen = psav->CloseScreen;

    /*
     * can't free vga/vbe/psav private data,when server generating at
     * the secondary time, it don't call PreInit to allocate them again
     * but ScreenInit and so on will use it soon
     */
    return (*pScreen->CloseScreen)(scrnIndex, pScreen);
}


static Bool SavageSaveScreen(ScreenPtr pScreen, int mode)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    TRACE(("SavageSaveScreen(0x%x)\n", mode));

    if (pScrn->vtSema && SAVPTR(pScrn)->hwcursor) {
        if (xf86IsUnblank(mode))
            SavageShowCursor(pScrn);
        else
            SavageHideCursor(pScrn);
    }

    return vgaHWSaveScreen(pScreen, mode);
}


void
SavageAdjustFrame(int scrnIndex, int x, int y, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    SavagePtr psav = SAVPTR(pScrn);
    DisplayModePtr currentMode = pScrn->currentMode;    
    int address=0,top=0,left=0;
    
    TRACE(("SavageAdjustFrame(%d,%d,%x)\n", x, y, flags));
    
    if (!psav->bTiled) {
        left = x - x % 64;
        top = y;
        address = (top * psav->lDelta) + left * (pScrn->bitsPerPixel >> 3);
        address = (address >> 5) << 5;
    } else {
        top = y - y % TILEHEIGHT;
        if (pScrn->bitsPerPixel == 16) {
            left = x - x % TILEWIDTH_16BPP;
            address = top * psav->lDelta + left * TILE_SIZE_BYTE / TILEWIDTH_16BPP;
        } else if (pScrn->bitsPerPixel == 32) {
            left = x - x % TILEWIDTH_32BPP;
            address = top * psav->lDelta + left * TILE_SIZE_BYTE / TILEWIDTH_32BPP;
        }
    }
    
    /*
     * because we align the viewport to the width and height of one tile
     * we shoud update the locate of frame
     */
    pScrn->frameX0 = left;
    pScrn->frameY0 = top;
    pScrn->frameX1 = left + currentMode->HDisplay - 1;
    pScrn->frameY1 = top+ currentMode->VDisplay - 1;

    if (psav->Chipset == S3_SAVAGE_MX) {
        OUTREG32(PRI_STREAM_FBUF_ADDR1, address & 0xFFFFFFFC);/* IGA1 */
        OUTREG32(PRI_STREAM2_FBUF_ADDR0, address & 0xFFFFFFFC);/* IGA2 */
    } else if (psav->Chipset == S3_SUPERSAVAGE) {
        /* IGA1 */
        OUTREG32(PRI_STREAM_FBUF_ADDR1, address & 0xFFFFFFF8);
        OUTREG32(PRI_STREAM_FBUF_ADDR0, 0x80000000);
        /* IGA2 */
        OUTREG32(PRI_STREAM2_FBUF_ADDR0, ((address & 0xFFFFFFF8) | 0x80000000));
    } else {
        OUTREG32(PRI_STREAM_FBUF_ADDR0,address |  0xFFFFFFFC);
        OUTREG32(PRI_STREAM_FBUF_ADDR1,address |  0x80000000);
    }
    
    return;
}


Bool
SavageSwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    SavagePtr psav = SAVPTR(pScrn);

    TRACE(("SavageSwitchMode\n"));

    if (psav->DisplayOutsActive & UT_DEVICE_TV) {
        if (mode->HDisplay > psav->TvInfo.TvSizeX
            || mode->VDisplay > psav->TvInfo.TvSizeY)
            return FALSE;
    }

    SavageAssertMode(xf86Screens[scrnIndex], mode);

    pScrn->currentMode = mode; /* Jiayo, our patch */
    
    SavagePanningCheck(pScrn);
    if (psav->NoAccel == FALSE) {
        SavageInitStreams(pScrn);
    }

    return TRUE;
}


static ModeStatus SavageValidMode(int index, DisplayModePtr pMode,
                                  Bool verbose, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[index];
    SavagePtr psav = SAVPTR(pScrn);
    int refresh;

    TRACE(("SavageValidMode\n"));

    if (psav->DisplayOutsActive & UT_DEVICE_TV) {
        if ((pMode->HDisplay > psav->TvInfo.TvSizeX)
            || (pMode->VDisplay > psav->TvInfo.TvSizeY))
            return MODE_BAD;
    }

    if (((psav->DisplayOutsActive & UT_DEVICE_LCD)
         && psav->PanelX
         && ((pMode->HDisplay > psav->PanelX)
             ||(pMode->VDisplay > psav->PanelY)))
			&& (!psav->CrtOnly)) {
	    return MODE_PANEL;
    }

    if (psav->UseBIOS) {
        refresh = (pMode->Clock * 1000) / (pMode->HTotal * pMode->VTotal);
        return (SavageMatchBiosMode(pScrn,pMode->HDisplay,
                                   pMode->VDisplay,
                                   refresh,NULL,NULL));
    }
    
    return MODE_OK;
}


static const OptionInfoRec *SavageAvailableOptions(int chipid, int busid)
{
    return SavageOptions;
}

static Bool SavageEnterVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    
#ifdef XF86DRI
    SavagePtr psav= SAVPTR(pScrn);
    ScreenPtr pScreen;
#endif
    
    TRACE(("SavageEnterVT(%d)\n", flags));
    
    gpScrn = pScrn;
    
    SavageEnableMMIO(pScrn);
    
#ifdef XF86DRI
    if (psav->directRenderingEnabled) {
        pScreen = screenInfo.screens[scrnIndex];
        DRIUnlock(pScreen);
        psav->LockHeld = 0;
    }
#endif
    
    SavageSave(pScrn);

    /* re-initialize the hardware  */
    SavageAssertMode(pScrn, pScrn->currentMode);
    
    if (psav->NoAccel == FALSE) {
        SavageInitStreams(pScrn);
    }

    return TRUE;
}


static void SavageLeaveVT(int scrnIndex, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    SavagePtr psav = SAVPTR(pScrn);

#ifdef XF86DRI
    ScreenPtr pScreen;
#endif

    TRACE(("SavageLeaveVT(%d)\n", flags));

    gpScrn = pScrn;
    
#ifdef XF86DRI
    if (psav->directRenderingEnabled) {
        pScreen = screenInfo.screens[scrnIndex];
        DRILock(pScreen, 0);
        psav->LockHeld = 1;
    }
#endif
    
    SavageEnableMode(pScrn,FALSE);
    
    SavageDisableMMIO(pScrn);
    
    /* restore the vt state */
    SavageSaveRestoreState(psav,0);
}


static void SavageDPMS(ScrnInfoPtr pScrn, int mode, int flags)
{
    SavagePtr psav = SAVPTR(pScrn);
    uchar byte;

    TRACE(("SavageDPMS(%d,%x)\n", mode, flags));

    UnLockExtRegs();

    OUTREG8(SEQ_ADDRESS_REG,0X0D);
    byte = INREG8(SEQ_DATA_REG) & 0X03;

    switch (mode) {
        case DPMSModeOn:
            break;
        case DPMSModeStandby:
            byte |= 0x10;
            break;
        case DPMSModeSuspend:
            byte |= 0x40;
            break;
        case DPMSModeOff:
            byte |= 0x50;
            break;
        default:
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid DPMS mode %d\n", mode);
            break;
    }
    OUTREG16(SEQ_ADDRESS_REG,byte << 8 | 0x0D);

    return;
}

void SavageProcessConfig(ScrnInfoPtr pScrn)
{
    SavagePtr psav= SAVPTR(pScrn);
    MessageType from = X_DEFAULT;
    char *s=NULL;
    
	xf86CollectOptions(pScrn, NULL);

    if (pScrn->depth == 8)
        pScrn->rgbBits = 6;

    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, SavageOptions);

    xf86GetOptValBool(SavageOptions, OPTION_PCI_BURST, &psav->pci_burst);

    if (psav->pci_burst) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: pci_burst - PCI burst read enabled\n");
    }

    psav->NoPCIRetry = 1;		/* default */
    if (xf86ReturnOptValBool(SavageOptions, OPTION_PCI_RETRY, FALSE)) {
        if (xf86ReturnOptValBool(SavageOptions, OPTION_PCI_BURST, FALSE)) {
            psav->NoPCIRetry = 0;
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Option: pci_retry\n");
        }
        else
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                       "\"pci_retry\" option requires \"pci_burst\"\n");
    }

    xf86GetOptValBool(SavageOptions, OPTION_SHADOW_FB, &psav->shadowFB);
    if (psav->shadowFB) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Option: shadow FB enabled\n");
    }

    psav->CrtOnly = FALSE;
    if( xf86GetOptValBool(SavageOptions, OPTION_CRT_ONLY, &psav->CrtOnly))
	xf86DrvMsg( pScrn->scrnIndex, X_CONFIG,
		    "Option: CrtOnly enabled\n" );
    
    if ((s = xf86GetOptValString(SavageOptions, OPTION_ROTATE))) {
        if(!xf86NameCmp(s, "CW")) {
            /* accel is disabled below for shadowFB */
            psav->shadowFB = TRUE;
            psav->rotate = 1;
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                       "Rotating screen clockwise - acceleration disabled\n");
        } else if(!xf86NameCmp(s, "CCW")) {
            psav->shadowFB = TRUE;
            psav->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");
        }
    }
    
    psav->NoAccel = FALSE;
    if (xf86GetOptValBool(SavageOptions, OPTION_NOACCEL, &psav->NoAccel)) {
        if (psav->NoAccel) {
            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                       "Option: NoAccel - Acceleration Disabled\n");
        }
    }

    if (psav->shadowFB && !psav->NoAccel) {
        xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                   "HW acceleration not supported with \"shadowFB\".\n");
        psav->NoAccel = TRUE;
    }

    if (pScrn->bitsPerPixel == 24 && !psav->NoAccel) {
        xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                   "HW acceleration not possible with depth 32 and bpp 24.\n");
        psav->NoAccel = TRUE;
    }

    /* Set AGP Mode from config */
    /* We support 1X 2X and 4X  */
#ifdef XF86DRI
    from = X_DEFAULT;
    /*psav->agpMode = SAVAGE_DEFAULT_AGP_MODE;*/
    psav->agpMode = SAVAGE_MAX_AGP_MODE;
    
    /* temporatly remove by Jiayo */
#if 0
    if (xf86GetOptValInteger(psav->Options,
                             OPTION_AGP_MODE, &(psav->agpMode))) {
        if (psav->agpMode < 1) {
            psav->agpMode = 1;
        }
        if (psav->agpMode > SAVAGE_MAX_AGP_MODE) {
            psav->agpMode = SAVAGE_MAX_AGP_MODE;
        }
        from = X_CONFIG;
    }
#endif

    xf86DrvMsg(pScrn->scrnIndex, from, "Using AGP %dx mode\n",
               psav->agpMode);
#endif

    /*
     * The SWCursor setting takes priority over HWCursor.  The default
     * if neither is specified is HW, unless ShadowFB is specified,
     * then SW.
     */
    from = X_DEFAULT;
    psav->hwcursor = psav->shadowFB ? FALSE : TRUE;
    if (xf86GetOptValBool(SavageOptions, OPTION_HWCURSOR, &psav->hwcursor))
        from = X_CONFIG;
    if (xf86GetOptValBool(SavageOptions, OPTION_SWCURSOR, &psav->hwcursor)) {
        psav->hwcursor = !psav->hwcursor;
        from = X_CONFIG;
    }
    xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
               psav->hwcursor ? "HW" : "SW");
    
    from = X_DEFAULT;

    if (xf86GetOptValBool( SavageOptions, OPTION_SHADOW_STATUS, &psav->ShadowStatus))
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                    "Option: ShadowStatus enabled\n");

    from = X_DEFAULT;
    psav->UseBIOS = TRUE;
    if (xf86GetOptValBool(SavageOptions, OPTION_USEBIOS, &psav->UseBIOS) )
	from = X_CONFIG;
    xf86DrvMsg(pScrn->scrnIndex, from, "%ssing video BIOS to set modes\n",
        psav->UseBIOS ? "U" : "Not u" );
    
    psav->LCDClock = 0.0;
    if (xf86GetOptValFreq(SavageOptions, OPTION_LCDCLOCK, OPTUNITS_MHZ, &psav->LCDClock)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: LCDClock %1.2f MHz\n", psav->LCDClock);
    }
    
    psav->TvOn = FALSE;
    if (xf86GetOptValBool(SavageOptions, OPTION_TV_ON, &psav->TvOn)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: TvOn %s\n",(psav->TvOn?"Enabled":"Disabled"));
    }

    /* Add more options here. */
    if (xf86GetOptValBool( SavageOptions, OPTION_SAA7111, &psav->SAA7111)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: Enable SAA7111 and Program It\n");
    }
    
    /* add by peterzhu*/
    if (xf86GetOptValBool(SavageOptions, OPTION_V4l_VIDEOIN, &psav->v4l_videoin)) {
        xf86DrvMsg( pScrn->scrnIndex, X_CONFIG,
                    "Option: Enable v4l video in and Program It\n" );
    }
    psav->v4l_devnum = 0;
    if (xf86GetOptValInteger(SavageOptions, OPTION_V4l_DEVNUM,&psav->v4l_devnum)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: V4l device number current used is %d\n", psav->v4l_devnum);
    }
    	
    if (xf86GetOptValBool(SavageOptions, OPTION_DOUBLE_BUFFER,&psav->double_buffer)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: Enable Double buffer and Program it \n");
    }

    /* we can use Option "DisableTile TRUE" to disable tile mode */
    psav->bDisableTile = TRUE; /* AGD: was FALSE; however, I gain about 30 fps 
				  defaulting to linear mode, and it seems to render
				  the same either way */
    if (xf86GetOptValBool(SavageOptions, OPTION_DISABLE_TILE,&psav->bDisableTile)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: %s Tile Mode and Program it \n",(psav->bDisableTile?"Disable":"Enable"));
    }
    psav->bDisableXvMC = FALSE; /* if you want to free up more mem for DRI,etc. */
    if (xf86GetOptValBool(SavageOptions, OPTION_DISABLE_XVMC,&psav->bDisableXvMC)) {
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "Option: %s Hardware XvMC support\n",(psav->bDisableXvMC?"Disable":"Enable"));
    }
}


Bool SavageBuildModeTable(ScrnInfoPtr pScrn)
{
    SavagePtr psav= SAVPTR(pScrn);
    ClockRangePtr clockRanges;    
    int i,j;
    
    if (psav->UseBIOS) {
        /* Go probe the BIOS for all the modes and refreshes at this depth. */
        if (psav->ModeTable) {
            SavageFreeBIOSModeTable(psav, &psav->ModeTable);
        }
        psav->ModeTable = SavageGetBIOSModeTable(psav, pScrn->bitsPerPixel);
        if (!psav->ModeTable || !(psav->ModeTable)->NumModes) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Failed to fetch any BIOS modes.  Disabling BIOS.\n");
            psav->UseBIOS = FALSE;
        }
        else {
            SavageModeEntryPtr pmt;

            xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
                       "Found %d modes supported by bios at depth %d :\n",
                       psav->ModeTable->NumModes,pScrn->bitsPerPixel);

            for (i = 0, pmt = psav->ModeTable->Modes;
                 i < psav->ModeTable->NumModes;
                 i++, pmt++ ) {
                xf86Msg(X_PROBED,
                        "    Vesa No.=%03x,Resolution=(%d x %d),Refreshes are:\n",
                        pmt->VesaMode, pmt->Width, pmt->Height);
                
                xf86Msg(X_PROBED,"        ");
                for (j = 0; j < pmt->RefreshCount; j++) {
                    xf86Msg(X_PROBED,"%dHz", pmt->RefreshRate[j] );
                }
                xf86Msg(X_PROBED,"\n");                
            }
        }
    }
    
    clockRanges = xnfalloc(sizeof(ClockRange));
    clockRanges->next = NULL;
    clockRanges->minClock = psav->minClock;
    clockRanges->maxClock = psav->maxClock;
    clockRanges->clockIndex = -1;
    clockRanges->interlaceAllowed = TRUE;
    clockRanges->doubleScanAllowed = TRUE;
    clockRanges->ClockDivFactor = 1.0;
    clockRanges->ClockMulFactor = 1.0;
    
    i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
                          pScrn->display->modes, clockRanges, NULL, 
                          256, 2048, 16 * pScrn->bitsPerPixel,
                          128, 2048, 
                          pScrn->virtualX,pScrn->virtualY,
                          psav->videoRambytes, LOOKUP_BEST_REFRESH);
    
    
    xfree(clockRanges);
    if (i == -1) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "xf86ValidateModes failure\n");
        return FALSE;
    }

    xf86PruneDriverModes(pScrn);

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

    xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V);
    pScrn->currentMode = pScrn->modes;
    xf86PrintModes(pScrn);

    xf86SetDpi(pScrn, 0, 0);

    return TRUE;
}

Bool SavageInitHardware(ScrnInfoPtr pScrn)
{
    SavagePtr psav= SAVPTR(pScrn);
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    unsigned char m, n, n1, n2, sr8, cr66 = 0;
    int mclk,endfb;
    pointer ddc;
     
	UnProtectCRTC();
    UnLockExtRegs();

    /* detect the video ram */
    if (psav->UseBIOS) {
        if (!SavageSetupDevice(psav)) {
            return FALSE;                    
        }
        pScrn->videoRam = psav->videoRambytes >> 10;
    } else {
        /* detect the video ram directly */    
        SavageDetectVidRam(pScrn);
    }

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

    UnLockExtRegs();

    /* reserved 128K for HWICON and DSTN buffer */
    endfb = psav->videoRambytes - 0x20000 - 1;
    
    /*
     * If we're running with acceleration, compute the command overflow
     * buffer location.  The command overflow buffer must END at a
     * 4MB boundary; for all practical purposes, that means the very
     * end of the frame buffer.
     */
    if (psav->NoAccel) {
        psav->cobIndex = 0;
        psav->cobSize = 0;
        psav->cobOffset = endfb;
    }
    else if (S3_SAVAGE4_SERIES(psav->Chipset)) {
        /*
         * The Savage4 and ProSavage have COB coherency bugs which render 
         * the buffer useless.
         */
        psav->cobIndex = 2;
        psav->cobSize = 0x8000 << psav->cobIndex;
        psav->cobOffset = endfb - psav->cobSize;
    }
    else {
        /* We use 128kB for the COB on all other chips. */        
        psav->cobSize = 0X20000;
        psav->cobIndex = 7;
        psav->cobOffset = endfb - psav->cobSize;
    }
             
    /* align cob to 128k */
    psav->cobOffset = ((endfb - psav->cobSize + 1) + 0x20000) & ~0x20000;
    endfb = psav->cobOffset - 1;
    
    /* The cursor must be aligned on a 4k boundary. */
    psav->CursorKByte = (endfb >> 10) - 4;
    endfb = (psav->CursorKByte << 10) - 1;
    
    if (psav->DisplayOutSupport & UT_DEVICE_TV) {    
        /*
         *  Set aside the TV scratch buffer.  The size recommended by the VDL
         *  is 128 KBytes.  Both start and end must be quadword aligned.
         */
        psav->TvInfo.TvEndScratchBuf = endfb - endfb % 8;
        psav->TvInfo.TvStartScratchBuf = psav->TvInfo.TvEndScratchBuf - 0x20000;
        endfb =  psav->TvInfo.TvStartScratchBuf -1;
    }
    
    psav->endfb = endfb;
    
    /* reset graphics engine to avoid memory corruption */
    OUTREG8(CRT_ADDRESS_REG, 0x66);
    cr66 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr66 | 0x02);
    usleep(10000);

    OUTREG8(CRT_ADDRESS_REG, 0x66);
    OUTREG8(CRT_DATA_REG, cr66 & ~0x02);	/* clear reset flag */
    usleep(10000);

    /* Set status word positions based on chip type. */
    switch (psav->Chipset) {
        case S3_SAVAGE3D:
        case S3_SAVAGE_MX:            
            psav->WaitQueue	= WaitQueue3D;
            psav->WaitIdle	= WaitIdle3D;
            psav->WaitIdleEmpty	= WaitIdleEmpty3D;
            break;

        case S3_SAVAGE4:
        case S3_PROSAVAGE:
        case S3_SUPERSAVAGE:
        case S3_TWISTER:
        case S3_PROSAVAGEDDR:
            psav->WaitQueue	= WaitQueue4;
            psav->WaitIdle	= WaitIdle4;
            psav->WaitIdleEmpty	= WaitIdleEmpty4;
            break;

        case S3_SAVAGE2000:
            psav->WaitQueue	= WaitQueue2K;
            psav->WaitIdle	= WaitIdle2K;
            psav->WaitIdleEmpty	= WaitIdleEmpty2K;
            break;
    }

    /* Do the DDC dance. */
    ddc = xf86LoadSubModule(pScrn, "ddc");
    if (ddc) {
        xf86LoaderReqSymLists(ddcSymbols, NULL);
        switch( psav->Chipset ) {
            case S3_SAVAGE3D:
            case S3_SAVAGE_MX:
            case S3_SUPERSAVAGE:
                psav->DDCPort = 0xAA;
                psav->I2CPort = 0xA0;
                break;

            case S3_SAVAGE4:
            case S3_PROSAVAGE:
            case S3_TWISTER:
            case S3_PROSAVAGEDDR:
                psav->DDCPort = 0xB1;
                psav->I2CPort = 0xA0;
                break;
                
            case S3_SAVAGE2000:
                psav->DDCPort = 0xAA;
                psav->I2CPort = 0xA0;
                break;
        }

        if (!SavageDDC1(pScrn->scrnIndex)) {
            /* DDC1 failed,switch to DDC2 */
            if (xf86LoadSubModule(pScrn, "i2c")) {
                xf86LoaderReqSymLists(i2cSymbols,NULL);
                if (SavageI2CInit(pScrn)) {
                    unsigned char tmp;
                    
                    InI2CREG(tmp,psav->DDCPort);
                    OutI2CREG(tmp | 0x13,psav->DDCPort);
                    xf86SetDDCproperties(pScrn,xf86PrintEDID(
                                             xf86DoEDID_DDC2(pScrn->scrnIndex,psav->I2C)));
                    OutI2CREG(tmp,psav->DDCPort);
                }
            }
        }
    }

    /* Savage ramdac speeds */
    pScrn->numClocks = 4;
    pScrn->clock[0] = 250000;
    pScrn->clock[1] = 250000;
    pScrn->clock[2] = 220000;
    pScrn->clock[3] = 220000;

    if (psav->dacSpeedBpp <= 0) {
        if (pScrn->bitsPerPixel > 24)
            psav->dacSpeedBpp = pScrn->clock[3];
        else if (pScrn->bitsPerPixel >= 24)
            psav->dacSpeedBpp = pScrn->clock[2];
        else if ((pScrn->bitsPerPixel > 8) && (pScrn->bitsPerPixel < 24))
            psav->dacSpeedBpp = pScrn->clock[1];
        else if (pScrn->bitsPerPixel <= 8)
            psav->dacSpeedBpp = pScrn->clock[0];
    }

    /* Set ramdac limits */
    psav->maxClock = psav->dacSpeedBpp;

    /* detect current mclk */
    OUTREG8(SEQ_ADDRESS_REG, 0x08);
    sr8 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_DATA_REG, 0x06);
    OUTREG8(SEQ_ADDRESS_REG, 0x10);
    n = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x11);
    m = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x08);
    OUTREG8(SEQ_DATA_REG, sr8);
    m &= 0x7f;
    n1 = n & 0x1f;
    n2 = (n >> 5) & 0x03;
    mclk = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
    xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
               "Detected current MCLK value of %1.3f MHz\n",
               mclk / 1000.0);

    psav->minClock = 10000;

    pScrn->maxHValue = 2048 << 3;	/* 11 bits of h_total 8-pixel units */
    pScrn->maxVValue = 2048;		/* 11 bits of v_total */
    pScrn->virtualX = pScrn->display->virtualX;
    pScrn->virtualY = pScrn->display->virtualY;

    /* Check LCD panel information */
    if (((psav->DisplayOutsActive & UT_DEVICE_LCD) 
        && (S3_SAVAGE_MOBILE_SERIES(psav->Chipset)
            || S3_MOBILE_TWISTER_SERIES(psav->Chipset))
			&& (!psav->CrtOnly))
        ) {
        unsigned char cr6b = hwp->readCrtc( hwp, 0x6b );

        int panelX = (hwp->readSeq(hwp, 0x61) + 
                      ((hwp->readSeq(hwp, 0x66) & 0x02) << 7) + 1) * 8;
        int panelY = hwp->readSeq(hwp, 0x69) + 
            ((hwp->readSeq(hwp, 0x6e) & 0x70) << 4) + 1;

        char * sTechnology = "Unknown";

        /*
         * OK, I admit it.  I don't know how to limit the max dot clock
         * for LCD panels of various sizes.  I thought I copied the formula
         * from the BIOS, but many users have informed me of my folly.
         *
         * Instead, I'll abandon any attempt to automatically limit the 
         * clock, and add an LCDClock option to XF86Config.  Some day,
         * I should come back to this.
         */

        enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */
            ActiveCRT = 0x01,
            ActiveLCD = 0x02,
            ActiveTV = 0x04,
            ActiveCRT2 = 0x20,
            ActiveDUO = 0x80
        };

        if ((hwp->readSeq( hwp, 0x39 ) & 0x03) == 0) {
            sTechnology = "TFT";
        }
        else if((hwp->readSeq( hwp, 0x30 ) & 0x01) == 0) {
            sTechnology = "DSTN";
        }
        else {
            sTechnology = "STN";
        }

        xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
                   "%dx%d %s LCD panel detected %s\n",
                   panelX, panelY, sTechnology,
                   cr6b & ActiveLCD ? "and active" : "but not active");

        if (cr6b & ActiveLCD) {
            /* If the LCD is active and panel expansion is enabled, */
            /* we probably want to kill the HW cursor. */

            xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
                       "- Limiting video mode to %dx%d\n",
                       panelX, panelY );

            psav->PanelX = panelX;
            psav->PanelY = panelY;
            psav->LcdOn = TRUE;
	    
            if (psav->LCDClock > 0.0) {
                psav->maxClock = psav->LCDClock * 1000.0;
                xf86DrvMsg( pScrn->scrnIndex, X_CONFIG,
                            "- Limiting dot clock to %1.2f MHz\n",
                            psav->LCDClock );
            }
        }
    }
    
    return TRUE;
}


static int SavageSubScreenInit(int scrnIndex, ScreenPtr pScreen)
{
    int ret = TRUE;
    ScrnInfoPtr pScrn;
    SavagePtr psav;
    int width, height, displayWidth;
    unsigned char *FBStart;

    TRACE(("SavageSubScreenInit()\n"));

    pScrn = xf86Screens[pScreen->myNum];
    psav = SAVPTR(pScrn);

    displayWidth = pScrn->displayWidth;

    if (psav->rotate) {
        height = pScrn->virtualX;
        width = pScrn->virtualY;
    } else {
        width = pScrn->virtualX;
        height = pScrn->virtualY;
    }
  

    if(psav->shadowFB) {
        psav->ShadowPitch = BitmapBytePad(pScrn->bitsPerPixel * width);
        psav->ShadowPtr = xalloc(psav->ShadowPitch * height);
        displayWidth = psav->ShadowPitch / (pScrn->bitsPerPixel >> 3);
        FBStart = psav->ShadowPtr;
    } else {
        psav->ShadowPtr = NULL;
        FBStart = psav->FBStart;
    }
    
    ret = fbScreenInit(pScreen, FBStart, width, height,
                       pScrn->xDpi, pScrn->yDpi,
                       psav->ulAperturePitch / (pScrn->bitsPerPixel >> 3),
                       pScrn->bitsPerPixel);
                           
    return ret;
}


static void
SavageProbeDDC(ScrnInfoPtr pScrn, int index)
{
    SavagePtr psav = SAVPTR(pScrn);
    ConfiguredMonitor = vbeDoEDID(psav->pVbe, NULL);
}



static unsigned int
SavageDDC1Read(ScrnInfoPtr pScrn)
{
    register unsigned char tmp;
    SavagePtr psav = SAVPTR(pScrn);

    UnLockExtRegs();
    
    VerticalRetraceWait();
    
    InI2CREG(tmp,psav->I2CPort);
    
    return ((unsigned int) (tmp & 0x08));
}

static Bool
SavageDDC1(int scrnIndex)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    SavagePtr psav = SAVPTR(pScrn);
    uchar byte;
    xf86MonPtr pMon;

    UnLockExtRegs();
    
    /* initialize chipset */
    InI2CREG(byte,psav->I2CPort);
    OutI2CREG(byte | 0x12,psav->I2CPort);

    pMon = xf86DoEDID_DDC1(scrnIndex,vgaHWddc1SetSpeed,SavageDDC1Read);
    if (!pMon)
        return FALSE;
    
    xf86PrintEDID(pMon);
    
    xf86SetDDCproperties(pScrn,pMon);

    /* undo initialization */
    OutI2CREG(byte,psav->I2CPort);

    return TRUE;
}


static Bool SavageAssertMode(ScrnInfoPtr pScrn,DisplayModePtr mode)
{
    unsigned char byte,cr67,cr6d,cr79;
    SavagePtr psav = SAVPTR(pScrn);
    int modeNum=0,refresh,dclk;
    
    TRACE(("SavageAsseWriteMode(%x)\n"));
    
#ifdef XF86DRI
    if (psav->directRenderingEnabled) {
        DRILock(screenInfo.screens[pScrn->scrnIndex], 0);
        psav->LockHeld = 1;
    }
#endif

    if (!psav->UseBIOS) {
	SavageNoBiosModeInit(pScrn, mode);
    	SavageEnableMode(pScrn,TRUE);
    	SavageAdjustFrame(pScrn->scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
#ifdef XF86DRI
    	if (psav->directRenderingEnabled)
    	    DRIUnlock(screenInfo.screens[pScrn->scrnIndex]);
    	psav->LockHeld = 0;
#endif
	return TRUE;
    }

    if (psav->UseBIOS) {
        refresh = (mode->Clock * 1000) / (mode->HTotal * mode->VTotal);        
        SavageMatchBiosMode(pScrn,mode->HDisplay,mode->VDisplay,refresh,
                            &modeNum,&refresh);
    }

    if (!modeNum) {
        /* 
         * Either BIOS use is disabled, or we failed to find a suitable
         * match.  Fall back to traditional register-crunching.
         */
        return FALSE;
    }

    /*
     * If we figured out a VESA mode number for this timing, just use
     * the S3 BIOS to do the switching, with a few additional tweaks.
     */
    /* make sure the start address has been reset to 0 */
    OUTREG16(CRT_ADDRESS_REG, (ushort) 0X0D);
    OUTREG16(CRT_ADDRESS_REG, (ushort) 0X0C);
    OUTREG16(CRT_ADDRESS_REG, (ushort) 0X69);
    
    if (pScrn->vtSema)
        SavageSetVESAMode(psav,modeNum | 0x8000,refresh);
    else
        SavageSetVESAMode(psav,modeNum,refresh);
    
    pScrn->vtSema = TRUE;
    
    UnLockExtRegs();
    
    /*
     *  After mode set, the BIOS may have turned off linear addressing.
     *  Be sure to re-enable it before doing any I/O!
     */
    OUTREG16(CRT_ADDRESS_REG, 0x1358);

    /*  Disable old MMIO  (just a safeguard) */
    OUTREG8(CRT_ADDRESS_REG, 0x53);
    byte = INREG8(CRT_DATA_REG) & ~0X10;
    OUTREG8(CRT_DATA_REG,byte);

    /* Set the color mode. */
    dclk = mode->Clock;
    cr67 = 0x00;

    switch (pScrn->depth) {
        case 8:
            if ((psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000))
                cr67 = 0x10;	/* 8bpp, 2 pixels/clock */
            else
                cr67 = 0x00;	/* 8bpp, 1 pixel/clock */
            break;
        case 15:
            if (S3_SAVAGE_MOBILE_SERIES(psav->Chipset)
                || ((psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000)))
                cr67 = 0x30;	/* 15bpp, 2 pixel/clock */
            else
                cr67 = 0x20;	/* 15bpp, 1 pixels/clock */
            break;
        case 16:
            if (S3_SAVAGE_MOBILE_SERIES(psav->Chipset)
                || ((psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000)))
                cr67 = 0x50;	/* 16bpp, 2 pixel/clock */
            else
                cr67 = 0x40;	/* 16bpp, 1 pixels/clock,RAMDAC Color Mode:5.6.5 */
            break;
        case 24:
            if (pScrn->bitsPerPixel == 24 )
                cr67 = 0x70;
            else
                cr67 = 0xd0;
            break;
    }

    OUTREG8(CRT_ADDRESS_REG, 0x67);
    OUTREG8(CRT_DATA_REG,cr67);

    /* Enable gamma correction. */

    OUTREG8(SEQ_ADDRESS_REG, 0x1b);
    if( (pScrn->bitsPerPixel == 32) && !psav->DGAactive )
	OUTREG8(SEQ_DATA_REG, 0x28 );
    else
	OUTREG8(SEQ_DATA_REG, 0x00 );

    /* Set FIFO fetch delay. */
    OUTREG8(CRT_ADDRESS_REG, 0x85);
    byte = (INREG8(CRT_DATA_REG) & 0XF8) | 0X03;
    OUTREG8(CRT_DATA_REG,byte);

    /* Patch CR79.  These values are magical. */
    if (!S3_SAVAGE_MOBILE_SERIES(psav->Chipset)) {
        OUTREG8(CRT_ADDRESS_REG, 0x6d);
        cr6d = INREG8(CRT_DATA_REG);

        cr79 = 0x04;

        if (pScrn->displayWidth >= 1024) {
            if (pScrn->bitsPerPixel == 32) {
                if (refresh >= 130)
                    cr79 = 0x03;
                else if (pScrn->displayWidth >= 1280)
                    cr79 = 0x02;
                else if ((pScrn->displayWidth == 1024)
                         && (refresh >= 75)) {
                    if (cr6d && LCD_ACTIVE)
                        cr79 = 0x05;
                    else
                        cr79 = 0x08;
                }
            }
            else if (pScrn->bitsPerPixel == 16) {

                /*
                 * The windows driver uses 0x13 for 16-bit 130Hz, but I see terrible
                 * screen artifacts with that value.  Let's keep it low for now.
                 *		if( restore->refresh >= 130 )
                 *		    cr79 = 0x13;
                 *		else
                 */
                if (pScrn->displayWidth == 1024) {
                    if( cr6d && LCD_ACTIVE )
                        cr79 = 0x08;
                    else
                        cr79 = 0x0e;
                }
            }
        }
    }

    if ((psav->Chipset != S3_SAVAGE2000)
        && !S3_SAVAGE_MOBILE_SERIES(psav->Chipset)) {
        OUTREG16(CRT_ADDRESS_REG, (cr79 << 8) | 0x79);
    }
        
    /*
     * Make sure 16-bit memory reads/writes are enabled.
     * CR31  bit 2 set = Enable 16-bit bus VGA  reads/writes.
     */
    OUTREG8(CRT_ADDRESS_REG, 0x31);
    byte = INREG8(CRT_DATA_REG) | 0x04;
    OUTREG8(CRT_DATA_REG,byte);

    /* Enable the graphics engine. */
    OUTREG16(CRT_ADDRESS_REG, 0x0140);
        
    SavageEnableMode(pScrn,TRUE);

    SavageAdjustFrame(pScrn->scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);

#ifdef XF86DRI
    if (psav->directRenderingEnabled)
        DRIUnlock(screenInfo.screens[pScrn->scrnIndex]);
    psav->LockHeld = 0;
#endif

    return TRUE;
}

void SavageEnableMode_Twister(ScrnInfoPtr pScrn,BOOL bEnable)
{
    SavagePtr psav = SAVPTR(pScrn);
    ulong       ulTmp;
    uchar byte;

    if (!bEnable) {
        /* reset primary stream stride register */
        OUTREG32(PRI_STREAM_STRIDE,0);
        OUTREG32(PRI_STREAM_FBUF_ADDR0,0);
        OUTREG32(PRI_STREAM_FBUF_ADDR1,0);
        
        OUTREG8(CRT_ADDRESS_REG,0X69);
        byte = INREG8(CRT_DATA_REG) & (~0X80);
        OUTREG8(CRT_DATA_REG,byte);

        return;
    }
    
    /* Disable BCI */
    OUTREG32(0x48C18, INREG32(0x48C18) & 0x3FF0);
    /* Setup BCI command overflow buffer */
    OUTREG32(0x48C14, (psav->cobOffset >> 11) | (psav->cobIndex << 29));
    /* Program shadow status update */
    OUTREG32(0x48C10, 0x0e440f04L);
    OUTREG32(0x48C0C, 0);
    OUTREG32(0x48C18, INREG32(0x48C18) | 0x0C);
    
    /* MM81C0 and 81C4 are used to control primary stream. */
    OUTREG32(PRI_STREAM_FBUF_ADDR0,0x00000000);
    OUTREG32(PRI_STREAM_FBUF_ADDR1,0x00000000);
    
    /*
     *  Program Primary Stream Stride Register.
     *
     *  Tell engine if tiling on or off, set primary stream stride, and
     *  if tiling, set tiling bits/pixel and primary stream tile offset.
     *  Note that tile offset (bits 16 - 29) must be scanline width in
     *  bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
     *  lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
     *  bytes padded up to an even number of tilewidths.
     */
    if (!psav->bTiled) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFFE000) |
                 (psav->lDelta & 0x00001fff));
    }
    else if (pScrn->bitsPerPixel == 16) {
        /* Scanline-length-in-bytes/128-bytes-per-tile * 256 Qwords/tile */
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFFE000)
                 | 0x80000000 | (psav->lDelta & 0x00001fff));
    }
    else if (pScrn->bitsPerPixel == 32) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFFE000)
                 | 0xC0000000 | (psav->lDelta & 0x00001fff));
    }

    /*
     *  CR69, bit 7 = 1
     *  to use MM streams processor registers to control primary stream.
     */
    OUTREG8(CRT_ADDRESS_REG,0X69);
    byte = INREG8(CRT_DATA_REG) | 0X80;
    OUTREG8(CRT_DATA_REG,byte);

    OUTREG32(0x8128, 0xFFFFFFFFL);
    OUTREG32(0x812C, 0xFFFFFFFFL);

    OUTREG32(0x816C,BCI_ENABLE | S3_LITTLE_ENDIAN | S3_BD64);

    if (psav->bTiled) {
        /* CR50, bit 7,6,0 = 111, Use GBD.*/
        OUTREG8(CRT_ADDRESS_REG,0X50);
        byte = INREG8(CRT_DATA_REG) | 0XC1;
        OUTREG8(CRT_DATA_REG, byte);
    }

    /*
     * if MS1NB style linear tiling mode.
     * bit MM850C[15] = 0 select NB linear tile mode.
     * bit MM850C[15] = 1 select MS-1 128-bit non-linear tile mode.
     */
    ulTmp = INREG32(0X850C) | 0x8000; /* use MS-s style tile mode*/
    OUTREG32(0X850C,ulTmp);

    /*
     * Set up Tiled Surface Registers
     *  Bit 25:20 - Surface width in tiles.
     *  Bit 29 - Y Range Flag.
     *  Bit 31:30 = 00, 4 bpp.
     *            = 01, 8 bpp.
     *            = 10, 16 bpp.
     *            = 11, 32 bpp.
     */
    /*
     * Global Bitmap Descriptor Register MM816C
     *   bit 24~25: tile format
     *          00: linear 
     *          01: destination tiling format
     *          10: texture tiling format
     *          11: reserved
     *   bit 29: block write disble/enable
     *          0: disable
     *          1: enable
     */
    if (!psav->bTiled) {
        /*
         *  Do not enable block_write even for non-tiling modes, because
         *  the driver cannot determine if the memory type is the certain
         *  type of SGRAM for which block_write can be used.
         */
        psav->GlobalBD.bd1.HighPart.ResBWTile = 0;/* linear */
    }
    else if (pScrn->bitsPerPixel == 16) {
        psav->GlobalBD.bd1.HighPart.ResBWTile = 1;/* destination tiling format */
        
        ulTmp = (((pScrn->virtualX + 0x3F) & 0x0000FFC0) >> 6) << 20;
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp | TILED_SURF_BPP16);
    }
    else if (pScrn->bitsPerPixel == 32) {

        psav->GlobalBD.bd1.HighPart.ResBWTile = 1;/* destination tiling format */
        
        ulTmp = ( ((pScrn->virtualX + 0x1F) & 0x0000FFE0) >> 5) << 20;
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp|TILED_SURF_BPP32);
    }

    psav->GlobalBD.bd1.HighPart.ResBWTile |= 0;/* disable block write */
    /* HW uses width */
    psav->GlobalBD.bd1.HighPart.Stride = (ushort) psav->lDelta / (pScrn->bitsPerPixel >> 3);
    psav->GlobalBD.bd1.HighPart.Bpp = (uchar) (pScrn->bitsPerPixel);
    psav->GlobalBD.bd1.Offset = 0;
    

    /*
     * CR88, bit 4 - Block write enabled/disabled.
     *
     *      Note: Block write must be disabled when writing to tiled
     *            memory.  Even when writing to non-tiled memory, block
     *            write should only be enabled for certain types of SGRAM.
     */
    OUTREG8(CRT_ADDRESS_REG,0X88);
    byte = INREG8(CRT_DATA_REG) | DISABLE_BLOCK_WRITE_2D;
    OUTREG8(CRT_DATA_REG,byte);

    /*
     * CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0).
     *       bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window
     *                  at A000:0.
     */
    OUTREG8(CRT_ADDRESS_REG,0X31);
    byte = INREG8(CRT_DATA_REG) & (~(ENABLE_CPUA_BASE_A0000));
    OUTREG8(CRT_DATA_REG,byte);

    /* turn on screen */
    OUTREG8(SEQ_ADDRESS_REG,0x01);
    byte = INREG8(SEQ_DATA_REG) & ~0X20;
    OUTREG8(SEQ_DATA_REG,byte);
    
    /* program the GBD */
    OUTREG32(S3_GLB_BD_LOW,psav->GlobalBD.bd2.LoPart);
    OUTREG32(S3_GLB_BD_HIGH,psav->GlobalBD.bd2.HiPart | BCI_ENABLE | S3_LITTLE_ENDIAN | S3_BD64);
}

void SavageEnableMode_Savage4(ScrnInfoPtr pScrn,BOOL bEnable)
{
    SavagePtr psav = SAVPTR(pScrn);
    ulong       ulTmp;
    uchar byte;

    if (!bEnable) {
        /* reset primary stream stride register */
        OUTREG32(PRI_STREAM_STRIDE,0);
        OUTREG32(PRI_STREAM_FBUF_ADDR0,0);
        OUTREG32(PRI_STREAM_FBUF_ADDR1,0);
        
        OUTREG8(CRT_ADDRESS_REG,0X69);
        byte = INREG8(CRT_DATA_REG) & (~0X80);
        OUTREG8(CRT_DATA_REG,byte);

        return;
    }
    
    /* Disable BCI */
    OUTREG32(0x48C18, INREG32(0x48C18) & 0x3FF0);
    /* Setup BCI command overflow buffer */
    OUTREG32(0x48C14, (psav->cobOffset >> 11) | (psav->cobIndex << 29));
    /* Program shadow status update */
    OUTREG32(0x48C10, 0x0e440f04L);
    OUTREG32(0x48C0C, 0);
    OUTREG32(0x48C18, INREG32(0x48C18) | 0x0C);
    
    /* MM81C0 and 81C4 are used to control primary stream. */
    OUTREG32(PRI_STREAM_FBUF_ADDR0,0x00000000);
    OUTREG32(PRI_STREAM_FBUF_ADDR1,0x00000000);
    
    /*
     *  Program Primary Stream Stride Register.
     *
     *  Tell engine if tiling on or off, set primary stream stride, and
     *  if tiling, set tiling bits/pixel and primary stream tile offset.
     *  Note that tile offset (bits 16 - 29) must be scanline width in
     *  bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
     *  lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
     *  bytes padded up to an even number of tilewidths.
     */
    if (!psav->bTiled) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFFE000) |
                 (psav->lDelta & 0x00001fff));
    }
    else if (pScrn->bitsPerPixel == 16) {
        /* Scanline-length-in-bytes/128-bytes-per-tile * 256 Qwords/tile */
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFFE000)
                 | 0x80000000 | (psav->lDelta & 0x00001fff));
    }
    else if (pScrn->bitsPerPixel == 32) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFFE000)
                 | 0xC0000000 | (psav->lDelta & 0x00001fff));
    }

    /*
     *  CR69, bit 7 = 1
     *  to use MM streams processor registers to control primary stream.
     */
    OUTREG8(CRT_ADDRESS_REG,0X69);
    byte = INREG8(CRT_DATA_REG) | 0X80;
    OUTREG8(CRT_DATA_REG,byte);

    OUTREG32(0x8128, 0xFFFFFFFFL);
    OUTREG32(0x812C, 0xFFFFFFFFL);
    OUTREG32(0x816C, 8 | S3_LITTLE_ENDIAN | S3_BD64);


    if (psav->bTiled) {
        /* CR50, bit 7,6,0 = 111, Use GBD.*/
        OUTREG8(CRT_ADDRESS_REG,0X50);
        byte = INREG8(CRT_DATA_REG) | 0XC1;
        OUTREG8(CRT_DATA_REG, byte);
    }

    /*
     * if MS1NB style linear tiling mode.
     * bit MM850C[15] = 0 select NB linear tile mode.
     * bit MM850C[15] = 1 select MS-1 128-bit non-linear tile mode.
     */
    ulTmp = INREG32(0X850C) | 0x8000; /* use MS-s style tile mode*/
    OUTREG32(0X850C,ulTmp);

    /*
     * Set up Tiled Surface Registers
     *  Bit 25:20 - Surface width in tiles.
     *  Bit 29 - Y Range Flag.
     *  Bit 31:30 = 00, 4 bpp.
     *            = 01, 8 bpp.
     *            = 10, 16 bpp.
     *            = 11, 32 bpp.
     */
    /*
     * Global Bitmap Descriptor Register MM816C
     *   bit 24~25: tile format
     *          00: linear 
     *          01: reserved
     *          10: 16 bpp tiles
     *          11: 32 bpp tiles
     *   bit 28: block write disable/enable
     *          0: enable
     *          1: disable
     */
    if (!psav->bTiled) {
        /*
         *  Do not enable block_write even for non-tiling modes, because
         *  the driver cannot determine if the memory type is the certain
         *  type of SGRAM for which block_write can be used.
         */
        psav->GlobalBD.bd1.HighPart.ResBWTile = 0;/* linear */
    }
    else if (pScrn->bitsPerPixel == 16) {
        psav->GlobalBD.bd1.HighPart.ResBWTile = 2;/* 16 bpp tiling format */
        
        ulTmp = (((pScrn->virtualX + 0x3F) & 0x0000FFC0) >> 6) << 20;
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp | TILED_SURF_BPP16);
    }
    else if (pScrn->bitsPerPixel == 32) {
        psav->GlobalBD.bd1.HighPart.ResBWTile = 3;/* 32 bpp tiling format */
        
        ulTmp = ( ((pScrn->virtualX + 0x1F) & 0x0000FFE0) >> 5) << 20;
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp | TILED_SURF_BPP32);
    }

    psav->GlobalBD.bd1.HighPart.ResBWTile |= 0x10;/* disable block write */
    /* HW uses width */
    psav->GlobalBD.bd1.HighPart.Stride = (ushort) psav->lDelta / (pScrn->bitsPerPixel >> 3);
    psav->GlobalBD.bd1.HighPart.Bpp = (uchar) (pScrn->bitsPerPixel);
    psav->GlobalBD.bd1.Offset = 0;
    

    /*
     * CR88, bit 4 - Block write enabled/disabled.
     *
     *      Note: Block write must be disabled when writing to tiled
     *            memory.  Even when writing to non-tiled memory, block
     *            write should only be enabled for certain types of SGRAM.
     */
    OUTREG8(CRT_ADDRESS_REG,0X88);
    byte = INREG8(CRT_DATA_REG) | DISABLE_BLOCK_WRITE_2D;
    OUTREG8(CRT_DATA_REG,byte);

    /*
     * CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0).
     *       bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window
     *                  at A000:0.
     */
    OUTREG8(CRT_ADDRESS_REG,0X31);
    byte = INREG8(CRT_DATA_REG) & (~(ENABLE_CPUA_BASE_A0000));
    OUTREG8(CRT_DATA_REG,byte);

    /* turn on screen */
    OUTREG8(SEQ_ADDRESS_REG,0x01);
    byte = INREG8(SEQ_DATA_REG) & ~0X20;
    OUTREG8(SEQ_DATA_REG,byte);
    
    /* program the GBD */
    OUTREG32(S3_GLB_BD_LOW,psav->GlobalBD.bd2.LoPart);
    OUTREG32(S3_GLB_BD_HIGH,psav->GlobalBD.bd2.HiPart | 8 | S3_LITTLE_ENDIAN | S3_BD64);

 /* AGD: DRI seems to use PBD for front/back/depth buffers, 
    but writing to them here doesn't seem to hurt anything so... */
    OUTREG32(S3_PRI_BD_LOW,psav->GlobalBD.bd2.LoPart);
    OUTREG32(S3_PRI_BD_HIGH,psav->GlobalBD.bd2.HiPart);
    OUTREG32(S3_SEC_BD_LOW,psav->GlobalBD.bd2.LoPart);
    OUTREG32(S3_SEC_BD_HIGH,psav->GlobalBD.bd2.HiPart);

}

void SavageEnableMode_M7(ScrnInfoPtr pScrn,BOOL bEnable)
{
    SavagePtr psav = SAVPTR(pScrn);
    ulong ulTmp;
    uchar byte;

    if (!bEnable) {
        /* non dual-head mode code path */
        /* IGA 1 */
        OUTREG32(PRI_STREAM_STRIDE,0);
        OUTREG32(PRI_STREAM2_STRIDE, 0);
        
        OUTREG32(PRI_STREAM_FBUF_ADDR0,0x00000000);
        OUTREG32(PRI_STREAM_FBUF_ADDR1,0x00000000);
        OUTREG32(PRI_STREAM2_FBUF_ADDR0,0x00000000);
        OUTREG32(PRI_STREAM2_FBUF_ADDR1,0x00000000);

        /* MM81C0 and 81C4 are used to control primary stream. */
        OUTREG8(CRT_ADDRESS_REG,0x67); 
        byte =  INREG8(CRT_DATA_REG) & ~0x08;
        OUTREG8(CRT_DATA_REG,byte);

        /* IGA 2 */
        OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA2_READS_WRITES);
        
        OUTREG8(CRT_ADDRESS_REG,0x67); 
        byte =  INREG8(CRT_DATA_REG) & ~0x08;
        OUTREG8(CRT_DATA_REG,byte);
        
        OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA1);

        return;
    }
    

    /* following is the enable case */
    
    /* disable BCI and program the COB,and then enable BCI */
    ulTmp= INREG32(S3_OVERFLOW_BUFFER_PTR); /* 0x48c18 */
    OUTREG32(S3_OVERFLOW_BUFFER_PTR, ulTmp & (~(ENABLE_BCI)));

    /* 0x48c14
     * Bits 0-11  = Bits 22-11 of the Command Buffer Offset.
     * Bits 12-28 = Total number of entries in the command buffer(Read only).
     * Bits 29-31 = COB size, 111 = 32K entries or 128K bytes
     *              (each entry is 4 bytes).
     */
    OUTREG32(S3_OVERFLOW_BUFFER,  
             psav->cobOffset >> 11 | 0xE0000000);

    ulTmp = INREG32(S3_OVERFLOW_BUFFER_PTR) & (~(ENABLE_BCI | ENABLE_COMMAND_OVERFLOW_BUF));
    
    /* Enable BCI, Enable  Command buffer. */
    OUTREG32(S3_OVERFLOW_BUFFER_PTR,
             (ulTmp | (ENABLE_BCI | ENABLE_COMMAND_OVERFLOW_BUF)));

    /* SR01:turn off screen */
    OUTREG8 (SEQ_ADDRESS_REG,0x01);
    byte = INREG8(SEQ_DATA_REG) | 0x20;
    OUTREG8(SEQ_DATA_REG,byte);

    /*
     * CR67_3:
     *  = 1  stream processor MMIO address and stride register
     *       are used to control the primary stream
     *  = 0  standard VGA address and stride registers
     *       are used t control the primary streams
     */
    OUTREG8(CRT_ADDRESS_REG,0x67); 
    byte =  INREG8(CRT_DATA_REG) | 0x08;
    OUTREG8(CRT_DATA_REG,byte);
    
    /* IGA 2 */
    OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA2_READS_WRITES);

    OUTREG8(CRT_ADDRESS_REG,0x67); 
    byte =  INREG8(CRT_DATA_REG) | 0x08;
    OUTREG8(CRT_DATA_REG,byte);
             
    OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA1);

    /* Set primary stream to bank 0 */
    OUTREG8(CRT_ADDRESS_REG, MEMORY_CTRL0_REG);/* CRCA */
    byte =  INREG8(CRT_DATA_REG) & ~(MEM_PS1 + MEM_PS2) ;
    OUTREG8(CRT_DATA_REG,byte);
    /*
     * if we have 8MB of frame buffer here then we must really be a 16MB
     * card and that means that the second device is always in the upper
     * bank of memory (MHS)
     */
    if (psav->videoRambytes >= 0x800000) {
        /* 16MB Video Memory cursor is at the end in Bank 1 */
        byte |= 0x3;
        OUTREG16(CRT_ADDRESS_REG, (byte << 8) | MEMORY_CTRL0_REG);
    }

    /* MM81C0 and 81C4 are used to control primary stream. */
    OUTREG32(PRI_STREAM_FBUF_ADDR0,0x00000000);
    OUTREG32(PRI_STREAM_FBUF_ADDR1,0x00000000);
    OUTREG32(PRI_STREAM2_FBUF_ADDR0,0x00000000);
    OUTREG32(PRI_STREAM2_FBUF_ADDR1,0x00000000);

    /*
     *  Program Primary Stream Stride Register.
     *
     *  Tell engine if tiling on or off, set primary stream stride, and
     *  if tiling, set tiling bits/pixel and primary stream tile offset.
     *  Note that tile offset (bits 16 - 29) must be scanline width in
     *  bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
     *  lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
     *  bytes padded up to an even number of tilewidths.
     */
    if (!psav->bTiled) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000) |
                 (psav->lDelta & 0x00003fff));
        OUTREG32(PRI_STREAM2_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000) |
                 (psav->lDelta & 0x00003fff));
    } else if (pScrn->bitsPerPixel == 16) {
        /* Scanline-length-in-bytes/128-bytes-per-tile * 256 Qwords/tile */
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0x80000000 | (psav->lDelta & 0x00003fff));
        OUTREG32(PRI_STREAM2_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0x80000000 | (psav->lDelta & 0x00003fff));
        
    } else if (pScrn->bitsPerPixel == 32) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0xC0000000 | (psav->lDelta & 0x00003fff));
        OUTREG32(PRI_STREAM2_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0xC0000000 | (psav->lDelta & 0x00003fff));
    }

    OUTREG32(0x8128, 0xFFFFFFFFL);
    OUTREG32(0x812C, 0xFFFFFFFFL);

    OUTREG32(0x816C, ENABLE_BCI | S3_LITTLE_ENDIAN | S3_BD64);
    
    if (psav->bTiled) {
        /* CR50, bit 7,6,0 = 111, Use GBD.*/
        OUTREG8(CRT_ADDRESS_REG,0X50);
        byte = INREG8(CRT_DATA_REG) | 0XC1;
        OUTREG8(CRT_DATA_REG, byte);
    }

    /*
     * CR78, bit 3  - Block write enabled(1)/disabled(0).
     *       bit 2  - Block write cycle time(0:2 cycles,1: 1 cycle)
     *      Note: Block write must be disabled when writing to tiled
     *            memory.  Even when writing to non-tiled memory, block
     *            write should only be enabled for certain types of SGRAM.
     */
    OUTREG8(CRT_ADDRESS_REG,0X78);
    /*byte = INREG8(CRT_DATA_REG) & ~0x0C;*/
    byte = INREG8(CRT_DATA_REG) | 0xfb;
    OUTREG8(CRT_DATA_REG,byte);
    
    /*
     * Tiled Surface 0 Registers MM48C40:
     *  bit 0~23: tile surface 0 frame buffer offset
     *  bit 24~29:tile surface 0 width
     *  bit 30~31:tile surface 0 bits/pixel
     *            00: reserved
     *            01, 8 bits
     *            10, 16 Bits.
     *            11, 32 Bits.
     */
    /*
     * Global Bitmap Descriptor Register MM816C
     *   bit 24~25: tile format
     *          00: linear
     *          01: reserved
     *          10: 16 bit
     *          11: 32 bit
     *   bit 29: block write disble/enable
     *          0: enable
     *          1: disable
     */
    if (!psav->bTiled) {
        /*
         *  Do not enable block_write even for non-tiling modes, because
         *  the driver cannot determine if the memory type is the certain
         *  type of SGRAM for which block_write can be used.
         */
        psav->GlobalBD.bd1.HighPart.ResBWTile = 0;/* linear */

    }
    else if (pScrn->bitsPerPixel == 16) {
        psav->GlobalBD.bd1.HighPart.ResBWTile = 2;/* 16 bit */

            ulTmp =  ((psav->lDelta / 2) >> 6) << 24;
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp | TILED_SURF_BPP16);
    }
    else if (pScrn->bitsPerPixel == 32) {
        psav->GlobalBD.bd1.HighPart.ResBWTile = 3;/* 32 bit */
     
            ulTmp =  ((psav->lDelta / 4) >> 5) << 24;        
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp | TILED_SURF_BPP32);
    }
    
    psav->GlobalBD.bd1.HighPart.ResBWTile |= 0x10;/* disable block write */
    /* HW uses width */
    psav->GlobalBD.bd1.HighPart.Stride = (ushort)(psav->lDelta / (pScrn->bitsPerPixel >> 3));
    psav->GlobalBD.bd1.HighPart.Bpp = (uchar) (pScrn->bitsPerPixel);
    psav->GlobalBD.bd1.Offset = 0;    


    /*
     * CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0).
     *       bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window
     *                  at A000:0.
     */
    OUTREG8(CRT_ADDRESS_REG,MEMORY_CONFIG_REG);
    byte = INREG8(CRT_DATA_REG) & (~(ENABLE_CPUA_BASE_A0000));
    OUTREG8(CRT_DATA_REG,byte);

    /* program the GBD */
    OUTREG32(S3_GLB_BD_LOW,psav->GlobalBD.bd2.LoPart );
    /* 8: bci enable */
    OUTREG32(S3_GLB_BD_HIGH,(psav->GlobalBD.bd2.HiPart
                             | 8 | S3_LITTLE_ENDIAN | S3_BD64));

 /* AGD: DRI seems to use PBD for front/back/depth buffers, 
    but writing to them here doesn't seem to hurt anything so... */
    OUTREG32(S3_PRI_BD_LOW,psav->GlobalBD.bd2.LoPart);
    OUTREG32(S3_PRI_BD_HIGH,psav->GlobalBD.bd2.HiPart);
    OUTREG32(S3_SEC_BD_LOW,psav->GlobalBD.bd2.LoPart);
    OUTREG32(S3_SEC_BD_HIGH,psav->GlobalBD.bd2.HiPart);


    /* turn on screen */
    OUTREG8(SEQ_ADDRESS_REG,0x01);
    byte = INREG8(SEQ_DATA_REG) & ~0X20;
    OUTREG8(SEQ_DATA_REG,byte);
}
                        
void SavageEnableMode_PM(ScrnInfoPtr pScrn,BOOL bEnable)
{
    SavagePtr psav = SAVPTR(pScrn);
    ulong ulTmp;
    uchar byte;

    if (!bEnable) {
        /* non dual-head mode code path */
        /* IGA 1 */
        OUTREG32(PRI_STREAM_STRIDE,0);
        OUTREG32(PRI_STREAM2_STRIDE, 0);
        
        OUTREG32(PRI_STREAM_FBUF_ADDR0,0x00000000);
        OUTREG32(PRI_STREAM_FBUF_ADDR1,0x00000000);
        OUTREG32(PRI_STREAM2_FBUF_ADDR0,0x00000000);
        OUTREG32(PRI_STREAM2_FBUF_ADDR1,0x00000000);

        /* MM81C0 and 81C4 are used to control primary stream. */
        OUTREG8(CRT_ADDRESS_REG,0x67); 
        byte =  INREG8(CRT_DATA_REG) & ~0x08;
        OUTREG8(CRT_DATA_REG,byte);

        /* IGA 2 */
        OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA2_READS_WRITES);
        
        OUTREG8(CRT_ADDRESS_REG,0x67); 
        byte =  INREG8(CRT_DATA_REG) & ~0x08;
        OUTREG8(CRT_DATA_REG,byte);
        
        OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA1);

        return;
    }
    

    /* following is the enable case */
    
    /* disable BCI and program the COB,and then enable BCI */
    ulTmp= INREG32(S3_OVERFLOW_BUFFER_PTR); /* 0x48c18 */
    OUTREG32(S3_OVERFLOW_BUFFER_PTR, ulTmp & (~(ENABLE_BCI)));    
    
    /*
     * Bits 14-0  = Bits 25-11 of the Command Buffer Offset.
     * Bits 28-15 = Total number of entries in the command buffer(Read only).
     * Bits 31-29 = COB size, number of entries (each entry is 4 bytes).
     *              000 = 8K   entries
     *              001 = 16K  entries
     *              010 = 32K  entries
     *              011 = 64K  entries
     *              100 = 128K entries
     *              101 = 256K entries
     *              110 = 512K entries
     *              111 = 1M   entries
     */
    OUTREG32(S3_OVERFLOW_BUFFER,  
             (psav->cobOffset >> 11) | 0x40000000);/* 128K,32K entries */

    ulTmp = INREG32(S3_OVERFLOW_BUFFER_PTR) & (ENABLE_BCI | ENABLE_COMMAND_OVERFLOW_BUF);

    /* Enable BCI, Enable  Command buffer. */
    OUTREG32(S3_OVERFLOW_BUFFER_PTR,
             (ulTmp | (ENABLE_BCI | ENABLE_COMMAND_OVERFLOW_BUF)));
             
    /* SR01:turn off screen */
    OUTREG8 (SEQ_ADDRESS_REG,0x01);
    byte = INREG8(SEQ_DATA_REG) | 0x20;
    OUTREG8(SEQ_DATA_REG,byte);

    /*
     * CR67_3:
     *  = 1  stream processor MMIO address and stride register
     *       are used to control the primary stream
     *  = 0  standard VGA address and stride registers
     *       are used t control the primary streams
     */
    OUTREG8(CRT_ADDRESS_REG,0x67); 
    byte =  INREG8(CRT_DATA_REG) | 0x08;
    OUTREG8(CRT_DATA_REG,byte);
    
    /* IGA 2 */
    OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA2_READS_WRITES);

    OUTREG8(CRT_ADDRESS_REG,0x67); 
    byte =  INREG8(CRT_DATA_REG) | 0x08;
    OUTREG8(CRT_DATA_REG,byte);
    
    OUTREG16(SEQ_ADDRESS_REG,SELECT_IGA1);

    /*
     * load ps1 active registers as determined by MM81C0/81C4
     * load ps2 active registers as determined by MM81B0/81B4
     */
    OUTREG8(CRT_ADDRESS_REG,0x65); 
    byte =  INREG8(CRT_DATA_REG) | 0x03;
    OUTREG8(CRT_DATA_REG,byte);
    
    /*
     *  Program Primary Stream Stride Register.
     *
     *  Tell engine if tiling on or off, set primary stream stride, and
     *  if tiling, set tiling bits/pixel and primary stream tile offset.
     *  Note that tile offset (bits 16 - 29) must be scanline width in
     *  bytes/128bytespertile * 256 Qwords/tile.  This is equivalent to
     *  lDelta * 2.  Remember that if tiling, lDelta is screenwidth in
     *  bytes padded up to an even number of tilewidths.
     */
    if (!psav->bTiled) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000) |
                 (psav->lDelta & 0x00001fff));
        OUTREG32(PRI_STREAM2_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000) |
                 (psav->lDelta & 0x00001fff));
    } else if (pScrn->bitsPerPixel == 16) {
        /* Scanline-length-in-bytes/128-bytes-per-tile * 256 Qwords/tile */
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0x80000000 | (psav->lDelta & 0x00001fff));
        OUTREG32(PRI_STREAM2_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0x80000000 | (psav->lDelta & 0x00001fff));
        
    } else if (pScrn->bitsPerPixel == 32) {
        OUTREG32(PRI_STREAM_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0xC0000000 | (psav->lDelta & 0x00001fff));
        OUTREG32(PRI_STREAM2_STRIDE,
                 (((psav->lDelta * 2) << 16) & 0x3FFF0000)
                 | 0xC0000000 | (psav->lDelta & 0x00001fff));
    }
    
    /* MM81C0 and 81C4 are used to control primary stream. */
    OUTREG32(PRI_STREAM_FBUF_ADDR0,0x80000000);
    OUTREG32(PRI_STREAM_FBUF_ADDR1,0x00000000);
    OUTREG32(PRI_STREAM2_FBUF_ADDR0,0x80000000);
    OUTREG32(PRI_STREAM2_FBUF_ADDR1,0x00000000);
    
    OUTREG32(0x8128, 0xFFFFFFFFL);
    OUTREG32(0x812C, 0xFFFFFFFFL);
    
    /* bit 28:block write disable */
    OUTREG32(S3_GLB_BD_HIGH,BCI_ENABLE | S3_BD64 | 0x10000000);
    
    if (psav->bTiled) {
        /* CR50, bit 7,6,0 = 111, Use GBD.*/
        OUTREG8(CRT_ADDRESS_REG,0X50);
        byte = INREG8(CRT_DATA_REG) | 0XC1;
        OUTREG8(CRT_DATA_REG, byte);
    }

    if (!psav->bTiled) {
        /*
         *  Do not enable block_write even for non-tiling modes, because
         *  the driver cannot determine if the memory type is the certain
         *  type of SGRAM for which block_write can be used.
         */
        psav->GlobalBD.bd1.HighPart.ResBWTile = 0;/* linear */
        
    }
    else if (pScrn->bitsPerPixel == 16) {
        psav->GlobalBD.bd1.HighPart.ResBWTile = 1;/* tile format destination */
        
        ulTmp =  (((pScrn->virtualX + 0x3f) & 0x0000ffc0) >> 6) << 20;
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp | TILED_SURF_BPP16);
    }
    else if (pScrn->bitsPerPixel == 32) {
        psav->GlobalBD.bd1.HighPart.ResBWTile = 1;/* tile format destination */
        
        ulTmp =  (((pScrn->virtualX + 0x1f) & 0x0000ffe0) >> 5) << 20;        
        OUTREG32(TILED_SURFACE_REGISTER_0,ulTmp | TILED_SURF_BPP32);
    }
    
    psav->GlobalBD.bd1.HighPart.ResBWTile |= 0x10;/* disable block write */
    /* HW uses width */
    psav->GlobalBD.bd1.HighPart.Stride = (ushort)(psav->lDelta / (pScrn->bitsPerPixel >> 3));
    psav->GlobalBD.bd1.HighPart.Bpp = (uchar) (pScrn->bitsPerPixel);
    psav->GlobalBD.bd1.Offset = 0;    

    /*
     * CR31, bit 0 = 0, Disable address offset bits(CR6A_6-0).
     *       bit 0 = 1, Enable 8 Mbytes of display memory thru 64K window
     *                  at A000:0.
     */
    OUTREG8(CRT_ADDRESS_REG,MEMORY_CONFIG_REG);
    byte = INREG8(CRT_DATA_REG) & (~(ENABLE_CPUA_BASE_A0000));
    OUTREG8(CRT_DATA_REG,byte);
    
    /* program the GBD */
    OUTREG32(S3_GLB_BD_LOW,psav->GlobalBD.bd2.LoPart );
    OUTREG32(S3_GLB_BD_HIGH,(psav->GlobalBD.bd2.HiPart
                             | S3_LITTLE_ENDIAN | 0x10000000 | S3_BD64)); /* AGD: shouldn't BCI be enabled? */
    
    /* turn on screen */
    OUTREG8(SEQ_ADDRESS_REG,0x01);
    byte = INREG8(SEQ_DATA_REG) & ~0X20;
    OUTREG8(SEQ_DATA_REG,byte);
}

void SavageEnableMode(ScrnInfoPtr pScrn,BOOL bEnable)
{
    SavagePtr psav = SAVPTR(pScrn);
    
    UnProtectCRTC();
    UnLockExtRegs();
    VerticalRetraceWait();

    if (!bEnable) {
        switch (psav->Chipset) {
            case S3_SAVAGE3D:
            case S3_SAVAGE_MX:
                SavageEnableMode_M7(pScrn,FALSE);
                break;
            case S3_SAVAGE4:
                SavageEnableMode_Savage4(pScrn,FALSE);
                break;
            case S3_TWISTER:
            case S3_PROSAVAGE:            
            case S3_PROSAVAGEDDR:
                SavageEnableMode_Twister(pScrn,FALSE);
                break;
            case S3_SUPERSAVAGE:
                SavageEnableMode_PM(pScrn,FALSE);
                break;
            case S3_SAVAGE2000:
                break;
            default:
                break;
        }

        return;
    }
    
    psav->lDelta = pScrn->virtualX * (pScrn->bitsPerPixel >> 3);

    /*
     * we can use Option "DisableTile" "TURE" to disable tile mode
     * if don't disable tile,we only support tile mode under 16/32bpp
     */
    if ((!psav->bDisableTile) && (psav->UseBIOS)
        && ((pScrn->bitsPerPixel == 16) || (pScrn->bitsPerPixel == 32))) {
        /* tileing in 16/32 BPP */
        psav->bTiled = TRUE;        
        psav->lDelta = ((psav->lDelta + 127) >> 7) << 7;
            
        if (psav->Chipset == S3_SAVAGE_MX)
            psav->ulAperturePitch = 0x2000;                            
        else            
            psav->ulAperturePitch = GetTileAperturePitch(pScrn->virtualX,pScrn->bitsPerPixel);
            
        /* Use the aperture for linear screen */
        psav->FBStart = psav->ApertureMap;
    } else {
        psav->bTiled = FALSE;
        /* 32: Alignment for nontiled mode */
        psav->lDelta = ((psav->lDelta + 31) >> 5) << 5;
        psav->ulAperturePitch = psav->lDelta;            
    }

    /* if you are using linear mode for 2D, 3D still needs to be tiled, linear AperturePitch
       seems to be wrong for savagespan */
    if (psav->Chipset == S3_SAVAGE_MX)
        psav->ul3DAperturePitch = 0x2000;                            
    else            
        psav->ul3DAperturePitch = GetTileAperturePitch(pScrn->virtualX,pScrn->bitsPerPixel);

    psav->l3DDelta = (((pScrn->virtualX * (pScrn->bitsPerPixel >> 3)) + 127) >> 7) << 7;
        
    psav->Bpp = pScrn->bitsPerPixel >> 3;
    psav->cxMemory = psav->lDelta / (psav->Bpp);
    psav->cyMemory = psav->endfb / psav->lDelta - 1;
    /* ??????????? */
    if (psav->cyMemory > 2048)
        psav->cyMemory = 2048;
        
    /*
     * If tiling, adjust down psav->cyMemory to the last multiple
     * of a tileheight, so that we don't try to use partial tiles.
     */
    if (psav->bTiled)  {
        psav->cyMemory -= (psav->cyMemory % 16);
    }
    
    /*
     *  Initialization per GX-3.
     * 
     *  1. MM48C18 - Disable BCI.
     *  2. MM48C0C - Enable updating shadow status
     *              and initialize shadow memory address.
     *  2b. MM48C18 - bit 1 = 1, Enable Command Buffer status updates
     *              (S3_OVERFLOW_BUFFER_PTR)
     *  3. MM48C10 - Initialize command buffer threshold
     *              (S3_BUFFER_THRESHOLD)
     *  4. MM48C14 - Setup command buffer offset and size
     *              (S3_OVERFLOW_BUFFER)
     *  5. MM816C  - Enable BCI.
     *  6. MM48C40 - Setup tiled surface 0 register.
     *  7. CR31 - bit 0 = 0, Disable address offset bits(CR6A_6-0).
     *  8. CR50 - bit 7,6,0 = 111, Use Global Bitmap Descriptor.
     *  9. CR88 - bit 4 = 0, Block write on (linear mode) IFF we know we
     *                       have the right kind of SGRAM memory,
     *                       bit 4 = 1, Block write off (always off if tiling)
     *  10.CR69 - Bit 7 = 1, MM81C0 and 81C4 are used to control
     *                       primary stream.
     *  11.MM8128, MM812c - Setup read/write mask registers
     *  12.MM816C, MM8168 - Set up Global Bitmap Descriptor 1 and 2.
     */
    switch (psav->Chipset) {
        case S3_SAVAGE3D:
        case S3_SAVAGE_MX:
            SavageEnableMode_M7(pScrn,TRUE);            
            break;
        case S3_SAVAGE4:
            SavageEnableMode_Savage4(pScrn,TRUE);
            break;
        case S3_TWISTER:
        case S3_PROSAVAGE:            
        case S3_PROSAVAGEDDR:
            SavageEnableMode_Twister(pScrn,TRUE);
            break;
        case S3_SUPERSAVAGE:
            SavageEnableMode_PM(pScrn,TRUE);
            break;
        case S3_SAVAGE2000:
            /* Disable BCI */
            OUTREG32(0x48C18, 0);
            /* Setup BCI command overflow buffer */
            OUTREG32(0x48C18, (psav->cobOffset >> 7) | (psav->cobIndex));
            if (psav->ShadowStatus) {
                /* Set shadow update threshholds. */
                OUTREG32(0x48C10, 0x6090);
                OUTREG32(0x48C14, 0x70A8);
                /* Enable shadow status update */
                OUTREG32(0x48A30, psav->ShadowPhysical);
                /* Enable BCI, command overflow buffer and shadow status. */
                OUTREG32(0x48C18, INREG32(0x48C18) | 0x00380000);
            }
            else {
                /* Disable shadow status update */
                OUTREG32(0x48A30, 0);
                /* Enable BCI and command overflow buffer */
                OUTREG32(0x48C18, INREG32(0x48C18) | 0x00280000);
            }
            break;
    }
        
#if 0
    /*  Program the Tv scratch buffer area */
    if (psav->DisplayOutSupport & UT_DEVICE_TV) {
        UnLockExtRegs();
        OUTREG16(SEQ_ADDRESS_REG,
                 (psav->TvInfo.TvEndScratchBuf & 0xFF) << 8 | TV_BUF_ENDADDR_0_REG);
        OUTREG16(SEQ_ADDRESS_REG,
                 (psav->TvInfo.TvEndScratchBuf & 0xFF00) | TV_BUF_ENDADDR_1_REG);
        OUTREG16(SEQ_ADDRESS_REG,
                 (psav->TvInfo.TvEndScratchBuf & 0x7F0000) >> 8 | TV_BUF_ENDADDR_2_REG);
        OUTREG16(SEQ_ADDRESS_REG,
                 (psav->TvInfo.TvStartScratchBuf & 0xFF) << 8 | TV_BUF_BASEADDR_0_REG);
        OUTREG16(SEQ_ADDRESS_REG,
                 (psav->TvInfo.TvStartScratchBuf & 0xFF00) | TV_BUF_BASEADDR_1_REG);
        OUTREG16(SEQ_ADDRESS_REG,
                 (psav->TvInfo.TvStartScratchBuf & 0x7F0000) >> 8 | TV_BUF_BASEADDR_2_REG);
    }
#endif    
}


/*
 * Note:we don't use any PIO operations befor MMIO enabled,
 * after it, we use INREG/OUTREG macro to access registers
 */
static Bool SavageEnableMMIO(ScrnInfoPtr pScrn)
{
    SavagePtr psav = SAVPTR(pScrn);
    
    TRACE(("SavageEnableMMIO()\n"));

    /*
     * we should enable MMIO in ScreenInit/EnterVt, and disable it in CloseScreen/LeaveVT,
     * but we will use MMIO befor ScreenInit(PreInit),so we may call EnableMMIO two times
     */
    if (!psav->MapBase) {
        vgaHWPtr hwp = VGAHWPTR(pScrn);
        unsigned char val;
        unsigned long tmp;
    
        xf86EnableIO();
        
        /* different chips have different definitions of PCI Base */
        if (S3_SAVAGE3D_SERIES(psav->Chipset)) {
            /* pci base 0 hold the fb,and fb + 16M is the mmio offset */            
            psav->MmioBase = psav->PciInfo->memBase[0] + SAVAGE_NEWMMIO_REGBASE_S3;
        }
        else {
            /* pci base 0 hold the mmio address,and pci base 1 hold the fb */
            psav->MmioBase = psav->PciInfo->memBase[0] + SAVAGE_NEWMMIO_REGBASE_S4;
        }

        xf86DrvMsg( pScrn->scrnIndex, X_PROBED,
                    "mapping MMIO @ 0x%x with size 0x%x\n",
                    psav->MmioBase, SAVAGE_NEWMMIO_REGSIZE);

        psav->MapBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_MMIO, psav->PciTag,
                                      psav->MmioBase,
                                      SAVAGE_NEWMMIO_REGSIZE);
        if (!psav->MapBase) {
            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                       "Internal error: cound not map registers\n");
            return FALSE;
        }

        psav->BciMem = psav->MapBase + 0x10000;
        vgaHWSetMmioFuncs(hwp, psav->MapBase, 0x8000);

        /*
         * do we need to wake up the chip? and we can not do PIO
         * to 0x3c3,use MM8510_0 instead
         */
        tmp = INREG32(0X8510);
        OUTREG32(0X8510,tmp | 0x01);

        /*
         * Miscellaneous Output Register
         * read: 0x3cc/write:0x3c2
         * bit_0 = 1: color emulation,address baseed at 3dx
         *       = 0: monochrome emulation.address based at 3bx
         */
        val = INREG8(MISC_OUTPUT_REG_READ);
        OUTREG(MISC_OUTPUT_REG_WRITE, val | 0x01);
        /*
         * CR40_0: 2D engine register I/O access disabled/enabled
         * this bit used only for S3 testing
         */
        if (psav->Chipset >= S3_SAVAGE4) {
            OUTREG8(CRT_ADDRESS_REG,0X40);
            val = INREG8(CRT_DATA_REG) | 1;
            OUTREG8(CRT_DATA_REG,val);
        }
        /* set hwp->IOBase to VGA_IOBASE_COLOR or  VGA_IOBASE_MONO */
        vgaHWGetIOBase(hwp);
    }
    
    return TRUE;
}


void SavageDisableMMIO(ScrnInfoPtr pScrn)
{
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    SavagePtr psav = SAVPTR(pScrn);
    uchar byte;
    
    TRACE(("SavageDisableMMIO\n"));

    if (psav->MapBase) {
        if (psav->Chipset >= S3_SAVAGE4) {
            /* CR40_0 = 1: 2D engine register I/O access enabled */
            OUTREG8(CRT_ADDRESS_REG,0X40);
            byte = INREG8(CRT_DATA_REG) | 1;
            OUTREG8(CRT_DATA_REG,byte);
        }
    
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)psav->MapBase,
                        SAVAGE_NEWMMIO_REGSIZE);
        psav->MapBase = NULL;
        psav->BciMem = NULL;
        
        vgaHWSetStdFuncs(hwp);        
    }

    return;
}


static Bool SavageMapFB(ScrnInfoPtr pScrn)
{
    SavagePtr psav = SAVPTR(pScrn);

    TRACE(("SavageMapFB()\n"));

    if (S3_SAVAGE3D_SERIES(psav->Chipset)) {
        /* Savage3D and SavageMX */
        psav->FrameBufferBase = psav->PciInfo->memBase[0];
    }
    else {
        psav->FrameBufferBase = psav->PciInfo->memBase[1];
    }
    
    xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
               "mapping framebuffer @ 0x%x with size 0x%x\n",
               psav->FrameBufferBase, psav->videoRambytes);

    psav->FBBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
                                 psav->PciTag, psav->FrameBufferBase,
                                 psav->videoRambytes);
    if (!psav->FBBase) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "Internal error: could not map framebuffer\n");
        return FALSE;
    }
    psav->FBStart = psav->FBBase;
    if (psav->Chipset == S3_SUPERSAVAGE)
        /* paramount aperture 0 is pci base 2 */
        psav->ApertureBase =  psav->PciInfo->memBase[2];
    else
        psav->ApertureBase = psav->FrameBufferBase + 0x02000000;
        
    psav->ApertureMap = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
                                      psav->PciTag, psav->ApertureBase,
                                      0x01000000 * 5);
    if (!psav->ApertureMap) {
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                   "Internal error: could not map aperture\n");
        return FALSE;
    }
    else
    {
        xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                   "map aperture:%p\n",psav->ApertureMap);

    }
    pScrn->memPhysBase = psav->PciInfo->memBase[0];
    
    pScrn->fbOffset = 0;

    return TRUE;
}


static void SavageUnmapFB(ScrnInfoPtr pScrn, int All)
{
    SavagePtr psav;

    psav = SAVPTR(pScrn);

    TRACE(("SavageUnmapFB(%x,%x)\n", psav->MapBase, psav->FBBase));


    if (psav->FBBase) {
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)psav->FBBase,
                        psav->videoRambytes);
        psav->FBBase = NULL;
    }

    if (psav->ApertureMap) {
        xf86UnMapVidMem(pScrn->scrnIndex, (pointer)psav->ApertureMap,
                        0x01000000 * 5 );
        psav->ApertureMap = NULL;
    }
#if 0
    xf86UnMapVidMem(pScrn->scrnIndex, (pointer)psav->MapBaseDense,
                    0x8000);
#endif

    return;
}


void SavageDetectVidRam(ScrnInfoPtr pScrn)
{
    SavagePtr psav= SAVPTR(pScrn);
    unsigned char config1;

    /* Next go on to detect amount of installed ram */
    OUTREG8(CRT_ADDRESS_REG, 0x36);  /* for register CR36 (CONFG_REG1), */
    config1 = INREG8(CRT_DATA_REG);  /* get amount of vram installed */
    
    /* Compute the amount of video memory and offscreen memory. */
    psav->MemOffScreen = 0;
    
    if (!pScrn->videoRam) {
        static unsigned char RamSavage3D[] = { 8, 4, 4, 2 };
        static unsigned char RamSavage4[] =  { 2, 4, 8, 12, 16, 32, 64, 32 };
        static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
        static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 16, 2 };
            
        switch (psav->Chipset) {
            case S3_SAVAGE3D:
                pScrn->videoRam = RamSavage3D[ (config1 & 0xC0) >> 6 ] * 1024;
                break;
                    
            case S3_SAVAGE4:
                /*
                 * The Savage4 has one ugly special case to consider.  On
                 * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB
                 * when it really means 8MB.  Why do it the same when you
                 * can do it different...
                 */
                OUTREG8(CRT_ADDRESS_REG, 0x68);	/* memory control 1 */
                if( (INREG8(CRT_DATA_REG) & 0xC0) == (0x01 << 6) )
                    RamSavage4[1] = 8;

                /*FALLTHROUGH*/

            case S3_SAVAGE2000:
                pScrn->videoRam = RamSavage4[ (config1 & 0xE0) >> 5 ] * 1024;
                break;

            case S3_SAVAGE_MX:
            case S3_SUPERSAVAGE:
                pScrn->videoRam = RamSavageMX[ (config1 & 0x0E) >> 1 ] * 1024;
                break;

            case S3_PROSAVAGE:
            case S3_TWISTER:
            case S3_PROSAVAGEDDR:
                pScrn->videoRam = RamSavageNB[ (config1 & 0xE0) >> 5 ] * 1024;
                break;

            default:
                /* How did we get here? */
                pScrn->videoRam = 0;
                break;
        }

        psav->videoRambytes = pScrn->videoRam * 1024;

        if (psav->MemOffScreen)
            xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
                       "probed videoram:  %dk plus %dk offscreen\n",
                       pScrn->videoRam,
                       psav->MemOffScreen);
        else
            xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
                       "probed videoram:  %dk\n",
                       pScrn->videoRam);
    } else {
        psav->videoRambytes = pScrn->videoRam * 1024;
        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
                   "videoram =  %dk\n",
                   pScrn->videoRam);
    }

}


void SavageLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indicies,
                       LOCO *colors, VisualPtr pVisual)
{
    SavagePtr psav = SAVPTR(pScrn);
    int i, index;

    for (i=0; i<numColors; i++) {
        index = indicies[i];
        OUTREG8(DAC_ADDRESS_WRITE_REG, index);
        OUTREG8(DAC_DATA_REG, colors[index].red);
        OUTREG8(DAC_DATA_REG, colors[index].green);
        OUTREG8(DAC_DATA_REG, colors[index].blue);
    }
}


void SavageLoadPaletteSavage4(ScrnInfoPtr pScrn, int numColors, int *indicies,
                              LOCO *colors, VisualPtr pVisual)
{
    SavagePtr psav = SAVPTR(pScrn);
    int i, index;
    
    VerticalRetraceWait();

    for (i=0; i<numColors; i++) {
        if (!(inStatus1()) & 0x08)
            VerticalRetraceWait();
        index = indicies[i];
        OUTREG8(DAC_ADDRESS_WRITE_REG, index);
        OUTREG8(DAC_DATA_REG, colors[index].red);
        OUTREG8(DAC_DATA_REG, colors[index].green);
        OUTREG8(DAC_DATA_REG, colors[index].blue);
    }
}

/* Function to check if panning enable */
static void
SavagePanningCheck(ScrnInfoPtr pScrn)
{
    SavagePtr psav = SAVPTR(pScrn);
    DisplayModePtr pMode;

    pMode = pScrn->currentMode;
    
	/*
     *  Fix by Yuchen 2002/11/28 for : mode to 640x480,
     *  85Hz,Cursor point and hot point not align.
     */
    psav->iResX = pMode->CrtcHDisplay;
    /* psav->iResY = pMode->CrtcVDisplay;*/    
	psav->iResY = pMode->VDisplay;

    if (psav->iResX < pScrn->virtualX || psav->iResY < pScrn->virtualY)
        psav->Panning = TRUE;
    else
        psav->Panning = FALSE;
      
    if (psav->LcdOn && (psav->iResX < psav->PanelX || psav->iResY < psav->PanelY))
        psav->FPExpansion = TRUE;
    else
        psav->FPExpansion = FALSE;

    if (psav->CrtOnly) {
	psav->FPExpansion = FALSE;
	psav->Panning = FALSE;
    }
}

/*
 * I'd rather have these wait macros be , but S3 has made it
 * darned near impossible.  The bit fields are in a different place in
 * all three families, the status register has a different address in the
 * three families, and even the idle vs busy sense flipped in the Sav2K.
 */
static  void ResetBCI2K( SavagePtr psav )
{
    CARD32 cob = INREG32(0x48c18);
    
    /* if BCI is enabled and BCI is busy... */
    if ((cob & 0x00000008)
        && !(ALT_STATUS_WORD0 & 0x00200000)) {
        ErrorF( "Resetting BCI, stat = %08x...\n", ALT_STATUS_WORD0);
        /* Turn off BCI */
        OUTREG32( 0x48c18, cob & ~8 );
        usleep(10000);
        /* Turn it back on */
        OUTREG32( 0x48c18, cob );
        usleep(10000);
    }
}

static  Bool ShadowWait( SavagePtr psav )
{
    BCI_GET_PTR;
    static int dwBCIWait2DIdle = 0;
    int loop = 0;

    if (!dwBCIWait2DIdle) {
        if( psav->Chipset == S3_SAVAGE2000 )
            dwBCIWait2DIdle = 0xc0040000;
        else
            dwBCIWait2DIdle = 0xc0020000;
    }

    psav->ShadowCounter = (psav->ShadowCounter + 1) & 0x7fff;
    BCI_SEND(dwBCIWait2DIdle);
    BCI_SEND(0x98000000 + psav->ShadowCounter);

    while ((psav->ShadowVirtual[1] & 0x7fff) != psav->ShadowCounter
           && (loop++ < MAXLOOP));

    return loop >= MAXLOOP;
}

/* Wait until "v" queue entries are free */
static  int
WaitQueue3D( SavagePtr psav, int v )
{
    int loop = 0;
    int slots = MAXFIFO - v;

    mem_barrier();
    if( psav->ShadowVirtual ) {
        psav->WaitIdle = ShadowWait;
        return ShadowWait(psav);
    }
    else {
        loop &= STATUS_WORD0;
        while (((STATUS_WORD0 & 0x0000ffff) > slots) && (loop++ < MAXLOOP));
    }
    return loop >= MAXLOOP;
}

static  int
WaitQueue4( SavagePtr psav, int v )
{
    int loop = 0;
    int slots = MAXFIFO - v;

    if( !psav->NoPCIRetry )
        return 0;
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdle = ShadowWait;
        return ShadowWait(psav);
    }
    else
        while (((ALT_STATUS_WORD0 & 0x001fffff) > slots)
               && (loop++ < MAXLOOP));
    
    return loop >= MAXLOOP;
}

static  int
WaitQueue2K( SavagePtr psav, int v )
{
    int loop = 0;
    int slots = MAXFIFO - v;

    if (!psav->NoPCIRetry)
        return 0;
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdle = ShadowWait;
        return ShadowWait(psav);
    }
    else
        while (((ALT_STATUS_WORD0 & 0x000fffff) > slots)
               && (loop++ < MAXLOOP));
    
    if (loop >= MAXLOOP)
        ResetBCI2K(psav);
    
    return loop >= MAXLOOP;
}

/* Wait until GP is idle and queue is empty */
static  int
WaitIdleEmpty3D(SavagePtr psav)
{
    int loop = 0;
    
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdleEmpty = ShadowWait;
        return ShadowWait(psav);
    }
    loop &= STATUS_WORD0;
    while (((STATUS_WORD0 & 0x0008ffff) != 0x80000)
           && (loop++ < MAXLOOP));
    
    return loop >= MAXLOOP;
}

static  int
WaitIdleEmpty4(SavagePtr psav)
{
    int loop = 0;
    
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdleEmpty = ShadowWait;
        return ShadowWait(psav);
    }
    while (((ALT_STATUS_WORD0 & 0x00e1ffff) != 0x00e00000)
           && (loop++ < MAXLOOP));
    
    return loop >= MAXLOOP;
}

static  int
WaitIdleEmpty2K(SavagePtr psav)
{
    int loop = 0;
    
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdleEmpty = ShadowWait;
        return ShadowWait(psav);
    }
    loop &= ALT_STATUS_WORD0;
    
    while (((ALT_STATUS_WORD0 & 0x009fffff) != 0)
           && (loop++ < MAXLOOP));
    
    if (loop >= MAXLOOP)
        ResetBCI2K(psav);
    
    return loop >= MAXLOOP;
}

/* Wait until GP is idle */
static  int
WaitIdle3D(SavagePtr psav)
{
    int loop = 0;
    
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdle = ShadowWait;
        return ShadowWait(psav);
    }
    while ((!(STATUS_WORD0 & 0x00080000))
           && (loop++ < MAXLOOP));
    
    return loop >= MAXLOOP;
}

static  int WaitIdle4(SavagePtr psav)
{
    int loop = 0;
    
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdle = ShadowWait;
        return ShadowWait(psav);
    }
    
    while (((ALT_STATUS_WORD0 & 0x00E00000)!=0x00E00000)
           && (loop++ < MAXLOOP));
    
    return loop >= MAXLOOP;
}

static  int WaitIdle2K(SavagePtr psav)
{
    int loop = 0;
    
    mem_barrier();
    if (psav->ShadowVirtual) {
        psav->WaitIdle = ShadowWait;
        return ShadowWait(psav);
    }
    loop &= ALT_STATUS_WORD0;
    while ((ALT_STATUS_WORD0 & 0x00900000) && (loop++ < MAXLOOP));
    
    return loop >= MAXLOOP;
}

static int LookupChipID(PciChipsets* pset, int ChipID)
{
    /* Is there a function to do this for me? */
    while (pset->numChipset >= 0) {
        if (pset->PCIid == ChipID)
            return pset->numChipset;
        pset++;
    }

    return -1;
}

static Bool SavageGetRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate)
        return TRUE;

    pScrn->driverPrivate = xnfcalloc(sizeof(SavageRec), 1);
    memset(pScrn->driverPrivate,sizeof(SavageRec),0);
    
    return TRUE;
}

static void SavageFreeRec(ScrnInfoPtr pScrn)
{
    TRACE(("SavageFreeRec(%x)\n", pScrn->driverPrivate));
    if (!pScrn->driverPrivate)
        return;
    xfree(pScrn->driverPrivate);
    pScrn->driverPrivate = NULL;
}

/*
 *  returns the aperture pitch for tiled mode.
 *  if MM850C_15 = 0 (use NB linear tile mode) the pitch is screen stride aligned to 128bytes
 *  if MM850C_15 = 1 (use MS-1 128bit non-linear tile mode),we should do it as follows
 *  we now only support the later, and don't use Y range flag,see tile surface register
*/
static int GetTileAperturePitch(ulong dwWidth, ulong dwBpp)
{
    switch (dwBpp) {
        case 4:
        case 8:
            return(0x2000);
            break;
        case 16:
            return(0x1000);
            break;
        case 32:
            return(0x2000);
            break;
        default:
            return(0x2000);
    }
}



/* This function is used to debug, it prints out the contents of s3 regs */
void SavagePrintRegs(ScrnInfoPtr pScrn)
{
    SavagePtr psav = SAVPTR(pScrn);
    unsigned char i;

    ErrorF("SR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF");

    for (i = 0; i < 0x70; i++) {
        if( !(i % 16) )
            ErrorF("\nSR%xx ", i >> 4);
        OUTREG8(SEQ_ADDRESS_REG, i );
        ErrorF(" %02x", INREG8(SEQ_DATA_REG));
    }

    ErrorF("\n\nCR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF");

    for( i = 0; i < 0xB7; i++ ) {
        if( !(i % 16) )
            ErrorF( "\nCR%xx ", i >> 4 );
        OUTREG8(CRT_ADDRESS_REG, i );
        ErrorF(" %02x", INREG8(CRT_DATA_REG));
    }

    ErrorF("\n\n");
}

static Bool SavageNoBiosModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    SavagePtr psav = SAVPTR(pScrn);
    int width, dclk, i, j; /*, refresh; */
    unsigned int m, n, r;
    unsigned char tmp = 0;
    SavageRegPtr new = &psav->ModeReg;
    vgaRegPtr vganew = &hwp->ModeReg;


    TRACE(("SavageModeInit(%dx%d, %dHz)\n", 
	mode->HDisplay, mode->VDisplay, mode->Clock));


    if (pScrn->bitsPerPixel == 8)
	psav->HorizScaleFactor = 1;
    else if (pScrn->bitsPerPixel == 16)
	psav->HorizScaleFactor = 1;	/* I don't think we ever want 2 */
    else
	psav->HorizScaleFactor = 1;

    if (psav->HorizScaleFactor == 2)
	if (!mode->CrtcHAdjusted) {
	    mode->CrtcHDisplay *= 2;
	    mode->CrtcHSyncStart *= 2;
	    mode->CrtcHSyncEnd *= 2;
	    mode->CrtcHTotal *= 2;
	    mode->CrtcHSkew *= 2;
	    mode->CrtcHAdjusted = TRUE;
	}

    if (!vgaHWInit(pScrn, mode))
	return FALSE;

    new->mode = 0;

    /* We need to set CR67 whether or not we use the BIOS. */

    dclk = mode->Clock;
    new->CR67 = 0x00;

    switch( pScrn->depth ) {
    case 8:
	if( (psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000) )
	    new->CR67 = 0x10;	/* 8bpp, 2 pixels/clock */
	else
	    new->CR67 = 0x00;	/* 8bpp, 1 pixel/clock */
	break;
    case 15:
	if( 
	    S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ||
	    ((psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000))
	)
	    new->CR67 = 0x30;	/* 15bpp, 2 pixel/clock */
	else
	    new->CR67 = 0x20;	/* 15bpp, 1 pixels/clock */
	break;
    case 16:
	if( 
	    S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ||
	    ((psav->Chipset == S3_SAVAGE2000) && (dclk >= 230000))
	)
	    new->CR67 = 0x50;	/* 16bpp, 2 pixel/clock */
	else
	    new->CR67 = 0x40;	/* 16bpp, 1 pixels/clock */
	break;
    case 24:
	if (pScrn->bitsPerPixel == 24 )
	    new->CR67 = 0x70;
	else
	    new->CR67 = 0xd0;
	break;
    }

	OUTREG8(CRT_ADDRESS_REG, 0x3a);
	tmp = INREG8(CRT_DATA_REG);
	if (psav->pci_burst)
	    new->CR3A = (tmp & 0x7f) | 0x15;
	else
	    new->CR3A = tmp | 0x95;

	new->CR53 = 0x00;
	new->CR31 = 0x8c;
	new->CR66 = 0x89;

	OUTREG8(CRT_ADDRESS_REG, 0x58);
	new->CR58 = INREG8(CRT_DATA_REG) & 0x80;
	new->CR58 |= 0x13;

#if 0
	OUTREG8(CRT_ADDRESS_REG, 0x55);
	new->CR55 = INREG8(CRT_DATA_REG);
	if (psav->hwcursor)
		new->CR55 |= 0x10;
#endif

	new->SR15 = 0x03 | 0x80;
	new->SR18 = 0x00;

/*	OUTREG8(SEQ_ADDRESS_REG, 0x1b);
	new->SR1B = INREG8(SEQ_DATA_REG);
	if( pScrn->depth == 24 )
		new->SR1B |= 0x28;
*/
	if( pScrn->depth == 24 )
	    new->SR1B = 0x28;
	else
	    new->SR1B = 0x00;


	new->CR43 = new->CR45 = new->CR65 = 0x00;

	OUTREG8(CRT_ADDRESS_REG, 0x40);
	new->CR40 = INREG8(CRT_DATA_REG) & ~0x01;

	new->MMPR0 = 0x010400;
	new->MMPR1 = 0x00;
	new->MMPR2 = 0x0808;
	new->MMPR3 = 0x08080810;

	if (psav->fifo_aggressive || psav->fifo_moderate ||
	    psav->fifo_conservative) {
		new->MMPR1 = 0x0200;
		new->MMPR2 = 0x1808;
		new->MMPR3 = 0x08081810;
	}

	if (psav->MCLK <= 0) {
		new->SR10 = 255;
		new->SR11 = 255;
	}

	psav->NeedSTREAMS = FALSE;

	SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000,
			&m, &n, &r);
	new->SR12 = (r << 6) | (n & 0x3f);
	new->SR13 = m & 0xff;
	new->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;

	if (psav->fifo_moderate) {
	    if (pScrn->bitsPerPixel < 24)
		new->MMPR0 -= 0x8000;
	    else
		new->MMPR0 -= 0x4000;
	} else if (psav->fifo_aggressive) {
	    if (pScrn->bitsPerPixel < 24)
		new->MMPR0 -= 0xc000;
	    else
		new->MMPR0 -= 0x6000;
	}

	if (mode->Flags & V_INTERLACE)
	    new->CR42 = 0x20;
	else
	    new->CR42 = 0x00;

	new->CR34 = 0x10;

	i = ((((mode->CrtcHTotal >> 3) - 5) & 0x100) >> 8) |
	    ((((mode->CrtcHDisplay >> 3) - 1) & 0x100) >> 7) |
	    ((((mode->CrtcHSyncStart >> 3) - 1) & 0x100) >> 6) |
	    ((mode->CrtcHSyncStart & 0x800) >> 7);

	if ((mode->CrtcHSyncEnd >> 3) - (mode->CrtcHSyncStart >> 3) > 64)
	    i |= 0x08;
	if ((mode->CrtcHSyncEnd >> 3) - (mode->CrtcHSyncStart >> 3) > 32)
	    i |= 0x20;
	j = (vganew->CRTC[0] + ((i & 0x01) << 8) +
	     vganew->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
	if (j - (vganew->CRTC[4] + ((i & 0x10) << 4)) < 4) {
	    if (vganew->CRTC[4] + ((i & 0x10) << 4) + 4 <= 
	        vganew->CRTC[0] + ((i & 0x01) << 8))
		j = vganew->CRTC[4] + ((i & 0x10) << 4) + 4;
	    else
		j = vganew->CRTC[0] + ((i & 0x01) << 8) + 1;
	}

	new->CR3B = j & 0xff;
	i |= (j & 0x100) >> 2;
	new->CR3C = (vganew->CRTC[0] + ((i & 0x01) << 8)) / 2;
	new->CR5D = i;
	new->CR5E = (((mode->CrtcVTotal - 2) & 0x400) >> 10) |
		    (((mode->CrtcVDisplay - 1) & 0x400) >> 9) |
		    (((mode->CrtcVSyncStart) & 0x400) >> 8) |
		    (((mode->CrtcVSyncStart) & 0x400) >> 6) | 0x40;
	width = (pScrn->displayWidth * (pScrn->bitsPerPixel / 8)) >> 3;
	new->CR91 = vganew->CRTC[19] = 0xff & width;
	new->CR51 = (0x300 & width) >> 4;
	new->CR90 = 0x80 | (width >> 8);
	vganew->MiscOutReg |= 0x0c;

	/* Set frame buffer description. */

	if (pScrn->bitsPerPixel <= 8)
	    new->CR50 = 0;
	else if (pScrn->bitsPerPixel <= 16)
	    new->CR50 = 0x10;
	else
	    new->CR50 = 0x30;
        /* may have to set this to GBD for all modes for DRI and tiling */
	if (pScrn->displayWidth == 640)
	    new->CR50 |= 0x40;
	else if (pScrn->displayWidth == 800)
	    new->CR50 |= 0x80;
	else if (pScrn->displayWidth == 1024)
	    new->CR50 |= 0x00;
	else if (pScrn->displayWidth == 1152)
	    new->CR50 |= 0x01;
	else if (pScrn->displayWidth == 1280)
	    new->CR50 |= 0xc0;
	else if (pScrn->displayWidth == 1600)
	    new->CR50 |= 0x81;
	else
	    new->CR50 |= 0xc1;	/* Use GBD */

	if( S3_SAVAGE_MOBILE_SERIES(psav->Chipset) )
	    new->CR33 = 0x00;
	else
	    new->CR33 = 0x08;
	     
	vganew->CRTC[0x17] = 0xeb;

	new->CR67 |= 1;

	OUTREG8(CRT_ADDRESS_REG, 0x36);
	new->CR36 = INREG8(CRT_DATA_REG);
	OUTREG8(CRT_ADDRESS_REG, 0x68);
	new->CR68 = INREG8(CRT_DATA_REG);
	new->CR69 = 0;
	OUTREG8(CRT_ADDRESS_REG, 0x6f);
	new->CR6F = INREG8(CRT_DATA_REG);
	OUTREG8(CRT_ADDRESS_REG, 0x88);
	new->CR86 = INREG8(CRT_DATA_REG) | 0x08;
	OUTREG8(CRT_ADDRESS_REG, 0xb0);
	new->CRB0 = INREG8(CRT_DATA_REG) | 0x80;
    }

    pScrn->vtSema = TRUE;

    /* do it! */
    SavageNoBiosWriteMode(pScrn, vganew, new, TRUE);

    return TRUE;
}

static void SavageNoBiosWriteMode(ScrnInfoPtr pScrn, vgaRegPtr vgaSavePtr,
			    SavageRegPtr restore)
{
    unsigned char tmp, cr3a, cr66, cr67;
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    SavagePtr psav = SAVPTR(pScrn);
    
    TRACE(("SavageWriteMode(%x)\n", restore->mode));

    OUTREG8(MISC_OUTPUT_REG_WRITE, 0x23);
    OUTREG16(CRT_ADDRESS_REG, 0x4838);
    OUTREG16(CRT_ADDRESS_REG, 0xa039);
    OUTREG16(SEQ_ADDRESS_REG, 0x0608);

    vgaHWProtect(pScrn, TRUE);

    /* will we be reenabling STREAMS for the new mode? */
    psav->STREAMSRunning = 0;
#if 0
    /* reset GE to make sure nothing is going on */
    OUTREG8(CRT_ADDRESS_REG, 0x66);
    if(INREG8(CRT_DATA_REG) & 0x01)
	SavageGEReset(pScrn,0,__LINE__,__FILE__);
#endif

    /* reset graphics engine to avoid memory corruption */
    OUTREG8(CRT_ADDRESS_REG, 0x66);
    cr66 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr66 | 0x02);
    usleep(10000);

    /*
     * Some Savage/MX and /IX systems go nuts when trying to exit the
     * server after WindowMaker has displayed a gradient background.  I
     * haven't been able to find what causes it, but a non-destructive
     * switch to mode 3 here seems to eliminate the issue.
     */

    if( ((restore->CR31 & 0x0a) == 0) && psav->pInt10 ) {
	SavageSetTextMode( psav );
    }

    OUTREG8(CRT_ADDRESS_REG, 0x67);
    cr67 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, restore->CR67 & ~0x0c); /* no STREAMS yet */

    /* restore extended regs */
    OUTREG8(CRT_ADDRESS_REG, 0x66);
    OUTREG8(CRT_DATA_REG, restore->CR66);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    OUTREG8(CRT_DATA_REG, restore->CR3A);
    OUTREG8(CRT_ADDRESS_REG, 0x31);
    OUTREG8(CRT_DATA_REG, restore->CR31);
    OUTREG8(CRT_ADDRESS_REG, 0x32);
    OUTREG8(CRT_DATA_REG, restore->CR32);
    OUTREG8(CRT_ADDRESS_REG, 0x58);
    OUTREG8(CRT_DATA_REG, restore->CR58);
    OUTREG8(CRT_ADDRESS_REG, 0x53);
    OUTREG8(CRT_DATA_REG, restore->CR53 & 0x7f);

    OUTREG16(SEQ_ADDRESS_REG, 0x0608);

    /* Restore DCLK registers. */

    OUTREG8(SEQ_ADDRESS_REG, 0x0e);
    OUTREG8(SEQ_DATA_REG, restore->SR0E);
    OUTREG8(SEQ_ADDRESS_REG, 0x0f);
    OUTREG8(SEQ_DATA_REG, restore->SR0F);
    OUTREG8(SEQ_ADDRESS_REG, 0x29);
    OUTREG8(SEQ_DATA_REG, restore->SR29);
    OUTREG8(SEQ_ADDRESS_REG, 0x15);
    OUTREG8(SEQ_DATA_REG, restore->SR15);

    /* Restore flat panel expansion regsters. */
    if( S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) {
	int i;
	for( i = 0; i < 8; i++ ) {
	    OUTREG8(SEQ_ADDRESS_REG, 0x54+i);
	    OUTREG8(SEQ_DATA_REG, restore->SR54[i]);
	}
    }

    /* restore the standard vga regs */
    if (xf86IsPrimaryPci(psav->PciInfo))
	vgaHWRestore(pScrn, vgaSavePtr, VGA_SR_ALL);
    else
	vgaHWRestore(pScrn, vgaSavePtr, VGA_SR_MODE);

    /* extended mode timing registers */
    OUTREG8(CRT_ADDRESS_REG, 0x53);
    OUTREG8(CRT_DATA_REG, restore->CR53);
    OUTREG8(CRT_ADDRESS_REG, 0x5d);
    OUTREG8(CRT_DATA_REG, restore->CR5D);
    OUTREG8(CRT_ADDRESS_REG, 0x5e);
    OUTREG8(CRT_DATA_REG, restore->CR5E);
    OUTREG8(CRT_ADDRESS_REG, 0x3b);
    OUTREG8(CRT_DATA_REG, restore->CR3B);
    OUTREG8(CRT_ADDRESS_REG, 0x3c);
    OUTREG8(CRT_DATA_REG, restore->CR3C);
    OUTREG8(CRT_ADDRESS_REG, 0x43);
    OUTREG8(CRT_DATA_REG, restore->CR43);
    OUTREG8(CRT_ADDRESS_REG, 0x65);
    OUTREG8(CRT_DATA_REG, restore->CR65);

    /* restore the desired video mode with cr67 */
    OUTREG8(CRT_ADDRESS_REG, 0x67);
    OUTREG8(CRT_DATA_REG, restore->CR67 & ~0x0c); /* no STREAMS yet */

    /* other mode timing and extended regs */
    OUTREG8(CRT_ADDRESS_REG, 0x34);
    OUTREG8(CRT_DATA_REG, restore->CR34);
    OUTREG8(CRT_ADDRESS_REG, 0x40);
    OUTREG8(CRT_DATA_REG, restore->CR40);
    OUTREG8(CRT_ADDRESS_REG, 0x42);
    OUTREG8(CRT_DATA_REG, restore->CR42);
    OUTREG8(CRT_ADDRESS_REG, 0x45);
    OUTREG8(CRT_DATA_REG, restore->CR45);
    OUTREG8(CRT_ADDRESS_REG, 0x50);
    OUTREG8(CRT_DATA_REG, restore->CR50);
    OUTREG8(CRT_ADDRESS_REG, 0x51);
    OUTREG8(CRT_DATA_REG, restore->CR51);

    /* memory timings */
    OUTREG8(CRT_ADDRESS_REG, 0x36);
    OUTREG8(CRT_DATA_REG, restore->CR36);
    OUTREG8(CRT_ADDRESS_REG, 0x60);
    OUTREG8(CRT_DATA_REG, restore->CR60);
    OUTREG8(CRT_ADDRESS_REG, 0x68);
    OUTREG8(CRT_DATA_REG, restore->CR68);
    OUTREG8(CRT_ADDRESS_REG, 0x69);
    OUTREG8(CRT_DATA_REG, restore->CR69);
    OUTREG8(CRT_ADDRESS_REG, 0x6f);
    OUTREG8(CRT_DATA_REG, restore->CR6F);

    OUTREG8(CRT_ADDRESS_REG, 0x33);
    OUTREG8(CRT_DATA_REG, restore->CR33);
    OUTREG8(CRT_ADDRESS_REG, 0x86);
    OUTREG8(CRT_DATA_REG, restore->CR86);
    OUTREG8(CRT_ADDRESS_REG, 0x88);
    OUTREG8(CRT_DATA_REG, restore->CR88);
    OUTREG8(CRT_ADDRESS_REG, 0x90);
    OUTREG8(CRT_DATA_REG, restore->CR90);
    OUTREG8(CRT_ADDRESS_REG, 0x91);
    OUTREG8(CRT_DATA_REG, restore->CR91);
    if( psav->Chipset == S3_SAVAGE4 )
    {
	OUTREG8(CRT_ADDRESS_REG, 0xb0);
	OUTREG8(CRT_DATA_REG, restore->CRB0);
    }

    OUTREG8(CRT_ADDRESS_REG, 0x32);
    OUTREG8(CRT_DATA_REG, restore->CR32);

    /* unlock extended seq regs */
    OUTREG8(SEQ_ADDRESS_REG, 0x08);
    OUTREG8(SEQ_DATA_REG, 0x06);

    /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates that 
     * we should leave the default SR10 and SR11 values there.
     */
    if (restore->SR10 != 255) {
	OUTREG8(SEQ_ADDRESS_REG, 0x10);
	OUTREG8(SEQ_DATA_REG, restore->SR10);
	OUTREG8(SEQ_ADDRESS_REG, 0x11);
	OUTREG8(SEQ_DATA_REG, restore->SR11);
    }

    /* restore extended seq regs for dclk */
    OUTREG8(SEQ_ADDRESS_REG, 0x0e);
    OUTREG8(SEQ_DATA_REG, restore->SR0E);
    OUTREG8(SEQ_ADDRESS_REG, 0x0f);
    OUTREG8(SEQ_DATA_REG, restore->SR0F);
    OUTREG8(SEQ_ADDRESS_REG, 0x12);
    OUTREG8(SEQ_DATA_REG, restore->SR12);
    OUTREG8(SEQ_ADDRESS_REG, 0x13);
    OUTREG8(SEQ_DATA_REG, restore->SR13);
    OUTREG8(SEQ_ADDRESS_REG, 0x29);
    OUTREG8(SEQ_DATA_REG, restore->SR29);

    OUTREG8(SEQ_ADDRESS_REG, 0x18);
    OUTREG8(SEQ_DATA_REG, restore->SR18);
    OUTREG8(SEQ_ADDRESS_REG, 0x1b);
    if( psav->DGAactive )
	OUTREG8(SEQ_DATA_REG, restore->SR1B & ~0x28);
    else
	OUTREG8(SEQ_DATA_REG, restore->SR1B);

    /* load new m, n pll values for dclk & mclk */
    OUTREG8(SEQ_ADDRESS_REG, 0x15);
    tmp = INREG8(SEQ_DATA_REG) & ~0x21;

    OUTREG8(SEQ_DATA_REG, tmp | 0x03);
    OUTREG8(SEQ_DATA_REG, tmp | 0x23);
    OUTREG8(SEQ_DATA_REG, tmp | 0x03);
    OUTREG8(SEQ_DATA_REG, restore->SR15);
    usleep( 100 );

    OUTREG8(SEQ_ADDRESS_REG, 0x30);
    OUTREG8(SEQ_DATA_REG, restore->SR30);
    OUTREG8(SEQ_ADDRESS_REG, 0x08);
    OUTREG8(SEQ_DATA_REG, restore->SR08);

    /* now write out cr67 in full, possibly starting STREAMS */
    VerticalRetraceWait(psav);
    OUTREG8(CRT_ADDRESS_REG, 0x67);
#if 0
    OUTREG8(CRT_DATA_REG, 0x50);
    usleep(10000);
    OUTREG8(CRT_ADDRESS_REG, 0x67);
#endif
    OUTREG8(CRT_DATA_REG, restore->CR67);

    OUTREG8(CRT_ADDRESS_REG, 0x66);
    cr66 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr66 | 0x80);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    cr3a = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr3a | 0x80);

    if( !S3_SAVAGE_MOBILE_SERIES(psav->Chipset) )
    {
	VerticalRetraceWait(psav);
	OUTREG(FIFO_CONTROL_REG, restore->MMPR0);
	OUTREG(MIU_CONTROL_REG, restore->MMPR1);
	OUTREG(STREAMS_TIMEOUT_REG, restore->MMPR2);
	OUTREG(MISC_TIMEOUT_REG, restore->MMPR3);
    }


    OUTREG8(CRT_ADDRESS_REG, 0x66);
    OUTREG8(CRT_DATA_REG, cr66);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    OUTREG8(CRT_DATA_REG, cr3a);

    vgaHWProtect(pScrn, FALSE);

    return;
}

static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,

	/* Make sure linear addressing is enabled after the BIOS call. */
	/* Note that we must use an I/O port to do this. */
			   int min_n2, int max_n2, long freq_min,
			   long freq_max, unsigned int *mdiv,
			   unsigned int *ndiv, unsigned int *r)
{
    double ffreq, ffreq_min, ffreq_max;
    double div, diff, best_diff;
    unsigned int m;
    unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2;

    ffreq = freq / 1000.0 / BASE_FREQ;
    ffreq_max = freq_max / 1000.0 / BASE_FREQ;
    ffreq_min = freq_min / 1000.0 / BASE_FREQ;

    if (ffreq < ffreq_min / (1 << max_n2)) {
	    ErrorF("invalid frequency %1.3f Mhz\n",
		   ffreq*BASE_FREQ);
	    ffreq = ffreq_min / (1 << max_n2);
    }
    if (ffreq > ffreq_max / (1 << min_n2)) {
	    ErrorF("invalid frequency %1.3f Mhz\n",
		   ffreq*BASE_FREQ);
	    ffreq = ffreq_max / (1 << min_n2);
    }

    /* work out suitable timings */

    best_diff = ffreq;

    for (n2=min_n2; n2<=max_n2; n2++) {
	for (n1=min_n1+2; n1<=max_n1+2; n1++) {
	    m = (int)(ffreq * n1 * (1 << n2) + 0.5);
	    if (m < min_m+2 || m > 127+2)
		continue;
	    div = (double)(m) / (double)(n1);
	    if ((div >= ffreq_min) &&
		(div <= ffreq_max)) {
		diff = ffreq - div / (1 << n2);
		if (diff < 0.0)
			diff = -diff;
		if (diff < best_diff) {
		    best_diff = diff;
		    best_m = m;
		    best_n1 = n1;
		    best_n2 = n2;
		}
	    }
	}
    }

    *ndiv = best_n1 - 2;
    *r = best_n2;
    *mdiv = best_m - 2;
}

static void SavageSave(ScrnInfoPtr pScrn)
{
    unsigned char cr3a, cr53, cr66;
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    vgaRegPtr vgaSavePtr = &hwp->SavedReg;
    SavagePtr psav = SAVPTR(pScrn);
    SavageRegPtr save = &psav->SavedReg;

    TRACE(("SavageSave()\n"));

    OUTREG16(CRT_ADDRESS_REG, 0x4838);
    OUTREG16(CRT_ADDRESS_REG, 0xa039);
    OUTREG16(SEQ_ADDRESS_REG, 0x0608);

    OUTREG8(CRT_ADDRESS_REG, 0x66);
    cr66 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr66 | 0x80);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    cr3a = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr3a | 0x80);
    OUTREG8(CRT_ADDRESS_REG, 0x53);
    cr53 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr53 & 0x7f);

    if (xf86IsPrimaryPci(psav->PciInfo))
	vgaHWSave(pScrn, vgaSavePtr, VGA_SR_ALL);
    else
	vgaHWSave(pScrn, vgaSavePtr, VGA_SR_MODE);

    OUTREG8(CRT_ADDRESS_REG, 0x66);
    OUTREG8(CRT_DATA_REG, cr66);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    OUTREG8(CRT_DATA_REG, cr3a);

    OUTREG8(CRT_ADDRESS_REG, 0x66);
    OUTREG8(CRT_DATA_REG, cr66);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    OUTREG8(CRT_DATA_REG, cr3a);

    /* unlock extended seq regs */
    OUTREG8(SEQ_ADDRESS_REG, 0x08);
    save->SR08 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_DATA_REG, 0x06);

    /* now save all the extended regs we need */
    OUTREG8(CRT_ADDRESS_REG, 0x31);
    save->CR31 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x32);
    save->CR32 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x34);
    save->CR34 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x36);
    save->CR36 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    save->CR3A = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x40);
    save->CR40 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x42);
    save->CR42 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x45);
    save->CR45 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x50);
    save->CR50 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x51);
    save->CR51 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x53);
    save->CR53 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x58);
    save->CR58 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x60);
    save->CR60 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x66);
    save->CR66 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x67);
    save->CR67 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x68);
    save->CR68 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x69);
    save->CR69 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x6f);
    save->CR6F = INREG8(CRT_DATA_REG);

    OUTREG8(CRT_ADDRESS_REG, 0x33);
    save->CR33 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x86);
    save->CR86 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x88);
    save->CR88 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x90);
    save->CR90 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x91);
    save->CR91 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0xb0);
    save->CRB0 = INREG8(CRT_DATA_REG) | 0x80;

    /* extended mode timing regs */
    OUTREG8(CRT_ADDRESS_REG, 0x3b);
    save->CR3B = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x3c);
    save->CR3C = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x43);
    save->CR43 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x5d);
    save->CR5D = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x5e);
    save->CR5E = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_ADDRESS_REG, 0x65);
    save->CR65 = INREG8(CRT_DATA_REG);

    /* save seq extended regs for DCLK PLL programming */
    OUTREG8(SEQ_ADDRESS_REG, 0x0e);
    save->SR0E = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x0f);
    save->SR0F = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x10);
    save->SR10 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x11);
    save->SR11 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x12);
    save->SR12 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x13);
    save->SR13 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x29);
    save->SR29 = INREG8(SEQ_DATA_REG);

    OUTREG8(SEQ_ADDRESS_REG, 0x15);
    save->SR15 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x30);
    save->SR30 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x18);
    save->SR18 = INREG8(SEQ_DATA_REG);
    OUTREG8(SEQ_ADDRESS_REG, 0x1b);
    save->SR1B = INREG8(SEQ_DATA_REG);

    /* Save flat panel expansion registers. */

    if( S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) {
	int i;
	for( i = 0; i < 8; i++ ) {
	    OUTREG8(SEQ_ADDRESS_REG, 0x54+i);
	    save->SR54[i] = INREG8(SEQ_DATA_REG);
	}
    }

    OUTREG8(CRT_ADDRESS_REG, 0x66);
    cr66 = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr66 | 0x80);
    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    cr3a = INREG8(CRT_DATA_REG);
    OUTREG8(CRT_DATA_REG, cr3a | 0x80);

    /* now save MIU regs */
    if( ! S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ) {
	save->MMPR0 = INREG(FIFO_CONTROL_REG);
	save->MMPR1 = INREG(MIU_CONTROL_REG);
	save->MMPR2 = INREG(STREAMS_TIMEOUT_REG);
	save->MMPR3 = INREG(MISC_TIMEOUT_REG);
    }

    OUTREG8(CRT_ADDRESS_REG, 0x3a);
    OUTREG8(CRT_DATA_REG, cr3a);
    OUTREG8(CRT_ADDRESS_REG, 0x66);
    OUTREG8(CRT_DATA_REG, cr66);

    if (!psav->ModeStructInit) {
	vgaHWCopyReg(&hwp->ModeReg, vgaSavePtr);
	memcpy(&psav->ModeReg, save, sizeof(SavageRegRec));
	psav->ModeStructInit = TRUE;
    }


    return;
}


