/*
	Haiku ATI video driver adapted from the X.org ATI driver.

	Copyright 1999, 2000 ATI Technologies Inc., Markham, Ontario,
						 Precision Insight, Inc., Cedar Park, Texas, and
						 VA Linux Systems Inc., Fremont, California.

	Copyright 2009 Haiku, Inc.  All rights reserved.
	Distributed under the terms of the MIT license.

	Authors:
	Gerald Zajac 2009
*/


#include "accelerant.h"
#include "rage128.h"




void
Rage128_EngineFlush()
{
	// Flush all dirty data in the Pixel Cache to memory.

	OUTREGM(R128_PC_NGUI_CTLSTAT, R128_PC_FLUSH_ALL, R128_PC_FLUSH_ALL);

	for (int i = 0; i < R128_TIMEOUT; i++) {
		if ( ! (INREG(R128_PC_NGUI_CTLSTAT) & R128_PC_BUSY))
			break;
	}
}


void
Rage128_EngineReset()
{
	// Reset graphics card to known state.

	Rage128_EngineFlush();

	uint32 clockCntlIndex = INREG(R128_CLOCK_CNTL_INDEX);
	uint32 mclkCntl = GetPLLReg(R128_MCLK_CNTL);

	SetPLLReg(R128_MCLK_CNTL, mclkCntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP);

	uint32 genResetCntl = INREG(R128_GEN_RESET_CNTL);

	OUTREG(R128_GEN_RESET_CNTL, genResetCntl | R128_SOFT_RESET_GUI);
	INREG(R128_GEN_RESET_CNTL);
	OUTREG(R128_GEN_RESET_CNTL, genResetCntl & ~R128_SOFT_RESET_GUI);
	INREG(R128_GEN_RESET_CNTL);

	SetPLLReg(R128_MCLK_CNTL, mclkCntl);
	OUTREG(R128_CLOCK_CNTL_INDEX, clockCntlIndex);
	OUTREG(R128_GEN_RESET_CNTL, genResetCntl);
}


void
Rage128_EngineInit(const DisplayModeEx& mode)
{
	// Initialize the acceleration hardware.

	SharedInfo& si = *gInfo.sharedInfo;

	TRACE("Rage128_EngineInit()  bits/pixel: %d\n", mode.bitsPerPixel);

	OUTREG(R128_SCALE_3D_CNTL, 0);
	Rage128_EngineReset();

	uint32 dataType = 0;

	switch (mode.bitsPerPixel) {
	case 8:
		dataType = 2;
		break;
	case 15:
		dataType = 3;
		break;
	case 16:
		dataType = 4;
		break;
	case 32:
		dataType = 6;
		break;
	default:
		TRACE("Unsupported color depth: %d bits/pixel\n", mode.bitsPerPixel);
	}

	gInfo.WaitForFifo(2);
	OUTREG(R128_DEFAULT_OFFSET, gInfo.sharedInfo->frameBufferOffset);
	OUTREG(R128_DEFAULT_PITCH, mode.timing.h_display / 8);

	gInfo.WaitForFifo(4);
	OUTREG(R128_AUX_SC_CNTL, 0);
	OUTREG(R128_DEFAULT_SC_BOTTOM_RIGHT, (R128_DEFAULT_SC_RIGHT_MAX
										| R128_DEFAULT_SC_BOTTOM_MAX));
	OUTREG(R128_SC_TOP_LEFT, 0);
	OUTREG(R128_SC_BOTTOM_RIGHT, (R128_DEFAULT_SC_RIGHT_MAX
								| R128_DEFAULT_SC_BOTTOM_MAX));

	si.r128_dpGuiMasterCntl = ((dataType << R128_GMC_DST_DATATYPE_SHIFT)
								| R128_GMC_CLR_CMP_CNTL_DIS
								| R128_GMC_AUX_CLIP_DIS);
	gInfo.WaitForFifo(1);
	OUTREG(R128_DP_GUI_MASTER_CNTL, (si.r128_dpGuiMasterCntl
									| R128_GMC_BRUSH_SOLID_COLOR
									| R128_GMC_SRC_DATATYPE_COLOR));

	gInfo.WaitForFifo(8);
	OUTREG(R128_DST_BRES_ERR, 0);
	OUTREG(R128_DST_BRES_INC, 0);
	OUTREG(R128_DST_BRES_DEC, 0);
	OUTREG(R128_DP_BRUSH_FRGD_CLR, 0xffffffff);
	OUTREG(R128_DP_BRUSH_BKGD_CLR, 0x00000000);
	OUTREG(R128_DP_SRC_FRGD_CLR, 0xffffffff);
	OUTREG(R128_DP_SRC_BKGD_CLR, 0x00000000);
	OUTREG(R128_DP_WRITE_MASK, 0xffffffff);

	gInfo.WaitForFifo(1);
	OUTREGM(R128_DP_DATATYPE, 0, R128_HOST_BIG_ENDIAN_EN);

	gInfo.WaitForIdle();
}


