/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 *
 * $Author: wenjin.peng $
 * $Date: 2017/05/18 $
 * $RCSfile: hdmi_earc_if.c,v $
 * $Revision:
 *
 *---------------------------------------------------------------------------*/
#include "x_typedef.h"
#include "x_timer.h"
#include "x_os.h"
#include "x_gpio.h"
#include "x_assert.h"

#include "general.h"
#include "hw_vdoin.h"
#include "hw_pdwnc.h"
#include "eeprom_if.h"
#include "drv_video.h"
#include "video_def.h"

#include "util.h"
#include "eeprom_if.h"
#include "hdmi_debug.h"
#include "hdmi_earc_if.h"
#include "drv_edid.h"
#include "drv_dvi2.h"
#include "drv_hdmi2com.h"
#include "hw_hdmi2.h"
#include "aud_if.h"
#include "cec_if.h"
#include "drvcust_if.h"
#include "drv_earc.h"
#include "hw_earc.h"

/***** *********************** ***********************************************/
EARCObject_t eARCObj = {
    .bMWReady = FALSE,
    .bTVStatus = FALSE,
    .bBtHpStatus = FALSE,
    .u1DDPOut = 0,
    .u1DDPCap = 0,
    .u1DDPeARC = 0,
    .pFuncTable = NULL,
    .pfnCallback[EARC_EVENT_CONNECT_CHG_TO_CEC] = NULL,
    .pfnCallback[EARC_EVENT_CONNECT_CHG_TO_AUDIO] = NULL,
    .pfnCallback[EARC_EVENT_CONNECT_CHG_TO_APP] = NULL,
    .pfnCallback[EARC_EVENT_HBRA_MODE] = NULL,
};

/***** platform-related functions ******************************************************/
extern void x_thread_delay(UINT32 u4Ms);
extern void HAL_GetRawTime(HAL_RAW_TIME_T* pRawTime);
extern UINT16 SIF_X_Write(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                   UINT8 *pu1Buf, UINT16 u2ByteCnt);
extern UINT16 SIF_X_Read(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                  UINT8 *pu1Buf, UINT16 u2ByteCnt);
extern void vHDMIEDIDGetLDDDP(UINT8 *pu1LDDP);
extern void vHDMIEDIDSetLDDDP(UINT8 *pu1LDDP);
extern void DRVCUST_SetGenParam(UINT32 u4Type, UINT8 u1Data[255], UINT8 u1Size);
extern void DRVCUST_SetBEGenParam(UINT32 u4Type, UINT8 u1Data[255], UINT8 u1Size);
extern void DRVCUST_SetPQGenParam(UINT32 u4Type, UINT8 u1Data[255], UINT8 u1Size);
extern EARC_STATUS_T g_tEarcStatus;
/****************************************************************************************************/
UINT8 u1HDMIEARCMenuEdidUpdate(void);


void vHDMIEARCSleepMsec(UINT32 u4Count)
{
    x_thread_delay(u4Count);
}

UINT32 u4HDMIEARCMsecQuery(void)
{
    HAL_TIME_T rTime;
    HAL_GetTime(&rTime);
    return (rTime.u4Seconds*1000 + rTime.u4Micros/1000);
}

UINT32 u4HDMIEARCI2CSlaveAddrGet(UINT32 u4DevID, UINT16 u2Addr,
                                        UINT8 *pI2CSlaveAddr, UINT8 *pOffsetSize)
{
    UINT32 ret_val = RET_SUCCESS;

    switch (u4DevID) 
    {
        case SII9438_INST_1:
            switch ((u2Addr >> 8) & 0xff)
            {
                case EARC_PAGE_0:
                    *pI2CSlaveAddr = SII9438_INST_1_I2C_ADDR_PAGE0;
                    break;
                case EARC_PAGE_1:
                    *pI2CSlaveAddr = SII9438_INST_1_I2C_ADDR_PAGE1;
                    break;
                case EARC_PAGE_2:
                    *pI2CSlaveAddr = SII9438_INST_1_I2C_ADDR_PAGE2;
                    break;
                case EARC_PAGE_3:
                    *pI2CSlaveAddr = SII9438_INST_1_I2C_ADDR_PAGE3;
                    break;
                case EARC_PAGE_4:
                    *pI2CSlaveAddr = SII9438_INST_1_I2C_ADDR_PAGE4;
                    break;
                default:
                    ret_val = RET_FAIL;
                    break;
            }
            *pOffsetSize = 1;
            break;
        case SII9438_INST_2:
            break;
        default:
            ret_val = RET_FAIL;
            break;
    }
    return ret_val;
}

UINT32 u4HDMIEARCHostBlockWrite(UINT32 u4DevID, UINT16 u2Addr,
        const UINT8 *pDataOut, UINT16 u2Length)
{
    UINT8 u1OffsetSize = 1;
    UINT8 u1I2CAddr;
    UINT16 u2Ret=0;
    UINT8 *pData = (UINT8*)pDataOut;

    if(u4HDMIEARCI2CSlaveAddrGet(u4DevID, u2Addr, &u1I2CAddr, &u1OffsetSize))
    {
        return 1;
    }
    u2Ret = SIF_X_Write(0, 0x100, u1I2CAddr, u1OffsetSize, u2Addr, pData, u2Length);
    if(u2Ret == 0)
    {
        return RET_FAIL;
    }
    return RET_SUCCESS;
}

UINT32 u4HDMIEARCHostBlockRead(UINT32 u4DevID, UINT16 u2Addr,
        UINT8 *pDataIn, UINT16 u2Length)
{
    UINT8 u1OffsetSize = 1;
    UINT8 u1I2CAddr;
    UINT16 u2Ret=0;

    if(u4HDMIEARCI2CSlaveAddrGet(u4DevID, u2Addr, &u1I2CAddr, &u1OffsetSize))
    {
        return RET_FAIL;
    }

    u2Ret = SIF_X_Read(0, 0x100, u1I2CAddr, u1OffsetSize, u2Addr, pDataIn, u2Length);
    if(u2Ret == 0)
    {
        return RET_FAIL;
    }
    return RET_SUCCESS;
}

