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

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "xorg-server.h"
#include "xf86.h"
#include "xf86_OSproc.h"

#include "xf86Pci.h"

#include "windowstr.h"
#include "shadow.h"
#include "shadowfb.h"

#include "i810.h"
#include "i810_dri.h"

static char I810KernelDriverName[] = "i810";
static char I810ClientDriverName[] = "i810";

static Bool I810CreateContext(ScreenPtr pScreen, VisualPtr visual,
			      drm_context_t hwContext, void *pVisualConfigPriv,
			      DRIContextType contextStore);
static void I810DestroyContext(ScreenPtr pScreen, drm_context_t hwContext,
			       DRIContextType contextStore);
static void I810DRISwapContext(ScreenPtr pScreen, DRISyncType syncType,
			       DRIContextType readContextType,
			       void *readContextStore,
			       DRIContextType writeContextType,
			       void *writeContextStore);
static void I810DRIInitBuffers(WindowPtr pWin, RegionPtr prgn, CARD32 index);
static void I810DRIMoveBuffers(WindowPtr pParent, DDXPointRec ptOldOrg,
			       RegionPtr prgnSrc, CARD32 index);


static void I810EnablePageFlip(ScreenPtr pScreen);
static void I810DisablePageFlip(ScreenPtr pScreen);
static void I810DRITransitionSingleToMulti3d(ScreenPtr pScreen);
static void I810DRITransitionMultiToSingle3d(ScreenPtr pScreen);
static void I810DRITransitionTo3d(ScreenPtr pScreen);
static void I810DRITransitionTo2d(ScreenPtr pScreen);

static void I810DRIRefreshArea(ScrnInfoPtr pScrn, int num, BoxPtr pbox);

static int i810_pitches[] = {
   512,
   1024,
   2048,
   4096,
   0
};

static int i810_pitch_flags[] = {
   0x0,
   0x1,
   0x2,
   0x3,
   0
};

static unsigned int i810_drm_version = 0;

Bool
I810CleanupDma(ScrnInfoPtr pScrn)
{
   I810Ptr pI810 = I810PTR(pScrn);
   drmI810Init info;

   memset(&info, 0, sizeof(drmI810Init));
   info.func = I810_CLEANUP_DMA;

   if (drmCommandWrite(pI810->drmSubFD, DRM_I810_INIT,
		       &info, sizeof(drmI810Init))) {
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		 "[dri] I810 Dma Cleanup Failed\n");
      return FALSE;
   }

   return TRUE;
}

Bool
I810InitDma(ScrnInfoPtr pScrn)
{
   I810Ptr pI810 = I810PTR(pScrn);
   I810RingBuffer *ring = pI810->LpRing;
   I810DRIPtr pI810DRI = (I810DRIPtr) pI810->pDRIInfo->devPrivate;
   drmI810Init info;

   memset(&info, 0, sizeof(drmI810Init));

   info.ring_start = ring->mem.Start;
   info.ring_end = ring->mem.End;
   info.ring_size = ring->mem.Size;
   info.mmio_offset = (unsigned int)pI810DRI->regs;
   info.buffers_offset = (unsigned int)pI810->buffer_map;
   info.sarea_priv_offset = sizeof(XF86DRISAREARec);

   info.front_offset = 0;
   info.back_offset = pI810->BackBuffer.Start;
   info.depth_offset = pI810->DepthBuffer.Start;
   info.overlay_offset = pI810->OverlayStart;
   info.overlay_physical = pI810->OverlayPhysical;
   info.w = pScrn->virtualX;
   info.h = pScrn->virtualY;
   info.pitch = pI810->auxPitch;
   info.pitch_bits = pI810->auxPitchBits;

   /* We require DRM v1.2 or greater. Since DRM v1.2 broke compatibility
    * we created a new v1.4 that supports a new init function. Eventually the
    * old init function will go away. If you change the drm interface, make a
    * new init type too so that we can detect the new client.
    */
   switch(i810_drm_version) {
   case ((1<<16) | 0):
   case ((1<<16) | 1):
   case ((1<<16) | 2):
   case ((1<<16) | 3):
      /* Use OLD drm < 1.4 init */
      info.func = I810_INIT_DMA;
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "[drm] Init PRE v1.4 interface.\n");
      break;
   default:
   case ((1<<16) | 4):
      /*  DRM version 1.3 or greater init */
      info.func = I810_INIT_DMA_1_4;
      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "[drm] Init v1.4 interface.\n");
      break;
   }

   if (drmCommandWrite(pI810->drmSubFD, DRM_I810_INIT,
		       &info, sizeof(drmI810Init))) {
      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		 "[drm] I810 Dma Initialization failed.\n");
      return FALSE;
   }

   return TRUE;
}

static unsigned int
mylog2(unsigned int n)
{
   unsigned int log2 = 1;

   while (n > 1)
      n >>= 1, log2++;
   return log2;
}

Bool
I810DRIScreenInit(ScreenPtr pScreen)
{
   ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
   I810Ptr pI810 = I810PTR(pScrn);
   DRIInfoPtr pDRIInfo;
   I810DRIPtr pI810DRI;
   unsigned long tom;
   drm_handle_t agpHandle;
   drm_handle_t dcacheHandle;
   int sysmem_size = 0;
   int back_size = 0;
   unsigned int pitch_idx = 0;
   int bufs;
   int width = pScrn->displayWidth * pI810->cpp;
   int i;

   /* Hardware 3D rendering only implemented for 16bpp */
   /* And it only works for 5:6:5 (Mark) */
   if (pScrn->depth != 16)
      return FALSE;

   /* Check that the DRI, and DRM modules have been loaded by testing
    * for known symbols in each module. */
   if (!xf86LoaderCheckSymbol("drmAvailable"))
      return FALSE;
   if (!xf86LoaderCheckSymbol("DRIQueryVersion")) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[dri] I810DRIScreenInit failed (libdri.a too old)\n");
      return FALSE;
   }

   /* adjust width first */
#define Elements(x) sizeof(x)/sizeof(*x)
   for (pitch_idx = 0; pitch_idx < Elements(i810_pitches); pitch_idx++)
      if (width <= i810_pitches[pitch_idx])
	 break;

   if (pitch_idx == Elements(i810_pitches)) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[dri] Couldn't find depth/back buffer pitch");
      DRICloseScreen(pScreen);
      return FALSE;
   } else {
      /* for tiled memory to work, the buffer needs to have the
       * number of lines as a multiple of 16 (the tile size),
       *  - airlied */
      int lines = (pScrn->virtualY + 15) / 16 * 16;
      back_size = i810_pitches[pitch_idx] * lines;
      back_size = ((back_size + 4096 - 1) / 4096) * 4096;
   }

   pScrn->displayWidth = i810_pitches[pitch_idx] / pI810->cpp;

   /* Check the DRI version */
   {
      int major, minor, patch;

      DRIQueryVersion(&major, &minor, &patch);
      if (major != DRIINFO_MAJOR_VERSION || minor < DRIINFO_MINOR_VERSION) {
	 xf86DrvMsg(pScreen->myNum, X_ERROR,
		    "[dri] I810DRIScreenInit failed because of a version mismatch.\n"
		    "[dri] libdri version is %d.%d.%d bug version %d.%d.x is needed.\n"
		    "[dri] Disabling DRI.\n", major, minor, patch,
                    DRIINFO_MAJOR_VERSION, DRIINFO_MINOR_VERSION);
	 return FALSE;
      }
   }

   pDRIInfo = DRICreateInfoRec();
   if (!pDRIInfo) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[dri] DRICreateInfoRec failed.  Disabling DRI.\n");
      return FALSE;
   }

