/*
 * Copyright 2000-2001 VA Linux Systems, 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
 * on 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
 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Keith Whitwell <keith@tungstengraphics.com>
 */


#include "main/mtypes.h"
#include "main/colormac.h"
#include "main/dd.h"
#include "main/mm.h"

#include "mgacontext.h"
#include "mgadd.h"
#include "mgastate.h"
#include "mgatex.h"
#include "mgavb.h"
#include "mgatris.h"
#include "mgaioctl.h"
#include "mgaregs.h"

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

#include "xmlpool.h"
#include "drirenderbuffer.h"


static void updateSpecularLighting( GLcontext *ctx );

static const GLuint mgarop_NoBLK[16] = {
   DC_atype_rpl  | 0x00000000, DC_atype_rstr | 0x00080000,
   DC_atype_rstr | 0x00040000, DC_atype_rpl  | 0x000c0000,
   DC_atype_rstr | 0x00020000, DC_atype_rstr | 0x000a0000,
   DC_atype_rstr | 0x00060000, DC_atype_rstr | 0x000e0000,
   DC_atype_rstr | 0x00010000, DC_atype_rstr | 0x00090000,
   DC_atype_rstr | 0x00050000, DC_atype_rstr | 0x000d0000,
   DC_atype_rpl  | 0x00030000, DC_atype_rstr | 0x000b0000,
   DC_atype_rstr | 0x00070000, DC_atype_rpl  | 0x000f0000
};

/* =============================================================
 * Alpha blending
 */

static void mgaDDAlphaFunc(GLcontext *ctx, GLenum func, GLfloat ref)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   GLubyte refByte;
   GLuint  a;

   CLAMPED_FLOAT_TO_UBYTE(refByte, ref);

   switch ( func ) {
   case GL_NEVER:
      a = AC_atmode_alt;
      refByte = 0;
      break;
   case GL_LESS:
      a = AC_atmode_alt;
      break;
   case GL_GEQUAL:
      a = AC_atmode_agte;
      break;
   case GL_LEQUAL:
      a = AC_atmode_alte;
      break;
   case GL_GREATER:
      a = AC_atmode_agt;
      break;
   case GL_NOTEQUAL:
      a = AC_atmode_ane;
      break;
   case GL_EQUAL:
      a = AC_atmode_ae;
      break;
   case GL_ALWAYS:
      a = AC_atmode_noacmp;
      break;
   default:
      a = 0;
      break;
   }

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.alpha_func = a | MGA_FIELD( AC_atref, refByte );
}

static void updateBlendLogicOp(GLcontext *ctx)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   GLboolean logicOp = RGBA_LOGICOP_ENABLED(ctx);

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );

   mmesa->hw.blend_func_enable =
      (ctx->Color.BlendEnabled && !logicOp) ? ~0 : 0;

   FALLBACK( ctx, MGA_FALLBACK_BLEND,
             ctx->Color.BlendEnabled && !logicOp &&
             mmesa->hw.blend_func == (AC_src_src_alpha_sat | AC_dst_zero) );
}

static void mgaDDBlendEquationSeparate(GLcontext *ctx, 
				       GLenum modeRGB, GLenum modeA)
{
   assert( modeRGB == modeA );
   updateBlendLogicOp( ctx );
}

static void mgaDDBlendFuncSeparate( GLcontext *ctx, GLenum sfactorRGB,
				    GLenum dfactorRGB, GLenum sfactorA,
				    GLenum dfactorA )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   GLuint   src;
   GLuint   dst;

   switch (ctx->Color.BlendSrcRGB) {
   case GL_ZERO:
      src = AC_src_zero; break;
   case GL_SRC_ALPHA:
      src = AC_src_src_alpha; break;
   case GL_ONE:
   default:		/* never happens */
      src = AC_src_one; break;
   case GL_DST_COLOR:
      src = AC_src_dst_color; break;
   case GL_ONE_MINUS_DST_COLOR:
      src = AC_src_om_dst_color; break;
   case GL_ONE_MINUS_SRC_ALPHA:
      src = AC_src_om_src_alpha; break;
   case GL_DST_ALPHA:
      src = (ctx->Visual.alphaBits > 0)
	  ? AC_src_dst_alpha : AC_src_one;
      break;
   case GL_ONE_MINUS_DST_ALPHA:
      src = (ctx->Visual.alphaBits > 0)
	  ? AC_src_om_dst_alpha : AC_src_zero;
      break;
   case GL_SRC_ALPHA_SATURATE:
      src = (ctx->Visual.alphaBits > 0)
	  ? AC_src_src_alpha_sat : AC_src_zero;
      break;
   }

   switch (ctx->Color.BlendDstRGB) {
   case GL_SRC_ALPHA:
      dst = AC_dst_src_alpha; break;
   case GL_ONE_MINUS_SRC_ALPHA:
      dst = AC_dst_om_src_alpha; break;
   default:		/* never happens */
   case GL_ZERO:
      dst = AC_dst_zero; break;
   case GL_ONE:
      dst = AC_dst_one; break;
   case GL_SRC_COLOR:
      dst = AC_dst_src_color; break;
   case GL_ONE_MINUS_SRC_COLOR:
      dst = AC_dst_om_src_color; break;
   case GL_DST_ALPHA:
      dst = (ctx->Visual.alphaBits > 0)
	  ? AC_dst_dst_alpha : AC_dst_one;
      break;
   case GL_ONE_MINUS_DST_ALPHA:
      dst = (ctx->Visual.alphaBits > 0)
	  ? AC_dst_om_dst_alpha : AC_dst_zero;
      break;
   }

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.blend_func = (src | dst);

   FALLBACK( ctx, MGA_FALLBACK_BLEND,
             ctx->Color.BlendEnabled && !RGBA_LOGICOP_ENABLED(ctx) &&
             mmesa->hw.blend_func == (AC_src_src_alpha_sat | AC_dst_zero) );
}

