/**************************************************************************
 * 
 * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
 * 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 TUNGSTEN 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 "glheader.h"
#include "context.h"
#include "macros.h"
#include "enums.h"
#include "dd.h"
#include "tnl/tnl.h"
#include "tnl/t_context.h"

#include "texmem.h"

#include "intel_screen.h"
#include "intel_batchbuffer.h"

#include "i915_context.h"
#include "i915_reg.h"

static int translate_compare_func( GLenum func )
{
   switch(func) {
   case GL_NEVER: 
      return COMPAREFUNC_NEVER; 
   case GL_LESS: 
      return COMPAREFUNC_LESS; 
   case GL_LEQUAL: 
      return COMPAREFUNC_LEQUAL; 
   case GL_GREATER: 
      return COMPAREFUNC_GREATER; 
   case GL_GEQUAL: 
      return COMPAREFUNC_GEQUAL; 
   case GL_NOTEQUAL: 
      return COMPAREFUNC_NOTEQUAL; 
   case GL_EQUAL: 
      return COMPAREFUNC_EQUAL; 
   case GL_ALWAYS: 
   default:
      return COMPAREFUNC_ALWAYS; 
   }
}


static void i915StencilFunc(GLcontext *ctx, GLenum func, GLint ref,
			    GLuint mask)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   int test = translate_compare_func( func );

   mask = mask & 0xff;

   if (INTEL_DEBUG&DEBUG_DRI)
      fprintf(stderr, "%s : func: %s, ref : 0x%x, mask: 0x%x\n", __FUNCTION__,
	      _mesa_lookup_enum_by_nr(func), ref, mask);


   I915_STATECHANGE(i915, I915_UPLOAD_CTX);
   i915->state.Ctx[I915_CTXREG_STATE4] &= ~MODE4_ENABLE_STENCIL_TEST_MASK;
   i915->state.Ctx[I915_CTXREG_STATE4] |= (ENABLE_STENCIL_TEST_MASK |
					 STENCIL_TEST_MASK(mask));

   i915->state.Ctx[I915_CTXREG_LIS5] &= ~(S5_STENCIL_REF_MASK |
					S5_STENCIL_TEST_FUNC_MASK);
					
   i915->state.Ctx[I915_CTXREG_LIS5] |= ((ref << S5_STENCIL_REF_SHIFT) |  
 				       (test << S5_STENCIL_TEST_FUNC_SHIFT)); 
}

static void i915StencilMask(GLcontext *ctx, GLuint mask)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);

   if (INTEL_DEBUG&DEBUG_DRI)
      fprintf(stderr, "%s : mask 0x%x\n", __FUNCTION__, mask);

   mask = mask & 0xff;

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);
   i915->state.Ctx[I915_CTXREG_STATE4] &= ~MODE4_ENABLE_STENCIL_WRITE_MASK;
   i915->state.Ctx[I915_CTXREG_STATE4] |= (ENABLE_STENCIL_WRITE_MASK |
					STENCIL_WRITE_MASK(mask));
}

static int translate_stencil_op( GLenum op )
{
   switch(op) {
   case GL_KEEP: 
      return STENCILOP_KEEP; 
   case GL_ZERO: 
      return STENCILOP_ZERO; 
   case GL_REPLACE: 
      return STENCILOP_REPLACE; 
   case GL_INCR: 
      return STENCILOP_INCRSAT;
   case GL_DECR: 
      return STENCILOP_DECRSAT;
   case GL_INCR_WRAP:
      return STENCILOP_INCR; 
   case GL_DECR_WRAP:
      return STENCILOP_DECR; 
   case GL_INVERT: 
      return STENCILOP_INVERT; 
   default: 
      return 0;
   }
}

static void i915StencilOp(GLcontext *ctx, GLenum fail, GLenum zfail,
			  GLenum zpass)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   int fop = translate_stencil_op(fail); 
   int dfop = translate_stencil_op(zfail); 
   int dpop = translate_stencil_op(zpass);


   if (INTEL_DEBUG&DEBUG_DRI)
      fprintf(stderr, "%s: fail : %s, zfail: %s, zpass : %s\n", __FUNCTION__,
	      _mesa_lookup_enum_by_nr(fail),
	      _mesa_lookup_enum_by_nr(zfail),
	      _mesa_lookup_enum_by_nr(zpass));

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);

   i915->state.Ctx[I915_CTXREG_LIS5] &= ~(S5_STENCIL_FAIL_MASK |
					S5_STENCIL_PASS_Z_FAIL_MASK |
					S5_STENCIL_PASS_Z_PASS_MASK);

   i915->state.Ctx[I915_CTXREG_LIS5] |= ((fop << S5_STENCIL_FAIL_SHIFT) |
				       (dfop << S5_STENCIL_PASS_Z_FAIL_SHIFT) |
				       (dpop << S5_STENCIL_PASS_Z_PASS_SHIFT));
}

static void i915AlphaFunc(GLcontext *ctx, GLenum func, GLfloat ref)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   int test = translate_compare_func( func );
   GLubyte refByte;

   UNCLAMPED_FLOAT_TO_UBYTE(refByte, ref);

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);
   i915->state.Ctx[I915_CTXREG_LIS6] &= ~(S6_ALPHA_TEST_FUNC_MASK |
					S6_ALPHA_REF_MASK);
   i915->state.Ctx[I915_CTXREG_LIS6] |= ((test << S6_ALPHA_TEST_FUNC_SHIFT) |
				       (((GLuint)refByte) << S6_ALPHA_REF_SHIFT));
}

/* This function makes sure that the proper enables are
 * set for LogicOp, Independant Alpha Blend, and Blending.
 * It needs to be called from numerous places where we
 * could change the LogicOp or Independant Alpha Blend without subsequent
 * calls to glEnable.
 */
