
#include <windows.h>
#include <ceddk.h> 

#include <hdmireg.h>

// for phy mapping 
#include <register_map.h>
#include "hdmi_phy.h"
#include "i2c_hdmi.h"

#include "libhdmi.h"

// for controlling audio input port
#include "libi2s.h"
#include "libspdif.h"

// for hdcp ddc
#include "libddc.h"

//TODO: change the debug zone
#define HDMI_ZONE	(0)

/** HDCP Rx device address */
#define HDCP_RX_DEV_ADDR                        (0x74)

/** HDCP Bksv offset */
#define HDCP_BKSV_OFFSET                        (0x00)

/** HDCP Aksv offset */
#define HDCP_AKSV_OFFSET                        (0x10)

/** HDCP An offset */
#define HDCP_AN_OFFSET                          (0x18)

//@{
/**
 * Bit values for HDCP Bcaps
 */
#define HDCP_BCAPS_REPEATER                     (6)
#define HDCP_BCAPS_READY                        (5)
//@}

/** HDCP Bcaps offset */
#define HDCP_BCAPS_OFFSET                       (0x40)

/** HDCP Ri offset */
#define HDCP_RI_OFFSET                          (0x08)

/** HDCP calculated SHA1 result offset */
#define HDCP_RX_SHA1_OFFSET                     (0x20)

/** HDCP Bstatus offset */
#define HDCP_BSTATUS_OFFSET                     (0x41)

/** Size of HDCP Btatus */
#define HDCP_BSTATUS_SIZE                       (2)

/** Size of HDCP KSV */
#define HDCP_KSV_SIZE                           (5)

/** Size of HDCP AN*/
#define HDCP_AN_SIZE                            (8)

/** Size of HDCP Ri */
#define HDCP_RI_SIZE                            (2)

/** Number of Compare */
//#define HDCP_RIRJ_COMPARE_NUM                    (5)
//For SimplayHD
#define HDCP_RIRJ_COMPARE_NUM                    (1)

/** Size of SHA1 result */
#define HDCP_SHA1_SIZE                          (20)


/** HDCP KSV list offset */
#define HDCP_KSV_LIST_OFFSET                    (0x43)

//@{
/**
 * Bit values for HDCP Bstatus
 */
#define MAX_DEVS_EXCEEDED_MASK                  (1<<7)
#define MAX_CASCADE_EXCEEDED_MASK               (1<<3)
#define DEVICE_COUNT_MASK                       (0x7F)
#define HDMI_MODE_MASK                          (1<<4)
#define HDCP_MAX_DEVS                           (128)
#define HDCP_MAX_CASCADE                        (8)
//@}

/**
 * @enum repeater_error
 * Error code for 2nd Authentication process
 */
enum {
    /** Not known error */
    REPEATER_ERROR_NOT_KNOWN,
    /** Time out error */
    REPEATER_TIME_OUT_ERROR,
    /** Illegal device error */
    REPEATER_ILLEGAL_DEVICE_ERROR,
};

/**
 * @enum hdcp_state
 * State of HDCP. For more information, refer HDCP Spec.
 */
enum hdcp_state_enum {
    /** IDLE State indicates it starts reading Bksv,Bcaps*/
    HDCP_STATE_IDLE,
    /** State indicates it starts writing Aksv,An */
    HDCP_STATE_BKSV_READ_DONE,
    /** State indicates it starts comparing Ri */
    HDCP_STATE_AKSV_WRITE_DONE,
    /** State indicates it starts 2nd authentication process */
    HDCP_STATE_SECOND_AUTH_START,
    /** State indicates it's on 3rd authentication process */
    HDCP_STATE_THIRD_AUTH,
    /** State indicates it need to be reset */
    HDCP_STATE_FATAL,
};

//! Structure for video timing parameters
static const struct hdmi_video_params
{
    /** [H Blank] */
    DWORD   HBlank;

    /** [V Blank] */
    DWORD   VBlank;

    /**
     * [H Total : V Total] @n
     * For more information, refer HDMI register map.
     */
    DWORD   HVLine;

    /**
     * [H Sync polarity : H Sync end point : H Sync start point]@n
     * For more information, refer HDMI register map.
     */
    DWORD   HSYNCGEN;

    /**
     * [V Sync start line num + V Sync end line num] @n
     * For more information, refer HDMI register map.
     */
    DWORD   VSYNCGEN;

    /** CEA VIC */
    BYTE   AVI_VIC;
    /** CEA VIC for 16:9 pixel ratio */
    BYTE   AVI_VIC_16_9;

    /** 0 - progresive, 1 - interlaced */
    BYTE   interlaced;

    /** Pixel repetition if double, set 1 */
    BYTE   repetition;

    /** V Sync polarity */
    BYTE   polarity;

    /**
     * In case of interlaced mode, @n
     * [end point of bottom field's active region : start point of that]@n
     * For more information, refer HDMI register map.
     */
    DWORD   VBLANK_F;

    /**
     * In case of interlaced mode, @n
     * [start line of bottom field's V Sync : end line of that]@n
     * For more information, refer HDMI register map.
     */
    DWORD   VSYNCGEN2;

    /**
     * In case of interlaced mode, @n
     * [start transition point of bottom field's V Sync : end transition of that]@n
     * For more information, refer HDMI register map.
     */
    DWORD   VSYNCGEN3;
    
    /**
     * PHY Clock for the video mode
     */
    PHYFreq PixelClock;
} HDMIVideoParams[] =
{
    { 0xA0 , 0x16A0D, 0x32020D, 0x11B80E, 0xA00C , 1 , 1 , 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_25_200, },  // v640x480p_60Hz
    { 0x8A , 0x16A0D, 0x35A20D, 0x11300E, 0x900F , 2 , 3 , 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_27_027, },  // v720x480p_60Hz
    { 0x172, 0xF2EE , 0x6722EE, 0x2506C , 0x500A , 4 , 4 , 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_74_250, },  // v1280x720p_60Hz
    { 0x118, 0xB232 , 0x898465, 0x20856 , 0x2007 , 5 , 5 , 1, 0, 0, 0x232A49, 0x234239, 0x4A44A4, PHY_FREQ_74_250, },  // v1920x1080i_60Hz
    { 0x114, 0xB106 , 0x6B420D, 0x128024, 0x4007 , 6 , 7 , 1, 1, 1, 0x10691D, 0x10A10D, 0x380380, PHY_FREQ_27_027, },  // v720x480i_60Hz
    { 0x114, 0xB106 , 0x6B4106, 0x128024, 0x4007 , 8 , 9 , 0, 1, 1, 0       , 0       , 0       , PHY_FREQ_27_027, },  // v720x240p_60Hz
    { 0x228, 0xB106 , 0xD6820D, 0x15084A, 0x4007 , 10, 11, 1, 1, 1, 0x10691D, 0x10A10D, 0x700700, PHY_FREQ_54_054, },  // v2880x480i_60Hz
    { 0x228, 0xB106 , 0x6B4106, 0x15084A, 0x4007 , 12, 13, 0, 1, 1, 0       , 0       , 0       , PHY_FREQ_54_054, },  // v2880x240p_60Hz
    { 0x114, 0x16A0D, 0x6B420D, 0x12681E, 0x900F , 14, 15, 0, 1, 1, 0       , 0       , 0       , PHY_FREQ_54_054, },  // v1440x480p_60Hz
    { 0x118, 0x16C65, 0x898465, 0x20856 , 0x4009 , 16, 16, 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_148_500,},  // v1920x1080p_60Hz

    { 0x90 , 0x18A71, 0x360271, 0x11280A, 0x500A , 17, 18, 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_27,     },  // v720x576p_50Hz
    { 0x2BC, 0xF2EE , 0x7BC2EE, 0x779B6 , 0x500A , 19, 19, 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_74_250, },  // v1280x720p_50Hz
    { 0x2D0, 0xB232 , 0xA50465, 0x8EA0E , 0x2007 , 20, 20, 1, 0, 0, 0x232A49, 0x234239, 0x738738, PHY_FREQ_74_250, },  // v1920x1080i_50Hz
    { 0x120, 0xC138 , 0x6C0271, 0x125016, 0x2005 , 21, 22, 1, 1, 1, 0x138951, 0x13A13D, 0x378378, PHY_FREQ_27,     },  // v720x576i_50Hz
    { 0x120, 0xC138 , 0x6C0138, 0x125016, 0x3006 , 23, 24, 0, 1, 1, 0       , 0       , 0       , PHY_FREQ_27,     },  // v720x288p_50Hz
    { 0x240, 0xC138 , 0xD80271, 0x14A82E, 0x2005 , 25, 26, 1, 1, 1, 0x138951, 0x13A13D, 0x6F06F0, PHY_FREQ_54,     },  // v2880x576i_50Hz
    { 0x240, 0xC138 , 0xD80138, 0x14A82E, 0x2005 , 27, 28, 0, 1, 1, 0       , 0       , 0       , PHY_FREQ_54,     },  // v2880x288p_50Hz
    { 0x120, 0x18A71, 0x6C0271, 0x125816, 0x500A , 29, 30, 0, 1, 1, 0       , 0       , 0       , PHY_FREQ_54,     },  // v1440x576p_50Hz
    { 0x2D0, 0x16C65, 0xA50465, 0x8EA0E , 0x4009 , 31, 31, 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_148_500,},  // v1920x1080p_50Hz
    { 0x33E, 0x16C65, 0xABE465, 0xAA27C , 0x4009 , 32, 32, 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_74_250, },  // v1920x1080p_24Hz

    { 0x2D0, 0x16C65, 0xA50465, 0x8EA0E , 0x4009 , 33, 33, 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_74_250, },  // v1920x1080p_25Hz
    { 0x118, 0x16C65, 0x898465, 0x20856 , 0x4009 , 34, 34, 0, 0, 0, 0			, 0 		, 0 		, PHY_FREQ_74_250,	},	// v1920x1080p_30Hz
    { 0x228, 0x16A0D, 0xD6820D, 0x14D83E, 0x900F , 35, 36, 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_108_108,},  // v2880x480p_60Hz
    { 0x240, 0x18A71, 0xD80271, 0x14B82E, 0x500A , 37, 38, 0, 1, 1, 0       , 0       , 0       , PHY_FREQ_108,    },  // v2880x576p_50Hz
    { 0x180, 0x2AA71, 0x9004E2, 0x3181E , 0x1701C, 39, 39, 0, 0, 0, 0x2712C6, 0x28728F, 0x4a44a4, PHY_FREQ_72,     },  // v1920x1080i_50Hz(1250)
    { 0x2D0, 0xB232 , 0xA50465, 0x8EA0E , 0x2007 , 40, 40, 1, 0, 0, 0x232A49, 0x234239, 0x738738, PHY_FREQ_148_500,},  // v1920x1080i_100Hz
    { 0x2BC, 0xF2EE , 0x7BC2EE, 0x779B6 , 0x500A , 41, 41, 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_148_500,},  // v1280x720p_100Hz
    { 0x90 , 0x18A71, 0x360271, 0x11280A, 0x500A , 42, 43, 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_54,     },  // v720x576p_100Hz
    { 0x120, 0xC138 , 0x6C0271, 0x125016, 0x2005 , 44, 45, 1, 1, 1, 0x138951, 0x13A13D, 0x378378, PHY_FREQ_54,     },  // v720x576i_100Hz
    { 0x118, 0xB232 , 0x898465, 0x20856 , 0x2007 , 46, 46, 1, 0, 0, 0x232A49, 0x234239, 0x4A44A4, PHY_FREQ_148_500,},  // v1920x1080i_120Hz

    { 0x172, 0xF2EE , 0x6722EE, 0x2506C , 0x500A , 47, 47, 0, 0, 0, 0       , 0       , 0       , PHY_FREQ_148_500,},  // v1280x720p_120Hz
    { 0x8A , 0x16A0D, 0x35A20D, 0x11300E, 0x900F , 48, 49, 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_54_054, },  // v720x480p_120Hz
    { 0x114, 0xB106 , 0x6B420D, 0x128024, 0x4007 , 50, 51, 1, 1, 1, 0x10691D, 0x10A10D, 0x380380, PHY_FREQ_54_054, },  // v720x480i_120Hz
    { 0x90 , 0x18A71, 0x360271, 0x11280A, 0x500A , 52, 53, 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_108,    },  // v720x576p_200Hz
    { 0x120, 0xC138 , 0x6C0271, 0x125016, 0x2005 , 54, 55, 1, 1, 1, 0x138951, 0x13A13D, 0x378378, PHY_FREQ_108,    },  // v720x576i_200Hz
    { 0x8A , 0x16A0D, 0x35A20D, 0x11300E, 0x900F , 56, 57, 0, 0, 1, 0       , 0       , 0       , PHY_FREQ_108_108,},  // v720x480p_240Hz
    { 0x114, 0xB106 , 0x6B420D, 0x128024, 0x4007 , 58, 59, 1, 1, 1, 0x10691D, 0x10A10D, 0x380380, PHY_FREQ_108_108,},  // v720x480i_240Hz

	
};

