/*
 * $Id$
 *
 * Copyright © 2003-2004 Anders Carlsson
 *
 * 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 Anders Carlsson not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Anders Carlsson makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * ANDERS CARLSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL ANDERS CARLSSON 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 HAVE_CONFIG_H
#include <kdrive-config.h>
#endif
#include "mga.h"
#include "g400_common.h"
#include "kaa.h"
#include <unistd.h>

CARD32 mgaRop[16] = {
    /* GXclear        */  MGA_ATYPE_RPL  | 0x00000000,	/* 0 */
    /* GXand          */  MGA_ATYPE_RSTR | 0x00080000,	/* src AND dst */
    /* GXandReverse   */  MGA_ATYPE_RSTR | 0x00040000,	/* src AND NOT dst */
    /* GXcopy         */  MGA_ATYPE_RSTR | 0x000c0000,	/* src */
    /* GXandInverted  */  MGA_ATYPE_RSTR | 0x00020000,	/* NOT src AND dst */
    /* GXnoop         */  MGA_ATYPE_RSTR | 0x000a0000,	/* dst */
    /* GXxor          */  MGA_ATYPE_RSTR | 0x00060000,	/* src XOR dst */
    /* GXor           */  MGA_ATYPE_RSTR | 0x000e0000,	/* src OR dst */
    /* GXnor          */  MGA_ATYPE_RSTR | 0x00010000,	/* NOT src AND NOT dst */
    /* GXequiv        */  MGA_ATYPE_RSTR | 0x00090000,	/* NOT src XOR dst */
    /* GXinvert       */  MGA_ATYPE_RSTR | 0x00050000,	/* NOT dst */
    /* GXorReverse    */  MGA_ATYPE_RSTR | 0x000d0000,	/* src OR NOT dst */
    /* GXcopyInverted */  MGA_ATYPE_RPL  | 0x00030000,	/* NOT src */
    /* GXorInverted   */  MGA_ATYPE_RSTR | 0x000b0000,	/* NOT src OR dst */
    /* GXnand         */  MGA_ATYPE_RSTR | 0x00070000,	/* NOT src OR NOT dst */
    /* GXset          */  MGA_ATYPE_RPL  | 0x000f0000	/* 1 */
};

VOL8 *mmio;
int fifo_size;
int pitch, src_pitch;
int dir;

void
mgaWaitAvail (int n)
{
    if (fifo_size < n) {
      while ((fifo_size = MGA_IN32 (mmio, MGA_REG_FIFOSTATUS) & 0xff) < n)
	;
    }
    
    fifo_size -= n;
}

#define MGA_OUT8(mmio, a, v) (*(VOL8 *) ((mmio) + (a)) = (v))
#define MGA_REG_CRTC_INDEX	(0x1fd4)

void
mgaWaitIdle (void)
{
    
    mgaWaitAvail (2);
    MGA_OUT32(mmio, MGA_REG_CACHEFLUSH, 0);
    /* MGA_OUT8 (mmio, MGA_REG_CRTC_INDEX, 0); */
    while (MGA_IN32 (mmio, MGA_REG_STATUS) & 0x10000)
	;
}

static void
mgaWaitMarker (ScreenPtr pScreen, int marker)
{
    KdScreenPriv (pScreen);
    mgaCardInfo (pScreenPriv);

    mmio = mgac->reg_base;

    mgaWaitIdle ();
}

Bool
mgaSetup (ScreenPtr pScreen, int dest_bpp, int wait)
{
  KdScreenPriv (pScreen);
  mgaScreenInfo (pScreenPriv);
  mgaCardInfo (pScreenPriv);

  fifo_size = 0;
  mmio = mgac->reg_base;
  pitch = mgas->pitch;
  
  if (!mmio)
    return FALSE;

  mgaWaitAvail (wait + 4);
  /* Set the format of the destination pixmap */
  switch (dest_bpp) {
  case 8:
    MGA_OUT32 (mmio, MGA_REG_MACCESS, MGA_PW8);
    break;
  case 16:
    MGA_OUT32 (mmio, MGA_REG_MACCESS, MGA_PW16);
    break;
  case 24:
  case 32:
    MGA_OUT32 (mmio, MGA_REG_MACCESS, MGA_PW24);
    break;
  }
  MGA_OUT32 (mmio, MGA_REG_CXBNDRY, 0xffff0000);
  MGA_OUT32 (mmio, MGA_REG_YTOP, 0x00000000);
  MGA_OUT32 (mmio, MGA_REG_YBOT, 0x007fffff);

  return TRUE;
}

