/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/savage/savage_cursor.c,v 1.6 2001/11/02 16:24:51 alanh Exp $ */

/*
 * Hardware cursor support for S3 Savage 4.0 driver. Taken with
 * very few changes from the s3virge cursor file.
 *
 * S. Marineau, 19/04/97.
 * Modified by Amancio Hasty and Jon Tombs
 * Ported to 4.0 by Tim Roberts.
 */

#include "savage_driver.h"
#include "savage_mergedfb.h"

static void SavageLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *src);
static void SavageSetCursorPosition(ScrnInfoPtr pScrn, int x, int y);
static void SavageSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg);


/*
 * Read/write to the DAC via MMIO 
 */

#define inCRReg(reg) (VGAHWPTR(pScrn))->readCrtc( VGAHWPTR(pScrn), reg )
#define outCRReg(reg, val) (VGAHWPTR(pScrn))->writeCrtc( VGAHWPTR(pScrn), reg, val )
#define inSRReg(reg) (VGAHWPTR(pScrn))->readSeq( VGAHWPTR(pScrn), reg )
#define outSRReg(reg, val) (VGAHWPTR(pScrn))->writeSeq( VGAHWPTR(pScrn), reg, val )
#if 0
#define inStatus1() (VGAHWPTR(pScrn))->readST01( VGAHWPTR(pScrn) )
#endif

/* 
 * certain HW cursor operations seem 
 * to require a delay to prevent lockups.
 */
#define waitHSync(n) { \
                       int num = n; \
                       while (num--) { \
			 while ((inStatus1()) & 0x01){};\
                         while (!(inStatus1()) & 0x01){};\
                        } \
                      } 
#define MAX_CURS 64


Bool 
SavageHWCursorInit(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    SavagePtr psav = SAVPTR(pScrn);
    xf86CursorInfoPtr infoPtr;

    infoPtr = xf86CreateCursorInfoRec();
    if(!infoPtr) 
        return FALSE;
    
    psav->CursorInfoRec = infoPtr;

    infoPtr->MaxWidth = MAX_CURS;
    infoPtr->MaxHeight = MAX_CURS;
    infoPtr->Flags = HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_16 |
		     HARDWARE_CURSOR_SWAP_SOURCE_AND_MASK |
		     HARDWARE_CURSOR_AND_SOURCE_WITH_MASK |
		     HARDWARE_CURSOR_BIT_ORDER_MSBFIRST |
	             HARDWARE_CURSOR_INVERT_MASK;

    /*
     * The /MX family is apparently unique among the Savages, in that
     * the cursor color is always straight RGB.  The rest of the Savages
     * use palettized values at 8-bit when not clock doubled.
     */

    if(
        ((psav->Chipset != S3_SAVAGE4) 
       && (inSRReg(0x18) & 0x80) && (inSRReg(0x15) & 0x50) )
	||
	S3_SAVAGE_MOBILE_SERIES(psav->Chipset)
      )
         infoPtr->Flags |= HARDWARE_CURSOR_TRUECOLOR_AT_8BPP; 

    infoPtr->SetCursorColors = SavageSetCursorColors;
    infoPtr->SetCursorPosition = SavageSetCursorPosition;
    infoPtr->LoadCursorImage = SavageLoadCursorImage;
    infoPtr->HideCursor = SavageHideCursor;
    infoPtr->ShowCursor = SavageShowCursor;
    infoPtr->UseHWCursor = NULL;

    if( !psav->CursorKByte )
	psav->CursorKByte = pScrn->videoRam - 4;

    return xf86InitCursor(pScreen, infoPtr);
}



void
SavageShowCursor(ScrnInfoPtr pScrn)
{
   SavagePtr psav = SAVPTR(pScrn);

   /* Turn cursor on. */
   outCRReg( 0x45, inCRReg(0x45) | 0x01 ); /* cursor1 bit 0*/
   if (psav->MergedFB)
	outCRReg( 0x45, inCRReg(0x45) | 0x04 ); /* cursor2 bit 2*/
}


void
SavageHideCursor(ScrnInfoPtr pScrn)
{
    SavagePtr psav = SAVPTR(pScrn);

    /* Turn cursor off. */

    if( S3_SAVAGE4_SERIES( psav->Chipset ) )
    {
       waitHSync(5);
    }
    outCRReg( 0x45, inCRReg(0x45) & 0xfe ); /* cursor1 */
    if (psav->MergedFB)
	outCRReg( 0x45, inCRReg(0x45) & 0xfb ); /* cursor2 */
}