//! array for setting PCM word length 
static const BYTE CUV_word_length[] =
{
    CUV_WL_24_NOT_DEFINED,  // UNKNOWN
    CUV_WL_20_16,           // WORD_16
    CUV_WL_20_17,           // WORD_17
    CUV_WL_20_18,           // WORD_18                
    CUV_WL_20_19,           // WORD_19
    CUV_WL_24_20,           // WORD_20
    CUV_WL_24_21,           // WORD_21
    CUV_WL_24_22,           // WORD_22
    CUV_WL_24_23,           // WORD_23
    CUV_WL_24_24,           // WORD_24
};

//! array for setting ACR packet
static const DWORD ACR_N_VALUE[] =
{
    ACR_N_32KHZ,
    ACR_N_44KHZ,
    ACR_N_48KHZ,
    ACR_N_88KHZ,
    ACR_N_96KHZ,
    ACR_N_176KHZ,
    ACR_N_192KHZ
};

//! array for setting AUI Sampling frequency field
static const DWORD AUI_SF_VALUE[] =
{
    HDMI_AUI_SF_SF_32KHZ,       // SF_32KHZ
    HDMI_AUI_SF_SF_44KHZ,       // SF_44KHZ
    HDMI_AUI_SF_SF_48KHZ,       // SF_48KHZ
    HDMI_AUI_SF_SF_88KHZ,       // SF_88KHZ
    HDMI_AUI_SF_SF_96KHZ,       // SF_96KHZ
    HDMI_AUI_SF_SF_176KHZ,      // SF_176KHZ
    HDMI_AUI_SF_SF_192KHZ,      // SF_192KHZ
};

#define EFUSE_ADDR_WIDTH_VAL			(240)
#define EFUSE_SIGDEV_ASSERT_VAL		(0)
#define EFUSE_SIGDEV_DEASSERT_VAL		(96)
#define EFUSE_PRCHG_ASSERT_VAL		(0)
#define EFUSE_PRCHG_DEASSERT_VAL		(144)
#define EFUSE_FSET_ASSERT_VAL			(48)
#define EFUSE_FSET_DEASSERT_VAL		(192)
#define EFUSE_SENSING_VAL				(240)
#define EFUSE_SCK_ASSERT_VAL			(48)
#define EFUSE_SCK_DEASSERT_VAL			(144)
#define EFUSE_SDOUT_OFFSET_VAL		(192)
#define EFUSE_READ_OFFSET_VAL			(192)

#define EFUSE_ECC_DONE_VAL				(1<<0)
#define EFUSE_ECC_BUSY_VAL				(1<<1)
#define EFUSE_ECC_FAIL_VAL				(1<<2)

#define HDCP_RI_RETRY_VAL				(5)



// HDMI mapped address
volatile static PHDMI_SS_REG pHDMISSReg = NULL;
volatile static PHDMI_CORE_REG pHDMICoreReg = NULL;
volatile static PHDMI_HDCP_REG pHDMIHDCPReg = NULL;
volatile static PHDMI_EFUSE_REG pHDMIEfuseReg = NULL;
volatile static PHDMI_I2S_REG pHDMII2SReg = NULL; 
volatile static PHDMI_SPDIF_REG pHDMISPDIFReg = NULL;

// for phy config
volatile static PCMU_CLK_REG pSysClkReg = NULL;
volatile static PIIC_REG pHDMIIICReg = NULL;

// HPD state
static BYTE last_hpd_state = HPD_LO;

// HDCP state
static DWORD hdcp_state = HDCP_STATE_IDLE;

// HPD call-back function
static HPDCallBack* _handler = NULL;

// static functions

// HDMI video
static BOOL SetHDMIMode(enum HDMIMode mode);
static BOOL SetColorDepth(COLOR_DEPTH cd);
static BOOL SetColorSpace(COLOR_SPACE cs);
static BOOL SetVideoResolution(VIDEO_FORMAT format, enum PixelAspectRatio ratio);
static BOOL SetColorimetry(HDMI_COLORMETRY colorimetry);

// HDMI audio
static BOOL SetAudioSampleFreq(SAMPLEING_FREG freq);
static VOID ClearAUIAudioSampleFreq(VOID);
static BOOL SetAudioNumberOfChannels(CHANNEL_NUM num);
static BOOL SetAudioPacketType(HDMI_ASP_TYPE type); 

// HDMI packet
static VOID UpdateAVIChecksum(VOID);
static VOID UpdateAUIChecksum(VOID);

// HPD
static BOOL HPDEventHandler(VOID);

// HDCP 
static BOOL HDCPEventHandler(VOID);
static VOID HDCPReset(VOID);
static BOOL HDCPLoadKey(VOID);
static VOID HDCPEnableEncryption(BOOL);
static BOOL HDCPCheckRepeater(PDWORD ErrorCode);

// PHY
static BOOL SetHDMIPHY(VIDEO_FORMAT format,COLOR_DEPTH cd);
BOOL HDMICheckPhyReady(void);
BOOL HDMICoreReset(void);

BOOL HDMIInit(PHDMI_SS_REG       pSSReg, 
                PHDMI_CORE_REG  pCoreReg, 
                PHDMI_HDCP_REG  pHDCPReg,
                PHDMI_EFUSE_REG pEfuseReg,
                PHDMI_I2S_REG   pI2SReg,
                PHDMI_SPDIF_REG pSPDIFReg,
                PCMU_CLK_REG    pCMUCLKReg,
                PIIC_REG        pIICReg)
{
    // set registers
    pHDMISSReg = pSSReg;
    pHDMICoreReg = pCoreReg;
    pHDMIHDCPReg = pHDCPReg;
    pHDMIEfuseReg = pEfuseReg;
    pHDMII2SReg = pI2SReg;
    pHDMISPDIFReg = pSPDIFReg; 
    pSysClkReg = pCMUCLKReg;
    pHDMIIICReg = pIICReg;
    
    if ( I2SInit(pHDMII2SReg) == FALSE )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Fail to init I2S library!!! \n\r"),TEXT(__FUNCTION__)));
		return FALSE;    
    }
    
    if ( SPDIFInit(pHDMISSReg, pHDMII2SReg, pHDMISPDIFReg) == FALSE )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Fail to init SPDIF library!!! \n\r"),TEXT(__FUNCTION__)));
		return FALSE;    
    }
    
    // for PHY Setting
    if (I2CHDMI_Init(pCMUCLKReg,pHDMIIICReg) == FALSE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Fail to init I2CHDMI library!!! \n\r"),TEXT(__FUNCTION__)));
		return FALSE;        
    }

    // for HPD
    pHDMICoreReg->HDMI_HPD_GEN = 0xFF;
    
    return TRUE;
}

BOOL HDMIDeInit(VOID)
{
    if ( I2SDeInit() == FALSE )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Fail to init I2S library!!! \n\r"),TEXT(__FUNCTION__)));
		return FALSE;    
    }
    
    if ( SPDIFDeInit() == FALSE )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Fail to init SPDIF library!!! \n\r"),TEXT(__FUNCTION__)));
		return FALSE;    
    }
    
    // set registers
    pHDMISSReg = NULL;
    pHDMICoreReg = NULL;
    pHDMIHDCPReg = NULL;
    pHDMIEfuseReg = NULL;
    pHDMII2SReg = NULL;
    pHDMISPDIFReg = NULL;
    pSysClkReg = NULL;
    pHDMIIICReg = NULL;
    
    return TRUE;
}

// handle HDMI interrupt event
// priority HPD > HDCP > SPDIF
BOOL HDMIEventHandler(VOID)
{
    // check if mapped register is available		           
    if (pHDMISSReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Subsystem register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    // check interrupt status
    if ( !(pHDMISSReg->HDMI_INTC_FLAG & (1<<HDMI_IRQ_HPD_UNPLUG | 1<<HDMI_IRQ_HPD_PLUG
                                            | 1<<HDMI_IRQ_HDCP | 1<<HDMI_IRQ_SPDIF)) )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] interrupt is not related to HDMI. Can not handle!!! (0x%02X)\n\r"),TEXT(__FUNCTION__),pHDMISSReg->HDMI_INTC_FLAG));
        pHDMISSReg->HDMI_INTC_FLAG = 0xFF;
        return FALSE;
    }                                               

    // clear pending bit is done inside event handler
    // if HPD interrupt happen
    if ( pHDMISSReg->HDMI_INTC_FLAG & (1<<HDMI_IRQ_HPD_UNPLUG | 1<<HDMI_IRQ_HPD_PLUG) )
    {
        // execute hpd event handler
        if (HPDEventHandler() == FALSE)
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to handle HPD event!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
        }
    }
    
    // if HDCP interrupt happen 
    if ( pHDMISSReg->HDMI_INTC_FLAG & (1<<HDMI_IRQ_HDCP) )
    {
        // execute hdcp event handler
        if (HDCPEventHandler() == FALSE)
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to handle HDCP event!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
        }
    }
    
    // if SPDIF interrupt happen
    if ( pHDMISSReg->HDMI_INTC_FLAG & (1<<HDMI_IRQ_SPDIF) )
    {
        // execute SPDIF event handler
        if (SPDIFHandleEvent() == FALSE)
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to handle SPDIF event!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
        }
    }
    
    //TODO: in case of CEC, interrupt will not happen
    
    
    return TRUE;
}

//TODO: add phy registers setting 
// set hdmi video registers. 
BOOL HDMISetVideoMode(HDMI_VIDEO_PARAMETER* pHDMIVideoParam)
{
	RETAILMSG(HDMI_ZONE,(_T("++ HDMISetVideoMode\n\r")));
	
	// check arguement
	if (pHDMIVideoParam == NULL)
	{
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
		return FALSE;
    }

	RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] video resolution = %d \n\r"),TEXT(__FUNCTION__),pHDMIVideoParam->resolution));
		          
    // check if mapped register is available		           
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Registers is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set PHY clk 
    if ( SetHDMIPHY(pHDMIVideoParam->resolution,pHDMIVideoParam->colorDepth) == FALSE )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Can not set PHY\n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
	// set HDMI mode
    if ( SetHDMIMode(pHDMIVideoParam->mode) == FALSE )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Can not set HDMI/DVI Mode\n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
	// set video resolution
    if ( SetVideoResolution(pHDMIVideoParam->resolution, pHDMIVideoParam->pixelAspectRatio) == FALSE )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Can not set Video Resolution \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
	
	// set color depth
	if ( SetColorDepth(pHDMIVideoParam->colorDepth) == FALSE )
	{
	    RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Can not set Color Depth\n\r"),TEXT(__FUNCTION__)));
        return FALSE;
	}
    
	// set color space
	if ( SetColorSpace(pHDMIVideoParam->colorSpace) == FALSE )
	{
	    RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Can not set Color Space\n\r"),TEXT(__FUNCTION__)));
        return FALSE;
	}
	
	// Set Colorimetry 
	if ( SetColorimetry(pHDMIVideoParam->colorimetry) == FALSE )
	{
	    RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Can not set Colorimetry\n\r"),TEXT(__FUNCTION__)));
        return FALSE;
	}

	RETAILMSG(HDMI_ZONE,(_T("-- HDMISetVideoMode\n\r")));
	
	return TRUE;
}