/*     pDRIInfo->wrap.ValidateTree = 0;    */
/*     pDRIInfo->wrap.PostValidateTree = 0;    */

   pI810->pDRIInfo = pDRIInfo;
   pI810->LockHeld = 0;

   pDRIInfo->drmDriverName = I810KernelDriverName;
   pDRIInfo->clientDriverName = I810ClientDriverName;
   if (xf86LoaderCheckSymbol("DRICreatePCIBusID")) {
      pDRIInfo->busIdString = DRICreatePCIBusID(pI810->PciInfo);
   } else {
      pDRIInfo->busIdString = malloc(64);
      if (pDRIInfo->busIdString)
	 sprintf(pDRIInfo->busIdString, "PCI:%d:%d:%d",
		 ((pI810->PciInfo->domain << 8) | pI810->PciInfo->bus),
		 pI810->PciInfo->dev, pI810->PciInfo->func
		);
   }
   if (!pDRIInfo->busIdString) {
      DRIDestroyInfoRec(pI810->pDRIInfo);
      pI810->pDRIInfo = NULL;
      return FALSE;
   }
   pDRIInfo->ddxDriverMajorVersion = I810_MAJOR_VERSION;
   pDRIInfo->ddxDriverMinorVersion = I810_MINOR_VERSION;
   pDRIInfo->ddxDriverPatchVersion = I810_PATCHLEVEL;
   pDRIInfo->frameBufferPhysicalAddress = (pointer) pI810->LinearAddr;
   pDRIInfo->frameBufferSize = (((pScrn->displayWidth *
				  pScrn->virtualY * pI810->cpp) +
				 4096 - 1) / 4096) * 4096;

   pDRIInfo->frameBufferStride = pScrn->displayWidth * pI810->cpp;
   pDRIInfo->ddxDrawableTableEntry = I810_MAX_DRAWABLES;

   if (SAREA_MAX_DRAWABLES < I810_MAX_DRAWABLES)
      pDRIInfo->maxDrawableTableEntry = SAREA_MAX_DRAWABLES;
   else
      pDRIInfo->maxDrawableTableEntry = I810_MAX_DRAWABLES;

   /* For now the mapping works by using a fixed size defined
    * in the SAREA header
    */
   if (sizeof(XF86DRISAREARec) + sizeof(I810SAREARec) > SAREA_MAX) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[dri] Data does not fit in SAREA\n");
      return FALSE;
   }
   pDRIInfo->SAREASize = SAREA_MAX;

   if (!(pI810DRI = (I810DRIPtr) calloc(sizeof(I810DRIRec), 1))) {
      DRIDestroyInfoRec(pI810->pDRIInfo);
      pI810->pDRIInfo = NULL;
      return FALSE;
   }
   pDRIInfo->devPrivate = pI810DRI;
   pDRIInfo->devPrivateSize = sizeof(I810DRIRec);
   pDRIInfo->contextSize = sizeof(I810DRIContextRec);

   pDRIInfo->CreateContext = I810CreateContext;
   pDRIInfo->DestroyContext = I810DestroyContext;
   pDRIInfo->SwapContext = I810DRISwapContext;
   pDRIInfo->InitBuffers = I810DRIInitBuffers;
   pDRIInfo->MoveBuffers = I810DRIMoveBuffers;
   pDRIInfo->bufferRequests = DRI_ALL_WINDOWS;
   pDRIInfo->TransitionTo2d = I810DRITransitionTo2d;
   pDRIInfo->TransitionTo3d = I810DRITransitionTo3d;
   pDRIInfo->TransitionSingleToMulti3D = I810DRITransitionSingleToMulti3d;
   pDRIInfo->TransitionMultiToSingle3D = I810DRITransitionMultiToSingle3d;

   pDRIInfo->createDummyCtx = TRUE;
   pDRIInfo->createDummyCtxPriv = FALSE;

   /* This adds the framebuffer as a drm map *before* we have asked agp
    * to allocate it.  Scary stuff, hold on...
    */
   if (!DRIScreenInit(pScreen, pDRIInfo, &pI810->drmSubFD)) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[dri] DRIScreenInit failed.  Disabling DRI.\n");
      free(pDRIInfo->devPrivate);
      pDRIInfo->devPrivate = NULL;
      DRIDestroyInfoRec(pI810->pDRIInfo);
      pI810->pDRIInfo = NULL;
      return FALSE;
   }

   /* Check the i810 DRM versioning */
   {
      drmVersionPtr version;

      /* Check the DRM lib version.
       * drmGetLibVersion was not supported in version 1.0, so check for
       * symbol first to avoid possible crash or hang.
       */
      if (xf86LoaderCheckSymbol("drmGetLibVersion")) {
	 version = drmGetLibVersion(pI810->drmSubFD);
      } else
      {
	 /* drmlib version 1.0.0 didn't have the drmGetLibVersion
	  * entry point.  Fake it by allocating a version record
	  * via drmGetVersion and changing it to version 1.0.0
	  */
	 version = drmGetVersion(pI810->drmSubFD);
	 version->version_major = 1;
	 version->version_minor = 0;
	 version->version_patchlevel = 0;
      }

#define REQ_MAJ 1
#define REQ_MIN 1
      if (version) {
	 if (version->version_major != REQ_MAJ ||
	     version->version_minor < REQ_MIN) {
	    /* incompatible drm library version */
	    xf86DrvMsg(pScreen->myNum, X_ERROR,
		       "[dri] I810DRIScreenInit failed because of a version mismatch.\n"
		       "[dri] libdrm.a module version is %d.%d.%d but version %d.%d.x is needed.\n"
		       "[dri] Disabling DRI.\n",
		       version->version_major,
		       version->version_minor, version->version_patchlevel,
		       REQ_MAJ, REQ_MIN);
	    drmFreeVersion(version);
	    I810DRICloseScreen(pScreen);
	    return FALSE;
	 }
	 drmFreeVersion(version);
      }

      /* Check the i810 DRM version */
      version = drmGetVersion(pI810->drmSubFD);
      if (version) {
	i810_drm_version = (version->version_major<<16) |
	                    version->version_minor;
	 if (version->version_major != 1 || version->version_minor < 2) {
	    /* incompatible drm version */
	    xf86DrvMsg(pScreen->myNum, X_ERROR,
		       "[dri] I810DRIScreenInit failed because of a version mismatch.\n"
		       "[dri] i810.o kernel module version is %d.%d.%d but version 1.2.0 or greater is needed.\n"
		       "[dri] Disabling DRI.\n",
		       version->version_major,
		       version->version_minor, version->version_patchlevel);
	    I810DRICloseScreen(pScreen);
	    drmFreeVersion(version);
	    return FALSE;
	 }
         pI810->drmMinor = version->version_minor;
	 drmFreeVersion(version);
      }
   }

   pI810DRI->regsSize = I810_REG_SIZE;
   if (drmAddMap(pI810->drmSubFD, (drm_handle_t) pI810->MMIOAddr,
		 pI810DRI->regsSize, DRM_REGISTERS, 0,
		 (drmAddress) &pI810DRI->regs) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR, "[drm] drmAddMap(regs) failed\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }
   xf86DrvMsg(pScreen->myNum, X_INFO, "[drm] Registers = 0x%08x\n",
	      (int)pI810DRI->regs);

   pI810->backHandle = DRM_AGP_NO_HANDLE;
   pI810->zHandle = DRM_AGP_NO_HANDLE;
   pI810->cursorHandle = DRM_AGP_NO_HANDLE;
   pI810->xvmcHandle = DRM_AGP_NO_HANDLE;
   pI810->sysmemHandle = DRM_AGP_NO_HANDLE;
   pI810->agpAcquired = FALSE;
   pI810->dcacheHandle = DRM_AGP_NO_HANDLE;

   /* Agp Support - Need this just to get the framebuffer.
    */
   if (drmAgpAcquire(pI810->drmSubFD) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR, "[agp] drmAgpAquire failed\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }
   pI810->agpAcquired = TRUE;

   if (drmAgpEnable(pI810->drmSubFD, 0) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR, "[agp] drmAgpEnable failed\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   memset(&pI810->DcacheMem, 0, sizeof(I810MemRange));
   memset(&pI810->BackBuffer, 0, sizeof(I810MemRange));
   memset(&pI810->DepthBuffer, 0, sizeof(I810MemRange));
   pI810->CursorPhysical = 0;
   pI810->CursorARGBPhysical = 0;

   /* Dcache - half the speed of normal ram, but has use as a Z buffer
    * under the DRI.
    */

   drmAgpAlloc(pI810->drmSubFD, 4096 * 1024, 1, NULL,
	       (drmAddress) &dcacheHandle);
   pI810->dcacheHandle = dcacheHandle;

   xf86DrvMsg(pScreen->myNum, X_INFO, "[agp] dcacheHandle : 0x%x\n",
	      (int)dcacheHandle);

   sysmem_size = pScrn->videoRam * 1024;
   if (dcacheHandle != DRM_AGP_NO_HANDLE) {
      if (back_size > 4 * 1024 * 1024) {
	 xf86DrvMsg(pScreen->myNum, X_INFO,
		    "[dri] Backsize is larger then 4 meg\n");
	 sysmem_size = sysmem_size - 2 * back_size;
	 drmAgpFree(pI810->drmSubFD, dcacheHandle);
	 pI810->dcacheHandle = dcacheHandle = DRM_AGP_NO_HANDLE;
      } else {
	 sysmem_size = sysmem_size - back_size;
      }
   } else {
      sysmem_size = sysmem_size - 2 * back_size;
   }

   /* Max size is 48 without XvMC, 41 with 6 surfaces, 40 with 7 surfaces */
   if (pI810->numSurfaces && (pI810->numSurfaces == 6)) {
      if (sysmem_size > (pI810->FbMapSize - 7 * 1024 * 1024)) {
	 sysmem_size = (pI810->FbMapSize - 7 * 1024 * 1024);
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "User requested more memory then fits in the agp aperture\n"
		    "Truncating to %d bytes of memory\n", sysmem_size);
      }
   }
   if (pI810->numSurfaces && (pI810->numSurfaces == 7)) {
      if (sysmem_size > (pI810->FbMapSize - 8 * 1024 * 1024)) {
	 sysmem_size = (pI810->FbMapSize - 8 * 1024 * 1024);
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "User requested more memory then fits in the agp aperture\n"
		    "Truncating to %d bytes of memory\n", sysmem_size);
      }
   }

   if (sysmem_size > pI810->FbMapSize) {
      sysmem_size = pI810->FbMapSize;

      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[dri] User requested more memory then fits in the agp"
		 " aperture\n\tTruncating to %d bytes of memory\n",
		 sysmem_size);
   }

   sysmem_size -= 4096;			/* remove 4k for the hw cursor */
   sysmem_size -= 16384;		/* remove 16k for the ARGB hw cursor */

   pI810->SysMem.Start = 0;
   pI810->SysMem.Size = sysmem_size;
   pI810->SysMem.End = sysmem_size;
   tom = sysmem_size;

   pI810->SavedSysMem = pI810->SysMem;

   if (dcacheHandle != DRM_AGP_NO_HANDLE) {
      if (drmAgpBind(pI810->drmSubFD, dcacheHandle, pI810->DepthOffset) == 0) {
	 memset(&pI810->DcacheMem, 0, sizeof(I810MemRange));
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] GART: Found 4096K Z buffer memory\n");
	 pI810->DcacheMem.Start = pI810->DepthOffset;
	 pI810->DcacheMem.Size = 1024 * 4096;
	 pI810->DcacheMem.End =
	       pI810->DcacheMem.Start + pI810->DcacheMem.Size;
	 if (!I810AllocLow
	     (&(pI810->DepthBuffer), &(pI810->DcacheMem), back_size)) {
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "[agp] Depth buffer allocation failed\n");
	    DRICloseScreen(pScreen);
	    return FALSE;
	 }
      } else {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] GART: dcache bind failed\n");
	 drmAgpFree(pI810->drmSubFD, dcacheHandle);
	 pI810->dcacheHandle = dcacheHandle = DRM_AGP_NO_HANDLE;
      }
   } else {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[agp] GART: no dcache memory found\n");
   }

   drmAgpAlloc(pI810->drmSubFD, back_size, 0, NULL,
	       (drmAddress) &agpHandle);
   pI810->backHandle = agpHandle;

   if (agpHandle != DRM_AGP_NO_HANDLE) {
      if (drmAgpBind(pI810->drmSubFD, agpHandle, pI810->BackOffset) == 0) {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] Bound backbuffer memory\n");

	 pI810->BackBuffer.Start = pI810->BackOffset;
	 pI810->BackBuffer.Size = back_size;
	 pI810->BackBuffer.End = (pI810->BackBuffer.Start +
				  pI810->BackBuffer.Size);
      } else {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] Unable to bind backbuffer.  Disabling DRI.\n");
	 DRICloseScreen(pScreen);
	 return FALSE;
      }
   } else {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[dri] Unable to allocate backbuffer memory.  Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   if (dcacheHandle == DRM_AGP_NO_HANDLE) {
     drmAgpAlloc(pI810->drmSubFD, back_size, 0, NULL,
		 (drmAddress) &agpHandle);

      pI810->zHandle = agpHandle;

      if (agpHandle != DRM_AGP_NO_HANDLE) {
	 if (drmAgpBind(pI810->drmSubFD, agpHandle, pI810->DepthOffset) == 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "[agp] Bound depthbuffer memory\n");
	    pI810->DepthBuffer.Start = pI810->DepthOffset;
	    pI810->DepthBuffer.Size = back_size;
	    pI810->DepthBuffer.End = (pI810->DepthBuffer.Start +
				      pI810->DepthBuffer.Size);
	 } else {
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "[agp] Unable to bind depthbuffer.  Disabling DRI.\n");
	    DRICloseScreen(pScreen);
	    return FALSE;
	 }
      } else {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] Unable to allocate depthbuffer memory.  Disabling DRI.\n");
	 DRICloseScreen(pScreen);
	 return FALSE;
      }
   }

   /* Now allocate and bind the agp space.  This memory will include the
    * regular framebuffer as well as texture memory.
    */
   drmAgpAlloc(pI810->drmSubFD, sysmem_size, 0, NULL,
	       (drmAddress)&agpHandle);
   pI810->sysmemHandle = agpHandle;

   if (agpHandle != DRM_AGP_NO_HANDLE) {
      if (drmAgpBind(pI810->drmSubFD, agpHandle, 0) == 0) {
	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		   "[agp] Bound System Texture Memory\n");
      } else {
          xf86DrvMsg(pScreen->myNum, X_ERROR, "[agp] Unable to bind system texture memory. Disabling DRI.\n");
	  DRICloseScreen(pScreen);
	  return FALSE;
      }
   } else {
      xf86DrvMsg(pScreen->myNum, X_ERROR, "[agp] Unable to allocate system texture memory. Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

/* Allocate 7 or 8MB for XvMC this is setup as follows to best use tiled
   regions and required surface pitches. (Numbers are adjusted if the
   AGP region is only 32MB
   For numSurfaces == 6
   44 - 48MB = 4MB Fence, 8 Tiles wide
   43 - 44MB = 1MB Fence, 8 Tiles wide
   42 - 43MB = 1MB Fence, 4 Tiles wide
   41 - 42MB = 1MB Fence, 4 Tiles wide
   For numSurfaces == 7
   44 - 48MB   = 4MB Fence, 8 Tiles wide
   43 - 44MB   = 1MB Fence, 8 Tiles wide
   42.5 - 43MB = 0.5MB Fence, 8 Tiles wide
   42 - 42.5MB = 0.5MB Fence, 4 Tiles wide
   40 - 42MB   = 2MB Fence, 4 Tiles wide
 */
   if (pI810->numSurfaces) {
      if (pI810->numSurfaces == 6) {
	 pI810->MC.Size = 7 * 1024 * 1024;
	 pI810->MC.Start = pI810->FbMapSize - 7 * 1024 * 1024;

      }
      if (pI810->numSurfaces == 7) {
	 pI810->MC.Size = 8 * 1024 * 1024;
	 pI810->MC.Start = pI810->FbMapSize - 8 * 1024 * 1024;
      }
      drmAgpAlloc(pI810->drmSubFD, pI810->MC.Size, 0, NULL,
		  (drmAddress) &agpHandle);

      pI810->xvmcHandle = agpHandle;

      if (agpHandle != DRM_AGP_NO_HANDLE) {
	 if (drmAgpBind(pI810->drmSubFD, agpHandle, pI810->MC.Start) == 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		       "GART: Allocated 7MB for HWMC\n");
	    pI810->MC.End = pI810->MC.Start + pI810->MC.Size;
	 } else {
	    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "GART: HWMC bind failed\n");
	    pI810->MC.Start = 0;
	    pI810->MC.Size = 0;
	    pI810->MC.End = 0;
	 }
      } else {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO, "GART: HWMC alloc failed\n");
	 pI810->MC.Start = 0;
	 pI810->MC.Size = 0;
	 pI810->MC.End = 0;
      }
      pI810->xvmcContext = 0;
   }

   drmAgpAlloc(pI810->drmSubFD, 4096, 2,
	       (unsigned long *)&pI810->CursorPhysical,
	       (drmAddress) &agpHandle);

   pI810->cursorHandle = agpHandle;

   if (agpHandle != DRM_AGP_NO_HANDLE) {
      tom = sysmem_size;

      if (drmAgpBind(pI810->drmSubFD, agpHandle, tom) == 0) {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] GART: Allocated 4K for mouse cursor image\n");
	 pI810->CursorStart = tom;
	 tom += 4096;
      } else {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] GART: cursor bind failed\n");
	 pI810->CursorPhysical = 0;
      }
   } else {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[agp] GART: cursor alloc failed\n");
      pI810->CursorPhysical = 0;
   }

   drmAgpAlloc(pI810->drmSubFD, 16384, 2,
	       (unsigned long *)&pI810->CursorARGBPhysical,
	       (drmAddress) &agpHandle);

   pI810->cursorARGBHandle = agpHandle;

   if (agpHandle != DRM_AGP_NO_HANDLE) {
      if (drmAgpBind(pI810->drmSubFD, agpHandle, tom) == 0) {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] GART: Allocated 16K for ARGB mouse cursor image\n");
	 pI810->CursorARGBStart = tom;
	 tom += 16384;
      } else {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[agp] GART: ARGB cursor bind failed\n");
	 pI810->CursorARGBPhysical = 0;
      }
   } else {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[agp] GART: ARGB cursor alloc failed\n");
      pI810->CursorARGBPhysical = 0;
   }

   /* Steal some of the excess cursor space for the overlay regs.
    */
   pI810->OverlayPhysical = pI810->CursorPhysical + 1024;
   pI810->OverlayStart = pI810->CursorStart + 1024;

   I810SetTiledMemory(pScrn, 1,
		      pI810->DepthBuffer.Start,
		      i810_pitches[pitch_idx], 8 * 1024 * 1024);

   I810SetTiledMemory(pScrn, 2,
		      pI810->BackBuffer.Start,
		      i810_pitches[pitch_idx], 8 * 1024 * 1024);

   /* These are for HWMC surfaces */
   if (pI810->numSurfaces == 6) {
      I810SetTiledMemory(pScrn, 3, pI810->MC.Start, 512, 1024 * 1024);

      I810SetTiledMemory(pScrn, 4,
			 pI810->MC.Start + 1024 * 1024, 512, 1024 * 1024);

      I810SetTiledMemory(pScrn, 5,
			 pI810->MC.Start + 1024 * 1024 * 2,
			 1024, 1024 * 1024);

      I810SetTiledMemory(pScrn, 6,
			 pI810->MC.Start + 1024 * 1024 * 3,
			 1024, 4 * 1024 * 1024);
   }
   if (pI810->numSurfaces == 7) {
      I810SetTiledMemory(pScrn, 3, pI810->MC.Start, 512, 2 * 1024 * 1024);

      I810SetTiledMemory(pScrn, 4,
			 pI810->MC.Start + 2 * 1024 * 1024, 512, 512 * 1024);

      I810SetTiledMemory(pScrn, 5,
			 pI810->MC.Start + 2 * 1024 * 1024 + 512 * 1024,
			 1024, 512 * 1024);

      I810SetTiledMemory(pScrn, 6,
			 pI810->MC.Start + 3 * 1024 * 1024,
			 1024, 1 * 1024 * 1024);

      I810SetTiledMemory(pScrn, 7,
			 pI810->MC.Start + 4 * 1024 * 1024,
			 1024, 4 * 1024 * 1024);

   }

   pI810->auxPitch = i810_pitches[pitch_idx];
   pI810->auxPitchBits = i810_pitch_flags[pitch_idx];
   pI810->SavedDcacheMem = pI810->DcacheMem;
   pI810DRI->backbufferSize = pI810->BackBuffer.Size;

   if (drmAddMap(pI810->drmSubFD, (drm_handle_t) pI810->BackBuffer.Start,
		 pI810->BackBuffer.Size, DRM_AGP, 0,
		 (drmAddress) &pI810DRI->backbuffer) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[drm] drmAddMap(backbuffer) failed.  Disabling DRI\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   pI810DRI->depthbufferSize = pI810->DepthBuffer.Size;
   if (drmAddMap(pI810->drmSubFD, (drm_handle_t) pI810->DepthBuffer.Start,
		 pI810->DepthBuffer.Size, DRM_AGP, 0,
		 (drmAddress) &pI810DRI->depthbuffer) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[drm] drmAddMap(depthbuffer) failed.  Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   /* Allocate FrontBuffer etc. */
   if (!I810AllocateFront(pScrn)) {
      DRICloseScreen(pScreen);
      return FALSE;
   }

   /* Allocate buffer memory */
   I810AllocHigh(&(pI810->BufferMem), &(pI810->SysMem),
		 I810_DMA_BUF_NR * I810_DMA_BUF_SZ);

   xf86DrvMsg(pScreen->myNum, X_INFO, "[dri] Buffer map : %lx\n",
	      pI810->BufferMem.Start);

   if (pI810->BufferMem.Start == 0 ||
       pI810->BufferMem.End - pI810->BufferMem.Start >
       I810_DMA_BUF_NR * I810_DMA_BUF_SZ) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[dri] Not enough memory for dma buffers.  Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }
   if (drmAddMap(pI810->drmSubFD, (drm_handle_t) pI810->BufferMem.Start,
		 pI810->BufferMem.Size, DRM_AGP, 0,
		 (drmAddress) &pI810->buffer_map) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[drm] drmAddMap(buffer_map) failed.  Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   pI810DRI->agp_buffers = pI810->buffer_map;
   pI810DRI->agp_buf_size = pI810->BufferMem.Size;

   if (drmAddMap(pI810->drmSubFD, (drm_handle_t) pI810->LpRing->mem.Start,
		 pI810->LpRing->mem.Size, DRM_AGP, 0,
		 (drmAddress) &pI810->ring_map) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[drm] drmAddMap(ring_map) failed.  Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   /* Use the rest of memory for textures. */
   pI810DRI->textureSize = pI810->SysMem.Size;

   i = mylog2(pI810DRI->textureSize / I810_NR_TEX_REGIONS);

   if (i < I810_LOG_MIN_TEX_REGION_SIZE)
      i = I810_LOG_MIN_TEX_REGION_SIZE;

   pI810DRI->logTextureGranularity = i;
   pI810DRI->textureSize = (pI810DRI->textureSize >> i) << i;	/* truncate */

   if (pI810DRI->textureSize < 512 * 1024) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[drm] Less then 512k memory left for textures.  Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   if (!I810AllocLow(&(pI810->TexMem), &(pI810->SysMem), pI810DRI->textureSize)) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[agp] Texure memory allocation failed\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   if (drmAddMap(pI810->drmSubFD, (drm_handle_t) pI810->TexMem.Start,
		 pI810->TexMem.Size, DRM_AGP, 0,
		 (drmAddress) &pI810DRI->textures) < 0) {
      xf86DrvMsg(pScreen->myNum, X_ERROR,
		 "[drm] drmAddMap(textures) failed.  Disabling DRI.\n");
      DRICloseScreen(pScreen);
      return FALSE;
   }

   if ((bufs = drmAddBufs(pI810->drmSubFD,
			  I810_DMA_BUF_NR,
			  I810_DMA_BUF_SZ,
			  DRM_AGP_BUFFER, pI810->BufferMem.Start)) <= 0) {
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		 "[drm] failure adding %d %d byte DMA buffers.  Disabling DRI.\n",
		 I810_DMA_BUF_NR, I810_DMA_BUF_SZ);
      DRICloseScreen(pScreen);
      return FALSE;
   }

   xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	      "[drm] added %d %d byte DMA buffers\n", bufs, I810_DMA_BUF_SZ);

   I810InitDma(pScrn);

   /* Okay now initialize the dma engine */

   if (!pI810DRI->irq) {
      pI810DRI->irq = drmGetInterruptFromBusID(pI810->drmSubFD,
					       ((pI810->PciInfo->domain << 8) |
						pI810->PciInfo->bus),
					       pI810->PciInfo->dev,
					       pI810->PciInfo->func
					       );
      if ((drmCtlInstHandler(pI810->drmSubFD, pI810DRI->irq)) != 0) {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		    "[drm] failure adding irq handler, there is a device "
		    "already using that irq\n Consider rearranging your "
		    "PCI cards.  Disabling DRI.\n");
	 DRICloseScreen(pScreen);
	 return FALSE;
      }
   }

   xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	      "[drm] dma control initialized, using IRQ %d\n", pI810DRI->irq);

   pI810DRI->deviceID = pI810->PciInfo->device_id;
   pI810DRI->width = pScrn->virtualX;
   pI810DRI->height = pScrn->virtualY;
   pI810DRI->mem = pScrn->videoRam * 1024;
   pI810DRI->cpp = pI810->cpp;

   pI810DRI->fbOffset = pI810->FrontBuffer.Start;
   pI810DRI->fbStride = pI810->auxPitch;

   pI810DRI->bitsPerPixel = pScrn->bitsPerPixel;

   pI810DRI->textureOffset = pI810->TexMem.Start;

   pI810DRI->backOffset = pI810->BackBuffer.Start;
   pI810DRI->depthOffset = pI810->DepthBuffer.Start;

   pI810DRI->ringOffset = pI810->LpRing->mem.Start;
   pI810DRI->ringSize = pI810->LpRing->mem.Size;

   pI810DRI->auxPitch = pI810->auxPitch;
   pI810DRI->auxPitchBits = pI810->auxPitchBits;
   pI810DRI->sarea_priv_offset = sizeof(XF86DRISAREARec);

   xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	      "[dri] visual configs initialized.\n");
   pI810->pDRIInfo->driverSwapMethod = DRI_HIDE_X_CONTEXT;

   return TRUE;
}