static Bool
mgaPrepareSolid (PixmapPtr pPixmap, int alu, Pixel pm, Pixel fg)
{

    KdScreenPriv(pPixmap->drawable.pScreen);
    int cmd;
    int dst_org;
    /* We must pad pm and fg depending on the format of the
     * destination pixmap 
     */
    switch (pPixmap->drawable.bitsPerPixel) {
    case 16:
        fg |= fg << 16;
        pm |= pm << 16;
        break;
    case 8:
        fg |= (fg << 8) | (fg << 16) | (fg << 24);
        pm |= (pm << 8) | (pm << 16) | (pm << 24);
        break;    
    }
    
    cmd = MGA_OPCOD_TRAP | MGA_DWGCTL_SOLID | MGA_DWGCTL_ARZERO | MGA_DWGCTL_SGNZERO |
	MGA_DWGCTL_SHIFTZERO | mgaRop[alu];

    dst_org = (int)pPixmap->devPrivate.ptr - (int)pScreenPriv->screen->memory_base;
    
    mgaSetup (pPixmap->drawable.pScreen, pPixmap->drawable.bitsPerPixel, 5);
    MGA_OUT32 (mmio, MGA_REG_DSTORG, dst_org);
    MGA_OUT32 (mmio, MGA_REG_PITCH, pPixmap->devKind / (pPixmap->drawable.bitsPerPixel >> 3));
    MGA_OUT32 (mmio, MGA_REG_DWGCTL, cmd);
    MGA_OUT32 (mmio, MGA_REG_FCOL, fg);
    MGA_OUT32 (mmio, MGA_REG_PLNWT, pm);

    return TRUE;
}

static void
mgaSolid (int x1, int y1, int x2, int y2)
{
  mgaWaitAvail (2);
  MGA_OUT32 (mmio, MGA_REG_FXBNDRY, (x2 << 16) | (x1 & 0xffff));
  MGA_OUT32 (mmio, MGA_REG_YDSTLEN | MGA_REG_EXEC, (y1 << 16) | (y2 - y1));
}

static void
mgaDoneSolid (void)
{
}

#define BLIT_LEFT	1
#define BLIT_UP		4

static Bool
mgaPrepareCopy (PixmapPtr pSrcPixmap, PixmapPtr pDstPixmap,
		int dx, int dy, int alu, Pixel pm)
{
  KdScreenPriv(pSrcPixmap->drawable.pScreen);
    int cmd;

    cmd = MGA_OPCOD_BITBLT | MGA_DWGCTL_BFCOL | MGA_DWGCTL_SHIFTZERO | mgaRop[alu];

    dir = 0;

    if (dy < 0)
	dir |= BLIT_UP;
    if (dx < 0)
	dir |= BLIT_LEFT;

    mgaSetup (pSrcPixmap->drawable.pScreen,
	      pDstPixmap->drawable.bitsPerPixel, 7);

    MGA_OUT32 (mmio, MGA_REG_SRCORG, ((int)pSrcPixmap->devPrivate.ptr -
				      (int)pScreenPriv->screen->memory_base));
    MGA_OUT32 (mmio, MGA_REG_DSTORG, ((int)pDstPixmap->devPrivate.ptr -
				      (int)pScreenPriv->screen->memory_base));
    MGA_OUT32 (mmio, MGA_REG_PITCH, (pDstPixmap->devKind /
				     (pDstPixmap->drawable.bitsPerPixel >> 3)));
    src_pitch = pSrcPixmap->devKind / (pSrcPixmap->drawable.bitsPerPixel >> 3);
	
    MGA_OUT32 (mmio, MGA_REG_DWGCTL, cmd);
    MGA_OUT32 (mmio, MGA_REG_SGN, dir);
    MGA_OUT32 (mmio, MGA_REG_PLNWT, pm);
    MGA_OUT32 (mmio, MGA_REG_AR5, src_pitch * (dy < 0 ? -1 : 1) );

    return TRUE;
}

