/*
 * $Id$
 *
 * Copyright © 2003 Eric Anholt
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Eric Anholt not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Eric Anholt makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL ERIC ANHOLT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
/* $Header$ */

#ifdef USE_DMA
#define TAG(x)		x##DMA
#define LOCALS		RING_LOCALS; \
			(void)atic
#define BEGIN(x)	BEGIN_RING(x * 2)
#define OUT_REG(reg, val) OUT_RING_REG(reg, val)
#define END()		ADVANCE_RING()
#else
#define TAG(x)		x##MMIO
#define LOCALS		char *mmio = atic->reg_base; \
			(void)atis
#define BEGIN(x)	ATIWaitAvailMMIO(x)
#define OUT_REG(reg, val) MMIO_OUT32((mmio), (reg), (val))
#define END()
#endif

static Bool
TAG(ATISetup)(PixmapPtr pDst, PixmapPtr pSrc)
{
	KdScreenPriv(pDst->drawable.pScreen);
	ATIScreenInfo(pScreenPriv);
	ATICardInfo(pScreenPriv);
	int dst_offset, dst_pitch;
	int bpp = pDst->drawable.bitsPerPixel;
	LOCALS;

	accel_atis = atis;

	dst_pitch = pDst->devKind;
	dst_offset = ((CARD8 *)pDst->devPrivate.ptr -
	    pScreenPriv->screen->memory_base);
	if ((dst_pitch & (atis->kaa.offscreenPitch - 1)) != 0)
		ATI_FALLBACK(("Bad dst pitch 0x%x\n", dst_pitch));
	if ((dst_offset & (atis->kaa.offscreenByteAlign - 1)) != 0)
		ATI_FALLBACK(("Bad dst offset 0x%x\n", dst_offset));

	if (pSrc != NULL) {
		src_pitch = pSrc->devKind;
		src_offset = ((CARD8 *)pSrc->devPrivate.ptr -
		    pScreenPriv->screen->memory_base);
		if ((src_pitch & (atis->kaa.offscreenPitch - 1)) != 0)
			ATI_FALLBACK(("Bad src pitch 0x%x\n", src_pitch));
		if ((src_offset & (atis->kaa.offscreenByteAlign - 1)) != 0)
			ATI_FALLBACK(("Bad src offset 0x%x\n", src_offset));
	}

#ifdef USE_DMA
	if (atic->is_radeon && !atic->is_r200)
		RadeonSwitchTo2D();
#endif
	BEGIN((pSrc != NULL) ? 3 : 2);
	if (atic->is_radeon) {
		OUT_REG(RADEON_REG_DST_PITCH_OFFSET,
		    ((dst_pitch >> 6) << 22) | (dst_offset >> 10));
		if (pSrc != NULL) {
			OUT_REG(RADEON_REG_SRC_PITCH_OFFSET,
			    ((src_pitch >> 6) << 22) | (src_offset >> 10));
		}
	} else {
		if (is_24bpp) {
			dst_pitch *= 3;
			src_pitch *= 3;
		}
		/* R128 pitch is in units of 8 pixels, offset in 32 bytes */
		OUT_REG(RADEON_REG_DST_PITCH_OFFSET,
		    ((dst_pitch/bpp) << 21) | (dst_offset >> 5));
		if (pSrc != NULL) {
			OUT_REG(RADEON_REG_SRC_PITCH_OFFSET,
			    ((src_pitch/bpp) << 21) | (src_offset >> 5));
		}
	}
	OUT_REG(RADEON_REG_DEFAULT_SC_BOTTOM_RIGHT,
	    (RADEON_DEFAULT_SC_RIGHT_MAX | RADEON_DEFAULT_SC_BOTTOM_MAX));
	END();

	return TRUE;
}

