/* $XFree86: xc/lib/GL/mesa/src/drv/gamma/gamma_span.c,v 1.4 2002/11/05 17:46:07 tsi Exp $ */

#include "gamma_context.h"
#include "gamma_lock.h"
#include "colormac.h"

#include "swrast/swrast.h"

#define DBG 0

#define LOCAL_VARS							\
   gammaContextPtr gmesa = GAMMA_CONTEXT(ctx);				\
   gammaScreenPtr gammascrn = gmesa->gammaScreen;			\
   __DRIscreenPrivate *sPriv = gmesa->driScreen;			\
   __DRIdrawablePrivate *dPriv = gmesa->driDrawable;			\
   GLuint pitch = sPriv->fbWidth * gammascrn->cpp;		\
   GLuint height = dPriv->h;						\
   char *buf = (char *)(sPriv->pFB +					\
			gmesa->drawOffset +				\
			(dPriv->x * gammascrn->cpp) +			\
			(dPriv->y * pitch));				\
   char *read_buf = (char *)(sPriv->pFB +				\
			     gmesa->readOffset +			\
			     (dPriv->x * gammascrn->cpp) +		\
			     (dPriv->y * pitch));			\
   GLuint p;								\
   (void) read_buf; (void) buf; (void) p

/* FIXME! Depth/Stencil read/writes don't work ! */
#define LOCAL_DEPTH_VARS				\
   gammaScreenPtr gammascrn = gmesa->gammaScreen;	\
   __DRIdrawablePrivate *dPriv = gmesa->driDrawable;	\
   __DRIscreenPrivate *sPriv = gmesa->driScreen;	\
   GLuint pitch = gammascrn->depthPitch;		\
   GLuint height = dPriv->h;				\
   char *buf = (char *)(sPriv->pFB +			\
			gammascrn->depthOffset +	\
			dPriv->x * gammascrn->cpp +	\
			dPriv->y * pitch)

#define LOCAL_STENCIL_VARS	LOCAL_DEPTH_VARS


#define CLIPPIXEL( _x, _y )						\
   ((_x >= minx) && (_x < maxx) && (_y >= miny) && (_y < maxy))


#define CLIPSPAN( _x, _y, _n, _x1, _n1, _i )				\
   if ( _y < miny || _y >= maxy ) {					\
      _n1 = 0, _x1 = x;							\
   } else {								\
      _n1 = _n;								\
      _x1 = _x;								\
      if ( _x1 < minx ) _i += (minx-_x1), n1 -= (minx-_x1), _x1 = minx; \
      if ( _x1 + _n1 >= maxx ) n1 -= (_x1 + n1 - maxx);		        \
   }

#define Y_FLIP( _y )		(height - _y - 1)

#define HW_LOCK()							\
   gammaContextPtr gmesa = GAMMA_CONTEXT(ctx);				\
   FLUSH_DMA_BUFFER(gmesa);						\
   gammaGetLock( gmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT );		\
   GAMMAHW_LOCK( gmesa );

#define HW_CLIPLOOP()							\
   do {									\
      __DRIdrawablePrivate *dPriv = gmesa->driDrawable;			\
      int _nc = dPriv->numClipRects;					\
									\
      while ( _nc-- ) {							\
	 int minx = dPriv->pClipRects[_nc].x1 - dPriv->x;		\
	 int miny = dPriv->pClipRects[_nc].y1 - dPriv->y;		\
	 int maxx = dPriv->pClipRects[_nc].x2 - dPriv->x;		\
	 int maxy = dPriv->pClipRects[_nc].y2 - dPriv->y;

#define HW_ENDCLIPLOOP()						\
      }									\
   } while (0)

#define HW_UNLOCK() GAMMAHW_UNLOCK( gmesa )



/* ================================================================
 * Color buffer
 */

/* 16 bit, RGB565 color spanline and pixel functions
 */
#define INIT_MONO_PIXEL(p, color) \
  p = PACK_COLOR_565( color[0], color[1], color[2] )

#define WRITE_RGBA( _x, _y, r, g, b, a )				\
   *(GLushort *)(buf + _x*2 + _y*pitch) = ((((int)r & 0xf8) << 8) |	\
					   (((int)g & 0xfc) << 3) |	\
					   (((int)b & 0xf8) >> 3))

#define WRITE_PIXEL( _x, _y, p )					\
   *(GLushort *)(buf + _x*2 + _y*pitch) = p

#define READ_RGBA( rgba, _x, _y )					\
   do {									\
      GLushort p = *(GLushort *)(read_buf + _x*2 + _y*pitch);		\
      rgba[0] = (p >> 8) & 0xf8;					\
      rgba[1] = (p >> 3) & 0xfc;					\
      rgba[2] = (p << 3) & 0xf8;					\
      rgba[3] = 0xff;							\
      if ( rgba[0] & 0x08 ) rgba[0] |= 0x07;				\
      if ( rgba[1] & 0x04 ) rgba[1] |= 0x03;				\
      if ( rgba[2] & 0x08 ) rgba[2] |= 0x07;				\
   } while (0)

#define TAG(x) gamma##x##_RGB565
#include "spantmp.h"