/* =============================================================
 * Depth testing
 */

static void mgaDDDepthFunc(GLcontext *ctx, GLenum func)
{
   mgaContextPtr mmesa = MGA_CONTEXT( ctx );
   int zmode;

   switch (func) {
   case GL_NEVER:
      /* can't do this in h/w, we'll use a s/w fallback */
      FALLBACK (ctx, MGA_FALLBACK_DEPTH, ctx->Depth.Test);

      /* FALLTHROUGH */
   case GL_ALWAYS:
      zmode = DC_zmode_nozcmp; break;
   case GL_LESS:
      zmode = DC_zmode_zlt; break;
   case GL_LEQUAL:
      zmode = DC_zmode_zlte; break;
   case GL_EQUAL:
      zmode = DC_zmode_ze; break;
   case GL_GREATER:
      zmode = DC_zmode_zgt; break;
   case GL_GEQUAL:
      zmode = DC_zmode_zgte; break;
   case GL_NOTEQUAL:
      zmode = DC_zmode_zne; break;
   default:
      zmode = 0; break;
   }

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.zmode &= DC_zmode_MASK;
   mmesa->hw.zmode |= zmode;
}

static void mgaDDDepthMask(GLcontext *ctx, GLboolean flag)
{
   mgaContextPtr mmesa = MGA_CONTEXT( ctx );


   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.zmode &= DC_atype_MASK;
   mmesa->hw.zmode |= (flag) ? DC_atype_zi : DC_atype_i;
}


static void mgaDDClearDepth(GLcontext *ctx, GLclampd d)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   /* Select the Z depth.  The ~ is used because the _MASK values in the
    * MGA driver are used to mask OFF the selected bits.  In this case,
    * we want to mask off everything except the MA_zwidth bits.
    */
   switch (mmesa->setup.maccess & ~MA_zwidth_MASK) {
   case MA_zwidth_16: mmesa->ClearDepth = d * 0x0000ffff; break;
   case MA_zwidth_24: mmesa->ClearDepth = d * 0xffffff00; break;
   case MA_zwidth_32: mmesa->ClearDepth = d * 0xffffffff; break;
   default: return;
   }
}


/* =============================================================
 * Fog
 */


static void mgaDDFogfv(GLcontext *ctx, GLenum pname, const GLfloat *param)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   if (pname == GL_FOG_COLOR) {
      GLuint color = PACK_COLOR_888((GLubyte)(ctx->Fog.Color[0]*255.0F), 
				    (GLubyte)(ctx->Fog.Color[1]*255.0F), 
				    (GLubyte)(ctx->Fog.Color[2]*255.0F));

      MGA_STATECHANGE(mmesa, MGA_UPLOAD_CONTEXT);   
      mmesa->setup.fogcolor = color;
   }
}


/* =============================================================
 * Scissoring
 */


void mgaUpdateClipping(const GLcontext *ctx)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   if (mmesa->driDrawable)
   {
      int x1 = mmesa->driDrawable->x + ctx->Scissor.X;
      int y1 = mmesa->driDrawable->y + mmesa->driDrawable->h
	 - (ctx->Scissor.Y + ctx->Scissor.Height);
      int x2 = x1 + ctx->Scissor.Width;
      int y2 = y1 + ctx->Scissor.Height;

      if (x1 < 0) x1 = 0;
      if (y1 < 0) y1 = 0;
      if (x2 < 0) x2 = 0;
      if (y2 < 0) y2 = 0;

      mmesa->scissor_rect.x1 = x1;
      mmesa->scissor_rect.y1 = y1;
      mmesa->scissor_rect.x2 = x2;
      mmesa->scissor_rect.y2 = y2;

      mmesa->dirty |= MGA_UPLOAD_CLIPRECTS;
   }
}


static void mgaDDScissor( GLcontext *ctx, GLint x, GLint y,
			  GLsizei w, GLsizei h )
{
   if ( ctx->Scissor.Enabled ) {
      FLUSH_BATCH( MGA_CONTEXT(ctx) );	/* don't pipeline cliprect changes */
      mgaUpdateClipping( ctx );
   }
}


/* =============================================================
 * Culling
 */


#define _CULL_DISABLE 0
#define _CULL_NEGATIVE ((1<<11)|(1<<5)|(1<<16))
#define _CULL_POSITIVE (1<<11)