static void i915EvalLogicOpBlendState(GLcontext *ctx)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);

   if (ctx->Color._LogicOpEnabled) {
      i915->state.Ctx[I915_CTXREG_LIS5] |= S5_LOGICOP_ENABLE;
      i915->state.Ctx[I915_CTXREG_LIS6] &= ~S6_CBUF_BLEND_ENABLE;
   } else {
      i915->state.Ctx[I915_CTXREG_LIS5] &= ~S5_LOGICOP_ENABLE;

      if (ctx->Color.BlendEnabled) {
	 i915->state.Ctx[I915_CTXREG_LIS6] |= S6_CBUF_BLEND_ENABLE;
      } else {
	 i915->state.Ctx[I915_CTXREG_LIS6] &= ~S6_CBUF_BLEND_ENABLE;
      }
   }
}

static void i915BlendColor(GLcontext *ctx, const GLfloat color[4])
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   GLubyte r, g, b, a;

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

   UNCLAMPED_FLOAT_TO_UBYTE(r, color[RCOMP]);
   UNCLAMPED_FLOAT_TO_UBYTE(g, color[GCOMP]);
   UNCLAMPED_FLOAT_TO_UBYTE(b, color[BCOMP]);
   UNCLAMPED_FLOAT_TO_UBYTE(a, color[ACOMP]);

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);
   i915->state.Ctx[I915_CTXREG_BLENDCOLOR1] = (a<<24) | (r<<16) | (g<<8) | b;
}

static int translate_blend_factor( GLenum factor )
{
   switch(factor) {
   case GL_ZERO: 
      return BLENDFACT_ZERO; 
   case GL_SRC_ALPHA: 
      return BLENDFACT_SRC_ALPHA; 
   case GL_ONE: 
      return BLENDFACT_ONE; 
   case GL_SRC_COLOR: 
      return BLENDFACT_SRC_COLR; 
   case GL_ONE_MINUS_SRC_COLOR: 
      return BLENDFACT_INV_SRC_COLR; 
   case GL_DST_COLOR: 
      return BLENDFACT_DST_COLR; 
   case GL_ONE_MINUS_DST_COLOR: 
      return BLENDFACT_INV_DST_COLR; 
   case GL_ONE_MINUS_SRC_ALPHA:
      return BLENDFACT_INV_SRC_ALPHA; 
   case GL_DST_ALPHA: 
      return BLENDFACT_DST_ALPHA; 
   case GL_ONE_MINUS_DST_ALPHA:
      return BLENDFACT_INV_DST_ALPHA; 
   case GL_SRC_ALPHA_SATURATE: 
      return BLENDFACT_SRC_ALPHA_SATURATE;
   case GL_CONSTANT_COLOR:
      return BLENDFACT_CONST_COLOR; 
   case GL_ONE_MINUS_CONSTANT_COLOR:
      return BLENDFACT_INV_CONST_COLOR;
   case GL_CONSTANT_ALPHA:
      return BLENDFACT_CONST_ALPHA; 
   case GL_ONE_MINUS_CONSTANT_ALPHA:
      return BLENDFACT_INV_CONST_ALPHA;
   default:
      return BLENDFACT_ZERO;
   }
}

#define DST_BLND_FACT(f) ((f)<<S6_CBUF_DST_BLEND_FACT_SHIFT)
#define SRC_BLND_FACT(f) ((f)<<S6_CBUF_SRC_BLEND_FACT_SHIFT)
#define DST_ABLND_FACT(f) ((f)<<IAB_DST_FACTOR_SHIFT)
#define SRC_ABLND_FACT(f) ((f)<<IAB_SRC_FACTOR_SHIFT)



static GLuint translate_blend_equation( GLenum mode )
{
   switch (mode) {
   case GL_FUNC_ADD: return BLENDFUNC_ADD; 
   case GL_MIN: return BLENDFUNC_MIN; 
   case GL_MAX: return BLENDFUNC_MAX; 
   case GL_FUNC_SUBTRACT: return BLENDFUNC_SUBTRACT; 
   case GL_FUNC_REVERSE_SUBTRACT: return BLENDFUNC_REVERSE_SUBTRACT; 
   default: return 0;
   }
}

