#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif

/**************************************************************************

Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
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 PRECISION INSIGHT 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.

**************************************************************************/

/*
 * Authors:
 *   Daryll Strauss <daryll@precisioninsight.com>
 *
 */

/*
 * This server does not support these XFree 4.0 features yet
 * DDC2 (requires I2C)
 * shadowFb (if requested or acceleration is off)
 * Overlay planes
 */

/*
 * These are X and server generic header files.
 */
#include "xf86.h"
#include "xf86_OSproc.h"
#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6
#include "xf86Resources.h"
#include "xf86RAC.h"
#endif
#include "vbe.h"
#include "xf86cmap.h"

/* If the driver uses port I/O directly, it needs: */

#include "compiler.h"

/* Drivers using the mi implementation of backing store need: */

#include "mibstore.h"

/* All drivers using the vgahw module need this */
/* This driver needs to be modified to not use vgaHW for multihead operation */
#include "vgaHW.h"

/* Drivers using the mi SW cursor need: */

#include "mipointer.h"

/* Drivers using the mi colourmap code need: */

#include "micmap.h"

/* Required for line biases */
#include "miline.h"

#include "fb.h"

/* !!! These need to be checked !!! */
#if 0
#define _XF86DGA_SERVER_
#include <X11/extensions/xf86dgastr.h>
#endif

/* The driver's own header file: */

#include "tdfx.h"

#include "regionstr.h"
#include "dixstruct.h"

#include "xf86xv.h"
#include <X11/extensions/Xv.h>

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

#define USE_INT10 1
#define USE_PCIVGAIO (GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 12)

/* Required Functions: */

static const OptionInfoRec *	TDFXAvailableOptions(int chipid, int busid);
/* Print a driver identifying message. */
static void TDFXIdentify(int flags);

/* Identify if there is any hardware present that I know how to drive. */
#ifdef XSERVER_LIBPCIACCESS
static Bool TDFXPciProbe(DriverPtr drv, int entity_num,
    struct pci_device *dev, intptr_t match_data);
#else
static Bool TDFXProbe(DriverPtr drv, int flags);
#endif

/* Process the config file and see if we have a valid configuration */
static Bool TDFXPreInit(ScrnInfoPtr pScrn, int flags);

/* Initialize a screen */
static Bool TDFXScreenInit(int Index, ScreenPtr pScreen, int argc, char **argv);

/* Enter from a virtual terminal */
static Bool TDFXEnterVT(int scrnIndex, int flags);

/* Leave to a virtual terminal */
static void TDFXLeaveVT(int scrnIndex, int flags);

/* Close down each screen we initialized */
static Bool TDFXCloseScreen(int scrnIndex, ScreenPtr pScreen);

/* Change screensaver state */
static Bool TDFXSaveScreen(ScreenPtr pScreen, int mode);

/* Cleanup server private data */
static void TDFXFreeScreen(int scrnIndex, int flags);

/* Check if a mode is valid on the hardware */
static ModeStatus TDFXValidMode(int scrnIndex, DisplayModePtr mode,
				Bool verbose, int flags);

static void TDFXBlockHandler(int, pointer, pointer, pointer);

/* Switch to various Display Power Management System levels */
static void TDFXDisplayPowerManagementSet(ScrnInfoPtr pScrn, 
					int PowerManagermentMode, int flags);

#ifdef XSERVER_LIBPCIACCESS
#define TDFX_DEVICE_MATCH(d, sub, i) \
    { 0x121A, (d), PCI_MATCH_ANY, (sub), 0, 0, (i) }

static const struct pci_id_match tdfx_device_match[] = {
    TDFX_DEVICE_MATCH(PCI_CHIP_BANSHEE, PCI_MATCH_ANY, Banshee),

    /* There are *many* missing PCI IDs here.
     */
    TDFX_DEVICE_MATCH(PCI_CHIP_VOODOO3, 0x0036, Voodoo3_2000),
    TDFX_DEVICE_MATCH(PCI_CHIP_VOODOO3, 0x003A, Voodoo3_3000),

    TDFX_DEVICE_MATCH(PCI_CHIP_VOODOO3, PCI_MATCH_ANY, Voodoo3_Unknown),
    TDFX_DEVICE_MATCH(PCI_CHIP_VOODOO5, PCI_MATCH_ANY, Voodoo5),
    { 0, 0, 0 }
};

static const int MaxClocks[MAX_VOODOO_CARDS] = {
    [Banshee] = 270000,
    [Voodoo3_2000] = 300000,
    [Voodoo3_3000] = 350000,
    [Voodoo3_Unknown] = 300000,
    [Voodoo5] = 350000
};
#endif


_X_EXPORT DriverRec TDFX = {
  TDFX_VERSION,
  TDFX_DRIVER_NAME,
  TDFXIdentify,
#ifdef XSERVER_LIBPCIACCESS
  NULL,
#else
  TDFXProbe,
#endif

  TDFXAvailableOptions,
  NULL,
  0,
  NULL,

#ifdef XSERVER_LIBPCIACCESS
  tdfx_device_match,
  TDFXPciProbe
#endif
};

/* Chipsets */
static SymTabRec TDFXChipsets[] = {
  { PCI_CHIP_BANSHEE, "3dfx Banshee"},
  { PCI_CHIP_VOODOO3, "3dfx Voodoo3"},
  { PCI_CHIP_VOODOO5, "3dfx Voodoo5"},
  { -1, NULL }
};

#ifndef XSERVER_LIBPCIACCESS
static PciChipsets TDFXPciChipsets[] = {
  { PCI_CHIP_BANSHEE, PCI_CHIP_BANSHEE, RES_SHARED_VGA },
  { PCI_CHIP_VOODOO3, PCI_CHIP_VOODOO3, RES_SHARED_VGA },
  { PCI_CHIP_VOODOO5, PCI_CHIP_VOODOO5, RES_SHARED_VGA },
  { -1, -1, RES_UNDEFINED }
};
#endif

/* !!! Do we want an option for alternate clocking? !!! */

typedef enum {
  OPTION_NOACCEL,
  OPTION_SW_CURSOR,
  OPTION_USE_PIO,
  OPTION_SHOWCACHE,
  OPTION_VIDEO_KEY,
  OPTION_NO_SLI,
  OPTION_TEXTURED_VIDEO,
  OPTION_DRI
} TDFXOpts;

static const OptionInfoRec TDFXOptions[] = {
  { OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, {0}, FALSE },
  { OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, {0}, FALSE },
  { OPTION_USE_PIO, "UsePIO", OPTV_BOOLEAN, {0}, FALSE},
  { OPTION_SHOWCACHE, "ShowCache", OPTV_BOOLEAN, {0}, FALSE},
  { OPTION_VIDEO_KEY, "VideoKey", OPTV_INTEGER, {0}, FALSE},
  { OPTION_NO_SLI, "NoSLI", OPTV_BOOLEAN, {0}, FALSE},
  { OPTION_TEXTURED_VIDEO, "TexturedVideo", OPTV_BOOLEAN, {1}, FALSE},
  { OPTION_DRI, "DRI", OPTV_BOOLEAN, {0}, FALSE},
  { -1, NULL, OPTV_NONE, {0}, FALSE}
};

#ifdef XFree86LOADER

static MODULESETUPPROTO(tdfxSetup);

static XF86ModuleVersionInfo tdfxVersRec =
{
  "tdfx",
  MODULEVENDORSTRING,
  MODINFOSTRING1,
  MODINFOSTRING2,
  XORG_VERSION_CURRENT,
  TDFX_MAJOR_VERSION, TDFX_MINOR_VERSION, TDFX_PATCHLEVEL,
  ABI_CLASS_VIDEODRV,
  ABI_VIDEODRV_VERSION,
  MOD_CLASS_VIDEODRV,
  {0,0,0,0}
};

_X_EXPORT XF86ModuleData tdfxModuleData = {&tdfxVersRec, tdfxSetup, 0};

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

    /* This module should be loaded only once, but check to be sure. */

    if (!setupDone) {
	setupDone = TRUE;
	xf86AddDriver(&TDFX, module, 1);

	/*
	 * The return value must be non-NULL on success even though there
	 * is no TearDownProc.
	 */
	return (pointer)1;
    } else {
	if (errmaj) *errmaj = LDR_ONCEONLY;
	return NULL;
    }
}

#endif

/*
 * TDFXGetRec and TDFXFreeRec --
 *
 * Private data for the driver is stored in the screen structure. 
 * These two functions create and destroy that private data.
 *
 */
static TDFXPtr
TDFXGetRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate == NULL) {
	pScrn->driverPrivate = xnfcalloc(sizeof(TDFXRec), 1);
    }

    return (TDFXPtr) pScrn->driverPrivate;
}

static void
TDFXFreeRec(ScrnInfoPtr pScrn) {
  if (!pScrn) return;
  if (!pScrn->driverPrivate) return;
  free(pScrn->driverPrivate);
  pScrn->driverPrivate=0;
}

/*
 * TDFXIdentify --
 *
 * Returns the string name for the driver based on the chipset. In this
 * case it will always be an TDFX, so we can return a static string.
 * 
 */
static void
TDFXIdentify(int flags) {
  xf86PrintChipsets(TDFX_NAME, "Driver for 3dfx Banshee/Voodoo3 chipsets", TDFXChipsets);
}

static const OptionInfoRec *
TDFXAvailableOptions(int chipid, int busid)
{
    return TDFXOptions;
}

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

#ifdef XSERVER_LIBPCIACCESS
/**
 * TDFXPciProbe
 *
 * Look through the PCI bus to find cards that are TDFX boards.
 * Setup the dispatch table for the rest of the driver functions.
 */
static Bool
TDFXPciProbe(DriverPtr drv, int entity_num, struct pci_device *dev,
	     intptr_t match_data)
{
    ScrnInfoPtr pScrn;

    TDFXTRACE("TDFXPciProbe start\n");


    /* Allocate new ScrnInfoRec and claim the slot */
    pScrn = xf86ConfigPciEntity(NULL, 0, entity_num, NULL, NULL,
				NULL, NULL, NULL, NULL);
    if (pScrn != NULL) {
	TDFXPtr pTDFX;

	pScrn->driverVersion = TDFX_VERSION;
	pScrn->driverName = TDFX_DRIVER_NAME;
	pScrn->name = TDFX_NAME;
	pScrn->Probe = NULL;
	pScrn->PreInit = TDFXPreInit;
	pScrn->ScreenInit = TDFXScreenInit;
	pScrn->SwitchMode = TDFXSwitchMode;
	pScrn->AdjustFrame = TDFXAdjustFrame;
	pScrn->EnterVT = TDFXEnterVT;
	pScrn->LeaveVT = TDFXLeaveVT;
	pScrn->FreeScreen = TDFXFreeScreen;
	pScrn->ValidMode = TDFXValidMode;

	/* Allocate driverPrivate */
	pTDFX = TDFXGetRec(pScrn);
	if (pTDFX == NULL) {
	    return FALSE;
	}

	pTDFX->initDone = FALSE;
	pTDFX->match_id = (enum tdfx_chips) match_data;
	pTDFX->pEnt = xf86GetEntityInfo(entity_num);
	pTDFX->PciInfo[0] = dev;
	pTDFX->numChips = 1;
	pTDFX->Primary = xf86IsPrimaryPci(dev);

	pTDFX->PIOBase[0] = dev->regions[2].base_addr;
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "PIO base = 0x%lx\n", pTDFX->PIOBase[0]);
    }

    return (pScrn != NULL);
}
#else
/**
 * TDFXProbe
 *
 * Look through the PCI bus to find cards that are TDFX boards.
 * Setup the dispatch table for the rest of the driver functions.
 */
