/**************************************************************************//**
 * @file     DrvSPU.c
 * @version  V3.00
 * @brief    N9H20 series SPU driver source file
 *
 * SPDX-License-Identifier: Apache-2.0
 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
 *****************************************************************************/
 
#include "string.h"
#include "stdlib.h"
#include "wblib.h"
#include "N9H20_SPU.h"

volatile S_CHANNEL_CTRL g_sChannelSettings;

PFN_DRVSPU_CB_FUNC	*g_pfnUserEventCallBack[32];
PFN_DRVSPU_CB_FUNC	*g_pfnSilentEventCallBack[32];
PFN_DRVSPU_CB_FUNC	*g_pfnLoopStartEventCallBack[32];
PFN_DRVSPU_CB_FUNC	*g_pfnEndEventCallBack[32];
PFN_DRVSPU_CB_FUNC	*g_pfnEndAddressEventCallBack[32];
PFN_DRVSPU_CB_FUNC	*g_pfnThresholdAddressEventCallBack[32];

/*---------------------------------------------------------------------------------------------------------*/
/* Function: DrvSPU_IntHandler                                                                             */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*      None																		                       */
/*                                                                                                         */
/* Returns:                                                                                                */
/*      None 						                                                                       */
/*                                                                                                         */
/* Description:                                                                                            */
/*      SPU Interrupt Service Routine                                                                      */
/*                                                                                                         */
/*---------------------------------------------------------------------------------------------------------*/

void DrvSPU_IntHandler(void)
{
	UINT8 ii;
	UINT32 u32Channel, u32InterruptFlag;	
	
	u32Channel = 1;
	
	for (ii=0; ii<32; ii++)
	{
		if (inp32(REG_SPU_CH_IRQ) & u32Channel)
		{
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);
			
			// load previous channel settings		
			outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (ii << 24));		
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);
				
			u32InterruptFlag = inp32(REG_SPU_CH_EVENT);
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT));				

			if (u32InterruptFlag & DRVSPU_USER_INT)
			{
				if ( inp32(REG_SPU_CH_EVENT) & EV_USR_EN)
					g_pfnUserEventCallBack[ii]((UINT8*)REG_SPU_CTRL);
			}				
	
			if (u32InterruptFlag & DRVSPU_SILENT_INT)
			{
				if ( inp32(REG_SPU_CH_EVENT) & EV_SLN_EN)
					g_pfnSilentEventCallBack[ii]((UINT8*)REG_SPU_CTRL);
			}				

			if (u32InterruptFlag & DRVSPU_LOOPSTART_INT)
			{
				if ( inp32(REG_SPU_CH_EVENT) & EV_LP_EN)
					g_pfnLoopStartEventCallBack[ii]((UINT8*)REG_SPU_CTRL);
			}				

			if (u32InterruptFlag & DRVSPU_END_INT)
			{
				if ( inp32(REG_SPU_CH_EVENT) & EV_END_EN)
					g_pfnEndEventCallBack[ii]((UINT8*)REG_SPU_CTRL);
			}				

			if (u32InterruptFlag & DRVSPU_ENDADDRESS_INT)				
			{
				if ( inp32(REG_SPU_CH_EVENT) & END_EN)
					g_pfnEndAddressEventCallBack[ii]((UINT8*)((UINT32)_pucPlayAudioBuff + HALF_FRAG_SIZE));
			}				
	
			if (u32InterruptFlag & DRVSPU_THADDRESS_INT)				
			{
				if ( inp32(REG_SPU_CH_EVENT) & TH_EN)
					g_pfnThresholdAddressEventCallBack[ii]((UINT8*)((UINT32)_pucPlayAudioBuff));
			}				

			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_IRQ_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		}
	
		u32Channel <<= 1; 
	}

	outp32(REG_SPU_CH_IRQ, inp32(REG_SPU_CH_IRQ));
}	