static Bool
TAG(ATIPrepareSolid)(PixmapPtr pPixmap, int alu, Pixel pm, Pixel fg)
{
	KdScreenPriv(pPixmap->drawable.pScreen);
	ATIScreenInfo(pScreenPriv);
	ATICardInfo(pScreenPriv);
	CARD32 datatype;
	LOCALS;

	if (is_24bpp) {
		/* Solid fills in fake-24bpp mode only work if the pixel color
		 * and planemask are all the same byte.
		 */
		if ((fg & 0xffffff) != (((fg & 0xff) << 16) | ((fg >> 8) &
		    0xffff)))
			ATI_FALLBACK(("Can't do solid color %d in 24bpp\n"));
		if ((pm & 0xffffff) != (((pm & 0xff) << 16) | ((pm >> 8) &
		    0xffff)))
			ATI_FALLBACK(("Can't do planemask %d in 24bpp\n"));
	}

	if (!ATIGetDatatypeBpp(pPixmap->drawable.bitsPerPixel, &datatype))
		return FALSE;
	if (!TAG(ATISetup)(pPixmap, NULL))
		return FALSE;

	BEGIN(4);
	OUT_REG(RADEON_REG_DP_GUI_MASTER_CNTL,
	    (datatype << 8) |
	    RADEON_GMC_CLR_CMP_CNTL_DIS |
	    RADEON_GMC_AUX_CLIP_DIS |
	    RADEON_GMC_BRUSH_SOLID_COLOR |
	    RADEON_GMC_DST_PITCH_OFFSET_CNTL |
	    RADEON_GMC_SRC_DATATYPE_COLOR |
	    (ATISolidRop[alu] << 16));
	OUT_REG(RADEON_REG_DP_BRUSH_FRGD_CLR, fg);
	OUT_REG(RADEON_REG_DP_WRITE_MASK, pm);
	OUT_REG(RADEON_REG_DP_CNTL, RADEON_DST_X_LEFT_TO_RIGHT |
	    RADEON_DST_Y_TOP_TO_BOTTOM);
	END();

	return TRUE;
}

static void
TAG(ATISolid)(int x1, int y1, int x2, int y2)
{
	ATIScreenInfo *atis = accel_atis;
	ATICardInfo *atic = atis->atic;
	LOCALS;
	
	if (is_24bpp) {
		x1 *= 3;
		x2 *= 3;
	}
	BEGIN(2);
	OUT_REG(RADEON_REG_DST_Y_X, (y1 << 16) | x1);
	OUT_REG(RADEON_REG_DST_WIDTH_HEIGHT, ((x2 - x1) << 16) | (y2 - y1));
	END();
}

static Bool
TAG(ATIPrepareCopy)(PixmapPtr pSrc, PixmapPtr pDst, int dx, int dy, int alu, Pixel pm)
{
	KdScreenPriv(pDst->drawable.pScreen);
	ATIScreenInfo(pScreenPriv);
	ATICardInfo(pScreenPriv);
	CARD32 datatype;
	LOCALS;

	copydx = dx;
	copydy = dy;

	if (is_24bpp && ((pm & 0xffffff) != (((pm & 0xff) << 16) | ((pm >> 8) &
	    0xffff))))
		ATI_FALLBACK(("Can't do planemask %d in 24bpp\n"));

	if (!ATIGetDatatypeBpp(pDst->drawable.bitsPerPixel, &datatype))
		return FALSE;
	if (!TAG(ATISetup)(pDst, pSrc))
		return FALSE;

	BEGIN(3);
	OUT_REG(RADEON_REG_DP_GUI_MASTER_CNTL,
	    (datatype << 8) |
	    RADEON_GMC_CLR_CMP_CNTL_DIS |
	    RADEON_GMC_AUX_CLIP_DIS |
	    RADEON_GMC_BRUSH_SOLID_COLOR |
	    RADEON_GMC_SRC_DATATYPE_COLOR |
	    (ATIBltRop[alu] << 16) |
	    RADEON_GMC_SRC_PITCH_OFFSET_CNTL |
	    RADEON_GMC_DST_PITCH_OFFSET_CNTL |
	    RADEON_DP_SRC_SOURCE_MEMORY);
	OUT_REG(RADEON_REG_DP_WRITE_MASK, pm);
	OUT_REG(RADEON_REG_DP_CNTL, 
	    (dx >= 0 ? RADEON_DST_X_LEFT_TO_RIGHT : 0) |
	    (dy >= 0 ? RADEON_DST_Y_TOP_TO_BOTTOM : 0));
	END();

	return TRUE;
}

static void
TAG(ATICopy)(int srcX, int srcY, int dstX, int dstY, int w, int h)
{
	ATIScreenInfo *atis = accel_atis;
	ATICardInfo *atic = atis->atic;
	LOCALS;

	if (is_24bpp) {
		srcX *= 3;
		dstX *= 3;
		w *= 3;
	}

	if (copydx < 0) {
		srcX += w - 1;
		dstX += w - 1;
	}

	if (copydy < 0)  {
		srcY += h - 1;
		dstY += h - 1;
	}

	BEGIN(3);
	OUT_REG(RADEON_REG_SRC_Y_X, (srcY << 16) | srcX);
	OUT_REG(RADEON_REG_DST_Y_X, (dstY << 16) | dstX);
	OUT_REG(RADEON_REG_DST_HEIGHT_WIDTH, (h << 16) | w);
	END();
}

#undef TAG
#undef LOCALS
#undef BEGIN
#undef OUT_REG
#undef END