static Bool
TDFXProbe(DriverPtr drv, int flags)
{
  int i, numUsed, numDevSections, *usedChips;
  GDevPtr *devSections;
  Bool foundScreen = FALSE;

  TDFXTRACE("TDFXProbe start\n");
  /*
   Find the config file Device sections that match this
   driver, and return if there are none.
   */
  if ((numDevSections = xf86MatchDevice(TDFX_DRIVER_NAME, &devSections))<=0) {
    return FALSE;
  }

  /* 
     Since these Probing is just checking the PCI data the server already
     collected.
  */
  if (!xf86GetPciVideoInfo()) return FALSE;

  numUsed = xf86MatchPciInstances(TDFX_NAME, PCI_VENDOR_3DFX,
				  TDFXChipsets, TDFXPciChipsets,
				  devSections, numDevSections,
				  drv, &usedChips);

  free(devSections);
  if (numUsed<=0) return FALSE;

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

    /* Allocate new ScrnInfoRec and claim the slot */
    pScrn = NULL;
    if ((pScrn = xf86ConfigPciEntity(pScrn, 0, usedChips[i],
			   TDFXPciChipsets, NULL, NULL, NULL, NULL, NULL))) {

	pScrn->driverVersion = TDFX_VERSION;
	pScrn->driverName = TDFX_DRIVER_NAME;
	pScrn->name = TDFX_NAME;
	pScrn->Probe = TDFXProbe;
	pScrn->PreInit = TDFXPreInit;
	pScrn->ScreenInit = TDFXScreenInit;
	pScrn->SwitchMode = TDFXSwitchMode;
	pScrn->AdjustFrame = TDFXAdjustFrame;
	pScrn->EnterVT = TDFXEnterVT;
	pScrn->LeaveVT = TDFXLeaveVT;
	pScrn->FreeScreen = TDFXFreeScreen;
	pScrn->ValidMode = TDFXValidMode;
	foundScreen = TRUE;
    }
  }
  free(usedChips);

  return foundScreen;
}
#endif

static int
TDFXCountRam(ScrnInfoPtr pScrn) {
  TDFXPtr pTDFX;
  int vmemSize;
  int vmemType=-1; /* SDRAM or SGRAM */

  pTDFX = TDFXPTR(pScrn);
  TDFXTRACE("TDFXCountRam start\n");
  vmemSize=0;
  if (pTDFX->PIOBase[0]) {
    CARD32 
      partSize,                 /* size of SGRAM chips in Mbits */
      nChips,                   /* # chips of SDRAM/SGRAM */
      banks,			/* Number of banks of chips */
      dramInit0_strap,    
      dramInit1_strap,    
      dramInit1,
      miscInit1;

    /* determine memory type: SDRAM or SGRAM */
    vmemType = MEM_TYPE_SGRAM;
    dramInit1_strap = pTDFX->readLong(pTDFX, DRAMINIT1);
    dramInit1_strap &= SST_MCTL_TYPE_SDRAM;
    if (dramInit1_strap) vmemType = MEM_TYPE_SDRAM;

    /* set memory interface delay values and enable refresh */
    /* these apply to all RAM vendors */
    dramInit1 = 0x0;
    dramInit1 |= 2<<SST_SGRAM_OFLOP_DEL_ADJ_SHIFT;
    dramInit1 |= SST_SGRAM_CLK_NODELAY;
    dramInit1 |= SST_DRAM_REFRESH_EN;
    dramInit1 |= (0x18 << SST_DRAM_REFRESH_VALUE_SHIFT) & SST_DRAM_REFRESH_VALUE;  
    dramInit1 &= ~SST_MCTL_TYPE_SDRAM;
    dramInit1 |= dramInit1_strap;

    /* Some information about the timing register (for later debugging) */
    xf86DrvMsg(pScrn->scrnIndex, X_INFO, 
               "DRAMINIT1 read 0x%x, programming 0x%lx (not Banshee)\n",
	       pTDFX->readLong(pTDFX, DRAMINIT1), (unsigned long)dramInit1);

    /* 
     * Here we don't whack the timing register on the Banshee boards as the
     * BIOS has done a perfectly good job. As Banshee boards were made by
     * different manufacturers we don't touch this, but carry on whacking it 
     * for Voodoo3 and Voodoo5 boards, as we've never had a problem with them.
     * I guess this could be removed for all boards as we probably shouldn't
     * be doing this to the V3/V5 boards too.
     */
    if (pTDFX->ChipType != PCI_CHIP_BANSHEE)
    	pTDFX->writeLong(pTDFX, DRAMINIT1, dramInit1);

    /* determine memory size from strapping pins (dramInit0 and dramInit1) */
    dramInit0_strap = pTDFX->readLong(pTDFX, DRAMINIT0);

    if (pTDFX->ChipType<=PCI_CHIP_VOODOO3) { /* Banshee/V3 */
      if (vmemType == MEM_TYPE_SDRAM) {
	vmemSize = 16;
      } else {
	nChips = ((dramInit0_strap & SST_SGRAM_NUM_CHIPSETS) == 0) ? 4 : 8;
    
	if ( (dramInit0_strap & SST_SGRAM_TYPE) == SST_SGRAM_TYPE_8MBIT )  {
	  partSize = 8;
	} else if ( (dramInit0_strap & SST_SGRAM_TYPE) == SST_SGRAM_TYPE_16MBIT) {
	  partSize = 16;
	} else {
	  ErrorF("Invalid sgram type = 0x%lx",
		 (dramInit0_strap & SST_SGRAM_TYPE) << SST_SGRAM_TYPE_SHIFT );
	  return 0;
	}
	vmemSize = (nChips * partSize) / 8;      /* in MBytes */
      }
    } else { /* V4, V5 */
      nChips = ((dramInit0_strap & SST_SGRAM_NUM_CHIPSETS)==0) ? 4 : 8;
      partSize=1<<((dramInit0_strap&0x38000000)>>28);
      banks=((dramInit0_strap&BIT(30))==0) ? 2 : 4;
      vmemSize=nChips*partSize*banks;
    }
    TDFXTRACEREG("dramInit0 = %lx dramInit1 = %lx\n",
		(unsigned long)dramInit0_strap, (unsigned long)dramInit1_strap);
    TDFXTRACEREG("MemConfig %d chips %ld size %ld total\n", (int)nChips,
		(unsigned long)partSize, (unsigned long)vmemSize);

    /*
      disable block writes for SDRAM
    */
    miscInit1 = pTDFX->readLong(pTDFX, MISCINIT1);
    if ( vmemType == MEM_TYPE_SDRAM ) {
      miscInit1 |= SST_DISABLE_2D_BLOCK_WRITE;
    }
    miscInit1|=1;
    pTDFX->writeLong(pTDFX, MISCINIT1, miscInit1);
  }

  /* return # of KBytes of board memory */
  return vmemSize*1024;
}

#if 0
static int TDFXCfgToSize(int cfg)
{
  if (cfg<4) return 0x8000000<<cfg;
  return 0x4000000>>(cfg-4);
}
#endif

static int TDFXSizeToCfg(int size)
{
  switch (size) {
  case 0x40000000: return 3;
  case 0x20000000: return 2;
  case 0x10000000: return 1;
  case 0x08000000: return 0;
  case 0x04000000: return 4;
  case 0x02000000: return 5;
  case 0x01000000: return 6;
  case 0x00800000: return 7;
  case 0x00400000: return 8;
  default:
    return -1;
  }
}

#ifndef XSERVER_LIBPCIACCESS
static void
TDFXFindChips(ScrnInfoPtr pScrn, pciVideoPtr match)
{
  TDFXPtr pTDFX;
  pciVideoPtr *ppPci;

  pTDFX=TDFXPTR(pScrn);
  pTDFX->numChips=0;
  pTDFX->ChipType=match->chipType;
  for (ppPci = xf86GetPciVideoInfo(); *ppPci != NULL; ppPci++) {
    if ((*ppPci)->bus == match->bus && 
	(*ppPci)->device == match->device) {
      pTDFX->PciTag[pTDFX->numChips] = pciTag((*ppPci)->bus, 
					      (*ppPci)->device,
					      (*ppPci)->func);
      pTDFX->PIOBase[pTDFX->numChips] =
	pScrn->domainIOBase + ((*ppPci)->ioBase[2] & 0xFFFFFFFCU);
      pTDFX->numChips++;
    }
  }
  xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
	"TDFXFindChips: found %d chip(s)\n", pTDFX->numChips);
  /* Disable the secondary chips for now */
  pTDFX->numChips=1;
}
#endif

static void
TDFXInitChips(ScrnInfoPtr pScrn)
{
    TDFXPtr pTDFX = TDFXPTR(pScrn);
    int i;
#if 0
    int v;
#endif
    uint32_t cfgbits, initbits;
    uint32_t mem0base, mem1base, mem0size, mem0bits, mem1size, mem1bits;


    PCI_READ_LONG(cfgbits, CFG_PCI_DECODE, 0);
    PCI_READ_LONG(mem0base, CFG_MEM0BASE, 0);
    PCI_READ_LONG(mem1base, CFG_MEM1BASE, 0);
    PCI_READ_LONG(initbits, CFG_INIT_ENABLE, 0);

    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		   "TDFXInitChips: numchips = %d\n", pTDFX->numChips);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		   "TDFXInitChips: cfgbits = 0x%08lx, initbits = 0x%08lx\n",
		   cfgbits, initbits);
    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		   "TDFXInitChips: mem0base = 0x%08lx, mem1base = 0x%08lx\n",
		   mem0base, mem1base);

    mem0size = 32 * 1024 * 1024; /* Registers are always 32MB */
    mem1size = pScrn->videoRam * 1024 * 2; /* Linear mapping is 2x memory */

    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		   "TDFXInitChips: mem0size = 0x%08lx, mem1size = 0x%08lx\n",
		   mem0size, mem1size);

    mem0bits = TDFXSizeToCfg(mem0size);
    mem1bits = TDFXSizeToCfg(mem1size) << 4;

    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		   "TDFXInitChips: mem0bits = 0x%08lx, mem1bits = 0x%08lx\n",
		   mem0bits, mem1bits);

    cfgbits = (cfgbits & ~(0xFF)) | mem0bits | mem1bits;

    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		   "TDFXInitChips: cfgbits = 0x%08lx\n", cfgbits);

    for (i = 0; i < pTDFX->numChips; i++) {
	PCI_WRITE_LONG(initbits | BIT(10), CFG_INIT_ENABLE, i);

#if 0
	v=pciReadWord(pTDFX->PciTag[i], CFG_PCI_COMMAND);
	if (!i)
	    pciWriteWord(pTDFX->PciTag[i], CFG_PCI_COMMAND, v | 0x3);
	else
	    pciWriteWord(pTDFX->PciTag[i], CFG_PCI_COMMAND, v | 0x2);
#endif

	pTDFX->MMIOAddr[i] = mem0base + (i * mem0size);

	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		       "TDFXInitChips: MMIOAddr[%d] = 0x%08lx\n",
		       i, pTDFX->MMIOAddr[i]);

	PCI_WRITE_LONG(pTDFX->MMIOAddr[i], CFG_MEM0BASE, i);

	pTDFX->MMIOAddr[i] &= 0xFFFFFF00;
	pTDFX->LinearAddr[i] = mem1base + (i * mem1size);

	PCI_WRITE_LONG(pTDFX->LinearAddr[i], CFG_MEM1BASE, i);

	xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3,
		       "TDFXInitChips: LinearAddr[%d] = 0x%08lx\n",
		       i, pTDFX->LinearAddr[i]);
	pTDFX->LinearAddr[i] &= 0xFFFFFF00;

	PCI_WRITE_LONG(cfgbits, CFG_PCI_DECODE, i);
	PCI_WRITE_LONG(initbits, CFG_INIT_ENABLE, i);
    }
}

void
TDFXPutBits(I2CBusPtr b, int  scl, int  sda)
{
  TDFXPtr pTDFX= b->DriverPrivate.ptr;
  CARD32 reg;

  reg = pTDFX->readLong(pTDFX, VIDSERIALPARALLELPORT);
  reg = (reg & ~(VSP_SDA0_OUT | VSP_SCL0_OUT)) |
  	(scl ? VSP_SCL0_OUT : 0) |
  	(sda ? VSP_SDA0_OUT : 0);
  pTDFX->writeLong(pTDFX, VIDSERIALPARALLELPORT, reg);
}

void
TDFXGetBits(I2CBusPtr b, int *scl, int *sda)
{
  TDFXPtr pTDFX = b->DriverPrivate.ptr;
  CARD32 reg;

  reg = pTDFX->readLong(pTDFX, VIDSERIALPARALLELPORT);
  *sda = (reg & VSP_SDA0_IN) ? 1 : 0;
  *scl = (reg & VSP_SCL0_IN) ? 1 : 0;
}

Bool TDFXI2cInit(ScrnInfoPtr pScrn)
{
  TDFXPtr pTDFX = TDFXPTR(pScrn);

  if (!(pTDFX->pI2CBus = xf86CreateI2CBusRec()))
  {
    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Unable to allocate I2C Bus record.\n");
    return FALSE;
  }

  /* Fill in generic structure fields */
  pTDFX->pI2CBus->BusName           = "DDC";
  pTDFX->pI2CBus->scrnIndex         = pScrn->scrnIndex;
  pTDFX->pI2CBus->I2CPutBits        = TDFXPutBits;
  pTDFX->pI2CBus->I2CGetBits        = TDFXGetBits;
  pTDFX->pI2CBus->DriverPrivate.ptr = pTDFX;

  /* longer timeouts as per the vesa spec */
  pTDFX->pI2CBus->ByteTimeout = 2200; /* VESA DDC spec 3 p. 43 (+10 %) */
  pTDFX->pI2CBus->StartTimeout = 550;
  pTDFX->pI2CBus->BitTimeout = 40;
  pTDFX->pI2CBus->ByteTimeout = 40;
  pTDFX->pI2CBus->AcknTimeout = 40;

  if (!xf86I2CBusInit(pTDFX->pI2CBus)) {
    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Unable to init I2C Bus.\n");
    return FALSE;
  }
  return TRUE;
}

