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


#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>

#include "main/mtypes.h"
#include "main/macros.h"
#include "main/dd.h"
#include "main/context.h"
#include "main/colormac.h"
#include "main/mm.h"
#include "swrast/swrast.h"

#include "savagecontext.h"
#include "savageioctl.h"
#include "savage_bci.h"
#include "savagestate.h"
#include "savagespan.h"

#include "drm.h"
#include <sys/ioctl.h>
#include <sys/timeb.h>

#define DEPTH_SCALE_16 ((1<<16)-1)
#define DEPTH_SCALE_24 ((1<<24)-1)


void savageGetDMABuffer( savageContextPtr imesa )
{
   int idx = 0;
   int size = 0;
   drmDMAReq dma;
   int retcode;
   drmBufPtr buf;

   assert (imesa->savageScreen->bufs);

   if (SAVAGE_DEBUG & DEBUG_DMA)
      fprintf(stderr,  "Getting dma buffer\n");

   dma.context = imesa->hHWContext;
   dma.send_count = 0;
   dma.send_list = NULL;
   dma.send_sizes = NULL;
   dma.flags = 0;
   dma.request_count = 1;
   dma.request_size = imesa->bufferSize;
   dma.request_list = &idx;
   dma.request_sizes = &size;
   dma.granted_count = 0;


   if (SAVAGE_DEBUG & DEBUG_DMA)
      fprintf(stderr, "drmDMA (get) ctx %d count %d size 0x%x\n",
	   dma.context, dma.request_count,
	   dma.request_size);

   while (1) {
      retcode = drmDMA(imesa->driFd, &dma);

      if (SAVAGE_DEBUG & DEBUG_DMA)
	 fprintf(stderr, "retcode %d sz %d idx %d count %d\n",
		 retcode,
		 dma.request_sizes[0],
		 dma.request_list[0],
		 dma.granted_count);

      if (retcode == 0 &&
	  dma.request_sizes[0] &&
	  dma.granted_count)
	 break;

      if (SAVAGE_DEBUG & DEBUG_DMA)
	 fprintf(stderr, "\n\nflush");
   }

   buf = &(imesa->savageScreen->bufs->list[idx]);

   if (SAVAGE_DEBUG & DEBUG_DMA)
      fprintf(stderr,
	   "drmDMA (get) returns size[0] 0x%x idx[0] %d\n"
	   "dma_buffer now: buf idx: %d size: %d used: %d addr %p\n",
	   dma.request_sizes[0], dma.request_list[0],
	   buf->idx, buf->total,
	   buf->used, buf->address);

   imesa->dmaVtxBuf.total = buf->total / 4;
   imesa->dmaVtxBuf.used = 0;
   imesa->dmaVtxBuf.flushed = 0;
   imesa->dmaVtxBuf.idx = buf->idx;
   imesa->dmaVtxBuf.buf = (uint32_t *)buf->address;

   if (SAVAGE_DEBUG & DEBUG_DMA)
      fprintf(stderr, "finished getbuffer\n");
}

#if 0
/* Still keeping this around because it demonstrates page flipping and
 * automatic z-clear. */