static void
SavageLoadCursorImage(
    ScrnInfoPtr pScrn,
    unsigned char* src)
{
    SavagePtr psav = SAVPTR(pScrn);

    /* cursor 1 */
    /* Set cursor location in frame buffer.  */
    outCRReg( 0x4d, (0xff & psav->CursorKByte));
    outCRReg( 0x4c, (0xff00 & psav->CursorKByte) >> 8);

    if (psav->MergedFB) {
	/* cursor 2 */
	SelectIGA2();
    	/* Set cursor location in frame buffer.  */
    	outCRReg( 0x4d, (0xff & psav->CursorKByte));
    	outCRReg( 0x4c, (0xff00 & psav->CursorKByte) >> 8);
	SelectIGA1();
    }

    /* Upload the cursor image to the frame buffer. */
    memcpy(psav->FBBase + psav->CursorKByte * 1024, src, 1024);

    if( S3_SAVAGE4_SERIES( psav->Chipset ) ) {
	/*
	 * Bug in Savage4 Rev B requires us to do an MMIO read after
	 * loading the cursor.
	 */
	volatile unsigned int i = ALT_STATUS_WORD0;
	(void)i;	/* Not to be optimised out */
    }
}

static void
SavageSetCursorPosition(
     ScrnInfoPtr pScrn,
     int x, 
     int y)
{
    SavagePtr psav = SAVPTR(pScrn);
    unsigned char xoff, yoff;

    if(info->MergedFB) {
       SavageSetCursorPositionMerged(pScrn, x, y);
       return;
    }

    if( S3_SAVAGE4_SERIES( psav->Chipset ) )
    {
	waitHSync(5);
    }
    /* adjust for frame buffer base address granularity */
    if (pScrn->bitsPerPixel == 8)
	x += ((pScrn->frameX0) & 3);
    else if (pScrn->bitsPerPixel == 16)
	x += ((pScrn->frameX0) & 1);
    else if (pScrn->bitsPerPixel == 32)
	x += ((pScrn->frameX0+2) & 3) - 2;

    /*
    * Make these even when used.  There is a bug/feature on at least
    * some chipsets that causes a "shadow" of the cursor in interlaced
    * mode.  Making this even seems to have no visible effect, so just
    * do it for the generic case.
    */

    if (x < 0) {
	xoff = ((-x) & 0xFE);
	x = 0;
    } else {
	xoff = 0;
    }

    if (y < 0) {
	yoff = ((-y) & 0xFE);
	y = 0;
    } else {
	yoff = 0;
    }

    /* cursor 1 */
    /* This is the recomended order to move the cursor */
    outCRReg( 0x46, (x & 0xff00)>>8 );
    outCRReg( 0x47, (x & 0xff) );
    outCRReg( 0x49, (y & 0xff) );
    outCRReg( 0x4e, xoff );
    outCRReg( 0x4f, yoff );
    outCRReg( 0x48, (y & 0xff00)>>8 );
#if 0
    /* cursor 2 */
    SelectIGA2();
    /* This is the recomended order to move the cursor */
    outCRReg( 0x46, (x & 0xff00)>>8 );
    outCRReg( 0x47, (x & 0xff) );
    outCRReg( 0x49, (y & 0xff) );
    outCRReg( 0x4e, xoff );
    outCRReg( 0x4f, yoff );
    outCRReg( 0x48, (y & 0xff00)>>8 );
    SelectIGA1();
#endif
}