static void mgaDDCullFaceFrontFace(GLcontext *ctx, GLenum unused)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   if (ctx->Polygon.CullFlag && 
       ctx->Polygon.CullFaceMode != GL_FRONT_AND_BACK) 
   {
      mmesa->hw.cull = _CULL_NEGATIVE;

      if (ctx->Polygon.CullFaceMode == GL_FRONT)
	 mmesa->hw.cull ^= (_CULL_POSITIVE ^ _CULL_NEGATIVE);

      if (ctx->Polygon.FrontFace != GL_CCW)
	 mmesa->hw.cull ^= (_CULL_POSITIVE ^ _CULL_NEGATIVE);

      mmesa->hw.cull_dualtex = mmesa->hw.cull ^
	  (_CULL_POSITIVE ^ _CULL_NEGATIVE); /* warp bug? */
   }
   else {
      mmesa->hw.cull = _CULL_DISABLE;
      mmesa->hw.cull_dualtex = _CULL_DISABLE;
   }
}


/* =============================================================
 * Masks
 */

static void mgaDDColorMask(GLcontext *ctx, 
			   GLboolean r, GLboolean g, 
			   GLboolean b, GLboolean a )
{
   mgaContextPtr mmesa = MGA_CONTEXT( ctx );
   mgaScreenPrivate *mgaScreen = mmesa->mgaScreen;
   GLuint mask = mgaPackColor(mgaScreen->cpp,
			      ctx->Color.ColorMask[0][RCOMP],
			      ctx->Color.ColorMask[0][GCOMP],
			      ctx->Color.ColorMask[0][BCOMP],
			      ctx->Color.ColorMask[0][ACOMP]);

   if (mgaScreen->cpp == 2)
      mask = mask | (mask << 16);

   if (mmesa->setup.plnwt != mask) {
      MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
      mmesa->setup.plnwt = mask;      
   }
}


/* =============================================================
 * Polygon state
 */

static int mgaStipples[16] = {
   0xffff,
   0xa5a5,
   0x5a5a,
   0xa0a0,
   0x5050,
   0x0a0a,
   0x0505,
   0x8020,
   0x0401,
   0x1040,
   0x0208,
   0x0802,
   0x4010,
   0x0104,
   0x2080,
   0x0000
};

/**
 * The MGA supports a subset of possible 4x4 stipples natively, GL
 * wants 32x32.  Fortunately stipple is usually a repeating pattern.
 *
 * \param ctx GL rendering context to be affected
 * \param mask Pointer to the 32x32 stipple mask
 */

static void mgaDDPolygonStipple( GLcontext *ctx, const GLubyte *mask )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   const GLubyte *m = mask;
   GLubyte p[4];
   int i,j,k;
   int active = (ctx->Polygon.StippleFlag && 
		 mmesa->raster_primitive == GL_TRIANGLES);
   GLuint stipple;

   FLUSH_BATCH(mmesa);
   mmesa->haveHwStipple = 0;

   if (active) {
      mmesa->dirty |= MGA_UPLOAD_CONTEXT;
      mmesa->setup.dwgctl &= ~(0xf<<20);
   }

   p[0] = mask[0] & 0xf; p[0] |= p[0] << 4;
   p[1] = mask[4] & 0xf; p[1] |= p[1] << 4;
   p[2] = mask[8] & 0xf; p[2] |= p[2] << 4;
   p[3] = mask[12] & 0xf; p[3] |= p[3] << 4;

   for (k = 0 ; k < 8 ; k++)
      for (j = 0 ; j < 4; j++)
	 for (i = 0 ; i < 4 ; i++)
	    if (*m++ != p[j]) {
	       return;
	    }

   stipple = ( ((p[0] & 0xf) << 0) |
	       ((p[1] & 0xf) << 4) |
	       ((p[2] & 0xf) << 8) |
	       ((p[3] & 0xf) << 12) );

   for (i = 0 ; i < 16 ; i++)
      if (mgaStipples[i] == stipple) {
	 mmesa->poly_stipple = i<<20;
	 mmesa->haveHwStipple = 1;
	 break;
      }
   
   if (active) {
      mmesa->setup.dwgctl &= ~(0xf<<20);
      mmesa->setup.dwgctl |= mmesa->poly_stipple;
   }
}


/* =============================================================
 * Rendering attributes
 *
 * We really don't want to recalculate all this every time we bind a
 * texture.  These things shouldn't change all that often, so it makes
 * sense to break them out of the core texture state update routines.
 */

static void updateSpecularLighting( GLcontext *ctx )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   unsigned int specen;

   specen = NEED_SECONDARY_COLOR(ctx) ? TMC_specen_enable : 0;

   if ( specen != mmesa->hw.specen ) {
      mmesa->hw.specen = specen;
      mmesa->dirty |= MGA_UPLOAD_TEX0 | MGA_UPLOAD_TEX1;
   }
}


/* =============================================================
 * Materials
 */


static void mgaDDLightModelfv(GLcontext *ctx, GLenum pname,
			      const GLfloat *param)
{
   if (pname == GL_LIGHT_MODEL_COLOR_CONTROL) {
      FLUSH_BATCH( MGA_CONTEXT(ctx) );
      updateSpecularLighting( ctx );
   }
}