static void savage_BCI_clear(GLcontext *ctx, drm_savage_clear_t *pclear)
{
	savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
	int nbox = imesa->sarea->nbox;
	drm_clip_rect_t *pbox = imesa->sarea->boxes;
        int i;

	
      	if (nbox > SAVAGE_NR_SAREA_CLIPRECTS)
     		nbox = SAVAGE_NR_SAREA_CLIPRECTS;

	for (i = 0 ; i < nbox ; i++, pbox++) {
		unsigned int x = pbox->x1;
		unsigned int y = pbox->y1;
		unsigned int width = pbox->x2 - x;
		unsigned int height = pbox->y2 - y;
 		uint32_t *bciptr;

		if (pbox->x1 > pbox->x2 ||
		    pbox->y1 > pbox->y2 ||
		    pbox->x2 > imesa->savageScreen->width ||
		    pbox->y2 > imesa->savageScreen->height)
			continue;

	   	if ( pclear->flags & SAVAGE_FRONT ) {
		        bciptr = savageDMAAlloc (imesa, 8);
			WRITE_CMD((bciptr) , 0x4BCC8C00,uint32_t);
			WRITE_CMD((bciptr) , imesa->savageScreen->frontOffset,uint32_t);
			WRITE_CMD((bciptr) , imesa->savageScreen->frontBitmapDesc,uint32_t);
			WRITE_CMD((bciptr) , pclear->clear_color,uint32_t);
			WRITE_CMD((bciptr) , (y <<16) | x,uint32_t);
			WRITE_CMD((bciptr) , (height << 16) | width,uint32_t);
			savageDMACommit (imesa, bciptr);
		}
		if ( pclear->flags & SAVAGE_BACK ) {
		        bciptr = savageDMAAlloc (imesa, 8);
			WRITE_CMD((bciptr) , 0x4BCC8C00,uint32_t);
			WRITE_CMD((bciptr) , imesa->savageScreen->backOffset,uint32_t);
			WRITE_CMD((bciptr) , imesa->savageScreen->backBitmapDesc,uint32_t);
			WRITE_CMD((bciptr) , pclear->clear_color,uint32_t);
			WRITE_CMD((bciptr) , (y <<16) | x,uint32_t);
			WRITE_CMD((bciptr) , (height << 16) | width,uint32_t);
			savageDMACommit (imesa, bciptr);
		}
		
		if ( pclear->flags & (SAVAGE_DEPTH |SAVAGE_STENCIL) ) {
		        uint32_t writeMask = 0x0;
		        if(imesa->hw_stencil)
		        {        
		            if(pclear->flags & SAVAGE_STENCIL)
		            {
		          
		                 writeMask |= 0xFF000000;
		            }
		            if(pclear->flags & SAVAGE_DEPTH)
		            {
		                 writeMask |= 0x00FFFFFF;
		            }
                        }
		        if(imesa->IsFullScreen && imesa->NotFirstFrame &&
			   imesa->savageScreen->chipset >= S3_SAVAGE4)
		        {
		            imesa->regs.s4.zBufCtrl.ni.autoZEnable = GL_TRUE;
                            imesa->regs.s4.zBufCtrl.ni.frameID =
				~imesa->regs.s4.zBufCtrl.ni.frameID;
                            
                            imesa->dirty |= SAVAGE_UPLOAD_GLOBAL;
		        }
		        else
		        {
		            if(imesa->IsFullScreen)
		                imesa->NotFirstFrame = GL_TRUE;
		                
			    if(imesa->hw_stencil)
			    {
				bciptr = savageDMAAlloc (imesa, 10);
			        if(writeMask != 0xFFFFFFFF)
			        {
                                    WRITE_CMD((bciptr) , 0x960100D7,uint32_t);
                                    WRITE_CMD((bciptr) , writeMask,uint32_t);
                                }
                            }
			    else
			    {
				bciptr = savageDMAAlloc (imesa, 6);
			    }

			    WRITE_CMD((bciptr) , 0x4BCC8C00,uint32_t);
			    WRITE_CMD((bciptr) , imesa->savageScreen->depthOffset,uint32_t);
			    WRITE_CMD((bciptr) , imesa->savageScreen->depthBitmapDesc,uint32_t);
			    WRITE_CMD((bciptr) , pclear->clear_depth,uint32_t);
			    WRITE_CMD((bciptr) , (y <<16) | x,uint32_t);
			    WRITE_CMD((bciptr) , (height << 16) | width,uint32_t);
			    if(imesa->hw_stencil)
			    {
			        if(writeMask != 0xFFFFFFFF)
			        {
			           WRITE_CMD((bciptr) , 0x960100D7,uint32_t);
                                   WRITE_CMD((bciptr) , 0xFFFFFFFF,uint32_t);  
			        }
			    }
			    savageDMACommit (imesa, bciptr);
			}
		}
	}
	/* FK: Make sure that the clear stuff is emitted. Otherwise a
	   software fallback may get overwritten by a delayed clear. */
	savageDMAFlush (imesa);
}

