/*
 * Copyright 2006 by Alan Hourihane, North Wales, UK.
 *
 * 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 the authors not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  The authors make no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE AUTHORS 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.
 *
 * Authors:  Alan Hourihane, <alanh@fairlite.demon.co.uk>
 *
 * Trident XP4/XP5 accelerated options.
 */

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

#include "exa.h"
#include "picture.h"

#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Pci.h"
#include "xaarop.h"

#include "trident.h"
#include "trident_regs.h"

static int ropcode;

static int CopyROP[16] =
{
    ROP_0,              /* GXclear */
    ROP_DSa,            /* GXand */
    ROP_SDna,           /* GXandReverse */
    ROP_S,              /* GXcopy */
    ROP_DSna,           /* GXandInverted */
    ROP_D,              /* GXnoop */
    ROP_DSx,            /* GXxor */
    ROP_DSo,            /* GXor */
    ROP_DSon,           /* GXnor */
    ROP_DSxn,           /* GXequiv */
    ROP_Dn,             /* GXinvert*/
    ROP_SDno,           /* GXorReverse */
    ROP_Sn,             /* GXcopyInverted */
    ROP_DSno,           /* GXorInverted */
    ROP_DSan,           /* GXnand */
    ROP_1               /* GXset */
};

static int PatternROP[16]=
{
    ROP_0,
    ROP_DPa,
    ROP_PDna,
    ROP_P,
    ROP_DPna,
    ROP_D,
    ROP_DPx,
    ROP_DPo,
    ROP_DPon,
    ROP_PDxn,
    ROP_Dn,
    ROP_PDno,
    ROP_Pn,
    ROP_DPno,
    ROP_DPan,
    ROP_1
};

static int
GetCopyROP(int i)
{
    return CopyROP[i];
}

static int
GetPatternROP(int i)
{
    return PatternROP[i];
}

static void
XP4WaitMarker(ScreenPtr pScreen, int Marker)
{
    /* Don't need a wait marker as we need to sync on all operations */
}

static void
XP4Done(PixmapPtr p)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(p->drawable.pScreen);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    int count = 0, timeout = 0;
    int busy;

    for (;;) {
        BLTBUSY(busy);
        if (busy != GE_BUSY) {
            return;
        }

        count++;
        if (count == 10000000) {
            ErrorF("XP: BitBLT engine time-out.\n");
            count = 9990000;
            timeout++;
            if (timeout == 4) {
                /* Reset BitBLT Engine */
                TGUI_STATUS(0x00);
                return;
            }
        }
    }
}

static Bool
XP4PrepareSolid(PixmapPtr pPixmap,
                int alu, Pixel planemask, Pixel fg)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pPixmap->drawable.pScreen);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    unsigned int dorg = exaGetPixmapOffset(pPixmap);
    unsigned int dptch = exaGetPixmapPitch(pPixmap);

    if (planemask != -1)
        return FALSE;

    ropcode = alu;

    MMIO_OUT32(pTrident->IOBase, 0x2150, (dptch << 18) | (dorg >> 4));

    REPLICATE(fg);
    MMIO_OUT32(pTrident->IOBase, 0x2158, fg);
    MMIO_OUT32(pTrident->IOBase, 0x2128, 1 << 14);

    return TRUE;
}

static void
XP4Solid(PixmapPtr pPixmap,
            int x1, int y1,
            int x2, int y2)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pPixmap->drawable.pScreen);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    int bpp;

    switch (pPixmap->drawable.bitsPerPixel) {
    case 8:
        bpp = 0x40;
        break;
    case 16:
        bpp = 0x41;
        break;
    case 32:
        bpp = 0x42;
        break;
    }

    MMIO_OUT32(pTrident->IOBase, 0x2138, (x1 << 16) | y1);
    MMIO_OUT32(pTrident->IOBase, 0x2140, ((x2 - x1) << 16) |
                                            (y2 - y1));
    MMIO_OUT32(pTrident->IOBase, 0x2124,
                                    (GetPatternROP(ropcode) << 24) |
                                    (bpp << 8) |
                                    2);
}