/* =============================================================
 * Stencil
 */


static void
mgaDDStencilFuncSeparate(GLcontext *ctx, GLenum face, GLenum func, GLint ref,
                         GLuint mask)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   GLuint  stencil;
   GLuint  stencilctl;

   stencil = MGA_FIELD( S_sref, ref ) | MGA_FIELD( S_smsk, mask );
   switch (func)
   {
   case GL_NEVER:
      stencilctl = SC_smode_snever;
      break;
   case GL_LESS:
      stencilctl = SC_smode_slt;
      break;
   case GL_LEQUAL:
      stencilctl = SC_smode_slte;
      break;
   case GL_GREATER:
      stencilctl = SC_smode_sgt;
      break;
   case GL_GEQUAL:
      stencilctl = SC_smode_sgte;
      break;
   case GL_NOTEQUAL:
      stencilctl = SC_smode_sne;
      break;
   case GL_EQUAL:
      stencilctl = SC_smode_se;
      break;
   case GL_ALWAYS:
   default:
      stencilctl = SC_smode_salways;
      break;
   }

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.stencil &= (S_sref_MASK & S_smsk_MASK);
   mmesa->hw.stencil |= stencil;
   mmesa->hw.stencilctl &= SC_smode_MASK;
   mmesa->hw.stencilctl |= stencilctl;
}

static void
mgaDDStencilMaskSeparate(GLcontext *ctx, GLenum face, GLuint mask)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.stencil &= S_swtmsk_MASK;
   mmesa->hw.stencil |= MGA_FIELD( S_swtmsk, mask );
}

static void
mgaDDStencilOpSeparate(GLcontext *ctx, GLenum face, GLenum fail, GLenum zfail,
                       GLenum zpass)
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   GLuint  stencilctl;

   stencilctl = 0;
   switch (ctx->Stencil.FailFunc[0])
   {
   case GL_KEEP:
      stencilctl |= SC_sfailop_keep;
      break;
   case GL_ZERO:
      stencilctl |= SC_sfailop_zero;
      break;
   case GL_REPLACE:
      stencilctl |= SC_sfailop_replace;
      break;
   case GL_INCR:
      stencilctl |= SC_sfailop_incrsat;
      break;
   case GL_DECR:
      stencilctl |= SC_sfailop_decrsat;
      break;
   case GL_INCR_WRAP:
      stencilctl |= SC_sfailop_incr;
      break;
   case GL_DECR_WRAP:
      stencilctl |= SC_sfailop_decr;
      break;
   case GL_INVERT:
      stencilctl |= SC_sfailop_invert;
      break;
   default:
      break;
   }

   switch (ctx->Stencil.ZFailFunc[0])
   {
   case GL_KEEP:
      stencilctl |= SC_szfailop_keep;
      break;
   case GL_ZERO:
      stencilctl |= SC_szfailop_zero;
      break;
   case GL_REPLACE:
      stencilctl |= SC_szfailop_replace;
      break;
   case GL_INCR:
      stencilctl |= SC_szfailop_incrsat;
      break;
   case GL_DECR:
      stencilctl |= SC_szfailop_decrsat;
      break;
   case GL_INCR_WRAP:
      stencilctl |= SC_szfailop_incr;
      break;
   case GL_DECR_WRAP:
      stencilctl |= SC_szfailop_decr;
      break;
   case GL_INVERT:
      stencilctl |= SC_szfailop_invert;
      break;
   default:
      break;
   }

   switch (ctx->Stencil.ZPassFunc[0])
   {
   case GL_KEEP:
      stencilctl |= SC_szpassop_keep;
      break;
   case GL_ZERO:
      stencilctl |= SC_szpassop_zero;
      break;
   case GL_REPLACE:
      stencilctl |= SC_szpassop_replace;
      break;
   case GL_INCR:
      stencilctl |= SC_szpassop_incrsat;
      break;
   case GL_DECR:
      stencilctl |= SC_szpassop_decrsat;
      break;
   case GL_INCR_WRAP:
      stencilctl |= SC_szpassop_incr;
      break;
   case GL_DECR_WRAP:
      stencilctl |= SC_szpassop_decr;
      break;
   case GL_INVERT:
      stencilctl |= SC_szpassop_invert;
      break;
   default:
      break;
   }

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.stencilctl &= (SC_sfailop_MASK & SC_szfailop_MASK 
			    & SC_szpassop_MASK);
   mmesa->hw.stencilctl |= stencilctl;
}


/* =============================================================
 * Window position and viewport transformation
 */

void mgaCalcViewport( GLcontext *ctx )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   const GLfloat *v = ctx->Viewport._WindowMap.m;
   GLfloat *m = mmesa->hw_viewport;

   /* See also mga_translate_vertex.
    */
   m[MAT_SX] =   v[MAT_SX];
   m[MAT_TX] =   v[MAT_TX] + mmesa->drawX + SUBPIXEL_X;
   m[MAT_SY] = - v[MAT_SY];
   m[MAT_TY] = - v[MAT_TY] + mmesa->driDrawable->h + mmesa->drawY + SUBPIXEL_Y;
   m[MAT_SZ] =   v[MAT_SZ] * mmesa->depth_scale;
   m[MAT_TZ] =   v[MAT_TZ] * mmesa->depth_scale;

   mmesa->SetupNewInputs = ~0;
}

