/*
 * 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.
 */

/**
 * \file via_context.c
 * 
 * \author John Sheng (presumably of either VIA Technologies or S3 Graphics)
 * \author Others at VIA Technologies?
 * \author Others at S3 Graphics?
 */

#include "glheader.h"
#include "context.h"
#include "matrix.h"
#include "state.h"
#include "simple_list.h"
#include "extensions.h"
#include "framebuffer.h"
#include "renderbuffer.h"

#include "swrast/swrast.h"
#include "swrast_setup/swrast_setup.h"
#include "tnl/tnl.h"
#include "vbo/vbo.h"

#include "tnl/t_pipeline.h"

#include "drivers/common/driverfuncs.h"

#include "via_screen.h"
#include "via_dri.h"

#include "via_state.h"
#include "via_tex.h"
#include "via_span.h"
#include "via_tris.h"
#include "via_ioctl.h"
#include "via_fb.h"

#include <stdio.h>
#include "macros.h"
#include "drirenderbuffer.h"

#define need_GL_ARB_multisample
#define need_GL_ARB_point_parameters
#define need_GL_ARB_vertex_buffer_object
#define need_GL_EXT_fog_coord
#define need_GL_EXT_secondary_color
#include "extension_helper.h"

#define DRIVER_DATE	"20060710"

#include "vblank.h"
#include "utils.h"

GLuint VIA_DEBUG = 0;

/**
 * Return various strings for \c glGetString.
 *
 * \sa glGetString
 */
static const GLubyte *viaGetString(GLcontext *ctx, GLenum name)
{
   static char buffer[128];
   unsigned   offset;


   switch (name) {
   case GL_VENDOR:
      return (GLubyte *)"VIA Technology";

   case GL_RENDERER: {
      static const char * const chipset_names[] = {
	 "UniChrome",
	 "CastleRock (CLE266)",
	 "UniChrome (KM400)",
	 "UniChrome (K8M800)",
	 "UniChrome (PM8x0/CN400)",
      };
      struct via_context *vmesa = VIA_CONTEXT(ctx);
      unsigned id = vmesa->viaScreen->deviceID;

      offset = driGetRendererString( buffer, 
				     chipset_names[(id > VIA_PM800) ? 0 : id],
				     DRIVER_DATE, 0 );
      return (GLubyte *)buffer;
   }

   default:
      return NULL;
   }
}


/**
 * Calculate a width that satisfies the hardware's alignment requirements.
 * On the Unichrome hardware, each scanline must be aligned to a multiple of
 * 16 pixels.
 *
 * \param width  Minimum buffer width, in pixels.
 * 
 * \returns A pixel width that meets the alignment requirements.
 */
static INLINE unsigned
buffer_align( unsigned width )
{
    return (width + 0x0f) & ~0x0f;
}


static void
viaDeleteRenderbuffer(struct gl_renderbuffer *rb)
{
   /* Don't free() since we're contained in via_context struct. */
}

static GLboolean
viaRenderbufferStorage(GLcontext *ctx, struct gl_renderbuffer *rb,
                       GLenum internalFormat, GLuint width, GLuint height)
{
   rb->Width = width;
   rb->Height = height;
   rb->InternalFormat = internalFormat;
   return GL_TRUE;
}


static void
viaInitRenderbuffer(struct via_renderbuffer *vrb, GLenum format,
		    __DRIdrawablePrivate *dPriv)
{
   const GLuint name = 0;
   struct gl_renderbuffer *rb = & vrb->Base;

   vrb->dPriv = dPriv;
   _mesa_init_renderbuffer(rb, name);

   /* Make sure we're using a null-valued GetPointer routine */
   assert(rb->GetPointer(NULL, rb, 0, 0) == NULL);

   rb->InternalFormat = format;

   if (format == GL_RGBA) {
      /* Color */
      rb->_BaseFormat = GL_RGBA;
      rb->DataType = GL_UNSIGNED_BYTE;
   }
   else if (format == GL_DEPTH_COMPONENT16) {
      /* Depth */
      rb->_BaseFormat = GL_DEPTH_COMPONENT;
      /* we always Get/Put 32-bit Z values */
      rb->DataType = GL_UNSIGNED_INT;
   }
   else if (format == GL_DEPTH_COMPONENT24) {
      /* Depth */
      rb->_BaseFormat = GL_DEPTH_COMPONENT;
      /* we always Get/Put 32-bit Z values */
      rb->DataType = GL_UNSIGNED_INT;
   }
   else {
      /* Stencil */
      ASSERT(format == GL_STENCIL_INDEX8_EXT);
      rb->_BaseFormat = GL_STENCIL_INDEX;
      rb->DataType = GL_UNSIGNED_BYTE;
   }

   rb->Delete = viaDeleteRenderbuffer;
   rb->AllocStorage = viaRenderbufferStorage;
}


