/* Copyright (c) 2003-2005 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 * */

/*
 * File Contents:   This file is consists of main Xfree acceleration supported
 *                  routines like solid fill used here.
 *
 * Project:         Geode Xfree Frame buffer device driver.
 * */

/* #undef OPT_ACCEL */

/* Xfree86 header files */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "vgaHW.h"
#include "xf86.h"
#include "xf86fbman.h"
#include "miline.h"
#include "xaarop.h"
#include "servermd.h"
#include "picture.h"
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Pci.h"
#include "geode.h"
#include "gfx_defs.h"
#include "gfx_regs.h"

/* Common macros for blend operations are here */

#include "geode_blend.h"

#undef ulong
typedef unsigned long ulong;

#undef uint
typedef unsigned int uint;

#undef ushort
typedef unsigned short ushort;

#undef uchar
typedef unsigned char uchar;

#define CALC_FBOFFSET(x, y) \
	        (((ulong)(y) * gu2_pitch + ((ulong)(x) << gu2_xshift)))

#define FBADDR(x,y)				\
		((unsigned char *)pGeode->FBBase + CALC_FBOFFSET(x, y))

#define OS_UDELAY 0
#if OS_UDELAY > 0
#define OS_USLEEP(usec) usleep(usec);
#else
#define OS_USLEEP(usec)
#endif

#ifdef OPT_ACCEL
static unsigned int BPP;
static unsigned int BLT_MODE;
static unsigned int ACCEL_STRIDE;

#define GU2_WAIT_PENDING while(READ_GP32(MGP_BLT_STATUS) & MGP_BS_BLT_PENDING)
#define GU2_WAIT_BUSY    while(READ_GP32(MGP_BLT_STATUS) & MGP_BS_BLT_BUSY)
#endif

#define HOOK(fn) localRecPtr->fn = GX##fn

#define DLOG(l, fmt, args...) ErrorF(fmt, ##args)

/* static storage declarations */

typedef struct sGBltBox {
    ulong x, y;
    ulong w, h;
    ulong color;
    int bpp, transparent;
} GBltBox;

static unsigned int gu2_xshift, gu2_yshift;
static unsigned int gu2_pitch;

/* pat  0xF0 */
/* src  0xCC */
/* dst  0xAA */

/* (src FUNC dst) */

static const int SDfn[16] = {
    0x00, 0x88, 0x44, 0xCC, 0x22, 0xAA, 0x66, 0xEE,
    0x11, 0x99, 0x55, 0xDD, 0x33, 0xBB, 0x77, 0xFF
};

/* ((src FUNC dst) AND pat-mask) OR (dst AND (NOT pat-mask)) */

static const int SDfn_PM[16] = {
    0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
    0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA
};

#ifdef OPT_ACCEL
static inline CARD32
amd_gx_BppToRasterMode(int bpp)
{
    switch (bpp) {
    case 16:
        return MGP_RM_BPPFMT_565;
    case 32:
        return MGP_RM_BPPFMT_8888;
    case 8:
        return MGP_RM_BPPFMT_332;
    default:
        return 0;
    }
}
#endif                          /* OPT_ACCEL */

/*----------------------------------------------------------------------------
 * GXAccelSync.
 *
 * Description  :This function is called to synchronize with the graphics
 *               engine and it waits the graphic engine is idle.  This is
 *               required before allowing direct access to the framebuffer.
 *
 *    Arg        Type     Comment
 *  pScrni   ScrnInfoPtr  pointer to Screeen info
 *
 * Returns              :none
 *---------------------------------------------------------------------------*/
void
GXAccelSync(ScrnInfoPtr pScrni)
{
    //ErrorF("GXAccelSync()\n");
#ifndef OPT_ACCEL
    gfx_wait_until_idle();
#else
    GU2_WAIT_BUSY;
#endif
}

#define VM_MAJOR_DEC 0
#define VM_MINOR_DEC 0