static void mgaViewport( GLcontext *ctx, 
			  GLint x, GLint y, 
			  GLsizei width, GLsizei height )
{
   mgaCalcViewport( ctx );
}

static void mgaDepthRange( GLcontext *ctx, 
			    GLclampd nearval, GLclampd farval )
{
   mgaCalcViewport( ctx );
}


/* =============================================================
 * Miscellaneous
 */

static void mgaDDClearColor(GLcontext *ctx, 
			    const GLfloat color[4] )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);
   GLubyte c[4];
   CLAMPED_FLOAT_TO_UBYTE(c[0], color[0]);
   CLAMPED_FLOAT_TO_UBYTE(c[1], color[1]);
   CLAMPED_FLOAT_TO_UBYTE(c[2], color[2]);
   CLAMPED_FLOAT_TO_UBYTE(c[3], color[3]);

   mmesa->ClearColor = mgaPackColor( mmesa->mgaScreen->cpp,
				     c[0], c[1], c[2], c[3]);
}


/* Fallback to swrast for select and feedback.
 */
static void mgaRenderMode( GLcontext *ctx, GLenum mode )
{
   FALLBACK( ctx, MGA_FALLBACK_RENDERMODE, (mode != GL_RENDER) );
}


static void mgaDDLogicOp( GLcontext *ctx, GLenum opcode )
{
   mgaContextPtr mmesa = MGA_CONTEXT( ctx );

   MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
   mmesa->hw.rop = mgarop_NoBLK[ opcode & 0x0f ];
}


static void mga_set_cliprects(mgaContextPtr mmesa)
{
   __DRIdrawable *driDrawable = mmesa->driDrawable;

   if ((mmesa->draw_buffer != MGA_FRONT)
       || (driDrawable->numBackClipRects == 0)) {
      if (driDrawable->numClipRects == 0) {
	  static drm_clip_rect_t zeroareacliprect = {0,0,0,0};
	  mmesa->numClipRects = 1;
	  mmesa->pClipRects = &zeroareacliprect;
      } else {
	  mmesa->numClipRects = driDrawable->numClipRects;
	  mmesa->pClipRects = driDrawable->pClipRects;
      }
      mmesa->drawX = driDrawable->x;
      mmesa->drawY = driDrawable->y;
   } else {
      mmesa->numClipRects = driDrawable->numBackClipRects;
      mmesa->pClipRects = driDrawable->pBackClipRects;
      mmesa->drawX = driDrawable->backX;
      mmesa->drawY = driDrawable->backY;
   }

   mmesa->setup.dstorg = mmesa->drawOffset;
   mmesa->dirty |= MGA_UPLOAD_CONTEXT | MGA_UPLOAD_CLIPRECTS;
}


void mgaUpdateRects( mgaContextPtr mmesa, GLuint buffers )
{
   __DRIdrawable *const driDrawable = mmesa->driDrawable;
   __DRIdrawable *const driReadable = mmesa->driReadable;

   mmesa->dirty_cliprects = 0;	

   driUpdateFramebufferSize(mmesa->glCtx, driDrawable);
   if (driDrawable != driReadable) {
      driUpdateFramebufferSize(mmesa->glCtx, driReadable);
   }

   mga_set_cliprects(mmesa);

   mgaUpdateClipping( mmesa->glCtx );
   mgaCalcViewport( mmesa->glCtx );
}


static void mgaDDDrawBuffer(GLcontext *ctx, GLenum mode )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   FLUSH_BATCH( mmesa );

   if (ctx->DrawBuffer->_NumColorDrawBuffers != 1) {
      /* GL_NONE or GL_FRONT_AND_BACK or stereo left&right, etc */
      FALLBACK( ctx, MGA_FALLBACK_DRAW_BUFFER, GL_TRUE );
      return;
   }

   switch ( ctx->DrawBuffer->_ColorDrawBufferIndexes[0] ) {
   case BUFFER_FRONT_LEFT:
      mmesa->setup.dstorg = mmesa->mgaScreen->frontOffset;
      mmesa->draw_buffer = MGA_FRONT;
      break;
   case BUFFER_BACK_LEFT:
      mmesa->setup.dstorg = mmesa->mgaScreen->backOffset;
      mmesa->draw_buffer = MGA_BACK;
      break;
   default:
      FALLBACK( ctx, MGA_FALLBACK_DRAW_BUFFER, GL_TRUE );
      return;
   }

   mmesa->dirty |= MGA_UPLOAD_CONTEXT;
   mga_set_cliprects(mmesa);
   FALLBACK(ctx, MGA_FALLBACK_DRAW_BUFFER, GL_FALSE);
}


static void mgaDDReadBuffer(GLcontext *ctx, GLenum mode )
{
   /* nothing, until we implement h/w glRead/CopyPixels or CopyTexImage */
}


/* =============================================================
 * State enable/disable
 */


