/******************** (C) COPYRIGHT 2013 SONiX *******************************
* COMPANY:			SONiX
* DATE:					2013/04
* AUTHOR:				SA1
* IC:						SN32F100
* DESCRIPTION:	I2S related functions.
*____________________________________________________________________________
*	REVISION	Date				User		Description
*	1.0				2013/04/08	SA1			First release
*
*____________________________________________________________________________
* THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS TIME TO MARKET.
* SONiX SHALL NOT BE HELD LIABLE FOR ANY DIRECT, INDIRECT OR CONSEQUENTIAL 
* DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT OF SUCH SOFTWARE
* AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION CONTAINED HEREIN 
* IN CONNECTION WITH THEIR PRODUCTS.
*****************************************************************************/

/*_____ I N C L U D E S ____________________________________________________*/
#include <SN32F100.h>
#include "I2S.h"


/*_____ D E C L A R A T I O N S ____________________________________________*/
uint8_t Tx_Pointer;
uint8_t Rx_Pointer;
uint32_t I2S_FIFO[I2S_LOCAL_BUF_NUM];
uint32_t gBufFullFlag = 0;
uint32_t gBufEmptyFlag = 0;
uint16_t ADC_data_delay = 0;

uint8_t Delay_Flag;

/*_____ D E F I N I T I O N S ______________________________________________*/


/*_____ M A C R O S ________________________________________________________*/


/*_____ F U N C T I O N S __________________________________________________*/
uint32_t MCLKDIV_Calculator()
{
	return ((MCLK_SOURCE_CLOCK_HZ)/(AUDIO_SAMPLE_RATE_HZ)) >> 9;
}

void I2S_init(void)
{	
	uint32_t i;
	
	for(i=0; i<I2S_LOCAL_BUF_NUM; i++) // initialize
		I2S_FIFO[i] = 0x0;
	
	// system setting for I2S
	SN_SYS1->APBCP1_b.I2SPRE = 0;      // I2S_PCLK = HCLK/1
	SN_SYS1->PRST_b.I2SRST = 1;        // clear I2S register configuration
	SN_SYS1->AHBCLKEN_b.I2SCLKEN = 1;  // clock enable
	SN_I2S->CTRL_b.I2SEN = 1; // switch clock pins from GPIO mode to I2S mode
		
	// I2S register setting
	SN_I2S->CTRL_b.I2SMOD = 1; // codec mode
	SN_I2S->CTRL_b.CHLENGTH = I2S_CHANNEL_LENGTH - 1;
  
	SN_I2S->CTRL_b.TXFIFOTH = I2S_TXFIFO_THRESHOLD;
	SN_I2S->CTRL_b.RXFIFOTH = I2S_RXFIFO_THRESHOLD;
	SN_I2S->CTRL_b.DL = (I2S_DATA_LENGTH/8) - 1;
	SN_I2S->CTRL_b.CLRTXFIFO = 1;
	SN_I2S->CTRL_b.CLRRXFIFO = 1;
	SN_I2S->CTRL_b.FORMAT = I2S_STANDARD_FORMAT;
	SN_I2S->CTRL_b.MS = I2S_MASTER_MODE;
	SN_I2S->CTRL_b.TXEN = 1;
	SN_I2S->CTRL_b.RXEN = 1;
	SN_I2S->CTRL_b.MONO = I2S_MONO;
	SN_I2S->CTRL_b.MUTE = 0;
	
	// Enable AUEHS 
	SN_SYS0->ANBCTRL_b.AUEHSFREQ = 1;
	SN_SYS0->ANBCTRL_b.AUEHSEN = 1;
	while ((SN_SYS0->CSST & 0x100) != 0x100);
	
	SN_I2S->CLK_b.MCLKSEL = I2S_CLK_AUEHS;
	SN_I2S->CLK_b.MCLKDIV = MCLKDIV_Calculator();   // MCLK is divided by 2*n
	
	// I2S interrupt enable
	SN_I2S->IE_b.RXFIFOTHIEN = 1;
	SN_I2S->IE_b.TXFIFOTHIEN = 0;
	SN_I2S->IE_b.TXFIFOOVFIEN = 0;
	SN_I2S->IE_b.RXFIFOUDFIEN = 0;
	SN_I2S->IC = 0xFF;           
	
	NVIC_ClearPendingIRQ(I2S_IRQn);
	NVIC_EnableIRQ(I2S_IRQn);	
	
	SN_I2S->CTRL_b.START=1;      // I2S start
}