static xf86MonPtr doTDFXDDC(ScrnInfoPtr pScrn)
{
  TDFXPtr pTDFX = TDFXPTR(pScrn);
  I2CBusPtr pI2CBus;
  xf86MonPtr pMon = NULL;
  CARD32 reg;

  reg = pTDFX->readLong(pTDFX, VIDSERIALPARALLELPORT);
  pTDFX->writeLong(pTDFX, VIDSERIALPARALLELPORT, reg | VSP_ENABLE_IIC0);

  pMon = xf86DoEDID_DDC2(pScrn->scrnIndex, pTDFX->pI2CBus);

  if (pMon == NULL)
    xf86Msg(X_WARNING, "No DDC2 capable monitor found\n");

  pTDFX->writeLong(pTDFX, VIDSERIALPARALLELPORT, reg);

  return pMon;
}

/*
 * TDFXPreInit --
 *
 * Do initial setup of the board before we know what resolution we will
 * be running at.
 *
 */
static Bool
TDFXPreInit(ScrnInfoPtr pScrn, int flags) 
{
  TDFXPtr pTDFX;
  ClockRangePtr clockRanges;
  xf86MonPtr pMon;
  int i;
  MessageType from;
  int flags24;
  rgb defaultWeight = {0, 0, 0};
#ifdef XSERVER_LIBPCIACCESS
    struct pci_device *match;
#else
  pciVideoPtr match;
#endif
  int availableMem;

  TDFXTRACE("TDFXPreInit start\n");
  if (pScrn->numEntities != 1) return FALSE;

#ifndef XSERVER_LIBPCIACCESS
  /* Allocate driverPrivate */
  pTDFX = TDFXGetRec(pScrn);
  if (pTDFX == NULL) {
    return FALSE;
  }


  pTDFX->initDone=FALSE;
  pTDFX->pEnt = xf86GetEntityInfo(pScrn->entityList[0]);
#else
    pTDFX = TDFXPTR(pScrn);
#endif

  if (flags & PROBE_DETECT) {
#if !defined(__powerpc__)
    TDFXProbeDDC(pScrn, pTDFX->pEnt->index);
    return TRUE;
#else
    return FALSE;
#endif
  }

  if (pTDFX->pEnt->location.type != BUS_PCI) return FALSE;

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

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

#if USE_INT10
#if !defined(__powerpc__)
  if (xf86LoadSubModule(pScrn, "int10")) {
    xf86Int10InfoPtr pInt;

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, 
               "Softbooting the board (through the int10 interface).\n");
    pInt = xf86InitInt10(pTDFX->pEnt->index);
    if (!pInt)
    {
      xf86DrvMsg(pScrn->scrnIndex, X_WARNING, 
                 "Softbooting the board failed.\n");
    }
    else
    {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, 
                 "Softbooting the board succeeded.\n");
      xf86FreeInt10(pInt);
    }
  }
#endif
#endif

#ifdef XSERVER_LIBPCIACCESS
    match = pTDFX->PciInfo[0];
    pTDFX->ChipType = DEVICE_ID(match);
#else
  match=pTDFX->PciInfo=xf86GetPciInfoForEntity(pTDFX->pEnt->index);
  TDFXFindChips(pScrn, match);
  pTDFX->Primary = xf86IsPrimaryPci(pTDFX->PciInfo);
#endif

#ifndef XSERVER_LIBPCIACCESS
  if (xf86RegisterResources(pTDFX->pEnt->index, NULL, ResExclusive)) {
      TDFXFreeRec(pScrn);
      return FALSE;
  }

  /* 
   * We don't need VGA resources during OPERATING state. However I'm
   * not sure if they are disabled.
   */
  xf86SetOperatingState(resVgaIo, pTDFX->pEnt->index, ResDisableOpr);

  /* Is VGA memory disabled during OPERATING state? */
  xf86SetOperatingState(resVgaMem, pTDFX->pEnt->index, ResUnusedOpr);
    /* 
     * I'm sure we don't need to set these. All resources 
     * for these operations are exclusive.
     */
  if (pTDFX->usePIO) {
      pScrn->racMemFlags = 0;
      pScrn->racIoFlags = RAC_FB | RAC_COLORMAP | RAC_CURSOR | RAC_VIEWPORT;
  } else
      pScrn->racMemFlags = 0;
#endif
  /* Set pScrn->monitor */
  pScrn->monitor = pScrn->confScreen->monitor;

  flags24=Support24bppFb | Support32bppFb | SupportConvert32to24;
  if (!xf86SetDepthBpp(pScrn, 0, 0, 0, flags24)) {
    return FALSE;
  } else {
    switch (pScrn->depth) {
    case 8:
    case 16:
    case 24:
      break;
    default:
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 
		 "Given depth (%d) is not supported by tdfx driver\n", 
		 pScrn->depth);
      return FALSE;
    }
  }
  xf86PrintDepthBpp(pScrn);

  pScrn->rgbBits=8;
  if (pScrn->depth>8) {
    if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
      return FALSE;
  }

  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;
    }
  }

  /* We use a programmable clock */
  pScrn->progClock = TRUE;

  pTDFX->cpp = pScrn->bitsPerPixel/8;

  /* Process the options */
  xf86CollectOptions(pScrn, NULL);
  if (!(pTDFX->Options = malloc(sizeof(TDFXOptions))))
    return FALSE;
  memcpy(pTDFX->Options, TDFXOptions, sizeof(TDFXOptions));
  xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pTDFX->Options);

  /*
   * Set the Chipset and ChipRev, allowing config file entries to
   * override.
   */
  if (pTDFX->pEnt->device->chipset && *pTDFX->pEnt->device->chipset) {
    pScrn->chipset = pTDFX->pEnt->device->chipset;
    from = X_CONFIG;
  } else if (pTDFX->pEnt->device->chipID >= 0) {
    pScrn->chipset = (char *)xf86TokenToString(TDFXChipsets, pTDFX->pEnt->device->chipID);
    from = X_CONFIG;
    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipID override: 0x%04X\n",
	       pTDFX->pEnt->device->chipID);
  } else {
    from = X_PROBED;
    pScrn->chipset = (char *)xf86TokenToString(TDFXChipsets, 
					       DEVICE_ID(match));
  }
  if (pTDFX->pEnt->device->chipRev >= 0) {
    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n",
	       pTDFX->pEnt->device->chipRev);
  }

  xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\"\n", pScrn->chipset);

  if (pTDFX->pEnt->device->MemBase != 0) {
    pTDFX->LinearAddr[0] = pTDFX->pEnt->device->MemBase;
    from = X_CONFIG;
  } else {
    if (PCI_MEM_BASE(match, 1) != 0) {
      pTDFX->LinearAddr[0] = PCI_MEM_BASE(match, 1);
      from = X_PROBED;
    } else {
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 
		 "No valid FB address in PCI config space\n");
      TDFXFreeRec(pScrn);
      return FALSE;
    }
  }
  xf86DrvMsg(pScrn->scrnIndex, from, "Linear framebuffer at 0x%lX\n",
	     (unsigned long)pTDFX->LinearAddr[0]);

  if (pTDFX->pEnt->device->IOBase != 0) {
    pTDFX->MMIOAddr[0] = pTDFX->pEnt->device->IOBase;
    from = X_CONFIG;
  } else {
    if (PCI_MEM_BASE(match, 0)) {
      pTDFX->MMIOAddr[0] = PCI_MEM_BASE(match, 0);
      from = X_PROBED;
    } else {
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		 "No valid MMIO address in PCI config space\n");
      TDFXFreeRec(pScrn);
      return FALSE;
    }
  }
  xf86DrvMsg(pScrn->scrnIndex, from, "MMIO registers at addr 0x%lX\n",
	     (unsigned long)pTDFX->MMIOAddr[0]);

  if (!PCI_IO_BASE(match, 2)) {
    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
	       "No valid PIO address in PCI config space\n");
    TDFXFreeRec(pScrn);
    return FALSE;
  }
  xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "PIO registers at addr 0x%lX\n",
	     (unsigned long)pTDFX->PIOBase[0]);

  /* We have to use PIO to probe, because we haven't mappend yet */
  TDFXSetPIOAccess(pTDFX);

  /* Calculate memory */
  pScrn->videoRam = TDFXCountRam(pScrn);
  from = X_PROBED;
  if (pTDFX->pEnt->device->videoRam) {
    pScrn->videoRam = pTDFX->pEnt->device->videoRam;
    from = X_CONFIG;
  }

  TDFXInitChips(pScrn);

  /* Multiple by two because tiled access requires more address space */
  pTDFX->FbMapSize = pScrn->videoRam*1024*2;
  xf86DrvMsg(pScrn->scrnIndex, from, "VideoRAM: %d kByte Mapping %ld kByte\n",
	     pScrn->videoRam, pTDFX->FbMapSize/1024);

  /* Since we can do gamma correction, we call xf86SetGamma */
  {
    Gamma zeros = {0.0, 0.0, 0.0};
    
    if (!xf86SetGamma(pScrn, zeros)) {
      return FALSE;
    }
  }

  pTDFX->MaxClock = 0;
  if (pTDFX->pEnt->device->dacSpeeds[0]) {
    switch (pScrn->bitsPerPixel) {
    case 8:
      pTDFX->MaxClock = pTDFX->pEnt->device->dacSpeeds[DAC_BPP8];
      break;
    case 16:
      pTDFX->MaxClock = pTDFX->pEnt->device->dacSpeeds[DAC_BPP16];
      break;
    case 24:
      pTDFX->MaxClock = pTDFX->pEnt->device->dacSpeeds[DAC_BPP24];
      break;
    case 32:
      pTDFX->MaxClock = pTDFX->pEnt->device->dacSpeeds[DAC_BPP32];
      break;
    }
    if (!pTDFX->MaxClock)
      pTDFX->MaxClock = pTDFX->pEnt->device->dacSpeeds[0];
    from = X_CONFIG;
  } else {
#ifdef XSERVER_LIBPCIACCESS
      pTDFX->MaxClock = MaxClocks[pTDFX->match_id];
#else
    switch (pTDFX->ChipType) {
    case PCI_CHIP_BANSHEE:
      pTDFX->MaxClock = 270000;
      break;
    case PCI_CHIP_VOODOO3:
      switch(match->subsysCard) {
      case PCI_CARD_VOODOO3_2000:
	pTDFX->MaxClock = 300000;
	break;
      case PCI_CARD_VOODOO3_3000:
	pTDFX->MaxClock = 350000;
	break;
      default:
	pTDFX->MaxClock = 300000;
	break;
      }
      break;
    case PCI_CHIP_VOODOO5:
      pTDFX->MaxClock = 350000;
      break;
    }
#endif
  }
  clockRanges = xnfcalloc(sizeof(ClockRange), 1);
  clockRanges->next=NULL;
  clockRanges->minClock= 12000; /* !!! What's the min clock? !!! */
  clockRanges->maxClock=pTDFX->MaxClock;
  clockRanges->clockIndex = -1;
  switch (pTDFX->ChipType) {
    case PCI_CHIP_BANSHEE:
      clockRanges->interlaceAllowed = FALSE;
      break;
    case PCI_CHIP_VOODOO3:
    case PCI_CHIP_VOODOO5:
      clockRanges->interlaceAllowed = TRUE;
      break;
    default:
      clockRanges->interlaceAllowed = FALSE;
      break;
  }    
  clockRanges->doubleScanAllowed = TRUE;

  /*
   * Max memory available for the framebuffer is the total less the
   * HW cursor space and FIFO space.
   */
  availableMem = pScrn->videoRam - 4096 -
		 (((255 <= CMDFIFO_PAGES) ? 255 : CMDFIFO_PAGES) << 12);

  if (!xf86LoadSubModule(pScrn, "fb")) {
    TDFXFreeRec(pScrn);
    return FALSE;
  }

  pTDFX->NoAccel = xf86ReturnOptValBool(pTDFX->Options, OPTION_NOACCEL, FALSE);
  if (!pTDFX->NoAccel) {
    if (!xf86LoadSubModule(pScrn, "xaa")) {
      xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "No acceleration available\n");
      pTDFX->NoAccel = TRUE;
    }
  }

  if (!xf86GetOptValBool(pTDFX->Options, OPTION_SHOWCACHE, &(pTDFX->ShowCache))) {
    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ShowCache %s\n", pTDFX->ShowCache ? "Enabled" : "Disabled");
  } else {
    pTDFX->ShowCache = FALSE;
  }

  if (xf86GetOptValBool(pTDFX->Options, OPTION_TEXTURED_VIDEO, &(pTDFX->TextureXvideo))) {
    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Texture Xvideo Adaptor %s\n", pTDFX->TextureXvideo ? "Enabled" : "Disabled");
  } else {
    pTDFX->TextureXvideo = FALSE;
  }

  if (xf86GetOptValInteger(pTDFX->Options, OPTION_VIDEO_KEY, &(pTDFX->videoKey))) {
    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "video key set to 0x%x\n", pTDFX->videoKey);
  } else {
    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "video key default 0x%x\n", pTDFX->videoKey = 0x1E);
  }

  if (!xf86ReturnOptValBool(pTDFX->Options, OPTION_SW_CURSOR, FALSE)) {
    if (!xf86LoadSubModule(pScrn, "ramdac")) {
      TDFXFreeRec(pScrn);
      return FALSE;
    }
  }

  /* Load DDC and I2C for monitor ID */
  if (!xf86LoadSubModule(pScrn, "i2c")) {
    TDFXFreeRec(pScrn);
    return FALSE;
  }

  if (!xf86LoadSubModule(pScrn, "ddc")) {
    TDFXFreeRec(pScrn);
    return FALSE;
  }

  /* try to read read DDC2 data */
  if (TDFXI2cInit(pScrn)) {
    pMon = doTDFXDDC(pScrn);
    if (pMon != NULL)
      xf86SetDDCproperties(pScrn,xf86PrintEDID(pMon));
  } else {
    /* try to use vbe if we didn't find anything */
#if USE_INT10
#if !defined(__powerpc__)
    /* Initialize DDC1 if possible */
    if (xf86LoadSubModule(pScrn, "vbe")) {
      vbeInfoPtr pVbe = VBEInit(NULL,pTDFX->pEnt->index);

      pMon = vbeDoEDID(pVbe, NULL);
      vbeFree(pVbe);
      xf86SetDDCproperties(pScrn,xf86PrintEDID(pMon));
    }
#endif
#endif
  }

  i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
			pScrn->display->modes, clockRanges,
			0, 320, 2048, 16*pScrn->bitsPerPixel, 
			200, 2047,
			pScrn->display->virtualX, pScrn->display->virtualY,
			availableMem, LOOKUP_BEST_REFRESH);

  if (i==-1) {
    TDFXFreeRec(pScrn);
    return FALSE;
  }

  xf86PruneDriverModes(pScrn);

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

  xf86SetCrtcForModes(pScrn, 0);

  pScrn->currentMode = pScrn->modes;

  xf86PrintModes(pScrn);

  xf86SetDpi(pScrn, 0, 0);

  if (xf86ReturnOptValBool(pTDFX->Options, OPTION_USE_PIO, FALSE)) {
    pTDFX->usePIO=TRUE;
  }