void vHDMIEARCDebugPrintf(char *str, UINT8 *pData, UINT32 u4Length)
{
    UINT32 i=0;

    if(str!=NULL)
    {
        LOG(1, "%s", str);
    }
    for(i=0;i<u4Length;i++)
    {   
        if((i%16) == 0)
        {
            Printf("\n");
        }
        Printf("0x%02x,",pData[i]);
    }
    Printf("\n");
}

UINT8 u1HDMIEARCGetActivePort(E_HDMI_SWITCH_NUM *pActivePort)
{
    E_HDMI_SWITCH_NUM ePort;

    if(!fgIsExtSrcHDMI())
    {
        return RET_FAIL;
    }
    if(fgIsMainDVI())
    {
        ePort = bDvi2GetActivePort(SV_VP_MAIN);
    }
    else
    {
        ePort = bDvi2GetActivePort(SV_VP_PIP);
    }
    *pActivePort = ePort;
    return RET_SUCCESS;
}

BOOL bHDMIEARCIsAudioValid(E_HDMI_SWITCH_NUM eActivePort)
{
    UINT16 u2HdmiBase;
    UINT8 u1Path = bDvi2GetPath(eActivePort);

    u2HdmiBase = u2HDMI2GetDigBaseAddr(eActivePort);
    if(u1Path == SV_VP_MAX)
    {
        return FALSE;
    }
    if((!fgHDMI2ComGetHdmiMode(u2HdmiBase)) ||\
                (bHDMI2GetStateMachine(eActivePort)!= HDMI_STABLE_STATE) ||\
                fgDvi2IsModeChg(u1Path))
    {
        return FALSE;
    }
    return TRUE;
}
void vHDMIEARCGetSupported(UINT8 *pu1Info)
{
    EEPROM_Read(0x1020A, (UPTR)(pu1Info), 0x1);
}

void vHDMIEARCGetEnable(UINT8 *pu1Info)
{
    UINT32 u4Addr = 0;
    UINT8 u1Value = 0;

    if ((u4Addr = DRVCUST_InitGet(eHdmiEARCEnable)) == 0x00)
    {
        LOG(0,"Error Address!\n");
        return;
    }
    if (EEPROM_Read(u4Addr, (UPTR)(&u1Value), 1) != 0)
    {
        LOG(0,"EEP Read Fail!\n");
        return;
    }
    LOG(0,"Get Enable, Offset:0x%x, Data:0x%x\n", u4Addr, u1Value);
    *pu1Info = u1Value;
}

void _vHDMIEARCSetEnable(BOOL bValue)
{
    UINT32 u4Addr = 0;

    if ((u4Addr = DRVCUST_InitGet(eHdmiEARCEnable)) == 0x00)
    {
        LOG(0,"Error Address!\n");
        return;
    }
    if (EEPROM_Write(u4Addr, (UPTR)(&bValue), 1) != 0)
    {
        LOG(0,"EEP Write Fail!\n");
        return;
    }
    LOG(0,"Set Enable, Offset:0x%x, Data:0x%x\n", u4Addr, bValue);
}

BOOL fgHDMIEARCIsSupported(void)
{
    return eARCObj.bSupported;
}

void vHDMIEARCResetSet( UINT8 u1Status )
{
    GPIO_SetOut(131, u1Status?1:0);
}

void HDMIEARCLogPrint(const char *pStr)
{
    Printf("%s",pStr);
}

void vHDMIEARCUnregisterInt(PFN_GPIO_CALLBACK pfnISR)
{
    GPIO_Reg(132, GPIO_TYPE_NONE, pfnISR);
}

void vHDMIEARCRegisterInt(PFN_GPIO_CALLBACK pfnISR)
{
    INT32 i4ret = 0;
    i4ret = GPIO_Reg(132, GPIO_TYPE_INTR_LEVEL_LOW, pfnISR);
    LOG(1, "[EARC] register int result:0x%x\n",i4ret);
}

UINT8 u1HDMIEARCRegCB(pfnEARCEventCallback pCallback, EARCEvent_t eType)
{
    if(eType >= EARC_EVENT_MAX)
    {
        LOG(0,"[EARC]RegisterCallback, Over MAX eType:0x%x\n",eType);
        return RET_FAIL;
    }
    eARCObj.pfnCallback[eType] = pCallback;
    return RET_SUCCESS;
}

void vHDMIEARCEventNotify(EARCEvent_t type, void *pArg)
{
    if(type >= EARC_EVENT_MAX)
    {
        LOG(0,"[EARC]Error: Over MAX type:0x%x\n", type);
        return;
    }

    if(type == EARC_EVENT_CONNECT_CHG_TO_CEC)
    {
        EARCPortStatus_t *ePortStatus = (EARCPortStatus_t*)pArg;
        LOG(1,"[EARC]CEC notify, Port: 0x%x, Status:0x%x\n",
                    ePortStatus->u4PortId, ePortStatus->eStatus);
        if(!fgHDMIEARCIsSupported())
        {
            ePortStatus->eStatus = 0;
        }
    }
    else if(type == EARC_EVENT_CONNECT_CHG_TO_AUDIO)
    {
        EARCAudioStatus_t *eAudioStatus = (EARCAudioStatus_t*)pArg;
        LOG(1,"[EARC]Aud notify, Connect:0x%x, Capability:0x%x, MPEG4AACLC:0x%x\n",\
                    eAudioStatus->bEARCConnect, eAudioStatus->u1Capability, eAudioStatus->u1Mpeg4AacLcCapability);
        if(eAudioStatus->bEARCConnect)
            CEC_SetArcEnable(TRUE);
        else if(eARCObj.u1ARCStatus == 0)
            CEC_SetArcEnable(FALSE);
        AUD_SetEarcAudioStatus(*eAudioStatus);
    }
    else if(type == EARC_EVENT_CONNECT_CHG_TO_APP)
    {
        EARCAudioStatus_t *eAudioStatus = (EARCAudioStatus_t*)pArg;
        LOG(1,"[EARC]JNI notify, Connect:0x%x, Capability:0x%x, MPEG4AACLC:0x%x\n",\
                    eAudioStatus->bEARCConnect, eAudioStatus->u1Capability, eAudioStatus->u1Mpeg4AacLcCapability);
    }
    else if(type == EARC_EVENT_HBRA_MODE)
    {
        BOOL *bHbrMode = (BOOL*)pArg;
        LOG(1,"[EARC]JNI notify, bHbrMode:0x%x\n", *bHbrMode);
    }

    if(eARCObj.pfnCallback[type])
    {
        eARCObj.pfnCallback[type](pArg);
    }
}