static void mgaDDEnable(GLcontext *ctx, GLenum cap, GLboolean state)
{
   mgaContextPtr mmesa = MGA_CONTEXT( ctx );

   switch(cap) {
   case GL_DITHER:
      MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
      if (!ctx->Color.DitherFlag)
	 mmesa->setup.maccess |= MA_nodither_enable;
      else
	 mmesa->setup.maccess &= ~MA_nodither_enable;
      break;
   case GL_LIGHTING:
   case GL_COLOR_SUM_EXT:
      FLUSH_BATCH( mmesa );
      updateSpecularLighting( ctx );
      break;
   case GL_ALPHA_TEST:
      MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
      mmesa->hw.alpha_func_enable = (state) ? ~0 : 0;
      break;
   case GL_DEPTH_TEST:
      MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
      FALLBACK (ctx, MGA_FALLBACK_DEPTH,
		ctx->Depth.Func == GL_NEVER && ctx->Depth.Test);
      break;

   case GL_SCISSOR_TEST:
      FLUSH_BATCH( mmesa );
      mmesa->scissor = state;
      mgaUpdateClipping( ctx );
      break;

   case GL_FOG:
      MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
      if (ctx->Fog.Enabled) 
	 mmesa->setup.maccess |= MA_fogen_enable;
      else
	 mmesa->setup.maccess &= ~MA_fogen_enable;
      break;
   case GL_CULL_FACE:
      mgaDDCullFaceFrontFace( ctx, 0 );
      break;
   case GL_TEXTURE_1D:
   case GL_TEXTURE_2D:
   case GL_TEXTURE_3D:
      break;
   case GL_POLYGON_STIPPLE:
      if (mmesa->haveHwStipple && mmesa->raster_primitive == GL_TRIANGLES) {
	 MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
	 mmesa->setup.dwgctl &= ~(0xf<<20);
	 if (state)
	    mmesa->setup.dwgctl |= mmesa->poly_stipple;
      }
      break;

   case GL_BLEND:
   case GL_COLOR_LOGIC_OP:
      updateBlendLogicOp( ctx );
      break;

   case GL_STENCIL_TEST:
      MGA_STATECHANGE( mmesa, MGA_UPLOAD_CONTEXT );
      if (mmesa->hw_stencil) {
	 mmesa->hw.stencil_enable = ( state ) ? ~0 : 0;
      }
      else {
	 FALLBACK( ctx, MGA_FALLBACK_STENCIL, state );
      }
   default:
      break;
   }
}


/* =============================================================
 */

static void mgaDDPrintDirty( const char *msg, GLuint state )
{
   fprintf(stderr, "%s (0x%03x): %s%s%s%s%s%s%s\n",
	   msg,
	   (unsigned int) state,
	   (state & MGA_WAIT_AGE)          ? "wait-age " : "",
	   (state & MGA_UPLOAD_TEX0IMAGE)  ? "upload-tex0-img " : "",
	   (state & MGA_UPLOAD_TEX1IMAGE)  ? "upload-tex1-img " : "",
	   (state & MGA_UPLOAD_CONTEXT)    ? "upload-ctx " : "",
	   (state & MGA_UPLOAD_TEX0)       ? "upload-tex0 " : "",
	   (state & MGA_UPLOAD_TEX1)       ? "upload-tex1 " : "",
	   (state & MGA_UPLOAD_PIPE)       ? "upload-pipe " : ""
      );
}

/* Push the state into the sarea and/or texture memory.
 */