/* 32 bit, ARGB8888 color spanline and pixel functions
 */

#undef INIT_MONO_PIXEL
#define INIT_MONO_PIXEL(p, color) \
  p = PACK_COLOR_8888( color[3], color[0], color[1], color[2] )

#define WRITE_RGBA( _x, _y, r, g, b, a )				\
   *(GLuint *)(buf + _x*4 + _y*pitch) = ((b <<  0) |			\
					 (g <<  8) |			\
					 (r << 16) |			\
					 (a << 24) )

#define WRITE_PIXEL( _x, _y, p )					\
   *(GLuint *)(buf + _x*4 + _y*pitch) = p

#define READ_RGBA( rgba, _x, _y )					\
do {									\
   GLuint p = *(GLuint *)(read_buf + _x*4 + _y*pitch);			\
   rgba[0] = (p >> 16) & 0xff;						\
   rgba[1] = (p >>  8) & 0xff;						\
   rgba[2] = (p >>  0) & 0xff;						\
   rgba[3] = (p >> 24) & 0xff;						\
} while (0)

#define TAG(x) gamma##x##_ARGB8888
#include "spantmp.h"


/* 16 bit depthbuffer functions.
 */
#define WRITE_DEPTH( _x, _y, d )	\
   *(GLushort *)(buf + _x*2 + _y*pitch) = d;

#define READ_DEPTH( d, _x, _y )		\
   d = *(GLushort *)(buf + _x*2 + _y*pitch);	

#define TAG(x) gamma##x##_16
#include "depthtmp.h"



#if 0 /* Unused */
/* 32 bit depthbuffer functions.
 */
#define WRITE_DEPTH( _x, _y, d )	\
   *(GLuint *)(buf + _x*4 + _y*pitch) = d;

#define READ_DEPTH( d, _x, _y )		\
   d = *(GLuint *)(buf + _x*4 + _y*pitch);	

#define TAG(x) gamma##x##_32
#include "depthtmp.h"
#endif


/* 24/8 bit interleaved depth/stencil functions
 */
#define WRITE_DEPTH( _x, _y, d ) {			\
   GLuint tmp = *(GLuint *)(buf + _x*4 + _y*pitch);	\
   tmp &= 0xff;						\
   tmp |= (d) & 0xffffff00;				\
   *(GLuint *)(buf + _x*4 + _y*pitch) = tmp;		\
}

#define READ_DEPTH( d, _x, _y )		\
   d = *(GLuint *)(buf + _x*4 + _y*pitch) & ~0xff;	


#define TAG(x) gamma##x##_24_8
#include "depthtmp.h"

#if 0
#define WRITE_STENCIL( _x, _y, d ) {			\
   GLuint tmp = *(GLuint *)(buf + _x*4 + _y*pitch);	\
   tmp &= 0xffffff00;					\
   tmp |= d & 0xff;					\
   *(GLuint *)(buf + _x*4 + _y*pitch) = tmp;		\
}

#define READ_STENCIL( d, _x, _y )		\
   d = *(GLuint *)(buf + _x*4 + _y*pitch) & 0xff;	

#define TAG(x) gamma##x##_24_8
#include "stenciltmp.h"

static void gammaReadRGBASpan8888( const GLcontext *ctx,
			       GLuint n, GLint x, GLint y,
			       GLubyte rgba[][4])
{
   gammaContextPtr gmesa = GAMMA_CONTEXT(ctx);
   gammaScreenPtr gammascrn = gmesa->gammaScreen;
   uint32_t dwords1, dwords2, i = 0;
   char *src = (char *)rgba[0];
   GLuint read = n * gammascrn->cpp; /* Number of bytes we are expecting */
   uint32_t data;

   FLUSH_DMA_BUFFER(gmesa);
   CHECK_DMA_BUFFER(gmesa, 16);
   WRITE(gmesa->buf, LBReadMode, gmesa->LBReadMode & ~(LBReadSrcEnable | LBReadDstEnable));
   WRITE(gmesa->buf, ColorDDAMode, ColorDDAEnable);
   WRITE(gmesa->buf, LBWriteMode, LBWriteModeDisable);
   WRITE(gmesa->buf, FBReadMode, (gmesa->FBReadMode & ~FBReadSrcEnable) | FBReadDstEnable | FBDataTypeColor);
   WRITE(gmesa->buf, FilterMode, 0x200); /* Pass FBColorData */
   WRITE(gmesa->buf, FBWriteMode, FBW_UploadColorData | FBWriteModeDisable);
   WRITE(gmesa->buf, StartXSub, (x+n)<<16);
   WRITE(gmesa->buf, StartXDom, x<<16);
   WRITE(gmesa->buf, StartY, y<<16);
   WRITE(gmesa->buf, GLINTCount, 1);
   WRITE(gmesa->buf, dXDom, 0<<16);
   WRITE(gmesa->buf, dXSub, 0<<16);
   WRITE(gmesa->buf, dY, 1<<16);
   WRITE(gmesa->buf, Render, PrimitiveTrapezoid);
   FLUSH_DMA_BUFFER(gmesa);

moredata:

   dwords1 = *(volatile uint32_t*)(void *)(((uint8_t*)gammascrn->regions[0].map) + (GlintOutFIFOWords));
   dwords2 = *(volatile uint32_t*)(void *)(((uint8_t*)gammascrn->regions[2].map) + (GlintOutFIFOWords));

   if (dwords1) {
	memcpy(src, (char*)gammascrn->regions[1].map + 0x1000, dwords1 << 2);
	src += dwords1 << 2;
	read -= dwords1 << 2;
   }
   if (dwords2) {
	memcpy(src, (char*)gammascrn->regions[3].map + 0x1000, dwords2 << 2);
	src += dwords2 << 2;
	read -= dwords2 << 2;
   }

   if (read)
	goto moredata;

done:

   CHECK_DMA_BUFFER(gmesa, 6);
   WRITE(gmesa->buf, ColorDDAMode, gmesa->ColorDDAMode);
   WRITE(gmesa->buf, LBWriteMode, LBWriteModeEnable);
   WRITE(gmesa->buf, LBReadMode, gmesa->LBReadMode);
   WRITE(gmesa->buf, FBReadMode, gmesa->FBReadMode);
   WRITE(gmesa->buf, FBWriteMode, FBWriteModeEnable);
   WRITE(gmesa->buf, FilterMode, 0x400);
}
#endif