UINT8 u1HDMIEARCGetFuncTable(EARCFuncTable_t **pFuncTable)
{
    if(eARCObj.pFuncTable == NULL)
    {
        return RET_FAIL;
    }
    *pFuncTable = eARCObj.pFuncTable;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCRegFuncTable(EARCFuncTable_t *pFuncTable)
{
    LOG(0,"[EARC]Reg Function Table!\n");
    eARCObj.pFuncTable  = pFuncTable;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetChannelStatus(EARCChannelStatus_t *peStatus)
{
    E_HDMI_SWITCH_NUM ePort;
    UINT16 u2HdmiBase;
    UINT32 u4Byte0_23;
    UINT32 u4Byte24_39;

    x_memset(peStatus, 0, sizeof(EARCChannelStatus_t));
    peStatus->u1Length = 5;

    if(u1HDMIEARCGetActivePort(&ePort) == RET_FAIL)
    {
        return RET_FAIL;
    }
    if(bHDMIEARCIsAudioValid(ePort) == FALSE)
    {
        return RET_FAIL;
    }

    u2HdmiBase = u2HDMI2GetDigBaseAddr(ePort);
    u4Byte0_23 = u4IO32Read4B(HDMI2_RX_CHST1 + u2HdmiBase);
    u4Byte24_39 = u4IO32Read4B(HDMI2_RX_SW_OW + u2HdmiBase);
    peStatus->u1Status[0] = u4Byte0_23&0xff;
    peStatus->u1Status[1] = (u4Byte0_23 >> 8) & 0xff;
    peStatus->u1Status[2] = (u4Byte0_23 >> 16) & 0xff;
    peStatus->u1Status[3] = (u4Byte24_39 >> 16) & 0xff;
    peStatus->u1Status[4] = (u4Byte24_39 >> 24) & 0xff;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetAudInfoFrame(EARCAudioInfoFrame_t *peInfoFrame)
{
    E_HDMI_SWITCH_NUM ePort;
    HdmiRxInfo_t tAudInfo;

    x_memset(peInfoFrame, 0, sizeof(EARCAudioInfoFrame_t));
    peInfoFrame->u1Length = 10;

    if(u1HDMIEARCGetActivePort(&ePort) == RET_FAIL)
    {
        return RET_FAIL;
    }
    if(bHDMIEARCIsAudioValid(ePort) == FALSE)
    {
        return RET_FAIL;
    }

    bHDMI20GetInfoFrameToIF(ePort, &tAudInfo, HDMI_INFO_FRAME_ID_AUD);
    x_memcpy(peInfoFrame->u1Info, (&tAudInfo.u1InfoData[0]) + 4,peInfoFrame->u1Length);

    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetValidityBit(BOOL *pValidity)
{
    E_HDMI_SWITCH_NUM ePort;
    UINT16 u2HdmiBase;
    UINT32 u4Byte0_23;

    *pValidity = 0;
    if(u1HDMIEARCGetActivePort(&ePort) == RET_FAIL)
    {
        return RET_FAIL;
    }
    if(bHDMIEARCIsAudioValid(ePort) == FALSE)
    {
        return RET_FAIL;
    }

    u2HdmiBase = u2HDMI2GetDigBaseAddr(ePort);
    u4Byte0_23 = u4IO32Read4B(HDMI2_RX_CHST1 + u2HdmiBase);
    *pValidity = (u4Byte0_23 & 2)?1:0;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCIsHBRInput(BOOL *pHbr)
{
    E_HDMI_SWITCH_NUM ePort;
    if(u1HDMIEARCGetActivePort(&ePort) == RET_FAIL)
    {
        return RET_FAIL;
    }
    if(bHDMIEARCIsAudioValid(ePort) == FALSE)
    {
        return RET_FAIL;
    }
    *pHbr = fgHDMI20AudHbrOn()?1:0;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetLayoutBit(BOOL *pLayout)
{
    E_HDMI_SWITCH_NUM ePort;
    UINT16 u2HdmiBase;

    *pLayout = 0;
    if(u1HDMIEARCGetActivePort(&ePort) == RET_FAIL)
    {
        return RET_FAIL;
    }
    if(bHDMIEARCIsAudioValid(ePort) == FALSE)
    {
        return RET_FAIL;
    }

    u2HdmiBase = u2HDMI2GetDigBaseAddr(ePort);
    *pLayout = fgHDMI2ComGetAudioLayout(u2HdmiBase);
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetAudioFs(UINT32 *pFs)
{
    E_HDMI_SWITCH_NUM ePort;
    UINT32 wCTS_HW,wN_HW;
    UINT32 wAudSampleRate,wTMDSClkFreq;
    UINT16 u2HdmiBase;

    *pFs = 0;
    if(u1HDMIEARCGetActivePort(&ePort) == RET_FAIL)
    {
        return RET_FAIL;
    }
    if(bHDMIEARCIsAudioValid(ePort) == FALSE)
    {
        return RET_FAIL;
    }

    u2HdmiBase = u2HDMI2GetDigBaseAddr(ePort);
    wCTS_HW = dwHDMI2ComGetCTS(u2HdmiBase);
    wN_HW = dwHDMI2ComGetN(u2HdmiBase);
    wTMDSClkFreq = bHDMI2ComGetTMDSClkFreq(u2HdmiBase);

    if(wCTS_HW == 0)
    {
        *pFs = 0;
        return RET_FAIL;
    }
    if(fgHDMI2ComGetTmdsClkRatio(u2HdmiBase))
    {
        wAudSampleRate= (((4 * wTMDSClkFreq)/wCTS_HW) *((wN_HW*100)/128))/10000;
    }
    else
    {
        wAudSampleRate= ((wTMDSClkFreq/wCTS_HW) *((wN_HW*100)/128))/10000;
    }
    *pFs = wAudSampleRate;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCIsHDMIInput(BOOL *pFlag)
{
    *pFlag  = fgIsExtSrcHDMI()?1:0;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetHPDStatus(UINT8 *pHPDStatus)
{
    E_HDMI_SWITCH_NUM ePort;
    UINT8 u1Status = 0;

    for(ePort = HDMI_SWITCH_1; ePort <= HDMI_MAX_PHYCIAL_PORT_NUM;ePort++)
    {
        if(!u1HDMI2IsHpdPulse(ePort))
        {
            u1Status |= 0x1<<(ePort-1);
        }
    }
    *pHPDStatus = u1Status;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGet5VStatus(UINT8 *p5VStatus)
{
    E_HDMI_SWITCH_NUM ePort;
    UINT8 u1Status = 0;

    for(ePort = HDMI_SWITCH_1; ePort <= HDMI_MAX_PHYCIAL_PORT_NUM;ePort++)
    {
        if(u1HDMI2_Get5VStatus(eHDMIGetVirtualPortId(ePort)))
        {
            u1Status |= 0x1<<(ePort -1);
        }
    }
    *p5VStatus = u1Status;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetInitialStatus(EARCInitInfo_t *pInitInfo)
{
    pInitInfo->u1HPDStatus = eARCObj.u1HPDStatus;
    pInitInfo->u15VStatus = eARCObj.u15VStatus;
    pInitInfo->bEnable = eARCObj.bEnable;
    pInitInfo->bTVStatus = eARCObj.bTVStatus;
    pInitInfo->bBtHpStatus = eARCObj.bBtHpStatus;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetAudioLatency(INT16 *pLatency)
{
    *pLatency = AUD_GetAdjustDelayResult();
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCGetMWReady(BOOL *pReady)
{
    *pReady = eARCObj.bMWReady;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCChgHPD(UINT8 u1HPDStatus)
{
    E_HDMI_SWITCH_NUM ePort;

    for(ePort = HDMI_SWITCH_1; ePort <= HDMI_MAX_PHYCIAL_PORT_NUM;ePort++)
    {
        if(u1HPDStatus&(0x1<<(ePort-1)))
        {
            vHDMIHPDHigh(ePort);
        }
        else
        {
            vHDMIHPDLow(ePort);
        }
    }
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCParserEDID(EARCEDIDInfo_t *pEdidInfo)
{
    UINT8 *u1BlockData;
    UINT16 u2Cnt = 1;
    UINT8 u1MaxCnt = 0;
    UINT8 u1IDFlag = 0;
    UINT8 u1MaxSADCnt;
    UINT8 u1TempCnt = 0;

    u1BlockData = pEdidInfo->u1DataBlock;
    pEdidInfo->u1EAC3 = 0;
    pEdidInfo->u1DDP = 0;
    pEdidInfo->AAC_LC = 0;
    pEdidInfo->AAC_LC_Ch = 0;
    pEdidInfo->AAC_LC_22ch = 0;
    do
    {
        if(u1BlockData[u2Cnt] == 1)
            break;
        if(u1BlockData[u2Cnt+1] ==0)
        {
            LOG(1,"[EDID]Parser EDID, No Block ID 1!!\n");
            return RET_FAIL;
        }
        u2Cnt += u1BlockData[u2Cnt+1] + 2;
    }while(u2Cnt < 256);

    if(u2Cnt >= 251)
    {
        LOG(1,"[EDID]Parser EDID, No Block ID 1!\n");
        return RET_FAIL;
    }
    u1MaxCnt = u2Cnt  +2 + u1BlockData[u2Cnt+1];
    u2Cnt += 2;
    while((u2Cnt < u1MaxCnt)&&(u1IDFlag != 1))
    {
        if(u2Cnt >= 253)
        {
            LOG(1, "[EDID]No SAD/SADB Block!\n");
            return RET_FAIL;
        }
        if((u1BlockData[u2Cnt]>>5) == 1)
        {
            LOG(1, "[EDID]Parser EDID, SAD Block Offset:0x%x, Length:0x%x\n", u2Cnt, u1BlockData[u2Cnt]&0x1f);
            u1MaxSADCnt = (u1BlockData[u2Cnt]&0x1f) + u2Cnt + 1;
            u2Cnt++;
            while(u2Cnt < u1MaxSADCnt)
            {
                if(u2Cnt >= 254)
                {
                    LOG(1, "[EDID]No 0x0A Block!\n");
                    return RET_FAIL;
                }
                if(((u1BlockData[u2Cnt]>>3) & 0xf) == 0x0A)
                {
                    pEdidInfo->u1EAC3 = 1;
                    pEdidInfo->u1DDP = (u1BlockData[u2Cnt + 2])&1;
                    //break;
                }
                //Search AFC=0xF & CXT=0x6 & 48khz=1
                if((((u1BlockData[u2Cnt]>>3) & 0xf) == 0xF) && (((u1BlockData[u2Cnt+1]>>2) & 0x1) == 0x1)\
                    && (((u1BlockData[u2Cnt+2]>>3) & 0x1F) == 0x6))
                {
                    pEdidInfo->AAC_LC = 1;
                    LOG(0, "[EDID] AFC=0xF & CXT=0x6 & 48khz=1 exit\n");

                    if(!u1TempCnt)
                    {
                        //check if last bit is 1/0
                        if(u1BlockData[u2Cnt+2] & 0x1)
                        {
                            pEdidInfo->AAC_LC_22ch = 1;
                        }
                        else
                        {
                            pEdidInfo->AAC_LC_22ch = 0;
                            pEdidInfo->AAC_LC_Ch = (u1BlockData[u2Cnt] & 0x7);
                        }
                    }
                    else
                    {
                        //other AFC=0xF & CXT=0x6 & 48khz=1 exit, compare last bit with AAC_LC_22ch;
                        if((u1BlockData[u2Cnt+2] & 0x1) != pEdidInfo->AAC_LC_22ch)
                        {
                            pEdidInfo->AAC_LC_22ch = (u1BlockData[u2Cnt+2] & 0x1) > (pEdidInfo->AAC_LC_22ch) ? \
                                (u1BlockData[u2Cnt+2] & 0x1) : (pEdidInfo->AAC_LC_22ch);
                        }
                        //other AFC=0xF & CXT=0x6 & 48khz=1 exit, compare Audio Channel Count with AAC_LC_Ch;
                        if((u1BlockData[u2Cnt] & 0x7) != pEdidInfo->AAC_LC_Ch)
                        {
                            pEdidInfo->AAC_LC_Ch = (u1BlockData[u2Cnt] & 0x7) > (pEdidInfo->AAC_LC_Ch) ? \
                                (u1BlockData[u2Cnt] & 0x7) : (pEdidInfo->AAC_LC_Ch);
                        }
                    }
                    u1TempCnt++;
                }
                u2Cnt += 3;
            }
            LOG(1, "[EDID]Parser EDID, EAC3:%d, DDPeARC:%d, u1TempCnt:%d, AAC_LC:%d, AAC_LC_Ch:%d, AAC_LC_22ch:%d\n", \
                pEdidInfo->u1EAC3, pEdidInfo->u1DDP, u1TempCnt, pEdidInfo->AAC_LC, pEdidInfo->AAC_LC_Ch, pEdidInfo->AAC_LC_22ch);
            return RET_SUCCESS;
        }
        u2Cnt += 1 + (u1BlockData[u2Cnt]&0x1f);
    }
    LOG(1, "[EDID]Parser EDID, No SAD Block!\n");
    return RET_FAIL;
}

UINT8 u1HDMIEARCUpdateEdid(EARCEDIDInfo_t *pEdidInfo)
{
#if (MS12_NO_ATMOS_CONTROL != 0) || (SUPPORT_ATMOS_UPDATE != 0)
    UINT8 u1DDPTmp;
#endif
    UINT8 u1HdmiPort = HDMI_SWITCH_INIT;

    pEdidInfo->u1DDP = 0;
    pEdidInfo->u1EAC3 = 0;
    pEdidInfo->AAC_LC = 0;
    pEdidInfo->AAC_LC_Ch = 0;
    pEdidInfo->AAC_LC_22ch = 0;
    eARCObj.u1eARCStatus = pEdidInfo->bEARCConnect;
    if(pEdidInfo->bEARCConnect == 0)
    {
        eARCObj.eEDIDInfo.bEARCConnect = 0;
        eARCObj.u1DDPeARC = 0;
        eARCObj.i2Latency = -32768;
    }
    else
    {
        x_memcpy(&eARCObj.eEDIDInfo, pEdidInfo, sizeof(EARCEDIDInfo_t));
        if(u1HDMIEARCParserEDID(&eARCObj.eEDIDInfo) == RET_FAIL)
        {
            return RET_FAIL;
        }
        eARCObj.u1DDPeARC = eARCObj.eEDIDInfo.u1DDP;
        pEdidInfo->u1DDP = eARCObj.eEDIDInfo.u1DDP;
        pEdidInfo->u1EAC3 = eARCObj.eEDIDInfo.u1EAC3;
        pEdidInfo->AAC_LC = eARCObj.eEDIDInfo.AAC_LC;
        pEdidInfo->AAC_LC_Ch = eARCObj.eEDIDInfo.AAC_LC_Ch;
        pEdidInfo->AAC_LC_22ch = eARCObj.eEDIDInfo.AAC_LC_22ch;
    }

    LOG(1, "[EARC]Edid Update, TVStatus:%d, DDPeARC:%d, DDPCap:%d, DDPOut:%d, AAC_LC:%d, AAC_LC_Ch:%d, AAC_LC_22ch:%d\n",\
            eARCObj.bTVStatus, eARCObj.u1DDPeARC, eARCObj.u1DDPCap, eARCObj.u1DDPOut, \
            eARCObj.eEDIDInfo.AAC_LC, eARCObj.eEDIDInfo.AAC_LC_Ch, eARCObj.eEDIDInfo.AAC_LC_22ch);

#if MS12_NO_ATMOS_CONTROL
    u1DDPTmp = 0;
    vHDMIEDIDSetLDDDP(&u1DDPTmp);

#elif SUPPORT_ATMOS_UPDATE
    u1DDPTmp = (eARCObj.u1DDPeARC||eARCObj.u1DDPCap)&&eARCObj.u1DDPOut;
    vHDMIEDIDSetLDDDP(&u1DDPTmp);

#endif

    vHDMI_INTERNAL_EDID_Init();
    for(u1HdmiPort=HDMI_SWITCH_1; u1HdmiPort <= HDMI_MAX_PHYCIAL_PORT_NUM; u1HdmiPort++)
    {
        if(u1HdmiPort == HDMI_SWITCH_3)
            continue;
        vHDMI2SetStateMachine(u1HdmiPort, HDMI_NOSIGNAL_STATE);
    }
    return RET_SUCCESS;
}

void vHDMIEARCSetGenParam(UINT32 u4Type, UINT8* pData, UINT8 u1Size)
{
    DRVCUST_SetGenParam(u4Type, pData, u1Size);
}

void vHDMIEARCSetBEGenParam(UINT32 u4Type, UINT8* pData, UINT8 u1Size)
{
    DRVCUST_SetBEGenParam(u4Type, pData, u1Size);
}

void vHDMIEARCSetPQGenParam(UINT32 u4Type, UINT8* pData, UINT8 u1Size)
{
    DRVCUST_SetPQGenParam(u4Type, pData, u1Size);
}
/*
 * For mtk driver
 *
 */
UINT8 u1HDMIEARCGetEDIDInfo(EARCEDIDInfo_t **pEdidInfo)
{
    *pEdidInfo = &eARCObj.eEDIDInfo;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCSetMWReady(BOOL bReady)
{
    eARCObj.bMWReady = bReady;
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCSetChannelStatus(EARCChannelStatus_t *eStatus)
{
    EARCFuncTable_t *pFuncTable;

    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetChannelStatus)
            return pFuncTable->pfnSetChannelStatus(eStatus);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetAudInfoFrame(EARCAudioInfoFrame_t *eInfoFrame)
{
    EARCFuncTable_t *pFuncTable;

    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetAudioInfoFrame)
            return pFuncTable->pfnSetAudioInfoFrame(eInfoFrame);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetValidityBit(BOOL bValidity)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.bValidity == bValidity)
        return RET_SUCCESS;

    LOG(1, "[EARC]Set Validity Bit:%d\n", bValidity);
    eARCObj.bValidity = bValidity;
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetValidityBit)
            return pFuncTable->pfnSetValidityBit(bValidity);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetHPDStatus(UINT8 u1HpdStatus)
{
    EARCFuncTable_t *pFuncTable;
    LOG(1, "[EARC]Set HPD status:0x%x\n", u1HpdStatus);
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetHPDStatus)
            return pFuncTable->pfnSetHPDStatus(u1HpdStatus);
    }
    return RET_FAIL;
}

void vHDMIEARCSetHPD(E_HDMI_SWITCH_NUM ePort, UINT8 u1HpdStatus)
{
    UINT8 u1PreStatus;

    if((ePort < HDMI_SWITCH_1) || (ePort > HDMI_MAX_PHYCIAL_PORT_NUM))
    {
        return;
    }
    u1PreStatus = (eARCObj.u1HPDStatus>>(ePort -1))&1;
    if(u1HpdStatus != u1PreStatus)
    {
        eARCObj.u1HPDStatus &= ~(0x1 << (ePort -1));
        eARCObj.u1HPDStatus |= (u1HpdStatus << (ePort -1));
        u1HDMIEARCSetHPDStatus(eARCObj.u1HPDStatus);
    }
}

UINT8 u1HDMIEARCSet5VStatus(UINT8 u15VStatus)
{
    EARCFuncTable_t *pFuncTable;
    LOG(1, "[EARC]Set 5V status:0x%x\n", u15VStatus);
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSet5VStatus)
            return pFuncTable->pfnSet5VStatus(u15VStatus);
    }
    return RET_FAIL;
}

void vHDMIEARC5VMonitor(E_HDMI_SWITCH_NUM ePort)
{
    UINT8 u15VStatus;
    UINT8 u1PreStatus;

    if((ePort < HDMI_SWITCH_1) || (ePort > HDMI_MAX_PHYCIAL_PORT_NUM))
    {
        return;
    }

    u15VStatus = u1HDMI2_Get5VStatus(eHDMIGetVirtualPortId(ePort));
    u1PreStatus = (eARCObj.u15VStatus>>(ePort -1))&1;
    if(u15VStatus != u1PreStatus)
    {
        eARCObj.u15VStatus &= ~(0x1 << (ePort -1));
        eARCObj.u15VStatus |= (u15VStatus << (ePort -1));
        u1HDMIEARCSet5VStatus(eARCObj.u15VStatus);
    }
}

UINT8 u1HDMIEARCSetHBRAInputStatus(BOOL bHbr)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.bHbrInputStatus == bHbr)
        return RET_SUCCESS;

    LOG(1, "[EARC]Set Hbr Input Status:%d\n", bHbr);
    eARCObj.bHbrInputStatus = bHbr;
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetHBRAInputStatus)
            return pFuncTable->pfnSetHBRAInputStatus(bHbr);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetLayoutBit(BOOL bLayout)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.bLayout == bLayout)
        return RET_SUCCESS;

    LOG(1, "[EARC]Set Layout Bit:%d\n", bLayout);
    eARCObj.bLayout = bLayout;
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetLayoutBit)
            return pFuncTable->pfnSetLayoutBit(bLayout);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetArcStatus(BOOL bOnOff)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.u1ARCStatus == bOnOff)
        return RET_SUCCESS;

    LOG(1, "[EARC]Set Arc Status:%d\n", bOnOff);
    eARCObj.u1ARCStatus = bOnOff;
#if MS12_NO_ATMOS_CONTROL
    u1HDMIEARCMenuEdidUpdate();
#endif
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetArcStatus)
            return pFuncTable->pfnSetArcStatus(bOnOff);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCGetArcStatus(void) {
    return eARCObj.u1ARCStatus;
}

UINT8 u1HDMIEARCSetAudioPath(BOOL bPath)
{
    EARCFuncTable_t *pFuncTable;

    LOG(1, "[EARC]Set Audio Path:%d\n", bPath);
    eARCObj.bPath = bPath;
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetAudioPath)
            return pFuncTable->pfnSetAudioPath(bPath);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetAudioLatency(INT16 i2Ms)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.i2Latency == i2Ms)
        return RET_SUCCESS;

    LOG(0, "[EARC]Set Audio Latency:%d\n", i2Ms);
    eARCObj.i2Latency = i2Ms;
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetAudioLatency)
            return pFuncTable->pfnSetAudioLatency(i2Ms);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetBtHpStatus(BOOL bConnect)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.bBtHpStatus == bConnect)
        return RET_SUCCESS;

    LOG(1, "[EARC]Menu BtHp Status:%d\n", bConnect);
    eARCObj.bBtHpStatus = bConnect;
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetBtHpStatus)
            return pFuncTable->pfnSetBtHpStatus(bConnect);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCMenuEdidUpdate(void)
{
    UINT8 u1DDPTmp = 0;
    UINT8 u1LDDP = 0;
    UINT8 u1HdmiPort = HDMI_SWITCH_INIT;
    UINT8 u15VStatus = u1GetEarc5VStatus();

    LOG(1, "[EARC]Menu Edid Update, TVStatus:%d, DDPeARC:%d, DDPCap:%d, DDPOut:%d, 5V:%d\n",\
           eARCObj.bTVStatus, eARCObj.u1DDPeARC, eARCObj.u1DDPCap, eARCObj.u1DDPOut, u15VStatus);

    if((eARCObj.bTVStatus) && (u15VStatus != 0))
    {
#if MS12_NO_ATMOS_CONTROL
        if ((u1EarcGetStateMachine() > EARC_TX_EARC_CONNECTED) && (eARCObj.u1DDPCap == 0))
        {   /* u1DDPCap | Raw DDPCap | meaning
             * 0        | 0          | AMP doesn't support ATMOS and DDP
             * 0        | 1          | AMP doesn't support ATMOS
             * 1        | 2          | AMP supports ATMOS and DDP
             */
            u1DDPTmp = 1u; /* NoATMOS */
        }
#else
        u1DDPTmp = (eARCObj.u1DDPeARC||eARCObj.u1DDPCap)&&eARCObj.u1DDPOut;
#endif /* MS12_NO_ATMOS_CONTROL */
    }
    vHDMIEDIDGetLDDDP(&u1LDDP);
    if(u1DDPTmp == u1LDDP)
    {
        return RET_SUCCESS;
    }
    vHDMIEDIDSetLDDDP(&u1DDPTmp);
    vHDMI_INTERNAL_EDID_Init();
    for(u1HdmiPort=HDMI_SWITCH_1; u1HdmiPort <= HDMI_MAX_PHYCIAL_PORT_NUM; u1HdmiPort++)
    {
        vHDMI2SetStateMachine(u1HdmiPort, HDMI_NOSIGNAL_STATE);
    }
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCMenuSetTVStatus(BOOL bTVStatus)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.bTVStatus == bTVStatus)
        return RET_SUCCESS;

    LOG(1, "[EARC]Menu Set TV Status:%d\n",bTVStatus);
    eARCObj.bTVStatus = bTVStatus;
#if SUPPORT_ATMOS_UPDATE
    if(eARCObj.u1eARCStatus == 0)
    {
        u1HDMIEARCMenuEdidUpdate();
    }
#endif
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetTVStatus)
            return pFuncTable->pfnSetTVStatus(bTVStatus);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCMenuSetDDPCap(UINT8 u1DDPCap)
{
#if MS12_NO_ATMOS_CONTROL
    UINT8 u1Tmp;

    u1Tmp = (u1DDPCap == 2)?1:0;
    LOG(1, "[EARC]Menu Set DDP Capability:%d\n",u1DDPCap);
    eARCObj.u1DDPCap = u1Tmp;

	u1HDMIEARCMenuEdidUpdate();

    return RET_SUCCESS;

#elif SUPPORT_ATMOS_UPDATE
    UINT8 u1Tmp;

    u1Tmp = (u1DDPCap == 2)?1:0;
    if(eARCObj.u1DDPCap == u1Tmp)
        return RET_SUCCESS;

    LOG(1, "[EARC]Menu Set DDP Capability:%d\n",u1DDPCap);
    eARCObj.u1DDPCap = u1Tmp;
    u1HDMIEARCMenuEdidUpdate();

    return RET_SUCCESS;
#endif
}

