/******************************************************************************
 Copyright (C) 2017 Sony Corporation. All Rights Reserved.

 File: host9438_if.c
 Description: Host Control of eARC TX device "Salzburg".
 *****************************************************************************/
/************************************************************
 * HEADER INCLUDE
 ************************************************************/
#include "host9438_if.h"
#include "sii_platform_api.h"
#include "sii9438_api.h"
#include "x_stl_lib.h"
#include "x_drv_cli.h"
#include "hdmi_debug.h"

extern BOOL fgHDMIEARCIsSupported(void);
extern UINT16 SIF_X_Write(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr,
		UINT8 u1WordAddrNum, UINT32 u4WordAddr, UINT8 *pu1Buf,
		UINT16 u2ByteCnt);
extern void SelfDiagTriggerEarcI2CError(void);

char *gStateName[SALZCTRL_STATE_MAX] = {
	"SUSPEND",
	"INITIALIZE",
	"IDLE1 (eARC enable)",
	"DISC1/DISC2 (eARC enable)",
	"EARC (eARC enable)",
	"IDLE2 (eARC enable)",
	"ARC (eARC enable)",
	"ARC (eARC enable2)",
	"IDLE1 (eARC disable)",
	"EARC (eARC disable)",
	"IDLE2 (eARC disable)",
	"ARC (eARC disable1)",
	"ARC (eARC disable2)",
};

char *gMsgName[SALZCTRL_MSG_MAX] = {
	"SUSPEND",
	"CB_LOG_FIFO_CHNG",
	"CB_EARC_HPD_CHNG",
	"CB_EARC_LINK_CHNG",
	"CB_SPDIF_CHAN_STAT_CHNG",
	"CB_EDID_CHNG",
	"CB_GPIO_CHNG",
	"CB_ASSERT",
	"CB_ERX_LATENCY_CHNG",
	"CB_EARC_DISC_TIMEOUT",
	"FUNC_SET_CHANNEL_STATUS",
	"FUNC_SET_AUDIO_INFO_FRAME",
	"FUNC_SET_VALIDITY_BIT",
	"FUNC_SET_5V_STATUS",
	"FUNC_SET_HBRA_INPUT_STATUS",
	"FUNC_SET_ARC_STATUS",
	"FUNC_SET_AUDIO_PATH",
	"FUNC_SET_AUDIO_LATENCY",
	"FUNC_SET_TV_STATUS",
	"FUNC_SET_BT_HP_STATUS",
	"FUNC_SET_ENABLE",
	"FUNC_SET_HDMI_MUTE",
	"FUNC_SET_AUDIO_MUTE",
};

/************************************************************
 * VARIABLE DATAS
 ************************************************************/
AUDIO_FS_TBL g_audio_fs_tbl[] = {
	{ 220 - FS_THRESHOLD, 220 + FS_THRESHOLD, 0x04 },
	{ 441 - FS_THRESHOLD, 441 + FS_THRESHOLD, 0x00 },
	{ 882 - FS_THRESHOLD, 882 + FS_THRESHOLD, 0x08 },
	{ 1764 - FS_THRESHOLD, 1764 + FS_THRESHOLD, 0x0c },
	{ 3528 - FS_THRESHOLD, 3528 + FS_THRESHOLD, 0x0d },
	{ 7056 - FS_THRESHOLD, 7056 + FS_THRESHOLD, 0x8d },
	{ 240 - FS_THRESHOLD, 240 + FS_THRESHOLD, 0x06 },
	{ 480 - FS_THRESHOLD, 480 + FS_THRESHOLD, 0x02 },
	{ 960 - FS_THRESHOLD, 960 + FS_THRESHOLD, 0x0a },
	{ 1920 - FS_THRESHOLD, 1920 + FS_THRESHOLD, 0x0e },
	{ 3840 - FS_THRESHOLD, 3840 + FS_THRESHOLD, 0x05 },
	{ 7680 - FS_THRESHOLD, 7680 + FS_THRESHOLD, 0x09 },
	{ 0, 0, 0 } /* END OF TABLE	*/
};
sii_inst_t s_sii9438_tx_inst = SII_INST_NULL;
HANDLE_T pSii9438MainThreadHdl = NULL_HANDLE;
HANDLE_T pSii9438SubThreadHdl = NULL_HANDLE;
HANDLE_T pSii9438QueueHdl = NULL_HANDLE;
SALZCTRL_STATE gState = SALZCTRL_STATE_SUSPEND;
BOOL gTVStatus;
BOOL gBtHpStatus;
UINT8 gAudioEDIDData[SII_EDID_AUD_SIZE];
EARCStatus_t gEARCStatus = HDMI_EARC_WAITING;
BOOL gEARCConnect = 0;
UINT8 gCapability = 0;
UINT8 gMpeg4AacLcCapability = 0;
BOOL bIsEARCEDIDUpdated; /* EDID is already updated with eARC. */
BOOL bNewAudioEDIDData; /* New Audio EDID data */

/************************************************************
 * Common function
 ************************************************************/
void setStatus(SALZCTRL_STATE inState) {
	LOG(1, "[SalzCtrl] Change State = %s. \n", gStateName[inState]);
	gState = inState;
}

INT32 send_message(SALZCTRL_MSG *pMsg) {
	if (NULL_HANDLE == pSii9438QueueHdl || NULL == pMsg) {
		LOG(0, "[SalzCtrl] x_msg_q_send args are NULL.()\n");
		return OSR_INV_ARG;
	}
	return x_msg_q_send(pSii9438QueueHdl, pMsg, sizeof(SALZCTRL_MSG),
			THREAD_PRIORITY);
}

/************************************************************
 * Sub function
 ************************************************************/
void Change_state_to_eARC_disconnected_at_eARC_enable(void);
void Sequence_to_Set_Audio_info(BOOL bPath);
void setAudioInfo_SPDIF(void);

BOOL setARCMode(enum sii_arc_mode mode) {
	enum sii_arc_mode arc_mode = mode;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_arc_mode_set(s_sii9438_tx_inst, &arc_mode)) {
		LOG(1, "[SalzCtrl] sii9438_arc_mode_set() is fail\n");
		return FALSE;
	}
	LOG(2, "[SalzCtrl] sii9438_arc_mode_set(%d) is success\n", mode);
	return TRUE;
}

void notifyConnectChgToCEC(EARCStatus_t eStatus) {
	BOOL bReady;
	EARCPortStatus_t ePortStatus;

	gEARCStatus = eStatus;
	if (RET_SUCCESS != u1HDMIEARCGetMWReady(&bReady)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetMWReady() is fail\n");
		return;
	}
	if (bReady) {
		LOG(2, "[SalzCtrl] Notify connectivity change to CEC : %d\n", eStatus);
		ePortStatus.u4PortId = HDMI_PORT;
		ePortStatus.eStatus = eStatus;
		vHDMIEARCEventNotify(EARC_EVENT_CONNECT_CHG_TO_CEC,
				(void *) &ePortStatus);
	}
}

void notifyConnectChgToAudio(BOOL eARCConnect, UINT8 capability, UINT8 Mpeg4AacLcCapability) {
	EARCAudioStatus_t eAudioStatus;
	eAudioStatus.bEARCConnect = eARCConnect;
	eAudioStatus.u1Capability = capability;
	eAudioStatus.u1Mpeg4AacLcCapability = Mpeg4AacLcCapability;
	LOG(2, "[SalzCtrl] Notify connectivity change to Audio : %d %d\n",
			eARCConnect, capability, Mpeg4AacLcCapability);
	vHDMIEARCEventNotify(EARC_EVENT_CONNECT_CHG_TO_AUDIO,
			(void *) &eAudioStatus);
}

void notifyHBRAMode(BOOL isHBRAudio) {
	BOOL hbra = isHBRAudio;
	LOG(2, "[SalzCtrl] Notify HBR audio mode : %d\n", hbra);
	vHDMIEARCEventNotify(EARC_EVENT_HBRA_MODE, (void *) &hbra);
}

void notifyConnectChgToApp(BOOL eARCConnect, UINT8 capability, UINT8 Mpeg4AacLcCapability) {
	BOOL bReady;
	EARCAudioStatus_t eAudioStatus;

	if (RET_SUCCESS != u1HDMIEARCGetMWReady(&bReady)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetMWReady() is fail\n");
		return;
	}
	if (bReady) {
		eAudioStatus.bEARCConnect = eARCConnect;
		eAudioStatus.u1Capability = capability;
		eAudioStatus.u1Mpeg4AacLcCapability = Mpeg4AacLcCapability;
		LOG(2, "[SalzCtrl] Notify connectivity change to App : %d %d\n",
				eARCConnect, capability, Mpeg4AacLcCapability);
		vHDMIEARCEventNotify(EARC_EVENT_CONNECT_CHG_TO_APP,
				(void *) &eAudioStatus);

	}
}

