>From f4929b64da2ebc2adaabce7a5da716026449b9d9 Mon Sep 17 00:00:00 2001
From: =?utf-8?q?Alex=20Villac=C3=ADs=20Lasso?= <alex@karlalex.palosanto.com>
Date: Sat, 11 Apr 2009 19:12:31 -0500
Subject: [PATCH] Implement use of Mastered Image Transfer through AGP for UploadToScreen EXA acceleration.

Under some conditions (documented in this patch), the UploadToScreen implementation can make use of the AGP scratch buffer used for XVideo as a convenient source for Mastered Image Transfer. The previous implementation is still available as a fallback for when AGP acceleration is not possible. This requires the AGP scratch buffer to be persistent, so the XVideo code is also made aware of an existing DRM mapping for the scratch buffer.
---
 man/savage.man     |    6 ++++-
 src/savage_exa.c   |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/savage_video.c |   11 ++++++--
 3 files changed, 78 insertions(+), 6 deletions(-)

diff --git a/man/savage.man b/man/savage.man
index 822a233..a1cbb1e 100644
--- a/man/savage.man
+++ b/man/savage.man
@@ -180,7 +180,7 @@ and twister (use BCI for Xv); off for savage4 (do not use the BCI for Xv).
 Instructs the BCI Xv pixel formatter to use AGP memory as a scratch buffer.
 Ordinarily the BCI formatter uses a an area in framebuffer memory to hold 
 YV12 planar data to be converted for display. This requires a somewhat expensive
-upload of YV12 data to framebuffer memory. The \*qAGPforXv\*q causes the BCI
+upload of YV12 data to framebuffer memory. The \*qAGPforXv\*q option causes the BCI
 formatter to place the YV12 data in AGP memory instead, which can be uploaded
 faster than the framebuffer. Use of this option cuts upload overhead by 25%
 according to benchmarks. This option also smooths out most of the shearing
@@ -189,6 +189,10 @@ present when using BCI for pixel conversion. Currently this option is
 and is disabled by default. Video width restrictions that apply to \*qBCIforXv\*q 
 also apply here. Only valid when \*qDRI\*q and \*qBCIforXv\*q are both active, 
 and only on AGP chipsets. Default: \*qoff\*q.
+.br
+If \*qAccelMethod\*q is set to \*qEXA\*q and \*qAGPforXv\*q is enabled, then the
+driver will also attempt to reuse the AGP scratch buffer for UploadToScreen 
+acceleration.
 .TP 
 .BI "Option \*qAGPMode\*q \*q" integer \*q
 Set AGP data transfer rate.