void
Rage128_FillRectangle(engine_token *et, uint32 color, fill_rect_params *pList, uint32 count)
{
	(void)et;		// avoid compiler warning for unused arg

	gInfo.WaitForFifo(3);
	OUTREG(R128_DP_GUI_MASTER_CNTL, (gInfo.sharedInfo->r128_dpGuiMasterCntl
									| R128_GMC_BRUSH_SOLID_COLOR
									| R128_GMC_SRC_DATATYPE_COLOR
									| R128_ROP3_P));	// use GXcopy for rop

	OUTREG(R128_DP_BRUSH_FRGD_CLR, color);
	OUTREG(R128_DP_CNTL, R128_DST_X_LEFT_TO_RIGHT | R128_DST_Y_TOP_TO_BOTTOM);

	while (count--) {
		int x = pList->left;
		int y = pList->top;
		int w = pList->right - x + 1;
		int h = pList->bottom - y + 1;

		gInfo.WaitForFifo(2);
		OUTREG(R128_DST_Y_X, (y << 16) | x);
		OUTREG(R128_DST_WIDTH_HEIGHT, (w << 16) | h);

		pList++;
	}
}


void
Rage128_FillSpan(engine_token *et, uint32 color, uint16 *pList, uint32 count)
{
	(void)et;		// avoid compiler warning for unused arg

	gInfo.WaitForFifo(2);
	OUTREG(R128_DP_GUI_MASTER_CNTL, (gInfo.sharedInfo->r128_dpGuiMasterCntl
									| R128_GMC_BRUSH_SOLID_COLOR
									| R128_GMC_SRC_DATATYPE_COLOR
									| R128_ROP3_P));	// use GXcopy for rop

	OUTREG(R128_DP_BRUSH_FRGD_CLR, color);

	while (count--) {
		int y = *pList++;
		int x = *pList++;
		int w = *pList++ - x + 1;

		if (w <= 0)
			continue;	// discard span with zero or negative width

		gInfo.WaitForFifo(2);
		OUTREG(R128_DST_Y_X, (y << 16) | x);
		OUTREG(R128_DST_WIDTH_HEIGHT, (w << 16) | 1);
	}
}


void
Rage128_InvertRectangle(engine_token *et, fill_rect_params *pList, uint32 count)
{
	(void)et;		// avoid compiler warning for unused arg

	gInfo.WaitForFifo(2);
	OUTREG(R128_DP_GUI_MASTER_CNTL, (gInfo.sharedInfo->r128_dpGuiMasterCntl
									| R128_GMC_BRUSH_NONE
									| R128_GMC_SRC_DATATYPE_COLOR
									| R128_DP_SRC_SOURCE_MEMORY
									| R128_ROP3_Dn));	// use GXinvert for rop

	OUTREG(R128_DP_CNTL, R128_DST_X_LEFT_TO_RIGHT | R128_DST_Y_TOP_TO_BOTTOM);

	while (count--) {
		int x = pList->left;
		int y = pList->top;
		int w = pList->right - x + 1;
		int h = pList->bottom - y + 1;

		gInfo.WaitForFifo(2);
		OUTREG(R128_DST_Y_X, (y << 16) | x);
		OUTREG(R128_DST_WIDTH_HEIGHT, (w << 16) | h);

		pList++;
	}
}


void
Rage128_ScreenToScreenBlit(engine_token *et, blit_params *pList, uint32 count)
{
	(void)et;		// avoid compiler warning for unused arg

	gInfo.WaitForFifo(1);
	OUTREG(R128_DP_GUI_MASTER_CNTL, (gInfo.sharedInfo->r128_dpGuiMasterCntl
									| R128_GMC_BRUSH_NONE
									| R128_GMC_SRC_DATATYPE_COLOR
									| R128_DP_SRC_SOURCE_MEMORY
									| R128_ROP3_S));	// use GXcopy for rop

	while (count--) {
		int cmd = 0;
		int src_x = pList->src_left;
		int src_y = pList->src_top;
		int dest_x = pList->dest_left;
		int dest_y = pList->dest_top;
		int width = pList->width;
		int height = pList->height;

		if (dest_x <= src_x) {
			cmd |= R128_DST_X_LEFT_TO_RIGHT;
		} else {
			src_x += width;
			dest_x += width;
		}

		if (dest_y <= src_y) {
			cmd |= R128_DST_Y_TOP_TO_BOTTOM;
		} else {
			src_y += height;
			dest_y += height;
		}

		gInfo.WaitForFifo(4);
		OUTREG(R128_DP_CNTL, cmd);
		OUTREG(R128_SRC_Y_X, (src_y << 16) | src_x);
		OUTREG(R128_DST_Y_X, (dest_y << 16) | dest_x);
		OUTREG(R128_DST_HEIGHT_WIDTH, ((height + 1) << 16) | (width + 1));

		pList ++;
	}
}