/*
 * Copyright 2007 Ben Skeggs
 * Copyright 2007 Stephane Marchesin
 * Copyright 2007 Jeremy Kolb
 * Copyright 2007 Patrice Mandin
 *
 * 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, sublicense,
 * 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 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS 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 "nv_include.h"
#include "nv30_shaders.h"
#include "hwdefs/nv_object.xml.h"
#include "hwdefs/nv30-40_3d.xml.h"

typedef struct nv_pict_surface_format {
	int	 pict_fmt;
	uint32_t card_fmt;
} nv_pict_surface_format_t;

typedef struct nv_pict_texture_format {
	int	 pict_fmt;
	uint32_t card_fmt;
	uint32_t card_swz;
} nv_pict_texture_format_t;

typedef struct nv_pict_op {
	Bool	 src_alpha;
	Bool	 dst_alpha;
	uint32_t src_card_op;
	uint32_t dst_card_op;
} nv_pict_op_t;

typedef struct nv30_exa_state {
	Bool have_mask;

	struct {
		PictTransformPtr transform;
		float width;
		float height;
	} unit[2];
} nv30_exa_state_t;
static nv30_exa_state_t exa_state;
#define NV30EXA_STATE nv30_exa_state_t *state = &exa_state

static nv_pict_surface_format_t
NV30SurfaceFormat[] = {
	{ PICT_a8r8g8b8	, 0x148 },
	{ PICT_a8b8g8r8	, 0x150 },
	{ PICT_x8r8g8b8	, 0x145 },
	{ PICT_x8b8g8r8	, 0x14f },
	{ PICT_r5g6b5	, 0x143 },
	{ PICT_a8       , 0x149 },
	{ PICT_x1r5g5b5	, 0x142 },
};

static nv_pict_surface_format_t *
NV30_GetPictSurfaceFormat(int format)
{
	int i;

	for(i=0;i<sizeof(NV30SurfaceFormat)/sizeof(NV30SurfaceFormat[0]);i++)
	{
		if (NV30SurfaceFormat[i].pict_fmt == format)
			return &NV30SurfaceFormat[i];
	}

	return NULL;
}

enum {
	NV30EXA_FPID_PASS_COL0 = 0,
	NV30EXA_FPID_PASS_TEX0 = 1,
	NV30EXA_FPID_COMPOSITE_MASK = 2,
	NV30EXA_FPID_COMPOSITE_MASK_SA_CA = 3,
	NV30EXA_FPID_COMPOSITE_MASK_CA = 4,
	NV30EXA_FPID_MAX = 5
} NV30EXA_FPID;

static nv_shader_t *nv40_fp_map[NV30EXA_FPID_MAX] = {
	&nv30_fp_pass_col0,
	&nv30_fp_pass_tex0,
	&nv30_fp_composite_mask,
	&nv30_fp_composite_mask_sa_ca,
	&nv30_fp_composite_mask_ca
};

static nv_shader_t *nv40_fp_map_a8[NV30EXA_FPID_MAX];

static void
NV30EXAHackupA8Shaders(ScrnInfoPtr pScrn)
{
	int s;

	for (s = 0; s < NV30EXA_FPID_MAX; s++) {
		nv_shader_t *def, *a8;

		def = nv40_fp_map[s];
		a8 = calloc(1, sizeof(nv_shader_t));
		a8->card_priv.NV30FP.num_regs = def->card_priv.NV30FP.num_regs;
		a8->size = def->size + 4;
		memcpy(a8->data, def->data, def->size * sizeof(uint32_t));
		nv40_fp_map_a8[s] = a8;

		a8->data[a8->size - 8 + 0] &= ~0x00000081;
		a8->data[a8->size - 4 + 0]  = 0x01401e81;
		a8->data[a8->size - 4 + 1]  = 0x1c9dfe00;
		a8->data[a8->size - 4 + 2]  = 0x0001c800;
		a8->data[a8->size - 4 + 3]  = 0x0001c800;
	}
}

/* should be in nouveau_reg.h at some point.. */
#define NV30_3D_TEX_SWIZZLE_UNIT_S0_X_ZERO	 0
#define NV30_3D_TEX_SWIZZLE_UNIT_S0_X_ONE	 1
#define NV30_3D_TEX_SWIZZLE_UNIT_S0_X_S1		 2

#define NV30_3D_TEX_SWIZZLE_UNIT_S0_X_SHIFT	14
#define NV30_3D_TEX_SWIZZLE_UNIT_S0_Y_SHIFT	12
#define NV30_3D_TEX_SWIZZLE_UNIT_S0_Z_SHIFT	10
#define NV30_3D_TEX_SWIZZLE_UNIT_S0_W_SHIFT	 8

#define NV30_3D_TEX_SWIZZLE_UNIT_S1_X_X		 3
#define NV30_3D_TEX_SWIZZLE_UNIT_S1_X_Y		 2
#define NV30_3D_TEX_SWIZZLE_UNIT_S1_X_Z		 1
#define NV30_3D_TEX_SWIZZLE_UNIT_S1_X_W		 0

