
#include <stdio.h>

#include "main/glheader.h"
#include "main/context.h"
#include "main/macros.h"
#include "main/dd.h"
#include "main/colormac.h"
#include "swrast/swrast.h"
#include "tnl/tnl.h"
#include "tnl/t_pipeline.h"
#include "vbo/vbo.h"
#include "swrast_setup/swrast_setup.h"

#include "texmem.h"

#include "i810screen.h"
#include "i810_dri.h"

#include "i810context.h"
#include "i810state.h"
#include "i810tex.h"
#include "i810ioctl.h"


static INLINE GLuint i810PackColor(GLuint format,
				       GLubyte r, GLubyte g,
				       GLubyte b, GLubyte a)
{

   if (I810_DEBUG&DEBUG_DRI)
      fprintf(stderr, "%s\n", __FUNCTION__);

   switch (format) {
   case DV_PF_555:
      return PACK_COLOR_1555( a, r, g, b );
   case DV_PF_565:
      return PACK_COLOR_565( r, g, b );
   default:
      fprintf(stderr, "unknown format %d\n", (int)format);
      return 0;
   }
}


static void i810AlphaFunc(GLcontext *ctx, GLenum func, GLfloat ref)
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   GLuint a = (ZA_UPDATE_ALPHAFUNC|ZA_UPDATE_ALPHAREF);
   GLubyte refByte;

   CLAMPED_FLOAT_TO_UBYTE(refByte, ref);

   switch (ctx->Color.AlphaFunc) {
   case GL_NEVER:    a |= ZA_ALPHA_NEVER;    break;
   case GL_LESS:     a |= ZA_ALPHA_LESS;     break;
   case GL_GEQUAL:   a |= ZA_ALPHA_GEQUAL;   break;
   case GL_LEQUAL:   a |= ZA_ALPHA_LEQUAL;   break;
   case GL_GREATER:  a |= ZA_ALPHA_GREATER;  break;
   case GL_NOTEQUAL: a |= ZA_ALPHA_NOTEQUAL; break;
   case GL_EQUAL:    a |= ZA_ALPHA_EQUAL;    break;
   case GL_ALWAYS:   a |= ZA_ALPHA_ALWAYS;   break;
   default: return;
   }

   a |= ((refByte & 0xfc) << ZA_ALPHAREF_SHIFT);

   I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
   imesa->Setup[I810_CTXREG_ZA] &= ~(ZA_ALPHA_MASK|ZA_ALPHAREF_MASK);
   imesa->Setup[I810_CTXREG_ZA] |= a;
}

static void i810BlendEquationSeparate(GLcontext *ctx,
				      GLenum modeRGB, GLenum modeA)
{
   assert( modeRGB == modeA );

   /* Can only do GL_ADD equation in hardware */
   FALLBACK( I810_CONTEXT(ctx), I810_FALLBACK_BLEND_EQ, 
	     modeRGB != GL_FUNC_ADD);

   /* BlendEquation sets ColorLogicOpEnabled in an unexpected
    * manner.
    */
   FALLBACK( I810_CONTEXT(ctx), I810_FALLBACK_LOGICOP,
	     (ctx->Color.ColorLogicOpEnabled &&
	      ctx->Color.LogicOp != GL_COPY));
}