void
I810DRICloseScreen(ScreenPtr pScreen)
{
   ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
   I810Ptr pI810 = I810PTR(pScrn);

   if (pI810->pDRIInfo) {
       I810DRIPtr pI810DRI = (I810DRIPtr) pI810->pDRIInfo->devPrivate;

       if (pI810DRI) {
	   if (pI810DRI->irq) {
	       drmCtlUninstHandler(pI810->drmSubFD);
	       pI810DRI->irq = 0;
	   }

	   free(pI810->pDRIInfo->devPrivate);
	   pI810->pDRIInfo->devPrivate = NULL;
       }

       I810CleanupDma(pScrn);

       DRICloseScreen(pScreen);
       DRIDestroyInfoRec(pI810->pDRIInfo);
       pI810->pDRIInfo = NULL;
   }

   if (pI810->dcacheHandle!=DRM_AGP_NO_HANDLE)
       drmAgpFree(pI810->drmSubFD, pI810->dcacheHandle);
   if (pI810->backHandle!=DRM_AGP_NO_HANDLE)
       drmAgpFree(pI810->drmSubFD, pI810->backHandle);
   if (pI810->zHandle!=DRM_AGP_NO_HANDLE)
       drmAgpFree(pI810->drmSubFD, pI810->zHandle);
   if (pI810->cursorHandle!=DRM_AGP_NO_HANDLE)
       drmAgpFree(pI810->drmSubFD, pI810->cursorHandle);
   if (pI810->xvmcHandle!=DRM_AGP_NO_HANDLE)
       drmAgpFree(pI810->drmSubFD, pI810->xvmcHandle);
   if (pI810->sysmemHandle!=DRM_AGP_NO_HANDLE)
       drmAgpFree(pI810->drmSubFD, pI810->sysmemHandle);

   if (pI810->agpAcquired == TRUE)
       drmAgpRelease(pI810->drmSubFD);

   pI810->backHandle = DRM_AGP_NO_HANDLE;
   pI810->zHandle = DRM_AGP_NO_HANDLE;
   pI810->cursorHandle = DRM_AGP_NO_HANDLE;
   pI810->xvmcHandle = DRM_AGP_NO_HANDLE;
   pI810->sysmemHandle = DRM_AGP_NO_HANDLE;
   pI810->agpAcquired = FALSE;
   pI810->dcacheHandle = DRM_AGP_NO_HANDLE;
}