#if XF86EXA

static void
amd_gx_exa_WaitMarker(ScreenPtr pScreen, int Marker)
{
    GU2_WAIT_BUSY;
}

static void
amd_gx_exa_Done(PixmapPtr p)
{
}

static Bool
amd_gx_exa_UploadToScreen(PixmapPtr pDst, int x, int y, int w, int h,
                          char *src, int src_pitch)
{
    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pDst);
    unsigned char *dst = pGeode->pExa->memoryBase + exaGetPixmapOffset(pDst);
    int dst_pitch = exaGetPixmapPitch(pDst);
    int bpp = pDst->drawable.bitsPerPixel;

    dst += y * dst_pitch + x * (bpp >> 3);
    GU2_WAIT_BUSY;
    geode_memory_to_screen_blt((unsigned long) src, (unsigned long) dst,
                               src_pitch, dst_pitch, w, h, bpp);
    return TRUE;
}

static Bool
amd_gx_exa_DownloadFromScreen(PixmapPtr pSrc, int x, int y, int w, int h,
                              char *dst, int dst_pitch)
{
    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pSrc);
    unsigned char *src = pGeode->pExa->memoryBase + exaGetPixmapOffset(pSrc);
    int src_pitch = exaGetPixmapPitch(pSrc);
    int bpp = pSrc->drawable.bitsPerPixel;

    src += (y * src_pitch) + (x * (bpp >> 3));
    GU2_WAIT_BUSY;
    geode_memory_to_screen_blt((unsigned long) src, (unsigned long) dst,
                               src_pitch, dst_pitch, w, h, bpp);
    return TRUE;
}

/* Solid */

static Bool
amd_gx_exa_PrepareSolid(PixmapPtr pxMap, int alu, Pixel planemask, Pixel fg)
{
    int dstPitch = exaGetPixmapPitch(pxMap);
    unsigned int ROP = amd_gx_BppToRasterMode(pxMap->drawable.bitsPerPixel)
        | (planemask == ~0U ? SDfn[alu] : SDfn_PM[alu]);

    //  FIXME: this should go away -- workaround for the blockparty icon corruption
    //if (pxMap->drawable.bitsPerPixel == 32)
    //  return FALSE;

    BLT_MODE = ((ROP ^ (ROP >> 2)) & 0x33) == 0 ? MGP_BM_SRC_MONO : 0;
    if (((ROP ^ (ROP >> 1)) & 0x55) != 0)
        BLT_MODE |= MGP_BM_DST_REQ;
    //ErrorF("amd_gx_exa_PrepareSolid(%#x,%#x,%#x - ROP=%x,BLT_MODE=%x)\n", alu, planemask, fg, ROP, BLT_MODE);
    GU2_WAIT_PENDING;
    WRITE_GP32(MGP_RASTER_MODE, ROP);
    WRITE_GP32(MGP_PAT_COLOR_0, planemask);
    WRITE_GP32(MGP_SRC_COLOR_FG, fg);
    WRITE_GP32(MGP_STRIDE, dstPitch);
    return TRUE;
}

static void
amd_gx_exa_Solid(PixmapPtr pxMap, int x1, int y1, int x2, int y2)
{
    int bpp = (pxMap->drawable.bitsPerPixel + 7) / 8;
    int pitch = exaGetPixmapPitch(pxMap);
    unsigned int offset = exaGetPixmapOffset(pxMap) + pitch * y1 + bpp * x1;
    unsigned int size = ((x2 - x1) << 16) | (y2 - y1);

    //ErrorF("amd_gx_exa_Solid() at %d,%d %d,%d - offset=%d, bpp=%d\n", x1, y1, x2, y2, offset, bpp);

    GU2_WAIT_PENDING;
    WRITE_GP32(MGP_DST_OFFSET, offset);
    WRITE_GP32(MGP_WID_HEIGHT, size);
    WRITE_GP32(MGP_BLT_MODE, BLT_MODE);
}

