#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "tseng.h"

/*
 *
 * ET4000 HWCursor is disabled currently: we need to find a way to set the
 * sprite colours.
 *
 */

/*
 * ET4000W32 sprite engine.
 */
static CARD8
ET4000CursorRead(CARD8 Index)
{
    outb(0x217A, Index);
    return inb(0x217B);
}

static void
ET4000CursorWrite(CARD8 Index, CARD8 Value)
{
    outb(0x217A, Index);
    outb(0x217B, Value);
}

/*
 *
 */
static Bool
TsengUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
{
    /* have this return false for DoubleScan and Interlaced ? */
    return TRUE;
}

static void
TsengShowCursor(ScrnInfoPtr pScrn)
{
    unsigned char tmp;
    TsengPtr pTseng = TsengPTR(pScrn);

    /* Enable the hardware cursor. */
    if (pTseng->ChipType == ET6000) {
	tmp = ET6000IORead(pTseng, 0x46);
	ET6000IOWrite(pTseng, 0x46, (tmp | 0x01));
    } else {
	tmp = ET4000CursorRead(0xF7);
        ET4000CursorWrite(0xF7, tmp | 0x80);
    }
}

static void
TsengHideCursor(ScrnInfoPtr pScrn)
{
    unsigned char tmp;
    TsengPtr pTseng = TsengPTR(pScrn);

    /* Disable the hardware cursor. */
    if (pTseng->ChipType == ET6000) {
	tmp = ET6000IORead(pTseng, 0x46);
	ET6000IOWrite(pTseng, 0x46, (tmp & 0xfe));
    } else {
	tmp = ET4000CursorRead(0xF7);
        ET4000CursorWrite(0xF7, tmp & ~0x80);
    }
}

static void
TsengSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
{
    int xorigin, yorigin;
    TsengPtr pTseng = TsengPTR(pScrn);

    /*
     * If the cursor is partly out of screen at the left or top,
     * we need to modify the origin.
     */
    xorigin = 0;
    yorigin = 0;
    if (x < 0) {
	xorigin = -x;
	x = 0;
    }
    if (y < 0) {
	yorigin = -y;
	y = 0;
    }
#ifdef TODO
    /* Correct cursor position in DoubleScan modes */
    if (XF86SCRNINFO(pScr)->modes->Flags & V_DBLSCAN)
	y *= 2;
#endif

    if (pTseng->ChipType == ET6000) {
	ET6000IOWrite(pTseng, 0x82, xorigin);
	ET6000IOWrite(pTseng, 0x83, yorigin);

	ET6000IOWrite(pTseng, 0x84, (x & 0xff));
	ET6000IOWrite(pTseng, 0x85, ((x >> 8) & 0x0f));

	ET6000IOWrite(pTseng, 0x86, (y & 0xff));
	ET6000IOWrite(pTseng, 0x87, ((y >> 8) & 0x0f));
    } else {
	ET4000CursorWrite(0xE2, xorigin);
        ET4000CursorWrite(0xE6, yorigin);

	ET4000CursorWrite(0xE0, x & 0xFF);
        ET4000CursorWrite(0xE1, (x >> 8) & 0x0F);

        ET4000CursorWrite(0xE4, y & 0xFF);
        ET4000CursorWrite(0xE5, (y >> 8) & 0x0F);
    }
}

/*
 * The ET6000 cursor color is only 6 bits, with 2 bits per color. This
 * is of course very inaccurate, but high-bit-depth color differences
 * are only visible on _large_ planes of equal color. i.e. small areas
 * of a certain color (like a cursor) don't need many bits per pixel at
 * all, because the difference will not be seen.
 * 
 * So it won't be as bad, but should still be documented nonetheless.
 */
static void
TsengSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
{
    TsengPtr pTseng = TsengPTR(pScrn);
    unsigned char et6k_fg, et6k_bg;

    if (pTseng->ChipType == ET6000) {
	et6k_fg = (fg & 0x00000003)
	    | ((fg & 0x00000300) >> 6)
	    | ((fg & 0x00030000) >> 12);
	et6k_bg = (bg & 0x00000003)
	    | ((bg & 0x00000300) >> 6)
	    | ((bg & 0x00030000) >> 12);

	ET6000IOWrite(pTseng, 0x67, 0x09);	/* prepare for colour data */
	ET6000IOWrite(pTseng, 0x69, et6k_bg);
	ET6000IOWrite(pTseng, 0x69, et6k_fg);
    } else {
	/*
	 * The ET4000 uses color 0 as sprite color "0", and color 0xFF as
	 * sprite color "1". Changing colors implies changing colors 0 and
	 * 255. This is currently not implemented.
	 *
	 * In non-8bpp modes, this would result in always black and white
	 * colors (since the colormap isn't there to translate 0 and 255 to
	 * other colors). And besides, in non-8bpp, there seem to be TWO
	 * cursor images on the screen...
	 */
	xf86Msg(X_ERROR, "Internal error: ET4000 hardware cursor color changes not implemented\n");
    }
}