static Bool
I810CreateContext(ScreenPtr pScreen, VisualPtr visual,
		  drm_context_t hwContext, void *pVisualConfigPriv,
		  DRIContextType contextStore)
{
   return TRUE;
}

static void
I810DestroyContext(ScreenPtr pScreen, drm_context_t hwContext,
		   DRIContextType contextStore)
{
}

Bool
I810DRIFinishScreenInit(ScreenPtr pScreen)
{
   I810SAREARec *sPriv = (I810SAREARec *) DRIGetSAREAPrivate(pScreen);
   ScrnInfoPtr        pScrn = xf86ScreenToScrn(pScreen);
   I810Ptr info  = I810PTR(pScrn);

   memset(sPriv, 0, sizeof(*sPriv));

   /* Have shadow run only while there is 3d active.
    */
   if (info->allowPageFlip && info->drmMinor >= 3) {
     ShadowFBInit( pScreen, I810DRIRefreshArea );
   }
   else
     info->allowPageFlip = 0;
   return DRIFinishScreenInit(pScreen);
}

void
I810DRISwapContext(ScreenPtr pScreen, DRISyncType syncType,
		   DRIContextType oldContextType, void *oldContext,
		   DRIContextType newContextType, void *newContext)
{
   ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
   I810Ptr pI810 = I810PTR(pScrn);

   if (syncType == DRI_3D_SYNC &&
       oldContextType == DRI_2D_CONTEXT && newContextType == DRI_2D_CONTEXT) {
      if (I810_DEBUG & DEBUG_VERBOSE_DRI)
	 ErrorF("I810DRISwapContext (in)\n");

      if (!pScrn->vtSema)
	  return;
      pI810->LockHeld = 1;
      I810RefreshRing(pScrn);
   } else if (syncType == DRI_2D_SYNC &&
	      oldContextType == DRI_NO_CONTEXT &&
	      newContextType == DRI_2D_CONTEXT) {
      pI810->LockHeld = 0;
      if (I810_DEBUG & DEBUG_VERBOSE_DRI)
	 ErrorF("I810DRISwapContext (out)\n");
   } else if (I810_DEBUG & DEBUG_VERBOSE_DRI)
      ErrorF("I810DRISwapContext (other)\n");
}