/* Copy */

static Bool
amd_gx_exa_PrepareCopy(PixmapPtr pxSrc, PixmapPtr pxDst, int dx, int dy,
                       int alu, Pixel planemask)
{
    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);
    int dstPitch = exaGetPixmapPitch(pxDst);
    unsigned int ROP;

    /* Punt if the color formats aren't the same */

    if (pxSrc->drawable.bitsPerPixel != pxDst->drawable.bitsPerPixel)
        return FALSE;

    //ErrorF("amd_gx_exa_PrepareCopy() dx%d dy%d alu %#x %#x\n",
    //  dx, dy, alu, planemask);

    pGeode->cpySrcOffset = exaGetPixmapOffset(pxSrc);
    pGeode->cpySrcPitch = exaGetPixmapPitch(pxSrc);
    pGeode->cpySrcBpp = (pxSrc->drawable.bitsPerPixel + 7) / 8;
    pGeode->cpyDx = dx;
    pGeode->cpyDy = dy;
    ROP = amd_gx_BppToRasterMode(pxSrc->drawable.bitsPerPixel) |
        (planemask == ~0U ? SDfn[alu] : SDfn_PM[alu]);

    BLT_MODE = ((ROP ^ (ROP >> 1)) & 0x55) != 0 ?
        MGP_BM_SRC_FB | MGP_BM_DST_REQ : MGP_BM_SRC_FB;
    GU2_WAIT_PENDING;
    WRITE_GP32(MGP_RASTER_MODE, ROP);
    WRITE_GP32(MGP_PAT_COLOR_0, planemask);
    WRITE_GP32(MGP_SRC_COLOR_FG, ~0);
    WRITE_GP32(MGP_SRC_COLOR_BG, ~0);
    WRITE_GP32(MGP_STRIDE, (pGeode->cpySrcPitch << 16) | dstPitch);
    return TRUE;
}

static void
amd_gx_exa_Copy(PixmapPtr pxDst, int srcX, int srcY, int dstX, int dstY,
                int w, int h)
{
    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);
    int dstBpp = (pxDst->drawable.bitsPerPixel + 7) / 8;
    int dstPitch = exaGetPixmapPitch(pxDst);
    unsigned int srcOffset =
        pGeode->cpySrcOffset + (pGeode->cpySrcPitch * srcY) +
        (pGeode->cpySrcBpp * srcX);
    unsigned int dstOffset =
        exaGetPixmapOffset(pxDst) + (dstPitch * dstY) + (dstBpp * dstX);
    unsigned int size = (w << 16) | h;
    unsigned int blt_mode = BLT_MODE;

    //ErrorF("amd_gx_exa_Copy() from %d,%d to %d,%d %dx%d\n", srcX, srcY,
    //   dstX, dstY, w, h);

    if (pGeode->cpyDx < 0) {
        srcOffset += w * pGeode->cpySrcBpp - 1;
        dstOffset += w * dstBpp - 1;
        blt_mode |= MGP_BM_NEG_XDIR;
    }
    if (pGeode->cpyDy < 0) {
        srcOffset += (h - 1) * pGeode->cpySrcPitch;
        dstOffset += (h - 1) * dstPitch;
        blt_mode |= MGP_BM_NEG_YDIR;
    }
    GU2_WAIT_PENDING;
    WRITE_GP32(MGP_SRC_OFFSET, srcOffset);
    WRITE_GP32(MGP_DST_OFFSET, dstOffset);
    WRITE_GP32(MGP_WID_HEIGHT, size);
    WRITE_GP16(MGP_BLT_MODE, blt_mode);
}