static void i810BlendFuncSeparate( GLcontext *ctx, GLenum sfactorRGB,
				     GLenum dfactorRGB, GLenum sfactorA,
				     GLenum dfactorA )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   GLuint a = SDM_UPDATE_SRC_BLEND | SDM_UPDATE_DST_BLEND;
   GLboolean fallback = GL_FALSE;

   switch (ctx->Color.BlendSrcRGB) {
   case GL_ZERO:                a |= SDM_SRC_ZERO; break;
   case GL_ONE:                 a |= SDM_SRC_ONE; break;
   case GL_SRC_COLOR:           a |= SDM_SRC_SRC_COLOR; break;
   case GL_ONE_MINUS_SRC_COLOR: a |= SDM_SRC_INV_SRC_COLOR; break;
   case GL_SRC_ALPHA:           a |= SDM_SRC_SRC_ALPHA; break;
   case GL_ONE_MINUS_SRC_ALPHA: a |= SDM_SRC_INV_SRC_ALPHA; break;
   case GL_DST_ALPHA:           a |= SDM_SRC_ONE; break;
   case GL_ONE_MINUS_DST_ALPHA: a |= SDM_SRC_ZERO; break;
   case GL_DST_COLOR:           a |= SDM_SRC_DST_COLOR; break;
   case GL_ONE_MINUS_DST_COLOR: a |= SDM_SRC_INV_DST_COLOR; break;

   /* (f, f, f, 1), f = min(As, 1 - Ad) = min(As, 1 - 1) = 0
    * So (f, f, f, 1) = (0, 0, 0, 1).  Since there is no destination alpha and
    * the only supported alpha operation is GL_FUNC_ADD, the result modulating
    * the source alpha with the alpha factor is largely irrelevant.
    */
   case GL_SRC_ALPHA_SATURATE:  a |= SDM_SRC_ZERO; break;

   case GL_CONSTANT_COLOR:
   case GL_ONE_MINUS_CONSTANT_COLOR:
   case GL_CONSTANT_ALPHA:
   case GL_ONE_MINUS_CONSTANT_ALPHA:
      fallback = GL_TRUE;
      break;
   default:
      return;
   }

   switch (ctx->Color.BlendDstRGB) {
   case GL_ZERO:                a |= SDM_DST_ZERO; break;
   case GL_ONE:                 a |= SDM_DST_ONE; break;
   case GL_SRC_COLOR:           a |= SDM_DST_SRC_COLOR; break;
   case GL_ONE_MINUS_SRC_COLOR: a |= SDM_DST_INV_SRC_COLOR; break;
   case GL_SRC_ALPHA:           a |= SDM_DST_SRC_ALPHA; break;
   case GL_ONE_MINUS_SRC_ALPHA: a |= SDM_DST_INV_SRC_ALPHA; break;
   case GL_DST_ALPHA:           a |= SDM_DST_ONE; break;
   case GL_ONE_MINUS_DST_ALPHA: a |= SDM_DST_ZERO; break;
   case GL_DST_COLOR:           a |= SDM_DST_DST_COLOR; break;
   case GL_ONE_MINUS_DST_COLOR: a |= SDM_DST_INV_DST_COLOR; break;

   case GL_CONSTANT_COLOR:
   case GL_ONE_MINUS_CONSTANT_COLOR:
   case GL_CONSTANT_ALPHA:
   case GL_ONE_MINUS_CONSTANT_ALPHA:
      fallback = GL_TRUE;
      break;
   default:
      return;
   }

   FALLBACK( imesa, I810_FALLBACK_BLEND_FUNC, fallback);
   if (!fallback) {
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_SDM] &= ~(SDM_SRC_MASK|SDM_DST_MASK);
      imesa->Setup[I810_CTXREG_SDM] |= a;
   }
}



static void i810DepthFunc(GLcontext *ctx, GLenum func)
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   int zmode;

   switch(func)  {
   case GL_NEVER: zmode = LCS_Z_NEVER; break;
   case GL_ALWAYS: zmode = LCS_Z_ALWAYS; break;
   case GL_LESS: zmode = LCS_Z_LESS; break;
   case GL_LEQUAL: zmode = LCS_Z_LEQUAL; break;
   case GL_EQUAL: zmode = LCS_Z_EQUAL; break;
   case GL_GREATER: zmode = LCS_Z_GREATER; break;
   case GL_GEQUAL: zmode = LCS_Z_GEQUAL; break;
   case GL_NOTEQUAL: zmode = LCS_Z_NOTEQUAL; break;
   default: return;
   }

   I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
   imesa->Setup[I810_CTXREG_LCS] &= ~LCS_Z_MASK;
   imesa->Setup[I810_CTXREG_LCS] |= zmode;
}

static void i810DepthMask(GLcontext *ctx, GLboolean flag)
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   I810_STATECHANGE(imesa, I810_UPLOAD_CTX);

   if (flag)
      imesa->Setup[I810_CTXREG_B2] |= B2_ZB_WRITE_ENABLE;
   else
      imesa->Setup[I810_CTXREG_B2] &= ~B2_ZB_WRITE_ENABLE;
}


/* =============================================================
 * Polygon stipple
 *
 * The i810 supports a 4x4 stipple natively, GL wants 32x32.
 * Fortunately stipple is usually a repeating pattern.
 */
static void i810PolygonStipple( GLcontext *ctx, const GLubyte *mask )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   const GLubyte *m = mask;
   GLubyte p[4];
   int i,j,k;
   int active = (ctx->Polygon.StippleFlag &&
		 imesa->reduced_primitive == GL_TRIANGLES);
   GLuint newMask;

   if (active) {
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_ST1] &= ~ST1_ENABLE;
   }

   p[0] = mask[12] & 0xf; p[0] |= p[0] << 4;
   p[1] = mask[8] & 0xf; p[1] |= p[1] << 4;
   p[2] = mask[4] & 0xf; p[2] |= p[2] << 4;
   p[3] = mask[0] & 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]) {
	       imesa->stipple_in_hw = 0;
	       return;
	    }

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

   if (newMask == 0xffff) {
      /* this is needed to make conform pass */
      imesa->stipple_in_hw = 0;
      return;
   }

   imesa->Setup[I810_CTXREG_ST1] &= ~0xffff;
   imesa->Setup[I810_CTXREG_ST1] |= newMask;
   imesa->stipple_in_hw = 1;

   if (active)
      imesa->Setup[I810_CTXREG_ST1] |= ST1_ENABLE;
}



/* =============================================================
 * Hardware clipping
 */