static void savage_BCI_swap(savageContextPtr imesa)
{
    int nbox = imesa->sarea->nbox;
    drm_clip_rect_t *pbox = imesa->sarea->boxes;
    int i;
    volatile uint32_t *bciptr;
    
    if (nbox > SAVAGE_NR_SAREA_CLIPRECTS)
        nbox = SAVAGE_NR_SAREA_CLIPRECTS;
    savageDMAFlush (imesa);
    
    if(imesa->IsFullScreen)
    { /* full screen*/
        unsigned int tmp0;
        tmp0 = imesa->savageScreen->frontOffset; 
        imesa->savageScreen->frontOffset = imesa->savageScreen->backOffset;
        imesa->savageScreen->backOffset = tmp0;
        
        if(imesa->toggle == TARGET_BACK)
            imesa->toggle = TARGET_FRONT;
        else
            imesa->toggle = TARGET_BACK; 
        
        driFlipRenderbuffers(imesa->glCtx->DrawBuffer,
                             imesa->toggle != TARGET_FRONT);

        imesa->regs.s4.destCtrl.ni.offset = imesa->savageScreen->backOffset>>11;
        imesa->dirty |= SAVAGE_UPLOAD_GLOBAL;
        bciptr = SAVAGE_GET_BCI_POINTER(imesa,3);
        *(bciptr) = 0x960100B0;
        *(bciptr) = (imesa->savageScreen->frontOffset); 
        *(bciptr) = 0xA0000000;
    } 
    
    else
    {  /* Use bitblt copy from back to front buffer*/
        
        for (i = 0 ; i < nbox; i++, pbox++)
        {
            unsigned int w = pbox->x2 - pbox->x1;
            unsigned int h = pbox->y2 - pbox->y1;
            
            if (pbox->x1 > pbox->x2 ||
                pbox->y1 > pbox->y2 ||
                pbox->x2 > imesa->savageScreen->width ||
                pbox->y2 > imesa->savageScreen->height)
                continue;

            bciptr = SAVAGE_GET_BCI_POINTER(imesa,6);
            
            *(bciptr) = 0x4BCC00C0;
            
            *(bciptr) = imesa->savageScreen->backOffset;
            *(bciptr) = imesa->savageScreen->backBitmapDesc;
            *(bciptr) = (pbox->y1 <<16) | pbox->x1;   /*x0, y0*/
            *(bciptr) = (pbox->y1 <<16) | pbox->x1;
            *(bciptr) = (h << 16) | w;
        }
        
    }
}
#endif


static GLboolean intersect_rect( drm_clip_rect_t *out,
				 const drm_clip_rect_t *a,
				 const drm_clip_rect_t *b )
{
   *out = *a;
   if (b->x1 > out->x1) out->x1 = b->x1;
   if (b->y1 > out->y1) out->y1 = b->y1;
   if (b->x2 < out->x2) out->x2 = b->x2;
   if (b->y2 < out->y2) out->y2 = b->y2;

   return ((out->x1 < out->x2) && (out->y1 < out->y2));
}


static GLuint savageIntersectClipRects(drm_clip_rect_t *dest,
				       const drm_clip_rect_t *src,
				       GLuint nsrc,
				       const drm_clip_rect_t *clip)
{
    GLuint i, ndest;

    for (i = 0, ndest = 0; i < nsrc; ++i, ++src) {
	if (intersect_rect(dest, src, clip)) {
	    dest++;
	    ndest++;
	}
    }

    return ndest;
}