static void 
TsengLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *bits)
{
    vgaHWPtr hwp = VGAHWPTR(pScrn);
    TsengPtr pTseng = TsengPTR(pScrn);
    CARD8 tmp;

#ifdef DEBUG_HWC
    int i;
    int d;

    for (i = 0; i < 1024; i++) {
	d = *(bits + i);
	ErrorF("%d%d%d%d", d & 0x03, (d >> 2) & 0x03, (d >> 4) & 0x03, (d >> 6) & 0x03);
	if ((i & 15) == 15)
	    ErrorF("\n");
    }
#endif

    /*
     * Program the cursor image address in video memory. 
     * We need to set it here or we might loose it on mode/vt switches.
     */

    if (pTseng->ChipType == ET6000) {
	/* bits 19:16 */
	tmp = hwp->readCrtc(hwp, 0x0E) & 0xF0;
	tmp |= ((pTseng->HWCursorBufferOffset / 4) >> 16) & 0x0F;
        hwp->writeCrtc(hwp, 0x0E, tmp);

	/* bits 15:8 */
	hwp->writeCrtc(hwp, 0x0F, ((pTseng->HWCursorBufferOffset / 4) >> 8) & 0xFF);
	/* on the ET6000, bits (7:0) are always 0 */
    } else {
	/* bits 19:16 */
	tmp = ET4000CursorRead(0xEA) & 0xF0;
	ET4000CursorWrite(0xEA, tmp | (((pTseng->HWCursorBufferOffset / 4) >> 16) & 0x0F));
	/* bits 15:8 */
        ET4000CursorWrite(0xE9, ((pTseng->HWCursorBufferOffset / 4) >> 8) & 0xFF);
	/* bits 7:0 */
        ET4000CursorWrite(0xE8, (pTseng->HWCursorBufferOffset / 4) & 0xFF);

	/* this needs to be set for the sprite */
	ET4000CursorWrite(0xEB, 0x02);

        tmp = ET4000CursorRead(0xEC);
        ET4000CursorWrite(0xEC, tmp & 0xFE);

        tmp = ET4000CursorRead(0xEF) & 0xF8;
        ET4000CursorWrite(0xEF, tmp | 0x02);

        ET4000CursorWrite(0xEE, 0x01);
    }
    /* this assumes the apertures have been set up correctly for banked mode */
    memcpy(pTseng->HWCursorBuffer, bits, 1024);
}

/*
  *
  *
  */
Bool 
TsengHWCursorInit(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    TsengPtr pTseng = TsengPTR(pScrn);
    xf86CursorInfoPtr infoPtr;

    PDEBUG("	TsengHWCursorInit\n");

    if (!pTseng->HWCursor)
	return FALSE;

    infoPtr = xf86CreateCursorInfoRec();
    if (!infoPtr)
	return FALSE;

    pTseng->CursorInfoRec = infoPtr;

    /* calculate memory addres from video memory offsets */
    pTseng->HWCursorBuffer =
	pTseng->FbBase + pTseng->HWCursorBufferOffset;

    /* set up the XAA HW cursor structure */
    infoPtr->MaxWidth = 64;
    infoPtr->MaxHeight = 64;
    infoPtr->Flags =
	HARDWARE_CURSOR_TRUECOLOR_AT_8BPP |
	HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 |
	HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
	HARDWARE_CURSOR_INVERT_MASK;
    infoPtr->SetCursorColors = TsengSetCursorColors;
    infoPtr->SetCursorPosition = TsengSetCursorPosition;
    infoPtr->LoadCursorImage = TsengLoadCursorImage;
    infoPtr->HideCursor = TsengHideCursor;
    infoPtr->ShowCursor = TsengShowCursor;
    infoPtr->UseHWCursor = TsengUseHWCursor;

    return (xf86InitCursor(pScreen, infoPtr));
}

/*
 *
 */
void
TsengCursorStore(ScrnInfoPtr pScrn, TsengRegPtr Reg)
{
    TsengPtr pTseng = TsengPTR(pScrn);

    if (pTseng->ChipType == ET4000)
        Reg->CursorCtrl = ET4000CursorRead(0xF7);
}

/*
 *
 */
void
TsengCursorRestore(ScrnInfoPtr pScrn, TsengRegPtr Reg)
{
    TsengPtr pTseng = TsengPTR(pScrn);
    
    if (pTseng->ChipType == ET4000)
        ET4000CursorWrite(0xF7, Reg->CursorCtrl);
}