/**
 * Calculate the framebuffer parameters for all buffers (front, back, depth,
 * and stencil) associated with the specified context.
 * 
 * \warning
 * This function also calls \c AllocateBuffer to actually allocate the
 * buffers.
 * 
 * \sa AllocateBuffer
 */
static GLboolean
calculate_buffer_parameters(struct via_context *vmesa,
			    struct gl_framebuffer *fb,
			    __DRIdrawablePrivate *dPriv)
{
   const unsigned shift = vmesa->viaScreen->bitsPerPixel / 16;
   const unsigned extra = 32;
   unsigned w;
   unsigned h;

   /* Normally, the renderbuffer would be added to the framebuffer just once
    * when the framebuffer was created.  The VIA driver is a bit funny
    * though in that the front/back/depth renderbuffers are in the per-context
    * state!
    * That should be fixed someday.
    */

   if (!vmesa->front.Base.InternalFormat) {
      /* do one-time init for the renderbuffers */
      viaInitRenderbuffer(&vmesa->front, GL_RGBA, dPriv);
      viaSetSpanFunctions(&vmesa->front, &fb->Visual);
      _mesa_add_renderbuffer(fb, BUFFER_FRONT_LEFT, &vmesa->front.Base);

      if (fb->Visual.doubleBufferMode) {
         viaInitRenderbuffer(&vmesa->back, GL_RGBA, dPriv);
         viaSetSpanFunctions(&vmesa->back, &fb->Visual);
         _mesa_add_renderbuffer(fb, BUFFER_BACK_LEFT, &vmesa->back.Base);
      }

      if (vmesa->glCtx->Visual.depthBits > 0) {
         viaInitRenderbuffer(&vmesa->depth,
                             (vmesa->glCtx->Visual.depthBits == 16
                              ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT24),
			     dPriv);
         viaSetSpanFunctions(&vmesa->depth, &fb->Visual);
         _mesa_add_renderbuffer(fb, BUFFER_DEPTH, &vmesa->depth.Base);
      }

      if (vmesa->glCtx->Visual.stencilBits > 0) {
         viaInitRenderbuffer(&vmesa->stencil, GL_STENCIL_INDEX8_EXT,
			     dPriv);
         viaSetSpanFunctions(&vmesa->stencil, &fb->Visual);
         _mesa_add_renderbuffer(fb, BUFFER_STENCIL, &vmesa->stencil.Base);
      }
   }

   assert(vmesa->front.Base.InternalFormat);
   assert(vmesa->front.Base.AllocStorage);
   if (fb->Visual.doubleBufferMode) {
      assert(vmesa->back.Base.AllocStorage);
   }
   if (fb->Visual.depthBits) {
      assert(vmesa->depth.Base.AllocStorage);
   }


   /* Allocate front-buffer */
   if (vmesa->drawType == GLX_PBUFFER_BIT) {
      w = vmesa->driDrawable->w;
      h = vmesa->driDrawable->h;

      vmesa->front.bpp = vmesa->viaScreen->bitsPerPixel;
      vmesa->front.pitch = buffer_align( w ) << shift; /* bytes, not pixels */
      vmesa->front.size = vmesa->front.pitch * h;

      if (vmesa->front.map)
	 via_free_draw_buffer(vmesa, &vmesa->front);
      if (!via_alloc_draw_buffer(vmesa, &vmesa->front))
	 return GL_FALSE;

   } else {
      w = vmesa->viaScreen->width;
      h = vmesa->viaScreen->height;

      vmesa->front.bpp = vmesa->viaScreen->bitsPerPixel;
      vmesa->front.pitch = buffer_align( w ) << shift; /* bytes, not pixels */
      vmesa->front.size = vmesa->front.pitch * h;
      if (getenv("ALTERNATE_SCREEN")) 
        vmesa->front.offset = vmesa->front.size;
      else
      	vmesa->front.offset = 0;
      vmesa->front.map = (char *) vmesa->driScreen->pFB;
   }


   /* Allocate back-buffer */
   if (vmesa->hasBack) {
      vmesa->back.bpp = vmesa->viaScreen->bitsPerPixel;
      vmesa->back.pitch = (buffer_align( vmesa->driDrawable->w ) << shift);
      vmesa->back.pitch += extra;
      vmesa->back.pitch = MIN2(vmesa->back.pitch, vmesa->front.pitch);
      vmesa->back.size = vmesa->back.pitch * vmesa->driDrawable->h;
      if (vmesa->back.map)
	 via_free_draw_buffer(vmesa, &vmesa->back);
      if (!via_alloc_draw_buffer(vmesa, &vmesa->back))
	 return GL_FALSE;
   }
   else {
      if (vmesa->back.map)
	 via_free_draw_buffer(vmesa, &vmesa->back);
      (void) memset( &vmesa->back, 0, sizeof( vmesa->back ) );
   }


   /* Allocate depth-buffer */
   if ( vmesa->hasStencil || vmesa->hasDepth ) {
      vmesa->depth.bpp = vmesa->depthBits;
      if (vmesa->depth.bpp == 24)
	 vmesa->depth.bpp = 32;

      vmesa->depth.pitch = (buffer_align( vmesa->driDrawable->w ) * 
			    (vmesa->depth.bpp/8)) + extra;
      vmesa->depth.size = vmesa->depth.pitch * vmesa->driDrawable->h;

      if (vmesa->depth.map)
	 via_free_draw_buffer(vmesa, &vmesa->depth);
      if (!via_alloc_draw_buffer(vmesa, &vmesa->depth)) {
	 return GL_FALSE;
      }
   }
   else {
      if (vmesa->depth.map)
   	 via_free_draw_buffer(vmesa, &vmesa->depth);
      (void) memset( & vmesa->depth, 0, sizeof( vmesa->depth ) );
   }

   /* stencil buffer is same as depth buffer */
   vmesa->stencil.handle = vmesa->depth.handle;
   vmesa->stencil.size = vmesa->depth.size;
   vmesa->stencil.offset = vmesa->depth.offset;
   vmesa->stencil.index = vmesa->depth.index;
   vmesa->stencil.pitch = vmesa->depth.pitch;
   vmesa->stencil.bpp = vmesa->depth.bpp;
   vmesa->stencil.map = vmesa->depth.map;
   vmesa->stencil.orig = vmesa->depth.orig;
   vmesa->stencil.origMap = vmesa->depth.origMap;

   if( vmesa->viaScreen->width == vmesa->driDrawable->w && 
       vmesa->viaScreen->height == vmesa->driDrawable->h ) {
      vmesa->doPageFlip = vmesa->allowPageFlip;
      if (vmesa->hasBack) {
         assert(vmesa->back.pitch == vmesa->front.pitch);
      }
   }
   else
      vmesa->doPageFlip = GL_FALSE;

   return GL_TRUE;
}