//TODO: check the audio clk setting register.
// set hdmi audio registers and audio input HW block 
BOOL HDMISetAudioMode(HDMI_AUDIO_PARAMETER* pHDMIAudioParam)
{
    BYTE audioType;
    // check arguement
    if (pHDMIAudioParam == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // check core register 
    if (pHDMICoreReg == NULL || pHDMII2SReg == NULL || pHDMISPDIFReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] registers are not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    if (pHDMIAudioParam->wordLength > WORD_24)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Input Param is invalid \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
   
    // check if audio encoding type is linear or non-linear
    if (pHDMIAudioParam->formatCode == LPCM_FORMAT)
    {
        audioType = AUDIO_LPCM;
    }
    else
    {
        audioType = AUDIO_NLPCM;
    }
    
    // check audio input port and set 
    switch (pHDMIAudioParam->inputPort)
    {
        case I2S_PORT:
            // set i2s cuv field
            // sampling freq
            if ( I2SSetCUVSamplingFreq(pHDMIAudioParam->sampleFreq) == FALSE )
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set I2S CUV sampling freq field!!! \n\r"),TEXT(__FUNCTION__)));
                return FALSE;
            }
            // encoding type
            // if LPCM mode
            if (I2SSetCUVEncodingType(audioType) == FALSE)
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set I2S CUV encoding type field!!! \n\r"),TEXT(__FUNCTION__)));
                return FALSE;
            }
            // if LPCM && wordlength parameter is valid, set word length
            if (audioType == AUDIO_LPCM && (pHDMIAudioParam->wordLength >= sizeof(CUV_word_length)/sizeof(BYTE) || pHDMIAudioParam->wordLength < 0))
            {
                 // set CUV word length fields
                 if (I2SSetCUVWordLength(CUV_word_length[pHDMIAudioParam->wordLength]) == FALSE )
                 {
                    RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set I2S CUV word length field!!! \n\r"),TEXT(__FUNCTION__)));
                    return FALSE;
                 }
            } // if audioType == AUDIO_LPCM
			
            // set I2S specific parameters
            if ( I2SSetBitPerChannel(pHDMIAudioParam->I2SParam.bpc) == FALSE ||
                    I2SSetDataFormat(pHDMIAudioParam->I2SParam.format) == FALSE ||
                    I2SSetClockPerFrame(pHDMIAudioParam->I2SParam.clk) == FALSE )
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set I2S specific parameters!!! \n\r"),TEXT(__FUNCTION__)));
                return FALSE;
            }

            // start I2S port
            if (I2SStart() == FALSE)
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to start I2S input port!!! \n\r"),TEXT(__FUNCTION__)));
                return FALSE;
            }
            break;
        case SPDIF_PORT:
            // set spdif audio encoding type
            if (SPDIFSetEncodingType(audioType) == FALSE)
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set SPDIF encoding type!!! \n\r"),TEXT(__FUNCTION__)));
                return FALSE;
            }
            
            // start spdif port
            if (SPDIFStart() == FALSE)
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to start SPDIF input port!!! \n\r"),TEXT(__FUNCTION__)));
                return FALSE;
            }
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement(Audio Inpuit Port)!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    } // switch 
    
    // set audio sample packet type
    if (SetAudioPacketType(pHDMIAudioParam->outPacket) == FALSE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set audio packet type!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set ACR packet and set audio sampling freq in AUI
    if (SetAudioSampleFreq(pHDMIAudioParam->sampleFreq) == FALSE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set ACR packet type!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
     
    // set audio sampling freq in AUI
    if (pHDMIAudioParam->outPacket == HDMI_ASP)
    {
        ClearAUIAudioSampleFreq();
    }
    
    // set number of audio channels 
    if (SetAudioNumberOfChannels(pHDMIAudioParam->channelNum) == FALSE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] FALSE to set ACR packet type!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    return TRUE;
}

// set MPEG InfoFrame packet body.
// the length of packet body should be 5 - from CEA 861-D
BOOL HDMISetMPGInfoFrame(PBYTE pucPacketBody)
{
    // check arguement
    if (pucPacketBody == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    // copy packet body to registers
    pHDMICoreReg->HDMI_MPG_CHECK_SUM = pucPacketBody[0];    // packet body 0
    pHDMICoreReg->HDMI_MPG_BYTE1 = pucPacketBody[1];        // packet body 1
    pHDMICoreReg->HDMI_MPG_BYTE2 = pucPacketBody[2];        // packet body 2
    pHDMICoreReg->HDMI_MPG_BYTE3 = pucPacketBody[3];        // packet body 3
    pHDMICoreReg->HDMI_MPG_BYTE4 = pucPacketBody[4];        // packet body 4
    
    return TRUE;
}

// start/stop transmitting MPEG InfoFrame packet
BOOL HDMIEnableMPGInfoFrame(BOOL bEn)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    if (bEn == TRUE)
    {
        // transmit every images
        pHDMICoreReg->HDMI_MPG_CON = TRANSMIT_EVERY_VSYNC;
    }
    else
    {
        // do not transmit
        pHDMICoreReg->HDMI_MPG_CON = DO_NOT_TRANSMIT;
    }
    
    return TRUE;
}

// set SPD InfoFrame packet body
// the size of SPD infoFrame packet body should be 25 - from CEA-861D
BOOL HDMISetSPDInfoFrame(PBYTE pucPacketBody)
{
    DWORD index;
    PDWORD pbaseAddr;
    // check arguement
    if (pucPacketBody == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set packet header
    pHDMICoreReg->HDMI_SPD_HEADER0 = SPD_INFO_PACKET_TYPE;
    pHDMICoreReg->HDMI_SPD_HEADER1 = SPD_INFO_PACKET_VERSION;
    pHDMICoreReg->HDMI_SPD_HEADER2 = SPD_INFO_PACKET_BYTE_LENGTH; 
    
    // set packet body
    for (index = 0,pbaseAddr = &(pHDMICoreReg->HDMI_SPD_DATA0); index < SPD_INFO_PACKET_BYTE_LENGTH; index++)
    {
        *(pbaseAddr + index) = pucPacketBody[index];    
    }
    
    // padding the remain packet body
    pHDMICoreReg->HDMI_SPD_DATA25 = 0x00;
    pHDMICoreReg->HDMI_SPD_DATA26 = 0x00;
    pHDMICoreReg->HDMI_SPD_DATA27 = 0x00;
    
    return TRUE;
}

// start/stop transmitting SPD InfoFrame packet or not
BOOL HDMIEnableSPDInfoFrame(BOOL bEn)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    if (bEn == TRUE)
    {
        // transmit every images
        pHDMICoreReg->HDMI_MPG_CON = TRANSMIT_EVERY_VSYNC;
    }
    else
    {
        // do not transmit
        pHDMICoreReg->HDMI_MPG_CON = DO_NOT_TRANSMIT;
    }
    
    return TRUE;
}

// set GAMUT packet header, body
BOOL HDMISetGamutDataPacket(PBYTE pucPacketHeader, 
                            PBYTE pucPacketBody,
                            BYTE ucPacketBodySize )
{
    BYTE index;
    PDWORD pbaseAddr;

    // check arguement
    // check gamut packet heaer type and packet body length
    if (pucPacketHeader == NULL || pucPacketBody == NULL || 
            ucPacketBodySize > GAMUT_PACKET_BODY_MAX_SIZE || pucPacketHeader[0] == GAMUT_PACKET_TYPE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set packet header
    for (index = 0, pbaseAddr = &(pHDMICoreReg->HDMI_GAMUT_HEADER0) ; index < HDMI_PACKET_HEADER_LENGTH; index++)
    {
        *(pbaseAddr + index) = pucPacketHeader[index];
    }
    
    // set packet body
    for (index = 0, pbaseAddr = &(pHDMICoreReg->HDMI_GAMUT_DATA00) ; index < GAMUT_PACKET_BODY_MAX_SIZE; index++)
    {
        if ( index < ucPacketBodySize )
        { 
            // set packet body if it is valid
            *(pbaseAddr + index) = pucPacketBody[index];
        }
        else
        { 
            // set default value if it is not valid
            *(pbaseAddr + index) = 0x00;
        }
    }
    return TRUE;
}

// start/stop transmitting GAMUT packet or not
BOOL HDMIEnableGamutDataPacket(BOOL bEn)
{    
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    if (bEn == TRUE)
    {
        // transmit every images
        pHDMICoreReg->HDMI_GAMUT_CON = TRANSMIT_EVERY_VSYNC;
    }
    else
    {
        // do not transmit
        pHDMICoreReg->HDMI_GAMUT_CON = DO_NOT_TRANSMIT;
    }
    
    return TRUE;
}

// set bluescreen color
BOOL HDMISetBlueScreenColor(BYTE ucCbB,BYTE ucYG,BYTE ucCrR)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set the color
    pHDMICoreReg->HDMI_BLUE_SCREEN0 = ucCbB;
    pHDMICoreReg->HDMI_BLUE_SCREEN1 = ucYG;
    pHDMICoreReg->HDMI_BLUE_SCREEN2 = ucCrR;
    
    return TRUE;    
}

// enable/disable bluescreen mode
BOOL HDMISetBlueScreen(BOOL bEn)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // check arg
    if (bEn == TRUE)
    {
        // enable blue screen
        pHDMICoreReg->HDMI_CON0 |= HDMI_BLUE_SCR_ENABLE;
    }
    else
    {
        // disable blue screen
        pHDMICoreReg->HDMI_CON0 &= ~HDMI_BLUE_SCR_ENABLE;
    }
    return TRUE;
}

// enable/disable AV Mute mode
BOOL HDMISetAVMute(BOOL bEn)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // check the HDMI/DVI mode
    // if HDMI mode
    if ( pHDMICoreReg->HDMI_MODE_SEL == HDMI_MODE_SEL_HDMI) 
    {
        // check arg
        if (bEn == TRUE) // send GCP with AVMUTE
        {
            // set SETAVMUTE bit in GCP packet
            pHDMICoreReg->HDMI_GCP_BYTE1 = GCP_AVMUTE_ON;
            // send GCP packet
            pHDMICoreReg->HDMI_GCP_CON = GCP_TRANSMIT_EVERY_VSYNC;
        }
        else // send GCP with CLEAR AV MUTE
        {
            // set CLEARAVMUTE bit in GCP packet
            pHDMICoreReg->HDMI_GCP_BYTE1 = GCP_AVMUTE_OFF;
            // send GCP packet
            pHDMICoreReg->HDMI_GCP_CON = GCP_TRANSMIT_EVERY_VSYNC;    
        }
    }
    else // DVI mode
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Now the mode is DVI, do nothing!!! \n\r"),TEXT(__FUNCTION__)));
    }
    return TRUE;    
}

// start/stop transmitting Audio
//TODO: check if we should send/not send the ACR packet 
BOOL HDMIAudioEnable(BOOL bEn)
{
	RETAILMSG(HDMI_ZONE,(_T("++ HDMIAudioEnable \n\r"),TEXT(__FUNCTION__)));

    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));

        return FALSE;
    }
    
    // check arg
    if (bEn == TRUE)
    {
        // enable audio sample packet out
        pHDMICoreReg->HDMI_CON0 |= HDMI_ASP_ENABLE;
    }
    else
    {
        // disable audio sample packet out
        pHDMICoreReg->HDMI_CON0 &= ~HDMI_ASP_ENABLE;
    }
	
	RETAILMSG(HDMI_ZONE,(_T("-- HDMIAudioEnable \n\r"),TEXT(__FUNCTION__)));
	
	return TRUE;
}