static void
I810DRISetNeedSync(ScrnInfoPtr pScrn)
{
#ifdef HAVE_XAA_H
   I810Ptr pI810 = I810PTR(pScrn);
   if (pI810->AccelInfoRec)
	pI810->AccelInfoRec->NeedToSync = TRUE;
#endif
}

static void
I810DRIInitBuffers(WindowPtr pWin, RegionPtr prgn, CARD32 index)
{
   ScreenPtr pScreen = pWin->drawable.pScreen;
   ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
   BoxPtr pbox = REGION_RECTS(prgn);
   int nbox = REGION_NUM_RECTS(prgn);

   if (I810_DEBUG & DEBUG_VERBOSE_DRI)
      ErrorF("I810DRIInitBuffers\n");

   I810SetupForSolidFill(pScrn, 0, GXcopy, -1);
   while (nbox--) {
      I810SelectBuffer(pScrn, I810_SELECT_BACK);
      I810SubsequentSolidFillRect(pScrn, pbox->x1, pbox->y1,
				  pbox->x2 - pbox->x1, pbox->y2 - pbox->y1);
      pbox++;
   }

   /* Clear the depth buffer - uses 0xffff rather than 0.
    */
   pbox = REGION_RECTS(prgn);
   nbox = REGION_NUM_RECTS(prgn);
   I810SelectBuffer(pScrn, I810_SELECT_DEPTH);
   I810SetupForSolidFill(pScrn, 0xffff, GXcopy, -1);
   while (nbox--) {
      I810SubsequentSolidFillRect(pScrn, pbox->x1, pbox->y1,
				  pbox->x2 - pbox->x1, pbox->y2 - pbox->y1);
      pbox++;
   }
   I810SelectBuffer(pScrn, I810_SELECT_FRONT);

   I810DRISetNeedSync(pScrn);
}