static void savageDDClear( GLcontext *ctx, GLbitfield mask )
{
   savageContextPtr imesa = SAVAGE_CONTEXT( ctx );
   GLuint colorMask, depthMask, clearColor, clearDepth, flags;
   GLint cx = ctx->DrawBuffer->_Xmin;
   GLint cy = ctx->DrawBuffer->_Ymin;
   GLint cw = ctx->DrawBuffer->_Xmax - cx;
   GLint ch = ctx->DrawBuffer->_Ymax - cy;

   /* XXX FIX ME: the cx,cy,cw,ch vars are currently ignored! */

   if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG)
       fprintf (stderr, "%s\n", __FUNCTION__);

   clearColor = imesa->ClearColor;
   if (imesa->float_depth) {
       if (imesa->savageScreen->zpp == 2)
	   clearDepth = savageEncodeFloat16(1.0 - ctx->Depth.Clear);
       else
	   clearDepth = savageEncodeFloat24(1.0 - ctx->Depth.Clear);
   } else {
       if (imesa->savageScreen->zpp == 2)
	   clearDepth = (GLuint) ((1.0 - ctx->Depth.Clear) * DEPTH_SCALE_16);
       else
	   clearDepth = (GLuint) ((1.0 - ctx->Depth.Clear) * DEPTH_SCALE_24);
   }

   colorMask = 0;
   depthMask = 0;
   switch (imesa->savageScreen->cpp) {
   case 2:
       colorMask = PACK_COLOR_565(ctx->Color.ColorMask[0],
				  ctx->Color.ColorMask[1],
				  ctx->Color.ColorMask[2]);
       break;
   case 4:
       colorMask = PACK_COLOR_8888(ctx->Color.ColorMask[3],
				   ctx->Color.ColorMask[2],
				   ctx->Color.ColorMask[1],
				   ctx->Color.ColorMask[0]);
       break;
   }

   flags = 0;

   if (mask & BUFFER_BIT_FRONT_LEFT) {
      flags |= SAVAGE_FRONT;
      mask &= ~BUFFER_BIT_FRONT_LEFT;
   }

   if (mask & BUFFER_BIT_BACK_LEFT) {
      flags |= SAVAGE_BACK;
      mask &= ~BUFFER_BIT_BACK_LEFT;
   }

   if ((mask & BUFFER_BIT_DEPTH) && ctx->Depth.Mask) {
      flags |= SAVAGE_DEPTH;
      depthMask |=
	  (imesa->savageScreen->zpp == 2) ? 0xffffffff : 0x00ffffff;
      mask &= ~BUFFER_BIT_DEPTH;
   }
   
   if((mask & BUFFER_BIT_STENCIL) && imesa->hw_stencil)
   {
      flags |= SAVAGE_DEPTH;
      depthMask |= 0xff000000;
      mask &= ~BUFFER_BIT_STENCIL;
   }

   savageFlushVertices(imesa);

   if (flags) {
       GLboolean depthCleared = GL_FALSE;
       if (flags & (SAVAGE_FRONT|SAVAGE_BACK)) {
	   drm_savage_cmd_header_t *cmd;
	   cmd = savageAllocCmdBuf(imesa, sizeof(drm_savage_cmd_header_t));
	   cmd[0].clear0.cmd = SAVAGE_CMD_CLEAR;
	   if ((flags & SAVAGE_DEPTH) &&
	       clearDepth == clearColor && depthMask == colorMask) {
	       cmd[0].clear0.flags = flags;
	       depthCleared = GL_TRUE;
	   } else
	       cmd[0].clear0.flags = flags & (SAVAGE_FRONT|SAVAGE_BACK);
	   cmd[1].clear1.mask = colorMask;
	   cmd[1].clear1.value = clearColor;
       }

       if ((flags & SAVAGE_DEPTH) && !depthCleared) {
	   drm_savage_cmd_header_t *cmd;
	   cmd = savageAllocCmdBuf(imesa, sizeof(drm_savage_cmd_header_t));
	   cmd[0].clear0.cmd = SAVAGE_CMD_CLEAR;
	   cmd[0].clear0.flags = SAVAGE_DEPTH;
	   cmd[1].clear1.mask = depthMask;
	   cmd[1].clear1.value = clearDepth;
       }
   }

   if (mask) 
      _swrast_Clear( ctx, mask );
}

/*
 * Copy the back buffer to the front buffer. 
 */