#if X_BYTE_ORDER == X_BIG_ENDIAN
  pTDFX->ModeReg.miscinit0 = pTDFX->readLong(pTDFX, MISCINIT0);
  pTDFX->SavedReg.miscinit0 = pTDFX->ModeReg.miscinit0;

  switch (pScrn->bitsPerPixel) {
  case 8:
    pTDFX->writeFifo = TDFXWriteFifo_8;
    pTDFX->ModeReg.miscinit0 &= ~BIT(30); /* LFB byte swizzle */
    pTDFX->ModeReg.miscinit0 &= ~BIT(31); /* LFB word swizzle */
    break;
  case 15:
  case 16:
    pTDFX->writeFifo = TDFXWriteFifo_16;
    pTDFX->ModeReg.miscinit0 |= BIT(30); /* LFB byte swizzle */
    pTDFX->ModeReg.miscinit0 |= BIT(31); /* LFB word swizzle */
    break;
  case 24:
  case 32:
    pTDFX->writeFifo = TDFXWriteFifo_24;
    pTDFX->ModeReg.miscinit0 |= BIT(30); /* LFB byte swizzle */
    pTDFX->ModeReg.miscinit0 &= ~BIT(31); /* LFB word swizzle */
    break;
  default:
    return FALSE;
    break;
  }
  pTDFX->writeLong(pTDFX, MISCINIT0, pTDFX->ModeReg.miscinit0);
#endif

#ifdef TDFXDRI
  /* Load the dri module if requested. */
  if (xf86ReturnOptValBool(pTDFX->Options, OPTION_DRI, FALSE)) {
    xf86LoadSubModule(pScrn, "dri");
  }
#endif
  return TRUE;
}

static Bool
TDFXMapMem(ScrnInfoPtr pScrn)
{
    int i;
    TDFXPtr pTDFX = TDFXPTR(pScrn);
#ifdef XSERVER_LIBPCIACCESS
    int err;
#else
    const int mmioFlags = VIDMEM_MMIO | VIDMEM_READSIDEEFFECT;
#endif

  TDFXTRACE("TDFXMapMem start\n");

#ifdef XSERVER_LIBPCIACCESS
    /* FIXME: I'm not convinced that this is correct for SLI cards, but I
     * FIXME: don't have any such hardware to test.
     */
    for (i = 0; i < pTDFX->numChips; i++) {
	err = pci_device_map_memory_range(pTDFX->PciInfo[i],
					  pTDFX->MMIOAddr[i],
					  TDFXIOMAPSIZE,
					  TRUE,
					  & pTDFX->MMIOBase[i]);
	if (err) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		       "Unable to map MMIO region for card %u (%d).\n",
		       i, err);
	    return FALSE;
	}
    }
    

    err = pci_device_map_memory_range(pTDFX->PciInfo[0],
				      pTDFX->LinearAddr[0],
				      pTDFX->FbMapSize,
				      TRUE,
				      & pTDFX->FbBase);
    if (err) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		   "Unable to map framebuffer (%d).\n", err);
	return FALSE;
    }
#else
  for (i=0; i<pTDFX->numChips; i++) {
    pTDFX->MMIOBase[i] = xf86MapPciMem(pScrn->scrnIndex, mmioFlags, 
				       pTDFX->PciTag[i], 
				       pTDFX->MMIOAddr[i],
				       TDFXIOMAPSIZE);

    if (!pTDFX->MMIOBase[i]) return FALSE;
  }

  pTDFX->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER,
				pTDFX->PciTag[0],
				pTDFX->LinearAddr[0],
				pTDFX->FbMapSize);
  if (!pTDFX->FbBase) return FALSE;
#endif

  return TRUE;
}

static Bool
TDFXUnmapMem(ScrnInfoPtr pScrn)
{
  TDFXPtr pTDFX;
  int i;

  TDFXTRACE("TDFXUnmapMem start\n");
  pTDFX = TDFXPTR(pScrn);

#ifdef XSERVER_LIBPCIACCESS
    pci_device_unmap_region(pTDFX->PciInfo[0], 0);
    pci_device_unmap_region(pTDFX->PciInfo[0], 1);

    (void) memset(pTDFX->MMIOBase, 0, sizeof(pTDFX->MMIOBase));
    pTDFX->FbBase = NULL;
#else
  for (i=0; i<pTDFX->numChips; i++) {
    xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pTDFX->MMIOBase[i], 
		    TDFXIOMAPSIZE);
    pTDFX->MMIOBase[i]=0;
  }

  xf86UnMapVidMem(pScrn->scrnIndex, (pointer)pTDFX->FbBase, pTDFX->FbMapSize);
  pTDFX->FbBase = 0;
#endif
  return TRUE;
}

static void
PrintRegisters(ScrnInfoPtr pScrn, TDFXRegPtr regs)
{
#ifdef TRACE
  int i;
  TDFXPtr pTDFX;
  vgaHWPtr pHW = VGAHWPTR(pScrn);
  vgaRegPtr pVga = &VGAHWPTR(pScrn)->ModeReg;


  pTDFX = TDFXPTR(pScrn);
#if 1
  ErrorF("VGA Registers\n");
  ErrorF("MiscOutReg = %x versus %x\n", pHW->readMiscOut(pHW), pVga->MiscOutReg);
  ErrorF("EnableReg = %x\n", pHW->readEnable(pHW));
  for (i=0; i<25; i++) {
    int x;
    x = pHW->readCrtc(pHW, i);
    ErrorF("CRTC[%d]=%d versus %d\n", i, x, pVga->CRTC[i]);
  }
  for (i=0; i<21; i++) {
    ErrorF("Attribute[%d]=%d versus %d\n", i, pHW->readAttr(pHW, i), pVga->Attribute[i]);
  }
  for (i=0; i<9; i++) {
    ErrorF("Graphics[%d]=%d versus %d\n", i, pHW->readGr(pHW, i), pVga->Graphics[i]);
  }
  for (i=0; i<5; i++) {
    ErrorF("Sequencer[%d]=%d versus %d\n", i, pHW->readSeq(pHW, i), pVga->Sequencer[i]);
  }
#endif
#if 1
  ErrorF("Banshee Registers\n");
  ErrorF("VidCfg = %x versus %x\n",  pTDFX->readLong(pTDFX, VIDPROCCFG), regs->vidcfg);
  ErrorF("DACmode = %x versus %x\n", pTDFX->readLong(pTDFX, DACMODE), regs->dacmode);
  ErrorF("Vgainit0 = %x versus %x\n", pTDFX->readLong(pTDFX, VGAINIT0), regs->vgainit0);
  ErrorF("Vgainit1 = %x versus %x\n", pTDFX->readLong(pTDFX, VGAINIT1), regs->vgainit1);
  ErrorF("Miscinit0 = %x versus %x\n", pTDFX->readLong(pTDFX, MISCINIT0), regs->miscinit0);
  ErrorF("Miscinit1 = %x versus %x\n", pTDFX->readLong(pTDFX, MISCINIT1), regs->miscinit1);
  ErrorF("DramInit0 = %x\n", pTDFX->readLong(pTDFX, DRAMINIT0));
  ErrorF("DramInit1 = %x\n", pTDFX->readLong(pTDFX, DRAMINIT1));
  ErrorF("VidPLL = %x versus %x\n", pTDFX->readLong(pTDFX, PLLCTRL0), regs->vidpll);
  ErrorF("screensize = %x versus %x\n", pTDFX->readLong(pTDFX, VIDSCREENSIZE), regs->screensize);
  ErrorF("stride = %x versus %x\n", pTDFX->readLong(pTDFX, VIDDESKTOPOVERLAYSTRIDE), regs->stride);
  ErrorF("startaddr = %x versus %x\n", pTDFX->readLong(pTDFX, VIDDESKTOPSTARTADDR), regs->startaddr);
  ErrorF("Input Status 0 = %x\n", pTDFX->readLong(pTDFX, 0xc2));
  ErrorF("Input Status 1 = %x\n", pTDFX->readLong(pTDFX, 0xda));
  ErrorF("2D Status = %x\n", pTDFX->readLong(pTDFX, SST_2D_OFFSET));
  ErrorF("3D Status = %x\n", pTDFX->readLong(pTDFX, SST_3D_OFFSET));
#endif
#endif
}

/*
 * TDFXSave --
 *
 * This function saves the video state.  It reads all of the SVGA registers
 * into the vgaTDFXRec data structure.  There is in general no need to
 * mask out bits here - just read the registers.
 */