UINT8 u1HDMIEARCMenuSetDDPOut(UINT8 u1DDPOut)
{
#if SUPPORT_ATMOS_UPDATE
    if(eARCObj.u1DDPOut == u1DDPOut)
        return RET_SUCCESS;

    LOG(1, "[EARC]Menu Set DDP Output:%d\n",u1DDPOut);
    eARCObj.u1DDPOut = u1DDPOut;
    u1HDMIEARCMenuEdidUpdate();
#endif
    return RET_SUCCESS;
}

UINT8 u1HDMIEARCSetEnable(BOOL bEn)
{
    EARCFuncTable_t *pFuncTable;

    if(eARCObj.bEnable == bEn)
        return RET_SUCCESS;

    LOG(1, "[EARC]Menu Set Enable Status:%d\n",bEn);
    eARCObj.bEnable = bEn;
    _vHDMIEARCSetEnable(bEn);
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetEnable)
            return pFuncTable->pfnSetEnable(bEn);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetHdmiMute(BOOL bMute)
{
    EARCFuncTable_t *pFuncTable;

    LOG(1, "[EARC]Set Hdmi Mute:%d\n",bMute);
    eARCObj.bHdmiMute = bMute;
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetHdmiMute)
            return pFuncTable->pfnSetHdmiMute(bMute);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCSetAudioMute(BOOL bMute)
{
    EARCFuncTable_t *pFuncTable;

    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnSetAudioMute)
            return pFuncTable->pfnSetAudioMute(bMute);
    }
    return RET_FAIL;
}