static void i915UpdateBlendState( GLcontext *ctx )
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   GLuint iab = (i915->state.Ctx[I915_CTXREG_IAB] & 
		 ~(IAB_SRC_FACTOR_MASK |
		   IAB_DST_FACTOR_MASK |
		   (BLENDFUNC_MASK << IAB_FUNC_SHIFT) |
		   IAB_ENABLE));

   GLuint lis6 = (i915->state.Ctx[I915_CTXREG_LIS6] & 
		  ~(S6_CBUF_SRC_BLEND_FACT_MASK |
		    S6_CBUF_DST_BLEND_FACT_MASK |
		    S6_CBUF_BLEND_FUNC_MASK));

   GLuint eqRGB = ctx->Color.BlendEquationRGB;
   GLuint eqA = ctx->Color.BlendEquationA;
   GLuint srcRGB = ctx->Color.BlendSrcRGB;
   GLuint dstRGB = ctx->Color.BlendDstRGB;
   GLuint srcA = ctx->Color.BlendSrcA;
   GLuint dstA = ctx->Color.BlendDstA;

   if (eqRGB == GL_MIN || eqRGB == GL_MAX) {
      srcRGB = dstRGB = GL_ONE;
   }

   if (eqA == GL_MIN || eqA == GL_MAX) {
      srcA = dstA = GL_ONE;
   }

   lis6 |= SRC_BLND_FACT(translate_blend_factor(srcRGB)); 
   lis6 |= DST_BLND_FACT(translate_blend_factor(dstRGB)); 
   lis6 |= translate_blend_equation( eqRGB ) << S6_CBUF_BLEND_FUNC_SHIFT;

   iab |= SRC_ABLND_FACT(translate_blend_factor(srcA)); 
   iab |= DST_ABLND_FACT(translate_blend_factor(dstA)); 
   iab |= translate_blend_equation( eqA ) << IAB_FUNC_SHIFT;

   if (srcA != srcRGB || dstA != dstRGB || eqA != eqRGB) 
      iab |= IAB_ENABLE;

   if (iab != i915->state.Ctx[I915_CTXREG_IAB] ||
       lis6 != i915->state.Ctx[I915_CTXREG_LIS6]) {
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      i915->state.Ctx[I915_CTXREG_IAB] = iab;
      i915->state.Ctx[I915_CTXREG_LIS6] = lis6;
   }

   /* This will catch a logicop blend equation */
   i915EvalLogicOpBlendState(ctx);
}


static void i915BlendFuncSeparate(GLcontext *ctx, GLenum srcRGB, 
				 GLenum dstRGB, GLenum srcA,
				 GLenum dstA )
{  
   i915UpdateBlendState( ctx );
}


static void i915BlendEquationSeparate(GLcontext *ctx, GLenum eqRGB,
				     GLenum eqA) 
{
   i915UpdateBlendState( ctx );
}


static void i915DepthFunc(GLcontext *ctx, GLenum func)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   int test = translate_compare_func( func );

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

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);
   i915->state.Ctx[I915_CTXREG_LIS6] &= ~S6_DEPTH_TEST_FUNC_MASK;
   i915->state.Ctx[I915_CTXREG_LIS6] |= test << S6_DEPTH_TEST_FUNC_SHIFT;
}

static void i915DepthMask(GLcontext *ctx, GLboolean flag)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);

   if (INTEL_DEBUG&DEBUG_DRI)
      fprintf(stderr, "%s flag (%d)\n", __FUNCTION__, flag);

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);

   if (flag && ctx->Depth.Test)
      i915->state.Ctx[I915_CTXREG_LIS6] |= S6_DEPTH_WRITE_ENABLE;
   else
      i915->state.Ctx[I915_CTXREG_LIS6] &= ~S6_DEPTH_WRITE_ENABLE;
}

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

   if (active) {
      I915_STATECHANGE(i915, I915_UPLOAD_STIPPLE);
      i915->state.Stipple[I915_STPREG_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 = 3 ; j >= 0; j--)
	 for (i = 0 ; i < 4 ; i++, m++)
	    if (*m != p[j]) {
	       i915->intel.hw_stipple = 0;
	       return;
	    }

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


   if (newMask == 0xffff || newMask == 0x0) {
      /* this is needed to make conform pass */
      i915->intel.hw_stipple = 0;
      return;
   }

   i915->state.Stipple[I915_STPREG_ST1] &= ~0xffff;
   i915->state.Stipple[I915_STPREG_ST1] |= newMask;
   i915->intel.hw_stipple = 1;

   if (active)
      i915->state.Stipple[I915_STPREG_ST1] |= ST1_ENABLE;
}


/* =============================================================
 * Hardware clipping
 */