BOOL Call_u1HDMIEARCChgHPD(UINT8 u1HPDStatus) {
	if (RET_SUCCESS != u1HDMIEARCChgHPD(u1HPDStatus)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCChgHPD() is fail\n");
		return FALSE;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCChgHPD(0x%02x) is success\n", u1HPDStatus);
	return TRUE;
}

/*
 * Lipsync is handled by CEC not eARC path.
 * It means that u2AudioLatency will not use until take Lipsync as eARC path.
 * Set latency infomation "255" (Unknown Latency)
 * */
void Call_sii9438_erx_latency_req_set(void) {
	struct sii_erx_latency_req erx_latency_req;
	erx_latency_req.data = 255;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_erx_latency_req_set(s_sii9438_tx_inst,
					&erx_latency_req)) {
		LOG(1, "[SalzCtrl] sii9438_erx_latency_req_set() is fail\n");
	}
	LOG(2, "[SalzCtrl] sii9438_erx_latency_req_set() is success\n");
}

void Call_sii9438_earc_latency_query(void) {
	struct sii_erx_latency erx_latency;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_erx_latency_query(s_sii9438_tx_inst, &erx_latency)) {
		LOG(1, "[SalzCtrl] sii9438_erx_latency_query() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] Audio Latency = %d[msec]\n", erx_latency.data);
}

void Config_GPIO3(void) {
	uint16_t GPIO3_output = 0x08; /*GPIO3 on salzburg */
	sii9438_gpio_config_set(s_sii9438_tx_inst, &GPIO3_output); /* Set output setting at GPIO3 on salzburg */
}

void Set_GPIO3_High(void) {
	uint16_t GPIO3_output = 0x08; /*GPIO3 on salzburg */
	sii9438_gpio_output_set(s_sii9438_tx_inst, &GPIO3_output); /* Set high at GPIO3 output on salzburg */
}

void Set_GPIO3_Low(void) {
	uint16_t GPIO3_output = 0x08; /*GPIO3 on salzburg */
	sii9438_gpio_output_clr(s_sii9438_tx_inst, &GPIO3_output); /* Set low at GPIO3 output on salzburg */
}

EARCEDIDInfo_t setEDID(BOOL bConnect) {
	EARCEDIDInfo_t eEDIDInfo;
	x_memset(&eEDIDInfo, 0, sizeof(EARCEDIDInfo_t));
	if (bConnect) {
		x_memcpy(eEDIDInfo.u1DataBlock, gAudioEDIDData,
				sizeof(eEDIDInfo.u1DataBlock));
	}
	eEDIDInfo.bEARCConnect = bConnect;
	if (RET_SUCCESS != u1HDMIEARCUpdateEdid(&eEDIDInfo)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCUpdateEdid() is fail\n");
		bIsEARCEDIDUpdated = FALSE;
	} else {
		LOG(2, "[SalzCtrl] u1HDMIEARCUpdateEdid() is success\n");
		if (eEDIDInfo.bEARCConnect) {
			bIsEARCEDIDUpdated = TRUE;
		} else {
			bIsEARCEDIDUpdated = FALSE;
		}
	}
	return eEDIDInfo;
}

/*
 * isEARCConnected
 * 		0:eARC disconnect / 1:eARC connect
 * path
 * 		0:SPDIF / 1:I2S
 * */
void setBEParam(UINT8 isEARCConnected, UINT8 path) {
	UINT8 BE_PARAM_INFO_SIZE = 8;
	UINT8 param[BE_PARAM_INFO_SIZE]; /* Audio EDID data */
	x_memset(param, 0, BE_PARAM_INFO_SIZE);
	param[0] = isEARCConnected;
	param[7] = path;
	LOG(1,
			"[SalzCtrl] call vHDMIEARCSetBEGenParam(eARC connection : %d, path : %d)\n",
			param[0], param[7]);
	vHDMIEARCSetBEGenParam(0x19, param, BE_PARAM_INFO_SIZE);
}

void setFrequency(UINT8 *pStatus, UINT32 fs) {
	AUDIO_FS_TBL *pFsTbl = g_audio_fs_tbl;
	while (pFsTbl->max != 0) {
		if (pFsTbl->min <= fs && fs <= pFsTbl->max) {
			pStatus[3] &= 0x30; /* Clear the frequency bit	7-6, 3-0*/
			pStatus[3] |= pFsTbl->bit; /* Set the frequency bit	*/
			LOG(1, "[SalzCtrl] Audio Frequency : 0x%x\n", (pStatus[3] & 0x0F));
			return;
		}
		pFsTbl++;
	}
}

void update_EDID_eARC(void) {
	EARCEDIDInfo_t eEDIDInfo;
	UINT8 u1Capability = 0;
	UINT8 u1Mpeg4AacLcCapability = 0;

	LOG(1, "[SalzCtrl] Update to eARC EDID\n");
	eEDIDInfo = setEDID(TRUE);

	if (1 == eEDIDInfo.u1EAC3) {
		u1Capability = (1 == eEDIDInfo.u1DDP) ? 2 : 1;
	}
	
	if (1 == eEDIDInfo.AAC_LC) {
		if (((5 == eEDIDInfo.AAC_LC_Ch) || (6 == eEDIDInfo.AAC_LC_Ch)) & (0 == eEDIDInfo.AAC_LC_22ch)) {
			u1Mpeg4AacLcCapability = 1;
		} else if ((7 == eEDIDInfo.AAC_LC_Ch) & (0 == eEDIDInfo.AAC_LC_22ch)) {
			u1Mpeg4AacLcCapability = 2;
		} else if (1 == eEDIDInfo.AAC_LC_22ch) {
			u1Mpeg4AacLcCapability = 3;
		}
	}
	
	gEARCConnect = TRUE;
	gCapability = u1Capability;
	gMpeg4AacLcCapability = u1Mpeg4AacLcCapability;
	notifyConnectChgToCEC(HDMI_EARC_ENABLED);
	notifyConnectChgToAudio(TRUE, u1Capability, u1Mpeg4AacLcCapability);
	notifyConnectChgToApp(TRUE, u1Capability, u1Mpeg4AacLcCapability);
	bNewAudioEDIDData = FALSE;
	x_thread_delay(150);
	Call_u1HDMIEARCChgHPD(HDMIALL_BIT);
}