UINT8 u1HDMIEARCGetStatus(E_HDMI_SWITCH_NUM ePort)
{
    UINT8 u1Ret = HDMI_EARC_NOT_ENABLED;
    EARCFuncTable_t *pFuncTable;

    if(!fgHDMI2IsValidPort(ePort)||!fgHDMIEARCIsSupported())
    {
        return HDMI_EARC_NOT_ENABLED;
    }
    ePort = eHDMIGetPhysicalPortId(ePort);
    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnGetEARCStatus)
            u1Ret = pFuncTable->pfnGetEARCStatus(ePort);
    }

    LOG(1,"[EARC]GetStatus, Port:0x%x ,Status:0x%x\n",ePort, u1Ret);
    #if MS12_NO_ATMOS_CONTROL
    u1HDMIEARCMenuEdidUpdate();
    #endif
    return u1Ret;
}

UINT8 u1HDMIEARCGetAudioStatus(EARCAudioStatus_t* eStatus)
{
    EARCFuncTable_t *pFuncTable;

    if(u1HDMIEARCGetFuncTable(&pFuncTable) == RET_SUCCESS)
    {
        if(pFuncTable->pfnGetEARCAudioStatus)
            return pFuncTable->pfnGetEARCAudioStatus(eStatus);
    }
    return RET_FAIL;
}