static void
DoSave(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, TDFXRegPtr tdfxReg, Bool saveFonts)
{
  TDFXPtr pTDFX;
  vgaHWPtr hwp;
  int i, dummy, count;

  TDFXTRACE("TDFXDoSave start\n");
  pTDFX = TDFXPTR(pScrn);
  hwp = VGAHWPTR(pScrn);

  /*
   * This function will handle creating the data structure and filling
   * in the generic VGA portion.
   */
  if (saveFonts && pTDFX->Primary) {
    /* Enable legacy VGA to access font memory. */
    dummy = pTDFX->readLong(pTDFX, VGAINIT0);
    pTDFX->writeLong(pTDFX, VGAINIT0, dummy & ~SST_VGA0_LEGACY_DECODE);
    vgaHWSave(pScrn, vgaReg, VGA_SR_MODE|VGA_SR_FONTS);
    pTDFX->writeLong(pTDFX, VGAINIT0, dummy);
  } else
    vgaHWSave(pScrn, vgaReg, VGA_SR_MODE);

  tdfxReg->ExtVga[0] = hwp->readCrtc(hwp, 0x1a);
  tdfxReg->ExtVga[1] = hwp->readCrtc(hwp, 0x1b);
  tdfxReg->miscinit1=pTDFX->readLong(pTDFX, MISCINIT1);
  tdfxReg->vidcfg=pTDFX->readLong(pTDFX, VIDPROCCFG);
  tdfxReg->vidpll=pTDFX->readLong(pTDFX, PLLCTRL0);
  tdfxReg->dacmode=pTDFX->readLong(pTDFX, DACMODE);
  tdfxReg->screensize=pTDFX->readLong(pTDFX, VIDSCREENSIZE);
  tdfxReg->stride=pTDFX->readLong(pTDFX, VIDDESKTOPOVERLAYSTRIDE);
  tdfxReg->cursloc=pTDFX->readLong(pTDFX, HWCURPATADDR);
  tdfxReg->startaddr=pTDFX->readLong(pTDFX, VIDDESKTOPSTARTADDR);
  tdfxReg->clip0min=TDFXReadLongMMIO(pTDFX, SST_2D_CLIP0MIN);
  tdfxReg->clip0max=TDFXReadLongMMIO(pTDFX, SST_2D_CLIP0MAX);
  tdfxReg->clip1min=TDFXReadLongMMIO(pTDFX, SST_2D_CLIP1MIN);
  tdfxReg->clip1max=TDFXReadLongMMIO(pTDFX, SST_2D_CLIP1MAX);
  tdfxReg->srcbaseaddr=TDFXReadLongMMIO(pTDFX, SST_2D_SRCBASEADDR);
  tdfxReg->dstbaseaddr=TDFXReadLongMMIO(pTDFX, SST_2D_DSTBASEADDR);
  for (i=0; i<512; i++) {
    count=0;
    do {
      TDFXWriteLongMMIO(pTDFX, DACADDR, i);
      dummy=TDFXReadLongMMIO(pTDFX, DACADDR);
    } while (count++<100 && dummy!=i);
    tdfxReg->dactable[i]=TDFXReadLongMMIO(pTDFX, DACDATA);
  }
  PrintRegisters(pScrn, tdfxReg);
}

static void
TDFXSave(ScrnInfoPtr pScrn)
{
  vgaHWPtr hwp;
  TDFXPtr pTDFX;

  TDFXTRACE("TDFXSave start\n");
  hwp = VGAHWPTR(pScrn);
  pTDFX = TDFXPTR(pScrn);
  /* Make sure VGA functionality is enabled */
  pTDFX->SavedReg.vgainit0=pTDFX->readLong(pTDFX, VGAINIT0);
#if 0
  pTDFX->SavedReg.vgainit1=pTDFX->readLong(pTDFX, VGAINIT1);
#endif
  pTDFX->writeLong(pTDFX, VGAINIT0, pTDFX->ModeReg.vgainit0);
#if 0
  pTDFX->writeLong(pTDFX, VGAINIT1, pTDFX->ModeReg.vgainit1);
#endif
  vgaHWEnable(hwp);

  DoSave(pScrn, &hwp->SavedReg, &pTDFX->SavedReg, TRUE);
}

static void
DoRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, TDFXRegPtr tdfxReg, 
	  Bool restoreFonts) {
  TDFXPtr pTDFX;
  vgaHWPtr hwp;
  int i, dummy, count;

  TDFXTRACE("TDFXDoRestore start\n");
  pTDFX = TDFXPTR(pScrn);
  hwp = VGAHWPTR(pScrn);

  pTDFX->sync(pScrn);

  vgaHWProtect(pScrn, TRUE);

  if (restoreFonts && pTDFX->Primary) {
    /* Enable legacy VGA to access font memory. */
    dummy = pTDFX->readLong(pTDFX, VGAINIT0);
    pTDFX->writeLong(pTDFX, VGAINIT0, dummy & ~SST_VGA0_LEGACY_DECODE);
    vgaHWRestore(pScrn, vgaReg, VGA_SR_FONTS|VGA_SR_MODE);
    pTDFX->writeLong(pTDFX, VGAINIT0, dummy);
  } else
    vgaHWRestore(pScrn, vgaReg, VGA_SR_MODE);

  hwp->writeCrtc(hwp, 0x1a, tdfxReg->ExtVga[0]);
  hwp->writeCrtc(hwp, 0x1b, tdfxReg->ExtVga[1]);
  pTDFX->writeLong(pTDFX, PLLCTRL0, tdfxReg->vidpll);
  pTDFX->writeLong(pTDFX, DACMODE, tdfxReg->dacmode);
  pTDFX->writeLong(pTDFX, VIDDESKTOPOVERLAYSTRIDE, tdfxReg->stride);
  pTDFX->writeLong(pTDFX, HWCURPATADDR, tdfxReg->cursloc);
  pTDFX->writeLong(pTDFX, VIDSCREENSIZE, tdfxReg->screensize);
  pTDFX->writeLong(pTDFX, VIDDESKTOPSTARTADDR, tdfxReg->startaddr);
  TDFXWriteLongMMIO(pTDFX, SST_2D_CLIP0MIN, tdfxReg->clip0min);
  TDFXWriteLongMMIO(pTDFX, SST_2D_CLIP0MAX, tdfxReg->clip0max);
  TDFXWriteLongMMIO(pTDFX, SST_2D_CLIP1MIN, tdfxReg->clip1min);
  TDFXWriteLongMMIO(pTDFX, SST_2D_CLIP1MAX, tdfxReg->clip1max);
#if X_BYTE_ORDER == X_BIG_ENDIAN
  pTDFX->writeLong(pTDFX, MISCINIT0, tdfxReg->miscinit0);
#endif
  pTDFX->writeLong(pTDFX, VIDPROCCFG, tdfxReg->vidcfg);
  TDFXWriteLongMMIO(pTDFX, SST_2D_SRCBASEADDR, tdfxReg->srcbaseaddr);
  TDFXWriteLongMMIO(pTDFX, SST_2D_DSTBASEADDR, tdfxReg->dstbaseaddr);
  for (i=0; i<512; i++) {
    count=0;
    do {
      TDFXWriteLongMMIO(pTDFX, DACADDR, i);
      dummy=TDFXReadLongMMIO(pTDFX, DACADDR);
    } while (count++<100 && dummy!=i);
    count=0;
    do {
      TDFXWriteLongMMIO(pTDFX, DACDATA, tdfxReg->dactable[i]);
      dummy=TDFXReadLongMMIO(pTDFX, DACDATA);
    } while (count++<100 && dummy!=tdfxReg->dactable[i]);
  }
  pTDFX->writeLong(pTDFX, VGAINIT0, tdfxReg->vgainit0);
#if 0
  pTDFX->writeLong(pTDFX, VGAINIT1, tdfxReg->vgainit1);
  pTDFX->writeLong(pTDFX, MISCINIT1, tdfxReg->miscinit1);
#endif
  vgaHWProtect(pScrn, FALSE);

  pTDFX->sync(pScrn);
  PrintRegisters(pScrn, tdfxReg);
}

static void
TDFXRestore(ScrnInfoPtr pScrn) {
  vgaHWPtr hwp;
  TDFXPtr pTDFX;

  TDFXTRACE("TDFXRestore start\n");
  hwp = VGAHWPTR(pScrn);
  pTDFX = TDFXPTR(pScrn);

  DoRestore(pScrn, &hwp->SavedReg, &pTDFX->SavedReg, TRUE);
}

#define REFFREQ 14318.18

static int
CalcPLL(int freq, int *f_out, int isBanshee) {
  int m, n, k, best_m, best_n, best_k, f_cur, best_error;
  int minm, maxm;

  TDFXTRACE("CalcPLL start\n");
  best_error=freq;
  best_n=best_m=best_k=0;
  if (isBanshee) {
    minm=24;
    maxm=24;
  } else {
    minm=1;
    maxm=57; /* This used to be 64, alas it seems the last 8 (funny that ?)
              * values cause jittering at lower resolutions. I've not done
              * any calculations to what the adjustment affects clock ranges,
              * but I can still run at 1600x1200@75Hz */
  }
  for (n=1; n<256; n++) {
    f_cur=REFFREQ*(n+2);
    if (f_cur<freq) {
      f_cur=f_cur/3;
      if (freq-f_cur<best_error) {
	best_error=freq-f_cur;
	best_n=n;
	best_m=1;
	best_k=0;
	continue;
      }
    }
    for (m=minm; m<maxm; m++) {
      for (k=0; k<4; k++) {
	f_cur=REFFREQ*(n+2)/(m+2)/(1<<k);
	if (abs(f_cur-freq)<best_error) {
	  best_error=abs(f_cur-freq);
	  best_n=n;
	  best_m=m;
	  best_k=k;
	}
      }
    }
  }
  n=best_n;
  m=best_m;
  k=best_k;
  *f_out=REFFREQ*(n+2)/(m+2)/(1<<k);
  return (n<<8)|(m<<2)|k;
}

static Bool
SetupVidPLL(ScrnInfoPtr pScrn, DisplayModePtr mode) {
  TDFXPtr pTDFX;
  TDFXRegPtr tdfxReg;
  int freq, f_out;

  TDFXTRACE("SetupVidPLL start\n");
  pTDFX = TDFXPTR(pScrn);
  tdfxReg = &pTDFX->ModeReg;
  freq=mode->Clock;
  tdfxReg->dacmode&=~SST_DAC_MODE_2X;
  tdfxReg->vidcfg&=~SST_VIDEO_2X_MODE_EN;
  if (freq>TDFX2XCUTOFF) {
    if (freq>pTDFX->MaxClock) {
      ErrorF("Overclocked PLLs\n");
      freq=pTDFX->MaxClock;
    }
    tdfxReg->dacmode|=SST_DAC_MODE_2X;
    tdfxReg->vidcfg|=SST_VIDEO_2X_MODE_EN;
  }
  tdfxReg->vidpll=CalcPLL(freq, &f_out, 0);
  TDFXTRACEREG("Vid PLL freq=%d f_out=%d reg=%x\n", freq, f_out, 
     tdfxReg->vidpll);
  return TRUE;
}

#if 0
static Bool
SetupMemPLL(int freq) {
  TDFXPtr pTDFX;
  vgaTDFXPtr tdfxReg;
  int f_out;

  TDFXTRACE("SetupMemPLL start\n");
  pTDFX=TDFXPTR();
  tdfxReg=(vgaTDFXPtr)vgaNewVideoState;
  tdfxReg->mempll=CalcPLL(freq, &f_out);
  pTDFX->writeLong(pTDFX, PLLCTRL1, tdfxReg->mempll);
  TDFXTRACEREG("Mem PLL freq=%d f_out=%d reg=%x\n", freq, f_out, 
     tdfxReg->mempll);
  return TRUE;
}

static Bool
SetupGfxPLL(int freq) {
  TDFXPtr pTDFX;
  vgaTDFXPtr tdfxReg;
  int f_out;

  TDFXTRACE("SetupGfxPLL start\n");
  pTDFX=TDFXPTR();
  tdfxReg=(vgaTDFXPtr)vgaNewVideoState;
  if (pTDFX->chipType==PCI_CHIP_BANSHEE)
    tdfxReg->gfxpll=CalcPLL(freq, &f_out, 1);
  else
    tdfxReg->gfxpll=CalcPLL(freq, &f_out, 0);
  pTDFX->writeLong(pTDFX, PLLCTRL2, tdfxReg->gfxpll);
  TDFXTRACEREG("Gfx PLL freq=%d f_out=%d reg=%x\n", freq, f_out, 
     tdfxReg->gfxpll);
  return TRUE;
}
#endif

#if 0
static Bool
TDFXInitWithBIOSData(ScrnInfoPtr pScrn)
{
  TDFXPtr pTDFX;
  unsigned char *bios = NULL;
  int offset1, offset2;
  int i;
  CARD32 uint[9];

  pTDFX=TDFXPTR(pScrn);

  if (pTDFX->initDone)
    return TRUE;

  if (pTDFX->Primary) {
    uint[0] = pTDFX->readLong(pTDFX, PCIINIT0);
    uint[1] = pTDFX->readLong(pTDFX, MISCINIT0);
    uint[2] = pTDFX->readLong(pTDFX, MISCINIT1);
    uint[3] = pTDFX->readLong(pTDFX, DRAMINIT0);
    uint[4] = pTDFX->readLong(pTDFX, DRAMINIT1);
    uint[5] = pTDFX->readLong(pTDFX, AGPINIT);
    uint[6] = pTDFX->readLong(pTDFX, PLLCTRL1);
    uint[7] = pTDFX->readLong(pTDFX, PLLCTRL2);
    for (i = 0; i < 8; i++) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Primary UINT32[%d] is 0x%08lx\n", i, uint[i]);
    }
    return TRUE;
  }

