/* KallistiOS ##version##

   pvr_init_shutdown.c
   Copyright (C)2002,2004 Dan Potter

 */

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <dc/pvr.h>
#include <dc/video.h>
#include <dc/asic.h>
#include <dc/vblank.h>
#include "pvr_internal.h"

/*

   Initialization and shutdown: stuff you should only ever have to do
   once in your program.

*/

CVSID("$Id: pvr_init_shutdown.c,v 1.13 2003/04/24 03:12:25 bardtx Exp $");

/* Simpler function which initializes the PVR using 16/16 for the opaque
   and translucent lists, and 0's for everything else; 512k of vertex
   buffer. This is equivalent to the old ta_init_defaults() for now. */
int pvr_init_defaults() {
	pvr_init_params_t params = {
		/* Enable opaque and translucent polygons with size 16 */
		{ PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_0 },

		/* Vertex buffer size 512K */
		512*1024,

		/* No DMA */
		0,

		/* No FSAA */
		0
	};

	return pvr_init(&params);
}

/* Initialize the PVR chip to ready status, enabling the specified lists
   and using the specified parameters; note that bins and vertex buffers
   come from the texture memory pool! Expects that a 2D mode was 
   initialized already using the vid_* API. */
int pvr_init(pvr_init_params_t *params) {
	/* If we're already initialized, fail */
	if (pvr_state.valid == 1) {
		dbglog(DBG_WARNING, "pvr: pvr_init called twice!\n");
		return -1;
	}

	/* Make sure we got valid parameters */
	assert(params != NULL);

	/* Make sure that a video mode has been initialized */
	assert(vid_mode != NULL);
	assert(vid_mode->width != 0 && vid_mode->height != 0);

	/* Check for compatibility with 3D stuff */
	if ((vid_mode->width % 32) != 0) {
		dbglog(DBG_WARNING, "pvr: mode %dx%d isn't usable for 3D (width not multiples of 32)\n",
			vid_mode->width, vid_mode->height);
		return -1;
	}

	/* Clear out video memory */
	vid_empty();

	/* Reset all PVR systems (in case it's still doing something) */
	PVR_SET(PVR_RESET, PVR_RESET_ALL);
	PVR_SET(PVR_RESET, PVR_RESET_NONE);

	/* Start off with a nice empty structure */
	memset((void *)&pvr_state, 0, sizeof(pvr_state));

	// Enable DMA if the user wants that.
	pvr_state.dma_mode = params->dma_enabled;

	// Copy over FSAA setting.
	pvr_state.fsaa = params->fsaa_enabled;

	/* Everything's clear, do the initial buffer pointer setup */
	pvr_allocate_buffers(params);

	// Initialize tile matrices
	pvr_init_tile_matrices();

	// Setup all pipeline targets. Yes, this is redundant. :) I just
	// like to have it explicit.
	pvr_state.ram_target = 0;
	pvr_state.ta_target = 0;
	pvr_state.view_target = 0;
	
	pvr_state.list_reg_open = -1;

	// Sync all the hardware registers with our pipeline state.
	pvr_sync_view();
	pvr_sync_reg_buffer();

	// Clear out our stats
	pvr_state.vbl_count = 0;
	pvr_state.frame_last_time = 0;
	pvr_state.buf_start_time = 0;
	pvr_state.reg_start_time = 0;
	pvr_state.rnd_start_time = 0;
	pvr_state.frame_last_len = -1;
	pvr_state.buf_last_len = -1;
	pvr_state.reg_last_len = -1;
	pvr_state.rnd_last_len = -1;
	pvr_state.vtx_buf_used = 0;
	pvr_state.vtx_buf_used_max = 0;

	/* If we're on a VGA box, disable vertical smoothing */
	if (vid_mode->cable_type == CT_VGA) {
		dbglog(DBG_KDEBUG, "pvr: disabling vertical scaling for VGA\n");
		if (pvr_state.fsaa)
			PVR_SET(PVR_SCALER_CFG, 0x10400);
		else
			PVR_SET(PVR_SCALER_CFG, 0x400);
	} else {
		dbglog(DBG_KDEBUG, "pvr: enabling vertical scaling for non-VGA\n");
		if (pvr_state.fsaa)
			PVR_SET(PVR_SCALER_CFG, 0x10401);
		else
			PVR_SET(PVR_SCALER_CFG, 0x401);
	}

	/* Hook the PVR interrupt events on G2 */
	pvr_state.vbl_handle = vblank_handler_add(pvr_int_handler);
	asic_evt_set_handler(ASIC_EVT_PVR_OPAQUEDONE, pvr_int_handler);		asic_evt_enable(ASIC_EVT_PVR_OPAQUEDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_OPAQUEMODDONE, pvr_int_handler);	asic_evt_enable(ASIC_EVT_PVR_OPAQUEMODDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_TRANSDONE, pvr_int_handler);		asic_evt_enable(ASIC_EVT_PVR_TRANSDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_TRANSMODDONE, pvr_int_handler);	asic_evt_enable(ASIC_EVT_PVR_TRANSMODDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_PTDONE, pvr_int_handler);		asic_evt_enable(ASIC_EVT_PVR_PTDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_RENDERDONE, pvr_int_handler);		asic_evt_enable(ASIC_EVT_PVR_RENDERDONE, ASIC_IRQ_DEFAULT);

	/* 3d-specific parameters; these are all about rendering and
	   nothing to do with setting up the video; some stuff in here
	   is still unknown. */
   	PVR_SET(PVR_UNK_00A8, 0x15d1c951);		/* M (Unknown magic value) */
   	PVR_SET(PVR_UNK_00A0, 0x00000020);		/* M */
   	PVR_SET(PVR_FB_CFG_2, 0x00000009);		/* alpha config */
	PVR_SET(PVR_UNK_0110, 0x00093f39);		/* M */
	PVR_SET(PVR_UNK_0098, 0x00800408);		/* M */
	PVR_SET(PVR_TEXTURE_CLIP, 0x00000000);		/* texture clip distance */
	PVR_SET(PVR_SPANSORT_CFG, 0x00000101);		/* M */
	PVR_SET(PVR_FOG_TABLE_COLOR, 0x007f7f7f);	/* Fog table color */
	PVR_SET(PVR_FOG_VERTEX_COLOR, 0x007f7f7f);	/* Fog vertex color */
	PVR_SET(PVR_COLOR_CLAMP_MIN, 0x00000000);	/* color clamp min */
	PVR_SET(PVR_COLOR_CLAMP_MAX, 0xffffffff);	/* color clamp max */
	PVR_SET(PVR_UNK_0080, 0x00000007);		/* M */
	PVR_SET(PVR_CHEAP_SHADOW, 0x00000001);		/* cheap shadow */
	PVR_SET(PVR_UNK_007C, 0x0027df77);		/* M */
	PVR_SET(PVR_TEXTURE_MODULO, 0x00000000);	/* stride width */
	PVR_SET(PVR_FOG_DENSITY, 0x0000ff07);		/* fog density */
	PVR_SET(PVR_UNK_00C8, PVR_GET(0x00d4) << 16);	/* M */
	PVR_SET(PVR_UNK_0118, 0x00008040);		/* M */

	/* Initialize PVR DMA */
	pvr_state.dma_lock = mutex_create();
	pvr_dma_init();

	/* Setup our wait-ready semaphore */
	pvr_state.ready_sem = sem_create(0);

	/* Set us as valid and return success */
	pvr_state.valid = 1;

	/* Validate our memory pool */
	pvr_mem_reset();
/* This doesn't work right now... */
/*#ifndef NDEBUG
	dbglog(DBG_KDEBUG, "pvr: free memory is %08lx bytes\n",
		pvr_mem_available());
#endif*//* !NDEBUG */

	return 0;
}