void viaReAllocateBuffers(GLcontext *ctx, GLframebuffer *drawbuffer,
                          GLuint width, GLuint height)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    calculate_buffer_parameters(vmesa, drawbuffer, vmesa->driDrawable);

    _mesa_resize_framebuffer(ctx, drawbuffer, width, height);
}

/* Extension strings exported by the Unichrome driver.
 */
const struct dri_extension card_extensions[] =
{
    { "GL_ARB_multisample",                GL_ARB_multisample_functions },
    { "GL_ARB_multitexture",               NULL },
    { "GL_ARB_point_parameters",           GL_ARB_point_parameters_functions },
    { "GL_ARB_texture_env_add",            NULL },
    { "GL_ARB_texture_env_combine",        NULL },
/*    { "GL_ARB_texture_env_dot3",           NULL }, */
    { "GL_ARB_texture_mirrored_repeat",    NULL },
    { "GL_ARB_vertex_buffer_object",       GL_ARB_vertex_buffer_object_functions },
    { "GL_EXT_fog_coord",                  GL_EXT_fog_coord_functions },
    { "GL_EXT_secondary_color",            GL_EXT_secondary_color_functions },
    { "GL_EXT_stencil_wrap",               NULL },
    { "GL_EXT_texture_env_combine",        NULL },
/*    { "GL_EXT_texture_env_dot3",           NULL }, */
    { "GL_EXT_texture_lod_bias",           NULL },
    { "GL_NV_blend_square",                NULL },
    { NULL,                                NULL }
};