#define T_B_SIZE (64 * 1024)
  bios = calloc(T_B_SIZE, 1);
  if (!bios)
    return FALSE;

#ifdef XSERVER_LIBPCIACCESS
    pci_device_read_rom(pTDFX->PciInfo[0], bios);
#else
  if (!xf86ReadPciBIOS(0, pTDFX->PciTag[0], 1, bios, T_B_SIZE)) {
#if 0
    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Bad BIOS read.\n");
    free(bios);
    return FALSE;
#endif
  }
#endif

  if (bios[0] != 0x55 || bios[1] != 0xAA) {
    xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Bad BIOS signature.\n");
    free(bios);
    return FALSE;
  }

  offset1 = bios[0x50] | (bios[0x51] << 8);
  xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Offset 1 is (0x%04x)\n", offset1);
  offset2 = bios[offset1] | (bios[offset1 + 1] << 8);
  xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Offset 2 is (0x%04x)\n", offset2);

  for (i = 0; i < 9; i++) {
    int o;

    o = offset2 + i * 4;
    uint[i] = bios[o] | (bios[o+1] << 8) | (bios[o+2] << 16) | (bios[o+3] << 24);
    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "UINT32[%d] is 0x%08lx\n", i, uint[i]);
  }

#if 1
  uint[0] = 0x0584fb04;
  uint[1] = 0x00000000;
  uint[2] = 0x03000001;
  uint[3] = 0x0c17a9e9;
  uint[4] = 0x00202031;
  uint[5] = 0x8000049e;
  uint[6] = 0x00003a05;
  uint[7] = 0x00000c00;
#endif

  pTDFX->writeLong(pTDFX, PCIINIT0, uint[0]);
  pTDFX->writeLong(pTDFX, MISCINIT0, uint[1]);
  pTDFX->writeLong(pTDFX, MISCINIT1, uint[2]);
  pTDFX->writeLong(pTDFX, DRAMINIT0, uint[3]);
  pTDFX->writeLong(pTDFX, DRAMINIT1, uint[4]);
  pTDFX->writeLong(pTDFX, AGPINIT, uint[5]);
  pTDFX->writeLong(pTDFX, PLLCTRL1, uint[6]);
  pTDFX->writeLong(pTDFX, PLLCTRL2, uint[7]);
#if 0
  pTDFX->writeLong(pTDFX, DRAMCOMMAND, uint[8]);
#endif

  /* reset */
  pTDFX->writeLong(pTDFX, MISCINIT0, 0xF3);
  pTDFX->writeLong(pTDFX, MISCINIT0, uint[1]);

  free(bios);
  return TRUE;
}
#endif


static Bool
TDFXInitVGA(ScrnInfoPtr pScrn)
{
  TDFXPtr pTDFX;
  TDFXRegPtr tdfxReg;

  TDFXTRACE("TDFXInitVGA start\n");
  pTDFX=TDFXPTR(pScrn);
  if (pTDFX->initDone) return TRUE;
  pTDFX->initDone=TRUE;

  tdfxReg = &pTDFX->ModeReg;
  tdfxReg->vgainit0 = 0;
  tdfxReg->vgainit0 |= SST_VGA0_EXTENSIONS;
  tdfxReg->vgainit0 |= SST_WAKEUP_3C3 << SST_VGA0_WAKEUP_SELECT_SHIFT;
#if USE_PCIVGAIO
  tdfxReg->vgainit0 |= SST_VGA0_LEGACY_DECODE;
#endif
  tdfxReg->vgainit0 |= SST_ENABLE_ALT_READBACK << SST_VGA0_CONFIG_READBACK_SHIFT;
  tdfxReg->vgainit0 |= SST_CLUT_SELECT_8BIT << SST_VGA0_CLUT_SELECT_SHIFT;

  tdfxReg->vgainit0 |= BIT(12);
#if 0
  tdfxReg->vgainit1 |= 0;
  tdfxReg->miscinit1 = 0;
#endif

  tdfxReg->vidcfg = SST_VIDEO_PROCESSOR_EN | SST_CURSOR_X11 | SST_DESKTOP_EN |
    (pTDFX->cpp-1)<<SST_DESKTOP_PIXEL_FORMAT_SHIFT;

  /* tdfxReg->vidcfg |= SST_DESKTOP_CLUT_BYPASS; */
   
  tdfxReg->stride = pTDFX->stride;

  tdfxReg->clip0min = tdfxReg->clip1min = 0;
  tdfxReg->clip0max = tdfxReg->clip1max = pTDFX->maxClip;

  return TRUE;
}  

static Bool
TDFXSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode) {
  TDFXPtr pTDFX;
  TDFXRegPtr tdfxReg;
  vgaRegPtr pVga;
  int hbs, hbe, vbs, vbe, hse;
  int hd, hss, ht, vt, vd;

  TDFXTRACE("TDFXSetMode start\n");

  pTDFX = TDFXPTR(pScrn);
  tdfxReg = &pTDFX->ModeReg;
  pVga = &VGAHWPTR(pScrn)->ModeReg;

  /* Tell the board we're using a programmable clock */
  pVga->MiscOutReg |= 0xC;

  /* Calculate the CRTC values */
  hd = (mode->CrtcHDisplay>>3)-1;
  hss = (mode->CrtcHSyncStart>>3);
  hse = (mode->CrtcHSyncEnd>>3);
  ht = (mode->CrtcHTotal>>3)-5;
  hbs = (mode->CrtcHBlankStart>>3)-1;
  hbe = (mode->CrtcHBlankEnd>>3)-1;

  vd = mode->CrtcVDisplay-1;
  vt = mode->CrtcVTotal-2;
  vbs = mode->CrtcVBlankStart-1;
  vbe = mode->CrtcVBlankEnd-1;

  /* Undo the "KGA fixes" */
  pVga->CRTC[3] = (hbe&0x1F)|0x80;
  pVga->CRTC[5] = (((hbe)&0x20)<<2) | (hse&0x1F);
  pVga->CRTC[22] =vbe&0xFF;

  /* Handle the higher resolution modes */
  tdfxReg->ExtVga[0] = (ht&0x100)>>8 | (hd&0x100)>>6 | (hbs&0x100)>>4 |
    (hbe&0x40)>>1 | (hss&0x100)>>2 | (hse&0x20)<<2; 

  tdfxReg->ExtVga[1] = (vt&0x400)>>10 | (vd&0x400)>>8 | (vbs&0x400)>>6 |
    (vbe&0x400)>>4;

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

  /* Set the screen size */
  if (mode->Flags&V_DBLSCAN) {
    pVga->CRTC[9] |= 0x80;
    tdfxReg->screensize=mode->HDisplay|(mode->VDisplay<<13);
    tdfxReg->vidcfg |= SST_HALF_MODE;
  } else {
    tdfxReg->screensize=mode->HDisplay|(mode->VDisplay<<12);
    tdfxReg->vidcfg &= ~SST_HALF_MODE;
  }
  if (mode->Flags&V_INTERLACE) {
    tdfxReg->vidcfg|=SST_INTERLACE;
  } else
    tdfxReg->vidcfg&=~SST_INTERLACE;

  TDFXTRACEREG("cpp=%d Hdisplay=%d Vdisplay=%d stride=%d screensize=%x\n", 
	     pTDFX->cpp, mode->HDisplay, mode->VDisplay, tdfxReg->stride, 
	     tdfxReg->screensize);

  return TRUE;
}

static Bool
TDFXModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
  vgaHWPtr hwp;
  TDFXPtr pTDFX;
  int hd, hbs, hss, hse, hbe, ht, hskew;
  Bool dbl;

  hd = hbs = hss = hse = hbe = ht = hskew = 0;
  hwp = VGAHWPTR(pScrn);
  pTDFX = TDFXPTR(pScrn);

  TDFXTRACE("TDFXModeInit start\n");
  dbl=FALSE;
  /* Check for 2x mode and halve all the timing values */
  if (mode->Clock>TDFX2XCUTOFF) {
    hd=mode->CrtcHDisplay;
    hbs=mode->CrtcHBlankStart;
    hss=mode->CrtcHSyncStart;
    hse=mode->CrtcHSyncEnd;
    hbe=mode->CrtcHBlankEnd;
    ht=mode->CrtcHTotal;
    hskew=mode->CrtcHSkew;
    mode->CrtcHDisplay=hd>>1;
    mode->CrtcHBlankStart=hbs>>1;
    mode->CrtcHSyncStart=hss>>1;
    mode->CrtcHSyncEnd=hse>>1;
    mode->CrtcHBlankEnd=hbe>>1;
    mode->CrtcHTotal=ht>>1;
    mode->CrtcHSkew=hskew>>1;
    dbl=TRUE;
  }

  vgaHWUnlock(hwp);

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

  pScrn->vtSema = TRUE;

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

  if (dbl) {
    mode->CrtcHDisplay=hd;
    mode->CrtcHBlankStart=hbs;
    mode->CrtcHSyncStart=hss;
    mode->CrtcHSyncEnd=hse;
    mode->CrtcHBlankEnd=hbe;
    mode->CrtcHTotal=ht;
    mode->CrtcHSkew=hskew;
  }    

#ifdef TDFXDRI
  if (pTDFX->directRenderingEnabled) {
    DRILock(screenInfo.screens[pScrn->scrnIndex], 0);
    TDFXSwapContextFifo(screenInfo.screens[pScrn->scrnIndex]);
  }
#endif
  DoRestore(pScrn, &hwp->ModeReg, &pTDFX->ModeReg, FALSE);
#ifdef TDFXDRI
  if (pTDFX->directRenderingEnabled) {
    DRIUnlock(screenInfo.screens[pScrn->scrnIndex]);
  }
#endif

  return TRUE;
}

static void
TDFXLoadPalette16(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors,
		  VisualPtr pVisual) {
  TDFXPtr pTDFX;
  int i, j, index, v, repeat, max;

  TDFXTRACE("TDFXLoadPalette16 start\n");
  pTDFX = TDFXPTR(pScrn);

  for (i=0; i<numColors; i++) {
    index=indices[i];
    v=(colors[index/2].red<<16)|(colors[index].green<<8)|colors[index/2].blue;
    max=min((index+1)<<2, 256);
    for (j = index<<2; j < max; j++)
    {
      repeat=100;
      do {
	TDFXWriteLongMMIO(pTDFX, DACADDR, j);
      } while (--repeat && TDFXReadLongMMIO(pTDFX, DACADDR)!=j);
      if (!repeat) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Failed to set dac index, bypassing CLUT\n");
	pTDFX->ModeReg.vidcfg |= SST_DESKTOP_CLUT_BYPASS;
	return;
      }

      repeat=100;
      do {
	TDFXWriteLongMMIO(pTDFX, DACDATA, v);
      } while (--repeat && TDFXReadLongMMIO(pTDFX, DACDATA)!=v);
      if (!repeat) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Failed to set dac value, bypassing CLUT\n");
	pTDFX->ModeReg.vidcfg |= SST_DESKTOP_CLUT_BYPASS;
	return;
      }
    }
  }
}

static void
TDFXLoadPalette24(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors,
		  VisualPtr pVisual) {
  TDFXPtr pTDFX;
  int i, index, v, repeat;

  TDFXTRACE("TDFXLoadPalette24 start\n");
  pTDFX = TDFXPTR(pScrn);
  for (i=0; i<numColors; i++) {
    index=indices[i];
    v=(colors[index].red<<16)|(colors[index].green<<8)|colors[index].blue;
    repeat=100;
    do {
      TDFXWriteLongMMIO(pTDFX, DACADDR, index);
    } while (--repeat && TDFXReadLongMMIO(pTDFX, DACADDR)!=index);
    if (!repeat) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Failed to set dac index, "
		 "bypassing CLUT\n");
      pTDFX->ModeReg.vidcfg |= SST_DESKTOP_CLUT_BYPASS;
      return;
    }
    repeat=100;
    do {
      TDFXWriteLongMMIO(pTDFX, DACDATA, v);
    } while (--repeat && TDFXReadLongMMIO(pTDFX, DACDATA)!=v);
    if (!repeat) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Failed to set dac value, "
		 "bypassing CLUT\n");
      pTDFX->ModeReg.vidcfg |= SST_DESKTOP_CLUT_BYPASS;
      return;
    }
  }
}