static void 
SavageSetCursorColors(
    ScrnInfoPtr pScrn,
    int bg,
    int fg)
{
    SavagePtr psav = SAVPTR(pScrn);
    Bool bNeedExtra = FALSE;

    UnLockExtRegs();

    /* Clock doubled modes need an extra cursor stack write. */

    bNeedExtra =
        (psav->CursorInfoRec->Flags & HARDWARE_CURSOR_TRUECOLOR_AT_8BPP);

    if(
        S3_SAVAGE_MOBILE_SERIES(psav->Chipset) ||
	(pScrn->depth == 24) ||
	((pScrn->depth == 8) && bNeedExtra)
    )
    {
	/* Do it straight, full 24 bit color. */
      
	/* Reset the cursor color stack pointer */
	inCRReg(0x45);
	/* Write low, mid, high bytes - foreground */
	outCRReg(0x4a, fg);
	outCRReg(0x4a, fg >> 8);
	outCRReg(0x4a, fg >> 16);
	/* Reset the cursor color stack pointer */
	inCRReg(0x45);
	/* Write low, mid, high bytes - background */
	outCRReg(0x4b, bg);
	outCRReg(0x4b, bg >> 8);
	outCRReg(0x4b, bg >> 16);

	if (psav->MergedFB) {
            /* cursor 2 */
	    /* Reset the cursor color stack pointer */
	    inCRReg(0x45);
	    SelectIGA2();
	    /* Write low, mid, high bytes - foreground */
	    outCRReg(0x4a, fg);
	    outCRReg(0x4a, fg >> 8);
	    outCRReg(0x4a, fg >> 16);
	    /* Reset the cursor color stack pointer */
	    inCRReg(0x45);
	    /* Write low, mid, high bytes - background */
	    outCRReg(0x4b, bg);
	    outCRReg(0x4b, bg >> 8);
	    outCRReg(0x4b, bg >> 16);
	    SelectIGA1();
	}
	return;
    }
    else if( (pScrn->depth == 15) || (pScrn->depth == 16) )
    {
	if (pScrn->depth == 15) {
	    fg = ((fg & 0xf80000) >> 9) |
		((fg & 0xf800) >> 6) |
		((fg & 0xf8) >> 3);
	    bg = ((bg & 0xf80000) >> 9) |
		((bg & 0xf800) >> 6) |
		((bg & 0xf8) >> 3);
	} else {
	    fg = ((fg & 0xf80000) >> 8) |
		((fg & 0xfc00) >> 5) |
		((fg & 0xf8) >> 3);
	    bg = ((bg & 0xf80000) >> 8) |
		((bg & 0xfc00) >> 5) |
		((bg & 0xf8) >> 3);
	}
	/* Reset the cursor color stack pointer */
        inCRReg( 0x45 );
        outCRReg( 0x4a, fg );
        outCRReg( 0x4a, fg>>8 );

	if (psav->MergedFB) {
        /* cursor 2 */
            inCRReg( 0x45 );
	    SelectIGA2();
            outCRReg( 0x4a, fg );
            outCRReg( 0x4a, fg>>8 );
	    SelectIGA1();
	}

	if( bNeedExtra )
	{
	    outCRReg( 0x4a, fg );
	    outCRReg( 0x4a, fg>>8 );

	    if (psav->MergedFB) {
	        SelectIGA2();
	        outCRReg( 0x4a, fg );
	        outCRReg( 0x4a, fg>>8 );
	        SelectIGA1();
	    }
	}
	/* Reset the cursor color stack pointer */
        inCRReg( 0x45 );
        outCRReg( 0x4b, bg );
        outCRReg( 0x4b, bg>>8 );

	if (psav->MergedFB) {
        /* cursor 2 */
        inCRReg( 0x45 );
	SelectIGA2();
        outCRReg( 0x4b, bg );
        outCRReg( 0x4b, bg>>8 );
	SelectIGA1();
	}

	if( bNeedExtra )
	{
	    outCRReg( 0x4b, bg );
	    outCRReg( 0x4b, bg>>8 );

		/* cursor 2 */
	    if (psav->MergedFB) {
	        SelectIGA2();
	        outCRReg( 0x4b, bg );
	        outCRReg( 0x4b, bg>>8 );
	        SelectIGA1();
	    }
	}
    }
    else if( pScrn->depth == 8 )
    {
	/* Reset the cursor color stack pointer */
	inCRReg(0x45);
	/* Write foreground */
	outCRReg(0x4a, fg);
	outCRReg(0x4a, fg);
	/* Reset the cursor color stack pointer */
	inCRReg(0x45);
	/* Write background */
	outCRReg(0x4b, bg);
	outCRReg(0x4b, bg);
	if (psav->MergedFB) {
	    /* cursor 2 */
	    /* Reset the cursor color stack pointer */
	    inCRReg(0x45);
	    SelectIGA2();
	    /* Write foreground */
	    outCRReg(0x4a, fg);
	    outCRReg(0x4a, fg);
	    SelectIGA1();
	    /* Reset the cursor color stack pointer */
	    inCRReg(0x45);
	    SelectIGA2();
	    /* Write background */
	    outCRReg(0x4b, bg);
	    outCRReg(0x4b, bg);
	    SelectIGA1();
	}
    }
}