extern const struct tnl_pipeline_stage _via_fastrender_stage;
extern const struct tnl_pipeline_stage _via_render_stage;

static const struct tnl_pipeline_stage *via_pipeline[] = {
    &_tnl_vertex_transform_stage,
    &_tnl_normal_transform_stage,
    &_tnl_lighting_stage,
    &_tnl_fog_coordinate_stage,
    &_tnl_texgen_stage,
    &_tnl_texture_transform_stage,
    /* REMOVE: point attenuation stage */
#if 1
    &_via_fastrender_stage,     /* ADD: unclipped rastersetup-to-dma */
#endif
    &_tnl_render_stage,
    0,
};


static const struct dri_debug_control debug_control[] =
{
    { "fall",  DEBUG_FALLBACKS },
    { "tex",   DEBUG_TEXTURE },
    { "ioctl", DEBUG_IOCTL },
    { "prim",  DEBUG_PRIMS },
    { "vert",  DEBUG_VERTS },
    { "state", DEBUG_STATE },
    { "verb",  DEBUG_VERBOSE },
    { "dri",   DEBUG_DRI },
    { "dma",   DEBUG_DMA },
    { "san",   DEBUG_SANITY },
    { "sync",  DEBUG_SYNC },
    { "sleep", DEBUG_SLEEP },
    { "pix",   DEBUG_PIXEL },
    { "2d",    DEBUG_2D },
    { NULL,    0 }
};


static GLboolean
AllocateDmaBuffer(struct via_context *vmesa)
{
    if (vmesa->dma)
        via_free_dma_buffer(vmesa);
    
    if (!via_alloc_dma_buffer(vmesa))
        return GL_FALSE;

    vmesa->dmaLow = 0;
    vmesa->dmaCliprectAddr = ~0;
    return GL_TRUE;
}

static void
FreeBuffer(struct via_context *vmesa)
{
    if (vmesa->front.map && vmesa->drawType == GLX_PBUFFER_BIT)
	via_free_draw_buffer(vmesa, &vmesa->front);

    if (vmesa->back.map)
        via_free_draw_buffer(vmesa, &vmesa->back);

    if (vmesa->depth.map)
        via_free_draw_buffer(vmesa, &vmesa->depth);

    if (vmesa->breadcrumb.map)
        via_free_draw_buffer(vmesa, &vmesa->breadcrumb);

    if (vmesa->dma)
        via_free_dma_buffer(vmesa);
}