diff --git a/src/savage_exa.c b/src/savage_exa.c
index 538e000..08524f0 100644
--- a/src/savage_exa.c
+++ b/src/savage_exa.c
@@ -463,10 +463,73 @@ SavageUploadToScreen(PixmapPtr pDst, int x, int y, int w, int h, char *src, int
     BCI_GET_PTR;
     int i, j, dwords, queue, Bpp;
     unsigned int cmd;
-    CARD32 * srcp; 
+    CARD32 * srcp;
+    unsigned int dst_pitch;
+    unsigned int dst_yoffset;
+    int agp_possible;
     
+    exaWaitSync(pDst->drawable.pScreen);
+
     Bpp = pDst->drawable.bitsPerPixel / 8;
 
+    /* Test for conditions for AGP Mastered Image Transfer (MIT). AGP memory
+       needs to be available, the XVideo AGP needs to be enabled, the 
+       framebuffer destination must be a multiple of 32 bytes, and the source
+       pitch must span the entirety of the destination pitch. This last 
+       condition allows the code to consider this upload as equivalent to a 
+       plain memcpy() call. */
+    dst_pitch = exaGetPixmapPitch(pDst);
+    dst_yoffset = exaGetPixmapOffset(pDst) + y * dst_pitch;
+    agp_possible = 
+        (!psav->IsPCI && psav->drmFD > 0 && psav->DRIServerInfo != NULL &&
+        psav->DRIServerInfo->agpXVideo.size > 0 &&
+        x == 0 && src_pitch == dst_pitch && w * Bpp == dst_pitch &&
+        (dst_yoffset & 0x1f) == 0);
+
+    if (agp_possible) {
+      	SAVAGEDRIServerPrivatePtr pSAVAGEDRIServer = psav->DRIServerInfo;
+        if (pSAVAGEDRIServer->agpXVideo.map != NULL || 
+            0 <= drmMap( psav->drmFD,
+		pSAVAGEDRIServer->agpXVideo.handle,
+		pSAVAGEDRIServer->agpXVideo.size,
+		&pSAVAGEDRIServer->agpXVideo.map)) {
+        
+            unsigned char * agpMap = pSAVAGEDRIServer->agpXVideo.map;
+            unsigned int agpOffset = drmAgpBase(psav->drmFD) + pSAVAGEDRIServer->agpXVideo.offset;
+            unsigned int bytesTotal = dst_pitch * h;            
+
+            while (bytesTotal > 0) {
+                unsigned int bytesTransfer = 
+                    (bytesTotal > pSAVAGEDRIServer->agpXVideo.size) 
+                    ? pSAVAGEDRIServer->agpXVideo.size 
+                    : bytesTotal;
+                unsigned int qwordsTransfer = bytesTransfer >> 3;
+
+                /* Copy source into AGP buffer */
+                memcpy(agpMap, src, bytesTransfer);
+                
+                psav->WaitQueue(psav,6);
+                BCI_SEND(BCI_SET_REGISTER | BCI_SET_REGISTER_COUNT(2) | 0x51);
+                BCI_SEND(agpOffset | 3);        /* Source buffer in AGP memory */
+                BCI_SEND(dst_yoffset);          /* Destination buffer in framebuffer */
+
+                BCI_SEND(BCI_SET_REGISTER | BCI_SET_REGISTER_COUNT(1) | 0x50);
+                BCI_SEND(0x00000002 | ((qwordsTransfer - 1) << 3)); /* Select MIT, sysmem to framebuffer */
+
+                /* I want to wait here for any reads from AGP memory and any 
+                   framebuffer writes performed by the MIT to stop. */
+                BCI_SEND(0xC0000000 | ((0x08 | 0x01) << 16));
+
+                bytesTotal -= bytesTransfer;
+                src += bytesTransfer;
+                dst_yoffset += bytesTransfer;
+            }
+            exaMarkSync(pDst->drawable.pScreen);
+            return TRUE;
+        }
+    }
+
+    /* If we reach here, AGP transfer is not possible, or failed to drmMap() */
     psav->sbd_offset = exaGetPixmapOffset(pDst);
     psav->sbd_high = SavageSetBD(psav, pDst);
 
@@ -515,7 +578,7 @@ SavageUploadToScreen(PixmapPtr pDst, int x, int y, int w, int h, char *src, int
     }
 
     /*exaWaitSync(pDst->drawable.pScreen);*/
-
+    exaMarkSync(pDst->drawable.pScreen);
     return TRUE;
 }
 
diff --git a/src/savage_video.c b/src/savage_video.c
index bccb801..cf51ba0 100644
--- a/src/savage_video.c
+++ b/src/savage_video.c
@@ -1048,8 +1048,12 @@ SavageStopVideo(ScrnInfoPtr pScrn, pointer data, Bool shutdown)
 
 	if (pPriv->agpBufferMap != NULL) {
 	    SAVAGEDRIServerPrivatePtr pSAVAGEDRIServer = psav->DRIServerInfo;
-	    drmUnmap(pPriv->agpBufferMap, pSAVAGEDRIServer->agpXVideo.size);
-	    pSAVAGEDRIServer->agpXVideo.map = NULL;
+
+            /* agpXVideo is reused to implement UploadToScreen in EXA */            
+            if (!psav->useEXA) {
+	        drmUnmap(pPriv->agpBufferMap, pSAVAGEDRIServer->agpXVideo.size);
+	        pSAVAGEDRIServer->agpXVideo.map = NULL;
+            }
 	    pPriv->agpBufferMap = NULL;
 	    pPriv->agpBufferOffset = 0;
 	}
@@ -1971,7 +1975,8 @@ SavagePutImage(
         
 	pPriv->tried_agp = TRUE;
 	if (pSAVAGEDRIServer->agpXVideo.size >= max(new_size, planarFrameSize)) {
-	    if ( drmMap( psav->drmFD,
+	    if (pSAVAGEDRIServer->agpXVideo.map == NULL &&
+	        drmMap( psav->drmFD,
 		pSAVAGEDRIServer->agpXVideo.handle,
 		pSAVAGEDRIServer->agpXVideo.size,
 		&pSAVAGEDRIServer->agpXVideo.map ) < 0 ) {
-- 
1.6.0.6