#define TILE_WIDTH 128
#define TILE_HEIGHT 32

static int
calcBufferStride(int xres, Bool tiled, int cpp)
{
  int strideInTiles;

  if (tiled == TRUE) {
    /* Calculate tile width stuff */
    strideInTiles = (xres+TILE_WIDTH-1)/TILE_WIDTH;
    
    return strideInTiles*cpp*TILE_WIDTH;
  } else {
    return xres*cpp;
  }
} /* calcBufferStride */

static int
calcBufferHeightInTiles(int yres)
{
  int heightInTiles;            /* Height of buffer in tiles */


  /* Calculate tile height stuff */
  heightInTiles = yres >> 5;
  
  if (yres & (TILE_HEIGHT - 1))
    heightInTiles++;

  return heightInTiles;

} /* calcBufferHeightInTiles */

#if 0
static int
calcBufferSizeInTiles(int xres, int yres, int cpp) {
  int bufSizeInTiles;           /* Size of buffer in tiles */

  bufSizeInTiles =
    calcBufferHeightInTiles(yres) * (calcBufferStride(xres, TRUE, cpp) >> 7);

  return bufSizeInTiles;

} /* calcBufferSizeInTiles */
#endif

static int
calcBufferSize(int xres, int yres, Bool tiled, int cpp)
{
  int stride, height, bufSize;

  if (tiled) {
    stride = calcBufferStride(xres, tiled, cpp);
    height = TILE_HEIGHT * calcBufferHeightInTiles(yres);
  } else {
    stride = xres*cpp;
    height = yres;
  }

  bufSize = stride * height;
  
  return bufSize;

} /* calcBufferSize */

static void allocateMemory(ScrnInfoPtr pScrn) {
  TDFXPtr pTDFX;
  int memRemaining, fifoSize, screenSizeInTiles, cursorSize;
  int fbSize;
  int verb;
  char *str;

  pTDFX = TDFXPTR(pScrn);

  if (pTDFX->cpp!=3) {
    screenSizeInTiles=calcBufferSize(pScrn->virtualX, pScrn->virtualY,
				     TRUE, pTDFX->cpp);
  }
  else {
    /* cpp==3 needs to bump up to 4 */
    screenSizeInTiles=calcBufferSize(pScrn->virtualX, pScrn->virtualY,
				     TRUE, 4);
  }

  /*
   * Layout is:
   *    cursor, fifo, fb, tex, bb, db
   */

  fbSize = (pScrn->virtualY + pTDFX->pixmapCacheLinesMin) * pTDFX->stride;

  memRemaining=((pScrn->videoRam<<10) - 1) &~ 0xFFF;
  /* Note that a page is 4096 bytes, and a  */
  /* tile is 32 x 128 = 4096 bytes.  So,    */
  /* page and tile boundaries are the same  */
  /* Place the depth offset first, forcing  */
  /* it to be on an *odd* page boundary.    */
  pTDFX->depthOffset = (memRemaining - screenSizeInTiles) &~ 0xFFF;
  if ((pTDFX->depthOffset & (0x1 << 12)) == 0) {
#if	1
    if (pTDFX->depthOffset > 0) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                 "Changing depth offset from 0x%08x to 0x%08x\n",
                 pTDFX->depthOffset,
                 pTDFX->depthOffset - (0x1 << 12));
    }
#endif
      pTDFX->depthOffset -= (0x1 << 12);
  }
  /* Now, place the back buffer, forcing it */
  /* to be on an *even* page boundary.      */
  pTDFX->backOffset = (pTDFX->depthOffset - screenSizeInTiles) &~ 0xFFF;
  if (pTDFX->backOffset & (0x1 << 12)) {
#if	1
    if (pTDFX->backOffset > 0) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                 "Changing back offset from 0x%08x to 0x%08x\n",
                 pTDFX->backOffset,
                 pTDFX->backOffset - (0x1 << 12));
    }
#endif
      pTDFX->backOffset -= (0x1 << 12);
  }
  /* Give the cmd fifo at least             */
  /* CMDFIFO_PAGES pages, but no more than  */
  /* 64. NOTE: Don't go higher than 64, as  */
  /* there is suspect code in Glide3 !      */
  fifoSize = ((64 <= CMDFIFO_PAGES) ? 64 : CMDFIFO_PAGES) << 12;

  /* We give 4096 bytes to the cursor  */
  cursorSize = 4096;
  pTDFX->cursorOffset = 0;

  pTDFX->fifoOffset = pTDFX->cursorOffset + cursorSize;
  pTDFX->fifoSize = fifoSize;
  /* Now, place the front buffer, forcing   */
  /* it to be on a page boundary too, just  */
  /* for giggles.                           */
  pTDFX->fbOffset = pTDFX->fifoOffset + pTDFX->fifoSize;
  pTDFX->texOffset = pTDFX->fbOffset + fbSize;
  if (pTDFX->depthOffset <= pTDFX->texOffset ||
	pTDFX->backOffset <= pTDFX->texOffset) {
    /*
     * pTDFX->texSize < 0 means that the DRI is disabled.  pTDFX->backOffset
     * is used to calculate the maximum amount of memory available for
     * 2D offscreen use.  With DRI disabled, set this to the top of memory.
     */

    pTDFX->texSize = -1;
    pTDFX->backOffset = pScrn->videoRam * 1024;
    pTDFX->depthOffset = -1;
    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	"Not enough video memory available for textures and depth buffer\n"
	"\tand/or back buffer.  Disabling DRI.  To use DRI try lower\n"
	"\tresolution modes and/or a smaller virtual screen size\n");
  } else {
    pTDFX->texSize = pTDFX->backOffset - pTDFX->texOffset;
    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Textures Memory %0.02f MB\n",
		(float)pTDFX->texSize/1024.0/1024.0);
  }

/* This could be set to 2 or 3 */
#define OFFSET_VERB 1
  xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, OFFSET_VERB,
             "Cursor Offset: [0x%08X,0x%08X)\n",
             pTDFX->cursorOffset,
             pTDFX->cursorOffset + cursorSize);
  xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, OFFSET_VERB,
             "Fifo Offset: [0x%08X, 0x%08X)\n",
             pTDFX->fifoOffset,
             pTDFX->fifoOffset + pTDFX->fifoSize);
  xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, OFFSET_VERB,
             "Front Buffer Offset: [0x%08X, 0x%08X)\n",
             pTDFX->fbOffset,
             pTDFX->fbOffset +
		(pScrn->virtualY+pTDFX->pixmapCacheLinesMin)*pTDFX->stride);
  if (pTDFX->texSize > 0) {
    verb = OFFSET_VERB;
    str = "";
  } else {
    verb = 3;
    str = "(NOT USED) ";
  }
  xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, verb,
             "%sTexture Offset: [0x%08X, 0x%08X)\n", str,
             pTDFX->texOffset,
             pTDFX->texOffset + pTDFX->texSize);
  xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, verb,
             "%sBackOffset: [0x%08X, 0x%08X)\n", str,
             pTDFX->backOffset,
             pTDFX->backOffset + screenSizeInTiles);
  xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, verb,
             "%sDepthOffset: [0x%08X, 0x%08X)\n", str,
             pTDFX->depthOffset,
             pTDFX->depthOffset + screenSizeInTiles);
}

static Bool
TDFXScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) {
  ScrnInfoPtr pScrn;
  vgaHWPtr hwp;
  TDFXPtr pTDFX;
  VisualPtr visual;
  BoxRec MemBox;
#ifdef TDFXDRI
  MessageType driFrom = X_DEFAULT;
#endif
  int scanlines;

  TDFXTRACE("TDFXScreenInit start\n");
  pScrn = xf86Screens[pScreen->myNum];
  pTDFX = TDFXPTR(pScrn);
  hwp = VGAHWPTR(pScrn);

  if (!TDFXMapMem(pScrn)) return FALSE;
  pScrn->memPhysBase = (int)pTDFX->LinearAddr[0];

  if (!pTDFX->usePIO) TDFXSetMMIOAccess(pTDFX);

#if USE_PCIVGAIO
  hwp->PIOOffset = pTDFX->PIOBase[0] - 0x300;
#endif
  vgaHWGetIOBase(hwp);
  /* Map VGA memory only for primary cards (to save/restore textmode data). */
  if (pTDFX->Primary) {
    if (!vgaHWMapMem(pScrn))
      return FALSE;
  }

  pTDFX->stride = pScrn->displayWidth*pTDFX->cpp;

  /* enough to do DVD */
  pTDFX->pixmapCacheLinesMin = ((720*480*pTDFX->cpp) + 
					pTDFX->stride - 1)/pTDFX->stride;

  if (pTDFX->ChipType > PCI_CHIP_VOODOO3) {
  	if ((pTDFX->pixmapCacheLinesMin + pScrn->virtualY) > 4095)
		pTDFX->pixmapCacheLinesMin = 4095 - pScrn->virtualY;
  } else {
  	if ((pTDFX->pixmapCacheLinesMin + pScrn->virtualY) > 2047)
		pTDFX->pixmapCacheLinesMin = 2047 - pScrn->virtualY;
  }

  allocateMemory(pScrn);

  pScrn->fbOffset = pTDFX->fbOffset;

#if 0
  if (pTDFX->numChips>1) {
    if (xf86ReturnOptValBool(pTDFX->Options, OPTION_NO_SLI, FALSE)) {
      TDFXSetupSLI(pScrn, FALSE, 0);
    } else {
      TDFXSetupSLI(pScrn, TRUE, 0);
    }
  }
#endif

  TDFXSetLFBConfig(pTDFX);

  /* We initialize in the state that our FIFO is up to date */
  pTDFX->syncDone=TRUE;
  if (!TDFXInitFifo(pScreen)) {
    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to initialize private\n");
    return FALSE;
  }

  scanlines = (pTDFX->backOffset - pTDFX->fbOffset) / pTDFX->stride;
  if(pTDFX->ChipType < PCI_CHIP_VOODOO5) {
      if (scanlines > 2047) 
	scanlines = 2047;
  } else {
      /* MaxClip seems to have only 12 bits => 0->4095 */
      if (scanlines > 4095) 
	scanlines = 4095;
  }

  pTDFX->pixmapCacheLinesMax = scanlines - pScrn->virtualY;

  /*
   * Note, pTDFX->pixmapCacheLinesMax may be smaller than
   * pTDFX->pixmapCacheLinesMin when pTDFX->texSize < 0.  DRI is disabled
   * in that case, so pTDFX->pixmapCacheLinesMin isn't used when that's true.
   */
  xf86DrvMsg(pScrn->scrnIndex, X_INFO, 
    "Minimum %d, Maximum %d lines of offscreen memory available\n",
	pTDFX->pixmapCacheLinesMin, pTDFX->pixmapCacheLinesMax);
     
  MemBox.y1 = 0;
  MemBox.x1 = 0;
  MemBox.x2 = pScrn->displayWidth;
  MemBox.y2 = scanlines;

  pTDFX->maxClip = MemBox.x2 | (MemBox.y2 << 16);

#if 0
  TDFXInitWithBIOSData(pScrn);
#endif
  TDFXInitVGA(pScrn);
  TDFXSave(pScrn);
  if (!TDFXModeInit(pScrn, pScrn->currentMode)) return FALSE;

  TDFXSetLFBConfig(pTDFX);

  miClearVisualTypes();

  if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth),
			pScrn->rgbBits, pScrn->defaultVisual))
    return FALSE;

  miSetPixmapDepths ();
    
#ifdef TDFXDRI
  /*
   * Setup DRI after visuals have been established, but before fbScreenInit
   * is called.   fbScreenInit will eventually call into the drivers
   * InitGLXVisuals call back.
   */
  if (!xf86ReturnOptValBool(pTDFX->Options, OPTION_DRI, TRUE) || pTDFX->NoAccel) {
      pTDFX->directRenderingEnabled = FALSE;
      driFrom = X_CONFIG;
  } else if (pTDFX->texSize < 0) {
      pTDFX->directRenderingEnabled = FALSE;
      driFrom = X_PROBED;
  } else {
      pTDFX->directRenderingEnabled = TDFXDRIScreenInit(pScreen);
  }