GLboolean
viaCreateContext(const __GLcontextModes *visual,
                 __DRIcontextPrivate *driContextPriv,
                 void *sharedContextPrivate)
{
    GLcontext *ctx, *shareCtx;
    struct via_context *vmesa;
    __DRIscreenPrivate *sPriv = driContextPriv->driScreenPriv;
    viaScreenPrivate *viaScreen = (viaScreenPrivate *)sPriv->private;
    drm_via_sarea_t *saPriv = (drm_via_sarea_t *)
        (((GLubyte *)sPriv->pSAREA) + viaScreen->sareaPrivOffset);
    struct dd_function_table functions;

    /* Allocate via context */
    vmesa = (struct via_context *) CALLOC_STRUCT(via_context);
    if (!vmesa) {
        return GL_FALSE;
    }

    /* Parse configuration files.
     */
    driParseConfigFiles (&vmesa->optionCache, &viaScreen->optionCache,
			 sPriv->myNum, "unichrome");

    /* pick back buffer */
    vmesa->hasBack = visual->doubleBufferMode;

    switch(visual->depthBits) {
    case 0:			
       vmesa->hasDepth = GL_FALSE;
       vmesa->depthBits = 0; 
       vmesa->depth_max = 1.0;
       break;
    case 16:
       vmesa->hasDepth = GL_TRUE;
       vmesa->depthBits = visual->depthBits;
       vmesa->have_hw_stencil = GL_FALSE;
       vmesa->depth_max = (GLfloat)0xffff;
       vmesa->depth_clear_mask = 0xf << 28;
       vmesa->ClearDepth = 0xffff;
       vmesa->polygon_offset_scale = 1.0 / vmesa->depth_max;
       break;
    case 24:
       vmesa->hasDepth = GL_TRUE;
       vmesa->depthBits = visual->depthBits;
       vmesa->depth_max = (GLfloat) 0xffffff;
       vmesa->depth_clear_mask = 0xe << 28;
       vmesa->ClearDepth = 0xffffff00;

       assert(visual->haveStencilBuffer);
       assert(visual->stencilBits == 8);

       vmesa->have_hw_stencil = GL_TRUE;
       vmesa->stencilBits = visual->stencilBits;
       vmesa->stencil_clear_mask = 0x1 << 28;
       vmesa->polygon_offset_scale = 2.0 / vmesa->depth_max;
       break;
    case 32:
       vmesa->hasDepth = GL_TRUE;
       vmesa->depthBits = visual->depthBits;
       assert(!visual->haveStencilBuffer);
       vmesa->have_hw_stencil = GL_FALSE;
       vmesa->depth_max = (GLfloat)0xffffffff;
       vmesa->depth_clear_mask = 0xf << 28;
       vmesa->ClearDepth = 0xffffffff;
       vmesa->polygon_offset_scale = 2.0 / vmesa->depth_max;
       break;
    default:
       assert(0); 
       break;
    }

    make_empty_list(&vmesa->freed_tex_buffers);
    make_empty_list(&vmesa->tex_image_list[VIA_MEM_VIDEO]);
    make_empty_list(&vmesa->tex_image_list[VIA_MEM_AGP]);
    make_empty_list(&vmesa->tex_image_list[VIA_MEM_SYSTEM]);

    _mesa_init_driver_functions(&functions);
    viaInitTextureFuncs(&functions);

    /* Allocate the Mesa context */
    if (sharedContextPrivate)
        shareCtx = ((struct via_context *) sharedContextPrivate)->glCtx;
    else
        shareCtx = NULL;

    vmesa->glCtx = _mesa_create_context(visual, shareCtx, &functions,
					(void*) vmesa);
    
    vmesa->shareCtx = shareCtx;
    
    if (!vmesa->glCtx) {
        FREE(vmesa);
        return GL_FALSE;
    }
    driContextPriv->driverPrivate = vmesa;

    ctx = vmesa->glCtx;

    if (driQueryOptionb(&vmesa->optionCache, "excess_mipmap"))
        ctx->Const.MaxTextureLevels = 11;
    else
        ctx->Const.MaxTextureLevels = 10;

    ctx->Const.MaxTextureUnits = 2;
    ctx->Const.MaxTextureImageUnits = ctx->Const.MaxTextureUnits;
    ctx->Const.MaxTextureCoordUnits = ctx->Const.MaxTextureUnits;

    ctx->Const.MinLineWidth = 1.0;
    ctx->Const.MinLineWidthAA = 1.0;
    ctx->Const.MaxLineWidth = 1.0;
    ctx->Const.MaxLineWidthAA = 1.0;
    ctx->Const.LineWidthGranularity = 1.0;

    ctx->Const.MinPointSize = 1.0;
    ctx->Const.MinPointSizeAA = 1.0;
    ctx->Const.MaxPointSize = 1.0;
    ctx->Const.MaxPointSizeAA = 1.0;
    ctx->Const.PointSizeGranularity = 1.0;

    ctx->Driver.GetString = viaGetString;

    ctx->DriverCtx = (void *)vmesa;
    vmesa->glCtx = ctx;

    /* Initialize the software rasterizer and helper modules.
     */
    _swrast_CreateContext(ctx);
    _vbo_CreateContext(ctx);
    _tnl_CreateContext(ctx);
    _swsetup_CreateContext(ctx);

    /* Install the customized pipeline:
     */
    _tnl_destroy_pipeline(ctx);
    _tnl_install_pipeline(ctx, via_pipeline);

    /* Configure swrast and T&L to match hardware characteristics:
     */
    _swrast_allow_pixel_fog(ctx, GL_FALSE);
    _swrast_allow_vertex_fog(ctx, GL_TRUE);
    _tnl_allow_pixel_fog(ctx, GL_FALSE);
    _tnl_allow_vertex_fog(ctx, GL_TRUE);

    vmesa->hHWContext = driContextPriv->hHWContext;
    vmesa->driFd = sPriv->fd;
    vmesa->driHwLock = &sPriv->pSAREA->lock;

    vmesa->viaScreen = viaScreen;
    vmesa->driScreen = sPriv;
    vmesa->sarea = saPriv;

    vmesa->renderIndex = ~0;
    vmesa->setupIndex = ~0;
    vmesa->hwPrimitive = GL_POLYGON+1;

    /* KW: Hardwire this.  Was previously set bogusly in
     * viaCreateBuffer.  Needs work before PBUFFER can be used:
     */
    vmesa->drawType = GLX_WINDOW_BIT;


    _math_matrix_ctr(&vmesa->ViewportMatrix);

    /* Do this early, before VIA_FLUSH_DMA can be called:
     */
    if (!AllocateDmaBuffer(vmesa)) {
	fprintf(stderr ,"AllocateDmaBuffer fail\n");
	FreeBuffer(vmesa);
        FREE(vmesa);
        return GL_FALSE;
    }

    /* Allocate a small piece of fb memory for synchronization:
     */
    vmesa->breadcrumb.bpp = 32;
    vmesa->breadcrumb.pitch = buffer_align( 64 ) << 2;
    vmesa->breadcrumb.size = vmesa->breadcrumb.pitch;

    if (!via_alloc_draw_buffer(vmesa, &vmesa->breadcrumb)) {
        fprintf(stderr ,"AllocateDmaBuffer fail\n");
        FreeBuffer(vmesa);
        FREE(vmesa);
        return GL_FALSE;
    }

    driInitExtensions( ctx, card_extensions, GL_TRUE );
    viaInitStateFuncs(ctx);
    viaInitTriFuncs(ctx);
    viaInitSpanFuncs(ctx);
    viaInitIoctlFuncs(ctx);
    viaInitState(ctx);
        
    if (getenv("VIA_DEBUG"))
       VIA_DEBUG = driParseDebugString( getenv( "VIA_DEBUG" ),
					debug_control );

    if (getenv("VIA_NO_RAST") ||
        driQueryOptionb(&vmesa->optionCache, "no_rast"))
       FALLBACK(vmesa, VIA_FALLBACK_USER_DISABLE, 1);

    if (getenv("VIA_PAGEFLIP"))
       vmesa->allowPageFlip = 1;

    (*sPriv->systemTime->getUST)( &vmesa->swap_ust );


    vmesa->regMMIOBase = (GLuint *)((unsigned long)viaScreen->reg);
    vmesa->pnGEMode = (GLuint *)((unsigned long)viaScreen->reg + 0x4);
    vmesa->regEngineStatus = (GLuint *)((unsigned long)viaScreen->reg + 0x400);
    vmesa->regTranSet = (GLuint *)((unsigned long)viaScreen->reg + 0x43C);
    vmesa->regTranSpace = (GLuint *)((unsigned long)viaScreen->reg + 0x440);
    vmesa->agpBase = viaScreen->agpBase;


    return GL_TRUE;
}