// start transmitting HDMI output
BOOL HDMIStart(VOID)
{
	RETAILMSG(HDMI_ZONE,(_T("++ HDMIStart \n\r"),TEXT(__FUNCTION__)));

    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    //TODO: change this to external
    // set internal mode
    pHDMICoreReg->HDMI_VIDEO_PATTERN_GEN = 0x01;
    //pHDMICoreReg->HDMI_VIDEO_PATTERN_GEN = HDMI_EXTERNAL_VIDEO;
    
    // check HDMI/DVI mode
    if ( pHDMICoreReg->HDMI_MODE_SEL == HDMI_MODE_SEL_HDMI )
    {
        // hdmi mode
    
        // enable AVI
        UpdateAVIChecksum();
        pHDMICoreReg->HDMI_AVI_CON = TRANSMIT_EVERY_VSYNC;
                
        // if audio parameter is set. enable aui and audio
        if (pHDMICoreReg->HDMI_ACR_CON)
        {
            // enable aui
            UpdateAUIChecksum();
            pHDMICoreReg->HDMI_AUI_CON = TRANSMIT_EVERY_VSYNC;
            
            // enable audio
            pHDMICoreReg->HDMI_CON0 |= HDMI_ASP_ENABLE;    
        }
        // if deep color mode, enable GCP 
        if (pHDMICoreReg->HDMI_DC_CONTROL)
        {
            // send GCP packet
            pHDMICoreReg->HDMI_GCP_CON = GCP_TRANSMIT_EVERY_VSYNC;    
        }
        // enable system
        //TODO: check about encoding option
        pHDMICoreReg->HDMI_CON0 |= HDMI_SYS_ENABLE;
    }
    else
    {
        // dvi mode
        
        // disable all packets
        pHDMICoreReg->HDMI_AVI_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_AUI_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_ACR_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_GCP_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_ACP_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_ISRC_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_MPG_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_SPD_CON = DO_NOT_TRANSMIT;
        pHDMICoreReg->HDMI_GAMUT_CON = DO_NOT_TRANSMIT;
        
        // enable system, disable audio
        pHDMICoreReg->HDMI_CON0 |= HDMI_SYS_ENABLE;
        pHDMICoreReg->HDMI_CON0 &= ~HDMI_ASP_ENABLE;    
    }
    RETAILMSG(HDMI_ZONE,(_T("[libhdmi: %s] HDMI CON0 = 0x%02X!!! \n\r"),TEXT(__FUNCTION__), pHDMICoreReg->HDMI_CON0));
	RETAILMSG(HDMI_ZONE,(_T("-- HDMIStart \n\r"),TEXT(__FUNCTION__)));	
	
    return TRUE; 
}

// stop transmitting HDMI output
BOOL HDMIStop(VOID)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // disable system
    pHDMICoreReg->HDMI_CON0 &= ~HDMI_SYS_ENABLE;
    
    return TRUE;
}

// set pixel color range
BOOL HDMISetColorRange(BYTE ucYMin,BYTE ucYMax,
                        BYTE ucCMin,BYTE ucCMax)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    pHDMICoreReg->HDMI_YMAX = ucYMax;
    pHDMICoreReg->HDMI_YMIN = ucYMin;
    pHDMICoreReg->HDMI_CMAX = ucCMax;
    pHDMICoreReg->HDMI_CMIN = ucCMin;
    
    return TRUE; 
}

// set speaker allocation information 
BOOL HDMISetSpeakerAllocation(BYTE ucInfo)
{
    // check core register 
    if (pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set AUI Byte    
    pHDMICoreReg->HDMI_AUI_BYTE4 = ucInfo;
    
    return TRUE;
}

/**
 * HDCP related API
 */
 
// start processing HDCP authentication
BOOL HDCPStart(VOID)
{

    RETAILMSG(HDMI_ZONE,(_T("++ %s \n\r"),TEXT(__FUNCTION__)));

    // check mapped registers
    if (pHDMISSReg == NULL || pHDMICoreReg == NULL 
            || pHDMIHDCPReg == NULL || pHDMIEfuseReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] registers are not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // initialize DDC library    
    if (DDCInit() != TRUE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] can not initialize DDC library!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // reset hdcp for sure
    HDCPReset();
    
    
    // load hdcp key
    if ( HDCPLoadKey() == FALSE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Can not load HDCP key!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }

    // unmask HDCP int in interrupt controller
    pHDMISSReg->HDMI_INTC_CON |= (1<<HDMI_IRQ_HDCP| 1<<HDMI_IRQ_GLOBAL);
    
	// enable interrupt
	pHDMICoreReg->HDMI_STATUS_EN |= ((1<<HDCP_I2C_INT_NUM) | (1<<HDCP_WATCHDOG_INT_NUM) | (1<<HDCP_AN_WRITE_INT_NUM) | (1<<HDCP_UPDATE_RI_INT_NUM));

    // enable hdcp
    pHDMIHDCPReg->HDCP_CTRL1 = HDCP_ENABLE;
    
    // enable blue screen
    HDMISetBlueScreen(TRUE);

    RETAILMSG(HDMI_ZONE,(_T("-- %s \n\r"),TEXT(__FUNCTION__)));
    
    return TRUE;
}

// stop processing HDCP authentication
BOOL HDCPStop(VOID)
{
    // check mapped registers
    if (pHDMISSReg == NULL || pHDMIHDCPReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] registers are not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // finalize DDC library
    if (DDCDeInit() != TRUE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] can not finalize DDC library!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }

	// disable interrupt
	pHDMICoreReg->HDMI_STATUS_EN &= ~(HDCP_I2C_INT_NUM | HDCP_WATCHDOG_INT_NUM | HDCP_AN_WRITE_INT_NUM | HDCP_UPDATE_RI_INT_NUM);

    // mask HDCP int in interrupt controller
    pHDMISSReg->HDMI_INTC_CON &= ~(1<<HDMI_IRQ_HDCP);
    
    // disable hdcp
    pHDMIHDCPReg->HDCP_CTRL1 = HDCP_DISABLE;
    
    return TRUE;
}


/**
 * HPD related API
 */