/* This routine is a modified form of XAADoBitBlt with the calls to
 * ScreenToScreenBitBlt built in. My routine has the prgnSrc as source
 * instead of destination. My origin is upside down so the ydir cases
 * are reversed.
 *
 * KW: can you believe that this is called even when a 2d window moves?
 */
static void
I810DRIMoveBuffers(WindowPtr pParent, DDXPointRec ptOldOrg,
		   RegionPtr prgnSrc, CARD32 index)
{
   ScreenPtr pScreen = pParent->drawable.pScreen;
   ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
   BoxPtr pboxTmp, pboxNext, pboxBase;
   DDXPointPtr pptTmp, pptNew2 = NULL;
   int xdir, ydir;

   int screenwidth = pScrn->virtualX;
   int screenheight = pScrn->virtualY;

   BoxPtr pbox = REGION_RECTS(prgnSrc);
   int nbox = REGION_NUM_RECTS(prgnSrc);

   BoxPtr pboxNew1 = NULL;
   BoxPtr pboxNew2 = NULL;
   DDXPointPtr pptNew1 = NULL;
   DDXPointPtr pptSrc = &ptOldOrg;

   int dx = pParent->drawable.x - ptOldOrg.x;
   int dy = pParent->drawable.y - ptOldOrg.y;

   /* If the copy will overlap in Y, reverse the order */
   if (dy > 0) {
      ydir = -1;

      if (nbox > 1) {
	 /* Keep ordering in each band, reverse order of bands */
	 pboxNew1 = (BoxPtr) malloc(sizeof(BoxRec) * nbox);
	 if (!pboxNew1)
	    return;
	 pptNew1 = (DDXPointPtr) malloc(sizeof(DDXPointRec) * nbox);
	 if (!pptNew1) {
	    free(pboxNew1);
	    return;
	 }
	 pboxBase = pboxNext = pbox + nbox - 1;
	 while (pboxBase >= pbox) {
	    while ((pboxNext >= pbox) && (pboxBase->y1 == pboxNext->y1))
	       pboxNext--;
	    pboxTmp = pboxNext + 1;
	    pptTmp = pptSrc + (pboxTmp - pbox);
	    while (pboxTmp <= pboxBase) {
	       *pboxNew1++ = *pboxTmp++;
	       *pptNew1++ = *pptTmp++;
	    }
	    pboxBase = pboxNext;
	 }
	 pboxNew1 -= nbox;
	 pbox = pboxNew1;
	 pptNew1 -= nbox;
	 pptSrc = pptNew1;
      }
   } else {
      /* No changes required */
      ydir = 1;
   }

   /* If the regions will overlap in X, reverse the order */
   if (dx > 0) {
      xdir = -1;

      if (nbox > 1) {
	 /*reverse orderof rects in each band */
	 pboxNew2 = (BoxPtr) malloc(sizeof(BoxRec) * nbox);
	 pptNew2 = (DDXPointPtr) malloc(sizeof(DDXPointRec) * nbox);
	 if (!pboxNew2 || !pptNew2) {
	    if (pptNew2)
	       free(pptNew2);
	    if (pboxNew2)
	       free(pboxNew2);
	    if (pboxNew1) {
	       free(pptNew1);
	       free(pboxNew1);
	    }
	    return;
	 }
	 pboxBase = pboxNext = pbox;
	 while (pboxBase < pbox + nbox) {
	    while ((pboxNext < pbox + nbox) && (pboxNext->y1 == pboxBase->y1))
	       pboxNext++;
	    pboxTmp = pboxNext;
	    pptTmp = pptSrc + (pboxTmp - pbox);
	    while (pboxTmp != pboxBase) {
	       *pboxNew2++ = *--pboxTmp;
	       *pptNew2++ = *--pptTmp;
	    }
	    pboxBase = pboxNext;
	 }
	 pboxNew2 -= nbox;
	 pbox = pboxNew2;
	 pptNew2 -= nbox;
	 pptSrc = pptNew2;
      }
   } else {
      /* No changes are needed */
      xdir = 1;
   }

   /* SelectBuffer isn't really a good concept for the i810.
    */
   I810EmitFlush(pScrn);
   I810SetupForScreenToScreenCopy(pScrn, xdir, ydir, GXcopy, -1, -1);
   for (; nbox--; pbox++) {

      int x1 = pbox->x1;
      int y1 = pbox->y1;
      int destx = x1 + dx;
      int desty = y1 + dy;
      int w = pbox->x2 - x1 + 1;
      int h = pbox->y2 - y1 + 1;

      if (destx < 0)
	 x1 -= destx, w += destx, destx = 0;
      if (desty < 0)
	 y1 -= desty, h += desty, desty = 0;
      if (destx + w > screenwidth)
	 w = screenwidth - destx;
      if (desty + h > screenheight)
	 h = screenheight - desty;
      if (w <= 0)
	 continue;
      if (h <= 0)
	 continue;

      if (I810_DEBUG & DEBUG_VERBOSE_DRI)
	 ErrorF("MoveBuffers %d,%d %dx%d dx: %d dy: %d\n",
		x1, y1, w, h, dx, dy);
      
      I810SelectBuffer(pScrn, I810_SELECT_BACK);
      I810SubsequentScreenToScreenCopy(pScrn, x1, y1, destx, desty, w, h);
      I810SelectBuffer(pScrn, I810_SELECT_DEPTH);
      I810SubsequentScreenToScreenCopy(pScrn, x1, y1, destx, desty, w, h);
   }
   I810SelectBuffer(pScrn, I810_SELECT_FRONT);
   I810EmitFlush(pScrn);

   if (pboxNew2) {
      free(pptNew2);
      free(pboxNew2);
   }
   if (pboxNew1) {
      free(pptNew1);
      free(pboxNew1);
   }

   I810DRISetNeedSync(pScrn);
}