void
viaDestroyContext(__DRIcontextPrivate *driContextPriv)
{
    GET_CURRENT_CONTEXT(ctx);
    struct via_context *vmesa =
       (struct via_context *)driContextPriv->driverPrivate;
    struct via_context *current = ctx ? VIA_CONTEXT(ctx) : NULL;

    assert(vmesa); /* should never be null */

    if (vmesa->driDrawable) {
       viaWaitIdle(vmesa, GL_FALSE);

       if (vmesa->doPageFlip) {
	  LOCK_HARDWARE(vmesa);
	  if (vmesa->pfCurrentOffset != 0) {
	     fprintf(stderr, "%s - reset pf\n", __FUNCTION__);
	     viaResetPageFlippingLocked(vmesa);
	  }
	  UNLOCK_HARDWARE(vmesa);
       }
    }

    /* check if we're deleting the currently bound context */
    if (vmesa == current) {
      VIA_FLUSH_DMA(vmesa);
      _mesa_make_current(NULL, NULL, NULL);
    }

    _swsetup_DestroyContext(vmesa->glCtx);
    _tnl_DestroyContext(vmesa->glCtx);
    _vbo_DestroyContext(vmesa->glCtx);
    _swrast_DestroyContext(vmesa->glCtx);
    /* free the Mesa context */
    _mesa_destroy_context(vmesa->glCtx);
    /* release our data */
    FreeBuffer(vmesa);

    assert (is_empty_list(&vmesa->tex_image_list[VIA_MEM_AGP]));
    assert (is_empty_list(&vmesa->tex_image_list[VIA_MEM_VIDEO]));
    assert (is_empty_list(&vmesa->tex_image_list[VIA_MEM_SYSTEM]));
    assert (is_empty_list(&vmesa->freed_tex_buffers));

    driDestroyOptionCache(&vmesa->optionCache);

    FREE(vmesa);
}