/* A=SRC, B=DST */
#define SRC_DST 0
/* B=SRC, A=DST */
#define DST_SRC MGP_RM_DEST_FROM_CHAN_A
/* A*alpha + B*0         */
#define Aa_B0   MGP_RM_ALPHA_TIMES_A
/* A*0     + B*(1-alpha) */
#define A0_B1a  MGP_RM_BETA_TIMES_B
/* A*1     + B*(1-alpha) */
#define A1_B1a  MGP_RM_A_PLUS_BETA_B
/* A*alpha + B*(1-alpha) */
#define Aa_B1a  MGP_RM_ALPHA_A_PLUS_BETA_B
/* alpha from A */
#define a_A MGP_RM_SELECT_ALPHA_A
/* alpha from B */
#define a_B MGP_RM_SELECT_ALPHA_B
/* alpha from const */
#define a_C MGP_RM_SELECT_ALPHA_R
/* alpha = 1 */
#define a_1 MGP_RM_SELECT_ALPHA_1

#define MGP_RM_ALPHA_TO_ARGB (MGP_RM_ALPHA_TO_ALPHA | MGP_RM_ALPHA_TO_RGB)
#define gxPictOpMAX PictOpAdd   /* highest accelerated op */

unsigned int amd_gx_exa_alpha_ops[] =
/*    A   B      OP     AS           const = 0 */
{
    (SRC_DST | Aa_B0 | a_C), 0, /* clear    (src*0) */
    (SRC_DST | Aa_B0 | a_1), 0, /* src      (src*1) */
    (DST_SRC | Aa_B0 | a_1), 0, /* dst      (dst*1) */
    (SRC_DST | A1_B1a | a_A), 0,        /* src-over (src*1 + dst(1-A)) */
    (DST_SRC | A1_B1a | a_A), 0,        /* dst-over (dst*1 + src(1-B)) */
    (SRC_DST | Aa_B0 | a_B), 0, /* src-in   (src*B) */
    (DST_SRC | Aa_B0 | a_B), 0, /* dst-in   (dst*A) */
    (DST_SRC | A0_B1a | a_A), 0,        /* src-out  (src*(1-B)) */
    (SRC_DST | A0_B1a | a_A), 0,        /* dst-out  (dst*(1-A)) */
/* pass1 (SRC=dst DST=scr=src), pass2 (SRC=src, DST=dst) */
    (DST_SRC | Aa_B0 | a_B),    /* srcatop  (src*B) */
    (SRC_DST | A0_B1a | a_A),   /*                  + (dst(1-A)) */
    (SRC_DST | Aa_B0 | a_B),    /* dstatop  (dst*A) */
    (DST_SRC | A0_B1a | a_A),   /*                  + (src(1-B) */
    (SRC_DST | A0_B1a | a_A),   /* xor      (src*(1-B) */
    (SRC_DST | A0_B1a | a_A),   /*                  + (dst(1-A) */
    (SRC_DST | A1_B1a | a_C), 0,        /* add      (src*1 + dst*1) */
};

typedef struct {
    int exa_fmt;
    int bpp;
    int gx_fmt;
    int alpha_bits;
} amd_gx_exa_fmt_t;

amd_gx_exa_fmt_t amd_gx_exa_fmts[] = {
    {PICT_a8r8g8b8, 32, MGP_RM_BPPFMT_8888, 8},
    {PICT_x8r8g8b8, 32, MGP_RM_BPPFMT_8888, 0},
    {PICT_a4r4g4b4, 16, MGP_RM_BPPFMT_4444, 4},
    {PICT_a1r5g5b5, 16, MGP_RM_BPPFMT_1555, 1},
    {PICT_r5g6b5, 16, MGP_RM_BPPFMT_565, 0},
    {PICT_r3g3b2, 8, MGP_RM_BPPFMT_332, 0},
};

