/*
 * $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$ */

#include <sys/io.h>

#ifdef HAVE_CONFIG_H
#include <kdrive-config.h>
#endif

#include "sis.h"
#include "sis_reg.h"

#if 0
#define SIS_FALLBACK(x)		\
do {				\
	ErrorF x;		\
	return FALSE;		\
} while (0)
#else
#define SIS_FALLBACK(x) return FALSE
#endif

CARD8 SiSSolidRop[16] = {
    /* GXclear      */      0x00,         /* 0 */
    /* GXand        */      0xa0,         /* src AND dst */
    /* GXandReverse */      0x50,         /* src AND NOT dst */
    /* GXcopy       */      0xf0,         /* src */
    /* GXandInverted*/      0x0a,         /* NOT src AND dst */
    /* GXnoop       */      0xaa,         /* dst */
    /* GXxor        */      0x5a,         /* src XOR dst */
    /* GXor         */      0xfa,         /* src OR dst */
    /* GXnor        */      0x05,         /* NOT src AND NOT dst */
    /* GXequiv      */      0xa5,         /* NOT src XOR dst */
    /* GXinvert     */      0x55,         /* NOT dst */
    /* GXorReverse  */      0xf5,         /* src OR NOT dst */
    /* GXcopyInverted*/     0x0f,         /* NOT src */
    /* GXorInverted */      0xaf,         /* NOT src OR dst */
    /* GXnand       */      0x5f,         /* NOT src OR NOT dst */
    /* GXset        */      0xff,         /* 1 */
};

CARD8 SiSBltRop[16] = {
    /* GXclear      */      0x00,         /* 0 */
    /* GXand        */      0x88,         /* src AND dst */
    /* GXandReverse */      0x44,         /* src AND NOT dst */
    /* GXcopy       */      0xcc,         /* src */
    /* GXandInverted*/      0x22,         /* NOT src AND dst */
    /* GXnoop       */      0xaa,         /* dst */
    /* GXxor        */      0x66,         /* src XOR dst */
    /* GXor         */      0xee,         /* src OR dst */
    /* GXnor        */      0x11,         /* NOT src AND NOT dst */
    /* GXequiv      */      0x99,         /* NOT src XOR dst */
    /* GXinvert     */      0x55,         /* NOT dst */
    /* GXorReverse  */      0xdd,         /* src OR NOT dst */
    /* GXcopyInverted*/     0x33,         /* NOT src */
    /* GXorInverted */      0xbb,         /* NOT src OR dst */
    /* GXnand       */      0x77,         /* NOT src OR NOT dst */
    /* GXset        */      0xff,         /* 1 */
};

int copydx, copydy;
int fifo_size;
SiSScreenInfo *accel_siss;
char *mmio;
CARD32 sis_color = 0;
CARD32 blitCmd;

static void
SiSWaitAvailMMIO(int n)
{
	while (fifo_size < n) {
		fifo_size = MMIO_IN32(mmio, REG_CommandQueue) & MASK_QueueLen;
	}
	fifo_size -= n;
}

static void
SiSWaitIdle(void)
{
	CARD32 engineState;
	do {
		engineState = MMIO_IN32(mmio, REG_CommandQueue);
	} while ((engineState & SiS_EngIdle) != SiS_EngIdle);
}

static Bool
SiSPrepareSolid(PixmapPtr pPixmap, int alu, Pixel pm, Pixel fg)
{
	KdScreenPriv(pPixmap->drawable.pScreen);
	SiSScreenInfo(pScreenPriv);
	SiSCardInfo(pScreenPriv);

	/* No acceleration for other formats (yet) */
	if (pPixmap->drawable.bitsPerPixel !=
	    pScreenPriv->screen->fb[0].bitsPerPixel)
		return FALSE;

	if ((pm & 0x00ffffff) != 0x00ffffff)	/* XXX */
		SIS_FALLBACK(("Unsupported planemask 0x%x\n", pm));

	accel_siss = siss;
	mmio = sisc->reg_base;

	SiSWaitAvailMMIO(4);
	MMIO_OUT32(mmio, REG_BLT_PATFG, fg);
	MMIO_OUT32(mmio, REG_BLT_DSTRECT, (-1 << 16) | pPixmap->devKind);
	MMIO_OUT32(mmio, REG_BLT_SRCPITCH, siss->depthSet);
	MMIO_OUT32(mmio, REG_BLT_DSTBASE, ((CARD8 *)pPixmap->devPrivate.ptr -
	    pScreenPriv->screen->memory_base));

	blitCmd = BLT_CMD_BITBLT | BLT_PAT_FG | BLT_X_INC | BLT_Y_INC |
	    BLT_NOCLIP | (SiSSolidRop[alu] << 8);

	return TRUE;
}

static void
SiSSolid(int x1, int y1, int x2, int y2)
{
	SiSWaitAvailMMIO(3);
	MMIO_OUT32(mmio, REG_BLT_DSTXY, (x1 << 16) | y1);
	MMIO_OUT32(mmio, REG_BLT_H_W, ((y2 - y1) << 16) | (x2 - x1));
	MMIO_OUT32(mmio, REG_BLT_CMD, blitCmd);
}

static void
SiSDoneSolid(void)
{
}