/* Use the miext/shadow module to maintain a list of dirty rectangles.
 * These are blitted to the back buffer to keep both buffers clean
 * during page-flipping when the 3d application isn't fullscreen.
 *
 * Unlike most use of the shadow code, both buffers are in video memory.
 *
 * An alternative to this would be to organize for all on-screen drawing
 * operations to be duplicated for the two buffers.  That might be
 * faster, but seems like a lot more work...
 */


/* This should be done *before* XAA syncs or fires its buffer.
 * Otherwise will have to fire it again???
 */
static void I810DRIRefreshArea(ScrnInfoPtr pScrn, int num, BoxPtr pbox)
{
    I810Ptr pI810 = I810PTR(pScrn);
    int i;
    I810SAREAPtr  pSAREAPriv = DRIGetSAREAPrivate(pScrn->pScreen);
    unsigned int br13;
    int cpp=2;

    
    /* Don't want to do this when no 3d is active and pages are
     * right-way-round
     */
    if (!pSAREAPriv->pf_active && pSAREAPriv->pf_current_page == 0)
      return;

    br13 = (pI810->auxPitch) | (0xcc << 16);
    
    for (i = 0 ; i < num ; i++, pbox++) {
      unsigned int w = min(pbox->y2, pScrn->virtualY-1) - max(pbox->y1, 0) + 1;
      unsigned int h = min(pbox->x2, pScrn->virtualX-1) - max(pbox->x1, 0) + 1;
      unsigned int dst = max(pbox->x1, 0)*cpp + (max(pbox->y1, 0)*pI810->auxPitch);
      
      BEGIN_LP_RING(6);

      OUT_RING(BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4);
      OUT_RING(br13);
      OUT_RING( (h<<16) | (w*cpp) );
      OUT_RING(pI810->BackBuffer.Start + dst);
      OUT_RING(br13 & 0xffff);
      OUT_RING(dst);

      ADVANCE_LP_RING();
    }

}