ERRCODE 
DrvSPU_DisableInt(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32InterruptFlag
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// set new channel settings for previous channel settings						
		if (u32InterruptFlag & DRVSPU_USER_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) & ~EV_USR_EN);		
		}
		if (u32InterruptFlag & DRVSPU_SILENT_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) & ~EV_SLN_EN);				
		}
		if (u32InterruptFlag & DRVSPU_LOOPSTART_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) & ~EV_LP_EN);						
		}
		if (u32InterruptFlag & DRVSPU_END_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) & ~EV_END_EN);						
		}
		if (u32InterruptFlag & DRVSPU_ENDADDRESS_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) & ~END_EN);						
		}
		if (u32InterruptFlag & DRVSPU_THADDRESS_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) & ~TH_EN);
		}
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_IRQ_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
	
		return 0;
	}
	else
		return -1;	   
}

ERRCODE 
DrvSPU_ClearInt(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32InterruptFlag
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// set new channel settings for previous channel settings						
		if (u32InterruptFlag & DRVSPU_USER_INT)
		{
			outp32(REG_SPU_CH_EVENT, (inp32(REG_SPU_CH_EVENT) & ~0x3F00) | EV_USR_FG);		
		}
		if (u32InterruptFlag & DRVSPU_SILENT_INT)
		{
			outp32(REG_SPU_CH_EVENT, (inp32(REG_SPU_CH_EVENT) & ~0x3F00) | EV_SLN_FG);				
		}
		if (u32InterruptFlag & DRVSPU_LOOPSTART_INT)
		{
			outp32(REG_SPU_CH_EVENT, (inp32(REG_SPU_CH_EVENT) & ~0x3F00) | EV_LP_FG);						
		}
		if (u32InterruptFlag & DRVSPU_END_INT)
		{
			outp32(REG_SPU_CH_EVENT, (inp32(REG_SPU_CH_EVENT) & ~0x3F00) | EV_END_FG);						
		}
		if (u32InterruptFlag & DRVSPU_ENDADDRESS_INT)
		{
			outp32(REG_SPU_CH_EVENT, (inp32(REG_SPU_CH_EVENT) & ~0x3F00) | END_FG);						
		}
		if (u32InterruptFlag & DRVSPU_THADDRESS_INT)
		{
			outp32(REG_SPU_CH_EVENT, (inp32(REG_SPU_CH_EVENT) & ~0x3F00) | TH_FG);														
		}
		
		outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) & ~AT_CLR_EN);			// clear Auto Clear Enable bit
		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_IRQ_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	else
		return -1;	   
}

ERRCODE 
DrvSPU_PollInt(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32InterruptFlag
)
{

	BOOL bStatus;
	
	bStatus = TRUE;
	
	if ( (u32Channel >=eDRVSPU_CHANNEL_0) && (u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// set new channel settings for previous channel settings						
		if (u32InterruptFlag == DRVSPU_USER_INT)
		{
			bStatus = (inp32(REG_SPU_CH_EVENT) & EV_USR_FG) ? TRUE : FALSE;		
		}
		else if (u32InterruptFlag == DRVSPU_SILENT_INT)
		{
			bStatus = (inp32(REG_SPU_CH_EVENT) & EV_SLN_FG) ? TRUE : FALSE;				
		}
		else if (u32InterruptFlag == DRVSPU_LOOPSTART_INT)
		{
			bStatus = (inp32(REG_SPU_CH_EVENT) & EV_LP_FG) ? TRUE : FALSE;						
		}
		else if (u32InterruptFlag == DRVSPU_END_INT)
		{
			bStatus = (inp32(REG_SPU_CH_EVENT) & EV_END_FG) ? TRUE : FALSE;						
		}
		else if (u32InterruptFlag == DRVSPU_ENDADDRESS_INT)
		{
			bStatus = (inp32(REG_SPU_CH_EVENT) & END_FG) ? TRUE : FALSE;						
		}
		else if (u32InterruptFlag == DRVSPU_THADDRESS_INT)
		{
			bStatus = (inp32(REG_SPU_CH_EVENT) & TH_FG) ? TRUE : FALSE;						
		}
		else 
			return E_DRVSPU_WRONG_INTERRUPT;	   		
		
		return bStatus;
	}
	 
	else
		return FALSE;
}

ERRCODE
DrvSPU_Open(void)
{
	UINT8 ii;

	// enable SPU engine clock 
	outp32(REG_AHBCLK, inp32(REG_AHBCLK) | ADO_CKE | SPU_CKE | HCLK4_CKE);			// enable SPU engine clock 

	// disable SPU engine 
//	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) & ~SPU_EN);
	outp32(REG_SPU_CTRL, 0x00);
	
	// given FIFO size = 4
	outp32(REG_SPU_CTRL, 0x04000000);		

	// reset SPU engine 
//	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) | SPU_EN);
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) & ~SPU_SWRST);	
	
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) | SPU_SWRST);
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) & ~SPU_SWRST);	
	
	// disable all channels
	outp32(REG_SPU_CH_EN, 0x00);		
	
	for (ii=0; ii<32; ii++)
	{
		DrvSPU_ClearInt(ii, DRVSPU_ALL_INT);
		DrvSPU_DisableInt(ii, DRVSPU_ALL_INT);
	}
	
	return 0;
}