static void gammaSetBuffer( GLcontext *ctx,
                            GLframebuffer *colorBuffer,
                            GLuint bufferBit )
{
   gammaContextPtr gmesa = GAMMA_CONTEXT(ctx);

   switch ( bufferBit ) {
   case DD_FRONT_LEFT_BIT:
      gmesa->readOffset = 0;
      break;
   case DD_BACK_LEFT_BIT:
      gmesa->readOffset = gmesa->driScreen->fbHeight * gmesa->driScreen->fbWidth * gmesa->gammaScreen->cpp; 
      break;
   default:
      _mesa_problem(ctx, "Unexpected buffer 0x%x in gammaSetBuffer()", bufferBit);
   }
}


void gammaDDInitSpanFuncs( GLcontext *ctx )
{
   gammaContextPtr gmesa = GAMMA_CONTEXT(ctx);
   struct swrast_device_driver *swdd = _swrast_GetDeviceDriverReference(ctx);

   swdd->SetBuffer = gammaSetBuffer;

   switch ( gmesa->gammaScreen->cpp ) {
   case 2:
      swdd->WriteRGBASpan	= gammaWriteRGBASpan_RGB565;
      swdd->WriteRGBSpan	= gammaWriteRGBSpan_RGB565;
      swdd->WriteMonoRGBASpan	= gammaWriteMonoRGBASpan_RGB565;
      swdd->WriteRGBAPixels	= gammaWriteRGBAPixels_RGB565;
      swdd->WriteMonoRGBAPixels	= gammaWriteMonoRGBAPixels_RGB565;
      swdd->ReadRGBASpan	= gammaReadRGBASpan_RGB565;
      swdd->ReadRGBAPixels      = gammaReadRGBAPixels_RGB565;
      break;

   case 4:
      swdd->WriteRGBASpan	= gammaWriteRGBASpan_ARGB8888;
      swdd->WriteRGBSpan	= gammaWriteRGBSpan_ARGB8888;
      swdd->WriteMonoRGBASpan   = gammaWriteMonoRGBASpan_ARGB8888;
      swdd->WriteRGBAPixels     = gammaWriteRGBAPixels_ARGB8888;
      swdd->WriteMonoRGBAPixels = gammaWriteMonoRGBAPixels_ARGB8888;
#if 1
      swdd->ReadRGBASpan	= gammaReadRGBASpan_ARGB8888;
#else
      swdd->ReadRGBASpan	= gammaReadRGBASpan8888;
#endif
      swdd->ReadRGBAPixels      = gammaReadRGBAPixels_ARGB8888;
      break;

   default:
      break;
   }

   switch ( gmesa->glCtx->Visual.depthBits ) {
   case 16:
      swdd->ReadDepthSpan	= gammaReadDepthSpan_16;
      swdd->WriteDepthSpan	= gammaWriteDepthSpan_16;
      swdd->ReadDepthPixels	= gammaReadDepthPixels_16;
      swdd->WriteDepthPixels	= gammaWriteDepthPixels_16;
      break;

   case 24:
      swdd->ReadDepthSpan	= gammaReadDepthSpan_24_8;
      swdd->WriteDepthSpan	= gammaWriteDepthSpan_24_8;
      swdd->ReadDepthPixels	= gammaReadDepthPixels_24_8;
      swdd->WriteDepthPixels	= gammaWriteDepthPixels_24_8;

#if 0
      swdd->ReadStencilSpan	= gammaReadStencilSpan_24_8;
      swdd->WriteStencilSpan	= gammaWriteStencilSpan_24_8;
      swdd->ReadStencilPixels	= gammaReadStencilPixels_24_8;
      swdd->WriteStencilPixels	= gammaWriteStencilPixels_24_8;
#endif
      break;

   default:
      break;
   }
}