static void i915Scissor(GLcontext *ctx, GLint x, GLint y, 
			GLsizei w, GLsizei h)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   intelScreenPrivate *screen = i915->intel.intelScreen;
   int x1, y1, x2, y2;

   if (!i915->intel.driDrawable)
      return;

   x1 = x;
   y1 = i915->intel.driDrawable->h - (y + h);
   x2 = x + w - 1;
   y2 = y1 + h - 1;

   if (INTEL_DEBUG&DEBUG_DRI)
      fprintf(stderr, "[%s] x(%d) y(%d) w(%d) h(%d)\n", __FUNCTION__,
	      x, y, w, h);

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

   if (x2 >= screen->width) x2 = screen->width-1;
   if (y2 >= screen->height) y2 = screen->height-1;
   if (x1 >= screen->width) x1 = screen->width-1;
   if (y1 >= screen->height) y1 = screen->height-1;


   I915_STATECHANGE(i915, I915_UPLOAD_BUFFERS);
   i915->state.Buffer[I915_DESTREG_SR1] = (y1 << 16) | (x1 & 0xffff);
   i915->state.Buffer[I915_DESTREG_SR2] = (y2 << 16) | (x2 & 0xffff);
}

static void i915LogicOp(GLcontext *ctx, GLenum opcode)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   int tmp = 0;

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

   switch(opcode) {
   case GL_CLEAR: 
      tmp = LOGICOP_CLEAR; 
      break;
   case GL_AND: 
      tmp = LOGICOP_AND; 
      break;
   case GL_AND_REVERSE: 
      tmp = LOGICOP_AND_RVRSE; 
      break;
   case GL_COPY: 
      tmp = LOGICOP_COPY; 
      break;
   case GL_COPY_INVERTED: 
      tmp = LOGICOP_COPY_INV; 
      break;
   case GL_AND_INVERTED: 
      tmp = LOGICOP_AND_INV; 
      break;
   case GL_NOOP: 
      tmp = LOGICOP_NOOP; 
      break;
   case GL_XOR: 
      tmp = LOGICOP_XOR; 
      break;
   case GL_OR: 
      tmp = LOGICOP_OR; 
      break;
   case GL_OR_INVERTED: 
      tmp = LOGICOP_OR_INV; 
      break;
   case GL_NOR: 
      tmp = LOGICOP_NOR; 
      break;
   case GL_EQUIV: 
      tmp = LOGICOP_EQUIV; 
      break;
   case GL_INVERT: 
      tmp = LOGICOP_INV; 
      break;
   case GL_OR_REVERSE: 
      tmp = LOGICOP_OR_RVRSE; 
      break;
   case GL_NAND: 
      tmp = LOGICOP_NAND; 
      break;
   case GL_SET: 
      tmp = LOGICOP_SET; 
      break;
   default:
      return;
   }

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);
   i915->state.Ctx[I915_CTXREG_STATE4] &= ~LOGICOP_MASK;
   i915->state.Ctx[I915_CTXREG_STATE4] |= LOGIC_OP_FUNC(tmp);
}



static void i915CullFaceFrontFace(GLcontext *ctx, GLenum unused)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   GLuint mode;

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

   if (!ctx->Polygon.CullFlag) {
      mode = S4_CULLMODE_NONE;
   }
   else if (ctx->Polygon.CullFaceMode != GL_FRONT_AND_BACK) {
      mode = S4_CULLMODE_CW;

      if (ctx->Polygon.CullFaceMode == GL_FRONT)
	 mode ^= (S4_CULLMODE_CW ^ S4_CULLMODE_CCW);
      if (ctx->Polygon.FrontFace != GL_CCW)
	 mode ^= (S4_CULLMODE_CW ^ S4_CULLMODE_CCW);
   }
   else {
      mode = S4_CULLMODE_BOTH;
   }

   I915_STATECHANGE(i915, I915_UPLOAD_CTX);
   i915->state.Ctx[I915_CTXREG_LIS4] &= ~S4_CULLMODE_MASK;
   i915->state.Ctx[I915_CTXREG_LIS4] |= mode;
}

static void i915LineWidth( GLcontext *ctx, GLfloat widthf )
{
   i915ContextPtr i915 = I915_CONTEXT( ctx );
   int lis4 = i915->state.Ctx[I915_CTXREG_LIS4] & ~S4_LINE_WIDTH_MASK;
   int width;

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

   width = (int)(widthf * 2);
   CLAMP_SELF(width, 1, 0xf);
   lis4 |= width << S4_LINE_WIDTH_SHIFT;

   if (lis4 != i915->state.Ctx[I915_CTXREG_LIS4]) {
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      i915->state.Ctx[I915_CTXREG_LIS4] = lis4;
   }
}

static void i915PointSize(GLcontext *ctx, GLfloat size)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   int lis4 = i915->state.Ctx[I915_CTXREG_LIS4] & ~S4_POINT_WIDTH_MASK;
   GLint point_size = (int)size;

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

   CLAMP_SELF(point_size, 1, 255);
   lis4 |= point_size << S4_POINT_WIDTH_SHIFT;

   if (lis4 != i915->state.Ctx[I915_CTXREG_LIS4]) {
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      i915->state.Ctx[I915_CTXREG_LIS4] = lis4;
   }
}


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