void DrvSPU_Close(void)
{
	// reset SPU engine 
//	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) | SPU_EN);
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) & ~SPU_SWRST);	
	
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) | SPU_SWRST);
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) & ~SPU_SWRST);	

	// disable SPU engine 
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) & ~SPU_EN);
	
	// disable SPU engine clock 
	outp32(REG_AHBCLK, inp32(REG_AHBCLK) & ~ADO_CKE & ~SPU_CKE);		// disable SPU engine clock 
}

ERRCODE
DrvSPU_ChannelOpen(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		outp32(REG_SPU_CH_EN, inp32(REG_SPU_CH_EN) | (0x0001 << u32Channel));
		return 0;
	}
	else		
		return -1;	   	
}

BOOL
DrvSPU_IsChannelEnabled(
	E_DRVSPU_CHANNEL e32Channel
)
{
	UINT32 u32Channel;
	
	u32Channel = 1;
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		u32Channel <<= e32Channel;	
		if (inp32(REG_SPU_CH_EN ) & u32Channel)
			return TRUE;
		else 
			return FALSE;
	}
	else		
		return FALSE;
}

ERRCODE 
DrvSPU_ChannelClose(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		outp32(REG_SPU_CH_EN, inp32(REG_SPU_CH_EN) & ~(0x0001 << u32Channel));
		return 0;
	}		
	else		
		return -1;	   	
}

ERRCODE 
DrvSPU_EnableInt(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32InterruptFlag, 
	PFN_DRVSPU_CB_FUNC* pfnCallBack
)
{

	if ( (u32Channel >=eDRVSPU_CHANNEL_0) && (u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// set new channel settings for previous channel settings						
		if (u32InterruptFlag & DRVSPU_USER_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) | EV_USR_EN);		
			g_pfnUserEventCallBack[u32Channel] = pfnCallBack;										
		}
		else if (u32InterruptFlag & DRVSPU_SILENT_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) | EV_SLN_EN);				
			g_pfnSilentEventCallBack[u32Channel] = pfnCallBack;													
		}
		else if (u32InterruptFlag & DRVSPU_LOOPSTART_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) | EV_LP_EN);						
			g_pfnLoopStartEventCallBack[u32Channel] = pfnCallBack;													
		}
		else if (u32InterruptFlag & DRVSPU_END_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) | EV_END_EN);						
			g_pfnEndEventCallBack[u32Channel] = pfnCallBack;													
		}

		else if (u32InterruptFlag & DRVSPU_ENDADDRESS_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) | END_EN);						
			g_pfnEndAddressEventCallBack[u32Channel] = pfnCallBack;													
		}

		else if (u32InterruptFlag & DRVSPU_THADDRESS_INT)
		{
			outp32(REG_SPU_CH_EVENT, inp32(REG_SPU_CH_EVENT) | TH_EN);														
			g_pfnThresholdAddressEventCallBack[u32Channel] = pfnCallBack;													
		}
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_IRQ_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		//DrvAIC_InstallISR(eDRVAIC_INT_LEVEL1, eDRVAIC_INT_SPU, (PVOID)DrvSPU_IntHandler, 0);			
		
		return 0;
	}
	 
	else
		return -1;	 
}