void update_EDID_ARC(void) {
	LOG(1, "[SalzCtrl] Update to ARC EDID\n");
	setEDID(FALSE);
	setAudioInfo_SPDIF();
	gEARCConnect = FALSE;
	gCapability = NO_AC3_CAPABILITY;
	gMpeg4AacLcCapability = NO_MPEG4AACLC_CAPABILITY;
	notifyConnectChgToCEC(HDMI_EARC_WAITING);
	notifyConnectChgToAudio(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
	notifyConnectChgToApp(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
	setBEParam(0x00, 0x00); /* eARC disconnect / SPDIF */
	x_thread_delay(150);
	Call_u1HDMIEARCChgHPD(HDMIALL_BIT);
}

void setAudioInfo_I2S_HBR(void) {
	UINT32 u4AudioFs;
	enum sii9438_insertion_mode mode;
	enum sii9438_vbit vbit;
	struct sii_audio_info audio_info;
	EARCChannelStatus_t eChannelStatus;
	EARCAudioInfoFrame_t eAudioInfoFrame;

	notifyHBRAMode(true);

	mode = SII9438_INSERTION_MODE__I2S8;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
		LOG(1, "[SalzCtrl] sii9438_insertion_mode_set(%d) is fail\n", mode);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_insertion_mode_set(%d) is success\n", mode);

	if (RET_SUCCESS != u1HDMIEARCGetChannelStatus(&eChannelStatus)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetChannelStatus() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetChannelStatus() is success\n");

	/* u4AudioFs: 1 = 100Hz */
	if (RET_SUCCESS != u1HDMIEARCGetAudioFs(&u4AudioFs)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetAudioFs() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetAudioFs() is %d\n", u4AudioFs);
	u4AudioFs *= FS_HBR_ADJUST;
	setFrequency(eChannelStatus.u1Status, u4AudioFs);
	eChannelStatus.u1Status[0] |= 0x02;

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_channel_status_set(s_sii9438_tx_inst,
					(struct sii_channel_status *) eChannelStatus.u1Status)) {
		LOG(1, "[SalzCtrl] sii9438_channel_status_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_channel_status_set() is success\n");

	if (RET_SUCCESS != u1HDMIEARCGetAudInfoFrame(&eAudioInfoFrame)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is success\n");

	x_memset(&audio_info, 0, sizeof(struct sii_audio_info));
	audio_info.speaker_alloc = eAudioInfoFrame.u1Info[3];
	audio_info.lfepbl = eAudioInfoFrame.u1Info[4] & 0x03; /* bit1 - bit0 */
	audio_info.lsv = (eAudioInfoFrame.u1Info[4] & 0x78) >> 3; /* bit6 - bit3 */
	audio_info.dm_inh = (eAudioInfoFrame.u1Info[4] & 0x80) >> 7; /* bit7 */
	/* Channel/Speaker Allocation is 0x00 when the channel status bit[1] is true. */
	audio_info.speaker_alloc = 0x00;

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_audio_info_set(s_sii9438_tx_inst, &audio_info)) {
		LOG(1, "[SalzCtrl] sii9438_audio_info_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_audio_info_set() is success\n");

	vbit = SII9438_VBIT_1;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
		LOG(1, "[SalzCtrl] sii9438_vbit_set(%d) is fail\n", vbit);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_vbit_set(%d) is success\n", vbit);
}

void setAudioInfo_I2S_NOT_HBR(void) {
	UINT8 u1Validity;
	UINT32 u4AudioFs;
	enum sii9438_insertion_mode mode;
	enum sii9438_vbit vbit;
	struct sii_audio_info audio_info;
	EARCChannelStatus_t eChannelStatus;
	EARCAudioInfoFrame_t eAudioInfoFrame;

	notifyHBRAMode(false);

	mode = SII9438_INSERTION_MODE__I2S8;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
		LOG(1, "[SalzCtrl] sii9438_insertion_mode_set(%d) is fail\n", mode);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_insertion_mode_set(%d) is success\n", mode);

	if (RET_SUCCESS != u1HDMIEARCGetChannelStatus(&eChannelStatus)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetChannelStatus() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetChannelStatus() is success\n");

	/* u4AudioFs: 1 = 100Hz */
	if (RET_SUCCESS != u1HDMIEARCGetAudioFs(&u4AudioFs)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetAudioFs() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetAudioFs() is %d\n", u4AudioFs);
	setFrequency(eChannelStatus.u1Status, u4AudioFs);

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_channel_status_set(s_sii9438_tx_inst,
					(struct sii_channel_status *) eChannelStatus.u1Status)) {
		LOG(1, "[SalzCtrl] sii9438_channel_status_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_channel_status_set() is success\n");

	if (RET_SUCCESS != u1HDMIEARCGetAudInfoFrame(&eAudioInfoFrame)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is success\n");

	audio_info.lfepbl = eAudioInfoFrame.u1Info[4] & 0x03; /* bit1 - bit0 */
	audio_info.lsv = (eAudioInfoFrame.u1Info[4] & 0x78) >> 3; /* bit6 - bit3 */
	audio_info.dm_inh = (eAudioInfoFrame.u1Info[4] & 0x80) >> 7; /* bit7 */
	/* Channel/Speaker Allocation is 0x00 when the channel status bit[1] is true. */
	if ((eChannelStatus.u1Status[0] & 0x02) == 0x02) {
		audio_info.speaker_alloc = 0x00;
	} else {
		audio_info.speaker_alloc = eAudioInfoFrame.u1Info[3];
	}

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_audio_info_set(s_sii9438_tx_inst, &audio_info)) {
		LOG(1, "[SalzCtrl] sii9438_audio_info_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_audio_info_set() is success\n");

	if (RET_SUCCESS != u1HDMIEARCGetValidityBit(&u1Validity)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetValidityBit() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetValidityBit() is success\n");

	vbit = (u1Validity) ? SII9438_VBIT_1 : SII9438_VBIT_0;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
		LOG(1, "[SalzCtrl] sii9438_vbit_set(%d) is fail\n", vbit);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_vbit_set(%d) is success\n", vbit);
}

void setAudioInfo_SPDIF(void) {
	enum sii9438_insertion_mode mode;
	enum sii9438_vbit vbit;
	struct sii_audio_info audio_info;

	mode = SII9438_INSERTION_MODE__SPDIF2;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
		LOG(1, "[SalzCtrl] sii9438_insertion_mode_set(%d) is fail\n", mode);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_insertion_mode_set(%d) is success\n", mode);

	audio_info.speaker_alloc = 0;
	audio_info.lfepbl = 0;
	audio_info.lsv = 0;
	audio_info.dm_inh = false;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_audio_info_set(s_sii9438_tx_inst, &audio_info)) {
		LOG(1, "[SalzCtrl] sii9438_audio_info_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_audio_info_set() is success\n");

	vbit = SII9438_VBIT_BYPASS;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
		LOG(1, "[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_BYPASS) is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_BYPASS) is success\n");
}

/*==================================================*/
/* Mute API                                         */
/*==================================================*/
void setHDMIMute(BOOL mute) {
	AUDIO_PATH_ST audioPath;
	if (mute) {
		return;
	}
	x_memset(&audioPath, 0, sizeof(AUDIO_PATH_ST));
	u1HDMIEARCIsHDMIInput(&audioPath.bPath);
	/* TRUE is HDMI. */
	if (audioPath.bPath && gEARCConnect) {
		Sequence_to_Set_Audio_info(audioPath.bPath);
	}
}

void setAudioMute(BOOL mute) {
	AUDIO_PATH_ST audioPath;
	if (mute) {
		return;
	}
	x_memset(&audioPath, 0, sizeof(AUDIO_PATH_ST));
	u1HDMIEARCIsHDMIInput(&audioPath.bPath);
	/* FALSE is SPDIF. */
	if (!audioPath.bPath && gEARCConnect) {
		Sequence_to_Set_Audio_info(audioPath.bPath);
	}
}

/*==================================================*/
/* Change Channel Status                            */
/*==================================================*/
void setChannelStatus(UINT8 *pStatus) {
	AUDIO_PATH_ST audioPath;
	UINT8 u1Hbr;
	UINT32 u4AudioFs;
	enum sii9438_insertion_mode mode;
	struct sii_channel_status channel_status;
	EARCAudioInfoFrame_t eAudioInfoFrame;
	struct sii_audio_info audio_info;

	x_memset(&audioPath, 0, sizeof(AUDIO_PATH_ST));
	u1HDMIEARCIsHDMIInput(&audioPath.bPath);
	if (!audioPath.bPath || !gEARCConnect) {
		return;
	}

	mode = SII9438_INSERTION_MODE__I2S8;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
		LOG(1, "[SalzCtrl] sii9438_insertion_mode_set(%d) is fail\n", mode);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_insertion_mode_set(%d) is success\n", mode);

	if (RET_SUCCESS != u1HDMIEARCIsHBRInput(&u1Hbr)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCIsHBRInput() is fail\n");
		u1Hbr = 0;
	} else {
		LOG(2, "[SalzCtrl] u1HDMIEARCIsHBRInput() is success\n");
	}

	/* u4AudioFs: 1 = 100Hz */
	if (RET_SUCCESS != u1HDMIEARCGetAudioFs(&u4AudioFs)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetAudioFs() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetAudioFs() is %d\n", u4AudioFs);

	if (u1Hbr) {
		LOG(1, "[SalzCtrl] HBR Audio\n");
		u4AudioFs *= FS_HBR_ADJUST;
		pStatus[0] |= 0x02;
	}

	setFrequency(pStatus, u4AudioFs);

	memcpy(channel_status.data, pStatus, SII_CHANNEL_STATUS_SIZE);
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_channel_status_set(s_sii9438_tx_inst, &channel_status)) {
		LOG(1, "[SalzCtrl] sii9438_channel_status_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_channel_status_set() is success\n");

	if (RET_SUCCESS != u1HDMIEARCGetAudInfoFrame(&eAudioInfoFrame)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is success\n");

	audio_info.lfepbl = eAudioInfoFrame.u1Info[4] & 0x03; /* bit1 - bit0 */
	audio_info.lsv = (eAudioInfoFrame.u1Info[4] & 0x78) >> 3; /* bit6 - bit3 */
	audio_info.dm_inh = (eAudioInfoFrame.u1Info[4] & 0x80) >> 7; /* bit7 */
	/* Channel/Speaker Allocation is 0x00 when the channel status bit[1] is true. */
	if ((pStatus[0] & 0x02) == 0x02) {
		audio_info.speaker_alloc = 0x00;
	} else {
		audio_info.speaker_alloc = eAudioInfoFrame.u1Info[3];
	}

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_audio_info_set(s_sii9438_tx_inst, &audio_info)) {
		LOG(1, "[SalzCtrl] sii9438_audio_info_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_audio_info_set() is success\n");
}

/*==================================================*/
/* Change Audio infoframe                           */
/*==================================================*/
void setAudioInfoFrame(UINT8 *pInfo) {
	AUDIO_PATH_ST audioPath;
	struct sii_audio_info audio_info;
	UINT8 u1Hbr;
	enum sii9438_insertion_mode mode;

	x_memset(&audioPath, 0, sizeof(AUDIO_PATH_ST));
	u1HDMIEARCIsHDMIInput(&audioPath.bPath);
	if (!audioPath.bPath || !gEARCConnect) {
		return;
	}

	mode = SII9438_INSERTION_MODE__I2S8;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
		LOG(1, "[SalzCtrl] sii9438_insertion_mode_set(%d) is fail\n", mode);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_insertion_mode_set(%d) is success\n", mode);

	audio_info.lfepbl = pInfo[4] & 0x03; /* bit1 - bit0 */
	audio_info.lsv = (pInfo[4] & 0x78) >> 3; /* bit6 - bit3 */
	audio_info.dm_inh = (pInfo[4] & 0x80) >> 7; /* bit7 */
	audio_info.speaker_alloc = pInfo[3];

	if (RET_SUCCESS != u1HDMIEARCIsHBRInput(&u1Hbr)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCIsHBRInput() is fail\n");
		u1Hbr = 0;
	} else {
		LOG(2, "[SalzCtrl] u1HDMIEARCIsHBRInput() is success\n");
	}

	if (u1Hbr) {
		audio_info.speaker_alloc = 0x00;
	} else {
		EARCChannelStatus_t eChannelStatus;
		if (RET_SUCCESS == u1HDMIEARCGetChannelStatus(&eChannelStatus)) {
			LOG(2, "[SalzCtrl] u1HDMIEARCGetChannelStatus() is success\n");
			/* Channel/Speaker Allocation is 0x00 when the channel status bit[1] is true. */
			if ((eChannelStatus.u1Status[0] & 0x02) == 0x02) {
				audio_info.speaker_alloc = 0x00;
			}
		} else {
			LOG(1, "[SalzCtrl] u1HDMIEARCGetChannelStatus() is fail\n");
		}
	}

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_audio_info_set(s_sii9438_tx_inst, &audio_info)) {
		LOG(1, "[SalzCtrl] sii9438_audio_info_set() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_audio_info_set() is success\n");
}

/*==================================================*/
/* Change Validity bit and HBRA input status        */
/*==================================================*/
void setHBRAInputStatus(BOOL bHbr) {
	AUDIO_PATH_ST audioPath;

	x_memset(&audioPath, 0, sizeof(AUDIO_PATH_ST));
	u1HDMIEARCIsHDMIInput(&audioPath.bPath);
	/* TRUE is HDMI. */
	if (audioPath.bPath && gEARCConnect) {
		Sequence_to_Set_Audio_info(audioPath.bPath);
	}
}

void setValidityBit(BOOL bValidity) {
	AUDIO_PATH_ST audioPath;
	enum sii9438_vbit vbit;
	UINT8 u1Hbr;
	enum sii9438_insertion_mode mode;

	x_memset(&audioPath, 0, sizeof(AUDIO_PATH_ST));
	u1HDMIEARCIsHDMIInput(&audioPath.bPath);
	if (!audioPath.bPath && gEARCConnect) {
		return;
	}

	mode = SII9438_INSERTION_MODE__I2S8;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
		LOG(1, "[SalzCtrl] sii9438_insertion_mode_set(%d) is fail\n", mode);
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_insertion_mode_set(%d) is success\n", mode);

	if (RET_SUCCESS != u1HDMIEARCIsHBRInput(&u1Hbr)) {
		LOG(1, "[SalzCtrl] u1HDMIEARCIsHBRInput() is fail\n");
		u1Hbr = 0;
	} else {
		LOG(2, "[SalzCtrl] u1HDMIEARCIsHBRInput() is success\n");
	}

	if (u1Hbr) {
		vbit = SII9438_VBIT_1;
	} else {
		vbit = (bValidity) ? SII9438_VBIT_1 : SII9438_VBIT_0;
	}

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
		LOG(1, "[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_BYPASS) is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_BYPASS) is success\n");
}

/*==================================================*/
/* TX IDLE1 at eARC_enable                          */
/*==================================================*/
void Go_to_TX_IDLE1_at_eARC_enable(void) {
	EARCInitInfo_t eStatusInfo;

	setStatus(SALZCTRL_STATE_IDLE1_EARC_ENABLE);
	notifyConnectChgToCEC(HDMI_EARC_WAITING);
	setARCMode(SII_ARC_MODE__EARC);
	x_thread_delay(500);
	Call_u1HDMIEARCChgHPD(HDMIALL_BIT);
	x_thread_delay(150);
	u1HDMIEARCGetInitialStatus(&eStatusInfo);
	if (eStatusInfo.u15VStatus & HDMI3_BIT) {
		Set_GPIO3_Low();
	}
	setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
}

/*==================================================*/
/* TX DISC1/DISC2 at eARC_enable                    */
/*==================================================*/
void Go_to_TX_DISC1DISC2_at_eARC_enable(void) {
	setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
}

/*==================================================*/
/* TX eARC at eARC_enable                           */
/*==================================================*/
void Go_to_TX_eARC_at_eARC_enable(void) {
	setStatus(SALZCTRL_STATE_EARC_EARC_ENABLE);
}

/*==================================================*/
/* EDID updating at eARC_enable                     */
/*==================================================*/
void EDID_updating_at_eARC_enable(void) {
	LOG(1, "[SalzCtrl] gBtHpStatus = %d\n", gBtHpStatus);
	LOG(1, "[SalzCtrl] gTVStatus = %d\n", gTVStatus);
	LOG(1, "[SalzCtrl] bIsEARCEDIDUpdated = %d\n", bIsEARCEDIDUpdated);
	LOG(1, "[SalzCtrl] bNewAudioEDIDData = %d\n", bNewAudioEDIDData);
	LOG(1, "[SalzCtrl] gAudioEDIDData[0] = %d\n", gAudioEDIDData[0]);
	if (gBtHpStatus || !gTVStatus) {
		if (bIsEARCEDIDUpdated) {
			update_EDID_ARC();
		}
	} else if (bNewAudioEDIDData
			|| (gAudioEDIDData[0] && !bIsEARCEDIDUpdated)) {
		update_EDID_eARC();
	}
}

/*==================================================*/
/* Audio output at eARC_enable                      */
/*==================================================*/
void Audio_output_at_eARC_enable(BOOL bPath) {
	if (bIsEARCEDIDUpdated) {
		Sequence_to_Set_Audio_info(bPath);
	}
}

/*==================================================*/
/* Notification of eARC_LINK_CHNG at eARC_enable    */
/*==================================================*/
void Notification_of_eARC_LINK_CHNG_at_eARC_enable(void) {
	BOOL b_earc_link_status = FALSE;
	INT16 u2AudioLatency; /* Not use for Uroboros DV */

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_earc_link_query(s_sii9438_tx_inst,
					&b_earc_link_status)) {
		LOG(1, "[SalzCtrl] sii9438_earc_link_query() is fail\n");
		return;
	}
	LOG(2, "[SalzCtrl] sii9438_earc_link_query() is success\n");

	if (b_earc_link_status) {
		LOG(1, "[SalzCtrl] eARC is Connected\n");
		if (RET_SUCCESS != u1HDMIEARCGetAudioLatency(&u2AudioLatency)) {
			LOG(1, "[SalzCtrl] u1HDMIEARCGetMWReady() is fail\n");
		} else {
			LOG(2, "[SalzCtrl] Audio Latency = %d[msec]: \n", u2AudioLatency);
		}
		Call_sii9438_erx_latency_req_set();
		Go_to_TX_eARC_at_eARC_enable();
	} else {
		LOG(1, "[SalzCtrl] eARC is Disconnected\n");
		Change_state_to_eARC_disconnected_at_eARC_enable();
	}
}

/*==================================================*/
/* Change state to eARC disconnected at eARC_enable */
/*==================================================*/
void Change_state_to_eARC_disconnected_at_eARC_enable(void) {
	setEDID(FALSE);
	setAudioInfo_SPDIF();
	gEARCConnect = FALSE;
	gCapability = NO_AC3_CAPABILITY;
	gMpeg4AacLcCapability = NO_MPEG4AACLC_CAPABILITY;
	notifyConnectChgToCEC(HDMI_EARC_WAITING);
	notifyConnectChgToAudio(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
	notifyConnectChgToApp(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
	setBEParam(0x00, 0x00); /* eARC disconnect / SPDIF */
	x_memset(gAudioEDIDData, 0, SII_EDID_AUD_SIZE);
	Go_to_TX_DISC1DISC2_at_eARC_enable();
}

/*==================================================*/
/* TX IDLE2 at eARC_enable                          */
/*==================================================*/
void Go_to_TX_IDLE2_at_eARC_enable(void) {
	setStatus(SALZCTRL_STATE_IDLE2_EARC_ENABLE);
}

/*==================================================*/
/* TX ARC at eARC_enable                            */
/*==================================================*/
void Go_to_TX_ARC_at_eARC_enable(void) {
	setStatus(SALZCTRL_STATE_ARC_EARC_ENABLE);
}

/*==================================================*/
/* TX ARC at eARC_enable2                            */
/*==================================================*/
void Go_to_TX_ARC_at_eARC_enable2(void) {
	setStatus(SALZCTRL_STATE_ARC_EARC_ENABLE2);
}

/*==================================================*/
/* TX DISC1/DISC2 at eARC_disable                   */
/*==================================================*/
void Go_to_TX_DISC1DISC2_at_eARC_disable(void) {
	notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
	Set_GPIO3_Low();
	setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
}

/*==================================================*/
/* TX eARC at eARC_disable                          */
/*==================================================*/
void Go_to_TX_eARC_at_eARC_disable(void) {
	setStatus(SALZCTRL_STATE_EARC_EARC_DISABLE);
}

/*==================================================*/
/* TX IDLE2 at eARC_disable                         */
/*==================================================*/
void Go_to_TX_IDLE2_at_eARC_disable(void) {
	setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
}

/*==================================================*/
/* TX ARC at eARC_disable 1                         */
/*==================================================*/
void Go_to_TX_ARC_at_eARC_disable1(void) {
	setStatus(SALZCTRL_STATE_ARC_EARC_DISABLE1);
}

/*==================================================*/
/* TX ARC at eARC_disable 2                         */
/*==================================================*/
void Go_to_TX_ARC_at_eARC_disable2(void) {
	setStatus(SALZCTRL_STATE_ARC_EARC_DISABLE2);
}

void Decide_insertion_mode_and_HBR_mode(UINT8 hbr);
/*==================================================*/
/* Sequence to Set Audio info                       */
/*==================================================*/
void Sequence_to_Set_Audio_info(BOOL bPath) {
	UINT8 u1Hbr;
	if (bPath) {
		LOG(1, "[SalzCtrl] Path : I2S\n");
		if (RET_SUCCESS != u1HDMIEARCIsHBRInput(&u1Hbr)) {
			LOG(1, "[SalzCtrl] u1HDMIEARCIsHBRInput() is fail\n");
			return;
		}
		LOG(2, "[SalzCtrl] u1HDMIEARCIsHBRInput() is success\n");

		setBEParam(0x01, 0x01); /* eARC connect / I2S */
		Decide_insertion_mode_and_HBR_mode(u1Hbr);
	} else {
		LOG(1, "[SalzCtrl] Path : SPDIF\n");
		setBEParam(0x01, 0x00); /* eARC connect / SPDIF */
		setAudioInfo_SPDIF();
	}
	return;
}

/*==================================================*/
/* Decide insertion mode and HBR mode               */
/*==================================================*/
void Decide_insertion_mode_and_HBR_mode(UINT8 hbr) {
	if (hbr) {
		LOG(1, "[SalzCtrl] setAudioInfo : HBR Audio\n");
		setAudioInfo_I2S_HBR();
	} else {
		LOG(1, "[SalzCtrl] setAudioInfo : NOT HBR Audio\n");
		setAudioInfo_I2S_NOT_HBR();
	}
}

void event_function(SALZCTRL_MSG *pMsg) {
	EARCInitInfo_t eStatusInfo;

	if (SALZCTRL_STATE_INITIALIZE == gState) {
		return;
	}

	if (SALZCTRL_MSG_SUSPEND == pMsg->id) {
		notifyConnectChgToAudio(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
		setStatus(SALZCTRL_STATE_SUSPEND);
		return;
	}

	switch (gState) {

	/*==================================*/
	/* TX DISC1/DISC2 at eARC_enable    */
	/*==================================*/
	case SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE:
		switch (pMsg->id) {
		case SALZCTRL_MSG_CB_EARC_LINK_CHNG: /* SII9438_EVENT_FLAGS_EARC_LINK_CHNG comes */
			Notification_of_eARC_LINK_CHNG_at_eARC_enable();
			break;
		case SALZCTRL_MSG_FUNC_SET_ENABLE: /* eARC_enable changes to disable */
			if (!pMsg->data.setEnable.bEn) {
				setARCMode(SII_ARC_MODE__ARC);
				Go_to_TX_DISC1DISC2_at_eARC_disable();
			}
			break;
		case SALZCTRL_MSG_CB_EARC_DISC_TIMEOUT: /* SII9438_EVENT_FLAGS_DISC_TIMEOUT comes */
			setARCMode(SII_ARC_MODE__ARC);
			notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
			Set_GPIO3_Low();
			Go_to_TX_IDLE2_at_eARC_enable();
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* TV Status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* BT and headphone status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		case SALZCTRL_MSG_FUNC_SET_5V_STATUS: /* eARC port of 5V changes */
			if (!(pMsg->data.set5VStatus.u15VStatus & HDMI3_BIT)) {
				u1HDMIEARCGetInitialStatus(&eStatusInfo);
				if (!(eStatusInfo.u15VStatus & HDMI3_BIT)) {
					Set_GPIO3_High();
					setARCMode(SII_ARC_MODE__NONE);
				}
			} else {
				setARCMode(SII_ARC_MODE__EARC);
				x_thread_delay(150);
				Set_GPIO3_Low();
			}
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX eARC at eARC_enable           */
	/*==================================*/
	case SALZCTRL_STATE_EARC_EARC_ENABLE:
		switch (pMsg->id) {
		case SALZCTRL_MSG_FUNC_SET_HDMI_MUTE:
			setHDMIMute(pMsg->data.setHdmiMute.bMute);
			break;
		case SALZCTRL_MSG_FUNC_SET_AUDIO_MUTE:
			setAudioMute(pMsg->data.setAudioMute.bMute);
			break;
		case SALZCTRL_MSG_FUNC_SET_CHANNEL_STATUS:
			setChannelStatus(
					pMsg->data.setChannelStatus.channelStatus.u1Status);
			break;
		case SALZCTRL_MSG_FUNC_SET_AUDIO_INFO_FRAME:
			setAudioInfoFrame(
					pMsg->data.setAudioInfoFrame.audioInfoFrame.u1Info);
			break;
		case SALZCTRL_MSG_FUNC_SET_VALIDITY_BIT:
			setValidityBit(pMsg->data.setValidityBit.bValidity);
			break;
		case SALZCTRL_MSG_FUNC_SET_HBRA_INPUT_STATUS:
			setHBRAInputStatus(pMsg->data.setHBRAInputStatus.bHbr);
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* BT and headphone status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			EDID_updating_at_eARC_enable();
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* TV Status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			EDID_updating_at_eARC_enable();
			break;
		case SALZCTRL_MSG_FUNC_SET_AUDIO_LATENCY: /* Audio driver passes latency data */
			Call_sii9438_erx_latency_req_set();
			{
				AUDIO_PATH_ST audioPath;
				x_memset(&audioPath, 0, sizeof(AUDIO_PATH_ST));
				u1HDMIEARCIsHDMIInput(&audioPath.bPath);
				/* eARC connect / (I2S or SPDIF */
				setBEParam(0x01, (audioPath.bPath) ? 0x01 : 0x00);
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_AUDIO_PATH: /* Audio driver sets audio path */
			Audio_output_at_eARC_enable(pMsg->data.setAudioPath.bPath);
			break;
		case SALZCTRL_MSG_CB_ERX_LATENCY_CHNG: /* SII9438_EVENT_FLAGS_ERX_LATENCY_CHNG comes */
			Call_sii9438_earc_latency_query();
			break;
		case SALZCTRL_MSG_FUNC_SET_5V_STATUS: /* eARC port of 5V changes false */
			if (!(pMsg->data.set5VStatus.u15VStatus & HDMI3_BIT)) {
				u1HDMIEARCGetInitialStatus(&eStatusInfo);
				if (!(eStatusInfo.u15VStatus & HDMI3_BIT)) {
					Set_GPIO3_High();
					setARCMode(SII_ARC_MODE__NONE);
				}
			}
			break;
		case SALZCTRL_MSG_CB_EDID_CHNG: /* SII9438_EVENT_FLAGS_EDID_CHNG comes */
			if (SII_RETURN_VALUE__SUCCESS
					!= sii9438_earc_caps_ds_query(s_sii9438_tx_inst, 0,
							gAudioEDIDData, SII_EDID_AUD_SIZE)) {
				LOG(1, "[SalzCtrl] sii9438_earc_caps_ds_query() is fail\n");
				x_memset(gAudioEDIDData, 0, SII_EDID_AUD_SIZE);
			} else {
				LOG(2, "[SalzCtrl] sii9438_earc_caps_ds_query() is success\n");
				bNewAudioEDIDData = TRUE;
				EDID_updating_at_eARC_enable();
			}
			break;
		case SALZCTRL_MSG_CB_EARC_LINK_CHNG: /* SII9438_EVENT_FLAGS_EARC_LINK_CHNG comes */
			Notification_of_eARC_LINK_CHNG_at_eARC_enable();
			break;
		case SALZCTRL_MSG_FUNC_SET_ENABLE: /* eARC_enable changes to disable */
			if (!pMsg->data.setEnable.bEn) {
				setARCMode(SII_ARC_MODE__ARC);
				Go_to_TX_eARC_at_eARC_disable();
			}
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX IDLE2 at eARC_enable          */
	/*==================================*/
	case SALZCTRL_STATE_IDLE2_EARC_ENABLE:
		switch (pMsg->id) {
		case SALZCTRL_MSG_FUNC_SET_ARC_STATUS: /* ARC is initiated */
			if (pMsg->data.setArcStatus.bOnOff) {
				Go_to_TX_ARC_at_eARC_enable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_ENABLE: /* eARC_enable changes to disable */
			if (!pMsg->data.setEnable.bEn) {
				Go_to_TX_IDLE2_at_eARC_disable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_5V_STATUS: /* eARC port of 5V changes false */
			if (!(pMsg->data.set5VStatus.u15VStatus & HDMI3_BIT)) {
				Call_u1HDMIEARCChgHPD(HDMIALL_BIT & ~HDMI3_BIT);
				Set_GPIO3_High();
				Go_to_TX_IDLE1_at_eARC_enable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* BT and headphone status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* TV Status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX ARC at eARC_enable            */
	/*==================================*/
	case SALZCTRL_STATE_ARC_EARC_ENABLE:
		switch (pMsg->id) {
		case SALZCTRL_MSG_FUNC_SET_ENABLE: /* eARC_enable changes to disable */
			if (!pMsg->data.setEnable.bEn) {
				Go_to_TX_ARC_at_eARC_disable1();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_ARC_STATUS: /* ARC is terminated */
			if (!pMsg->data.setArcStatus.bOnOff) {
				Go_to_TX_IDLE2_at_eARC_enable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* BT and headphone status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* TV Status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		case SALZCTRL_MSG_FUNC_SET_5V_STATUS: /* eARC port of 5V changes false */
			if (!(pMsg->data.set5VStatus.u15VStatus & HDMI3_BIT)) {
				Call_u1HDMIEARCChgHPD(HDMIALL_BIT & ~HDMI3_BIT);
				Set_GPIO3_High();
				notifyConnectChgToCEC(HDMI_EARC_WAITING);
				Go_to_TX_ARC_at_eARC_enable2();
			}
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX ARC at eARC_enable2            */
	/*==================================*/
	case SALZCTRL_STATE_ARC_EARC_ENABLE2:
		switch (pMsg->id) {
		case SALZCTRL_MSG_FUNC_SET_ENABLE: /* eARC_enable changes to disable */
			if (!pMsg->data.setEnable.bEn) {
				Call_u1HDMIEARCChgHPD(HDMIALL_BIT);
				Set_GPIO3_Low();
				Go_to_TX_DISC1DISC2_at_eARC_disable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_ARC_STATUS: /* ARC is terminated */
			if (!pMsg->data.setArcStatus.bOnOff) {
				Go_to_TX_IDLE1_at_eARC_enable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* BT and headphone status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* TV Status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX eARC at eARC_disable          */
	/*==================================*/
	case SALZCTRL_STATE_EARC_EARC_DISABLE:
		switch (pMsg->id) {
		case SALZCTRL_MSG_CB_EARC_LINK_CHNG: /* SII9438_EVENT_FLAGS_EARC_LINK_CHNG comes */
		{
			EARCEDIDInfo_t eEDIDInfo;
			x_memset(&eEDIDInfo, 0, sizeof(EARCEDIDInfo_t));
			if (RET_SUCCESS != u1HDMIEARCUpdateEdid(&eEDIDInfo)) {
				LOG(1, "[SalzCtrl] u1HDMIEARCUpdateEdid() is fail\n");
			} else {
				LOG(2, "[SalzCtrl] u1HDMIEARCUpdateEdid() is success\n");
			}
			setAudioInfo_SPDIF();
			bIsEARCEDIDUpdated = FALSE;
			gEARCConnect = FALSE;
			gCapability = NO_AC3_CAPABILITY;
			gMpeg4AacLcCapability = NO_MPEG4AACLC_CAPABILITY;
			notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
			notifyConnectChgToAudio(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
			notifyConnectChgToApp(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
			setBEParam(0x00, 0x00); /* eARC disconnect / SPDIF */
			Go_to_TX_DISC1DISC2_at_eARC_disable();
		}
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* BT and headphone status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* TV Status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX IDLE2 at eARC_disable         */
	/*==================================*/
	case SALZCTRL_STATE_IDLE2_EARC_DISABLE:
		switch (pMsg->id) {
		case SALZCTRL_MSG_FUNC_SET_ARC_STATUS: /* ARC is initiated */
			if (pMsg->data.setArcStatus.bOnOff) {
				Go_to_TX_ARC_at_eARC_disable1();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_ENABLE: /* eARC_enable changes to enable */
			if (pMsg->data.setEnable.bEn) {
				Call_u1HDMIEARCChgHPD(HDMIALL_BIT & ~HDMI3_BIT);
				Go_to_TX_IDLE1_at_eARC_enable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* BT and headphone status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* TV Status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX ARC at eARC_disable1          */
	/*==================================*/
	case SALZCTRL_STATE_ARC_EARC_DISABLE1:
		switch (pMsg->id) {
		case SALZCTRL_MSG_FUNC_SET_ENABLE: /* eARC_enable changes to enable */
			if (pMsg->data.setEnable.bEn) {
				Call_u1HDMIEARCChgHPD(HDMIALL_BIT & ~HDMI3_BIT);
				notifyConnectChgToCEC(HDMI_EARC_WAITING);
				Go_to_TX_ARC_at_eARC_disable2();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_ARC_STATUS: /* ARC is terminated */
			if (!pMsg->data.setArcStatus.bOnOff) {
				Go_to_TX_IDLE2_at_eARC_disable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* BT and headphone status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* TV Status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		default:
			break;
		}
		break;

	/*==================================*/
	/* TX ARC at eARC_disable2          */
	/*==================================*/
	case SALZCTRL_STATE_ARC_EARC_DISABLE2:
		switch (pMsg->id) {
		case SALZCTRL_MSG_FUNC_SET_ARC_STATUS: /* ARC is terminated */
			if (!pMsg->data.setArcStatus.bOnOff) {
				Go_to_TX_IDLE1_at_eARC_enable();
			}
			break;
		case SALZCTRL_MSG_FUNC_SET_TV_STATUS: /* BT and headphone status is changed */
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			break;
		case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS: /* TV Status is changed */
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			break;
		default:
			break;
		}
		break;
	default:
		break;
	}
}

/************************************************************
 * PUBLIC FUNCTIONS
 ************************************************************/
UINT8 u1SetChannelStatus(EARCChannelStatus_t *eStatus) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_CHANNEL_STATUS;
	memcpy(&msg.data.setChannelStatus.channelStatus, eStatus,
			sizeof(EARCChannelStatus_t));
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Channel Status:\n");
	send_message(&msg);
	return 0;
}

UINT8 u1SetAudioInfoFrame(EARCAudioInfoFrame_t *eInfoFrame) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_AUDIO_INFO_FRAME;
	memcpy(&msg.data.setAudioInfoFrame.audioInfoFrame, eInfoFrame,
			sizeof(EARCAudioInfoFrame_t));
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Audio InfoFrame:\n");
	send_message(&msg);
	return 0;
}

UINT8 u1SetValidityBit(BOOL bValidity) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_VALIDITY_BIT;
	msg.data.setValidityBit.bValidity = bValidity;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Validity Bit:0x%x\n", bValidity);
	send_message(&msg);
	return 0;
}

UINT8 u1Set5VStatus(UINT8 u15VStatus) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_5V_STATUS;
	msg.data.set5VStatus.u15VStatus = u15VStatus;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set 5V Status:0x%x\n", u15VStatus);
	send_message(&msg);
	return 0;
}

UINT8 u1SetHBRAInputStatus(BOOL bHbr) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_HBRA_INPUT_STATUS;
	msg.data.setHBRAInputStatus.bHbr = bHbr;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set HBRA Input Status:0x%x\n", bHbr);
	send_message(&msg);
	return 0;
}

UINT8 u1SetArcStatus(BOOL bOnOff) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_ARC_STATUS;
	msg.data.setArcStatus.bOnOff = bOnOff;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Arc Status:0x%x\n", bOnOff);
	send_message(&msg);
	return 0;
}

UINT8 u1SetAudioPath(BOOL bPath) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_AUDIO_PATH;
	msg.data.setAudioPath.bPath = bPath;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Audio Path:%s\n",
			bPath ? "I2S" : "SPDIF");
	send_message(&msg);
	return 0;
}

UINT8 u1SetAudioLatency(INT16 i2Ms) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_AUDIO_LATENCY;
	msg.data.setAudioLatency.i2Ms = i2Ms;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Audio Latency:%dms\n", i2Ms);
	send_message(&msg);
	return 0;
}

UINT8 u1SetTVStatus(BOOL bTVStatus) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_TV_STATUS;
	msg.data.setTVStatus.bTVStatus = bTVStatus;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set TV Status:0x%x\n", bTVStatus);
	send_message(&msg);
	return 0;
}

UINT8 u1SetBtHpStatus(BOOL bConnect) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS;
	msg.data.setBtHpStatus.bConnect = bConnect;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set BtHp Status:0x%x\n", bConnect);
	send_message(&msg);
	return 0;
}

UINT8 u1SetEnable(BOOL bEn) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_ENABLE;
	msg.data.setEnable.bEn = bEn;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Enable:0x%x\n", bEn);
	send_message(&msg);
	return 0;
}

UINT8 u1SetHdmiMute(BOOL bMute) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_HDMI_MUTE;
	msg.data.setHdmiMute.bMute = bMute;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Hdmi Mute:0x%x\n", bMute);
	send_message(&msg);
	return 0;
}

UINT8 u1SetAudioMute(BOOL bMute) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_AUDIO_MUTE;
	msg.data.setAudioMute.bMute = bMute;
	LOG(1, "[SalzCtrl] MSG_FUNC : Set Audio Mute:0x%x\n", bMute);
	send_message(&msg);
	return 0;
}

EARCStatus_t eGetEARCStatus(UINT32 u4PortId) {
	EARCStatus_t ret;
	if (u4PortId == HDMI_PORT) {
		LOG(1, "[SalzCtrl] Get EARC Status, Port:0x%x, Status:0x%x\n",
				HDMI_PORT, gEARCStatus);
		ret = gEARCStatus;
	} else {
		LOG(1,
				"[SalzCtrl] Get EARC Status, Port:0x%x -> It's not eARC HDMI Port \n",
				u4PortId);
		ret = HDMI_EARC_WAITING;
	}
	return ret;
}

UINT8 u1GetEARCAudioStatus(EARCAudioStatus_t* eStatus) {
	if (NULL == eStatus) {
		return 1;
	}
	eStatus->bEARCConnect = gEARCConnect;
	eStatus->u1Capability = gCapability;
	return 0;
}

static BOOL sii9438_creating_flag = FALSE;
UINT32 hostBlockWrite(UINT32 dev_id, UINT16 addr, const UINT8 *p_data,
		UINT16 size) {
	UINT32 ret = u4HDMIEARCHostBlockWrite(dev_id, addr, p_data, size);
	if (RET_FAIL == ret && FALSE == sii9438_creating_flag) {
		LOG(0, "[SalzCtrl] u4HDMIEARCHostBlockWrite() is fail\n");
		SelfDiagTriggerEarcI2CError();
	}
	return ret;
}

UINT32 hostBlockRead(UINT32 dev_id, UINT16 addr, UINT8 *p_data, UINT16 size) {
	UINT32 ret = u4HDMIEARCHostBlockRead(dev_id, addr, p_data, size);
	if (RET_FAIL == ret && FALSE == sii9438_creating_flag) {
		LOG(0, "[SalzCtrl] u4HDMIEARCHostBlockRead() is fail\n");
		SelfDiagTriggerEarcI2CError();
	}
	return ret;
}

/* ==========================================================
 * FUNCTION TABLE
 * ==========================================================*/
EARCFuncTable_t gEventFuncTable = {
	u1SetChannelStatus,		/*	UINT8 (*pfnSetChannelStatus)(EARCChannelStatus_t *eStatus); */
	u1SetAudioInfoFrame,	/*	UINT8 (*pfnSetAudioInfoFrame)(EARCAudioInfoFrame_t *eInfoFrame); */
	u1SetValidityBit,		/*	UINT8 (*pfnSetValidityBit)(BOOL bValidity); */
	NULL,					/*	UINT8 (*pfnSetHPDStatus)(UINT8 u1HpdStatus); */
	u1Set5VStatus,			/*	UINT8 (*pfnSet5VStatus)(UINT8 u15VStatus); */
	u1SetHBRAInputStatus,	/*	UINT8 (*pfnSetHBRAInputStatus)(BOOL bHbr); */
	NULL,					/*	UINT8 (*pfnSetLayoutBit)(BOOL bLayout); */
	u1SetArcStatus,			/*	UINT8 (*pfnSetArcStatus)(BOOL bOnOff); */
	u1SetAudioPath,			/*	UINT8 (*pfnSetAudioPath)(BOOL bPath); */
	u1SetAudioLatency,		/*	UINT8 (*pfnSetAudioLatency)(INT16 i2Ms); */
	u1SetTVStatus,			/*	UINT8 (*pfnSetTVStatus)(BOOL bTVStatus); */
	u1SetBtHpStatus,		/*	UINT8 (*pfnSetBtHpStatus)(BOOL bConnect); */
	u1SetEnable,			/*	UINT8 (*pfnSetEnable)(BOOL bEn); */
	u1SetHdmiMute,			/*	UINT8 (*pfnSetHdmiMute)(BOOL bMute); */
	u1SetAudioMute,			/*	UINT8 (*pfnSetAudioMute)(BOOL bMute); */
	eGetEARCStatus,			/*	EARCStatus_t (*pfnGetEARCStatus)(UINT32 u4PortId); */
	u1GetEARCAudioStatus,	/*	UINT8 (*pfnGetEARCAudioStatus)(EARCAudioStatus_t* eStatus); */
};

/* ==========================================================
 * SALZBURG LIBRARY CALLBACK
 * ==========================================================*/
void sii9438_callback(uint32_t *p_event_flags) {
	SALZCTRL_MSG msg;
	if (*p_event_flags & SII9438_EVENT_FLAGS__ASSERT) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__ASSERT\n");
		msg.id = SALZCTRL_MSG_CB_ASSERT;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__EARC_LINK_CHNG) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__EARC_LINK_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_EARC_LINK_CHNG;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__CAPS_DS_CHNG) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__CAPS_DS_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_EDID_CHNG;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__ERX_LATENCY_CHNG) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__ERX_LATENCY_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_ERX_LATENCY_CHNG;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__EARC_DISC_TIMEOUT) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__EARC_DISC_TIMEOUT\n");
		msg.id = SALZCTRL_MSG_CB_EARC_DISC_TIMEOUT;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__LOG_FIFO_CHNG) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__LOG_FIFO_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_LOG_FIFO_CHNG;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__EARC_HPD_CHNG) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__EARC_HPD_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_EARC_HPD_CHNG;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__SPDIF_CHAN_STAT_CHNG) {
		LOG(1,
				"[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__SPDIF_CHAN_STAT_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_SPDIF_CHAN_STAT_CHNG;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__GPIO_CHNG) {
		LOG(1, "[SalzCtrl] MSG_CB : SII9438_EVENT_FLAGS__GPIO_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_GPIO_CHNG;
		send_message(&msg);
	}
}

/* ==========================================================
 * SUB THREAD
 * ==========================================================*/
void cb_handle(void *args) {
	LOG(0, "[SalzCtrl] Launch Salz Ctrl Sub Thread. \n");
	while (SALZCTRL_STATE_SUSPEND != gState) {
		if (!GPIO_GetIn(132)) {
			LOG(1, "[SalzCtrl] GPIO interrupt occurred. \n");
			sii9438_handle(s_sii9438_tx_inst);
		}
		x_thread_delay(50);
	}
	pSii9438SubThreadHdl = NULL_HANDLE;
	LOG(0, "[SalzCtrl] Exit Salz Ctrl Sub Thread. \n");
}

/* ==========================================================
 * LOCAL INITIALIZE FUNCTION FOR MAIN THREAD
 * ==========================================================*/
BOOL initialize(SALZ_INIT_SET_STATE *pInitSetState) {
	struct sii9438_config tx_config = {
	SII9438_INST_1, sii9438_callback, 128,
	SII9438_INST_1_I2C_ADDR_PAGE1,
	SII9438_INST_1_I2C_ADDR_PAGE2,
	SII9438_INST_1_I2C_ADDR_PAGE3,
	SII9438_INST_1_I2C_ADDR_PAGE4 };
	BOOL b_spdif_cs_passthru;
	enum sii9438_mclk_div mclk_div;
	EARCInitInfo_t eInitInfo;

	LOG(2, "[SalzCtrl] call initialize(%d)\n", *pInitSetState);

	GPIO_SetOut(130, 0);
	GPIO_SetOut(131, 1);
	x_thread_delay(10);
	GPIO_SetOut(131, 0);
	LOG(2, "[SalzCtrl] GPIO(132)=%d\n", GPIO_GetIn(132));

	if (OSR_OK
			!= x_msg_q_create(&pSii9438QueueHdl, "SII9438MsgQueue",
					sizeof(SALZCTRL_MSG), QUEUE_MSG_COUNT_MAX)) {
		LOG(0, "[SalzCtrl] x_msg_q_create(SII9438MsgQueue) is fail\n");
		return FALSE;
	}
	LOG(2, "[SalzCtrl] x_msg_q_create(SII9438MsgQueue) is success\n");

	sii_platform_register_api(hostBlockWrite, hostBlockRead, vHDMIEARCResetSet,
			u4HDMIEARCMsecQuery, HDMIEARCLogPrint, vHDMIEARCSleepMsec);

	if (RET_SUCCESS != u1HDMIEARCGetInitialStatus(&eInitInfo)) {
		LOG(0, "[SalzCtrl] u1HDMIEARCGetInitialStatus() is fail\n");
		return FALSE;
	}
	LOG(2,
			"[SalzCtrl] u1HDMIEARCGetInitialStatus() is success : \
			Get Init Status, HPD:0x%x, 5V:0x%x, En:%d, TV:%d, BT:%d\n",
			eInitInfo.u1HPDStatus, eInitInfo.u15VStatus, eInitInfo.bEnable,
			eInitInfo.bTVStatus, eInitInfo.bBtHpStatus);
	gTVStatus = eInitInfo.bTVStatus;
	gBtHpStatus = eInitInfo.bBtHpStatus;

	x_memset(gAudioEDIDData, 0, SII_EDID_AUD_SIZE);

	gEARCConnect = FALSE;
	gCapability = NO_AC3_CAPABILITY;
	notifyConnectChgToCEC(HDMI_EARC_WAITING);
	notifyConnectChgToApp(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);

	if (SALZ_INIT_SET_RESUME == *pInitSetState && bIsEARCEDIDUpdated) {
		setEDID(FALSE);
	}
	bIsEARCEDIDUpdated = FALSE;

	sii9438_creating_flag = TRUE;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_create(&tx_config, &s_sii9438_tx_inst)) {
		LOG(0, "[SalzCtrl] sii9438_create() is fail\n");
		sii9438_creating_flag = FALSE;
		return FALSE;
	}
	LOG(0, "[SalzCtrl] sii9438_create() is success\n");
	sii9438_creating_flag = FALSE;

	b_spdif_cs_passthru = TRUE;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_spdif_cs_passthru_enable_set(s_sii9438_tx_inst,
					&b_spdif_cs_passthru)) {
		LOG(0, "[SalzCtrl] sii9438_spdif_cs_passthru_enable_set() is fail\n");
		return FALSE;
	}
	LOG(2, "[SalzCtrl] sii9438_spdif_cs_passthru_enable_set() is success\n");

	mclk_div = SII9438_MCLK_DIV__NO_DIV;
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_mclk_div_set(s_sii9438_tx_inst, &mclk_div)) {
		LOG(0, "[SalzCtrl] sii9438_mclk_div_set() is fail\n");
		return FALSE;
	}
	LOG(2, "[SalzCtrl] sii9438_mclk_div_set() is success\n");

	if (OSR_OK
			!= x_thread_create(&pSii9438SubThreadHdl, "SII9438SubThread",
					STACK_SIZE, THREAD_PRIORITY, cb_handle, 0, NULL)) {
		LOG(0, "[SalzCtrl] x_thread_create(SII9438SubThread) is fail\n");
		return FALSE;
	}
	LOG(2, "[SalzCtrl] x_thread_create(SII9438SubThread) is success\n");

	setAudioInfo_SPDIF();
	Config_GPIO3();
	setBEParam(0x00, 0x00); /* eARC disconnect / SPDIF */

	if (eInitInfo.bEnable) {
		LOG(2, "[SalzCtrl] eARC enable\n");
		Call_u1HDMIEARCChgHPD(HDMIALL_BIT & ~HDMI3_BIT);
		Set_GPIO3_High();
		x_thread_delay(150);
		Call_u1HDMIEARCChgHPD(HDMIALL_BIT);
		x_thread_delay(150);
		Set_GPIO3_Low();
		setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
	} else {
		LOG(2, "[SalzCtrl] eARC disable\n");
		setARCMode(SII_ARC_MODE__ARC);
		/* TX DISC1/DISC2 at eARC disable */
		notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
		setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
	}
	return TRUE;
}

/* ==========================================================
 * LOCAL FINALIZE FUNCTION FOR MAIN THREAD
 * ==========================================================*/
void finalize(void) {
	setStatus(SALZCTRL_STATE_SUSPEND);
	u1HDMIEARCRegFuncTable(NULL);
	if (SII_INST_NULL != s_sii9438_tx_inst) {
		if (SII_RETURN_VALUE__SUCCESS != sii9438_delete(s_sii9438_tx_inst)) {
			LOG(0, "[SalzCtrl] sii9438_delete() is fail\n");
		} else {
			LOG(2, "[SalzCtrl] sii9438_delete() is success\n");
		}
		s_sii9438_tx_inst = SII_INST_NULL;
	}
	if (NULL_HANDLE != pSii9438QueueHdl) {
		x_msg_q_delete(pSii9438QueueHdl);
		pSii9438QueueHdl = NULL_HANDLE;
	}
	pSii9438MainThreadHdl = NULL_HANDLE;
}

/* ==========================================================
 * MAIN THREAD
 * ==========================================================*/
void sii9438_main(void *args) {
	UINT16 ui2_index;
	LOG(0, "[SalzCtrl] Launch Salz Ctrl Main Thread. \n");

	if (TRUE != initialize((SALZ_INIT_SET_STATE *) args)) {
		LOG(0, "[SalzCtrl] Fail initialize()\n");
		goto failure;
	}

	while (1) {
		SALZCTRL_MSG msg;
		SIZE_T msg_size = sizeof(SALZCTRL_MSG);
		if (OSR_OK
				== x_msg_q_receive(&ui2_index, &msg, &msg_size,
						&pSii9438QueueHdl, 1, X_MSGQ_OPTION_WAIT)) {
			LOG(1, "[SalzCtrl] Receive Message = %s. \n", gMsgName[msg.id]);
			event_function(&msg);
			if (SALZCTRL_STATE_SUSPEND == gState)
				break;
		}
	}

	failure: finalize();
	LOG(0, "[SalzCtrl] Exit Salz Ctrl Main Thread. \n");
}

BOOL LaunchSii9438(SALZ_INIT_SET_STATE initSetState) {
	if (!fgHDMIEARCIsSupported()) {
		LOG(0, "[SalzCtrl] Salzburg is not Supported\n");
		gEARCConnect = FALSE;
		gCapability = NO_AC3_CAPABILITY;
		notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
		notifyConnectChgToApp(FALSE, NO_AC3_CAPABILITY, NO_MPEG4AACLC_CAPABILITY);
		return FALSE;
	}
	LOG(2, "[SalzCtrl] Salzburg is Supported\n");

	if (NULL_HANDLE != pSii9438MainThreadHdl) {
		LOG(0, "[SalzCtrl] SiI9438 is already launched.()\n");
		return FALSE;
	}
	setStatus(SALZCTRL_STATE_INITIALIZE);
	if (OSR_OK
			!= x_thread_create(&pSii9438MainThreadHdl, "SII9438MainThread",
					STACK_SIZE, THREAD_PRIORITY, sii9438_main,
					sizeof(SALZ_INIT_SET_STATE), &initSetState)) {
		LOG(0, "[SalzCtrl] x_thread_create(SII9438MainThread) is fail\n");
		setStatus(SALZCTRL_STATE_SUSPEND);
		return FALSE;
	}
	u1HDMIEARCRegFuncTable(&gEventFuncTable);
	LOG(2, "[SalzCtrl] x_thread_create(SII9438MainThread) is success\n");
	return TRUE;
}

/* ==========================================================
 * PUBLIC INIT FUNCTION
 * Create instance and Initial settings at AC ON.
 * ==========================================================*/
BOOL Sii9438Init(void) {
	LOG(0, "[SalzCtrl] call Sii9438Init()\n");
	return LaunchSii9438(SALZ_INIT_SET_ACON);
}

/* ==========================================================
 * PUBLIC RESUME FUNCTION
 * Create instance and Initial settings at resume.
 * ==========================================================*/
BOOL Sii9438Resume(void) {
	LOG(0, "[SalzCtrl] call Sii9438Resume()\n");
	return LaunchSii9438(SALZ_INIT_SET_RESUME);
}

/* ==========================================================
 * PUBLIC SUSPEND FUNCTION
 * Delete instance at suspend.
 * ==========================================================*/
void Sii9438Suspend(void) {
	SALZCTRL_MSG msg;

	LOG(0, "[SalzCtrl] call Sii9438Suspend()\n");
	if (!fgHDMIEARCIsSupported()) {
		LOG(0, "[SalzCtrl] Salzburg is not Supported\n");
		return;
	}
	LOG(2, "[SalzCtrl] Salzburg is Supported\n");

	if (NULL_HANDLE == pSii9438MainThreadHdl) {
		LOG(0, "[SalzCtrl] SiI9438 is already Suspended.()\n");
		return;
	}

	while (SALZCTRL_STATE_INITIALIZE == gState) {
		x_thread_delay(1);
	}

	u1HDMIEARCRegFuncTable(NULL);
	msg.id = SALZCTRL_MSG_SUSPEND;
	send_message(&msg);

	while (NULL_HANDLE != pSii9438MainThreadHdl) {
		x_thread_delay(1);
	}
	LOG(0, "[SalzCtrl] Sii9438 is suspended\n");
}