void viaXMesaWindowMoved(struct via_context *vmesa)
{
   __DRIdrawablePrivate *const drawable = vmesa->driDrawable;
   __DRIdrawablePrivate *const readable = vmesa->driReadable;
   struct via_renderbuffer * draw_buffer;
   struct via_renderbuffer * read_buffer;
   GLuint bytePerPixel = vmesa->viaScreen->bitsPerPixel >> 3;

   if (!drawable)
      return;

   draw_buffer =  (struct via_renderbuffer *) drawable->driverPrivate;
   read_buffer =  (struct via_renderbuffer *) readable->driverPrivate;
   
   switch (vmesa->glCtx->DrawBuffer->_ColorDrawBufferIndexes[0]) {
   case BUFFER_BACK_LEFT: 
      if (drawable->numBackClipRects == 0) {
	 vmesa->numClipRects = drawable->numClipRects;
	 vmesa->pClipRects = drawable->pClipRects;
      } 
      else {
	 vmesa->numClipRects = drawable->numBackClipRects;
	 vmesa->pClipRects = drawable->pBackClipRects;
      }
      break;
   case BUFFER_FRONT_LEFT:
      vmesa->numClipRects = drawable->numClipRects;
      vmesa->pClipRects = drawable->pClipRects;
      break;
   default:
      vmesa->numClipRects = 0;
      break;
   }

   if ((draw_buffer->drawW != drawable->w) 
       || (draw_buffer->drawH != drawable->h)) {
      calculate_buffer_parameters(vmesa, vmesa->glCtx->DrawBuffer,
				  drawable);
   }

   draw_buffer->drawX = drawable->x;
   draw_buffer->drawY = drawable->y;
   draw_buffer->drawW = drawable->w;
   draw_buffer->drawH = drawable->h;

   if (drawable != readable) {
      if ((read_buffer->drawW != readable->w) 
	  || (read_buffer->drawH != readable->h)) {
	 calculate_buffer_parameters(vmesa, vmesa->glCtx->ReadBuffer,
				     readable);
      }

      read_buffer->drawX = readable->x;
      read_buffer->drawY = readable->y;
      read_buffer->drawW = readable->w;
      read_buffer->drawH = readable->h;
   }

   vmesa->front.orig = (vmesa->front.offset + 
			draw_buffer->drawY * vmesa->front.pitch + 
			draw_buffer->drawX * bytePerPixel);

   vmesa->front.origMap = (vmesa->front.map + 
			draw_buffer->drawY * vmesa->front.pitch + 
			draw_buffer->drawX * bytePerPixel);

   vmesa->back.orig = (vmesa->back.offset +
			draw_buffer->drawY * vmesa->back.pitch +
			draw_buffer->drawX * bytePerPixel);

   vmesa->back.origMap = (vmesa->back.map +
			draw_buffer->drawY * vmesa->back.pitch +
			draw_buffer->drawX * bytePerPixel);

   vmesa->depth.orig = (vmesa->depth.offset +
			draw_buffer->drawY * vmesa->depth.pitch +
			draw_buffer->drawX * bytePerPixel);   

   vmesa->depth.origMap = (vmesa->depth.map +
			draw_buffer->drawY * vmesa->depth.pitch +
			draw_buffer->drawX * bytePerPixel);

   viaCalcViewport(vmesa->glCtx);
}

GLboolean
viaUnbindContext(__DRIcontextPrivate *driContextPriv)
{
    return GL_TRUE;
}