BOOL
DrvSPU_IsIntEnabled(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32InterruptFlag
)
{
	UINT32 u32Flag;

	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		switch (u32InterruptFlag)
		{
			case DRVSPU_USER_INT:
				u32Flag = inp32(REG_SPU_CH_EVENT) & EV_USR_EN;					
				break;

			case DRVSPU_SILENT_INT:
				u32Flag = inp32(REG_SPU_CH_EVENT) & EV_SLN_EN;
				break;

			case DRVSPU_LOOPSTART_INT:
				u32Flag = inp32(REG_SPU_CH_EVENT) & EV_LP_EN;
				break;

			case DRVSPU_END_INT:
				u32Flag = inp32(REG_SPU_CH_EVENT) & EV_END_EN;						
				break;

			case DRVSPU_ENDADDRESS_INT:
				u32Flag = inp32(REG_SPU_CH_EVENT) & END_EN;						
				break;

			case DRVSPU_THADDRESS_INT:
				u32Flag = inp32(REG_SPU_CH_EVENT) & TH_EN;														
				break;

			default:
				u32Flag = 0;			
				break;
		}

		if (u32Flag) return TRUE;
		else return FALSE;
	}
	 
	else
		return FALSE;
}

ERRCODE 
DrvSPU_SetPauseAddress_PCM16(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32Address
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		outp32(REG_SPU_PA_ADDR, u32Address);		

//		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_ALL_SETTINGS);				
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~0xFFFF);
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_PARTIAL_SETTINGS | DRVSPU_UPDATE_PAUSE_PARTIAL);				

		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	else
		return -1;	   
}

UINT32
DrvSPU_GetPauseAddress_PCM16(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		return (inp32(REG_SPU_PA_ADDR));
	}
	else
		return 0;
}



ERRCODE 
DrvSPU_SetBaseAddress(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32Address
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		outp32(REG_SPU_S_ADDR, u32Address);		

		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_ALL_SETTINGS);				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	else
		return -1;	   
}

UINT32
DrvSPU_GetBaseAddress(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		return (inp32(REG_SPU_S_ADDR));
	}
	else
		return 0;
}

ERRCODE 
DrvSPU_SetThresholdAddress(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32Address
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		outp32(REG_SPU_M_ADDR, u32Address);		

		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_ALL_SETTINGS);				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	 
	else
		return -1;	   
}

UINT32
DrvSPU_GetThresholdAddress(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		return inp32(REG_SPU_M_ADDR);
	}
	else
		return 0;
}

ERRCODE 
DrvSPU_SetToneAmp(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32Amp
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		outp32(REG_SPU_TONE_AMP, u32Amp);		

		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_ALL_SETTINGS);				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	 
	else
		return -1;	   
}

ERRCODE 
DrvSPU_SetTonePulse(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32Pulse
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		outp32(REG_SPU_TONE_PULSE, u32Pulse);		

		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_ALL_SETTINGS);				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	 
	else
		return -1;	   
}

ERRCODE 
DrvSPU_SetEndAddress(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32Address
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		outp32(REG_SPU_E_ADDR, u32Address);		

		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_ALL_SETTINGS);				
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	 
	else
		return -1;	   
}

UINT32
DrvSPU_GetEndAddress(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		return inp32(REG_SPU_E_ADDR);
	}
	else
		return 0;
}

UINT32
DrvSPU_GetCurrentAddress(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		return inp32(REG_SPU_CUR_ADDR);
	}
	else return 0;	   
}

UINT32
DrvSPU_GetLoopStartAddress(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT32 u32Address
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		return inp32(REG_SPU_LP_ADDR);
	}
	else return 0;	   
}