static void I810EnablePageFlip(ScreenPtr pScreen)
{
    ScrnInfoPtr         pScrn      = xf86ScreenToScrn(pScreen);
    I810Ptr       pI810       = I810PTR(pScrn);
    I810SAREAPtr  pSAREAPriv = DRIGetSAREAPrivate(pScreen);
    int cpp=2;
    pSAREAPriv->pf_enabled = pI810->allowPageFlip;
    pSAREAPriv->pf_active = 0;
    
   if (pI810->allowPageFlip) {
      unsigned int br13 = pI810->auxPitch | (0xcc << 16);
      
      BEGIN_LP_RING(6);

      OUT_RING(BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4);
      OUT_RING(br13);
      OUT_RING((pScrn->virtualY << 16) | (pScrn->virtualX*cpp));
      OUT_RING(pI810->BackBuffer.Start);
      OUT_RING(br13 & 0xFFFF);
      OUT_RING(0);
      ADVANCE_LP_RING();

      pSAREAPriv->pf_active = 1;
   }
   
}

static void I810DisablePageFlip(ScreenPtr pScreen)
{
    I810SAREAPtr  pSAREAPriv = DRIGetSAREAPrivate(pScreen);

    pSAREAPriv->pf_active=0;
}

static void I810DRITransitionSingleToMulti3d(ScreenPtr pScreen)
{
    /* Tell the clients not to pageflip.  How?
     *   -- Field in sarea, plus bumping the window counters.
     *   -- DRM needs to cope with Front-to-Back swapbuffers.
     */
    I810DisablePageFlip(pScreen);
}

static void I810DRITransitionMultiToSingle3d(ScreenPtr pScreen)
{
    /* Let the remaining 3d app start page flipping again */
    I810EnablePageFlip(pScreen);
}

static void I810DRITransitionTo3d(ScreenPtr pScreen)
{
    ScrnInfoPtr    pScrn = xf86ScreenToScrn(pScreen);
    I810Ptr  pI810  = I810PTR(pScrn);

    I810EnablePageFlip(pScreen);
    pI810->have3DWindows = 1;
}

static void I810DRITransitionTo2d(ScreenPtr pScreen)
{
    ScrnInfoPtr         pScrn      = xf86ScreenToScrn(pScreen);
    I810Ptr       pI810       = I810PTR(pScrn);
    I810SAREAPtr  pSAREAPriv = DRIGetSAREAPrivate(pScreen);

    /* Try flipping back to the front page if necessary */
    if (pSAREAPriv->pf_current_page == 1)
	drmCommandNone(pI810->drmSubFD, DRM_I810_FLIP);

    /* Shut down shadowing if we've made it back to the front page */
    if (pSAREAPriv->pf_current_page == 0) {
	I810DisablePageFlip(pScreen);
    }
    pI810->have3DWindows = 0;
}

Bool
I810DRILeave(ScrnInfoPtr pScrn)
{
   I810Ptr pI810 = I810PTR(pScrn);
    
   if (pI810->directRenderingEnabled) {
      if (pI810->dcacheHandle != 0) 
	 if (drmAgpUnbind(pI810->drmSubFD, pI810->dcacheHandle) != 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
	    return FALSE;
	 }
      if (pI810->backHandle != 0) 
	 if (drmAgpUnbind(pI810->drmSubFD, pI810->backHandle) != 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
 	    return FALSE;
	 }
      if (pI810->zHandle != 0)
	 if (drmAgpUnbind(pI810->drmSubFD, pI810->zHandle) != 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
  	    return FALSE;
	 }
      if (pI810->sysmemHandle != 0)
	 if (drmAgpUnbind(pI810->drmSubFD, pI810->sysmemHandle) != 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
  	    return FALSE;
	 }
      if (pI810->xvmcHandle != 0)
	 if (drmAgpUnbind(pI810->drmSubFD, pI810->xvmcHandle) != 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
  	    return FALSE;
	 }
      if (pI810->cursorHandle != 0)
	 if (drmAgpUnbind(pI810->drmSubFD, pI810->cursorHandle) != 0) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
	    return FALSE;
	 }
      if (pI810->cursorARGBHandle != 0)
	  if (drmAgpUnbind(pI810->drmSubFD, pI810->cursorARGBHandle) != 0) {
	      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
	      return FALSE;
	  }
      if (pI810->agpAcquired == TRUE)
	 drmAgpRelease(pI810->drmSubFD);
      pI810->agpAcquired = FALSE;
   }
   return TRUE;
}

Bool
I810DRIEnter(ScrnInfoPtr pScrn)
{
   I810Ptr pI810 = I810PTR(pScrn);

   if (pI810->directRenderingEnabled) {

      if (pI810->agpAcquired == FALSE)
	 drmAgpAcquire(pI810->drmSubFD);
      pI810->agpAcquired = TRUE;
      if (pI810->dcacheHandle != 0)
	 if (drmAgpBind(pI810->drmSubFD, pI810->dcacheHandle,
			pI810->DepthOffset) != 0)
	    return FALSE;
      if (pI810->backHandle != 0)
	 if (drmAgpBind(pI810->drmSubFD, pI810->backHandle,
			pI810->BackOffset) != 0)
	    return FALSE;
      if (pI810->zHandle != 0)
	 if (drmAgpBind(pI810->drmSubFD, pI810->zHandle,
			pI810->DepthOffset) != 0)
	    return FALSE;
      if (pI810->sysmemHandle != 0)
	 if (drmAgpBind(pI810->drmSubFD, pI810->sysmemHandle, 0) != 0)
	    return FALSE;
      if (pI810->xvmcHandle != 0)
	 if (drmAgpBind(pI810->drmSubFD, pI810->xvmcHandle,
			pI810->MC.Start) != 0)
	    return FALSE;
      if (pI810->cursorHandle != 0)
	 if (drmAgpBind(pI810->drmSubFD, pI810->cursorHandle,
			pI810->CursorStart) != 0)
	    return FALSE;
      if (pI810->cursorARGBHandle != 0)
	 if (drmAgpBind(pI810->drmSubFD, pI810->cursorARGBHandle,
			pI810->CursorARGBStart) != 0)
	    return FALSE;
   }

   I810SelectBuffer(pScrn, I810_SELECT_FRONT);
   return TRUE;
}