#define NV30_3D_TEX_SWIZZLE_UNIT_S1_X_SHIFT	 6
#define NV30_3D_TEX_SWIZZLE_UNIT_S1_Y_SHIFT	 4
#define NV30_3D_TEX_SWIZZLE_UNIT_S1_Z_SHIFT	 2
#define NV30_3D_TEX_SWIZZLE_UNIT_S1_W_SHIFT	 0

#define _(r,tf,ts0x,ts0y,ts0z,ts0w,ts1x,ts1y,ts1z,ts1w)                       \
  {                                                                           \
  PICT_##r,                                                                   \
  (tf),                                                                       \
  (NV30_3D_TEX_SWIZZLE_UNIT_S0_X_##ts0x << NV30_3D_TEX_SWIZZLE_UNIT_S0_X_SHIFT)|\
  (NV30_3D_TEX_SWIZZLE_UNIT_S0_X_##ts0y << NV30_3D_TEX_SWIZZLE_UNIT_S0_Y_SHIFT)|\
  (NV30_3D_TEX_SWIZZLE_UNIT_S0_X_##ts0z << NV30_3D_TEX_SWIZZLE_UNIT_S0_Z_SHIFT)|\
  (NV30_3D_TEX_SWIZZLE_UNIT_S0_X_##ts0w << NV30_3D_TEX_SWIZZLE_UNIT_S0_W_SHIFT)|\
  (NV30_3D_TEX_SWIZZLE_UNIT_S1_X_##ts1x << NV30_3D_TEX_SWIZZLE_UNIT_S1_X_SHIFT)|\
  (NV30_3D_TEX_SWIZZLE_UNIT_S1_X_##ts1y << NV30_3D_TEX_SWIZZLE_UNIT_S1_Y_SHIFT)|\
  (NV30_3D_TEX_SWIZZLE_UNIT_S1_X_##ts1z << NV30_3D_TEX_SWIZZLE_UNIT_S1_Z_SHIFT)|\
  (NV30_3D_TEX_SWIZZLE_UNIT_S1_X_##ts1w << NV30_3D_TEX_SWIZZLE_UNIT_S1_W_SHIFT)\
  }

static nv_pict_texture_format_t
NV30TextureFormat[] = {
	_(a8r8g8b8, 0x12,   S1,   S1,   S1,   S1, X, Y, Z, W),
	_(a8b8g8r8, 0x12,   S1,   S1,   S1,   S1, Z, Y, X, W),
	_(x8r8g8b8, 0x12,   S1,   S1,   S1,  ONE, X, Y, Z, W),
	_(x8b8g8r8, 0x12,   S1,   S1,   S1,  ONE, Z, Y, X, W),

	_(a1r5g5b5, 0x10,   S1,   S1,   S1,   S1, X, Y, Z, W),
	_(x1r5g5b5, 0x10,   S1,   S1,   S1,  ONE, X, Y, Z, W),
	_(a1b5g5r5, 0x10,   S1,   S1,   S1,   S1, Z, Y, X, W),
	_(x1b5g5r5, 0x10,   S1,   S1,   S1,  ONE, Z, Y, X, W),

	_(x4r4g4b4, 0x1d,   S1,   S1,   S1,  ONE, X, Y, Z, W),
	_(a4r4g4b4, 0x1d,   S1,   S1,   S1,   S1, X, Y, Z, W),
	_(x4b4g4r4, 0x1d,   S1,   S1,   S1,  ONE, Z, Y, X, W),
	_(a4b4g4r4, 0x1d,   S1,   S1,   S1,   S1, Z, Y, X, W),

	_(      a8, 0x1b, ZERO, ZERO, ZERO,   S1, X, X, X, X),

	_(  r5g6b5, 0x11,   S1,   S1,   S1,  ONE, X, Y, Z, W),
	_(  b5g6r5, 0x11,   S1,   S1,   S1,  ONE, Z, Y, X, W),
};


static nv_pict_texture_format_t *
NV30_GetPictTextureFormat(int format)
{
	int i;

	for(i=0;i<sizeof(NV30TextureFormat)/sizeof(NV30TextureFormat[0]);i++)
	{
		if (NV30TextureFormat[i].pict_fmt == format)
			return &NV30TextureFormat[i];
	}

	return NULL;
}

#define NV30_3D_BF_ZERO                                     0x0000
#define NV30_3D_BF_ONE                                      0x0001
#define NV30_3D_BF_SRC_COLOR                                0x0300
#define NV30_3D_BF_ONE_MINUS_SRC_COLOR                      0x0301
#define NV30_3D_BF_SRC_ALPHA                                0x0302
#define NV30_3D_BF_ONE_MINUS_SRC_ALPHA                      0x0303
#define NV30_3D_BF_DST_ALPHA                                0x0304
#define NV30_3D_BF_ONE_MINUS_DST_ALPHA                      0x0305
#define NV30_3D_BF_DST_COLOR                                0x0306
#define NV30_3D_BF_ONE_MINUS_DST_COLOR                      0x0307
#define NV30_3D_BF_ALPHA_SATURATE                           0x0308
#define BF(bf) NV30_3D_BF_##bf

static nv_pict_op_t 
NV30PictOp[] = {
/* Clear       */ { 0, 0, BF(               ZERO), BF(               ZERO) },
/* Src         */ { 0, 0, BF(                ONE), BF(               ZERO) },
/* Dst         */ { 0, 0, BF(               ZERO), BF(                ONE) },
/* Over        */ { 1, 0, BF(                ONE), BF(ONE_MINUS_SRC_ALPHA) },
/* OverReverse */ { 0, 1, BF(ONE_MINUS_DST_ALPHA), BF(                ONE) },
/* In          */ { 0, 1, BF(          DST_ALPHA), BF(               ZERO) },
/* InReverse   */ { 1, 0, BF(               ZERO), BF(          SRC_ALPHA) },
/* Out         */ { 0, 1, BF(ONE_MINUS_DST_ALPHA), BF(               ZERO) },
/* OutReverse  */ { 1, 0, BF(               ZERO), BF(ONE_MINUS_SRC_ALPHA) },
/* Atop        */ { 1, 1, BF(          DST_ALPHA), BF(ONE_MINUS_SRC_ALPHA) },
/* AtopReverse */ { 1, 1, BF(ONE_MINUS_DST_ALPHA), BF(          SRC_ALPHA) },
/* Xor         */ { 1, 1, BF(ONE_MINUS_DST_ALPHA), BF(ONE_MINUS_SRC_ALPHA) },
/* Add         */ { 0, 0, BF(                ONE), BF(                ONE) }
};

static nv_pict_op_t *
NV30_GetPictOpRec(int op)
{
	if (op >= PictOpSaturate)
		return NULL;
#if 0
	switch(op)
	{
		case 0:ErrorF("Op Clear\n");break;
		case 1:ErrorF("Op Src\n");break;
		case 2:ErrorF("Op Dst\n");break;
		case 3:ErrorF("Op Over\n");break;
		case 4:ErrorF("Op OverReverse\n");break;
		case 5:ErrorF("Op In\n");break;
		case 6:ErrorF("Op InReverse\n");break;
		case 7:ErrorF("Op Out\n");break;
		case 8:ErrorF("Op OutReverse\n");break;
		case 9:ErrorF("Op Atop\n");break;
		case 10:ErrorF("Op AtopReverse\n");break;
		case 11:ErrorF("Op Xor\n");break;
		case 12:ErrorF("Op Add\n");break;
	}
#endif
	return &NV30PictOp[op];
}

static void
NV30_SetupBlend(ScrnInfoPtr pScrn, nv_pict_op_t *blend,
		PictFormatShort dest_format, Bool component_alpha)
{
	NVPtr pNv = NVPTR(pScrn);
	struct nouveau_channel *chan = pNv->chan;
	uint32_t sblend, dblend;

	sblend = blend->src_card_op;
	dblend = blend->dst_card_op;

	if (blend->dst_alpha) {
		if (!PICT_FORMAT_A(dest_format)) {
			if (sblend == BF(DST_ALPHA)) {
				sblend = BF(ONE);
			} else if (sblend == BF(ONE_MINUS_DST_ALPHA)) {
				sblend = BF(ZERO);
			}
		} else if (dest_format == PICT_a8) {
			if (sblend == BF(DST_ALPHA)) {
				sblend = BF(DST_COLOR);
			} else if (sblend == BF(ONE_MINUS_DST_ALPHA)) {
				sblend = BF(ONE_MINUS_DST_COLOR);
			}
		}
	}

	if (blend->src_alpha && (component_alpha || dest_format == PICT_a8)) {
		if (dblend == BF(SRC_ALPHA)) {
			dblend = BF(SRC_COLOR);
		} else if (dblend == BF(ONE_MINUS_SRC_ALPHA)) {
			dblend = BF(ONE_MINUS_SRC_COLOR);
		}
	}

	if (sblend == BF(ONE) && dblend == BF(ZERO)) {
		BEGIN_NV04(chan, NV30_3D(BLEND_FUNC_ENABLE), 1);
		OUT_RING  (chan, 0);
	} else {
		BEGIN_NV04(chan, NV30_3D(BLEND_FUNC_ENABLE), 3);
		OUT_RING  (chan, 1);
		OUT_RING  (chan, (sblend << 16) | sblend);
		OUT_RING  (chan, (dblend << 16) | dblend);
	}
}

static Bool
NV30EXATexture(ScrnInfoPtr pScrn, PixmapPtr pPix, PicturePtr pPict, int unit)
{
	NVPtr pNv = NVPTR(pScrn);
	struct nouveau_channel *chan = pNv->chan;
	struct nouveau_bo *bo = nouveau_pixmap_bo(pPix);
	nv_pict_texture_format_t *fmt;
	uint32_t card_filter, card_repeat;
	uint32_t tex_reloc = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_RD;
	NV30EXA_STATE;

	fmt = NV30_GetPictTextureFormat(pPict->format);
	if (!fmt)
		return FALSE;

	card_repeat = 3; /* repeatNone */

	if (pPict->filter == PictFilterBilinear)
		card_filter = 2;
	else
		card_filter = 1;

	BEGIN_NV04(chan, NV30_3D(TEX_OFFSET(unit)), 8);
	if (OUT_RELOCl(chan, bo, 0, tex_reloc) ||
	    OUT_RELOCd(chan, bo, NV30_3D_TEX_FORMAT_DIMS_2D | (1 << 16) | 8 |
		       (fmt->card_fmt << NV30_3D_TEX_FORMAT_FORMAT__SHIFT) |
		       (log2i(pPix->drawable.width) <<
			NV30_3D_TEX_FORMAT_BASE_SIZE_U__SHIFT) |
		       (log2i(pPix->drawable.height) <<
			NV30_3D_TEX_FORMAT_BASE_SIZE_V__SHIFT),
		       tex_reloc | NOUVEAU_BO_OR,
		       NV30_3D_TEX_FORMAT_DMA0, NV30_3D_TEX_FORMAT_DMA1))
		return FALSE;
	OUT_RING  (chan, (card_repeat << NV30_3D_TEX_WRAP_S__SHIFT) |
			(card_repeat << NV30_3D_TEX_WRAP_T__SHIFT) |
			(card_repeat << NV30_3D_TEX_WRAP_R__SHIFT));
	OUT_RING  (chan, NV30_3D_TEX_ENABLE_ENABLE);
	OUT_RING  (chan, (((uint32_t)exaGetPixmapPitch(pPix)) << NV30_3D_TEX_SWIZZLE_RECT_PITCH__SHIFT ) | 
			fmt->card_swz);

	OUT_RING  (chan, (card_filter << NV30_3D_TEX_FILTER_MIN__SHIFT) /* min */ |
			(card_filter << NV30_3D_TEX_FILTER_MAG__SHIFT) /* mag */ |
			0x2000 /* engine lock */);
	OUT_RING  (chan, (pPix->drawable.width << NV30_3D_TEX_NPOT_SIZE_W__SHIFT) | pPix->drawable.height);
	OUT_RING  (chan, 0); /* border ARGB */

	state->unit[unit].width		= (float)pPix->drawable.width;
	state->unit[unit].height	= (float)pPix->drawable.height;
	state->unit[unit].transform	= pPict->transform;

	return TRUE;
}

static Bool
NV30_SetupSurface(ScrnInfoPtr pScrn, PixmapPtr pPix, PicturePtr pPict)
{
	NVPtr pNv = NVPTR(pScrn);
	struct nouveau_channel *chan = pNv->chan;
	struct nouveau_bo *bo = nouveau_pixmap_bo(pPix);
	nv_pict_surface_format_t *fmt;

	fmt = NV30_GetPictSurfaceFormat(pPict->format);
	if (!fmt) {
		ErrorF("AIII no format\n");
		return FALSE;
	}

	uint32_t pitch = (uint32_t)exaGetPixmapPitch(pPix);

	BEGIN_NV04(chan, NV30_3D(RT_FORMAT), 3);
	OUT_RING  (chan, fmt->card_fmt); /* format */
	OUT_RING  (chan, pitch << 16 | pitch);
	if (OUT_RELOCl(chan, bo, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR))
		return FALSE;

	return TRUE;
}

static Bool
NV30EXACheckCompositeTexture(PicturePtr pPict, PicturePtr pdPict, int op)
{
	nv_pict_texture_format_t *fmt;
	int w, h;

	if (!pPict->pDrawable)
		NOUVEAU_FALLBACK("Solid and gradient pictures unsupported\n");

	w = pPict->pDrawable->width;
	h = pPict->pDrawable->height;

	if ((w > 4096) || (h > 4096))
		NOUVEAU_FALLBACK("picture too large, %dx%d\n", w, h);

	fmt = NV30_GetPictTextureFormat(pPict->format);
	if (!fmt)
		NOUVEAU_FALLBACK("picture format 0x%08x not supported\n",
				pPict->format);

	if (pPict->filter != PictFilterNearest &&
			pPict->filter != PictFilterBilinear)
		NOUVEAU_FALLBACK("filter 0x%x not supported\n", pPict->filter);

	if (!(w==1 && h==1) && pPict->repeat && pPict->repeatType != RepeatNone)
		NOUVEAU_FALLBACK("repeat 0x%x not supported (surface %dx%d)\n",
				 pPict->repeatType,w,h);

	/* Opengl and Render disagree on what should be sampled outside an XRGB 
	 * texture (with no repeating). Opengl has a hardcoded alpha value of 
	 * 1.0, while render expects 0.0. We assume that clipping is done for 
	 * untranformed sources.
	 */
	if (NV30PictOp[op].src_alpha && !pPict->repeat &&
		pPict->transform && (PICT_FORMAT_A(pPict->format) == 0)
		&& (PICT_FORMAT_A(pdPict->format) != 0))
		NOUVEAU_FALLBACK("REPEAT_NONE unsupported for XRGB source\n");

	return TRUE;
}

Bool
NV30EXACheckComposite(int op, PicturePtr psPict,
		PicturePtr pmPict,
		PicturePtr pdPict)
{
	nv_pict_surface_format_t *fmt;
	nv_pict_op_t *opr;

	opr = NV30_GetPictOpRec(op);
	if (!opr)
		NOUVEAU_FALLBACK("unsupported blend op 0x%x\n", op);

	fmt = NV30_GetPictSurfaceFormat(pdPict->format);
	if (!fmt)
		NOUVEAU_FALLBACK("dst picture format 0x%08x not supported\n",
				pdPict->format);

	if (!NV30EXACheckCompositeTexture(psPict, pdPict, op))
		NOUVEAU_FALLBACK("src picture\n");
	if (pmPict) {
		if (pmPict->componentAlpha &&
				PICT_FORMAT_RGB(pmPict->format) &&
				opr->src_alpha && opr->src_card_op != BF(ZERO))
			NOUVEAU_FALLBACK("mask CA + SA\n");
		if (!NV30EXACheckCompositeTexture(pmPict, pdPict, op))
			NOUVEAU_FALLBACK("mask picture\n");
	}

	return TRUE;
}

static void
NV30EXAStateCompositeReemit(struct nouveau_channel *chan)
{
	ScrnInfoPtr pScrn = chan->user_private;
	NVPtr pNv = NVPTR(pScrn);

	NV30EXAPrepareComposite(pNv->alu, pNv->pspict, pNv->pmpict, pNv->pdpict,
				pNv->pspix, pNv->pmpix, pNv->pdpix);
}

Bool
NV30EXAPrepareComposite(int op, PicturePtr psPict,
		PicturePtr pmPict,
		PicturePtr pdPict,
		PixmapPtr  psPix,
		PixmapPtr  pmPix,
		PixmapPtr  pdPix)
{
	ScrnInfoPtr pScrn = xf86Screens[psPix->drawable.pScreen->myNum];
	NVPtr pNv = NVPTR(pScrn);
	struct nouveau_channel *chan = pNv->chan;
	nv_pict_op_t *blend;
	int fpid = NV30EXA_FPID_PASS_COL0;
	NV30EXA_STATE;

	if (MARK_RING(chan, 128, 1 + 1 + 4))
		return FALSE;

	blend = NV30_GetPictOpRec(op);

	NV30_SetupBlend(pScrn, blend, pdPict->format,
			(pmPict && pmPict->componentAlpha &&
			 PICT_FORMAT_RGB(pmPict->format)));

	if (!NV30_SetupSurface(pScrn, pdPix, pdPict) ||
	    !NV30EXATexture(pScrn, psPix, psPict, 0)) {
		MARK_UNDO(chan);
		return FALSE;
	}

#if 0
#define printformat(f) ErrorF("(%xh %s %dbpp A%dR%dG%dB%d)",f,(f>>16)&0xf==2?"ARGB":"ABGR",(f>>24),(f&0xf000)>>12,(f&0xf00)>>8,(f&0xf0)>>4,f&0xf)
	ErrorF("Preparecomposite src(%dx%d)",psPict->pDrawable->width,psPict->pDrawable->height);
	printformat((psPict->format));
	ErrorF(" dst(%dx%d)",pdPict->pDrawable->width,pdPict->pDrawable->height);
	printformat((pdPict->format));
	if (pmPict)
	{
		ErrorF(" mask(%dx%d)",pmPict->pDrawable->width,pmPict->pDrawable->height);
		printformat((pmPict->format));
	}
	ErrorF("\n");
#endif

	if (pmPict) {
		if (!NV30EXATexture(pScrn, pmPix, pmPict, 1)) {
			MARK_UNDO(chan);
			return FALSE;
		}

		if (pmPict->componentAlpha && PICT_FORMAT_RGB(pmPict->format)) {
			if (blend->src_alpha)
				fpid = NV30EXA_FPID_COMPOSITE_MASK_SA_CA;
			else
				fpid = NV30EXA_FPID_COMPOSITE_MASK_CA;
		} else {
			fpid = NV30EXA_FPID_COMPOSITE_MASK;
		}

		state->have_mask = TRUE;
	} else {
		fpid = NV30EXA_FPID_PASS_TEX0;

		state->have_mask = FALSE;
	}

	if (!NV30_LoadFragProg(pScrn, (pdPict->format == PICT_a8) ?
			       nv40_fp_map_a8[fpid] : nv40_fp_map[fpid])) {
		MARK_UNDO(chan);
		return FALSE;
	}

	BEGIN_NV04(chan, SUBC_3D(0x23c), 1);
	OUT_RING  (chan, pmPict?3:1);

	pNv->alu = op;
	pNv->pspict = psPict;
	pNv->pmpict = pmPict;
	pNv->pdpict = pdPict;
	pNv->pspix = psPix;
	pNv->pmpix = pmPix;
	pNv->pdpix = pdPix;
	chan->flush_notify = NV30EXAStateCompositeReemit;
	return TRUE;
}

#define xFixedToFloat(v) \
	((float)xFixedToInt((v)) + ((float)xFixedFrac(v) / 65536.0))

static void
NV30EXATransformCoord(PictTransformPtr t, int x, int y, float sx, float sy,
					  float *x_ret, float *y_ret)
{
	PictVector v;

	if (t) {
		v.vector[0] = IntToxFixed(x);
		v.vector[1] = IntToxFixed(y);
		v.vector[2] = xFixed1;
		PictureTransformPoint(t, &v);
		*x_ret = xFixedToFloat(v.vector[0]);
		*y_ret = xFixedToFloat(v.vector[1]);
	} else {
		*x_ret = (float)x;
		*y_ret = (float)y;
	}
}

#define CV_OUTm(sx,sy,mx,my,dx,dy) do {                                        \
	BEGIN_NV04(chan, NV30_3D(VTX_ATTR_2F_X(8)), 4);                   \
	OUT_RINGf (chan, (sx)); OUT_RINGf (chan, (sy));                        \
	OUT_RINGf (chan, (mx)); OUT_RINGf (chan, (my));                        \
	BEGIN_NV04(chan, NV30_3D(VTX_ATTR_2I(0)), 1);                     \
	OUT_RING  (chan, ((dy)<<16)|(dx));                                     \
} while(0)
#define CV_OUT(sx,sy,dx,dy) do {                                               \
	BEGIN_NV04(chan, NV30_3D(VTX_ATTR_2F_X(8)), 2);                   \
	OUT_RINGf (chan, (sx)); OUT_RINGf (chan, (sy));                        \
	BEGIN_NV04(chan, NV30_3D(VTX_ATTR_2I(0)), 1);                     \
	OUT_RING  (chan, ((dy)<<16)|(dx));                                     \
} while(0)

void
NV30EXAComposite(PixmapPtr pdPix, int srcX , int srcY,
				  int maskX, int maskY,
				  int dstX , int dstY,
				  int width, int height)
{
	ScrnInfoPtr pScrn = xf86Screens[pdPix->drawable.pScreen->myNum];
	NVPtr pNv = NVPTR(pScrn);
	struct nouveau_channel *chan = pNv->chan;
	float sX0, sX1, sX2, sY0, sY1, sY2;
	float mX0, mX1, mX2, mY0, mY1, mY2;
	NV30EXA_STATE;

	WAIT_RING(chan, 64);

	/* We're drawing a triangle, we need to scissor it to a quad. */
	/* The scissors are here for a good reason, we don't get the full image, but just a part. */
	/* Handling the cliprects is done for us already. */
	BEGIN_NV04(chan, NV30_3D(SCISSOR_HORIZ), 2);
	OUT_RING  (chan, (width << 16) | dstX);
	OUT_RING  (chan, (height << 16) | dstY);
	BEGIN_NV04(chan, NV30_3D(VERTEX_BEGIN_END), 1);
	OUT_RING  (chan, NV30_3D_VERTEX_BEGIN_END_TRIANGLES);

#if 0
	ErrorF("Composite [%dx%d] (%d,%d)IN(%d,%d)OP(%d,%d)\n",width,height,srcX,srcY,maskX,maskY,dstX,dstY);
#endif
	NV30EXATransformCoord(state->unit[0].transform, 
				srcX, srcY - height,
				state->unit[0].width,
				state->unit[0].height, &sX0, &sY0);
	NV30EXATransformCoord(state->unit[0].transform,
				srcX, srcY + height,
				state->unit[0].width,
				state->unit[0].height, &sX1, &sY1);
	NV30EXATransformCoord(state->unit[0].transform,
				srcX + 2*width, srcY + height,
				state->unit[0].width,
				state->unit[0].height, &sX2, &sY2);

	if (state->have_mask) {
		NV30EXATransformCoord(state->unit[1].transform, 
					maskX, maskY - height,
					state->unit[1].width,
					state->unit[1].height, &mX0, &mY0);
		NV30EXATransformCoord(state->unit[1].transform,
					maskX, maskY + height,
					state->unit[1].width,
					state->unit[1].height, &mX1, &mY1);
		NV30EXATransformCoord(state->unit[1].transform,
					maskX + 2*width, maskY + height,
					state->unit[1].width,
					state->unit[1].height, &mX2, &mY2);

		CV_OUTm(sX0 , sY0 , mX0, mY0, dstX			,	dstY - height);
		CV_OUTm(sX1 , sY1 , mX1, mY1, dstX			,	dstY + height);
		CV_OUTm(sX2 , sY2 , mX2, mY2, dstX + 2*width	, 	dstY + height);
	} else {
		CV_OUT(sX0 , sY0 , dstX			,	dstY - height);
		CV_OUT(sX1 , sY1 , dstX			,	dstY + height);
		CV_OUT(sX2 , sY2 , dstX + 2*width	, 	dstY + height);
	}

	BEGIN_NV04(chan, NV30_3D(VERTEX_BEGIN_END), 1);
	OUT_RING  (chan, 0);
}

void
NV30EXADoneComposite(PixmapPtr pdPix)
{
	ScrnInfoPtr pScrn = xf86Screens[pdPix->drawable.pScreen->myNum];
	NVPtr pNv = NVPTR(pScrn);
	struct nouveau_channel *chan = pNv->chan;

	chan->flush_notify = NULL;
}

Bool
NVAccelInitNV30TCL(ScrnInfoPtr pScrn)
{
	NVPtr pNv = NVPTR(pScrn);
	struct nouveau_channel *chan = pNv->chan;
	uint32_t class = 0, chipset;
	int next_hw_offset = 0, i;

	if (!nv40_fp_map_a8[0])
		NV30EXAHackupA8Shaders(pScrn);

#define NV30TCL_CHIPSET_3X_MASK 0x00000003
#define NV35TCL_CHIPSET_3X_MASK 0x000001e0
#define NV30_3D_CHIPSET_3X_MASK 0x00000010

	chipset = pNv->dev->chipset;
	if ((chipset & 0xf0) != NV_ARCH_30)
		return TRUE;
	chipset &= 0xf;

	if (NV30TCL_CHIPSET_3X_MASK & (1<<chipset))
		class = NV30_3D_CLASS;
	else if (NV35TCL_CHIPSET_3X_MASK & (1<<chipset))
		class = NV35_3D_CLASS;
	else if (NV30_3D_CHIPSET_3X_MASK & (1<<chipset))
		class = NV34_3D_CLASS;
	else {
		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			   "NV30EXA: Unknown chipset NV3%1x\n", chipset);
		return FALSE;
	}


	if (!pNv->Nv3D) {
		if (nouveau_grobj_alloc(chan, Nv3D, class, &pNv->Nv3D))
			return FALSE;
	}

	if (!pNv->shader_mem) {
		if (nouveau_bo_new(pNv->dev, NOUVEAU_BO_VRAM | NOUVEAU_BO_MAP,
				   0, 0x1000, &pNv->shader_mem)) {
			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
				   "Couldn't alloc fragprog buffer!\n");
			nouveau_grobj_free(&pNv->Nv3D);
			return FALSE;
		}
	}

	BEGIN_NV04(chan, NV01_SUBC(3D, OBJECT), 1);
	OUT_RING  (chan, pNv->Nv3D->handle);
	BEGIN_NV04(chan, NV30_3D(DMA_TEXTURE0), 3);
	OUT_RING  (chan, pNv->chan->vram->handle);
	OUT_RING  (chan, pNv->chan->gart->handle);
	OUT_RING  (chan, pNv->chan->vram->handle);
	BEGIN_NV04(chan, NV30_3D(DMA_UNK1AC), 1);
	OUT_RING  (chan, pNv->chan->vram->handle);
	BEGIN_NV04(chan, NV30_3D(DMA_COLOR0), 2);
	OUT_RING  (chan, pNv->chan->vram->handle);
	OUT_RING  (chan, pNv->chan->vram->handle);
	BEGIN_NV04(chan, NV30_3D(DMA_UNK1B0), 1);
	OUT_RING  (chan, pNv->chan->vram->handle);

	for (i=1; i<8; i++) {
		BEGIN_NV04(chan, NV30_3D(VIEWPORT_CLIP_HORIZ(i)), 2);
		OUT_RING  (chan, 0);
		OUT_RING  (chan, 0);
	}

	BEGIN_NV04(chan, SUBC_3D(0x220), 1);
	OUT_RING  (chan, 1);

	BEGIN_NV04(chan, SUBC_3D(0x03b0), 1);
	OUT_RING  (chan, 0x00100000);
	BEGIN_NV04(chan, SUBC_3D(0x1454), 1);
	OUT_RING  (chan, 0);
	BEGIN_NV04(chan, SUBC_3D(0x1d80), 1);
	OUT_RING  (chan, 3);
	BEGIN_NV04(chan, SUBC_3D(0x1450), 1);
	OUT_RING  (chan, 0x00030004);

	/* NEW */
	BEGIN_NV04(chan, SUBC_3D(0x1e98), 1);
	OUT_RING  (chan, 0);
	BEGIN_NV04(chan, SUBC_3D(0x17e0), 3);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, 0x3f800000);
	BEGIN_NV04(chan, SUBC_3D(0x1f80), 16);
	OUT_RING  (chan, 0); OUT_RING  (chan, 0); OUT_RING  (chan, 0); OUT_RING  (chan, 0); 
	OUT_RING  (chan, 0); OUT_RING  (chan, 0); OUT_RING  (chan, 0); OUT_RING  (chan, 0); 
	OUT_RING  (chan, 0x0000ffff);
	OUT_RING  (chan, 0); OUT_RING  (chan, 0); OUT_RING  (chan, 0); OUT_RING  (chan, 0); 
	OUT_RING  (chan, 0); OUT_RING  (chan, 0); OUT_RING  (chan, 0); 

	BEGIN_NV04(chan, SUBC_3D(0x120), 3);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, 1);
	OUT_RING  (chan, 2);

	BEGIN_NV04(chan, SUBC_BLIT(0x120), 3);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, 1);
	OUT_RING  (chan, 2);

	BEGIN_NV04(chan, SUBC_3D(0x1d88), 1);
	OUT_RING  (chan, 0x00001200);

	BEGIN_NV04(chan, NV30_3D(RC_ENABLE), 1);
	OUT_RING  (chan, 0);

	/* Attempt to setup a known state.. Probably missing a heap of
	 * stuff here..
	 */
	BEGIN_NV04(chan, NV30_3D(STENCIL_ENABLE(0)), 1);
	OUT_RING  (chan, 0);
	BEGIN_NV04(chan, NV30_3D(STENCIL_ENABLE(1)), 1);
	OUT_RING  (chan, 0);
	BEGIN_NV04(chan, NV30_3D(ALPHA_FUNC_ENABLE), 1);
	OUT_RING  (chan, 0);
	BEGIN_NV04(chan, NV30_3D(DEPTH_WRITE_ENABLE), 2);
	OUT_RING  (chan, 0); /* wr disable */
	OUT_RING  (chan, 0); /* test disable */
	BEGIN_NV04(chan, NV30_3D(COLOR_MASK), 1);
	OUT_RING  (chan, 0x01010101); /* TR,TR,TR,TR */
	BEGIN_NV04(chan, NV30_3D(CULL_FACE_ENABLE), 1);
	OUT_RING  (chan, 0);
	BEGIN_NV04(chan, NV30_3D(BLEND_FUNC_ENABLE), 5);
	OUT_RING  (chan, 0);				/* Blend enable */
	OUT_RING  (chan, 0);				/* Blend src */
	OUT_RING  (chan, 0);				/* Blend dst */
	OUT_RING  (chan, 0x00000000);			/* Blend colour */
	OUT_RING  (chan, 0x8006);			/* FUNC_ADD */
	BEGIN_NV04(chan, NV30_3D(COLOR_LOGIC_OP_ENABLE), 2);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, 0x1503 /*GL_COPY*/);
	BEGIN_NV04(chan, NV30_3D(DITHER_ENABLE), 1);
	OUT_RING  (chan, 1);
	BEGIN_NV04(chan, NV30_3D(SHADE_MODEL), 1);
	OUT_RING  (chan, 0x1d01 /*GL_SMOOTH*/);
	BEGIN_NV04(chan, NV30_3D(POLYGON_OFFSET_FACTOR),2);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	BEGIN_NV04(chan, NV30_3D(POLYGON_MODE_FRONT), 2);
	OUT_RING  (chan, 0x1b02 /*GL_FILL*/);
	OUT_RING  (chan, 0x1b02 /*GL_FILL*/);
	/* - Disable texture units
	 * - Set fragprog to MOVR result.color, fragment.color */
	for (i=0;i<4;i++) {
		BEGIN_NV04(chan, NV30_3D(TEX_ENABLE(i)), 1);
		OUT_RING  (chan, 0);
	}
	/* Polygon stipple */
	BEGIN_NV04(chan, NV30_3D(POLYGON_STIPPLE_PATTERN(0)), 0x20);
	for (i=0;i<0x20;i++)
		OUT_RING  (chan, 0xFFFFFFFF);

	BEGIN_NV04(chan, NV30_3D(DEPTH_RANGE_NEAR), 2);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);

	/* Ok.  If you start X with the nvidia driver, kill it, and then
	 * start X with nouveau you will get black rendering instead of
	 * what you'd expect.  This fixes the problem, and it seems that
	 * it's not needed between nouveau restarts - which suggests that
	 * the 3D context (wherever it's stored?) survives somehow.
	 */
	//BEGIN_NV04(chan, SUBC_3D(0x1d60),1);
	//OUT_RING  (chan, 0x03008000);

	int w=4096;
	int h=4096;
	int pitch=4096*4;
	BEGIN_NV04(chan, NV30_3D(RT_HORIZ), 5);
	OUT_RING  (chan, w<<16);
	OUT_RING  (chan, h<<16);
	OUT_RING  (chan, 0x148); /* format */
	OUT_RING  (chan, pitch << 16 | pitch);
	OUT_RING  (chan, 0x0);
	BEGIN_NV04(chan, NV30_3D(VIEWPORT_TX_ORIGIN), 1);
	OUT_RING  (chan, 0);
        BEGIN_NV04(chan, SUBC_3D(0x0a00), 2);
        OUT_RING  (chan, (w<<16) | 0);
        OUT_RING  (chan, (h<<16) | 0);
	BEGIN_NV04(chan, NV30_3D(VIEWPORT_CLIP_HORIZ(0)), 2);
	OUT_RING  (chan, (w-1)<<16);
	OUT_RING  (chan, (h-1)<<16);
	BEGIN_NV04(chan, NV30_3D(SCISSOR_HORIZ), 2);
	OUT_RING  (chan, w<<16);
	OUT_RING  (chan, h<<16);
	BEGIN_NV04(chan, NV30_3D(VIEWPORT_HORIZ), 2);
	OUT_RING  (chan, w<<16);
	OUT_RING  (chan, h<<16);

	BEGIN_NV04(chan, NV30_3D(VIEWPORT_TRANSLATE_X), 8);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 0.0);

	BEGIN_NV04(chan, NV30_3D(MODELVIEW_MATRIX(0)), 16);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);

	BEGIN_NV04(chan, NV30_3D(PROJECTION_MATRIX(0)), 16);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 0.0);
	OUT_RINGf (chan, 1.0);

	BEGIN_NV04(chan, NV30_3D(SCISSOR_HORIZ), 2);
	OUT_RING  (chan, 4096<<16);
	OUT_RING  (chan, 4096<<16);

	for (i = 0; i < NV30EXA_FPID_MAX; i++) {
		NV30_UploadFragProg(pNv, nv40_fp_map[i], &next_hw_offset);
		NV30_UploadFragProg(pNv, nv40_fp_map_a8[i], &next_hw_offset);
	}
	NV30_UploadFragProg(pNv, &nv30_fp_yv12_bicubic, &next_hw_offset);
	NV30_UploadFragProg(pNv, &nv30_fp_yv12_bilinear, &next_hw_offset);

	return TRUE;
}
