
// for base address
//#include <S5PV210.h>

// for SPDIF registers

#include "libspdif.h"

//TODO: check release msg zone
#define HDMI_ZONE   0

#define SPDIF_ENCODING_TYPE_INVALID 255

// mapped SPDIF registers
volatile static PHDMI_SPDIF_REG pHDMISPDIFReg = NULL;
volatile static PHDMI_I2S_REG pHDMII2SReg = NULL;
volatile static PHDMI_SS_REG pHDMISSReg = NULL;

// coding type
static BYTE SPDIFEncodingType = SPDIF_ENCODING_TYPE_INVALID;

BOOL SPDIFHandleEvent(VOID)
{
    BYTE state;
    
    // check mapped register
    if (pHDMISPDIFReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] SPDIF register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // processing SPDIF IRQ
    // check irq state
    state = (BYTE)(pHDMISPDIFReg->SPDIFIN_IRQ_STATUS);
#if 0
    if (status & SPDIF_CLK_RECOVERY_FAIL_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIFKERN_INFO_CLK_RECOVERY_FALSE_MASK\n\r")));
    }

    if (status & SPDIF_STATUS_RECOVERED_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIF_STATUS_RECOVERED_MASK\n\r")));
    }

    if (status & SPDIF_PREAMBLE_DETECTED_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIF_PREAMBLE_DETECTED_MASK\n\r")));
    }

    if (status & SPDIF_HEADER_NOT_DETECTED_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIF_HEADER_NOT_DETECTED_MASK\n\r")));
    }

    if (status & SPDIF_HEADER_DETECTED_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIF_HEADER_DETECTED_MASK\n\r")));
    }

    if (status & SPDIF_PAPD_NOT_DETECTED_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIF_PAPD_NOT_DETECTED_MASK\n\r")));
    }

    if (status & SPDIF_ABNORMAL_PD_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIF_ABNORMAL_PD_MASK\n\r")));
    }

    if (status & SPDIF_BUFFER_OVERFLOW_MASK)
    {
        RETAILMSG(HDMI_ZONE,(_T("SPDIF_BUFFER_OVERFLOW_MASK\n\r")));
    }

#endif    
    // clear interrupt
    pHDMISPDIFReg->SPDIFIN_IRQ_STATUS = state;
    
    if ( SPDIFEncodingType == AUDIO_NLPCM )// non-linear-PCM type
    {
        switch (state)
        {
            // normal but not synchronized yet
            case SPDIF_STATUS_RECOVERED_MASK:
            case SPDIF_HEADER_DETECTED_MASK:
                RETAILMSG(HDMI_ZONE,(_T("%s: Normal Procedure\n\r"),TEXT(__FUNCTION__)));
                break;
            
            // synchronized 
            case (SPDIF_HEADER_DETECTED_MASK | SPDIF_STATUS_RECOVERED_MASK):
                // mask this interrupt. because this will happen continuously even if in normal
                pHDMISPDIFReg->SPDIFIN_IRQ_MASK &= ~(SPDIF_HEADER_DETECTED_MASK | SPDIF_STATUS_RECOVERED_MASK);
                RETAILMSG(HDMI_ZONE,(_T("%s: Succeed! Audio Start\n\r"),TEXT(__FUNCTION__)));
                pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_RUNNING;
                break;
            
            // error state
            default:
                RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] FALSE to synchronize!! Audio Start\n\r"),TEXT(__FUNCTION__)));
                pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_SIGNAL_RESET;
                // enable all irq
                pHDMISPDIFReg->SPDIFIN_IRQ_MASK = SPDIF_IRQ_ENABLE_ALL;
                // start to detect
                pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_SIGNAL_DETECT;
                break;
        } // switch
    } // if ( SPDIFEncodingType == AUDIO_NLPCM )
    else if (SPDIFEncodingType == AUDIO_LPCM) // linear PCM type
    {
        switch (state)
        {
            // synchronized 
            case SPDIF_STATUS_RECOVERED_MASK:
            case (SPDIF_STATUS_RECOVERED_MASK | SPDIF_HEADER_NOT_DETECTED_MASK):
                pHDMISPDIFReg->SPDIFIN_IRQ_MASK &= ~(SPDIF_STATUS_RECOVERED_MASK | SPDIF_HEADER_NOT_DETECTED_MASK);
                RETAILMSG(HDMI_ZONE,(_T("%s: Succeed! Audio Start\n\r"),TEXT(__FUNCTION__)));
                pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_RUNNING;
                break;
            
            // error state
            default:
                RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] FALSE to synchronize!! Audio Start\n\r"),TEXT(__FUNCTION__)));
                pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_SIGNAL_RESET;
                // enable all irq
                pHDMISPDIFReg->SPDIFIN_IRQ_MASK = SPDIF_BUFFER_OVERFLOW_MASK | SPDIF_PREAMBLE_DETECTED_MASK 
                                        | SPDIF_STATUS_RECOVERED_MASK | SPDIF_CLK_RECOVERY_FAIL_MASK;
                // start to detect
                pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_SIGNAL_DETECT;
                break;
        } // switch
    } // else if (SPDIFEncodingType == AUDIO_LPCM)
    else
    {
        RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] audio encoding type is invalid!!\n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    return TRUE;
}