static Bool
SiSPrepareCopy(PixmapPtr pSrc, PixmapPtr pDst, int dx, int dy, int alu,
    Pixel pm)
{
	KdScreenPriv(pDst->drawable.pScreen);
	SiSScreenInfo(pScreenPriv);
	SiSCardInfo(pScreenPriv);

	/* No acceleration for other formats (yet) */
	if (pDst->drawable.bitsPerPixel !=
	    pScreenPriv->screen->fb[0].bitsPerPixel)
		return FALSE;

	if ((pm & 0x00ffffff) != 0x00ffffff)	/* XXX */
		SIS_FALLBACK(("Unsupported pixel mask 0x%x\n", pm));

	accel_siss = siss;
	mmio = sisc->reg_base;

	SiSWaitAvailMMIO(4);
	MMIO_OUT32(mmio, REG_BLT_SRCPITCH, siss->depthSet | pSrc->devKind);
	MMIO_OUT32(mmio, REG_BLT_DSTRECT, (-1 << 16) | pDst->devKind);
	MMIO_OUT32(mmio, REG_BLT_SRCBASE, ((CARD8 *)pSrc->devPrivate.ptr -
	    pScreenPriv->screen->memory_base));
	MMIO_OUT32(mmio, REG_BLT_DSTBASE, ((CARD8 *)pDst->devPrivate.ptr -
	    pScreenPriv->screen->memory_base));

	blitCmd = BLT_CMD_BITBLT | BLT_PAT_FG | BLT_NOCLIP |
	    (SiSBltRop[alu] << 8);

	if (pSrc != pDst || dx >= 0)
		blitCmd |= BLT_X_INC;
	if (pSrc != pDst || dy >= 0)
		blitCmd |= BLT_Y_INC;

	return TRUE;
}

static void
SiSCopy(int srcX, int srcY, int dstX, int dstY, int w, int h)
{
	if (!(blitCmd & BLT_X_INC)) {
		srcX += w - 1;
		dstX += w - 1;
	}

	if (!(blitCmd & BLT_Y_INC)) {
		srcY += h - 1;
		dstY += h - 1;
	}

	SiSWaitAvailMMIO(4);
	MMIO_OUT32(mmio, REG_BLT_H_W, (h << 16) | w);
	MMIO_OUT32(mmio, REG_BLT_SRCXY, (srcX << 16) | srcY);
	MMIO_OUT32(mmio, REG_BLT_DSTXY, (dstX << 16) | dstY);
	MMIO_OUT32(mmio, REG_BLT_CMD, blitCmd);
}

static void
SiSDoneCopy(void)
{
}

KaaScreenInfoRec SiSKaa = {
	SiSPrepareSolid,
	SiSSolid,
	SiSDoneSolid,
	SiSPrepareCopy,
	SiSCopy,
	SiSDoneCopy,
	KAA_OFFSCREEN_PIXMAPS,
	8,
	8
};

#define USE_TURBOQUEUE 0

Bool
SiSDrawInit(ScreenPtr pScreen)
{
	KdScreenPriv(pScreen);
	SiSScreenInfo(pScreenPriv);
	CARD8 tmp;
#if USE_TURBOQUEUE
	int tqsize;
#endif

	switch (pScreenPriv->screen->fb[0].depth)
	{
	case 8:
		siss->depthSet = 0x00000000;
		break;
	case 15:
		siss->depthSet = 0x40000000;
		break;
	case 16:
		siss->depthSet = 0x80000000;
		break;
	case 24:
		if (pScreenPriv->screen->fb[0].bitsPerPixel == 32) {
			siss->depthSet = 0xc0000000;
			break;
		}
		/* FALLTHROUGH*/
	default:
		ErrorF("Unsupported depth/bpp %d/%d\n",
		    pScreenPriv->screen->fb[0].depth,
		    pScreenPriv->screen->fb[0].bitsPerPixel);
		return FALSE;
	}

	outb(0x05, 0x3c4);
	outb(0x86, 0x3c5); /* unlock registers */

	outb(0x20, 0x3c4);
	outb(0xA1, 0x3c5); /* enable pci linear addressing, MMIO, PCI_IO */

	outb(0x1e, 0x3c4);
	tmp = inb(0x3c5);
	outb(tmp | 0x42 | 0x18, 0x3c5); /* Enable 2d and 3d */

#if USE_TURBOQUEUE
	tqsize = (pScreenPriv->screen->memory_size / 1024) / 64 - 8;
	/* Enable TQ */
	outb(0x26, 0x3c4);
	outb(tqsize & 0xff, 0x3c5);
	outb(0x27, 0x3c4);
	tmp = inb(0x3c5);
	outb(((tqsize >> 8) & 0x03) | (tmp & 0x0c) | 0xF0, 0x3c5);
	
	/* XXX: Adjust offscreen size to avoid TQ area (last 512k) */
#endif

	ErrorF("Screen: %d/%d depth/bpp\n", pScreenPriv->screen->fb[0].depth,
	    pScreenPriv->screen->fb[0].bitsPerPixel);

	if (!kaaDrawInit(pScreen, &SiSKaa))
		return FALSE;

	return TRUE;
}

void
SiSDrawEnable(ScreenPtr pScreen)
{
	KdMarkSync(pScreen);
}

void
SiSDrawDisable(ScreenPtr pScreen)
{
}

void
SiSDrawFini(ScreenPtr pScreen)
{
	kaaDrawFini (pScreen);
}

void
SiSDrawSync(ScreenPtr pScreen)
{
	KdScreenPriv(pScreen);
	SiSScreenInfo(pScreenPriv);
	SiSCardInfo(pScreenPriv);

	accel_siss = siss;
	mmio = sisc->reg_base;

	SiSWaitIdle();
}