#ifdef OPT_DIRECT_SET_DFA
	ERRCODE 
	DrvSPU_SetDFA(
		E_DRVSPU_CHANNEL u32Channel, 
		UINT16 u16DFA
	)
	{
		if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
		{
			// wait to finish previous channel settings
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);
			
			// load previous channel settings		
			outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);
			
			outp32(REG_SPU_CH_PAR_2, u16DFA);		
	
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_DFA_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				
	
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
			
			return 0;
		}
		 
		else
			return -1;	   
	}

#else
	ERRCODE 
	DrvSPU_SetDFA(
		E_DRVSPU_CHANNEL u32Channel, 
		UINT16 u16SrcSampleRate,
		UINT16 u16OutputSampleRate
	)
	{
		UINT32 u32DFA;
		
	//	u32DFA = (u16SrcSampleRate/u16OutputSampleRate) * 1024;
		u32DFA = (u16SrcSampleRate*1024)/u16OutputSampleRate;
	
		if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
		{
			// wait to finish previous channel settings
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);
			
			// load previous channel settings		
			outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);
	
			outp32(REG_SPU_CH_PAR_2, u32DFA);		
	
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
			outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_DFA_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				
	
			while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
			
			return 0;
		}
		 
		else
			return -1;	   
	}
#endif	

UINT16
DrvSPU_GetDFA(
	E_DRVSPU_CHANNEL u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		return inp32(REG_SPU_CH_PAR_2) & 0x1FFF;
	}
	else
		return 0;
}

// MSB 8-bit = left channel; LSB 8-bit = right channel
ERRCODE 
DrvSPU_SetPAN(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT16 u16PAN
)
{
	UINT32 u32PAN;
	
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		u32PAN = u16PAN;
		u32PAN <<= 8;			
		u32PAN &= (PAN_L + PAN_R);
		outp32(REG_SPU_CH_PAR_1, (inp32(REG_SPU_CH_PAR_1) & (~(PAN_L+PAN_R))) | u32PAN);		

		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_PAN_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				

		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	 
	else
		return -1;	   
}

UINT16
DrvSPU_GetPAN(
	E_DRVSPU_CHANNEL u32Channel
)
{
	UINT32 u32PAN;
	
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		u32PAN = inp32(REG_SPU_CH_PAR_1);
		u32PAN >>= 8;
		return (u32PAN & 0xFFFF);
	}
	else
		return 0;
}


ERRCODE 
DrvSPU_SetSrcType(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT8 u8DataFormat
)
{

	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		outp32(REG_SPU_CH_PAR_1, (inp32(REG_SPU_CH_PAR_1) & ~SRC_TYPE) | u8DataFormat);		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_UPDATE_ALL_SETTINGS );				

		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	 
	else
		return -1;	   
}

UINT16
DrvSPU_GetSrcType(
	E_DRVSPU_CHANNEL u32Channel
)
{
	UINT8 u8DataFormat;
	
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		u8DataFormat = inp32(REG_SPU_CH_PAR_1);
		return (u8DataFormat & 0x07);
	}
	else
		return 0;
}

ERRCODE 
DrvSPU_SetChannelVolume(
	E_DRVSPU_CHANNEL u32Channel, 
	UINT8 u8Volume
)
{
	UINT32 u32PAN;
	
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		u32PAN = u8Volume;
		u32PAN <<= 24;
		outp32(REG_SPU_CH_PAR_1, (inp32(REG_SPU_CH_PAR_1) & 0x00FFFFFF) | u32PAN);		

		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) & ~DRVSPU_UPDATE_ALL_PARTIALS);		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | (DRVSPU_UPDATE_VOL_PARTIAL + DRVSPU_UPDATE_PARTIAL_SETTINGS));				

		while(inp32(REG_SPU_CH_CTRL) & CH_FN);		
		
		return 0;
	}
	 
	else
		return -1;	   
}

UINT8
DrvSPU_GetChannelVolume(
	E_DRVSPU_CHANNEL u32Channel
)
{
	UINT16 u32PAN;
	
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		u32PAN = inp32(REG_SPU_CH_PAR_1);
		u32PAN >>= 24;
		return (u32PAN & 0xFF);
	}
	else
		return 0;
}