#endif

  switch (pScrn->bitsPerPixel) {
  case 8:
  case 16:
  case 24:
  case 32:
    if (!fbScreenInit(pScreen, pTDFX->FbBase+pTDFX->fbOffset, 
		       pScrn->virtualX, pScrn->virtualY,
		       pScrn->xDpi, pScrn->yDpi,
		       pScrn->displayWidth, pScrn->bitsPerPixel))
      return FALSE;
    break;
  default:
    xf86DrvMsg(scrnIndex, X_ERROR,
	       "Internal error: invalid bpp (%d) in TDFXScrnInit\n",
	       pScrn->bitsPerPixel);
    return FALSE;
  }

  if (pScrn->bitsPerPixel>8) {
    visual = pScreen->visuals + pScreen->numVisuals;
    while (--visual >= pScreen->visuals) {
      if ((visual->class | DynamicClass) == DirectColor) {
	visual->offsetRed = pScrn->offset.red;
	visual->offsetGreen = pScrn->offset.green;
	visual->offsetBlue = pScrn->offset.blue;
	visual->redMask = pScrn->mask.red;
	visual->greenMask = pScrn->mask.green;
	visual->blueMask = pScrn->mask.blue;
      }
    }
  }

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

  xf86SetBlackWhitePixels(pScreen);

  TDFXDGAInit(pScreen);

  xf86InitFBManager(pScreen, &MemBox);

  if (!pTDFX->NoAccel) {
    if (!TDFXAccelInit(pScreen)) {
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		 "Hardware acceleration initialization failed\n");
    }
  }

  miInitializeBackingStore(pScreen);
  xf86SetBackingStore(pScreen);
  xf86SetSilkenMouse(pScreen);

  miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

  if (!xf86ReturnOptValBool(pTDFX->Options, OPTION_SW_CURSOR, FALSE)) {
    if (!TDFXCursorInit(pScreen)) {
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		 "Hardware cursor initialization failed\n");
    }
  }

  if (!miCreateDefColormap(pScreen)) return FALSE;

  if (pScrn->bitsPerPixel==16) {
    if (!xf86HandleColormaps(pScreen, 256, 8, TDFXLoadPalette16, 0,
			     CMAP_PALETTED_TRUECOLOR|CMAP_RELOAD_ON_MODE_SWITCH))
      return FALSE;
  } else {
    if (!xf86HandleColormaps(pScreen, 256, 8, TDFXLoadPalette24, 0,
			     CMAP_PALETTED_TRUECOLOR|CMAP_RELOAD_ON_MODE_SWITCH))
      return FALSE;
  }

  TDFXAdjustFrame(scrnIndex, 0, 0, 0);

  xf86DPMSInit(pScreen, TDFXDisplayPowerManagementSet, 0);

  /* Initialize Xv support */
  TDFXInitVideo (pScreen);

  pScreen->SaveScreen = TDFXSaveScreen;
  pTDFX->CloseScreen = pScreen->CloseScreen;
  pScreen->CloseScreen = TDFXCloseScreen;

  pTDFX->BlockHandler = pScreen->BlockHandler;
  pScreen->BlockHandler = TDFXBlockHandler;

  /*
   * DRICloseScreen isn't called thru a wrapper but explicitely
   * in of TDFXCloseScreen() before the rest is unwrapped
   */
  
#ifdef TDFXDRI
  if (pTDFX->directRenderingEnabled) {
	/* Now that mi, fb, drm and others have done their thing, 
         * complete the DRI setup.
         */
	pTDFX->directRenderingEnabled = TDFXDRIFinishScreenInit(pScreen);
  }
  if (pTDFX->directRenderingEnabled) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Direct rendering enabled\n");
  } else {
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Direct rendering disabled\n");
  }
#endif

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

  return TRUE;
}

Bool
TDFXSwitchMode(int scrnIndex, DisplayModePtr mode, int flags) {
  ScrnInfoPtr pScrn;

  TDFXTRACE("TDFXSwitchMode start\n");
  pScrn=xf86Screens[scrnIndex];
  return TDFXModeInit(pScrn, mode);
}

void
TDFXAdjustFrame(int scrnIndex, int x, int y, int flags) {
  ScrnInfoPtr pScrn;
  TDFXPtr pTDFX;
  TDFXRegPtr tdfxReg;

  TDFXTRACE("TDFXAdjustFrame start\n");
  pScrn = xf86Screens[scrnIndex];
  pTDFX = TDFXPTR(pScrn);

  if (pTDFX->ShowCache && y && pScrn->vtSema)
    y += pScrn->virtualY - 1;

  tdfxReg = &pTDFX->ModeReg;
  if(pTDFX->ShowCache && y && pScrn->vtSema) 
     y += pScrn->virtualY - 1;
  tdfxReg->startaddr = pTDFX->fbOffset+y*pTDFX->stride+(x*pTDFX->cpp);
  TDFXTRACE("TDFXAdjustFrame to x=%d y=%d offset=%d\n", x, y, tdfxReg->startaddr);
  pTDFX->writeLong(pTDFX, VIDDESKTOPSTARTADDR, tdfxReg->startaddr);
}

static Bool
TDFXEnterVT(int scrnIndex, int flags) {
  ScrnInfoPtr pScrn;
  ScreenPtr pScreen;
#ifdef TDFXDRI
  TDFXPtr pTDFX;
#endif

  TDFXTRACE("TDFXEnterVT start\n");
  pScrn = xf86Screens[scrnIndex];
  pScreen = screenInfo.screens[scrnIndex];
  TDFXInitFifo(pScreen);
#ifdef TDFXDRI
  pTDFX = TDFXPTR(pScrn);
  if (pTDFX->directRenderingEnabled) {
    DRIUnlock(pScreen);
  }
#endif
  if (!TDFXModeInit(pScrn, pScrn->currentMode)) return FALSE;
  TDFXAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
  return TRUE;
}

static void
TDFXLeaveVT(int scrnIndex, int flags) {
  ScrnInfoPtr pScrn;
  vgaHWPtr hwp;
  ScreenPtr pScreen;
  TDFXPtr pTDFX;

  TDFXTRACE("TDFXLeaveVT start\n");
  pScrn = xf86Screens[scrnIndex];
  hwp=VGAHWPTR(pScrn);
  TDFXRestore(pScrn);
  vgaHWLock(hwp);
  pScreen = screenInfo.screens[scrnIndex];
  pTDFX = TDFXPTR(pScrn);
  pTDFX->sync(pScrn);
  TDFXShutdownFifo(pScreen);
#ifdef TDFXDRI
  if (pTDFX->directRenderingEnabled) {
    DRILock(pScreen, 0);
  }
#endif
}

static Bool
TDFXCloseScreen(int scrnIndex, ScreenPtr pScreen)
{
  ScrnInfoPtr pScrn;
  vgaHWPtr hwp;
  TDFXPtr pTDFX;

  TDFXTRACE("TDFXCloseScreen start\n");
  pScrn = xf86Screens[scrnIndex];
  hwp = VGAHWPTR(pScrn);
  pTDFX = TDFXPTR(pScrn);

#ifdef TDFXDRI
    if (pTDFX->directRenderingEnabled) {
	TDFXDRICloseScreen(pScreen);
	pTDFX->directRenderingEnabled=FALSE;
    }
#endif

  TDFXShutdownFifo(pScreen);

  if (pScrn->vtSema) {
      TDFXRestore(pScrn);
      vgaHWLock(hwp);
      TDFXUnmapMem(pScrn);
      vgaHWUnmapMem(pScrn);
  }
  
  if (pTDFX->AccelInfoRec) XAADestroyInfoRec(pTDFX->AccelInfoRec);
  pTDFX->AccelInfoRec=0;
  if (pTDFX->DGAModes) free(pTDFX->DGAModes);
  pTDFX->DGAModes=0;
  if (pTDFX->scanlineColorExpandBuffers[0])
    free(pTDFX->scanlineColorExpandBuffers[0]);
  pTDFX->scanlineColorExpandBuffers[0]=0;
  if (pTDFX->scanlineColorExpandBuffers[1])
    free(pTDFX->scanlineColorExpandBuffers[1]);
  pTDFX->scanlineColorExpandBuffers[1]=0;
  if (pTDFX->overlayAdaptor)
    free(pTDFX->overlayAdaptor);
  pTDFX->overlayAdaptor=0;
  if (pTDFX->textureAdaptor)
    free(pTDFX->textureAdaptor);
  pTDFX->textureAdaptor=0;

  pScrn->vtSema=FALSE;

  pScreen->BlockHandler = pTDFX->BlockHandler;
  pScreen->CloseScreen = pTDFX->CloseScreen;
  return (*pScreen->CloseScreen)(scrnIndex, pScreen);
}

static void
TDFXFreeScreen(int scrnIndex, int flags) {
  TDFXTRACE("TDFXFreeScreen start\n");
  TDFXFreeRec(xf86Screens[scrnIndex]);
  if (xf86LoaderCheckSymbol("vgaHWFreeHWRec"))
    vgaHWFreeHWRec(xf86Screens[scrnIndex]);
}

static ModeStatus
TDFXValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags) {
  ScrnInfoPtr pScrn;
  TDFXPtr pTDFX;

  TDFXTRACE("TDFXValidMode start\n");
  if ((mode->HDisplay>2048) || (mode->VDisplay>1536)) 
    return MODE_BAD;
  /* Banshee doesn't support interlace, but Voodoo 3 and higher do. */
  pScrn = xf86Screens[scrnIndex];
  pTDFX = TDFXPTR(pScrn);
  if (mode->Flags&V_INTERLACE) {
    switch (pTDFX->ChipType) {
      case PCI_CHIP_BANSHEE:
        return MODE_BAD;
        break;
      case PCI_CHIP_VOODOO3:
      case PCI_CHIP_VOODOO5:
        return MODE_OK;
        break;
      default:
        return MODE_BAD;
        break;
    }
  }
  /* In clock doubled mode widths must be divisible by 16 instead of 8 */
  if ((mode->Clock>TDFX2XCUTOFF) && (mode->HDisplay%16))
    return MODE_BAD;
  return MODE_OK;
}

/* replacement of vgaHWBlankScreen(pScrn, unblank) which doesn't unblank
 * the screen if it is already unblanked. */
static void
TDFXBlankScreen(ScrnInfoPtr pScrn, Bool unblank)
{
  vgaHWPtr hwp = VGAHWPTR(pScrn);
  unsigned char scrn;

  TDFXTRACE("TDFXBlankScreen start\n");

  scrn = hwp->readSeq(hwp, 0x01);

  if (unblank) {
    if((scrn & 0x20) == 0) return;
    scrn &= ~0x20;                    /* enable screen */
  } else {
    scrn |= 0x20;                     /* blank screen */
  }

  vgaHWSeqReset(hwp, TRUE);
  hwp->writeSeq(hwp, 0x01, scrn);     /* change mode */
  vgaHWSeqReset(hwp, FALSE);
}

static Bool
TDFXSaveScreen(ScreenPtr pScreen, int mode)
{
  ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
  Bool unblank;

  TDFXTRACE("TDFXSaveScreen start\n");

  unblank = xf86IsUnblank(mode);

  if (unblank)
    SetTimeSinceLastInputEvent();

  if (pScrn->vtSema) {
    TDFXBlankScreen(pScrn, unblank);
  }
  return TRUE;
}                                                                             

static void
TDFXBlockHandler(int i, pointer blockData, pointer pTimeout, pointer pReadmask)
{
    ScreenPtr   pScreen = screenInfo.screens[i];
    ScrnInfoPtr pScrn   = xf86Screens[i];
    TDFXPtr     pTDFX   = TDFXPTR(pScrn);

    pScreen->BlockHandler = pTDFX->BlockHandler;
    (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask);
    pScreen->BlockHandler = TDFXBlockHandler;

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

static void
TDFXDisplayPowerManagementSet(ScrnInfoPtr pScrn, int PowerManagementMode, 
			      int flags) {
  TDFXPtr pTDFX;
  int dacmode, state=0;

  TDFXTRACE("TDFXDPMS start\n");
  pTDFX = TDFXPTR(pScrn);
  dacmode=pTDFX->readLong(pTDFX, DACMODE);
  switch (PowerManagementMode) {
  case DPMSModeOn:
    /* Screen: On; HSync: On, VSync: On */
    state=0;
    break;
  case DPMSModeStandby:
    /* Screen: Off; HSync: Off, VSync: On */
    state=BIT(3);
    break;
  case DPMSModeSuspend:
    /* Screen: Off; HSync: On, VSync: Off */
    state=BIT(1);
    break;
  case DPMSModeOff:
    /* Screen: Off; HSync: Off, VSync: Off */
    state=BIT(1)|BIT(3);
    break;
  }
  dacmode&=~(BIT(1)|BIT(3));
  dacmode|=state;
  pTDFX->writeLong(pTDFX, DACMODE, dacmode);
}