static void i810Scissor( GLcontext *ctx, GLint x, GLint y,
			 GLsizei w, GLsizei h )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);

   if (ctx->Scissor.Enabled) {
      I810_FIREVERTICES(imesa);	/* don't pipeline cliprect changes */
      imesa->upload_cliprects = GL_TRUE;
   }

   imesa->scissor_rect.x1 = x;
   imesa->scissor_rect.y1 = imesa->driDrawable->h - (y + h);
   imesa->scissor_rect.x2 = x + w;
   imesa->scissor_rect.y2 = imesa->driDrawable->h - y;
}


static void i810LogicOp( GLcontext *ctx, GLenum opcode )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   FALLBACK( imesa, I810_FALLBACK_LOGICOP,
	     (ctx->Color.ColorLogicOpEnabled && opcode != GL_COPY) );
}

/* Fallback to swrast for select and feedback.
 */
static void i810RenderMode( GLcontext *ctx, GLenum mode )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   FALLBACK( imesa, I810_FALLBACK_RENDERMODE, (mode != GL_RENDER) );
}


void i810DrawBuffer(GLcontext *ctx, GLenum mode )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   int front = 0;
  
   if (ctx->DrawBuffer->_NumColorDrawBuffers != 1) {
      /* GL_NONE or GL_FRONT_AND_BACK or stereo left&right, etc */
      FALLBACK( imesa, I810_FALLBACK_DRAW_BUFFER, GL_TRUE );
      return;
   }

   switch ( ctx->DrawBuffer->_ColorDrawBufferIndexes[0]) {
   case BUFFER_FRONT_LEFT:
     front = 1;
     break;
   case BUFFER_BACK_LEFT:
     front = 0;
     break;
   default:
      FALLBACK( imesa, I810_FALLBACK_DRAW_BUFFER, GL_TRUE );
      return;
   }

   if ( imesa->sarea->pf_current_page == 1 ) 
     front ^= 1;
 
   FALLBACK( imesa, I810_FALLBACK_DRAW_BUFFER, GL_FALSE );
   I810_FIREVERTICES(imesa);
   I810_STATECHANGE(imesa, I810_UPLOAD_BUFFERS);

   if (front)
   {
     imesa->BufferSetup[I810_DESTREG_DI1] = (imesa->i810Screen->fbOffset |
					     imesa->i810Screen->backPitchBits);
     i810XMesaSetFrontClipRects( imesa );
   }
   else
   {
     imesa->BufferSetup[I810_DESTREG_DI1] = (imesa->i810Screen->backOffset |
					     imesa->i810Screen->backPitchBits);
     i810XMesaSetBackClipRects( imesa );
   }
}


static void i810ReadBuffer(GLcontext *ctx, GLenum mode )
{
   /* XXX anything? */
}


static void i810ClearColor(GLcontext *ctx, const GLfloat color[4] )
{
   i810ContextPtr imesa = I810_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]);
   imesa->ClearColor = i810PackColor( imesa->i810Screen->fbFormat,
				      c[0], c[1], c[2], c[3] );
}


/* =============================================================
 * Culling - the i810 isn't quite as clean here as the rest of
 *           its interfaces, but it's not bad.
 */
static void i810CullFaceFrontFace(GLcontext *ctx, GLenum unused)
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   GLuint mode = LCS_CULL_BOTH;

   if (ctx->Polygon.CullFaceMode != GL_FRONT_AND_BACK) {
      mode = LCS_CULL_CW;
      if (ctx->Polygon.CullFaceMode == GL_FRONT)
	 mode ^= (LCS_CULL_CW ^ LCS_CULL_CCW);
      if (ctx->Polygon.FrontFace != GL_CCW)
	 mode ^= (LCS_CULL_CW ^ LCS_CULL_CCW);
   }

   imesa->LcsCullMode = mode;

   if (ctx->Polygon.CullFlag)
   {
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_LCS] &= ~LCS_CULL_MASK;
      imesa->Setup[I810_CTXREG_LCS] |= mode;
   }
}


static void i810LineWidth( GLcontext *ctx, GLfloat widthf )
{
   i810ContextPtr imesa = I810_CONTEXT( ctx );
   /* AA, non-AA limits are same */
   const int width = (int) CLAMP(ctx->Line.Width,
                                 ctx->Const.MinLineWidth,
                                 ctx->Const.MaxLineWidth);

   imesa->LcsLineWidth = 0;
   if (width & 1) imesa->LcsLineWidth |= LCS_LINEWIDTH_1_0;
   if (width & 2) imesa->LcsLineWidth |= LCS_LINEWIDTH_2_0;

   if (imesa->reduced_primitive == GL_LINES) {
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_LCS] &= ~LCS_LINEWIDTH_3_0;
      imesa->Setup[I810_CTXREG_LCS] |= imesa->LcsLineWidth;
   }
}