// register HPD callback function 
BOOL HPDSetCallback(HPDCallBack* callbackfn)
{
    // check arguement
    if (callbackfn == NULL)
    { 
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HPD call back function is not valid!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    _handler = callbackfn;
    
    return TRUE;        
}

// get current HPD status
BYTE HPDGetStatus(VOID)
{
    if (pHDMISSReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Subsystem register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    if (pHDMISSReg->HDMI_HPD)
        return HPD_HI;
    else
        return HPD_LO;    
}

// start detecting HPD signal
BOOL HPDStart(VOID)
{
    // check subsystem register 
    if (pHDMISSReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Subsystem register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
   
    // unmask plug, unplug HPD interrupts
    pHDMISSReg->HDMI_INTC_CON |= (1<<HDMI_IRQ_GLOBAL | 1<<HDMI_IRQ_HPD_PLUG
                                        | 1<<HDMI_IRQ_HPD_UNPLUG);

	RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Subsystem register 0x%02X!!! \n\r"),TEXT(__FUNCTION__),pHDMISSReg->HDMI_INTC_CON));
	                                        
    return TRUE;
}

// stop detiecting HPD signal
BOOL HPDStop(VOID)
{
	RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s]!!! \n\r"),TEXT(__FUNCTION__)));
	
    // check subsystem register 
    if (pHDMISSReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Subsystem register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
       
    // mask plug, unplug HPD interrupts
    pHDMISSReg->HDMI_INTC_CON &= ~((1<<HDMI_IRQ_HPD_PLUG) | (1<<HDMI_IRQ_HPD_UNPLUG));
    
    return TRUE;
}

// set HDMI/DVI Mode
BOOL SetHDMIMode(HDMI_MODE mode)
{
    DWORD HdmiMode;
    DWORD HdmiCfg2;
    
    // check mode
    switch (mode)
    {
        case HDMI:
            HdmiMode = HDMI_MODE_SEL_HDMI;
            HdmiCfg2 = HDMICON2_HDMI;
            break;
        case DVI:
            HdmiMode = HDMI_MODE_SEL_DVI;
            HdmiCfg2 = HDMICON2_DVI;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }
    
    // set mode    
    pHDMICoreReg->HDMI_MODE_SEL = HdmiMode;
    pHDMICoreReg->HDMI_CON2 = HdmiCfg2;
    return TRUE;
}

BOOL SetColorDepth(COLOR_DEPTH cd)
{
    DWORD GcpByte2;
    DWORD HdmiDc;
    
    // check depth
    switch (cd)
    {
        case HDMI_CD_36:
            GcpByte2 = GCP_CD_36BPP;
            HdmiDc = HDMI_DC_CTL_12;
            break;
        case HDMI_CD_30:
            GcpByte2 = GCP_CD_30BPP;
            HdmiDc = HDMI_DC_CTL_10;
            break;
        case HDMI_CD_24:
            GcpByte2 = GCP_CD_24BPP;
            HdmiDc = HDMI_DC_CTL_8;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }
    // set color depth
    pHDMICoreReg->HDMI_GCP_BYTE2 = GcpByte2;
    pHDMICoreReg->HDMI_DC_CONTROL = HdmiDc;
        
    return TRUE;
}

// set colorspace in AVI packet and set pixel limit to limited range
BOOL SetColorSpace(COLOR_SPACE cs)
{
    DWORD AviByte1;

    pHDMICoreReg->HDMI_CON1 &= ~HDMICON1_LIMIT_MASK;

    // check color space
    switch (cs)
    {                 
        case HDMI_CS_YCBCR422:
//TODO: check if YCBCR422 mode, we should set TG H/W            
            AviByte1 = AVI_CS_Y422;
            pHDMICoreReg->HDMI_CON1 |= HDMICON1_YCBCR_LIMIT;
            break;
        case HDMI_CS_YCBCR444:
            AviByte1 = AVI_CS_Y444;
            pHDMICoreReg->HDMI_CON1 |= HDMICON1_YCBCR_LIMIT;
            break;
        case HDMI_CS_RGB:
            AviByte1 = AVI_CS_RGB;
            pHDMICoreReg->HDMI_CON1 |= HDMICON1_RGB_LIMIT;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;     
    }
     
    // set color space
    // clear YY field in AVIByte1
    pHDMICoreReg->HDMI_AVI_BYTE1 &= ~(AVI_CS_Y422|AVI_CS_Y444);
    // set YY field in AVIByte1
    pHDMICoreReg->HDMI_AVI_BYTE1 |= (AviByte1);     
    
    // pixel limit as limit
    pHDMICoreReg->HDMI_AVI_BYTE3 &= ~AVI_QUANTIZATION_MASK;
    pHDMICoreReg->HDMI_AVI_BYTE3 |= AVI_QUANTIZATION_LIMITED;
    
    return TRUE;    
}

BOOL SetVideoResolution(VIDEO_FORMAT format, PIXEL_ASPECT_RATIO ratio)
{              
    // check argument
    if ( (format >= sizeof(HDMIVideoParams)/sizeof(struct hdmi_video_params)) || 
        (ratio != HDMI_PIXEL_RATIO_4_3 && ratio != HDMI_PIXEL_RATIO_16_9) )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }    
        
    // set HBLANK;
    pHDMICoreReg->HDMI_H_BLANK0 = (HDMIVideoParams[format].HBlank &0xFF);
    pHDMICoreReg->HDMI_H_BLANK1 = ((HDMIVideoParams[format].HBlank>>8) & 0xFF);

    // set VBlank
    pHDMICoreReg->HDMI_V_BLANK0 = (HDMIVideoParams[format].VBlank & 0xFF);
    pHDMICoreReg->HDMI_V_BLANK1 = ((HDMIVideoParams[format].VBlank>>8) & 0xFF);
    pHDMICoreReg->HDMI_V_BLANK2 = ((HDMIVideoParams[format].VBlank>>16) & 0xFF);
    
    // set HVLine
    pHDMICoreReg->HDMI_H_V_LINE0 = (HDMIVideoParams[format].HVLine & 0xFF);
    pHDMICoreReg->HDMI_H_V_LINE1 = ((HDMIVideoParams[format].HVLine>>8) & 0xFF);
    pHDMICoreReg->HDMI_H_V_LINE2 = ((HDMIVideoParams[format].HVLine>>16) & 0xFF);
    
    // set VSync Polarity
    pHDMICoreReg->HDMI_VSYNC_POL = HDMIVideoParams[format].polarity;

    // set HSyncGen
    pHDMICoreReg->HDMI_H_SYNC_GEN0 = (HDMIVideoParams[format].HSYNCGEN & 0xFF);
    pHDMICoreReg->HDMI_H_SYNC_GEN1 = ((HDMIVideoParams[format].HSYNCGEN>>8) & 0xFF);
    pHDMICoreReg->HDMI_H_SYNC_GEN2 = ((HDMIVideoParams[format].HSYNCGEN>>16) & 0xFF);

    // set VSyncGen1
    pHDMICoreReg->HDMI_V_SYNC_GEN1_0 = (HDMIVideoParams[format].VSYNCGEN & 0xFF);
    pHDMICoreReg->HDMI_V_SYNC_GEN1_1 = ((HDMIVideoParams[format].VSYNCGEN>>8) & 0xFF);
    pHDMICoreReg->HDMI_V_SYNC_GEN1_2 = ((HDMIVideoParams[format].VSYNCGEN>>16) & 0xFF);

    // set interlace or progresive format
    pHDMICoreReg->HDMI_INT_PRO_MODE = HDMIVideoParams[format].interlaced;

    if ( HDMIVideoParams[format].interlaced ) // interlaced format
    {
        // set VBlank_F
        pHDMICoreReg->HDMI_V_BLANK_F0 = (HDMIVideoParams[format].VBLANK_F & 0xFF);
        pHDMICoreReg->HDMI_V_BLANK_F1 = ((HDMIVideoParams[format].VBLANK_F>>8) & 0xFF);
        pHDMICoreReg->HDMI_V_BLANK_F2 = ((HDMIVideoParams[format].VBLANK_F>>16) & 0xFF);

        // set VSyncGen2
        pHDMICoreReg->HDMI_V_SYNC_GEN2_0 = (HDMIVideoParams[format].VSYNCGEN2 & 0xFF);
        pHDMICoreReg->HDMI_V_SYNC_GEN2_1 = ((HDMIVideoParams[format].VSYNCGEN2>>8) & 0xFF);
        pHDMICoreReg->HDMI_V_SYNC_GEN2_2 = ((HDMIVideoParams[format].VSYNCGEN2>>16) & 0xFF);

        // set VSyncGen3
        pHDMICoreReg->HDMI_V_SYNC_GEN3_0 = (HDMIVideoParams[format].VSYNCGEN3 & 0xFF);
        pHDMICoreReg->HDMI_V_SYNC_GEN3_1 = ((HDMIVideoParams[format].VSYNCGEN3>>8) & 0xFF);
        pHDMICoreReg->HDMI_V_SYNC_GEN3_2 = ((HDMIVideoParams[format].VSYNCGEN3>>16) & 0xFF);
    }
    else
    {
        // set VBlank_F with default value
        pHDMICoreReg->HDMI_V_BLANK_F0 = 0x00;
        pHDMICoreReg->HDMI_V_BLANK_F1 = 0x00;
        pHDMICoreReg->HDMI_V_BLANK_F2 = 0x00;

        // set VSyncGen2 with default value                          
        pHDMICoreReg->HDMI_V_SYNC_GEN2_0 = 0x01;
        pHDMICoreReg->HDMI_V_SYNC_GEN2_1 = 0x10;
        pHDMICoreReg->HDMI_V_SYNC_GEN2_2 = 0x00;

        // set VSyncGen3 with default value
        pHDMICoreReg->HDMI_V_SYNC_GEN3_0 = 0x01;
        pHDMICoreReg->HDMI_V_SYNC_GEN3_1 = 0x10;
        pHDMICoreReg->HDMI_V_SYNC_GEN3_2 = 0x00;
    }

    // set pixel repetition
    if ( HDMIVideoParams[format].repetition )
    {
        // set pixel repetition
        pHDMICoreReg->HDMI_CON1 |= HDMICON1_DOUBLE_PIXEL_REPETITION;
        // set avi packet
        pHDMICoreReg->HDMI_AVI_BYTE5 = AVI_PIXEL_REPETITION_DOUBLE;
    }
    else
    {
        // clear pixel repetition
        pHDMICoreReg->HDMI_CON1 &= ~(1<<1|1<<0);
        // set avi packet
        pHDMICoreReg->HDMI_AVI_BYTE5 = 0x00;
    }

    // set pixel aspect ratio
    // clear AVI packet field related to pixel aspect ratio
    pHDMICoreReg->HDMI_AVI_BYTE2 &= ~(AVI_PICTURE_ASPECT_RATIO_4_3|AVI_PICTURE_ASPECT_RATIO_16_9);                                    
    // set AVI packet with VIC
    if ( ratio == HDMI_PIXEL_RATIO_4_3 )                                 
    {
        pHDMICoreReg->HDMI_AVI_BYTE4 = HDMIVideoParams[format].AVI_VIC; 
        pHDMICoreReg->HDMI_AVI_BYTE2 |= AVI_PICTURE_ASPECT_RATIO_4_3; 
    }
    else  // 16:9
    {
        pHDMICoreReg->HDMI_AVI_BYTE4 = HDMIVideoParams[format].AVI_VIC_16_9;     
        pHDMICoreReg->HDMI_AVI_BYTE2 |= AVI_PICTURE_ASPECT_RATIO_16_9; 
    }
    
    return TRUE;
}

// set colorimetry
BOOL SetColorimetry(HDMI_COLORMETRY colorimetry)
{
    // clear 
    pHDMICoreReg->HDMI_AVI_BYTE2 &= ~AVI_COLORIMETRY_MASK;
    pHDMICoreReg->HDMI_AVI_BYTE3 &= ~AVI_COLORIMETRY_EXT_MASK;
    
    switch (colorimetry)
    {
        case HDMI_COLORIMETRY_NO_DATA:
            break;

        case HDMI_COLORIMETRY_ITU601:
            pHDMICoreReg->HDMI_AVI_BYTE2 |= AVI_COLORIMETRY_ITU601;
            break;

        case HDMI_COLORIMETRY_ITU709:
            pHDMICoreReg->HDMI_AVI_BYTE2 |= AVI_COLORIMETRY_ITU709;
            break;

        case HDMI_COLORIMETRY_EXTENDED_xvYCC601:
            pHDMICoreReg->HDMI_AVI_BYTE2 |= AVI_COLORIMETRY_EXTENDED;
            pHDMICoreReg->HDMI_AVI_BYTE3 |= AVI_COLORIMETRY_EXT_xvYCC601;
            break;

        case HDMI_COLORIMETRY_EXTENDED_xvYCC709:
            pHDMICoreReg->HDMI_AVI_BYTE2 |= AVI_COLORIMETRY_EXTENDED;
            pHDMICoreReg->HDMI_AVI_BYTE3 |= AVI_COLORIMETRY_EXT_xvYCC709;
            break;

        default:
            return FALSE;
    }
    return TRUE;
}

// set Sampling frequency field in AUI packet and set ACR packet
BOOL SetAudioSampleFreq(SAMPLEING_FREG freq)
{
    // check param
    if ( freq >= sizeof(ACR_N_VALUE)/sizeof(DWORD) || freq < 0 )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set ACR packet
    // set N value
    pHDMICoreReg->HDMI_ACR_N0 = ACR_N_VALUE[freq] & 0xFF;
    pHDMICoreReg->HDMI_ACR_N1 = (ACR_N_VALUE[freq]>>8) & 0xFF;
    pHDMICoreReg->HDMI_ACR_N2 = (ACR_N_VALUE[freq]>>16) & 0xFF;
    
    // set as measure cts mode
    pHDMICoreReg->HDMI_ACR_CON = ACR_MEASURED_CTS_MODE;    

    // set AUI packet
    pHDMICoreReg->HDMI_AUI_BYTE2 &= ~HDMI_AUI_SF_MASK;
    pHDMICoreReg->HDMI_AUI_BYTE2 |= AUI_SF_VALUE[freq];
    
    return TRUE;
}

VOID ClearAUIAudioSampleFreq(VOID)
{
    pHDMICoreReg->HDMI_AUI_BYTE2 &= ~HDMI_AUI_SF_MASK;
}

// set audio number of channels in AUI packet
BOOL SetAudioNumberOfChannels(CHANNEL_NUM num)
{
    BYTE reg;
    // clear field
    pHDMICoreReg->HDMI_ASP_CON &= ~(ASP_MODE_MASK|ASP_SP_MASK);

    // set layout & SP_PRESENT on ASP_CON
    // set AUI Packet
    switch (num)
    {
        case CH_2:
            reg = (ASP_LAYOUT_0|ASP_SP_0);
            pHDMICoreReg->HDMI_AUI_BYTE1 = AUI_CC_2CH;
            break;
        case CH_3:
            reg = (ASP_LAYOUT_1|ASP_SP_0|ASP_SP_1);
            pHDMICoreReg->HDMI_AUI_BYTE1 = AUI_CC_3CH;
            break;
        case CH_4:
            reg = (ASP_LAYOUT_1|ASP_SP_0|ASP_SP_1);
            pHDMICoreReg->HDMI_AUI_BYTE1 = AUI_CC_4CH;
            break;
        case CH_5:
            reg = (ASP_LAYOUT_1|ASP_SP_0|ASP_SP_1|ASP_SP_2);
            pHDMICoreReg->HDMI_AUI_BYTE1 = AUI_CC_5CH;
            break;
        case CH_6:
            reg = (ASP_LAYOUT_1|ASP_SP_0|ASP_SP_1|ASP_SP_2);
            pHDMICoreReg->HDMI_AUI_BYTE1 = AUI_CC_6CH;
            break;
        case CH_7:
            reg = (ASP_LAYOUT_1|ASP_SP_0|ASP_SP_1|ASP_SP_2|ASP_SP_3);
            pHDMICoreReg->HDMI_AUI_BYTE1 = AUI_CC_7CH;
            break;
        case CH_8:
            reg = (ASP_LAYOUT_1|ASP_SP_0|ASP_SP_1|ASP_SP_2|ASP_SP_3);
            pHDMICoreReg->HDMI_AUI_BYTE1 = AUI_CC_8CH;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }
    pHDMICoreReg->HDMI_ASP_CON |= reg;

    return TRUE;
}

// set audio sample packet type
BOOL SetAudioPacketType(HDMI_ASP_TYPE type)
{
    BYTE reg;
    
    // clear field
    pHDMICoreReg->HDMI_ASP_CON &= ~ASP_TYPE_MASK;

    switch (type)
    {
        case HDMI_ASP:
            reg = ASP_LPCM_TYPE;
            break;
        case HDMI_DSD:
            reg = ASP_DSD_TYPE;
            break;
        case HDMI_HBR:
            reg = ASP_HBR_TYPE;
            break;
        case HDMI_DST:
            reg = ASP_DST_TYPE;
            break;
        default:
            RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
    }
    pHDMICoreReg->HDMI_ASP_CON |= reg;
    
    return TRUE;
}    

VOID UpdateAUIChecksum(VOID)
{
    BYTE index, checksum = AUI_HEADER;
    PDWORD pBaseAddr = &(pHDMICoreReg->HDMI_AUI_BYTE1);
    
    for (index = 0; index < AUI_PACKET_BYTE_LENGTH; index++)
    {
        // when write this byte(PB5), HW shift 3 bit to right direction.
        // to compensate it, when read it, SW should shift 3 bit to left.
        if (index == 4)
            checksum += (BYTE)(*(pBaseAddr + index)<<3);
        else
            checksum += (BYTE)(*(pBaseAddr + index));
    }
    pHDMICoreReg->HDMI_AUI_CHECK_SUM = ~checksum+1;
}

VOID UpdateAVIChecksum(VOID)
{
    BYTE index, checksum = AVI_HEADER;
    PDWORD pBaseAddr = &(pHDMICoreReg->HDMI_AVI_BYTE1);
    
    for (index = 0; index < AVI_PACKET_BYTE_LENGTH; index++)
    {
            checksum += (BYTE)(*(pBaseAddr + index));
    }
    pHDMICoreReg->HDMI_AVI_CHECK_SUM = ~checksum+1;
}

// HPD event handler
BOOL HPDEventHandler(VOID)
{
    DWORD flag;
    
    RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s]!!! \n\r"),TEXT(__FUNCTION__)));
    // check mapped registers
    if (pHDMISSReg == NULL || pHDMICoreReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Subsystem or Core register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    // check the HPD status
    /* workaround: ignore HPD IRQ caused by reseting HDCP engine */
    if (pHDMICoreReg->HDMI_HPD & HPD_SW_ENABLE) {
        // disable SW HPD
        pHDMICoreReg->HDMI_HPD = HPD_SW_DISABLE;
        
        // clear interrupt pending bit
        pHDMISSReg->HDMI_INTC_FLAG |= (1<<HDMI_IRQ_HPD_UNPLUG | 1<<HDMI_IRQ_HPD_PLUG);
        
        return TRUE;
    }

    flag = pHDMISSReg->HDMI_INTC_FLAG & (1<<HDMI_IRQ_HPD_PLUG | 1<<HDMI_IRQ_HPD_UNPLUG);

    // happen both at the same time
    if (flag == (1<<HDMI_IRQ_HPD_PLUG | 1<<HDMI_IRQ_HPD_UNPLUG)) {
        if ( last_hpd_state == HPD_HI && pHDMISSReg->HDMI_HPD )
            flag = 1<<HDMI_IRQ_HPD_UNPLUG;
        else
            flag = 1<<HDMI_IRQ_HPD_PLUG;
    }
    
    if (flag & (1<<HDMI_IRQ_HPD_PLUG)) {
        // clear pending bit
        pHDMISSReg->HDMI_INTC_FLAG |= 1<<HDMI_IRQ_HPD_PLUG;
        last_hpd_state = HPD_HI;
        // enable only HDMI_IRQ_HPD_UNPLUG interrupt
        pHDMISSReg->HDMI_INTC_CON &= ~(1<<HDMI_IRQ_HPD_PLUG);
        pHDMISSReg->HDMI_INTC_CON |= 1<<HDMI_IRQ_HPD_UNPLUG;
        
        RETAILMSG(FALSE,(_T("[%s] clear pending for HPD PLUG, Interrupt Enable = 0x%02X, Interrupt Status = 0x%02X \n\r"),TEXT(__FUNCTION__), pHDMISSReg->HDMI_INTC_CON, pHDMISSReg->HDMI_INTC_FLAG));
    } else if (flag & (1<<HDMI_IRQ_HPD_UNPLUG)) {
        // clear pending bit
        pHDMISSReg->HDMI_INTC_FLAG |= 1<<HDMI_IRQ_HPD_UNPLUG;
        last_hpd_state = HPD_LO;
        // enable only HDMI_IRQ_HPD_PLUG interrupt
        pHDMISSReg->HDMI_INTC_CON &= ~(1<<HDMI_IRQ_HPD_UNPLUG);
        pHDMISSReg->HDMI_INTC_CON |= 1<<HDMI_IRQ_HPD_PLUG;
        RETAILMSG(FALSE,(_T("[%s] clear pending for HPD UNPLUG, Interrupt Enable = 0x%02X, Interrupt Status = 0x%02X \n\r"),TEXT(__FUNCTION__), pHDMISSReg->HDMI_INTC_CON, pHDMISSReg->HDMI_INTC_FLAG));
    }

    // execute call-back ft
    if (_handler == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HPD callback ft is not valid!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }   
    
    _handler(last_hpd_state);
    
    // return 
    return TRUE;
}

// HDCP event handler
BOOL HDCPEventHandler(VOID)
{
    DWORD hdcp_int_flag;
    
    // check mapped registers
    if (pHDMICoreReg == NULL || pHDMIHDCPReg == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMI Core or HDCP register is not mapped!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }

    // check state
    hdcp_int_flag = (pHDMICoreReg->HDMI_STATUS) & HDCP_INT_MASK;
    
    // clear pending first
    pHDMICoreReg->HDMI_STATUS = hdcp_int_flag;
    pHDMIHDCPReg->HDCP_I2C_INT      = 0x00;
    pHDMIHDCPReg->HDCP_AN_INT       = 0x00;
    pHDMIHDCPReg->HDCP_WATCHDOG_INT = 0x00;
    pHDMIHDCPReg->HDCP_RI_INT       = 0x00;
        
    while (hdcp_int_flag)
    {
        // handle hdcp event
        DWORD retry = 0;
        // I2C INT
        // if I2C INT happen, start authentication from beginning
        if (hdcp_int_flag & (1<<HDCP_I2C_INT_NUM) )
        {
            // Bcaps
            BYTE Bcaps, Bksv[HDCP_KSV_SIZE];
            
            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] HDCP_I2C_INT_NUM !!! \n\r"),TEXT(__FUNCTION__)));
            
            // clear all flag, start from beginning
            hdcp_int_flag = 0;
                            
            // read Bcaps and Bksv
            if ( DDCRead(HDCP_RX_DEV_ADDR,HDCP_BCAPS_OFFSET,1,&Bcaps) && 
                DDCRead(HDCP_RX_DEV_ADDR,HDCP_BKSV_OFFSET,HDCP_KSV_SIZE,Bksv) )
            {
                DWORD index;
                PDWORD baseAddr;
                
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Bcaps[0x%x] \n\r"),Bcaps));
                
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Bksv[0][0x%x] \n\r"),Bksv[0]));
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Bksv[1][0x%x] \n\r"),Bksv[1]));
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Bksv[2][0x%x] \n\r"),Bksv[2]));
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Bksv[3][0x%x] \n\r"),Bksv[3]));
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Bksv[4][0x%x] \n\r"),Bksv[4]));
                
                // Set Bcaps
                pHDMIHDCPReg->HDCP_BCAPS = (DWORD)Bcaps;
                
                // Set Bksv
                for (index=0,baseAddr = &(pHDMIHDCPReg->HDCP_BKSV_0); 
                        index < HDCP_KSV_SIZE; index++)
                {
                    *(baseAddr+index) = (DWORD)Bksv[index];
                }

                // set state
                hdcp_state = HDCP_STATE_BKSV_READ_DONE;
                
                retry = 0;                        
            }
            else // if fail
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Fail to Read BCAPS and BKSV !!! \n\r"),TEXT(__FUNCTION__)));
                
                // change to idle
                hdcp_state = HDCP_STATE_FATAL;
                
                // check HPD is low, do not try infinitely
                if (retry++ > 2 )
                {
                    // if HPD is off
                    if (pHDMISSReg->HDMI_HPD == HPD_LO)
                    {
                        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Current HPD is Low  !!! \n\r"),TEXT(__FUNCTION__)));
                        return TRUE;
                    }
                }
                
                // sleep for 100 miliseconds
                Sleep(100);
            }                        
        } // if (hdcp_int_flag & (1<<HDCP_I2C_INT_NUM) )
        // AN INT
        else if ( hdcp_int_flag & (1<<HDCP_AN_WRITE_INT_NUM) )
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] HDCP_AN_WRITE_INT_NUM !!! \n\r"),TEXT(__FUNCTION__)));
            
            // clear hdcp_int_flag
            hdcp_int_flag ^= (1<<HDCP_AN_WRITE_INT_NUM);
            
            if ( hdcp_state == HDCP_STATE_BKSV_READ_DONE )
            {
                BYTE Aksv[HDCP_KSV_SIZE],An[HDCP_AN_SIZE],Bcaps;
                DWORD index;
                PDWORD baseAddr;
                
                // get An
                for (index=0,baseAddr = &(pHDMIHDCPReg->HDCP_AN_0); 
                            index < HDCP_AN_SIZE; index++)
                {
                    An[index] = (BYTE)*(baseAddr+index);
                }
                
                // get Aksv
                for (index=0,baseAddr = &(pHDMIHDCPReg->HDCP_AKSV_0); 
                            index < HDCP_KSV_SIZE; index++)
                {
                    Aksv[index] = (BYTE)*(baseAddr+index);
                }
                
                // write Aksv & An
                if ( /*!DDCWrite(HDCP_RX_DEV_ADDR,HDCP_AKSV_OFFSET,HDCP_KSV_SIZE,Aksv) ||*/ //CommentTemp Dummy Write
                    !DDCWrite(HDCP_RX_DEV_ADDR,HDCP_AN_OFFSET,HDCP_AN_SIZE+1,An) ||
                    !DDCWrite(HDCP_RX_DEV_ADDR,HDCP_AKSV_OFFSET,HDCP_KSV_SIZE+1,Aksv) ||
                    !DDCRead(HDCP_RX_DEV_ADDR,HDCP_BCAPS_OFFSET,1,&Bcaps) || 
                    !DDCRead(HDCP_RX_DEV_ADDR,HDCP_BKSV_OFFSET,HDCP_KSV_SIZE,Aksv) )
                { // if fail
                    
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Fail to Write An and AKSV !!! \n\r"),TEXT(__FUNCTION__)));

                    hdcp_state = HDCP_STATE_FATAL;
                }
                else // if success
                {
                    // sleep for 100 miliseconds
                    Sleep(100);
                    
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[0][0x%x] \n\r"),An[0]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[1][0x%x] \n\r"),An[1]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[2][0x%x] \n\r"),An[2]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[3][0x%x] \n\r"),An[3]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[4][0x%x] \n\r"),An[4]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[0][0x%x] \n\r"),An[5]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[1][0x%x] \n\r"),An[6]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] An[2][0x%x] \n\r"),An[7]));