void vHDMIEARCMuteMonitor(void)
{
    E_HDMI_SWITCH_NUM ePort;

    if(u1HDMIEARCGetActivePort(&ePort) == RET_FAIL)
    {
        return;
    }

    if(bHDMIEARCIsAudioValid(ePort) == FALSE)
    {
        if(eARCObj.bHdmiMute == FALSE)
        {
            u1HDMIEARCSetHdmiMute(TRUE);
        }
    }
    else
    {
        if(eARCObj.bHdmiMute == TRUE)
        {
            u1HDMIEARCSetHdmiMute(FALSE);
        }
    }
}

void vHDMIEARCChgHandler(E_HDMI_SWITCH_NUM ePort)
{
    EARCChannelStatus_t eStatus;
    EARCAudioInfoFrame_t eInfoFrame;
    BOOL bValidity;
    BOOL bHbrStatus;
    BOOL bLayout;

    vHDMIEARCMuteMonitor();
    vHDMIEARC5VMonitor(ePort);
    if(u1HDMIEARCGetChannelStatus(&eStatus) == RET_SUCCESS)
    {
        if(x_memcmp(eStatus.u1Status, eARCObj.eChStatus.u1Status,5) != 0)
        {
            u1HDMIEARCSetChannelStatus(&eStatus);
            x_memcpy(&eARCObj.eChStatus, &eStatus, sizeof(EARCChannelStatus_t));
        }
    }
    if(u1HDMIEARCGetAudInfoFrame(&eInfoFrame) == RET_SUCCESS)
    {
        if(x_memcmp(eInfoFrame.u1Info, eARCObj.eAudInfoFrame.u1Info,5) != 0)
        {
            u1HDMIEARCSetAudInfoFrame(&eInfoFrame);
            x_memcpy(&eARCObj.eAudInfoFrame, &eInfoFrame, sizeof(EARCAudioInfoFrame_t));
        }
    }
    if(u1HDMIEARCGetValidityBit(&bValidity) == RET_SUCCESS)
    {
        u1HDMIEARCSetValidityBit(bValidity);
    }
    if(u1HDMIEARCIsHBRInput(&bHbrStatus) == RET_SUCCESS)
    {
        u1HDMIEARCSetHBRAInputStatus(bHbrStatus);
    }
    if(u1HDMIEARCGetLayoutBit(&bLayout) == RET_SUCCESS)
    {
        u1HDMIEARCSetLayoutBit(bLayout);
    }

}