static void i810PointSize( GLcontext *ctx, GLfloat sz )
{
   i810ContextPtr imesa = I810_CONTEXT( ctx );
   /* AA, non-AA limits are same */
   const int size = (int) CLAMP(ctx->Point.Size,
                                ctx->Const.MinPointSize,
                                ctx->Const.MaxPointSize);

   imesa->LcsPointSize = 0;
   if (size & 1) imesa->LcsPointSize |= LCS_LINEWIDTH_1_0;
   if (size & 2) imesa->LcsPointSize |= LCS_LINEWIDTH_2_0;

   if (imesa->reduced_primitive == GL_POINTS) {
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_LCS] &= ~LCS_LINEWIDTH_3_0;
      imesa->Setup[I810_CTXREG_LCS] |= imesa->LcsPointSize;
   }
}

/* =============================================================
 * Color masks
 */

static void i810ColorMask(GLcontext *ctx,
			  GLboolean r, GLboolean g,
			  GLboolean b, GLboolean a )
{
   i810ContextPtr imesa = I810_CONTEXT( ctx );
   GLuint tmp = 0;

   if (r && g && b) {
      tmp = imesa->Setup[I810_CTXREG_B2] | B2_FB_WRITE_ENABLE;
      FALLBACK( imesa, I810_FALLBACK_COLORMASK, GL_FALSE );
   } else if (!r && !g && !b) {
      tmp = imesa->Setup[I810_CTXREG_B2] & ~B2_FB_WRITE_ENABLE;
      FALLBACK( imesa, I810_FALLBACK_COLORMASK, GL_FALSE );
   } else {
      FALLBACK( imesa, I810_FALLBACK_COLORMASK, GL_TRUE );
      return;
   }

   if (tmp != imesa->Setup[I810_CTXREG_B2]) {
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_B2] = tmp;
      imesa->dirty |= I810_UPLOAD_CTX;
   }
}

/* Seperate specular not fully implemented on the i810.
 */
static void i810LightModelfv(GLcontext *ctx, GLenum pname,
			       const GLfloat *param)
{
   if (pname == GL_LIGHT_MODEL_COLOR_CONTROL)
   {
      i810ContextPtr imesa = I810_CONTEXT( ctx );
      FALLBACK( imesa, I810_FALLBACK_SPECULAR,
		(ctx->Light.Enabled &&
		 ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR));
   }
}

/* But the 815 has it...
 */
static void i810LightModelfv_i815(GLcontext *ctx, GLenum pname,
				    const GLfloat *param)
{
   if (pname == GL_LIGHT_MODEL_COLOR_CONTROL)
   {
      i810ContextPtr imesa = I810_CONTEXT( ctx );

      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      if (ctx->Light.Model.ColorControl == GL_SEPARATE_SPECULAR_COLOR)
	 imesa->Setup[I810_CTXREG_B1] |= B1_SPEC_ENABLE;
      else
	 imesa->Setup[I810_CTXREG_B1] &= ~B1_SPEC_ENABLE;
   }
}

/* In Mesa 3.5 we can reliably do native flatshading.
 */
static void i810ShadeModel(GLcontext *ctx, GLenum mode)
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
   if (mode == GL_FLAT)
      imesa->Setup[I810_CTXREG_LCS] |= LCS_INTERP_FLAT;
   else
      imesa->Setup[I810_CTXREG_LCS] &= ~LCS_INTERP_FLAT;
}



/* =============================================================
 * Fog
 */
static void i810Fogfv(GLcontext *ctx, GLenum pname, const GLfloat *param)
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);

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

      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_FOG] = ((GFX_OP_FOG_COLOR | color) &
				      ~FOG_RESERVED_MASK);
   }
}


/* =============================================================
 */