/*
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Aksv[0][0x%x] \n\r"),Aksv[0]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Aksv[1][0x%x] \n\r"),Aksv[1]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Aksv[2][0x%x] \n\r"),Aksv[2]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Aksv[3][0x%x] \n\r"),Aksv[3]));
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: ] Aksv[4][0x%x] \n\r"),Aksv[4]));
*/                
                    // set state
                    hdcp_state = HDCP_STATE_AKSV_WRITE_DONE;  

                }
            } // if ( hdcp_state == HDCP_STATE_BKSV_READ_DONE )
            else // hdcp_state is not HDCP_STATE_BKSV_READ_DONE
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] HDCP_AN_WRITE_INT_NUM but state is not correct(%d)!!! \n\r"),TEXT(__FUNCTION__),hdcp_state));
                // change to idle
                hdcp_state = HDCP_STATE_FATAL;
            }
        } // if ( hdcp_int_flag & (1<<HDCP_AN_WRITE_INT_NUM) )
        
        // RI INT
        else if ( hdcp_int_flag & (1<<HDCP_UPDATE_RI_INT_NUM) )
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] HDCP_UPDATE_RI_INT_NUM !!! \n\r"),TEXT(__FUNCTION__)));
            // clear hdcp_int_flag
            hdcp_int_flag ^= (1<<HDCP_UPDATE_RI_INT_NUM);
            
            if ( hdcp_state == HDCP_STATE_AKSV_WRITE_DONE
                    || hdcp_state == HDCP_STATE_THIRD_AUTH )
            {
                BYTE Ri[HDCP_RI_SIZE], Rj[HDCP_RI_SIZE];
		BOOL CompareRjRi, DDCReadResult;
		int ii;

		CompareRjRi = FALSE;
                
                memset(Ri,'\0',HDCP_RI_SIZE);
                memset(Rj,'\0',HDCP_RI_SIZE);
                
		for(ii = 0; ii < HDCP_RIRJ_COMPARE_NUM; ii++)
                {
                    DDCReadResult = DDCRead(HDCP_RX_DEV_ADDR, HDCP_RI_OFFSET, HDCP_RI_SIZE, Rj);

                    // compare Ri
                    Ri[0] = (BYTE)(pHDMIHDCPReg->HDCP_RI_0);
                    Ri[1] = (BYTE)(pHDMIHDCPReg->HDCP_RI_1);


                    if ( (Ri[0] == Rj[0]) && (Ri[1] == Rj[1]) && (Ri[0] | Ri[1]) )
                    {
                        CompareRjRi = TRUE;
                        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] RI MATCHED Ri = [0x%x][0x%x], Rj = [0x%x][0x%x]\n\r"),TEXT(__FUNCTION__),Ri[0],Ri[1],Rj[0],Rj[1]));
                    }
                    else
                    {
                        CompareRjRi = FALSE;
                    }

			if(CompareRjRi)
				break;
		}
				
                // read Rj
				if(DDCReadResult)
                {
					if(CompareRjRi)
                    { // Ri and Rj are matched
                        //RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] RI MATCHED !!! \n\r"),TEXT(__FUNCTION__)));
            //RETAILMSG(0,(_T("[HDCP: %s] RI MATCHED !!! \n\r"),TEXT(__FUNCTION__)));                        
                        
                        if ( hdcp_state == HDCP_STATE_AKSV_WRITE_DONE )
                        {
                            // set the HDCP result register as match
                            pHDMIHDCPReg->HDCP_CHECK_RESULT = HDCP_RI_MATCH;

                            RETAILMSG(HDMI_ZONE,(_T("HDCP_CHECK_RESULT 0x%x\n\r"),pHDMIHDCPReg->HDCP_CHECK_RESULT));
                            RETAILMSG(HDMI_ZONE,(_T("HDMI_STATUS 0x%x\n\r"),pHDMICoreReg->HDMI_STATUS));

                            // clear Result
                            pHDMIHDCPReg->HDCP_CHECK_RESULT = 0x00;
                            
                            // if repeater 
                            if ( (pHDMIHDCPReg->HDCP_BCAPS) & (1<<HDCP_BCAPS_REPEATER) )
                            {
                                RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Repeater !!! \n\r"),TEXT(__FUNCTION__)));
                                hdcp_state = HDCP_STATE_SECOND_AUTH_START;
                            }
                            else // not repeater
                            {
                                // check authentication done
                                if ( (pHDMICoreReg->HDMI_STATUS) & (1<<HDCP_AUTHEN_ACK_NUM) )
                                {
                                    // set start encryption
                                    HDCPEnableEncryption(TRUE);

                                    hdcp_state = HDCP_STATE_THIRD_AUTH;
                                }
                                else // authentication not done
                                {
                                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Authentication is not acknowledged !!! \n\r"),TEXT(__FUNCTION__)));

                                    hdcp_state = HDCP_STATE_FATAL;
                                }
                            } // else repeater
                        } // if ( hdcp_state == HDCP_STATE_AKSV_WRITE_DONE )
                        // third authentication, do nothing
                    }
                    else // Ri and Rj are not matched
                    {
                        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] RI NOT MATCHED Ri = [0x%x][0x%x], Rj = [0x%x][0x%x]\n\r"),TEXT(__FUNCTION__),Ri[0],Ri[1],Rj[0],Rj[1]));
                        RETAILMSG(0,(_T("[HDCP: %s] RI NOT MATCHED Ri = 0x%x%x, Rj = 0x%x%x\n\r"),TEXT(__FUNCTION__),Ri[0],Ri[1],Rj[0],Rj[1]));
/*
                        RETAILMSG(HDMI_ZONE,(_T("An_0 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_0));
                        RETAILMSG(HDMI_ZONE,(_T("An_1 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_1));
                        RETAILMSG(HDMI_ZONE,(_T("An_2 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_2));
                        RETAILMSG(HDMI_ZONE,(_T("An_3 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_3));
                        RETAILMSG(HDMI_ZONE,(_T("An_4 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_4));
                        RETAILMSG(HDMI_ZONE,(_T("An_5 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_5));
                        RETAILMSG(HDMI_ZONE,(_T("An_6 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_6));
                        RETAILMSG(HDMI_ZONE,(_T("An_7 0x%x\n\r"),pHDMIHDCPReg->HDCP_AN_7));	

                        RETAILMSG(HDMI_ZONE,(_T("AKSV_0 0x%x\n\r"),pHDMIHDCPReg->HDCP_AKSV_0));
                        RETAILMSG(HDMI_ZONE,(_T("AKSV_1 0x%x\n\r"),pHDMIHDCPReg->HDCP_AKSV_1));
                        RETAILMSG(HDMI_ZONE,(_T("AKSV_2 0x%x\n\r"),pHDMIHDCPReg->HDCP_AKSV_2));
                        RETAILMSG(HDMI_ZONE,(_T("AKSV_3 0x%x\n\r"),pHDMIHDCPReg->HDCP_AKSV_3));
                        RETAILMSG(HDMI_ZONE,(_T("AKSV_4 0x%x\n\r"),pHDMIHDCPReg->HDCP_AKSV_4));

                        RETAILMSG(HDMI_ZONE,(_T("Bcaps 0x%x\n\r"),pHDMIHDCPReg->HDCP_BCAPS));

                        RETAILMSG(HDMI_ZONE,(_T("BKSV_0 0x%x\n\r"),pHDMIHDCPReg->HDCP_BKSV_0));
                        RETAILMSG(HDMI_ZONE,(_T("BKSV_1 0x%x\n\r"),pHDMIHDCPReg->HDCP_BKSV_1));
                        RETAILMSG(HDMI_ZONE,(_T("BKSV_2 0x%x\n\r"),pHDMIHDCPReg->HDCP_BKSV_2));
                        RETAILMSG(HDMI_ZONE,(_T("BKSV_3 0x%x\n\r"),pHDMIHDCPReg->HDCP_BKSV_3));
                        RETAILMSG(HDMI_ZONE,(_T("BKSV_4 0x%x\n\r"),pHDMIHDCPReg->HDCP_BKSV_4));
*/ 
                        
                        // set Ri_not_matched
                        pHDMIHDCPReg->HDCP_CHECK_RESULT = HDCP_RI_NOT_MATCH;
                        // clear Result
                        pHDMIHDCPReg->HDCP_CHECK_RESULT = 0x00;
                        
                        // if third auth
                        if ( hdcp_state == HDCP_STATE_THIRD_AUTH )                    
                        {
                            // disable encryption
                            HDCPEnableEncryption(FALSE);                                                
                        }
                                            
                        // state is idle
                        hdcp_state = HDCP_STATE_IDLE;
                        
                        // for SIMPLAYHD 7-11
                        Sleep(100);
                    } // else Ri and Rj are not matched
                }
                else // if Read Rj failed
                {
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Fail to Read Rj !!! \n\r"),TEXT(__FUNCTION__)));
                    // change to idle
                    hdcp_state = HDCP_STATE_FATAL;
                }
            } // if ( hdcp_state == HDCP_STATE_AKSV_WRITE_DONE 
              //        || hdcp_state == HDCP_STATE_THIRD_AUTH )
            else 
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] HDCP_UPDATE_RI_INT_NUM but state is not correct(%d)!!! \n\r"),TEXT(__FUNCTION__),hdcp_state));
                // change to idle
                hdcp_state = HDCP_STATE_FATAL;
            }
        } // if ( hdcp_int_flag & (1<<HDCP_UPDATE_RI_INT_NUM) )
        
        // WATCHDOG INT
        else if ( hdcp_int_flag & (1<<HDCP_WATCHDOG_INT_NUM) )
        {
            // clear hdcp_int_flag
            hdcp_int_flag ^= (1<<HDCP_WATCHDOG_INT_NUM);

            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] HDCP_WATCHDOG_INT_NUM !!! \n\r"),TEXT(__FUNCTION__)));
            
            if ( hdcp_state == HDCP_STATE_SECOND_AUTH_START )
            {
                DWORD error = REPEATER_ERROR_NOT_KNOWN;
                
                // check repeater
                if (HDCPCheckRepeater(&error))
                {
                    RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] CheckRepeater is Done \n\r"),TEXT(__FUNCTION__)));
                    HDCPEnableEncryption(TRUE);
                    hdcp_state = HDCP_STATE_THIRD_AUTH;                        
                }
                else // CheckRepeater failed
                {
                    if (error == REPEATER_ILLEGAL_DEVICE_ERROR)
                    {
                        // set
                        pHDMIHDCPReg->HDCP_CTRL2 = HDCP_REVOCATION_SET;
                        // clear
                        pHDMIHDCPReg->HDCP_CTRL2 = 0x00;
                        
                        hdcp_state = HDCP_STATE_IDLE;
                    }
                    else if (error == REPEATER_TIME_OUT_ERROR)
                    {
                    	//TODO: check!!!! 
                    	// Auth not start again.
                        pHDMIHDCPReg->HDCP_CTRL1 |= HDCP_TIMEOUT;
                        pHDMIHDCPReg->HDCP_CTRL1 &= ~HDCP_TIMEOUT;
                        hdcp_state = HDCP_STATE_IDLE;
                    }
                    else
                    {
                        hdcp_state = HDCP_STATE_FATAL;
                    }
                } // CheckRepeater failed
                
            } // if ( hdcp_state == HDCP_STATE_SECOND_AUTH_START )
            else
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] HDCP_WATCHDOG_INT_NUM but state is not correct(%d)!!! \n\r"),TEXT(__FUNCTION__),hdcp_state));
                hdcp_state = HDCP_STATE_FATAL;
            }
        } // if ( hdcp_int_flag & (1<<HDCP_WATCHDOG_INT_NUM) )
        else // if interrupt is not related to HDCP
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Interrupt is not related to HDCP(0x%x)!!! \n\r"),TEXT(__FUNCTION__),hdcp_int_flag));
            hdcp_state = HDCP_STATE_FATAL;
        }
        
        // if state is fatal, reset 
        if (hdcp_state == HDCP_STATE_FATAL)
        {
            hdcp_state = HDCP_STATE_IDLE;
            hdcp_int_flag = 0;
            HDCPReset();
        }    
    } // while (hdcp_int_flag)

    return TRUE;
}