void mgaEmitHwStateLocked( mgaContextPtr mmesa )
{
   drm_mga_sarea_t *sarea = mmesa->sarea;
   GLcontext * ctx = mmesa->glCtx;

   if (MGA_DEBUG & DEBUG_VERBOSE_MSG)
      mgaDDPrintDirty( __FUNCTION__, mmesa->dirty );

   if (mmesa->dirty & MGA_UPLOAD_CONTEXT) {
      mmesa->setup.wflag = _CULL_DISABLE;
      if (mmesa->raster_primitive == GL_TRIANGLES) {
	 if ((ctx->Texture.Unit[0]._ReallyEnabled == TEXTURE_2D_BIT &&
	      ctx->Texture.Unit[1]._ReallyEnabled == TEXTURE_2D_BIT)) {
	    mmesa->setup.wflag = mmesa->hw.cull_dualtex;
	 }
	 else {
	    mmesa->setup.wflag = mmesa->hw.cull;
	 }
      }

      mmesa->setup.stencil = mmesa->hw.stencil 
	  & mmesa->hw.stencil_enable;
      mmesa->setup.stencilctl = mmesa->hw.stencilctl
	  & mmesa->hw.stencil_enable;

      /* If depth testing is not enabled, then use the no Z-compare / no
       * Z-write mode.  Otherwise, use whatever is set in hw.zmode.
       */
      mmesa->setup.dwgctl &= (DC_zmode_MASK & DC_atype_MASK);
      mmesa->setup.dwgctl |= (ctx->Depth.Test)
	  ? mmesa->hw.zmode : (DC_zmode_nozcmp | DC_atype_i);

      mmesa->setup.dwgctl &= DC_bop_MASK;
      mmesa->setup.dwgctl |= RGBA_LOGICOP_ENABLED(ctx)
	  ? mmesa->hw.rop : mgarop_NoBLK[ GL_COPY & 0x0f ];

      mmesa->setup.alphactrl &= AC_src_MASK & AC_dst_MASK & AC_atmode_MASK
	 & AC_atref_MASK & AC_alphasel_MASK;
      mmesa->setup.alphactrl |= 
	 (mmesa->hw.alpha_func & mmesa->hw.alpha_func_enable) |
	 (mmesa->hw.blend_func & mmesa->hw.blend_func_enable) |
	 ((AC_src_one | AC_dst_zero) & ~mmesa->hw.blend_func_enable) |
	 mmesa->hw.alpha_sel;

      memcpy( &sarea->context_state, &mmesa->setup, sizeof(mmesa->setup));
   }

   if ((mmesa->dirty & MGA_UPLOAD_TEX0) && mmesa->CurrentTexObj[0]) {
      memcpy(&sarea->tex_state[0],
	     &mmesa->CurrentTexObj[0]->setup,
	     sizeof(sarea->tex_state[0]));
   }

   if ((mmesa->dirty & MGA_UPLOAD_TEX1) && mmesa->CurrentTexObj[1]) {
      memcpy(&sarea->tex_state[1],
	     &mmesa->CurrentTexObj[1]->setup,
	     sizeof(sarea->tex_state[1]));
   }

   if (mmesa->dirty & (MGA_UPLOAD_TEX0 | MGA_UPLOAD_TEX1)) {
      sarea->tex_state[0].texctl2 &= ~TMC_specen_enable;
      sarea->tex_state[1].texctl2 &= ~TMC_specen_enable;
      sarea->tex_state[0].texctl2 |= mmesa->hw.specen;
      sarea->tex_state[1].texctl2 |= mmesa->hw.specen;
   }

   if (mmesa->dirty & MGA_UPLOAD_PIPE) {
/*        mmesa->sarea->wacceptseq = mmesa->hw_primitive; */
      mmesa->sarea->warp_pipe = mmesa->vertex_format;
      mmesa->sarea->vertsize = mmesa->vertex_size;
   }

   mmesa->sarea->dirty |= mmesa->dirty;
   mmesa->dirty &= MGA_UPLOAD_CLIPRECTS;
}

/* =============================================================
 */


static void mgaDDValidateState( GLcontext *ctx )
{
   mgaContextPtr mmesa = MGA_CONTEXT( ctx );

   FLUSH_BATCH( mmesa );

   if (mmesa->NewGLState & _NEW_TEXTURE) {
      mgaUpdateTextureState(ctx);
   }

   if (!mmesa->Fallback) {
      if (mmesa->NewGLState & _MGA_NEW_RASTERSETUP) {
         mgaChooseVertexState( ctx );
      }

      if (mmesa->NewGLState & _MGA_NEW_RENDERSTATE) {
         mgaChooseRenderState( ctx );
      }
   }

   mmesa->NewGLState = 0;
}


static void mgaDDInvalidateState( GLcontext *ctx, GLuint new_state )
{
   _swrast_InvalidateState( ctx, new_state );
   _swsetup_InvalidateState( ctx, new_state );
   _vbo_InvalidateState( ctx, new_state );
   _tnl_InvalidateState( ctx, new_state );
   MGA_CONTEXT(ctx)->NewGLState |= new_state;
}


static void mgaRunPipeline( GLcontext *ctx )
{
   mgaContextPtr mmesa = MGA_CONTEXT(ctx);

   if (mmesa->NewGLState) {
      mgaDDValidateState( ctx );
   }

   if (mmesa->dirty) {
       mgaEmitHwStateLocked( mmesa );
   }

   _tnl_run_pipeline( ctx );
}