static void
mgaCopy (int srcX, int srcY, int dstX, int dstY, int w, int h)
{
    int start, end;
    if (dir & BLIT_UP)
    {
	srcY += h - 1;
	dstY += h - 1;
    }

    w--;
    start = end = srcY * src_pitch + srcX;

    if (dir & BLIT_LEFT)
	start += w;
    else
	end += w;

    mgaWaitAvail (4);
    MGA_OUT32 (mmio, MGA_REG_AR0, end);
    MGA_OUT32 (mmio, MGA_REG_AR3, start);
    MGA_OUT32 (mmio, MGA_REG_FXBNDRY, ((dstX + w) << 16) | (dstX & 0xffff));
    MGA_OUT32 (mmio, MGA_REG_YDSTLEN | MGA_REG_EXEC, (dstY << 16) | h);
}

static void
mgaDoneCopy (void)
{
}

#if 0
static Bool
mgaUploadToScreen(PixmapPtr pDst, char *src, int src_pitch) {
  /*fprintf(stderr,"Upload to Screen %p [%d]\n",src,src_pitch);*/
  return TRUE;
}
#endif

Bool
mgaDrawInit (ScreenPtr pScreen)
{
    KdScreenPriv(pScreen);
    mgaScreenInfo (pScreenPriv);
    KdCardInfo *card = pScreenPriv->card;
        
    memset(&mgas->kaa, 0, sizeof(KaaScreenInfoRec));
    mgas->kaa.waitMarker	= mgaWaitMarker;
    mgas->kaa.PrepareSolid	= mgaPrepareSolid;
    mgas->kaa.Solid		= mgaSolid;
    mgas->kaa.DoneSolid		= mgaDoneSolid;
    mgas->kaa.PrepareCopy	= mgaPrepareCopy;
    mgas->kaa.Copy		= mgaCopy;
    mgas->kaa.DoneCopy		= mgaDoneCopy;
    /* In PW24 mode, we need to align to "3 64-bytes" */
    mgas->kaa.offsetAlign	= 192;
    /* Pitch alignment is in sets of 32 pixels, and we need to cover 32bpp, so
     * 128 bytes
     */
    mgas->kaa.pitchAlign	= 128;
    mgas->kaa.flags		= KAA_OFFSCREEN_PIXMAPS;
    
    if (card->attr.deviceID == MGA_G4XX_DEVICE_ID) {
	mgas->kaa.CheckComposite = mgaCheckComposite;
	mgas->kaa.PrepareComposite = mgaPrepareComposite;
	mgas->kaa.Composite	= mgaComposite;
	mgas->kaa.DoneComposite	= mgaDoneComposite;
    }
    
    /*mgas->kaa.UploadToScreen=mgaUploadToScreen;*/
        
    if (!kaaDrawInit (pScreen, &mgas->kaa))
	return FALSE;

    return TRUE;
}

void
mgaDrawEnable (ScreenPtr pScreen)
{
    KdScreenPriv (pScreen);
    mgaScreenInfo (pScreenPriv);

    mgas->pitch = pScreenPriv->screen->width;

    switch (pScreenPriv->screen->fb[0].depth) {
    case 8:
      mgas->pw = MGA_PW8;
      break;
    case 16:
      mgas->pw = MGA_PW16;
      break;
    case 24:
    case 32:
      mgas->pw = MGA_PW24;
      break;
    default:
      FatalError ("unsupported pixel format");
    }

    kaaMarkSync (pScreen);
}

void
mgaDrawDisable (ScreenPtr pScreen)
{
    kaaWaitSync(pScreen);
}

void
mgaDrawFini (ScreenPtr pScreen)
{
}