void savageSwapBuffers( __DRIdrawablePrivate *dPriv )
{
   savageContextPtr imesa;

   if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG)
       fprintf (stderr, "%s\n================================\n", __FUNCTION__);

   assert(dPriv);
   assert(dPriv->driContextPriv);
   assert(dPriv->driContextPriv->driverPrivate);

   imesa = (savageContextPtr) dPriv->driContextPriv->driverPrivate;
   if (imesa->IsDouble)
       _mesa_notifySwapBuffers( imesa->glCtx );

   FLUSH_BATCH(imesa);

   if (imesa->sync_frames)
       imesa->lastSwap = savageEmitEvent( imesa, 0 );

   if (imesa->lastSwap != 0)
       savageWaitEvent( imesa, imesa->lastSwap );

   {
       drm_savage_cmd_header_t *cmd = savageAllocCmdBuf(imesa, 0);
       cmd->cmd.cmd = SAVAGE_CMD_SWAP;
       imesa->inSwap = GL_TRUE; /* ignore scissors in savageFlushCmdBuf */
       savageFlushCmdBuf(imesa, GL_FALSE);
       imesa->inSwap = GL_FALSE;
   }

   if (!imesa->sync_frames)
       /* don't sync, but limit the lag to one frame. */
       imesa->lastSwap = savageEmitEvent( imesa, 0 );
}

unsigned int savageEmitEventLocked( savageContextPtr imesa, unsigned int flags )
{
    drm_savage_event_emit_t event;
    int ret;
    event.count = 0;
    event.flags = flags;
    ret = drmCommandWriteRead( imesa->driFd, DRM_SAVAGE_BCI_EVENT_EMIT,
			       &event, sizeof(event) );
    if (ret) {
	fprintf (stderr, "emit event returned %d\n", ret);
	exit (1);
    }
    return event.count;
}
unsigned int savageEmitEvent( savageContextPtr imesa, unsigned int flags )
{
    unsigned int ret;
    LOCK_HARDWARE( imesa );
    ret = savageEmitEventLocked( imesa, flags );
    UNLOCK_HARDWARE( imesa );
    return ret;
}


void savageWaitEvent( savageContextPtr imesa, unsigned int count )
{
    drm_savage_event_wait_t event;
    int ret;
    event.count = count;
    event.flags = 0;
    ret = drmCommandWriteRead( imesa->driFd, DRM_SAVAGE_BCI_EVENT_WAIT,
			       &event, sizeof(event) );
    if (ret) {
	fprintf (stderr, "wait event returned %d\n", ret);
	exit (1);
    }
}


void savageFlushVertices( savageContextPtr imesa )
{
    struct savage_vtxbuf_t *buffer = imesa->vtxBuf;

    if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG)
	fprintf (stderr, "%s\n", __FUNCTION__);

    if (!buffer->total)
	return;

    if (buffer->used > buffer->flushed) {
	drm_savage_cmd_header_t *cmd;
	/* State must be updated "per primitive" because hardware
	 * culling must be disabled for unfilled primitives, points
	 * and lines. */
	savageEmitChangedState (imesa);
	cmd = savageAllocCmdBuf(imesa, 0);
	cmd->prim.cmd = buffer == &imesa->dmaVtxBuf ?
	    SAVAGE_CMD_DMA_PRIM : SAVAGE_CMD_VB_PRIM;
	cmd->prim.prim = imesa->HwPrim;
	cmd->prim.skip = imesa->skip;
	cmd->prim.start = buffer->flushed / imesa->HwVertexSize;
	cmd->prim.count = buffer->used / imesa->HwVertexSize - cmd->prim.start;
	buffer->flushed = buffer->used;
    }
}