static void i915ColorMask(GLcontext *ctx,
			 GLboolean r, GLboolean g,
			 GLboolean b, GLboolean a)
{
   i915ContextPtr i915 = I915_CONTEXT( ctx );
   GLuint tmp = i915->state.Ctx[I915_CTXREG_LIS5] & ~S5_WRITEDISABLE_MASK;

   if (INTEL_DEBUG&DEBUG_DRI)
      fprintf(stderr, "%s r(%d) g(%d) b(%d) a(%d)\n", __FUNCTION__, r, g, b, a);

   if (!r) tmp |= S5_WRITEDISABLE_RED;
   if (!g) tmp |= S5_WRITEDISABLE_GREEN;
   if (!b) tmp |= S5_WRITEDISABLE_BLUE;
   if (!a) tmp |= S5_WRITEDISABLE_ALPHA;

   if (tmp != i915->state.Ctx[I915_CTXREG_LIS5]) {
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      i915->state.Ctx[I915_CTXREG_LIS5] = tmp;
   }
}

static void update_specular( GLcontext *ctx )
{
   /* A hack to trigger the rebuild of the fragment program.
    */
   INTEL_CONTEXT(ctx)->NewGLState |= _NEW_TEXTURE;
   I915_CONTEXT(ctx)->tex_program.translated = 0; 
}

static void i915LightModelfv(GLcontext *ctx, GLenum pname, 
			     const GLfloat *param)
{
   if (INTEL_DEBUG&DEBUG_DRI)
      fprintf(stderr, "%s\n", __FUNCTION__);

   if (pname == GL_LIGHT_MODEL_COLOR_CONTROL) {
      update_specular( ctx );
   }
}

static void i915ShadeModel(GLcontext *ctx, GLenum mode)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   I915_STATECHANGE(i915, I915_UPLOAD_CTX);

   if (mode == GL_SMOOTH) {
     i915->state.Ctx[I915_CTXREG_LIS4] &= ~(S4_FLATSHADE_ALPHA | 
					  S4_FLATSHADE_COLOR | 
					  S4_FLATSHADE_SPECULAR);
   } else {
     i915->state.Ctx[I915_CTXREG_LIS4] |= (S4_FLATSHADE_ALPHA | 
					 S4_FLATSHADE_COLOR | 
					 S4_FLATSHADE_SPECULAR);
   }
}

/* =============================================================
 * Fog
 */
static void update_fog( GLcontext *ctx )
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);
   GLenum mode;
   GLboolean enabled;
   GLboolean try_pixel_fog;
   
   if (ctx->FragmentProgram.Enabled && ctx->FragmentProgram.Current) {
      /* Pull in static fog state from program */
      
      mode = ctx->FragmentProgram.Current->FogOption;
      enabled = (mode != GL_NONE);
      try_pixel_fog = 1;
   }
   else {
      enabled = ctx->Fog.Enabled;
      mode = ctx->Fog.Mode;

      try_pixel_fog = (ctx->Fog.FogCoordinateSource == GL_FRAGMENT_DEPTH_EXT &&
		       ctx->Hint.Fog == GL_NICEST && 
		       0);	/* XXX - DISABLE -- Need ortho fallback */
   }

   if (!enabled) {
      i915->vertex_fog = I915_FOG_NONE;
   }
   else if (try_pixel_fog) {

      I915_STATECHANGE(i915, I915_UPLOAD_FOG);
      i915->state.Fog[I915_FOGREG_MODE1] &= ~FMC1_FOGFUNC_MASK;
      i915->vertex_fog = I915_FOG_PIXEL;
	 
      switch (mode) {
      case GL_LINEAR:
	 if (ctx->Fog.End <= ctx->Fog.Start) {
	    /* XXX - this won't work with fragment programs.  Need to
	     * either fallback or append fog instructions to end of
	     * program in the case of linear fog.
	     */
	    i915->state.Fog[I915_FOGREG_MODE1] |= FMC1_FOGFUNC_VERTEX;
	    i915->vertex_fog = I915_FOG_VERTEX;
	 }
	 else {
	    GLfloat c1 = ctx->Fog.End/(ctx->Fog.End-ctx->Fog.Start);
	    GLfloat c2 = 1.0/(ctx->Fog.End-ctx->Fog.Start);

	    i915->state.Fog[I915_FOGREG_MODE1] &= ~FMC1_C1_MASK;
	    i915->state.Fog[I915_FOGREG_MODE1] |= FMC1_FOGFUNC_PIXEL_LINEAR;
	    i915->state.Fog[I915_FOGREG_MODE1] |= 
	       ((GLuint)(c1 * FMC1_C1_ONE)) & FMC1_C1_MASK;

	    if (i915->state.Fog[I915_FOGREG_MODE1] & FMC1_FOGINDEX_Z) {
	       i915->state.Fog[I915_FOGREG_MODE2] = (GLuint)(c2 * FMC2_C2_ONE);
	    }
	    else {
	       union { float f; int i; } fi;
	       fi.f = c2; 
	       i915->state.Fog[I915_FOGREG_MODE2] = fi.i; 
	    }
	 }
	 break;
      case GL_EXP:
	 i915->state.Fog[I915_FOGREG_MODE1] |= FMC1_FOGFUNC_PIXEL_EXP;
	 break;
      case GL_EXP2:
	 i915->state.Fog[I915_FOGREG_MODE1] |= FMC1_FOGFUNC_PIXEL_EXP2;
	 break;
      default:
	 break;
      }
   }
   else /* if (i915->vertex_fog != I915_FOG_VERTEX) */ {      
      I915_STATECHANGE(i915, I915_UPLOAD_FOG);
      i915->state.Fog[I915_FOGREG_MODE1] &= ~FMC1_FOGFUNC_MASK;
      i915->state.Fog[I915_FOGREG_MODE1] |= FMC1_FOGFUNC_VERTEX;
      i915->vertex_fog = I915_FOG_VERTEX;
   }

   {
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      I915_ACTIVESTATE(i915, I915_UPLOAD_FOG, enabled);
      if (enabled)
	 i915->state.Ctx[I915_CTXREG_LIS5] |= S5_FOG_ENABLE;
      else
	 i915->state.Ctx[I915_CTXREG_LIS5] &= ~S5_FOG_ENABLE;
   }

   if (enabled) {
      _tnl_allow_vertex_fog( ctx, (i915->vertex_fog == I915_FOG_VERTEX) ); 
      _tnl_allow_pixel_fog( ctx, (i915->vertex_fog != I915_FOG_VERTEX) ); 
   }
}