// HDCP reset
VOID HDCPReset(VOID)
{
    // disable encryption 
    // because can be called at any time, even in HDCP Authentication state
    HDCPEnableEncryption(FALSE);
    
    // hpd off with S/W
    pHDMICoreReg->HDMI_HPD = HPD_SW_ENABLE|HPD_OFF;
    
    // hpd on with S/W
    pHDMICoreReg->HDMI_HPD = HPD_SW_ENABLE|HPD_ON;
}

// HDCP enable encryption (EESS signal)
VOID HDCPEnableEncryption(BOOL enable)
{
    if (enable)
    {
        // disable bluescreen
        HDMISetBlueScreen(FALSE);
        // enable encryption 
        pHDMICoreReg->HDMI_ENC_EN = HDMI_ENC_ENABLE;
    }
    else
    {
        // disable encryption
        pHDMICoreReg->HDMI_ENC_EN = HDMI_ENC_DISABLE;
        
        // enable bluescreen
        HDMISetBlueScreen(TRUE);
    }  
}

/**
 * Set the timing parameter for load e-fuse key
 */
BOOL HDCP_SetEfuseTiming(void)
{
/* an unused function

	UINT32 uTime, uValue;

//	uTime = 1000000000/g_uPclkDsys;
	uTime = 1000000000/ PCLKDSYS_CLK_BL;

	uValue = EFUSE_ADDR_WIDTH_VAL/uTime;

//Remove DEAD_CODE in prevent server
//	if(EFUSE_ADDR_WIDTH_VAL%uTime)
//		uValue += 1;		

	
//	HdcpOutp8(rEFUSE_ADDR_WIDTH, uValue);
	pHDMIEfuseReg->EFUSE_ADDR_WIDTH = uValue;

	uValue = EFUSE_SIGDEV_ASSERT_VAL/uTime;
	if(EFUSE_SIGDEV_ASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_SIGDEV_ASSERT, uValue);
	pHDMIEfuseReg->EFUSE_SIGDEV_ASSERT = uValue;

	uValue = EFUSE_SIGDEV_DEASSERT_VAL/uTime;
	if(EFUSE_SIGDEV_DEASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_SIGDEV_DEASSERT, uValue);
	pHDMIEfuseReg->EFUSE_SIGDEV_DEASSERT = uValue;

	uValue = EFUSE_PRCHG_ASSERT_VAL/uTime;
	if(EFUSE_PRCHG_ASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_PRCHG_ASSERT, uValue);
	pHDMIEfuseReg->EFUSE_PRCHG_ASSERT = uValue;

	uValue = EFUSE_PRCHG_DEASSERT_VAL/uTime;
	if(EFUSE_PRCHG_DEASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_PRCHG_DEASSERT, uValue);
	pHDMIEfuseReg->EFUSE_PRCHG_DEASSERT = uValue;

	uValue = EFUSE_FSET_ASSERT_VAL/uTime;
	if(EFUSE_FSET_ASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_FSET_ASSERT, uValue);
	pHDMIEfuseReg->EFUSE_FSET_ASSERT = uValue;

	uValue = EFUSE_FSET_DEASSERT_VAL/uTime;
	if(EFUSE_FSET_DEASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_FSET_DEASSERT, uValue);
	pHDMIEfuseReg->EFUSE_FSET_DEASSERT = uValue;

	uValue = EFUSE_SENSING_VAL/uTime;
	if(EFUSE_SENSING_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_SENSING, uValue);
	pHDMIEfuseReg->EFUSE_SENSING = uValue;

	uValue = EFUSE_SCK_ASSERT_VAL/uTime;
	if(EFUSE_SCK_ASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_SCK_ASSERT, uValue);
	pHDMIEfuseReg->EFUSE_SCK_ASSERT = uValue;

	uValue = EFUSE_SCK_DEASSERT_VAL/uTime;
	if(EFUSE_SCK_DEASSERT_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_SCK_DEASSERT, uValue);
	pHDMIEfuseReg->EFUSE_SCK_DEASSERT = uValue;

	uValue = EFUSE_SDOUT_OFFSET_VAL/uTime;
	if(EFUSE_SDOUT_OFFSET_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_SDOUT_OFFSET, uValue);
	pHDMIEfuseReg->EFUSE_SDOUT_OFFSET = uValue;

	uValue = EFUSE_READ_OFFSET_VAL/uTime;
	if(EFUSE_READ_OFFSET_VAL%uTime)
		uValue += 1;		
//	HdcpOutp8(rEFUSE_READ_OFFSET, uValue);
	pHDMIEfuseReg->EFUSE_READ_OFFSET = uValue;
*/

	return TRUE;
}