void DrvSPU_EqOpen(
	E_DRVSPU_EQ_BAND eEqBand,
	E_DRVSPU_EQ_GAIN eEqGain		
)
{
	switch (eEqBand)
	{
		case eDRVSPU_EQBAND_DC:
			outp32(REG_SPU_EQGain1, (inp32(REG_SPU_EQGain1) & (~Gaindc)) | eEqGain <<16);
			break;
	
		case eDRVSPU_EQBAND_1:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain01)) | eEqGain);
			break;
	
		case eDRVSPU_EQBAND_2:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain02)) | eEqGain <<4);
			break;

		case eDRVSPU_EQBAND_3:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain03)) | eEqGain <<8);
			break;

		case eDRVSPU_EQBAND_4:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain04)) | eEqGain <<12);
			break;

		case eDRVSPU_EQBAND_5:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain05)) | eEqGain <<16);
			break;

		case eDRVSPU_EQBAND_6:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain06)) | eEqGain <<20);
			break;

		case eDRVSPU_EQBAND_7:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain07)) | eEqGain <<24);
			break;

		case eDRVSPU_EQBAND_8:
			outp32(REG_SPU_EQGain0, (inp32(REG_SPU_EQGain0) & (~Gain08)) | eEqGain <<28);
			break;

		case eDRVSPU_EQBAND_9:
			outp32(REG_SPU_EQGain1, (inp32(REG_SPU_EQGain1) & (~Gain09)) | eEqGain);
			break;

		default:
		case eDRVSPU_EQBAND_10:
			outp32(REG_SPU_EQGain1, (inp32(REG_SPU_EQGain1) & (~Gain10)) | eEqGain <<4);
			break;
	}
	
	outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) | EQU_EN | ZERO_EN);
}

void DrvSPU_EqClose(void)
{
	outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) & (~EQU_EN) & (~ZERO_EN));
}

void DrvSPU_SetVolume(
	UINT16 u16Volume	// MSB: left channel; LSB right channel
)	
{
	//outp32(REG_SPU_DAC_VOL, (inp32(REG_SPU_DAC_VOL) & ~(DWA_SEL | ANA_PD | LHPVL | RHPVL)) | (u16Volume & 0x3F3F));
	outp32(REG_SPU_DAC_VOL, (inp32(REG_SPU_DAC_VOL) & ~(0x3F3F)) | (u16Volume & 0x3F3F));
}

UINT16 
DrvSPU_GetVolume(void)
{
	return (inp32(REG_SPU_DAC_VOL) & 0x3F3F);
}	

void DrvSPU_StartPlay(void)
{
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) | SPU_EN);
}

void DrvSPU_StopPlay(void)
{
	outp32(REG_SPU_CTRL, inp32(REG_SPU_CTRL) & ~SPU_EN);
}

void delay_loop(int PLL)	// delay 1mS for PLL input
{
	volatile int ii, jj;
	
	PLL /= 5;
	for (ii=0; ii<PLL; ii++)
	{
//		for (jj=0; jj<1000; jj++);
		for (jj=0; jj<200; jj++);
	}
}