static void i810Enable(GLcontext *ctx, GLenum cap, GLboolean state)
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);

   switch(cap) {
   case GL_ALPHA_TEST:
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_B1] &= ~B1_ALPHA_TEST_ENABLE;
      if (state)
	 imesa->Setup[I810_CTXREG_B1] |= B1_ALPHA_TEST_ENABLE;
      break;
   case GL_BLEND:
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_B1] &= ~B1_BLEND_ENABLE;
      if (state)
	 imesa->Setup[I810_CTXREG_B1] |= B1_BLEND_ENABLE;

      /* For some reason enable(GL_BLEND) affects ColorLogicOpEnabled.
       */
      FALLBACK( imesa, I810_FALLBACK_LOGICOP,
		(ctx->Color.ColorLogicOpEnabled &&
		 ctx->Color.LogicOp != GL_COPY));
      break;
   case GL_DEPTH_TEST:
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_B1] &= ~B1_Z_TEST_ENABLE;
      if (state)
	 imesa->Setup[I810_CTXREG_B1] |= B1_Z_TEST_ENABLE;
      break;
   case GL_SCISSOR_TEST:
      /* XXX without these next two lines, conform's scissor test fails */
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      I810_STATECHANGE(imesa, I810_UPLOAD_BUFFERS);
      I810_FIREVERTICES(imesa);	/* don't pipeline cliprect changes */
      imesa->upload_cliprects = GL_TRUE;
      imesa->scissor = state;
      break;
   case GL_POLYGON_STIPPLE:
      if (imesa->stipple_in_hw && imesa->reduced_primitive == GL_TRIANGLES)
      {
	 I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
	 imesa->Setup[I810_CTXREG_ST1] &= ~ST1_ENABLE;
	 if (state)
	    imesa->Setup[I810_CTXREG_ST1] |= ST1_ENABLE;
      }
      break;
   case GL_LINE_SMOOTH:
      /* Need to fatten the lines by .5, or they disappear...
       */
      if (imesa->reduced_primitive == GL_LINES) {
	 I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
	 imesa->Setup[I810_CTXREG_AA] &= ~AA_ENABLE;
	 imesa->Setup[I810_CTXREG_LCS] &= ~LCS_LINEWIDTH_0_5;
	 if (state) {
	    imesa->Setup[I810_CTXREG_AA] |= AA_ENABLE;
	    imesa->Setup[I810_CTXREG_LCS] |= LCS_LINEWIDTH_0_5;
	 }
      }
      break;
   case GL_POINT_SMOOTH:
      if (imesa->reduced_primitive == GL_POINTS) {
	 I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
	 imesa->Setup[I810_CTXREG_AA] &= ~AA_ENABLE;
	 imesa->Setup[I810_CTXREG_LCS] &= ~LCS_LINEWIDTH_0_5;
	 if (state) {
	    imesa->Setup[I810_CTXREG_AA] |= AA_ENABLE;
	    imesa->Setup[I810_CTXREG_LCS] |= LCS_LINEWIDTH_0_5;
	 }
      }
      break;
   case GL_POLYGON_SMOOTH:
      if (imesa->reduced_primitive == GL_TRIANGLES) {
	 I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
	 imesa->Setup[I810_CTXREG_AA] &= ~AA_ENABLE;
	 if (state)
	    imesa->Setup[I810_CTXREG_AA] |= AA_ENABLE;
      }
      break;
   case GL_FOG:
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_B1] &= ~B1_FOG_ENABLE;
      if (state)
	 imesa->Setup[I810_CTXREG_B1] |= B1_FOG_ENABLE;
      break;
   case GL_CULL_FACE:
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      imesa->Setup[I810_CTXREG_LCS] &= ~LCS_CULL_MASK;
      if (state)
	 imesa->Setup[I810_CTXREG_LCS] |= imesa->LcsCullMode;
      else
	 imesa->Setup[I810_CTXREG_LCS] |= LCS_CULL_DISABLE;
      break;
   case GL_TEXTURE_2D:
   case GL_TEXTURE_RECTANGLE_NV:
      I810_STATECHANGE(imesa, I810_UPLOAD_CTX);
      if (ctx->Texture.CurrentUnit == 0) {
	 imesa->Setup[I810_CTXREG_MT] &= ~MT_TEXEL0_ENABLE;
	 if (state)
	    imesa->Setup[I810_CTXREG_MT] |= MT_TEXEL0_ENABLE;
      } else {
	 imesa->Setup[I810_CTXREG_MT] &= ~MT_TEXEL1_ENABLE;
	 if (state)
	    imesa->Setup[I810_CTXREG_MT] |= MT_TEXEL1_ENABLE;
      }
      break;
   case GL_COLOR_LOGIC_OP:
      FALLBACK( imesa, I810_FALLBACK_LOGICOP,
		(state && ctx->Color.LogicOp != GL_COPY));
      break;
   case GL_STENCIL_TEST:
      FALLBACK( imesa, I810_FALLBACK_STENCIL, state );
      break;
   default:
      ;
   }
}







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




void i810EmitDrawingRectangle( i810ContextPtr imesa )
{
   __DRIdrawable *dPriv = imesa->driDrawable;
   i810ScreenPrivate *i810Screen = imesa->i810Screen;
   int x0 = imesa->drawX;
   int y0 = imesa->drawY;
   int x1 = x0 + dPriv->w;
   int y1 = y0 + dPriv->h;
   GLuint dr2, dr3, dr4;


   /* Coordinate origin of the window - may be offscreen.
    */
   dr4 = imesa->BufferSetup[I810_DESTREG_DR4] = ((y0<<16) |
						 (((unsigned)x0)&0xFFFF));

   /* Clip to screen.
    */
   if (x0 < 0) x0 = 0;
   if (y0 < 0) y0 = 0;
   if (x1 > i810Screen->width-1) x1 = i810Screen->width-1;
   if (y1 > i810Screen->height-1) y1 = i810Screen->height-1;


   /* Onscreen drawing rectangle.
    */
   dr2 = imesa->BufferSetup[I810_DESTREG_DR2] = ((y0<<16) | x0);
   dr3 = imesa->BufferSetup[I810_DESTREG_DR3] = (((y1+1)<<16) | (x1+1));


   imesa->dirty |= I810_UPLOAD_BUFFERS;
}



