
#include "libi2s.h"


// TODO: move to config.h
// configuration of I2S Receiver input pin
#define I2S_SCLK_PIN	I2S_PIN_SEL_AUDIO_5
#define I2S_LRCLK_PIN   I2S_PIN_SEL_AUDIO_6
#define I2S_SDAT_0_PIN  I2S_PIN_SEL_AUDIO_4
#define I2S_SDAT_1_PIN  I2S_PIN_SEL_AUDIO_3
#define I2S_SDAT_2_PIN  I2S_PIN_SEL_AUDIO_2
#define I2S_SDAT_3_PIN  I2S_PIN_SEL_AUDIO_1
#define I2S_DSD_PIN  	I2S_PIN_SEL_AUDIO_0
                                           
//TODO: check release msg zone
#define HDMI_ZONE   0

// memory mapped registers
volatile static PHDMI_I2S_REG pHDMII2SReg = NULL; 

void I2SDBGPrint()
{

	RETAILMSG(1,(_T("  I2S_CLK_CON=[0x%x]\n\r"),pHDMII2SReg->I2S_CLK_CON));

	RETAILMSG(1,(_T("  I2S_CON_1=[0x%x]\n\r"),pHDMII2SReg->I2S_CON_1));
	RETAILMSG(1,(_T("  I2S_CON_2=[0x%x]\n\r"),pHDMII2SReg->I2S_CON_2));
	
	RETAILMSG(1,(_T("  I2S_PIN_SEL_0=[0x%x]\n\r"),pHDMII2SReg->I2S_PIN_SEL_0));
	RETAILMSG(1,(_T("  I2S_PIN_SEL_1=[0x%x]\n\r"),pHDMII2SReg->I2S_PIN_SEL_1));
	RETAILMSG(1,(_T("  I2S_PIN_SEL_2=[0x%x]\n\r"),pHDMII2SReg->I2S_PIN_SEL_2));
	RETAILMSG(1,(_T("  I2S_PIN_SEL_3=[0x%x]\n\r"),pHDMII2SReg->I2S_PIN_SEL_3));
	
	RETAILMSG(1,(_T("  I2S_DSD_CON=[0x%x]\n\r"),pHDMII2SReg->I2S_DSD_CON));
	RETAILMSG(1,(_T("  I2S_MUX_CON=[0x%x]\n\r"),pHDMII2SReg->I2S_MUX_CON));
	
	RETAILMSG(1,(_T("  I2S_CH_ST_CON=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_CON));
	
	RETAILMSG(1,(_T("  I2S_CH_ST_0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_0));
	RETAILMSG(1,(_T("  I2S_CH_ST_1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_1));
	RETAILMSG(1,(_T("  I2S_CH_ST_2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_2));
	RETAILMSG(1,(_T("  I2S_CH_ST_3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_3));
	RETAILMSG(1,(_T("  I2S_CH_ST_4=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_4));

	RETAILMSG(1,(_T("  I2S_CH_ST_SH_0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_SH_0));	
	RETAILMSG(1,(_T("  I2S_CH_ST_SH_1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_SH_1));
	RETAILMSG(1,(_T("  I2S_CH_ST_SH_2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_SH_2));
	RETAILMSG(1,(_T("  I2S_CH_ST_SH_3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_SH_3));
	RETAILMSG(1,(_T("  I2S_CH_ST_SH_4=[0x%x]\n\r"),pHDMII2SReg->I2S_CH_ST_SH_4));
	
	RETAILMSG(1,(_T("  I2S_VD_DATA=[0x%x]\n\r"),pHDMII2SReg->I2S_VD_DATA));
	RETAILMSG(1,(_T("  I2S_MUX_CH=[0x%x]\n\r"),pHDMII2SReg->I2S_MUX_CH));
	RETAILMSG(1,(_T("  I2S_MUX_CUV=[0x%x]\n\r"),pHDMII2SReg->I2S_MUX_CUV));
	
	RETAILMSG(1,(_T("  I2S_IRQ_MASK=[0x%x]\n\r"),pHDMII2SReg->I2S_IRQ_MASK));	
	RETAILMSG(1,(_T("  I2S_IRQ_STATUS=[0x%x]\n\r"),pHDMII2SReg->I2S_IRQ_STATUS));	
	
	RETAILMSG(1,(_T("  I2S_CH0_L0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_L0));
	RETAILMSG(1,(_T("  I2S_CH0_L1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_L1));
	RETAILMSG(1,(_T("  I2S_CH0_L2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_L2));
	RETAILMSG(1,(_T("  I2S_CH0_L3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_L3));
	RETAILMSG(1,(_T("  I2S_CH0_R0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_R0));
	RETAILMSG(1,(_T("  I2S_CH0_R1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_R1));
	RETAILMSG(1,(_T("  I2S_CH0_R2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_R2));
	RETAILMSG(1,(_T("  I2S_CH0_R3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH0_R3));	
	
	RETAILMSG(1,(_T("  I2S_CH1_L0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_L0));
	RETAILMSG(1,(_T("  I2S_CH1_L1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_L1));
	RETAILMSG(1,(_T("  I2S_CH1_L2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_L2));
	RETAILMSG(1,(_T("  I2S_CH1_L3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_L3));
	RETAILMSG(1,(_T("  I2S_CH1_R0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_R0));
	RETAILMSG(1,(_T("  I2S_CH1_R1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_R1));
	RETAILMSG(1,(_T("  I2S_CH1_R2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_R2));
	RETAILMSG(1,(_T("  I2S_CH1_R3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH1_R3));	

	RETAILMSG(1,(_T("  I2S_CH2_L0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_L0));
	RETAILMSG(1,(_T("  I2S_CH2_L1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_L1));
	RETAILMSG(1,(_T("  I2S_CH2_L2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_L2));
	RETAILMSG(1,(_T("  I2S_CH2_L3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_L3));
	RETAILMSG(1,(_T("  I2S_CH2_R0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_R0));
	RETAILMSG(1,(_T("  I2S_CH2_R1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_R1));
	RETAILMSG(1,(_T("  I2S_CH2_R2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_R2));
	RETAILMSG(1,(_T("  I2S_CH2_R3=[0x%x]\n\r"),pHDMII2SReg->I2S_CH2_R3));	

	RETAILMSG(1,(_T("  I2S_CH3_L0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH3_L0));
	RETAILMSG(1,(_T("  I2S_CH3_L1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH3_L1));
	RETAILMSG(1,(_T("  I2S_CH3_L2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH3_L2));
	RETAILMSG(1,(_T("  I2S_CH3_R0=[0x%x]\n\r"),pHDMII2SReg->I2S_CH3_R0));
	RETAILMSG(1,(_T("  I2S_CH3_R1=[0x%x]\n\r"),pHDMII2SReg->I2S_CH3_R1));
	RETAILMSG(1,(_T("  I2S_CH3_R2=[0x%x]\n\r"),pHDMII2SReg->I2S_CH3_R2));

}


// Initialize I2S library
BOOL I2SInit(PHDMI_I2S_REG pReg)
{
    // store memory mapped registers
    pHDMII2SReg = pReg;
    
    return TRUE;
}

// Deinitialize I2S library
BOOL I2SDeInit(VOID)
{
    // set memory mapped registers to invalid
    pHDMII2SReg = NULL;
    
    return TRUE;
}

// Set the sampling frequency in I2S Receiver.
BOOL I2SSetCUVSamplingFreq(SAMPLEING_FREG freq)
{
    BYTE reg;
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    // check CUV value according to sampling frequency
    switch (freq)
    {
        case SF_32KHZ:
            reg = I2S_CH_ST_3_SF_32KHZ;
            break;
        case SF_44KHZ:
            reg = I2S_CH_ST_3_SF_44KHZ;
            break;
        case SF_88KHZ:
            reg = I2S_CH_ST_3_SF_88KHZ;
            break;
        case SF_176KHZ:
            reg = I2S_CH_ST_3_SF_176KHZ;
            break;
        case SF_48KHZ:
            reg = I2S_CH_ST_3_SF_48KHZ;
            break;
        case SF_96KHZ:
            reg = I2S_CH_ST_3_SF_96KHZ;
            break;
        case SF_192KHZ:
            reg = I2S_CH_ST_3_SF_192KHZ;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] invalid argument \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }
    // set CUV register
    pHDMII2SReg->I2S_CH_ST_3 &= ~(I2S_CH_ST_3_SF_MASK);
    pHDMII2SReg->I2S_CH_ST_3 |= reg;
    
    return TRUE;
}

// Set the audio encoding type in I2S Receiver.
BOOL I2SSetCUVEncodingType(AUDIO_CODING encodingType)
{
    BYTE reg;
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    switch (encodingType)
    {
        case AUDIO_LPCM:
            reg = I2S_CH_ST_0_TYPE_LPCM;
            break;

        case AUDIO_NLPCM:
            reg = I2S_CH_ST_0_TYPE_NLPCM;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] invalid argument \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }

    // set CUV register
    pHDMII2SReg->I2S_CH_ST_0 &= ~(I2S_CH_ST_0_TYPE_MASK);
    pHDMII2SReg->I2S_CH_ST_0 |= reg;
    
    return TRUE;
}

// Set the word length in I2S Receiver.
BOOL I2SSetCUVWordLength(CUV_WORD_LENGTH wordLength)
{
    BYTE reg;
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    switch (wordLength)
    {
        case CUV_WL_20_NOT_DEFINED:
            reg = I2S_CH_ST_4_WL_20_NOT_DEFINED;
            break;
        case CUV_WL_20_16:
            reg = I2S_CH_ST_4_WL_20_16;
            break;
        case CUV_WL_20_18:
            reg = I2S_CH_ST_4_WL_20_18;
            break;
        case CUV_WL_20_19:
            reg = I2S_CH_ST_4_WL_20_19;
            break;
        case CUV_WL_20_20:
            reg = I2S_CH_ST_4_WL_20_20;
            break;
        case CUV_WL_20_17:
            reg = I2S_CH_ST_4_WL_20_17;
            break;
        case CUV_WL_24_NOT_DEFINED:
            reg = I2S_CH_ST_4_WL_24_NOT_DEFINED;
            break;
        case CUV_WL_24_20:
            reg = I2S_CH_ST_4_WL_24_20;
            break;
        case CUV_WL_24_22:
            reg = I2S_CH_ST_4_WL_24_22;
            break;
        case CUV_WL_24_23:
            reg = I2S_CH_ST_4_WL_24_23;
            break;
        case CUV_WL_24_24:
            reg = I2S_CH_ST_4_WL_24_24;
            break;
        case CUV_WL_24_21:
            reg = I2S_CH_ST_4_WL_24_21;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] invalid argument \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }

    // set CUV register
    pHDMII2SReg->I2S_CH_ST_4 &= ~(I2S_CH_ST_4_WL_MASK);
    pHDMII2SReg->I2S_CH_ST_4 |= reg;
    
    return TRUE;
}

// Set the bits per channel in I2S Receiver.
BOOL I2SSetBitPerChannel(I2S_BITS_PER_CHANNEL bpc)
{
    BYTE reg;
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    switch (bpc)
    {
        case I2S_BPC_16:
            reg = I2S_CON_DATA_NUM_16;
            break;
        case I2S_BPC_20:
            reg = I2S_CON_DATA_NUM_20;
            break;
        case I2S_BPC_24:
            reg = I2S_CON_DATA_NUM_24;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] invalid argument \n\r"),TEXT(__FUNCTION__)));
            return FALSE;

    }
    
    // set CUV register
    pHDMII2SReg->I2S_CON_2 &= ~(I2S_CON_DATA_NUM_MASK);
    pHDMII2SReg->I2S_CON_2 |= reg;
    
    return TRUE;    
}    

// Set the data format in I2S Receiver.
BOOL I2SSetDataFormat(I2S_DATA_FORMAT format)
{
    BYTE reg;
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    switch (format)
    {
        case I2S_BASIC:
            reg = I2S_CON_I2S_MODE_BASIC;
            break;

        case I2S_LEFT_JUSTIFIED:
            reg = I2S_CON_I2S_MODE_LEFT_JUSTIFIED;
            break;

        case I2S_RIGHT_JUSTIFIED:
            reg = I2S_CON_I2S_MODE_RIGHT_JUSTIFIED;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] invalid argument \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }
    
    // set CUV register
    pHDMII2SReg->I2S_CON_2 &= ~(I2S_CON_I2S_MODE_MASK);
    pHDMII2SReg->I2S_CON_2 |= reg;
    
    return TRUE; 
}

// Set the clock per frame in I2S Receiver.
BOOL I2SSetClockPerFrame(I2S_CLOCK_PER_FRAME clk)
{
    BYTE reg;
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    switch (clk)
    {
        case I2S_32FS:
            // clear data number field and set to 16 bit
            pHDMII2SReg->I2S_CON_2 &= ~(I2S_CON_DATA_NUM_MASK);
            reg = (I2S_CON_BIT_CH_32 | I2S_CON_DATA_NUM_16);
            break;

        case I2S_48FS:
            reg = I2S_CON_BIT_CH_48;
            break;

        case I2S_64FS:
            reg = I2S_CON_BIT_CH_64;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] invalid argument \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }
    
    // set CUV register
    pHDMII2SReg->I2S_CON_2 &= ~(I2S_CON_BIT_CH_MASK);
    pHDMII2SReg->I2S_CON_2 |= reg;
    
    return TRUE; 
}

BOOL I2SStart(VOID)
{
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // update CUV field
    pHDMII2SReg->I2S_CH_ST_CON = I2S_CH_ST_RELOAD;
    
    // enable audio
    pHDMII2SReg->I2S_CLK_CON = I2S_CLK_CON_ENABLE;

    // disable DSD
    pHDMII2SReg->I2S_DSD_CON = I2S_DSD_CON_DISABLE;

    // pin select for I2S
    
    pHDMII2SReg->I2S_PIN_SEL_0 = I2S_LRCLK_PIN | (I2S_SCLK_PIN << 4);
    pHDMII2SReg->I2S_PIN_SEL_1 = I2S_SDAT_0_PIN | (I2S_SDAT_1_PIN << 4);
    pHDMII2SReg->I2S_PIN_SEL_2 = I2S_SDAT_2_PIN | (I2S_SDAT_3_PIN << 4);
    pHDMII2SReg->I2S_PIN_SEL_3 = I2S_DSD_PIN;

    // I2S control
    pHDMII2SReg->I2S_CON_1 = I2S_CON_SC_POL_FALLING | I2S_CON_CH_POL_LOW;
        
    // I2S MUX Control
    pHDMII2SReg->I2S_MUX_CON = I2S_IN_MUX_ENABLE | I2S_IN_MUX_CUV_ENABLE 
                                | I2S_IN_MUX_SELECT_I2S | I2S_IN_MUX_IN_ENABLE;
    
    // enable all channels
    pHDMII2SReg->I2S_MUX_CH = I2S_MUX_CH_ALL_ENABLE;
    
    // enable CUV from right and left channel
    pHDMII2SReg->I2S_MUX_CUV = I2S_MUX_CUV_LEFT_ENABLE| I2S_MUX_CUV_RIGHT_ENABLE;

//	I2SDBGPrint();
    return TRUE;
}

BOOL I2SStop(VOID)
{
    // check mapped register
    if (pHDMII2SReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[I2S: %s] I2S register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // disable MUX
    pHDMII2SReg->I2S_MUX_CON = I2S_IN_MUX_DISABLE;
    
    // disable audio
    pHDMII2SReg->I2S_CLK_CON = I2S_CLK_CON_DISABLE ;
         
    return TRUE;
}