void I2S_SendData(uint32_t *data)
{
	uint16_t i, TxToBeSend;
	
	if((Rx_Pointer - Tx_Pointer) >= 0)
		TxToBeSend = Rx_Pointer - Tx_Pointer;
	else
		TxToBeSend = Tx_Pointer - Rx_Pointer;

	if(TxToBeSend >= I2S_TX_SEND_MAX)  // 8 words at most at one transmission
	{	
		TxToBeSend = I2S_TX_SEND_MAX;
	}
	
	if(TxToBeSend == 0) // interrupt many times when marked this statement
	{				
		for(i = 0; i< I2S_TXFIFO_THRESHOLD+1; i++)
		{
			if(SN_I2S->STATUS_b.TXFIFOFULL != 1)  // TxFIFO not full 
			{	
				SN_I2S->TXFIFO = 0x00;
			}
		}	
		gBufEmptyFlag++;
	}
	
	for(i = 0; i< TxToBeSend; i++)
	{ 
		if(SN_I2S->STATUS_b.TXFIFOFULL != 1)  // TxFIFO not full 
		{	
			SN_I2S->TXFIFO = data[Tx_Pointer];
			Tx_Pointer++;
			
			if(Tx_Pointer == I2S_LOCAL_BUF_NUM)
				Tx_Pointer = 0;
		}
		else
		{	
			break;
		}	
	}	

}


void I2S_ReceiveData(uint32_t *data)
{
	while(SN_I2S->STATUS_b.RXFIFOEMPTY != 1) // Rx not empty
	{	
			data[Rx_Pointer] = SN_I2S->RXFIFO;
	#if 1
		if(ADC_data_delay < ADC_OUTPUT_DELAY)
				data[Rx_Pointer] = 0;
		//else 
		//	  data[Rx_Pointer] = 0xFFDCFFD9;
	#endif 		

			Rx_Pointer++;
			
			if(Rx_Pointer == I2S_LOCAL_BUF_NUM)
			{	
				Rx_Pointer = 0;
				if(ADC_data_delay < ADC_OUTPUT_DELAY)
				{	
					ADC_data_delay++;
				}
			}

			if(Delay_Flag == 0)
			{
				Delay_Flag = 1;
				SN_I2S->IE_b.TXFIFOTHIEN = 1;
			}	
	}	
}

__irq void I2S_IRQHandler(void)
{	
	
	if(SN_I2S->RIS_b.RXFIFOTHIF == 1) // Rx threshold, allow to receive data
	{
		I2S_ReceiveData(&I2S_FIFO[0]);
	
		SN_I2S->IC_b.RXFIFOTHIC = 1;
		SN_I2S->IC_b.RXFIFOUDIC = 1;         // clear underflow flag 
		SN_GPIO2->DATA_b.DATA0 = 0;
	}
	
	else if(SN_I2S->RIS_b.TXFIFOTHIF == 1) // Tx threshold, allow to send data
	{				
	  I2S_SendData(&I2S_FIFO[0]);		

		SN_I2S->IC_b.TXFIFOTHIC = 1;	
		SN_I2S->IC_b.TXFIFOOVIC = 1;         // clear overflow flag
	}
	
	else if(SN_I2S->RIS_b.RXFIFOUDIF == 1)  // underflow: read when there is no more data in FIFO
	{
		SN_I2S->IC_b.RXFIFOUDIC = 1;
		while(1);
	}		
	else if(SN_I2S->RIS_b.TXFIFOOVIF == 1) // overflow: send when the FIFO is full
	{
		SN_I2S->IC_b.TXFIFOOVIC = 1;
		while(1);
	}			
}