/**
 * Start AES decryption for HDCP Private Keys
 */
/*
// load HDCP key
BOOL HDCPLoadKey(VOID)
{
    //TODO: implement by using EFUSE
    
    return TRUE;
}
*/

BOOL HDCPLoadKey(void)
{
	UINT8 ucStatus;

//Use Default Value	
//	HDCP_SetEfuseTiming();

//	HdcpOutp8(rEFUSE_CTRL, 0x1);
	pHDMIEfuseReg->HDCP_E_FUSE_CTRL = 0x1;

	do
	{
//		ucStatus = HdcpInp8(rEFUSE_STATUS);
		ucStatus = pHDMIEfuseReg->HDCP_E_FUSE_STATUS;

	}while(!(ucStatus&EFUSE_ECC_DONE_VAL));

//	if(HdcpInp8(rEFUSE_STATUS) & EFUSE_ECC_FAIL)
	if(pHDMIEfuseReg->HDCP_E_FUSE_STATUS & EFUSE_ECC_FAIL_VAL)
	{
		return FALSE;
	}	

	return TRUE;
}


BOOL HDCPCheckRepeater(PDWORD pErrorCode)
{
    BYTE Bcaps, 
         Bstatus[2],
         dev_cnt,
         Sha1[HDCP_SHA1_SIZE],
         time = 0;
    
	DWORD index;
	PDWORD baseAddr;

    
    if (pErrorCode == NULL)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] not valid arguement !!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
         
    // read Bcaps and check ready bit
    while(time <= 50)
    {
        if (!DDCRead(HDCP_RX_DEV_ADDR,HDCP_BCAPS_OFFSET,1,&Bcaps))
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] fail to Read Bcaps!!! \n\r")TEXT(__FUNCTION__)));
            return FALSE;
        }
        
        if ( Bcaps & (1<<HDCP_BCAPS_READY) )
        {
            break;
        }
        else
        {
            Sleep(100);
            time++;
        }
    }
    
    // if timeout
    if ( !(Bcaps & (1<<HDCP_BCAPS_READY)) )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Bcaps is not ready. time out!!! \n\r"),TEXT(__FUNCTION__)));
        *pErrorCode = REPEATER_TIME_OUT_ERROR;
        return FALSE;
    }
    
    // set Bcaps
    pHDMIHDCPReg->HDCP_BCAPS = (DWORD)Bcaps;
    
    // read Bstatus and check status bit
    if (!DDCRead(HDCP_RX_DEV_ADDR,HDCP_BSTATUS_OFFSET,HDCP_BSTATUS_SIZE,Bstatus))
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] fail to Read Bstatus!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    if ( (Bstatus[0] & MAX_DEVS_EXCEEDED_MASK) || (Bstatus[1] & MAX_CASCADE_EXCEEDED_MASK) )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Bstatus exceeds max_devices or max_cascade!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }

    pHDMIHDCPReg->HDCP_BSTATUS_0 = (DWORD)Bstatus[0];
    pHDMIHDCPReg->HDCP_BSTATUS_1 = (DWORD)Bstatus[1];

    dev_cnt = Bstatus[0] & DEVICE_COUNT_MASK;
    
    // read ksv list and set
    
//TODO: implement checking revocation list
    // check_revocation_list(&list)
    // if fail, return REPEATER_ILLEGAL_DEVICE_ERROR

	RETAILMSG(HDMI_ZONE,(_T("dev_cnt = %d\n\r"),dev_cnt));

    if (dev_cnt)
    {
        PBYTE pKSVList = LocalAlloc(0,dev_cnt*HDCP_KSV_SIZE);
        BYTE cnt;
        if (pKSVList == NULL)
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] fail to allocate memory for KSV list!!! \n\r"),TEXT(__FUNCTION__)));
            return FALSE;
        }

        // read ksv list
        if (!DDCRead(HDCP_RX_DEV_ADDR,HDCP_KSV_LIST_OFFSET,HDCP_KSV_SIZE*dev_cnt,pKSVList))
        {
            RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] fail to read KSV_LIST from RX!!! \n\r"),TEXT(__FUNCTION__)));
            LocalFree(pKSVList);
            return FALSE;
        }
        
        // write before last one
        for (cnt = 0; cnt < dev_cnt-1; cnt++)
        {
            pHDMIHDCPReg->HDCP_KSV_LIST_0 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE];
            pHDMIHDCPReg->HDCP_KSV_LIST_1 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+1];
            pHDMIHDCPReg->HDCP_KSV_LIST_2 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+2];
            pHDMIHDCPReg->HDCP_KSV_LIST_3 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+3];
            pHDMIHDCPReg->HDCP_KSV_LIST_4 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+4];
/*
			RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_0 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_0));
			RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_1 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_1));
			RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_2 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_2));
			RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_3 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_3));
			RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_4 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_4));
*/

			Sleep(10);
//			RETAILMSG(HDMI_ZONE,(_T("Before Write Done = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_CON));
            pHDMIHDCPReg->HDCP_KSV_LIST_CON = HDCP_KSV_WRITE_DONE;
//			RETAILMSG(HDMI_ZONE,(_T("After Write Done = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_CON));


            //TODO: need to sleep?
            Sleep(10);

//			RETAILMSG(HDMI_ZONE,(_T("Before checking Read Done = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_CON));

            if (!(pHDMIHDCPReg->HDCP_KSV_LIST_CON & HDCP_KSV_LIST_READ_DONE))
            {
                RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] fail to write KSV_LIST!!! \n\r"),TEXT(__FUNCTION__)));
                return FALSE;
            }
			
        } // for(;;)

        pHDMIHDCPReg->HDCP_KSV_LIST_0 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE];
        pHDMIHDCPReg->HDCP_KSV_LIST_1 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+1];
        pHDMIHDCPReg->HDCP_KSV_LIST_2 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+2];
        pHDMIHDCPReg->HDCP_KSV_LIST_3 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+3];
        pHDMIHDCPReg->HDCP_KSV_LIST_4 = (DWORD)pKSVList[cnt*HDCP_KSV_SIZE+4];
/*
		RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_0 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_0));
		RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_1 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_1));
		RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_2 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_2));
		RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_3 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_3));
		RETAILMSG(HDMI_ZONE,(_T("HDCP_KSV_LIST_4 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_4));
*/
        
        // last ksv        
		Sleep(10);        
//        RETAILMSG(HDMI_ZONE,(_T("Before Write Done&End = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_CON));
        pHDMIHDCPReg->HDCP_KSV_LIST_CON = HDCP_KSV_LIST_END | HDCP_KSV_WRITE_DONE;
//        RETAILMSG(HDMI_ZONE,(_T("After Write Done&End = 0x%x\n\r"),pHDMIHDCPReg->HDCP_KSV_LIST_CON));

        LocalFree(pKSVList);
    }
    else // dev_cnt = 0
    {
        pHDMIHDCPReg->HDCP_KSV_LIST_CON = HDCP_KSV_LIST_EMPTY;
    }
    
    RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] Read and Write KSV list is Done!!! \n\r"),TEXT(__FUNCTION__)));
    
    // read Rx SHA value
    if (!DDCRead(HDCP_RX_DEV_ADDR,HDCP_RX_SHA1_OFFSET,HDCP_SHA1_SIZE,Sha1))
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDCP: %s] fail to read RX SHA1 from RX!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
	// Write SHA1_RX
	for (index=0,baseAddr = &(pHDMIHDCPReg->HDCP_SHA1_00); 
			index < HDCP_SHA1_SIZE; index++)
	{
		*(baseAddr+index) = (DWORD)Sha1[index];
	}
/*
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_00 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_00));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_01 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_01));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_02 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_02));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_03 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_03));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_10 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_10));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_11 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_11));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_12 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_12));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_13 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_13));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_20 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_20));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_21 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_21));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_22 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_22));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_23 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_23));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_30 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_30));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_31 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_31));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_32 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_32));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_33 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_33));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_40 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_40));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_41 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_41));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_42 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_42));
	RETAILMSG(HDMI_ZONE,(_T("HDCP_SHA1_43 = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA1_43));
*/
	
    // check if V is matched 
    if ( pHDMIHDCPReg->HDCP_SHA_RESULT == (HDCP_SHA1_VALID_READY|HDCP_SHA1_VALID) )
    {
        RETAILMSG(HDMI_ZONE,(_T("[IF]HDCP_SHA_RESULT = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA_RESULT));

        pHDMIHDCPReg->HDCP_SHA_RESULT = 0x00;
        return TRUE;
    }
    else 
    {
        RETAILMSG(HDMI_ZONE,(_T("[ELSE]HDCP_SHA_RESULT = 0x%x\n\r"),pHDMIHDCPReg->HDCP_SHA_RESULT));

        pHDMIHDCPReg->HDCP_SHA_RESULT = 0x00;
        return FALSE;
    }
}

static BOOL SetHDMIPHY(VIDEO_FORMAT format,COLOR_DEPTH cd)
{
	RETAILMSG(HDMI_ZONE,(_T("++ SetHDMIPHY\n\r")));

    // check argument
    if ( (format >= sizeof(HDMIVideoParams)/sizeof(struct hdmi_video_params)) )
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Invalid Arguement!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    
    // set clk off
    //MIXER SEL -> SCLK_HDMI
	pSysClkReg->CLK_SRC.CLK_SRC1 &= ~(BW_MUX_HDMI_SEL<<BP_MUX_HDMI_SEL);
	//DAC SEL -> SCLK VPLL
	pSysClkReg->CLK_SRC.CLK_SRC1 &= ~(BW_MUX_MIXER_SEL<<BP_MUX_MIXER_SEL);
	
    // set phy
    if (PHYConfig(HDMIVideoParams[format].PixelClock,cd) == FALSE)
    {
        RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] fail to config phy!!! \n\r"),TEXT(__FUNCTION__)));
        return FALSE;
    }
    

    HDMICoreReset();
	
    if(!HDMICheckPhyReady())
    {
		RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] HDMICheckPhyReady() fail \n\r"),TEXT(__FUNCTION__)));
		return FALSE;
    }

    // set clk on
    //HDMI SEL -> SCLK_HDMIPHY
	pSysClkReg->CLK_SRC.CLK_SRC1 |= (BW_MUX_HDMI_SEL<<BP_MUX_HDMI_SEL);
	//DAC SEL -> SCLK VPLL
	pSysClkReg->CLK_SRC.CLK_SRC1 |= (BW_MUX_MIXER_SEL<<BP_MUX_MIXER_SEL);
	
	RETAILMSG(HDMI_ZONE,(_T("-- SetHDMIPHY\n\r")));
	
	return TRUE;
}

BOOL SetHDMIPHYOnOff(BOOL bEn)
{
	return Hdmi_phy_OnOff(bEn);
}

BOOL HDMICheckPhyReady(void)
{
	int Loop;

	Loop = 0;

	while(!(pHDMICoreReg->HDMI_PHY_STATUS & HDMI_PHY_READY));
	{

		RETAILMSG(1,(_T("[HDMI: %s] 1 \n\r"),TEXT(__FUNCTION__)));

		if(Loop > 1000)
		{
			RETAILMSG(HDMI_ZONE,(_T("[HDMI: %s] Fail to hdmi phy on [pHDMICoreReg->HDMI_PHY_STATUS=0x%x]\n\r"),TEXT(__FUNCTION__),pHDMICoreReg->HDMI_PHY_STATUS));
			return FALSE;
		}
		
		Loop++;
		Sleep(1);		
	}

	RETAILMSG(1,(_T("[HDMI: %s] Successed to hdmi phy on [pHDMICoreReg->HDMI_PHY_STATUS=0x%x]\n\r"),TEXT(__FUNCTION__),pHDMICoreReg->HDMI_PHY_STATUS));
	
	return TRUE;
}

BOOL HDMICoreReset(void)
{

	pHDMISSReg->HDMI_CORE_RSTOUT = 0x0;
	Sleep(10);
	pHDMISSReg->HDMI_CORE_RSTOUT = 0x1;

	RETAILMSG(1,(_T("[HDMI: %s] pHDMISSReg->HDMI_CORE_RSTOUT=0x%x \n\r"),TEXT(__FUNCTION__),pHDMISSReg->HDMI_CORE_RSTOUT));

	return TRUE;
	
}