static amd_gx_exa_fmt_t *
amd_gx_exa_check_format(PicturePtr p)
{
    int i;
    int bpp = p->pDrawable ? p->pDrawable->bitsPerPixel : 0;
    amd_gx_exa_fmt_t *fp = &amd_gx_exa_fmts[0];

    for (i = sizeof(amd_gx_exa_fmts) / sizeof(amd_gx_exa_fmts[0]); --i >= 0;
         ++fp) {
        if (fp->bpp < bpp)
            return NULL;
        if (fp->bpp != bpp)
            continue;
        if (fp->exa_fmt == p->format)
            break;
    }
    return i < 0 ? NULL : fp;
}

/* Composite */

static Bool
amd_gx_exa_CheckComposite(int op, PicturePtr pSrc, PicturePtr pMsk,
                          PicturePtr pDst)
{
    GeodeRec *pGeode = GEODEPTR_FROM_PICTURE(pDst);

    if (op > gxPictOpMAX)
        return FALSE;
    if (pMsk)
        return FALSE;
    if (usesPasses(op) && pGeode->exaBfrSz == 0)
        return FALSE;
    if (pSrc->filter != PictFilterNearest &&
        pSrc->filter != PictFilterFast &&
        pSrc->filter != PictFilterGood && pSrc->filter != PictFilterBest)
        return FALSE;
    if (pSrc->repeat)
        return FALSE;
    if (pSrc->transform)
        return FALSE;
    return TRUE;
}

static Bool
amd_gx_exa_PrepareComposite(int op, PicturePtr pSrc, PicturePtr pMsk,
                            PicturePtr pDst, PixmapPtr pxSrc, PixmapPtr pxMsk,
                            PixmapPtr pxDst)
{
    int srcPitch;
    amd_gx_exa_fmt_t *sfp, *dfp;
    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);

    if (!pxSrc || !pSrc->pDrawable)
        return FALSE;

    //ErrorF("amd_gx_exa_PrepareComposite()\n");

    if ((sfp = amd_gx_exa_check_format(pSrc)) == NULL)
        return FALSE;
    if (sfp->alpha_bits == 0 && usesSrcAlpha(op))
        return FALSE;
    if ((dfp = amd_gx_exa_check_format(pDst)) == NULL)
        return FALSE;
    if (dfp->alpha_bits == 0 && usesDstAlpha(op))
        return FALSE;
    if (sfp->gx_fmt != dfp->gx_fmt)
        return FALSE;
    srcPitch = exaGetPixmapPitch(pxSrc);
    if (usesPasses(op) && srcPitch > pGeode->exaBfrSz)
        return FALSE;
    pGeode->cmpSrcPitch = srcPitch;
    pGeode->cmpOp = op;
    pGeode->cmpSrcOffset = exaGetPixmapOffset(pxSrc);
    pGeode->cmpSrcBpp = (pxSrc->drawable.bitsPerPixel + 7) / 8;
    pGeode->cmpSrcFmt = sfp->gx_fmt;
    pGeode->cmpDstFmt = dfp->gx_fmt | (dfp->alpha_bits == 0 ?
                                       MGP_RM_ALPHA_TO_RGB :
                                       MGP_RM_ALPHA_TO_ARGB);
    return TRUE;
}