static void i915Fogfv(GLcontext *ctx, GLenum pname, const GLfloat *param)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);

   switch (pname) {
   case GL_FOG_COORDINATE_SOURCE_EXT: 
   case GL_FOG_MODE:
   case GL_FOG_START:
   case GL_FOG_END: 
      update_fog( ctx );
      break;

   case GL_FOG_DENSITY:
      I915_STATECHANGE(i915, I915_UPLOAD_FOG);

      if (i915->state.Fog[I915_FOGREG_MODE1] & FMC1_FOGINDEX_Z) {
	 i915->state.Fog[I915_FOGREG_MODE3] = (GLuint)(ctx->Fog.Density *
						     FMC3_D_ONE);
      }
      else {
	 union { float f; int i; } fi;
	 fi.f = ctx->Fog.Density; 
	 i915->state.Fog[I915_FOGREG_MODE3] = fi.i; 
      }
      break;

   case GL_FOG_COLOR: 
      I915_STATECHANGE(i915, I915_UPLOAD_FOG);
      i915->state.Fog[I915_FOGREG_COLOR] = 
	 (_3DSTATE_FOG_COLOR_CMD | 
	  ((GLubyte)(ctx->Fog.Color[0]*255.0F) << 16) |
	  ((GLubyte)(ctx->Fog.Color[1]*255.0F) << 8) |
	  ((GLubyte)(ctx->Fog.Color[2]*255.0F) << 0));
      break;

   default:
      break;
   }
}

static void i915Hint(GLcontext *ctx, GLenum target, GLenum state)
{
   switch (target) {
   case GL_FOG_HINT:
      update_fog( ctx );
      break;
   default:
      break;
   }
}

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