static void i810CalcViewport( GLcontext *ctx )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   const GLfloat *v = ctx->Viewport._WindowMap.m;
   GLfloat *m = imesa->ViewportMatrix.m;

   /* See also i810_translate_vertex.  SUBPIXEL adjustments can be done
    * via state vars, too.
    */
   m[MAT_SX] =   v[MAT_SX];
   m[MAT_TX] =   v[MAT_TX] + SUBPIXEL_X;
   m[MAT_SY] = - v[MAT_SY];
   m[MAT_TY] = - v[MAT_TY] + imesa->driDrawable->h + SUBPIXEL_Y;
   m[MAT_SZ] =   v[MAT_SZ] * (1.0 / 0xffff);
   m[MAT_TZ] =   v[MAT_TZ] * (1.0 / 0xffff);
}

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

static void i810DepthRange( GLcontext *ctx,
			    GLclampd nearval, GLclampd farval )
{
   i810CalcViewport( ctx );
}



void i810PrintDirty( const char *msg, GLuint state )
{
   fprintf(stderr, "%s (0x%x): %s%s%s%s\n",
	   msg,
	   (unsigned int) state,
	   (state & I810_UPLOAD_TEX0)  ? "upload-tex0, " : "",
	   (state & I810_UPLOAD_TEX1)  ? "upload-tex1, " : "",
	   (state & I810_UPLOAD_CTX)        ? "upload-ctx, " : "",
	   (state & I810_UPLOAD_BUFFERS)    ? "upload-bufs, " : ""
	   );
}