static void
amd_gx_exa_Composite(PixmapPtr pxDst, int srcX, int srcY, int maskX,
                     int maskY, int dstX, int dstY, int width, int height)
{
    int op, current_line, max_lines, lines, pass, scratchPitch;
    unsigned int srcOffset, srcOfs = 0, srcPitch, srcPch = 0, srcBpp;
    unsigned int dstOffset, dstOfs = 0, dstPitch, dstPch = 0, dstBpp;
    unsigned int sizes, strides, blt_mode = 0, rop = 0;
    GeodeRec *pGeode = GEODEPTR_FROM_PIXMAP(pxDst);

    //ErrorF("amd_gx_exa_Composite() from %d,%d to %d,%d %dx%d\n",
    //    srcX, srcY, dstX, dstY, width, height);

    op = pGeode->cmpOp;
    if (usesPasses(op)) {
        int cacheLineSz = 32;
        int cachelines =
            (width * pGeode->cmpSrcBpp + cacheLineSz - 1) / cacheLineSz;
        scratchPitch = cachelines * cacheLineSz;
        if (scratchPitch > pGeode->cmpSrcPitch)
            scratchPitch = pGeode->cmpSrcPitch;
        max_lines = pGeode->exaBfrSz / scratchPitch;
    }
    else {
        scratchPitch = 0;
        max_lines = height;
    }

    dstBpp = (pxDst->drawable.bitsPerPixel + 7) / 8;
    dstPitch = exaGetPixmapPitch(pxDst);
    dstOffset = exaGetPixmapOffset(pxDst) + dstPitch * dstY + dstBpp * dstX;
    srcBpp = pGeode->cmpSrcBpp;
    srcPitch = pGeode->cmpSrcPitch;
    srcOffset = pGeode->cmpSrcOffset + srcPitch * srcY + srcBpp * srcX;

    current_line = pass = 0;
    while (current_line < height) {
        if (usesPasses(op)) {
            lines = height - current_line;
            if (lines > max_lines)
                lines = max_lines;
            switch (pass) {
            case 0:            /* copy src to scratch */
                srcPch = srcPitch;
                srcOfs = srcOffset + current_line * srcPch;
                dstPch = scratchPitch;
                dstOfs = pGeode->exaBfrOffset;
                rop = pGeode->cmpSrcFmt | MGP_RM_ALPHA_TO_ARGB;
                rop |= amd_gx_exa_alpha_ops[PictOpSrc * 2];
                blt_mode = usesChanB0(PictOpSrc) ?
                    MGP_BM_SRC_FB | MGP_BM_DST_REQ : MGP_BM_SRC_FB;
                ++pass;
                break;
            case 1:            /* pass1 */
                srcPch = dstPitch;
                srcOfs = dstOffset + current_line * srcPch;
                dstPch = scratchPitch;
                dstOfs = pGeode->exaBfrOffset;
                rop = pGeode->cmpSrcFmt | MGP_RM_ALPHA_TO_ARGB;
                rop |= amd_gx_exa_alpha_ops[op * 2];
                blt_mode = usesChanB1(op) ?
                    MGP_BM_SRC_FB | MGP_BM_DST_REQ : MGP_BM_SRC_FB;
                ++pass;
                break;
            case 2:            /* pass2 */
                srcPch = srcPitch;
                srcOfs = srcOffset + current_line * srcPch;
                dstPch = dstPitch;
                dstOfs = dstOffset + current_line * dstPch;
                rop = pGeode->cmpSrcFmt | MGP_RM_ALPHA_TO_ARGB;
                rop |= amd_gx_exa_alpha_ops[op * 2 + 1];
                blt_mode = usesChanB2(op) ?
                    MGP_BM_SRC_FB | MGP_BM_DST_REQ : MGP_BM_SRC_FB;
                ++pass;
                break;
            case 3:            /* add */
                srcPch = scratchPitch;
                srcOfs = pGeode->exaBfrOffset;
                dstPch = dstPitch;
                dstOfs = dstOffset + current_line * dstPch;
                rop = pGeode->cmpDstFmt;
                rop |= amd_gx_exa_alpha_ops[PictOpAdd * 2];
                blt_mode = usesChanB0(PictOpAdd) ?
                    MGP_BM_SRC_FB | MGP_BM_DST_REQ : MGP_BM_SRC_FB;
                current_line += lines;
                pass = 0;
                break;
            }
            strides = (srcPch << 16) | dstPch;
        }
        else {                  /* not multi pass */
            srcOfs = srcOffset;
            dstOfs = dstOffset;
            current_line = lines = height;
            strides = (srcPitch << 16) | dstPitch;
            rop = pGeode->cmpDstFmt | amd_gx_exa_alpha_ops[op * 2];
            blt_mode = usesChanB0(op) ?
                MGP_BM_SRC_FB | MGP_BM_DST_REQ : MGP_BM_SRC_FB;
        }
        sizes = (width << 16) | lines;
        if (srcOfs < dstOfs) {
            srcOfs += (lines - 1) * srcPitch + width * srcBpp - 1;
            dstOfs += (lines - 1) * dstPitch + width * dstBpp - 1;
            blt_mode |= MGP_BM_NEG_XDIR | MGP_BM_NEG_YDIR;
        }
        GU2_WAIT_PENDING;
        WRITE_GP32(MGP_RASTER_MODE, rop);
        WRITE_GP32(MGP_SRC_OFFSET, srcOfs);
        WRITE_GP32(MGP_DST_OFFSET, dstOfs);
        WRITE_GP32(MGP_WID_HEIGHT, sizes);
        WRITE_GP32(MGP_STRIDE, strides);
        WRITE_GP16(MGP_BLT_MODE, blt_mode);
    }
}
#endif                          /* #if XF86EXA */