UINT32  
DrvSPU_SetSampleRate (
	UINT32  u32SystemClock,
	UINT32  SampleRate
)
{
	UINT32 u32ClockDivider, u32RealSampleRate,u32ClkSel=0,u32DFA;
	UINT32 u32Buf;
	int ii, jj;
	

	outp32(REG_AHBCLK, inp32(REG_AHBCLK) | ADO_CKE | SPU_CKE);			// enable SPU engine clock 

	u32ClockDivider = 	u32SystemClock / (128*SampleRate);
	
	if (u32ClockDivider > 255)
	{
	
		u32ClkSel = u32ClockDivider / 255;
		u32ClockDivider = u32ClockDivider / u32ClkSel;
	}

	u32Buf = inp32(REG_CLKDIV1);
	u32Buf &= ~ADO_S;
	u32Buf |= (0x3 << 19) | (u32ClkSel <<16);
	u32Buf &= ~ADO_N1;
	u32ClockDivider &= 0xFF;
	u32ClockDivider	--;
	u32Buf  |= u32ClockDivider<<24;	

	jj = inp32(REG_SPU_DAC_VOL) & 0xFF;
	for (ii=0; ii<jj; ii++)
	{
		outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) - 0x0101);	// set volume to zero			
		delay_loop(240);	// delay 1mS for 240MHz PLL output
	}			
	outp32(REG_CLKDIV1, u32Buf);			
	
	for (ii=0; ii<jj; ii++)
	{
		outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) + 0x0101);	// set volume to zero			
		delay_loop(240);	// delay 1mS for 240MHz PLL output		
//		sysDelay(1);		
	}			

	u32ClockDivider++;
	u32ClkSel++;
	u32RealSampleRate = u32SystemClock / (128*u32ClockDivider*u32ClkSel);
	
	u32DFA= SampleRate * 1024 / u32RealSampleRate;

	// set DFA 
	DrvSPU_SetDFA(0, u32DFA);	
	DrvSPU_SetDFA(1, u32DFA);		

//	outp32(REG_SPU_CH_EN, u32Buf2);
	return u32RealSampleRate;
}

ERRCODE
DrvSPU_UploadChannelContents (
	UINT32 u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		// wait to finish previous channel settings
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		
		// load previous channel settings		
		outp32(REG_SPU_CH_CTRL, (inp32(REG_SPU_CH_CTRL) & ~CH_NO) | (u32Channel << 24));		
		outp32(REG_SPU_CH_CTRL, inp32(REG_SPU_CH_CTRL) | DRVSPU_LOAD_SELECTED_CHANNEL);
		while(inp32(REG_SPU_CH_CTRL) & CH_FN);
		return 0;
	}
	else
		return -1;	   	
}


ERRCODE 
DrvSPU_ChannelCtrl(
	S_CHANNEL_CTRL *psChannelCtrl
)
{
	volatile UINT32 u32Channel; 
	
	u32Channel = psChannelCtrl->u32ChannelIndex;
	
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		DrvSPU_SetChannelVolume(u32Channel, psChannelCtrl->u8ChannelVolume);	
		DrvSPU_SetPAN(u32Channel, psChannelCtrl->u16PAN);	
		DrvSPU_SetSrcType(u32Channel, psChannelCtrl->u8DataFormat);
		
#ifdef OPT_DIRECT_SET_DFA		
//		DrvSPU_SetDFA(u32Channel, psChannelCtrl->u16DFA);		
#else		
//		DrvSPU_SetDFA(u32Channel, psChannelCtrl->u16SrcSampleRate, psChannelCtrl->u16OutputSampleRate);				
#endif		
		DrvSPU_SetBaseAddress(u32Channel, psChannelCtrl->u32SrcBaseAddr);		
		DrvSPU_SetThresholdAddress(u32Channel, psChannelCtrl->u32SrcThresholdAddr);
		DrvSPU_SetEndAddress(u32Channel, psChannelCtrl->u32SrcEndAddr);
		
		return 0;
	}
	else
		return -1;	   	
}

ERRCODE
DrvSPU_ChannelPause (
	UINT32 u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		outp32(REG_SPU_CH_PAUSE, inp32(REG_SPU_CH_PAUSE) | (0x0001 << u32Channel));
		return 0;
	}
	else		
		return -1;	   	
}

ERRCODE
DrvSPU_ChannelResume (
	UINT32 u32Channel
)
{
	if ( ((INT)u32Channel >=eDRVSPU_CHANNEL_0) && ((INT)u32Channel <=eDRVSPU_CHANNEL_31) )
	{
		outp32(REG_SPU_CH_PAUSE, inp32(REG_SPU_CH_PAUSE) & ~(0x0001 << u32Channel));
		return 0;
	}
	else		
		return -1;	   	
}