// initialize SPDIF library
BOOL SPDIFInit(PHDMI_SS_REG pSsReg, 
                PHDMI_I2S_REG pI2SReg, 
                PHDMI_SPDIF_REG pSPDIFReg)
{
    // store mapped registers
    pHDMISPDIFReg = pSPDIFReg;
    pHDMII2SReg = pI2SReg;
    pHDMISSReg = pSsReg;
    
    return TRUE;
}

// deinitialize SPDIF library
// disable interrupt
BOOL SPDIFDeInit(VOID)
{
    // set to invalid 
    pHDMISPDIFReg = NULL;
    pHDMII2SReg = NULL;
    pHDMISSReg = NULL;
    
    return TRUE;
}

// set encoding type of input audio stream
BOOL SPDIFSetEncodingType(AUDIO_CODING type)
{
    if (type != AUDIO_LPCM && type != AUDIO_NLPCM)
    {
        RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] invalid argument!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    SPDIFEncodingType = type;
    
    return TRUE;
}

// start processing input audio stream
BOOL SPDIFStart(VOID)
{
    // start processing
    if (pHDMISPDIFReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] SPDIF register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // SPDIF interrupt status clear
    
    // unmask SPIDF interrupt 
    pHDMISSReg->HDMI_INTC_CON |= (1<<HDMI_IRQ_SPDIF | 1<<HDMI_IRQ_GLOBAL);
    
    
    // enable audio
    pHDMII2SReg->I2S_CLK_CON = I2S_CLK_CON_ENABLE;
    
    // select SPDIF
    pHDMII2SReg->I2S_MUX_CON = I2S_IN_MUX_SELECT_SPDIF | I2S_IN_MUX_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;
    
    // enable SPDIF clock
    pHDMISPDIFReg->SPDIFIN_CLK_CTRL = SPDIF_CLK_CTRL_DISABLE;
    pHDMISPDIFReg->SPDIFIN_CLK_CTRL = SPDIF_CLK_CTRL_ENABLE;
    
    // set SPDIF configuration
    pHDMISPDIFReg->SPDIFIN_CONFIG_1 = SPDIF_CONFIG_1_NOISE_FILTER_3_SAMPLES | SPDIF_CONFIG_1_UVCP_ENABLE 
                                    | SPDIF_CONFIG_1_WORD_LENGTH_MANUAL | SPDIF_CONFIG_1_ALIGN_32BIT 
                                    | SPDIF_CONFIG_1_HDMI_2_BURST;
    // set 
    pHDMISPDIFReg->SPDIFIN_USER_VALUE_1 = SPDIF_USER_VALUE_WORD_24BIT;
    
    // check encoding type and unmask irq
    if (SPDIFEncodingType == SPDIF_ENCODING_TYPE_INVALID)
    {
        RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] please set SPDIF encoding type first!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;    
    }
    
    if (SPDIFEncodingType == AUDIO_LPCM) 
    { // linear PCM
        pHDMISPDIFReg->SPDIFIN_IRQ_MASK = SPDIF_BUFFER_OVERFLOW_MASK | SPDIF_PREAMBLE_DETECTED_MASK 
                                        | SPDIF_STATUS_RECOVERED_MASK | SPDIF_CLK_RECOVERY_FAIL_MASK;
    }
    else // non-linear PCM
    {
        pHDMISPDIFReg->SPDIFIN_IRQ_MASK = SPDIF_IRQ_ENABLE_ALL;
    }
    
    // start detecting signal 
    pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_SIGNAL_DETECT;
   
    return TRUE;
}

// stop processing input audio stream
BOOL SPDIFStop(VOID)
{
    if (pHDMISPDIFReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[SPDIF: %s] SPDIF register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // stop processing
    // mask SPDIF interrupt
    pHDMISSReg->HDMI_INTC_CON &= ~(1<<HDMI_IRQ_SPDIF);
        
    //TODO: check!!
    pHDMISPDIFReg->SPDIFIN_OP_CTRL = SPDIF_SIGNAL_RESET;
    pHDMISPDIFReg->SPDIFIN_CLK_CTRL = SPDIF_CLK_CTRL_DISABLE;
    
    return TRUE;    
}