void savageFlushCmdBufLocked( savageContextPtr imesa, GLboolean discard )
{
    __DRIdrawablePrivate *dPriv = imesa->driDrawable;

    if (!imesa->dmaVtxBuf.total)
	discard = GL_FALSE;

    /* complete indexed drawing commands */
    savageFlushElts(imesa);

    if (imesa->cmdBuf.write != imesa->cmdBuf.start || discard) {
	drm_savage_cmdbuf_t cmdbuf;
	drm_savage_cmd_header_t *start;
	int ret;

	/* If we lost the context we must restore the initial state (at
	 * the start of the command buffer). */
	if (imesa->lostContext) {
	    start = imesa->cmdBuf.base;
	    imesa->lostContext = GL_FALSE;
	} else
	    start = imesa->cmdBuf.start;

	if ((SAVAGE_DEBUG & DEBUG_DMA) && discard)
	    fprintf (stderr, "Discarding DMA buffer, used=%u\n",
		     imesa->dmaVtxBuf.used);

	cmdbuf.dma_idx = imesa->dmaVtxBuf.idx;
	cmdbuf.discard = discard;
	cmdbuf.vb_addr = imesa->clientVtxBuf.buf;
	cmdbuf.vb_size = imesa->clientVtxBuf.total*4;
	cmdbuf.vb_stride = imesa->HwVertexSize;
	cmdbuf.cmd_addr = start;
	cmdbuf.size = (imesa->cmdBuf.write - start);
	if (!imesa->inSwap && imesa->scissor.enabled) {
	    drm_clip_rect_t *box = dPriv->pClipRects, *ibox;
	    drm_clip_rect_t scissor;
	    GLuint nbox = dPriv->numClipRects, nibox;
	    /* transform and clip scissor to viewport */
	    scissor.x1 = MAX2(imesa->scissor.x, 0) + dPriv->x;
	    scissor.y1 = MAX2(dPriv->h - imesa->scissor.y - imesa->scissor.h,
			      0) + dPriv->y;
	    scissor.x2 = MIN2(imesa->scissor.x + imesa->scissor.w,
			      dPriv->w) + dPriv->x;
	    scissor.y2 = MIN2(dPriv->h - imesa->scissor.y,
			      dPriv->h) + dPriv->y;
	    /* intersect cliprects with scissor */
	    ibox = malloc(dPriv->numClipRects*sizeof(drm_clip_rect_t));
	    if (!ibox) {
		fprintf(stderr, "Out of memory.\n");
		exit(1);
	    }
	    nibox = savageIntersectClipRects(ibox, box, nbox, &scissor);
	    cmdbuf.nbox = nibox;
	    cmdbuf.box_addr = ibox;
	} else {
	    cmdbuf.nbox = dPriv->numClipRects;
	    cmdbuf.box_addr = dPriv->pClipRects;
	}

	ret = drmCommandWrite( imesa->driFd, DRM_SAVAGE_BCI_CMDBUF,
			       &cmdbuf, sizeof(cmdbuf) );
	if (ret) {
	    fprintf (stderr, "cmdbuf ioctl returned %d\n", ret);
	    exit(1);
	}

	if (cmdbuf.box_addr != dPriv->pClipRects) {
	    free(cmdbuf.box_addr);
	}

	/* Save the current state at the start of the command buffer. That
	 * state will only be emitted, if the context was lost since the
	 * last command buffer. */
	imesa->cmdBuf.write = imesa->cmdBuf.base;
	savageEmitOldState(imesa);
	imesa->cmdBuf.start = imesa->cmdBuf.write;
    }

    if (discard) {
	assert (!savageHaveIndexedVerts(imesa));
	imesa->dmaVtxBuf.total = 0;
	imesa->dmaVtxBuf.used = 0;
	imesa->dmaVtxBuf.flushed = 0;
    }
    if (!savageHaveIndexedVerts(imesa)) {
	imesa->clientVtxBuf.used = 0;
	imesa->clientVtxBuf.flushed = 0;
    }
}


void savageFlushCmdBuf( savageContextPtr imesa, GLboolean discard ) 
{
    if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG)
	fprintf (stderr, "%s\n", __FUNCTION__);
    LOCK_HARDWARE(imesa);
    savageFlushCmdBufLocked (imesa, discard);
    UNLOCK_HARDWARE(imesa);
}


static void savageDDFlush( GLcontext *ctx )
{
    savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
    if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG)
	fprintf (stderr, "%s\n", __FUNCTION__);
    savageFlushVertices (imesa);
    savageFlushCmdBuf(imesa, GL_FALSE);
}

static void savageDDFinish( GLcontext *ctx  ) 
{
    savageContextPtr imesa = SAVAGE_CONTEXT(ctx);
    if (SAVAGE_DEBUG & DEBUG_VERBOSE_MSG)
	fprintf (stderr, "%s\n", __FUNCTION__);
    savageFlushVertices (imesa);
    savageFlushCmdBuf(imesa, GL_FALSE);
    WAIT_IDLE_EMPTY(imesa);
}

void savageDDInitIoctlFuncs( GLcontext *ctx )
{
   ctx->Driver.Clear = savageDDClear;
   ctx->Driver.Flush = savageDDFlush;
   ctx->Driver.Finish = savageDDFinish;
}