/*
 * SBus Weitek P9100 EXA support
 */
/*-
 * Copyright (c) 2021 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Julian Coleman.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
/* $NetBSD: pnozz_exa.c,v 1.6 2023/11/05 17:06:14 jdc Exp $ */

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

#include "pnozz.h"
#include "pnozz_regs.h"


static CARD32 PnozzCopyROP[] = {
	/*GXclear*/		0,
	/*GXand*/		ROP_SRC & ROP_DST,
	/*GXandReverse*/	ROP_SRC & (~ROP_DST),
	/*GXcopy*/		ROP_SRC,
	/*GXandInverted*/	(~ROP_SRC) & ROP_DST,
	/*GXnoop*/		ROP_DST,
	/*GXxor*/		ROP_SRC ^ ROP_DST,
	/*GXor*/		ROP_SRC | ROP_DST,
	/*GXnor*/		(~ROP_SRC) & (~ROP_DST),
	/*GXequiv*/		(~ROP_SRC) ^ ROP_DST,
	/*GXinvert*/		(~ROP_DST),
	/*GXorReverse*/		ROP_SRC | (~ROP_DST),
	/*GXcopyInverted*/	(~ROP_SRC),
	/*GXorInverted*/	(~ROP_SRC) | ROP_DST,
	/*GXnand*/		(~ROP_SRC) | (~ROP_DST),
	/*GXset*/		ROP_SET
};

static CARD32 PnozzDrawROP[] = {
	/*GXclear*/		0,
	/*GXand*/		ROP_PAT & ROP_DST,
	/*GXandReverse*/	ROP_PAT & (~ROP_DST),
	/*GXcopy*/		ROP_PAT,
	/*GXandInverted*/	(~ROP_PAT) & ROP_DST,
	/*GXnoop*/		ROP_DST,
	/*GXxor*/		ROP_PAT ^ ROP_DST,
	/*GXor*/		ROP_PAT | ROP_DST,
	/*GXnor*/		(~ROP_PAT) & (~ROP_DST),
	/*GXequiv*/		(~ROP_PAT) ^ ROP_DST,
	/*GXinvert*/		(~ROP_DST),
	/*GXorReverse*/		ROP_PAT | (~ROP_DST),
	/*GXcopyInverted*/	(~ROP_PAT),
	/*GXorInverted*/	(~ROP_PAT) | ROP_DST,
	/*GXnand*/		(~ROP_PAT) | (~ROP_DST),
	/*GXset*/		ROP_PAT
};

#define waitReady(pPnozz) while((pnozz_read_4(pPnozz, ENGINE_STATUS) & \
				(ENGINE_BUSY | BLITTER_BUSY)) !=0 )

void PnozzInitEngine(PnozzPtr);
void pnozz_write_colour(PnozzPtr pPnozz, int reg, CARD32 colour);

extern CARD32 MaxClip, junk;

static void
PnozzWaitMarker(ScreenPtr pScreen, int Marker)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    PnozzPtr p = GET_PNOZZ_FROM_SCRN(pScrn);

    waitReady(p);
}

static Bool
PnozzPrepareCopy
(
    PixmapPtr pSrcPixmap,
    PixmapPtr pDstPixmap,
    int       xdir,
    int       ydir,
    int       alu,
    Pixel     planemask
)
{
    ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum];
    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
    
    waitReady(pPnozz);
    pnozz_write_4(pPnozz, RASTER_OP, (PnozzCopyROP[alu] & 0xff));
    pnozz_write_4(pPnozz, PLANE_MASK, planemask);
    pPnozz->srcoff = exaGetPixmapOffset(pSrcPixmap) / pPnozz->width;

    return TRUE;
}