static Bool
XP4PrepareCopy(PixmapPtr pSrcPixmap, PixmapPtr pDstPixmap,
                int dx, int dy,
                int alu, Pixel planemask)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pDstPixmap->drawable.pScreen);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    unsigned int sorg = exaGetPixmapOffset(pSrcPixmap);
    unsigned int dorg = exaGetPixmapOffset(pDstPixmap);
    unsigned int sptch = exaGetPixmapPitch(pSrcPixmap);
    unsigned int dptch = exaGetPixmapPitch(pDstPixmap);

    if (planemask != -1)
        return FALSE;

    pTrident->BltScanDirection = 0;
    if (dx < 0)
        pTrident->BltScanDirection |= XNEG;
    if (dy < 0)
        pTrident->BltScanDirection |= YNEG;

    ropcode = alu;

    MMIO_OUT32(pTrident->IOBase, 0x2154, (sptch << 18) | (sorg >> 4));
    MMIO_OUT32(pTrident->IOBase, 0x2150, (dptch << 18) | (dorg >> 4));

    return TRUE;
}

static void
XP4Copy(PixmapPtr pDstPixmap,
        int x1, int y1,
        int x2, int y2,
        int w, int h)
{
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pDstPixmap->drawable.pScreen);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);
    int bpp;

    switch (pDstPixmap->drawable.bitsPerPixel) {
    case 8:
        bpp = 0x40;
        break;
    case 16:
        bpp = 0x41;
        break;
    case 32:
        bpp = 0x42;
        break;
    }

    if (pTrident->BltScanDirection & YNEG) {
        y1 = y1 + h - 1;
        y2 = y2 + h - 1;
    }
    if (pTrident->BltScanDirection & XNEG) {
        x1 = x1 + w - 1;
        x2 = x2 + w - 1;
    }
    MMIO_OUT32(pTrident->IOBase, 0x2128, pTrident->BltScanDirection |
                                            SCR2SCR);
    MMIO_OUT32(pTrident->IOBase, 0x2138, (x2 << 16) | y2);
    MMIO_OUT32(pTrident->IOBase, 0x213C, (x1 << 16) | y1);
    MMIO_OUT32(pTrident->IOBase, 0x2140, (w << 16) | h);
    MMIO_OUT32(pTrident->IOBase, 0x2124, (GetCopyROP(ropcode) << 24) |
                                            (bpp << 8) |
                                            1);
}

Bool
XP4ExaInit(ScreenPtr pScreen)
{
    ExaDriverPtr pExa;
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
    TRIDENTPtr pTrident = TRIDENTPTR(pScrn);

    if (pTrident->NoAccel)
        return FALSE;

    if (!(pExa = pTrident->EXADriverPtr = exaDriverAlloc())) {
        pTrident->NoAccel = TRUE;
        return FALSE;
    }

    pExa->exa_major = 2;
    pExa->exa_minor = 0;

    pExa->flags = EXA_OFFSCREEN_PIXMAPS;
    pExa->memoryBase = pTrident->FbBase;
    pExa->memorySize = pTrident->FbMapSize;
    pExa->offScreenBase = pScrn->displayWidth * pScrn->virtualY *
                            ((pScrn->bitsPerPixel + 7) / 8);

    pExa->pixmapOffsetAlign = 16;
    pExa->pixmapPitchAlign = 16;

    pExa->maxX = 4095;
    pExa->maxY = 4095;

    pExa->WaitMarker = XP4WaitMarker;

    pExa->PrepareSolid = XP4PrepareSolid;
    pExa->Solid = XP4Solid;
    pExa->DoneSolid = XP4Done;

    pExa->PrepareCopy = XP4PrepareCopy;
    pExa->Copy = XP4Copy;
    pExa->DoneCopy = XP4Done;

    return (exaDriverInit(pScreen, pExa));
}