void vHDMIEARCPrintStatus(void)
{
    BOOL bHdmiInputStatus;
    UINT32 u4AudioFs = 0;

    u1HDMIEARCIsHDMIInput(&bHdmiInputStatus);
    u1HDMIEARCGetAudioFs(&u4AudioFs);

    LOG(0, "[EARC]Supported:%d\n", eARCObj.bSupported);
    LOG(0, "[EARC]MW Ready:%d\n", eARCObj.bMWReady);
    LOG(0, "[EARC]Menu TV Status:%d\n", eARCObj.bTVStatus);
    LOG(0, "[EARC]Menu Enable:%d\n", eARCObj.bEnable);
    LOG(0, "[EARC]Menu DDP Out:%d\n", eARCObj.u1DDPOut);
    LOG(0, "[EARC]Menu DDP Capability:%d\n", eARCObj.u1DDPCap);
    LOG(0, "[EARC]Menu DDP eARC:%d\n", eARCObj.u1DDPeARC);
    LOG(0, "[EARC]BtHp Status:%d\n", eARCObj.bBtHpStatus);
    LOG(0, "[EARC]HBR Input Status:%d\n", eARCObj.bHbrInputStatus);
    LOG(0, "[EARC]HPD Status:0x%x\n", eARCObj.u1HPDStatus);
    LOG(0, "[EARC]5V Status:0x%x\n", eARCObj.u15VStatus);
    LOG(0, "[EARC]HDMI Input Status:%d\n", bHdmiInputStatus);
    LOG(0, "[EARC]Audio Frequency:%d\n", u4AudioFs);
    LOG(0, "[EARC]Validity Status:%d\n", eARCObj.bValidity);
    LOG(0, "[EARC]Layout Status:%d\n", eARCObj.bLayout);
    LOG(0, "[EARC]Path Status:%d\n", eARCObj.bPath);
    LOG(0, "[EARC]ARC Status:%d\n", eARCObj.u1ARCStatus);
    LOG(0, "[EARC]eARC Status:%d\n", eARCObj.u1eARCStatus);
    LOG(0, "[EARC]Latency Status:%d\n", eARCObj.i2Latency);
    LOG(0, "[EARC]EDID Connect Status:%d\n", eARCObj.eEDIDInfo.bEARCConnect);
    LOG(0, "[EARC]EDID DDPeARC Status:%d\n", eARCObj.eEDIDInfo.u1DDP);
    LOG(0, "[EARC]EDID EAC3 Status:%d\n", eARCObj.eEDIDInfo.u1EAC3);
    vHDMIEARCDebugPrintf("[EARC]Channel Status:\n", eARCObj.eChStatus.u1Status, eARCObj.eChStatus.u1Length);
    vHDMIEARCDebugPrintf("[EARC]Aud InfoFrame:\n", eARCObj.eAudInfoFrame.u1Info, eARCObj.eAudInfoFrame.u1Length);
    vHDMIEARCDebugPrintf("[EARC]EDID Data Block:\n", eARCObj.eEDIDInfo.u1DataBlock, 256);
}

