/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/apm/apm_cursor.c,v 1.14 2000/06/30 18:27:02 dawes Exp $ */


#include "X.h"
#include "Xproto.h"
#include "misc.h"
#include "input.h"
#include "cursorstr.h"
#include "regionstr.h"
#include "scrnintstr.h"
#include "servermd.h"
#include "windowstr.h"
#include "mfb.h"
#include "mipointer.h"

#include "apm.h"

#define CURSORWIDTH	64
#define CURSORHEIGHT	64
#define CURSORSIZE	(CURSORWIDTH * CURSORHEIGHT / 8)
#define CURSORALIGN	((CURSORSIZE + 1023) & ~1023l)

static void	ApmShowCursor(ScrnInfoPtr pScrn);
static void	ApmHideCursor(ScrnInfoPtr pScrn);
static void	ApmSetCursorPosition(ScrnInfoPtr pScrn, int x, int y);
static void	ApmSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg);
static void	ApmLoadCursorImage(ScrnInfoPtr pScrn, u8* data);
static Bool	ApmUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs);

static u8 ConvertTable[256];

/* Inline functions */
static __inline__ void
WaitForFifo(ApmPtr pApm, int slots)
{
  if (!pApm->UsePCIRetry) {
    volatile int i;
#define MAXLOOP 1000000

    for(i = 0; i < MAXLOOP; i++) {
      if ((STATUS() & STATUS_FIFO) >= slots)
	break;
    }
    if (i == MAXLOOP) {
      unsigned int status = STATUS();

      WRXB(0x1FF, 0);
      if (!xf86ServerIsExiting())
	FatalError("Hung in WaitForFifo() (Status = 0x%08X)\n", status);
    }
  }
}

void ApmHWCursorReserveSpace(ApmPtr pApm)
{
  pApm->OffscreenReserved	+= 2 * CURSORALIGN;
  pApm->DisplayedCursorAddress	= pApm->BaseCursorAddress =
  pApm->CursorAddress	= 1024 * xf86Screens[pApm->pScreen->myNum]->videoRam -
					pApm->OffscreenReserved;
}


int ApmHWCursorInit(ScreenPtr pScreen)
{
  ScrnInfoPtr		pScrn = xf86Screens[pScreen->myNum];
  APMDECL(pScrn);
  xf86CursorInfoPtr	infoPtr;
  u32			i;

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

  pApm->CursorInfoRec		= infoPtr;

  infoPtr->MaxWidth	= CURSORWIDTH;
  infoPtr->MaxHeight	= CURSORHEIGHT;

  infoPtr->Flags = HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1;
  infoPtr->SetCursorColors	= ApmSetCursorColors;
  infoPtr->SetCursorPosition	= ApmSetCursorPosition;
  infoPtr->LoadCursorImage	= ApmLoadCursorImage;
  infoPtr->HideCursor		= ApmHideCursor;
  infoPtr->ShowCursor		= ApmShowCursor;
  infoPtr->UseHWCursor		= ApmUseHWCursor;
  
  /*ErrorF("%s %s: %s: Using hardware cursor (XAA).\n", 
         XCONFIG_PROBED, vga256InfoRec.name, vga256InfoRec.chipset);    

  if(XAACursorInfoRec.Flags & USE_HARDWARE_CURSOR) {
    vgaHWCursor.Init = XAACursorInit;
    vgaHWCursor.Initialized = TRUE;
    vgaHWCursor.Restore = XAARestoreCursor;
    vgaHWCursor.Warp = XAAWarpCursor;
    vgaHWCursor.QueryBestSize = XAAQueryBestSize;
  }*/

  /* Set up the convert table for the input cursor data */
  for (i = 0; i < 256; i++)
    ConvertTable[i] = ((~i) & 0xAA) | (i & (i >> 1) & 0x55);

  return xf86InitCursor(pScreen, infoPtr);
}

 
static void 
ApmShowCursor(ScrnInfoPtr pScrn)
{
  APMDECL(pScrn);

  WaitForFifo(pApm, 2);
  WRXW(0x144, pApm->CursorAddress >> 10);
  WRXB(0x140, 1);
  pApm->DisplayedCursorAddress = pApm->CursorAddress;
}


static void
ApmHideCursor(ScrnInfoPtr pScrn)
{
  APMDECL(pScrn);

  WaitForFifo(pApm, 1);
  WRXB(0x140, 0);
}

static Bool ApmUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs)
{
    return APMPTR(xf86Screens[pScreen->myNum])->CurrentLayout.bitsPerPixel >= 8;
}

static void
ApmSetCursorPosition(ScrnInfoPtr pScrn, int x, int y)
{
  APMDECL(pScrn);
  int	xoff, yoff;

  if (x < -CURSORWIDTH || y < -CURSORHEIGHT) {
      WaitForFifo(pApm, 1);
      WRXB(0x140, 0);
      return;
  }

  if (x < 0) {
      xoff = -x;
      x = 0;
  }
  else
      xoff = 0;
  if (y < 0) {
      yoff = -y;
      y = 0;
  }
  else
      yoff = 0;

  WaitForFifo(pApm, 2);
  WRXW(0x14C, (yoff << 8) | (xoff & 0xFF));
  WRXL(0x148, (y << 16) | (x & 0xFFFF));
}


static void
ApmSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg)
{	 
  APMDECL(pScrn);
  u16 packedcolfg, packedcolbg;

  if (pApm->CurrentLayout.bitsPerPixel == 8)
  {
    WaitForFifo(pApm, 2);
    WRXB(0x141, fg);
    WRXB(0x142, bg);
  }
  else
  {
    packedcolfg = 
      ((fg & 0xE00000) >> 16) |
      ((fg & 0x00E000) >> 11) |
      ((fg & 0x0000C0) >> 6);
    packedcolbg = 
      ((bg & 0xE00000) >> 16) |
      ((bg & 0x00E000) >> 11) |
      ((bg & 0x0000C0) >> 6);
    WaitForFifo(pApm, 2);
    WRXB(0x141, packedcolfg);
    WRXB(0x142, packedcolbg);
  }
}


static void 
ApmLoadCursorImage(ScrnInfoPtr pScrn, u8* data)
{
  APMDECL(pScrn);
  u32 i;
  u8 tmp[2 * CURSORSIZE];

  /* Correct input data */
  for (i = 0; i < sizeof tmp; i++)
    tmp[i] = ConvertTable[data[i]];
  /*
   * To avoid flicker.
   * Note: 2*pApm->BaseCursorAddress + CURSORALIGN (=1024) < 2^31 all the time.
   */
  pApm->CursorAddress = 2*pApm->BaseCursorAddress + CURSORALIGN - pApm->DisplayedCursorAddress;
  memcpy((u8*)pApm->FbBase + pApm->CursorAddress, tmp, sizeof tmp);
}