static void
PnozzCopy
(
    PixmapPtr pDstPixmap,
    int       xSrc,
    int       ySrc,
    int       xDst,
    int       yDst,
    int       w,
    int       h
)
{
    ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum];
    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
    CARD32 src, dst, srcw, dstw;
    int doff = exaGetPixmapOffset(pDstPixmap) / pPnozz->width;
    
    src = (((xSrc << pPnozz->depthshift) & 0x1fff) << 16) |
	((ySrc + pPnozz->srcoff) & 0x1fff);
    dst = (((xDst << pPnozz->depthshift) & 0x1fff) << 16) |
	((yDst + doff) & 0x1fff);
    srcw = ((((xSrc + w) << pPnozz->depthshift) - 1) << 16) | 
        ((ySrc + pPnozz->srcoff + h - 1) & 0x1fff);
    dstw = ((((xDst + w) << pPnozz->depthshift) - 1) << 16) |
        ((yDst + doff + h - 1) & 0x1fff);

    waitReady(pPnozz);

    pnozz_write_4(pPnozz, ABS_XY0, src);
    pnozz_write_4(pPnozz, ABS_XY1, srcw);
    pnozz_write_4(pPnozz, ABS_XY2, dst);
    pnozz_write_4(pPnozz, ABS_XY3, dstw);
    junk = pnozz_read_4(pPnozz, COMMAND_BLIT);
    exaMarkSync(pDstPixmap->drawable.pScreen);
}

static void
PnozzDoneCopy(PixmapPtr pDstPixmap)
{
    ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum];
    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);

    waitReady(pPnozz);
}

static Bool
PnozzPrepareSolid(
    PixmapPtr pPixmap,
    int alu,
    Pixel planemask,
    Pixel fg)
{
    ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
    
    waitReady(pPnozz);
    pnozz_write_colour(pPnozz, FOREGROUND_COLOR, fg);
    pnozz_write_4(pPnozz, RASTER_OP, PnozzDrawROP[alu] & 0xff);
    pnozz_write_4(pPnozz, PLANE_MASK, planemask);
    pnozz_write_4(pPnozz, COORD_INDEX, 0);

    return TRUE;
}

static void
PnozzSolid(
    PixmapPtr pPixmap,
    int x,
    int y,
    int x2,
    int y2)
{
    ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
    int w = x2 - x - 1;
    int h = y2 - y - 1;

    waitReady(pPnozz);
    pnozz_write_4(pPnozz, RECT_RTW_XY, ((x & 0x1fff) << 16) | 
        (y & 0x1fff));
    pnozz_write_4(pPnozz, RECT_RTP_XY, (((w & 0x1fff) << 16) | 
        (h & 0x1fff)));
    junk = pnozz_read_4(pPnozz, COMMAND_QUAD);
    exaMarkSync(pPixmap->drawable.pScreen);
}

int
PnozzEXAInit(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
    ExaDriverPtr pExa;
    
    PnozzInitEngine(pPnozz);

    pExa = exaDriverAlloc();
    if (!pExa)
	return FALSE;

    pPnozz->pExa = pExa;

    pExa->exa_major = EXA_VERSION_MAJOR;
    pExa->exa_minor = EXA_VERSION_MINOR;

    pExa->memoryBase = pPnozz->fb;

    /* round to multiple of pixmap pitch */
    pExa->memorySize = (pPnozz->vidmem / pPnozz->width) * pPnozz->width;
    pExa->offScreenBase = pPnozz->width * pPnozz->height;

    /*
     * our blitter can't deal with variable pitches
     */
    pExa->pixmapOffsetAlign = pPnozz->width;
    pExa->pixmapPitchAlign = pPnozz->width;

    pExa->flags = EXA_MIXED_PIXMAPS;

    pExa->maxX = 4096;
    pExa->maxY = 4096;

    pExa->WaitMarker = PnozzWaitMarker;

    pExa->PrepareSolid = PnozzPrepareSolid;
    pExa->Solid = PnozzSolid;
    pExa->DoneSolid = PnozzDoneCopy;

    pExa->PrepareCopy = PnozzPrepareCopy;
    pExa->Copy = PnozzCopy;
    pExa->DoneCopy = PnozzDoneCopy;

    return exaDriverInit(pScreen, pExa);;
}