void i810InitState( GLcontext *ctx )
{
   i810ContextPtr imesa = I810_CONTEXT(ctx);
   i810ScreenPrivate *i810Screen = imesa->i810Screen;

   memset(imesa->Setup, 0, sizeof(imesa->Setup));

   imesa->Setup[I810_CTXREG_VF] = 0;

   imesa->Setup[I810_CTXREG_MT] = (GFX_OP_MAP_TEXELS |
				   MT_UPDATE_TEXEL1_STATE |
				   MT_TEXEL1_COORD1 |
				   MT_TEXEL1_MAP1 |
				   MT_TEXEL1_DISABLE |
				   MT_UPDATE_TEXEL0_STATE |
				   MT_TEXEL0_COORD0 |
				   MT_TEXEL0_MAP0 |
				   MT_TEXEL0_DISABLE);

   imesa->Setup[I810_CTXREG_MC0] = ( GFX_OP_MAP_COLOR_STAGES |
				     MC_STAGE_0 |
				     MC_UPDATE_DEST |
				     MC_DEST_CURRENT |
				     MC_UPDATE_ARG1 |
				     ((MC_ARG_ITERATED_COLOR |
				       MC_ARG_DONT_REPLICATE_ALPHA |
				       MC_ARG_DONT_INVERT) << MC_ARG1_SHIFT) |
				     MC_UPDATE_ARG2 |
				     ((MC_ARG_ONE |
				       MC_ARG_DONT_REPLICATE_ALPHA |
				       MC_ARG_DONT_INVERT) << MC_ARG2_SHIFT) |
				     MC_UPDATE_OP |
				     MC_OP_ARG1 );

   imesa->Setup[I810_CTXREG_MC1] = ( GFX_OP_MAP_COLOR_STAGES |
				     MC_STAGE_1 |
				     MC_UPDATE_DEST |
				     MC_DEST_CURRENT |
				     MC_UPDATE_ARG1 |
				     ((MC_ARG_ONE |
				       MC_ARG_DONT_REPLICATE_ALPHA |
				       MC_ARG_DONT_INVERT) << MC_ARG1_SHIFT) |
				     MC_UPDATE_ARG2 |
				     ((MC_ARG_ONE |
				       MC_ARG_DONT_REPLICATE_ALPHA |
				       MC_ARG_DONT_INVERT) << MC_ARG2_SHIFT) |
				     MC_UPDATE_OP |
				     MC_OP_DISABLE );


   imesa->Setup[I810_CTXREG_MC2] = ( GFX_OP_MAP_COLOR_STAGES |
				     MC_STAGE_2 |
				     MC_UPDATE_DEST |
				     MC_DEST_CURRENT |
				     MC_UPDATE_ARG1 |
				     ((MC_ARG_CURRENT_COLOR |
				       MC_ARG_REPLICATE_ALPHA |
				       MC_ARG_DONT_INVERT) << MC_ARG1_SHIFT) |
				     MC_UPDATE_ARG2 |
				     ((MC_ARG_ONE |
				       MC_ARG_DONT_REPLICATE_ALPHA |
				       MC_ARG_DONT_INVERT) << MC_ARG2_SHIFT) |
				     MC_UPDATE_OP |
				     MC_OP_DISABLE );


   imesa->Setup[I810_CTXREG_MA0] = ( GFX_OP_MAP_ALPHA_STAGES |
				     MA_STAGE_0 |
				     MA_UPDATE_ARG1 |
				     ((MA_ARG_ITERATED_ALPHA |
				       MA_ARG_DONT_INVERT) << MA_ARG1_SHIFT) |
				     MA_UPDATE_ARG2 |
				     ((MA_ARG_CURRENT_ALPHA |
				       MA_ARG_DONT_INVERT) << MA_ARG2_SHIFT) |
				     MA_UPDATE_OP |
				     MA_OP_ARG1 );


   imesa->Setup[I810_CTXREG_MA1] = ( GFX_OP_MAP_ALPHA_STAGES |
				     MA_STAGE_1 |
				     MA_UPDATE_ARG1 |
				     ((MA_ARG_CURRENT_ALPHA |
				       MA_ARG_DONT_INVERT) << MA_ARG1_SHIFT) |
				     MA_UPDATE_ARG2 |
				     ((MA_ARG_CURRENT_ALPHA |
				       MA_ARG_DONT_INVERT) << MA_ARG2_SHIFT) |
				     MA_UPDATE_OP |
				     MA_OP_ARG1 );


   imesa->Setup[I810_CTXREG_MA2] = ( GFX_OP_MAP_ALPHA_STAGES |
				     MA_STAGE_2 |
				     MA_UPDATE_ARG1 |
				     ((MA_ARG_CURRENT_ALPHA |
				       MA_ARG_DONT_INVERT) << MA_ARG1_SHIFT) |
				     MA_UPDATE_ARG2 |
				     ((MA_ARG_CURRENT_ALPHA |
				       MA_ARG_DONT_INVERT) << MA_ARG2_SHIFT) |
				     MA_UPDATE_OP |
				     MA_OP_ARG1 );


   imesa->Setup[I810_CTXREG_SDM] = ( GFX_OP_SRC_DEST_MONO |
				     SDM_UPDATE_MONO_ENABLE |
				     0 |
				     SDM_UPDATE_SRC_BLEND |
				     SDM_SRC_ONE |
				     SDM_UPDATE_DST_BLEND |
				     SDM_DST_ZERO );

   /* Use for colormask:
    */
   imesa->Setup[I810_CTXREG_CF0] = GFX_OP_COLOR_FACTOR;
   imesa->Setup[I810_CTXREG_CF1] = 0xffffffff;

   imesa->Setup[I810_CTXREG_ZA] = (GFX_OP_ZBIAS_ALPHAFUNC |
				   ZA_UPDATE_ALPHAFUNC |
				   ZA_ALPHA_ALWAYS |
				   ZA_UPDATE_ZBIAS |
				   0 |
				   ZA_UPDATE_ALPHAREF |
				   0x0);

   imesa->Setup[I810_CTXREG_FOG] = (GFX_OP_FOG_COLOR |
				    (0xffffff & ~FOG_RESERVED_MASK));

   /* Choose a pipe
    */
   imesa->Setup[I810_CTXREG_B1] = ( GFX_OP_BOOL_1 |
				    B1_UPDATE_SPEC_SETUP_ENABLE |
				    0 |
				    B1_UPDATE_ALPHA_SETUP_ENABLE |
				    B1_ALPHA_SETUP_ENABLE |
				    B1_UPDATE_CI_KEY_ENABLE |
				    0 |
				    B1_UPDATE_CHROMAKEY_ENABLE |
				    0 |
				    B1_UPDATE_Z_BIAS_ENABLE |
				    0 |
				    B1_UPDATE_SPEC_ENABLE |
				    0 |
				    B1_UPDATE_FOG_ENABLE |
				    0 |
				    B1_UPDATE_ALPHA_TEST_ENABLE |
				    0 |
				    B1_UPDATE_BLEND_ENABLE |
				    0 |
				    B1_UPDATE_Z_TEST_ENABLE |
				    0 );

   imesa->Setup[I810_CTXREG_B2] = ( GFX_OP_BOOL_2 |
				    B2_UPDATE_MAP_CACHE_ENABLE |
				    B2_MAP_CACHE_ENABLE |
				    B2_UPDATE_ALPHA_DITHER_ENABLE |
				    0 |
				    B2_UPDATE_FOG_DITHER_ENABLE |
				    0 |
				    B2_UPDATE_SPEC_DITHER_ENABLE |
				    0 |
				    B2_UPDATE_RGB_DITHER_ENABLE |
				    B2_RGB_DITHER_ENABLE |
				    B2_UPDATE_FB_WRITE_ENABLE |
				    B2_FB_WRITE_ENABLE |
				    B2_UPDATE_ZB_WRITE_ENABLE |
				    B2_ZB_WRITE_ENABLE );

   imesa->Setup[I810_CTXREG_LCS] = ( GFX_OP_LINEWIDTH_CULL_SHADE_MODE |
				     LCS_UPDATE_ZMODE |
				     LCS_Z_LESS |
				     LCS_UPDATE_LINEWIDTH |
				     LCS_LINEWIDTH_1_0 |
				     LCS_UPDATE_ALPHA_INTERP |
				     LCS_ALPHA_INTERP |
				     LCS_UPDATE_FOG_INTERP |
				     0 |
				     LCS_UPDATE_SPEC_INTERP |
				     0 |
				     LCS_UPDATE_RGB_INTERP |
				     LCS_RGB_INTERP |
				     LCS_UPDATE_CULL_MODE |
				     LCS_CULL_DISABLE);

   imesa->LcsCullMode = LCS_CULL_CW;
   imesa->LcsLineWidth = LCS_LINEWIDTH_1_0;
   imesa->LcsPointSize = LCS_LINEWIDTH_1_0;

   imesa->Setup[I810_CTXREG_PV] = ( GFX_OP_PV_RULE |
				    PV_UPDATE_PIXRULE |
				    PV_PIXRULE_ENABLE |
				    PV_UPDATE_LINELIST |
				    PV_LINELIST_PV1 |
				    PV_UPDATE_TRIFAN |
				    PV_TRIFAN_PV2 |
				    PV_UPDATE_TRISTRIP |
				    PV_TRISTRIP_PV2 );


   imesa->Setup[I810_CTXREG_ST0] = GFX_OP_STIPPLE;
   imesa->Setup[I810_CTXREG_ST1] = 0;

   imesa->Setup[I810_CTXREG_AA] = ( GFX_OP_ANTIALIAS |
				    AA_UPDATE_EDGEFLAG |
				    0 |
				    AA_UPDATE_POLYWIDTH |
				    AA_POLYWIDTH_05 |
				    AA_UPDATE_LINEWIDTH |
				    AA_LINEWIDTH_05 |
				    AA_UPDATE_BB_EXPANSION |
				    0 |
				    AA_UPDATE_AA_ENABLE |
				    0 );

   memset(imesa->BufferSetup, 0, sizeof(imesa->BufferSetup));
   imesa->BufferSetup[I810_DESTREG_DI0] = CMD_OP_DESTBUFFER_INFO;

   if (imesa->glCtx->Visual.doubleBufferMode && imesa->sarea->pf_current_page == 0) {
      /* use back buffer by default */
      imesa->BufferSetup[I810_DESTREG_DI1] = (i810Screen->backOffset |
					      i810Screen->backPitchBits);
   } else {
      /* use front buffer by default */
      imesa->BufferSetup[I810_DESTREG_DI1] = (i810Screen->fbOffset |
					      i810Screen->backPitchBits);
   }

   imesa->BufferSetup[I810_DESTREG_DV0] = GFX_OP_DESTBUFFER_VARS;
   imesa->BufferSetup[I810_DESTREG_DV1] = (DV_HORG_BIAS_OGL |
					   DV_VORG_BIAS_OGL |
					   i810Screen->fbFormat);

   imesa->BufferSetup[I810_DESTREG_DR0] = GFX_OP_DRAWRECT_INFO;
   imesa->BufferSetup[I810_DESTREG_DR1] = DR1_RECT_CLIP_ENABLE;
}