static void i915Enable(GLcontext *ctx, GLenum cap, GLboolean state)
{
   i915ContextPtr i915 = I915_CONTEXT(ctx);

   switch(cap) {
   case GL_TEXTURE_2D:
      break;

   case GL_LIGHTING:
   case GL_COLOR_SUM:
      update_specular( ctx );
      break;

   case GL_ALPHA_TEST:
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      if (state)
	 i915->state.Ctx[I915_CTXREG_LIS6] |= S6_ALPHA_TEST_ENABLE;
      else
	 i915->state.Ctx[I915_CTXREG_LIS6] &= ~S6_ALPHA_TEST_ENABLE;
      break;

   case GL_BLEND:
      i915EvalLogicOpBlendState(ctx);
      break;

   case GL_COLOR_LOGIC_OP:
      i915EvalLogicOpBlendState(ctx);

      /* Logicop doesn't seem to work at 16bpp:
       */
      if (i915->intel.intelScreen->cpp == 2)
	 FALLBACK( &i915->intel, I915_FALLBACK_LOGICOP, state );
      break;

   case GL_FRAGMENT_PROGRAM_ARB:
      update_fog( ctx );
      break;

   case GL_DITHER:
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      if (state)
	 i915->state.Ctx[I915_CTXREG_LIS5] |= S5_COLOR_DITHER_ENABLE;
      else
	 i915->state.Ctx[I915_CTXREG_LIS5] &= ~S5_COLOR_DITHER_ENABLE;
      break;

   case GL_DEPTH_TEST:
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      if (state)
	 i915->state.Ctx[I915_CTXREG_LIS6] |= S6_DEPTH_TEST_ENABLE;
      else
	 i915->state.Ctx[I915_CTXREG_LIS6] &= ~S6_DEPTH_TEST_ENABLE;

      i915DepthMask( ctx, ctx->Depth.Mask );
      break;

   case GL_SCISSOR_TEST:
      I915_STATECHANGE(i915, I915_UPLOAD_BUFFERS);
      if (state)
	 i915->state.Buffer[I915_DESTREG_SENABLE] = (_3DSTATE_SCISSOR_ENABLE_CMD |
						   ENABLE_SCISSOR_RECT);
      else
	 i915->state.Buffer[I915_DESTREG_SENABLE] = (_3DSTATE_SCISSOR_ENABLE_CMD |
						   DISABLE_SCISSOR_RECT);
      break;

   case GL_LINE_SMOOTH:
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      if (state)
	 i915->state.Ctx[I915_CTXREG_LIS4] |= S4_LINE_ANTIALIAS_ENABLE;
      else
	 i915->state.Ctx[I915_CTXREG_LIS4] &= ~S4_LINE_ANTIALIAS_ENABLE;
      break;

   case GL_FOG:
      update_fog( ctx );
      break;

   case GL_CULL_FACE:
      i915CullFaceFrontFace(ctx, 0);
      break;

   case GL_STENCIL_TEST:
      if (i915->intel.hw_stencil) {
	 I915_STATECHANGE(i915, I915_UPLOAD_CTX);
	 if (state)
	    i915->state.Ctx[I915_CTXREG_LIS5] |= (S5_STENCIL_TEST_ENABLE |
						S5_STENCIL_WRITE_ENABLE);
	 else
	    i915->state.Ctx[I915_CTXREG_LIS5] &= ~(S5_STENCIL_TEST_ENABLE | 
						 S5_STENCIL_WRITE_ENABLE);
      } else {
	 FALLBACK( &i915->intel, I915_FALLBACK_STENCIL, state );
      }
      break;

   case GL_POLYGON_STIPPLE:
      /* The stipple command worked on my 855GM box, but not my 845G.
       * I'll do more testing later to find out exactly which hardware
       * supports it.  Disabled for now.
       */
      if (i915->intel.hw_stipple && 
	  i915->intel.reduced_primitive == GL_TRIANGLES)
      {
	 I915_STATECHANGE(i915, I915_UPLOAD_STIPPLE);
	 if (state)
	    i915->state.Stipple[I915_STPREG_ST1] |= ST1_ENABLE;
	 else
	    i915->state.Stipple[I915_STPREG_ST1] &= ~ST1_ENABLE;
      }
      break;

   default:
      ;
   }
}