/* Shut down the PVR chip from ready status, leaving it in 2D mode as it
   was before the init. */
int pvr_shutdown() {
	if (!pvr_state.valid)
		return -1;
		
	/* Set us invalid */
	pvr_state.valid = 0;

	/* Stop anything that might be going on */
	PVR_SET(PVR_RESET, PVR_RESET_ALL);
	PVR_SET(PVR_RESET, PVR_RESET_NONE);
		
	/* Unhook any int handlers */
	vblank_handler_remove(pvr_state.vbl_handle);
	asic_evt_set_handler(ASIC_EVT_PVR_OPAQUEDONE, NULL);		asic_evt_disable(ASIC_EVT_PVR_OPAQUEDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_OPAQUEMODDONE, NULL);		asic_evt_disable(ASIC_EVT_PVR_OPAQUEMODDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_TRANSDONE, NULL);		asic_evt_disable(ASIC_EVT_PVR_TRANSDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_TRANSMODDONE, NULL);		asic_evt_disable(ASIC_EVT_PVR_TRANSMODDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_PTDONE, NULL);		asic_evt_disable(ASIC_EVT_PVR_PTDONE, ASIC_IRQ_DEFAULT);
	asic_evt_set_handler(ASIC_EVT_PVR_RENDERDONE, NULL);		asic_evt_disable(ASIC_EVT_PVR_RENDERDONE, ASIC_IRQ_DEFAULT);

	/* Shut down PVR DMA */
	pvr_dma_shutdown();

	/* Invalidate our memory pool */
	pvr_mem_reset();

	/* Destroy the semaphore */
	sem_destroy(pvr_state.ready_sem);
	mutex_destroy(pvr_state.dma_lock);

	/* Clear video memory */
	vid_empty();
				
	/* Reset the frame buffer offset */
	vid_waitvbl();
	vid_set_start(0);

	/* Return success */
	return 0;
}