void mgaInitState( mgaContextPtr mmesa )
{
   mgaScreenPrivate *mgaScreen = mmesa->mgaScreen;
   GLcontext *ctx = mmesa->glCtx;

   if (ctx->Visual.doubleBufferMode) {
      /* use back buffer by default */
      mmesa->draw_buffer = MGA_BACK;
      mmesa->drawOffset = mmesa->mgaScreen->backOffset;
      mmesa->readOffset = mmesa->mgaScreen->backOffset;
      mmesa->setup.dstorg = mgaScreen->backOffset;
   } else {
      /* use front buffer by default */
      mmesa->draw_buffer = MGA_FRONT;
      mmesa->drawOffset = mmesa->mgaScreen->frontOffset;
      mmesa->readOffset = mmesa->mgaScreen->frontOffset;
      mmesa->setup.dstorg = mgaScreen->frontOffset;
   }

   mmesa->setup.maccess = (MA_memreset_disable |
			   MA_fogen_disable |
			   MA_tlutload_disable |
			   MA_nodither_disable |
			   MA_dit555_disable);
   if (driQueryOptioni (&mmesa->optionCache, "color_reduction") !=
       DRI_CONF_COLOR_REDUCTION_DITHER)
      mmesa->setup.maccess |= MA_nodither_enable;

   switch (mmesa->mgaScreen->cpp) {
   case 2:
      mmesa->setup.maccess |= MA_pwidth_16;
      break;
   case 4:
      mmesa->setup.maccess |= MA_pwidth_32;
      break;
   default:
      fprintf( stderr, "Error: unknown cpp %d, exiting...\n",
	       mmesa->mgaScreen->cpp );
      exit( 1 );
   }

   switch (mmesa->glCtx->Visual.depthBits) {
   case 16:
      mmesa->setup.maccess |= MA_zwidth_16;
      break;
   case 24:
      mmesa->setup.maccess |= MA_zwidth_24;
      break;
   case 32:
      mmesa->setup.maccess |= MA_zwidth_32;
      break;
   }

   mmesa->hw.blend_func = AC_src_one | AC_dst_zero;
   mmesa->hw.blend_func_enable = 0;
   mmesa->hw.alpha_func = AC_atmode_noacmp | MGA_FIELD( AC_atref, 0x00 );
   mmesa->hw.alpha_func_enable = 0;
   mmesa->hw.rop = mgarop_NoBLK[ GL_COPY & 0x0f ];
   mmesa->hw.zmode = DC_zmode_zlt | DC_atype_zi;
   mmesa->hw.stencil = MGA_FIELD( S_sref, 0x00) | MGA_FIELD( S_smsk, 0xff ) |
      MGA_FIELD( S_swtmsk, 0xff );
   mmesa->hw.stencilctl = SC_smode_salways | SC_sfailop_keep 
      | SC_szfailop_keep | SC_szpassop_keep;
   mmesa->hw.stencil_enable = 0;
   mmesa->hw.cull = _CULL_DISABLE;
   mmesa->hw.cull_dualtex = _CULL_DISABLE;
   mmesa->hw.specen = 0;
   mmesa->hw.alpha_sel = AC_alphasel_diffused;

   mmesa->setup.dwgctl = (DC_opcod_trap |
			  DC_linear_xy |
			  DC_solid_disable |
			  DC_arzero_disable |
			  DC_sgnzero_disable |
			  DC_shftzero_enable |
			  MGA_FIELD( DC_bop, 0xC ) |
			  MGA_FIELD( DC_trans, 0x0 ) |
			  DC_bltmod_bmonolef |
			  DC_pattern_disable |
			  DC_transc_disable |
			  DC_clipdis_disable);

   mmesa->setup.plnwt = ~0;
   mmesa->setup.alphactrl = (AC_amode_alpha_channel |
			     AC_astipple_disable |
			     AC_aten_disable);

   mmesa->setup.fogcolor = PACK_COLOR_888((GLubyte)(ctx->Fog.Color[0]*255.0F),
					  (GLubyte)(ctx->Fog.Color[1]*255.0F),
					  (GLubyte)(ctx->Fog.Color[2]*255.0F));

   mmesa->setup.wflag = 0;
   mmesa->setup.tdualstage0 = 0;
   mmesa->setup.tdualstage1 = 0;
   mmesa->setup.fcol = 0;
   mmesa->dirty |= MGA_UPLOAD_CONTEXT;

   mmesa->envcolor[0] = 0;
   mmesa->envcolor[1] = 0;
}


void mgaDDInitStateFuncs( GLcontext *ctx )
{
   ctx->Driver.UpdateState = mgaDDInvalidateState;
   ctx->Driver.Enable = mgaDDEnable;
   ctx->Driver.LightModelfv = mgaDDLightModelfv;
   ctx->Driver.AlphaFunc = mgaDDAlphaFunc;
   ctx->Driver.BlendEquationSeparate = mgaDDBlendEquationSeparate;
   ctx->Driver.BlendFuncSeparate = mgaDDBlendFuncSeparate;
   ctx->Driver.DepthFunc = mgaDDDepthFunc;
   ctx->Driver.DepthMask = mgaDDDepthMask;
   ctx->Driver.Fogfv = mgaDDFogfv;
   ctx->Driver.Scissor = mgaDDScissor;
   ctx->Driver.CullFace = mgaDDCullFaceFrontFace;
   ctx->Driver.FrontFace = mgaDDCullFaceFrontFace;
   ctx->Driver.ColorMask = mgaDDColorMask;

   ctx->Driver.DrawBuffer = mgaDDDrawBuffer;
   ctx->Driver.ReadBuffer = mgaDDReadBuffer;
   ctx->Driver.ClearColor = mgaDDClearColor;
   ctx->Driver.ClearDepth = mgaDDClearDepth;
   ctx->Driver.LogicOpcode = mgaDDLogicOp;

   ctx->Driver.PolygonStipple = mgaDDPolygonStipple;

   ctx->Driver.StencilFuncSeparate = mgaDDStencilFuncSeparate;
   ctx->Driver.StencilMaskSeparate = mgaDDStencilMaskSeparate;
   ctx->Driver.StencilOpSeparate = mgaDDStencilOpSeparate;

   ctx->Driver.DepthRange = mgaDepthRange;
   ctx->Driver.Viewport = mgaViewport;
   ctx->Driver.RenderMode = mgaRenderMode;

   TNL_CONTEXT(ctx)->Driver.RunPipeline = mgaRunPipeline;
}