static void i915_init_packets( i915ContextPtr i915 )
{
   intelScreenPrivate *screen = i915->intel.intelScreen;

   /* Zero all state */
   memset(&i915->state, 0, sizeof(i915->state));


   {
      I915_STATECHANGE(i915, I915_UPLOAD_CTX);
      /* Probably don't want to upload all this stuff every time one 
       * piece changes.
       */
      i915->state.Ctx[I915_CTXREG_LI] = (_3DSTATE_LOAD_STATE_IMMEDIATE_1 | 
				       I1_LOAD_S(2) |
				       I1_LOAD_S(4) |
				       I1_LOAD_S(5) |
				       I1_LOAD_S(6) | 
				       (4));
      i915->state.Ctx[I915_CTXREG_LIS2] = 0;
      i915->state.Ctx[I915_CTXREG_LIS4] = 0;
      i915->state.Ctx[I915_CTXREG_LIS5] = 0;

      if (screen->cpp == 2)
	 i915->state.Ctx[I915_CTXREG_LIS5] |= S5_COLOR_DITHER_ENABLE;


      i915->state.Ctx[I915_CTXREG_LIS6] = (S6_COLOR_WRITE_ENABLE |
					 (2 << S6_TRISTRIP_PV_SHIFT));

      i915->state.Ctx[I915_CTXREG_STATE4] = (_3DSTATE_MODES_4_CMD |
					   ENABLE_LOGIC_OP_FUNC |
					   LOGIC_OP_FUNC(LOGICOP_COPY) |
					   ENABLE_STENCIL_TEST_MASK |
					   STENCIL_TEST_MASK(0xff) |
					   ENABLE_STENCIL_WRITE_MASK |
					   STENCIL_WRITE_MASK(0xff));


      i915->state.Ctx[I915_CTXREG_IAB] = (_3DSTATE_INDEPENDENT_ALPHA_BLEND_CMD |
					IAB_MODIFY_ENABLE |
					IAB_MODIFY_FUNC |
					IAB_MODIFY_SRC_FACTOR |
					IAB_MODIFY_DST_FACTOR);

      i915->state.Ctx[I915_CTXREG_BLENDCOLOR0] = _3DSTATE_CONST_BLEND_COLOR_CMD;
      i915->state.Ctx[I915_CTXREG_BLENDCOLOR1] = 0;

   }

   {
      I915_STATECHANGE(i915, I915_UPLOAD_STIPPLE);
      i915->state.Stipple[I915_STPREG_ST0] = _3DSTATE_STIPPLE;
   }


   {
      I915_STATECHANGE(i915, I915_UPLOAD_FOG);
      i915->state.Fog[I915_FOGREG_MODE0] = _3DSTATE_FOG_MODE_CMD;
      i915->state.Fog[I915_FOGREG_MODE1] = (FMC1_FOGFUNC_MODIFY_ENABLE |
					  FMC1_FOGFUNC_VERTEX |
					  FMC1_FOGINDEX_MODIFY_ENABLE |
					  FMC1_FOGINDEX_W |
					  FMC1_C1_C2_MODIFY_ENABLE |
					  FMC1_DENSITY_MODIFY_ENABLE);
      i915->state.Fog[I915_FOGREG_COLOR] = _3DSTATE_FOG_COLOR_CMD;
   }


   {
      I915_STATECHANGE(i915, I915_UPLOAD_BUFFERS);
      i915->state.Buffer[I915_DESTREG_CBUFADDR0] = _3DSTATE_BUF_INFO_CMD;
      i915->state.Buffer[I915_DESTREG_CBUFADDR1] = 
	 (BUF_3D_ID_COLOR_BACK | 
	  BUF_3D_PITCH(screen->frontPitch * screen->cpp) |
	  BUF_3D_USE_FENCE);


      i915->state.Buffer[I915_DESTREG_DBUFADDR0] = _3DSTATE_BUF_INFO_CMD;
      i915->state.Buffer[I915_DESTREG_DBUFADDR1] = 
	 (BUF_3D_ID_DEPTH |
	  BUF_3D_PITCH(screen->depthPitch * screen->cpp) |
	  BUF_3D_USE_FENCE);
      i915->state.Buffer[I915_DESTREG_DBUFADDR2] = screen->depthOffset;


      i915->state.Buffer[I915_DESTREG_DV0] = _3DSTATE_DST_BUF_VARS_CMD;

      switch (screen->fbFormat) {
      case DV_PF_555:
      case DV_PF_565:
	 i915->state.Buffer[I915_DESTREG_DV1] = (DSTORG_HORT_BIAS(0x8) | /* .5 */
					       DSTORG_VERT_BIAS(0x8) | /* .5 */
					       LOD_PRECLAMP_OGL |
					       TEX_DEFAULT_COLOR_OGL |
					       DITHER_FULL_ALWAYS |
					       screen->fbFormat |
					       DEPTH_FRMT_16_FIXED);
	 break;
      case DV_PF_8888:
	 i915->state.Buffer[I915_DESTREG_DV1] = (DSTORG_HORT_BIAS(0x8) | /* .5 */
					       DSTORG_VERT_BIAS(0x8) | /* .5 */
					       LOD_PRECLAMP_OGL |
					       TEX_DEFAULT_COLOR_OGL |
					       screen->fbFormat |
					       DEPTH_FRMT_24_FIXED_8_OTHER);
	 break;
      }
      i915->state.Buffer[I915_DESTREG_SENABLE] = (_3DSTATE_SCISSOR_ENABLE_CMD |
						DISABLE_SCISSOR_RECT);
      i915->state.Buffer[I915_DESTREG_SR0] = _3DSTATE_SCISSOR_RECT_0_CMD;
      i915->state.Buffer[I915_DESTREG_SR1] = 0;
      i915->state.Buffer[I915_DESTREG_SR2] = 0;
   }


   /* These will be emitted every at the head of every buffer, unless
    * we get hardware contexts working.
    */
   i915->state.active = (I915_UPLOAD_PROGRAM | 
			I915_UPLOAD_STIPPLE | 
			I915_UPLOAD_CTX | 
			I915_UPLOAD_BUFFERS);
}

void i915InitStateFunctions( struct dd_function_table *functions )
{
   functions->AlphaFunc = i915AlphaFunc;
   functions->BlendColor = i915BlendColor;
   functions->BlendEquationSeparate = i915BlendEquationSeparate;
   functions->BlendFuncSeparate = i915BlendFuncSeparate;
   functions->ColorMask = i915ColorMask;
   functions->CullFace = i915CullFaceFrontFace;
   functions->DepthFunc = i915DepthFunc;
   functions->DepthMask = i915DepthMask;
   functions->Enable = i915Enable;
   functions->Fogfv = i915Fogfv;
   functions->FrontFace = i915CullFaceFrontFace;
   functions->Hint = i915Hint;
   functions->LightModelfv = i915LightModelfv;
   functions->LineWidth = i915LineWidth;
   functions->LogicOpcode = i915LogicOp;
   functions->PointSize = i915PointSize;
   functions->PolygonStipple = i915PolygonStipple;
   functions->Scissor = i915Scissor;
   functions->ShadeModel = i915ShadeModel;
   functions->StencilFunc = i915StencilFunc;
   functions->StencilMask = i915StencilMask;
   functions->StencilOp = i915StencilOp;
}


void i915InitState( i915ContextPtr i915 )
{
   GLcontext *ctx = &i915->intel.ctx;

   i915_init_packets( i915 );

   intelInitState( ctx );

   memcpy( &i915->initial, &i915->state, sizeof(i915->state) );
   i915->current = &i915->state;
}