/*----------------------------------------------------------------------------
 * GXAccelInit.
 *
 * Description:	This function sets up the supported acceleration routines and
 *              appropriate flags.
 *
 * Parameters:
 *      pScrn:	Screeen pointer structure.
 *
 * Returns:		TRUE on success and FALSE on Failure
 *
 * Comments:	This function is called in GXScreenInit in
 *              geode_driver.c to set  * the acceleration.
 *----------------------------------------------------------------------------
 */
Bool
GXAccelInit(ScreenPtr pScrn)
{
    ScrnInfoPtr pScrni = xf86ScreenToScrn(pScrn);
    GeodeRec *pGeode = GEODEPTR(pScrni);

#if XF86EXA
    ExaDriverPtr pExa = pGeode->pExa;
#endif

    gu2_xshift = pScrni->bitsPerPixel >> 4;

    /* XXX - fixme - this will change - we'll need to update it */

    gu2_pitch = pGeode->Pitch;

    switch (pGeode->Pitch) {
    case 1024:
        gu2_yshift = 10;
        break;
    case 2048:
        gu2_yshift = 11;
        break;
    case 4096:
        gu2_yshift = 12;
        break;
    default:
        gu2_yshift = 13;
        break;
    }

#ifdef OPT_ACCEL
    ACCEL_STRIDE = (pGeode->Pitch << 16) | pGeode->Pitch;
    BPP = amd_gx_BppToRasterMode(pScrni->bitsPerPixel);
#endif

#if XF86EXA
    if (pExa && pGeode->useEXA) {
        pExa->exa_major = EXA_VERSION_MAJOR;
        pExa->exa_minor = EXA_VERSION_MINOR;

        /* Sync */
        pExa->WaitMarker = amd_gx_exa_WaitMarker;
        /* UploadToScreen */
        pExa->UploadToScreen = amd_gx_exa_UploadToScreen;
        pExa->DownloadFromScreen = amd_gx_exa_DownloadFromScreen;

        /* Solid fill */
        pExa->PrepareSolid = amd_gx_exa_PrepareSolid;
        pExa->Solid = amd_gx_exa_Solid;
        pExa->DoneSolid = amd_gx_exa_Done;

        /* Copy */
        pExa->PrepareCopy = amd_gx_exa_PrepareCopy;
        pExa->Copy = amd_gx_exa_Copy;
        pExa->DoneCopy = amd_gx_exa_Done;

        /* Composite */
        pExa->CheckComposite = amd_gx_exa_CheckComposite;
        pExa->PrepareComposite = amd_gx_exa_PrepareComposite;
        pExa->Composite = amd_gx_exa_Composite;
        pExa->DoneComposite = amd_gx_exa_Done;

        return exaDriverInit(pScrn, pGeode->pExa);
    }
#endif

    return FALSE;
}