void vHDMIEARCObjInit(void)
{
    eARCObj.bHbrInputStatus = FALSE;
    eARCObj.bValidity = FALSE;
    eARCObj.bLayout = FALSE;
    eARCObj.bHdmiMute = FALSE;
    eARCObj.bAudioMute = FALSE;
    eARCObj.u1HPDStatus = 0;
    eARCObj.u15VStatus = 0;
    eARCObj.u1ARCStatus = 0;
    eARCObj.u1eARCStatus = 0;
    eARCObj.i2Latency = 0;
    eARCObj.pFuncTable = NULL;
    x_memset(&eARCObj.eChStatus, 0, sizeof(EARCChannelStatus_t));
    x_memset(&eARCObj.eAudInfoFrame, 0, sizeof(EARCAudioInfoFrame_t));
    x_memset(&eARCObj.eEDIDInfo, 0, sizeof(EARCEDIDInfo_t));
    vHDMIEARCGetSupported(&eARCObj.bSupported);
    vHDMIEARCGetEnable(&eARCObj.bEnable);
}

void vHDMIEARCSystemInit(void)
{
    vHDMIEARCObjInit();
    //add earc lib init function here.
    LOG(0,"[EARC]eARC Init!\n");
}

void vHDMIEARCSystemSuspend(void)
{
    vHDMIEARCSuspend();
    //add earc lib suspend function here.
    LOG(0,"[EARC]eARC suspend!\n");
}

void vHDMIEARCSystemResume(void)
{
    vHDMIEARCResume();
    //add earc lib Resume function here.
    LOG(0,"[EARC]eARC Resume!\n");
}

/* add cause no eARC framework.
 * simulate cec framework to send <Request ARC Terminate> for eARC connect.
 */
void vHDMIEARCReqArcTerminate(void) {
    CEC_FRAME_DESCRIPTION txInfo;

    if(u1EarcGetStateMachine() != EARC_TX_EARC_CONNECTED) {
        return;
    }

    LOG(0,"[EARC] %s \n",__FUNCTION__);

    x_memset(&txInfo, 0, sizeof(CEC_FRAME_DESCRIPTION));
    /* size = header+opcode+oprand */
    txInfo.size = 3;
    txInfo.blocks.header.initiator = 0x00;
    txInfo.blocks.header.destination= 0x05;
    txInfo.blocks.opcode = 0xC4;

    //CTxQueueWrite(&txInfo);
    CEC_EnqueueTx(&txInfo);
    return;
}