static void i810InvalidateState( 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 );
   I810_CONTEXT(ctx)->new_state |= new_state;
}


void i810InitStateFuncs(GLcontext *ctx)
{
   /* Callbacks for internal Mesa events.
    */
   ctx->Driver.UpdateState = i810InvalidateState;

   /* API callbacks
    */
   ctx->Driver.AlphaFunc = i810AlphaFunc;
   ctx->Driver.BlendEquationSeparate = i810BlendEquationSeparate;
   ctx->Driver.BlendFuncSeparate = i810BlendFuncSeparate;
   ctx->Driver.ClearColor = i810ClearColor;
   ctx->Driver.ColorMask = i810ColorMask;
   ctx->Driver.CullFace = i810CullFaceFrontFace;
   ctx->Driver.DepthFunc = i810DepthFunc;
   ctx->Driver.DepthMask = i810DepthMask;
   ctx->Driver.Enable = i810Enable;
   ctx->Driver.Fogfv = i810Fogfv;
   ctx->Driver.FrontFace = i810CullFaceFrontFace;
   ctx->Driver.LineWidth = i810LineWidth;
   ctx->Driver.LogicOpcode = i810LogicOp;
   ctx->Driver.PolygonStipple = i810PolygonStipple;
   ctx->Driver.RenderMode = i810RenderMode;
   ctx->Driver.Scissor = i810Scissor;
   ctx->Driver.DrawBuffer = i810DrawBuffer;
   ctx->Driver.ReadBuffer = i810ReadBuffer;
   ctx->Driver.ShadeModel = i810ShadeModel;
   ctx->Driver.DepthRange = i810DepthRange;
   ctx->Driver.Viewport = i810Viewport;
   ctx->Driver.PointSize = i810PointSize;

   if (IS_I815(I810_CONTEXT(ctx))) {
      ctx->Driver.LightModelfv = i810LightModelfv_i815;
   } else {
      ctx->Driver.LightModelfv = i810LightModelfv;
   }
}