GLboolean
viaMakeCurrent(__DRIcontextPrivate *driContextPriv,
               __DRIdrawablePrivate *driDrawPriv,
               __DRIdrawablePrivate *driReadPriv)
{
    if (VIA_DEBUG & DEBUG_DRI) {
	fprintf(stderr, "driContextPriv = %016lx\n", (unsigned long)driContextPriv);
	fprintf(stderr, "driDrawPriv = %016lx\n", (unsigned long)driDrawPriv);    
	fprintf(stderr, "driReadPriv = %016lx\n", (unsigned long)driReadPriv);
    }	

    if (driContextPriv) {
        struct via_context *vmesa = 
	   (struct via_context *)driContextPriv->driverPrivate;
	GLcontext *ctx = vmesa->glCtx;
        struct gl_framebuffer *drawBuffer, *readBuffer;

        drawBuffer = (GLframebuffer *)driDrawPriv->driverPrivate;
        readBuffer = (GLframebuffer *)driReadPriv->driverPrivate;

       if ((vmesa->driDrawable != driDrawPriv)
	   || (vmesa->driReadable != driReadPriv)) {
	  if (driDrawPriv->swap_interval == (unsigned)-1) {
	     driDrawPriv->vblFlags =
		vmesa->viaScreen->irqEnabled ?
		driGetDefaultVBlankFlags(&vmesa->optionCache) :
		VBLANK_FLAG_NO_IRQ;

	     driDrawableInitVBlank(driDrawPriv);
	  }

	  vmesa->driDrawable = driDrawPriv;
	  vmesa->driReadable = driReadPriv;

	  if ((drawBuffer->Width != driDrawPriv->w) 
	      || (drawBuffer->Height != driDrawPriv->h)) {
	     _mesa_resize_framebuffer(ctx, drawBuffer,
				      driDrawPriv->w, driDrawPriv->h);
	     drawBuffer->Initialized = GL_TRUE;
	  }

	  if (!calculate_buffer_parameters(vmesa, drawBuffer, driDrawPriv)) {
	     return GL_FALSE;
	  }

	  if (driDrawPriv != driReadPriv) {
	     if ((readBuffer->Width != driReadPriv->w)
		 || (readBuffer->Height != driReadPriv->h)) {
		_mesa_resize_framebuffer(ctx, readBuffer,
					 driReadPriv->w, driReadPriv->h);
		readBuffer->Initialized = GL_TRUE;
	     }

	     if (!calculate_buffer_parameters(vmesa, readBuffer, driReadPriv)) {
		return GL_FALSE;
	     }
	  }
       }

        _mesa_make_current(vmesa->glCtx, drawBuffer, readBuffer);

	ctx->Driver.DrawBuffer( ctx, ctx->Color.DrawBuffer[0] );
	   
        viaXMesaWindowMoved(vmesa);
	ctx->Driver.Scissor(vmesa->glCtx,
			    vmesa->glCtx->Scissor.X,
			    vmesa->glCtx->Scissor.Y,
			    vmesa->glCtx->Scissor.Width,
			    vmesa->glCtx->Scissor.Height);
    }
    else {
        _mesa_make_current(NULL, NULL, NULL);
    }
        
    return GL_TRUE;
}

void viaGetLock(struct via_context *vmesa, GLuint flags)
{
    __DRIdrawablePrivate *dPriv = vmesa->driDrawable;
    __DRIscreenPrivate *sPriv = vmesa->driScreen;

    drmGetLock(vmesa->driFd, vmesa->hHWContext, flags);

    DRI_VALIDATE_DRAWABLE_INFO(sPriv, dPriv);
    if (dPriv != vmesa->driReadable) {
	DRI_VALIDATE_DRAWABLE_INFO(sPriv, vmesa->driReadable);
    }

    if (vmesa->sarea->ctxOwner != vmesa->hHWContext) {
       vmesa->sarea->ctxOwner = vmesa->hHWContext;
       vmesa->newEmitState = ~0;
    }

    if (vmesa->lastStamp != dPriv->lastStamp) {
       viaXMesaWindowMoved(vmesa);
       driUpdateFramebufferSize(vmesa->glCtx, dPriv);
       vmesa->newEmitState = ~0;
       vmesa->lastStamp = dPriv->lastStamp;
    }

    if (vmesa->doPageFlip &&
	vmesa->pfCurrentOffset != vmesa->sarea->pfCurrentOffset) {
       fprintf(stderr, "%s - reset pf\n", __FUNCTION__);
       viaResetPageFlippingLocked(vmesa);
    }
}


void
viaSwapBuffers(__DRIdrawablePrivate *drawablePrivate)
{
    __DRIdrawablePrivate *dPriv = (__DRIdrawablePrivate *)drawablePrivate;

    if (dPriv && 
	dPriv->driContextPriv && 
	dPriv->driContextPriv->driverPrivate) {
        struct via_context *vmesa = 
	   (struct via_context *)dPriv->driContextPriv->driverPrivate;
        GLcontext *ctx = vmesa->glCtx;

	_mesa_notifySwapBuffers(ctx);

        if (ctx->Visual.doubleBufferMode) {
            if (vmesa->doPageFlip) {
                viaPageFlip(dPriv);
            }
            else {
                viaCopyBuffer(dPriv);
            }
        }
	else
	    VIA_FLUSH_DMA(vmesa);
    }
    else {
        _mesa_problem(NULL, "viaSwapBuffers: drawable has no context!\n");
    }
}
