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

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

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

/************************************************************
 * DEFINITION
 ************************************************************/
#define THREAD_PRIORITY 60 /* 1(H)~255(L)*/
#define STACK_SIZE (1024*2)
#define QUEUE_MSG_COUNT_MAX 127
#define HDMI1_BIT	0x01	// Unused
#define HDMI2_BIT	0x02	// Unused
#define HDMI3_BIT	0x04
#define HDMI4_BIT	0x08	// Unused
#define HDMI_PORT	3
#define SII_EDID_AUD_SIZE 256

typedef enum {
	SALZ_INIT_SET_ACON = 0,
	SALZ_INIT_SET_RESUME
} SALZ_INIT_SET_STATE;

typedef enum {
	SALZCTRL_STATE_SUSPEND = 0,
	SALZCTRL_STATE_INITIALIZE,
	SALZCTRL_STATE_IDLE1_EARC_ENABLE,
	SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE,
	SALZCTRL_STATE_EARC_EARC_ENABLE,
	SALZCTRL_STATE_IDLE2_EARC_ENABLE,
	SALZCTRL_STATE_ARC_EARC_ENABLE,
	SALZCTRL_STATE_IDLE1_EARC_DISABLE,
	SALZCTRL_STATE_EARC_EARC_DISABLE,
	SALZCTRL_STATE_IDLE2_EARC_DISABLE,
	SALZCTRL_STATE_ARC_EARC_DISABLE1,
	SALZCTRL_STATE_ARC_EARC_DISABLE2,
	SALZCTRL_STATE_MAX
} SALZCTRL_STATE;

typedef enum {
	SALZCTRL_MSG_SUSPEND = 0,
	SALZCTRL_MSG_CB_LOG_FIFO_CHNG,
	SALZCTRL_MSG_CB_EARC_HPD_CHNG,
	SALZCTRL_MSG_CB_EARC_LINK_CHNG,
	SALZCTRL_MSG_CB_SPDIF_CHAN_STAT_CHNG,
	SALZCTRL_MSG_CB_EDID_CHNG,
	SALZCTRL_MSG_CB_GPIO_CHNG,
	SALZCTRL_MSG_CB_ASSERT,
	SALZCTRL_MSG_CB_ERX_LATENCY_CHNG,
	SALZCTRL_MSG_CB_EARC_DISC_TIMEOUT,
	SALZCTRL_MSG_FUNC_SET_CHANNEL_STATUS,
	SALZCTRL_MSG_FUNC_SET_AUDIO_INFO_FRAME,
	SALZCTRL_MSG_FUNC_SET_VALIDITY_BIT,
	SALZCTRL_MSG_FUNC_SET_HPD_STATUS,	// Unused
	SALZCTRL_MSG_FUNC_SET_5V_STATUS,
	SALZCTRL_MSG_FUNC_SET_HBRA_INPUT_STATUS,
	SALZCTRL_MSG_FUNC_SET_LAYOUT_BIT,
	SALZCTRL_MSG_FUNC_SET_ARC_STATUS,
	SALZCTRL_MSG_FUNC_SET_AUDIO_PATH,
	SALZCTRL_MSG_FUNC_SET_AUDIO_LATENCY,
	SALZCTRL_MSG_FUNC_SET_TV_STATUS,
	SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS,
	SALZCTRL_MSG_FUNC_SET_ENABLE,
	SALZCTRL_MSG_FUNC_SET_HDMI_MUTE,
	SALZCTRL_MSG_FUNC_SET_AUDIO_MUTE,
	SALZCTRL_MSG_MAX
} SALZCTRL_MSG_ID;

typedef struct {
	EARCChannelStatus_t *eStatus;
} CHANNEL_STATUS_ST;

typedef struct {
	EARCAudioInfoFrame_t *eInfoFrame;
} AUDIO_INFO_FRAME_ST;

typedef struct {
	BOOL bValidity;
} VALIDITY_BIT_ST;

typedef struct {
	UINT8 u1HpdStatus;
} HPD_STATUS_ST;

typedef struct {
	UINT8 u15VStatus;
} V_STATUS_ST;

typedef struct {
	BOOL bHbr;
} HBRA_INPUT_STATUS_ST;

typedef struct {
	BOOL bLayout;
} LAYOUT_BIT_ST;

typedef struct {
	BOOL bOnOff;
} ARC_STATUS_ST;

typedef struct {
	BOOL bPath;
} AUDIO_PATH_ST;

typedef struct {
	INT16 i2Ms;
} AUDIO_LATENCY_ST;

typedef struct {
	BOOL bTVStatus;
} TV_STATUS_ST;

typedef struct {
	BOOL bConnect;
} BT_HP_STATUS_ST;

typedef struct {
	BOOL bEn;
} ENABLE_ST;

typedef struct {
	BOOL bMute;
} HDMI_MUTE_ST;

typedef struct {
	BOOL bMute;
} AUDIO_MUTE_ST;

typedef union {
	UINT32 buff[64];
	CHANNEL_STATUS_ST setChannelStatus;
	AUDIO_INFO_FRAME_ST setAudioInfoFrame;
	VALIDITY_BIT_ST setValidityBit;
	HPD_STATUS_ST setHPDStatus;
	V_STATUS_ST set5VStatus;
	HBRA_INPUT_STATUS_ST setHBRAInputStatus;
	LAYOUT_BIT_ST setLayoutBit;
	ARC_STATUS_ST setArcStatus;
	AUDIO_PATH_ST setAudioPath;
	AUDIO_LATENCY_ST setAudioLatency;
	TV_STATUS_ST setTVStatus;
	BT_HP_STATUS_ST setBtHpStatus;
	ENABLE_ST setEnable;
	HDMI_MUTE_ST setHdmiMute;
	AUDIO_MUTE_ST setAudioMute;
} SALZCTRL_MSG_DATA;

typedef struct {
	SALZCTRL_MSG_ID id;
	SALZCTRL_MSG_DATA data;
} SALZCTRL_MSG;

#define FS_THRESHOLD	10	//1kHz
#define FS_HBR_ADJUST	4	//Quadruple Frequency value for HBRA
typedef struct {
	UINT32 min;
	UINT32 max;
	UINT8 bit;
} AUDIO_FS_TBL;

/************************************************************
 * VARIABLE DATAS
 ************************************************************/
AUDIO_FS_TBL g_audio_fs_tbl[] = {//TODO:bit変更ありかも
	{ 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	*/
};
static sii_inst_t s_sii9438_tx_inst = SII_INST_NULL;
static HANDLE_T pSii9438MainThreadHdl = NULL_HANDLE;
static HANDLE_T pSii9438SubThreadHdl = NULL_HANDLE;
static HANDLE_T pSii9438QueueHdl = NULL_HANDLE;
static HANDLE_T pSii9438SemaphoreHdl = NULL_HANDLE;
static SALZCTRL_STATE gState = SALZCTRL_STATE_SUSPEND;
static BOOL gTVStatus;
static BOOL gBtHpStatus;
static UINT8 gAudioEDIDData[SII_EDID_AUD_SIZE];
static EARCStatus_t gEARCStatus = HDMI_EARC_WAITING; /* for pfnGetEARCStatus() */
static BOOL bIsEARCEDIDupdated; // EDID is already updated with eARC.
static BOOL bNewAudioEDIDData; // New Audio EDID data

/* ==========================================================
 * LOG FUNCTION									*
 * ==========================================================*/
/* NO LOG(-1) / ERR(0) / WAR(1) / INFO(2)		*/
int host9438_log_level = 2;
//TODO: module_param( host9438_log_level, int, S_IRUGO|S_IWUSR );
void host9438_Printf(int level, const char* format, ...) {
	if (host9438_log_level >= level) {
		va_list va;
		va_start(va, format);
		printk(format, va);
		va_end(va);
	}
}
#define SALZCTRL_LOG_I(...)	host9438_Printf(2, __VA_ARGS__)
#define SALZCTRL_LOG_W(...)	host9438_Printf(1, __VA_ARGS__)
#define SALZCTRL_LOG_E(...)	host9438_Printf(0, __VA_ARGS__)
/* ==========================================================*/

/************************************************************
 * PRIVATE FUNCTIONS
 ************************************************************/
static void setStatus(SALZCTRL_STATE inState) {
	SALZCTRL_LOG_I("[SalzCtrl] Change State = %d. \n", inState);
	gState = inState;
}

static INT32 send_message(SALZCTRL_MSG *pMsg) {
	SALZCTRL_LOG_I("[SalzCtrl] Send Message ID(%d). \n", pMsg->id);
	return x_msg_q_send(pSii9438QueueHdl, pMsg, sizeof(SALZCTRL_MSG),
	THREAD_PRIORITY);
}

static void sema_lock(void) {
	SALZCTRL_LOG_I("[SalzCtrl] lock semaphore. \n");
	x_sema_lock(pSii9438SemaphoreHdl, X_SEMA_OPTION_WAIT);
}
static void sema_unlock(void) {
	SALZCTRL_LOG_I("[SalzCtrl] unlock semaphore. \n");
	x_sema_unlock(pSii9438SemaphoreHdl);
}

static BOOL setEDID(BOOL bConnect) {
	EARCEDIDInfo_t eEDIDInfo;
	x_memset(&eEDIDInfo, 0, sizeof(EARCEDIDInfo_t));
	eEDIDInfo.bEARCConnect = bConnect;
	if (RET_SUCCESS != u1HDMIEARCUpdateEdid(&eEDIDInfo)) {
		SALZCTRL_LOG_W("[SalzCtrl] u1HDMIEARCUpdateEdid() is fail\n");
		return FALSE;
	}
	SALZCTRL_LOG_I("[SalzCtrl] u1HDMIEARCUpdateEdid() is success\n");
	return TRUE;
}

/*
 * isEARCConnected
 * 		0:eARC disconnect / 1:eARC connect
 * path
 * 		0:SPDIF / 1:I2S
 * */
static 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;
	vHDMIEARCSetBEGenParam(0x19, param, BE_PARAM_INFO_SIZE);
}

static void setFrequency(EARCChannelStatus_t *pChannelStatus, UINT32 fs) {
	AUDIO_FS_TBL *pFsTbl = g_audio_fs_tbl;
	while (pFsTbl->max != 0) {
		if (pFsTbl->min <= fs && fs <= pFsTbl->max) {
			//TODO:bit変更ありかも
			pChannelStatus->u1Status[3] &= 0x30; /* Clear the frequency bit	7-6, 3-0*/
			pChannelStatus->u1Status[3] |= pFsTbl->bit; /* Set the frequency bit	*/
			//pChannelStatus->u1Status[3] &= 0x0F; /* Clear the frequency bit	*/
			//pChannelStatus->u1Status[3] |= ((pFsTbl->bit << 4) & 0xF0); /* Set the frequency bit	*/
			SALZCTRL_LOG_I("[SalzCtrl] Audio Frequency : %x\n",
					(pChannelStatus->u1Status[3] & 0x0F));
			return;
		}
		pFsTbl++;
	}
}

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

static BOOL changeHPDStatus(UINT8 u1HPDStatus) {
	SALZCTRL_LOG_I("[SalzCtrl] change HPD status(%d)\n", u1HPDStatus);
	if (RET_SUCCESS != u1HDMIEARCChgHPD(u1HPDStatus)) {
		SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCChgHPD() is fail\n");
		return FALSE;
	}
	SALZCTRL_LOG_I("[SalzCtrl] u1HDMIEARCChgHPD() is success\n");
	return TRUE;
}

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

	gEARCStatus = eStatus;

	if (RET_SUCCESS != u1HDMIEARCGetMWReady(&bReady)) {
		SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCGetMWReady() is fail\n");
		return;
	}
	if (bReady) {
		SALZCTRL_LOG_I("[SalzCtrl] MW is Ready, execute notify\n");
		ePortStatus.u4PortId = HDMI_PORT;
		ePortStatus.eStatus = eStatus;
		vHDMIEARCEventNotify(EARC_EVENT_CONNECT_CHG_TO_CEC,
				(void *) &ePortStatus);
	} else {
		SALZCTRL_LOG_I("[SalzCtrl] MW is not Ready\n");
	}
}

static void notifyConnectChgToAudio(BOOL eARCConnect, UINT8 capability) {
	EARCAudioStatus_t eAudioStatus;
	eAudioStatus.bEARCConnect = eARCConnect;
	eAudioStatus.u1Capability = capability;
	vHDMIEARCEventNotify(EARC_EVENT_CONNECT_CHG_TO_AUDIO, (void *) &eAudioStatus);
}

static void updateEdidEARC(void) {
	EARCEDIDInfo_t eEDIDInfo;
	UINT8 u1Capability;

	SALZCTRL_LOG_I("[SalzCtrl] Update to eARC EDID\n");

	eEDIDInfo.bEARCConnect = TRUE;
	x_memcpy(eEDIDInfo.u1DataBlock, gAudioEDIDData,
			sizeof(EARCEDIDInfo_t));
	if (FALSE == setEDID(TRUE)) {
		u1Capability = 0;
		bIsEARCEDIDupdated = FALSE;
	} else {
		u1Capability = 1;
		bIsEARCEDIDupdated = TRUE;
	}
	notifyConnectChgToCEC(HDMI_EARC_ENABLED);
	notifyConnectChgToAudio(TRUE, u1Capability);
	bNewAudioEDIDData = FALSE;
	changeHPDStatus(0x0F);
}

static void updateEdidARC(void) {
	SALZCTRL_LOG_I("[SalzCtrl] Update to ARC EDID\n");
	setEDID(FALSE);
	notifyConnectChgToCEC(HDMI_EARC_WAITING);
	notifyConnectChgToAudio(FALSE, 0);
	changeHPDStatus(0x0F);
}

static void changeEARCLink(void) {
	BOOL b_earc_link_status = FALSE;
	UINT16 u2AudioLatency;	//Not use for Uroboros DV
	struct sii_erx_latency_req erx_latency_req;

	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_earc_link_query(s_sii9438_tx_inst,
					&b_earc_link_status)) {
		SALZCTRL_LOG_W("[SalzCtrl] sii9438_earc_link_query() is fail\n");
	} else {
		SALZCTRL_LOG_I("[SalzCtrl] sii9438_earc_link_query() is success\n");
	}

	if (b_earc_link_status) {
		SALZCTRL_LOG_I("[SalzCtrl] eARC is Connected\n");
		if (RET_SUCCESS != u1HDMIEARCGetAudioLatency(&u2AudioLatency)) {
			SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCGetMWReady() is fail\n");
		}
		SALZCTRL_LOG_E("[SalzCtrl] Audio Latency = %d[msec]: \n", u2AudioLatency);

		erx_latency_req.data = 255;	//eARC TX does not have latency infomation. (unknown latency)
		if (SII_RETURN_VALUE__SUCCESS
				!= sii9438_erx_latency_req_set(s_sii9438_tx_inst, &erx_latency_req)) {
			SALZCTRL_LOG_E("[SalzCtrl] sii9438_erx_latency_req_set() is fail\n");
		}
		SALZCTRL_LOG_I("[SalzCtrl] sii9438_erx_latency_req_set() is success\n");
		setStatus(SALZCTRL_STATE_EARC_EARC_ENABLE);
	} else {
		SALZCTRL_LOG_I("[SalzCtrl] eARC is Disconnected\n");
		setEDID(FALSE);
		notifyConnectChgToCEC(HDMI_EARC_WAITING);
		setBEParam(0x00, 0x00); // eARC disconnect / SPDIF
		x_memset(gAudioEDIDData, 0, SII_EDID_AUD_SIZE);
		setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
	}
}

static void setAudioInfo(AUDIO_PATH_ST *status) {
	UINT8 u1Hbr;
	UINT8 u1Validity;
	UINT8 u1Layout;
	UINT32 u4AudioFs;
	enum sii9438_insertion_mode mode;
	enum sii9438_vbit vbit;
	struct sii_audio_info audio_info;
	EARCChannelStatus_t eChannelStatus;
	EARCAudioInfoFrame_t eAudioInfoFrame;

	SALZCTRL_LOG_I("[SalzCtrl] setAudioInfo()\n");
	if (status->bPath) {
		SALZCTRL_LOG_I("[SalzCtrl] Path : I2S\n");

		setBEParam(0x01, 0x01); // eARC connect / I2S

		if (RET_SUCCESS != u1HDMIEARCIsHBRInput(&u1Hbr)) {
			SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCIsHBRInput() is fail\n");
			return;
		}
		SALZCTRL_LOG_I("[SalzCtrl] u1HDMIEARCIsHBRInput() is success\n");

		if (u1Hbr) {
			SALZCTRL_LOG_I("[SalzCtrl] setAudioInfo : HBR Audio\n");

			mode = SII9438_INSERTION_MODE__I2S8;
			if (SII_RETURN_VALUE__SUCCESS
					!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] sii9438_insertion_mode_set(SII9438_INSERTION_MODE__I2S8) is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] sii9438_insertion_mode_set(SII9438_INSERTION_MODE__I2S8) is success\n");

			if (RET_SUCCESS != u1HDMIEARCGetChannelStatus(&eChannelStatus)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] u1HDMIEARCGetChannelStatus() is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] u1HDMIEARCGetChannelStatus() is success\n");

			// u4AudioFs: 1 = 100Hz
			if (RET_SUCCESS != u1HDMIEARCGetAudioFs(&u4AudioFs)) {
				SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCGetAudioFs() is fail\n");
				return;
			}
			SALZCTRL_LOG_I("[SalzCtrl] u1HDMIEARCGetAudioFs() is success\n");
			u4AudioFs *= FS_HBR_ADJUST;
			SALZCTRL_LOG_I("[SalzCtrl] u4AudioFs : %d\n", u4AudioFs);
			// High bit rate audio は4倍にして設定する必要がある?
			setFrequency(&eChannelStatus, u4AudioFs);

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

			if (RET_SUCCESS != u1HDMIEARCGetAudInfoFrame(&eAudioInfoFrame)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is success\n");

			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
			if (SII_RETURN_VALUE__SUCCESS
					!= sii9438_audio_info_set(s_sii9438_tx_inst, &audio_info)) {
				SALZCTRL_LOG_E("[SalzCtrl] sii9438_audio_info_set() is fail\n");
				return;
			}
			SALZCTRL_LOG_I("[SalzCtrl] sii9438_audio_info_set() is success\n");

			if (RET_SUCCESS != u1HDMIEARCGetValidityBit(&u1Validity)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] u1HDMIEARCGetValidityBit() is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] u1HDMIEARCGetValidityBit() is success\n");

			if (u1Validity) {
				vbit = SII9438_VBIT_1;
				if (SII_RETURN_VALUE__SUCCESS
						!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
					SALZCTRL_LOG_E(
							"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_1) is fail\n");
					return;
				}
				SALZCTRL_LOG_I(
						"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_1) is success\n");
			} else {
				vbit = SII9438_VBIT_0;
				if (SII_RETURN_VALUE__SUCCESS
						!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
					SALZCTRL_LOG_E(
							"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_0) is fail\n");
					return;
				}
				SALZCTRL_LOG_I(
						"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_0) is success\n");
			}
		} else {
			SALZCTRL_LOG_I("[SalzCtrl] setAudioInfo : NOT HBR Audio\n");

			if (RET_SUCCESS != u1HDMIEARCGetLayoutBit(&u1Layout)) {
				SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCGetLayoutBit() is fail\n");
				return;
			}
			SALZCTRL_LOG_I("[SalzCtrl] u1HDMIEARCGetLayoutBit() is success\n");

			if (u1Layout) {
				SALZCTRL_LOG_I("[SalzCtrl] LAYOUT == 1\n");
				mode = SII9438_INSERTION_MODE__I2S8;
			} else {
				SALZCTRL_LOG_I("[SalzCtrl] LAYOUT == 0\n");
				mode = SII9438_INSERTION_MODE__I2S2;
			}

			if (SII_RETURN_VALUE__SUCCESS
					!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] sii9438_insertion_mode_set() is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] sii9438_insertion_mode_set() is success\n");

			if (RET_SUCCESS != u1HDMIEARCGetChannelStatus(&eChannelStatus)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] u1HDMIEARCGetChannelStatus() is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] u1HDMIEARCGetChannelStatus() is success\n");

			// u4AudioFs: 1 = 100Hz
			if (RET_SUCCESS != u1HDMIEARCGetAudioFs(&u4AudioFs)) {
				SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCGetAudioFs() is fail\n");
				return;
			}
			SALZCTRL_LOG_I("[SalzCtrl] u1HDMIEARCGetAudioFs() is success\n");
			SALZCTRL_LOG_I("[SalzCtrl] u4AudioFs : %d\n", u4AudioFs);
			setFrequency(&eChannelStatus, u4AudioFs);

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

			if (RET_SUCCESS != u1HDMIEARCGetAudInfoFrame(&eAudioInfoFrame)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] u1HDMIEARCGetAudInfoFrame() is success\n");

			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
			if ((eChannelStatus.u1Status[0] & 0x2) == 0x01) {
				SALZCTRL_LOG_I("[SalzCtrl] bit1 of channel status bit is 0x01\n");
				audio_info.speaker_alloc = 0;
			} else {
				SALZCTRL_LOG_I("[SalzCtrl] bit1 of channel status bit is NOT 0x01\n");
			}

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

			if (RET_SUCCESS != u1HDMIEARCGetValidityBit(&u1Validity)) {
				SALZCTRL_LOG_E(
						"[SalzCtrl] u1HDMIEARCGetValidityBit() is fail\n");
				return;
			}
			SALZCTRL_LOG_I(
					"[SalzCtrl] u1HDMIEARCGetValidityBit() is success\n");

			if (u1Validity) {
				vbit = SII9438_VBIT_1;
				if (SII_RETURN_VALUE__SUCCESS
						!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
					SALZCTRL_LOG_E(
							"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_1) is fail\n");
					return;
				}
				SALZCTRL_LOG_I(
						"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_1) is success\n");
			} else {
				vbit = SII9438_VBIT_0;
				if (SII_RETURN_VALUE__SUCCESS
						!= sii9438_vbit_set(s_sii9438_tx_inst, &vbit)) {
					SALZCTRL_LOG_E(
							"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_0) is fail\n");
					return;
				}
				SALZCTRL_LOG_I(
						"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_0) is success\n");
			}
		}
	} else {
		SALZCTRL_LOG_I("[SalzCtrl] Path : SPDIF\n");

		setBEParam(0x01, 0x00); // eARC connect / SPDIF

		mode = SII9438_INSERTION_MODE__SPDIF2;
		if (SII_RETURN_VALUE__SUCCESS
				!= sii9438_insertion_mode_set(s_sii9438_tx_inst, &mode)) {
			SALZCTRL_LOG_E(
					"[SalzCtrl] sii9438_insertion_mode_set(SII9438_INSERTION_MODE__SPDIF2) is fail\n");
			return;
		}
		SALZCTRL_LOG_I(
				"[SalzCtrl] sii9438_insertion_mode_set(SII9438_INSERTION_MODE__SPDIF2) is success\n");

		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)) {
			SALZCTRL_LOG_E("[SalzCtrl] sii9438_audio_info_set() is fail\n");
			return;
		}
		SALZCTRL_LOG_I("[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)) {
			SALZCTRL_LOG_E(
					"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_BYPASS) is fail\n");
			return;
		}
		SALZCTRL_LOG_I(
				"[SalzCtrl] sii9438_vbit_set(SII9438_VBIT_BYPASS) is success\n");
	}
}

static void updateEDID(void) {
	SALZCTRL_LOG_I("[SalzCtrl] updateEDID()\n");

	SALZCTRL_LOG_I("[SalzCtrl] gBtHpStatus = %d\n", gBtHpStatus);
	SALZCTRL_LOG_I("[SalzCtrl] gTVStatus = %d\n", gTVStatus);
	SALZCTRL_LOG_I("[SalzCtrl] bIsEARCEDIDupdated = %d\n", bIsEARCEDIDupdated);
	SALZCTRL_LOG_I("[SalzCtrl] bNewAudioEDIDData = %d\n", bNewAudioEDIDData);
	SALZCTRL_LOG_I("[SalzCtrl] gAudioEDIDData[0] = %d\n", gAudioEDIDData[0]);

//	//Tentative code until JNI(pfnSetTVStatus, pfnSetBtHpStatus) will implemented
//	gBtHpStatus = FALSE;
//	gTVStatus = TRUE;

	if ((gBtHpStatus || (!gBtHpStatus && !gTVStatus)) && bIsEARCEDIDupdated) {
		updateEdidARC();
	} else if (bNewAudioEDIDData || (gAudioEDIDData[0] && !bIsEARCEDIDupdated)) {
		updateEdidEARC();
	}
}

static void transition_func11(void) {
	struct sii_erx_latency_req erx_latency_req;
	SALZCTRL_LOG_I("[SalzCtrl] transition_func11()\n");
	erx_latency_req.data = 255;	//eARC TX does not have latency infomation. (unknown latency)
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_erx_latency_req_set(s_sii9438_tx_inst, &erx_latency_req)) {
		SALZCTRL_LOG_E("[SalzCtrl] sii9438_erx_latency_req_set() is fail\n");
	}
	SALZCTRL_LOG_I("[SalzCtrl] sii9438_erx_latency_req_set() is success\n");
}

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

static void setEARCModeEnable(ENABLE_ST *pEnableST) {
	if (pEnableST->bEn) {
		if (SALZCTRL_STATE_IDLE2_EARC_DISABLE == gState) {
			changeHPDStatus(0x0B);
			setStatus(SALZCTRL_STATE_IDLE1_EARC_ENABLE);
			setARCMode(SII_ARC_MODE__ARC);
			notifyConnectChgToCEC(HDMI_EARC_WAITING);
			changeHPDStatus(0x0F);
			setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
		} else if (SALZCTRL_STATE_ARC_EARC_DISABLE1 == gState) {
			changeHPDStatus(0x0B);
			notifyConnectChgToCEC(HDMI_EARC_WAITING);
			setStatus(SALZCTRL_STATE_ARC_EARC_DISABLE2);
		}
	} else {
		if (SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE == gState) {
			setARCMode(SII_ARC_MODE__ARC);
			notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
			setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
		} else if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState) {
			setARCMode(SII_ARC_MODE__ARC);
			setStatus(SALZCTRL_STATE_EARC_EARC_DISABLE);
		} else if (SALZCTRL_STATE_IDLE2_EARC_ENABLE == gState) {
			setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
		} else if (SALZCTRL_STATE_ARC_EARC_ENABLE == gState) {
			setStatus(SALZCTRL_STATE_ARC_EARC_DISABLE1);
		}
	}
}

static void setEARCStatus(ARC_STATUS_ST *pArcStatusST) {
	if (pArcStatusST->bOnOff) {
		if (SALZCTRL_STATE_IDLE2_EARC_ENABLE == gState) {
			setStatus(SALZCTRL_STATE_ARC_EARC_ENABLE);
		} else if (SALZCTRL_STATE_IDLE2_EARC_DISABLE == gState) {
			setStatus(SALZCTRL_STATE_ARC_EARC_DISABLE1);
		}
	} else {
		if (SALZCTRL_STATE_ARC_EARC_ENABLE == gState) {
			setStatus(SALZCTRL_STATE_IDLE2_EARC_ENABLE);
		} else if (SALZCTRL_STATE_ARC_EARC_DISABLE1 == gState) {
			setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
		} else if (SALZCTRL_STATE_ARC_EARC_DISABLE2 == gState) {
			setStatus(SALZCTRL_STATE_IDLE1_EARC_ENABLE);
			setARCMode(SII_ARC_MODE__ARC);
			notifyConnectChgToCEC(HDMI_EARC_WAITING);
			changeHPDStatus(0x0F);
			setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
		}
	}
}

static void event_function(SALZCTRL_MSG *pMsg) {

	if (SALZCTRL_STATE_INITIALIZE == gState)
		return;

	switch (pMsg->id) {
	case SALZCTRL_MSG_SUSPEND:
		{
			notifyConnectChgToAudio(FALSE, 0);
			setStatus(SALZCTRL_STATE_SUSPEND);
		}
		break;
	case SALZCTRL_MSG_CB_LOG_FIFO_CHNG:
		break;
	case SALZCTRL_MSG_CB_EARC_HPD_CHNG:
		break;
	case SALZCTRL_MSG_CB_EARC_LINK_CHNG:
		if ((SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE == gState)
				|| (SALZCTRL_STATE_EARC_EARC_ENABLE == gState)) {
			changeEARCLink();
		} else if (SALZCTRL_STATE_EARC_EARC_DISABLE == gState) {
			setEDID(FALSE);
			notifyConnectChgToAudio(FALSE, 0);
			notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
			setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
		}
		break;
	case SALZCTRL_MSG_CB_SPDIF_CHAN_STAT_CHNG:
		break;
	case SALZCTRL_MSG_CB_EDID_CHNG:
		if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState) {
			if (SII_RETURN_VALUE__SUCCESS
					!= sii9438_earc_caps_ds_query(s_sii9438_tx_inst, 0,
							gAudioEDIDData, SII_EDID_AUD_SIZE)) {
				SALZCTRL_LOG_W("[SalzCtrl] sii9438_earc_caps_ds_query() is fail\n");
				x_memset(gAudioEDIDData, 0, SII_EDID_AUD_SIZE);
			} else {
				SALZCTRL_LOG_I("[SalzCtrl] sii9438_earc_caps_ds_query() is success\n");
				bNewAudioEDIDData = TRUE;
				updateEDID();
			}
		}
		break;
	case SALZCTRL_MSG_CB_GPIO_CHNG:
		break;
	case SALZCTRL_MSG_CB_ASSERT:
		break;
	case SALZCTRL_MSG_CB_ERX_LATENCY_CHNG:
		if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState) {
			transition_func12();
		}
		break;
	case SALZCTRL_MSG_CB_EARC_DISC_TIMEOUT:
		if (SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE) {
			setARCMode(SII_ARC_MODE__ARC);
			notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
			setStatus(SALZCTRL_STATE_IDLE2_EARC_ENABLE);
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_CHANNEL_STATUS:
	case SALZCTRL_MSG_FUNC_SET_AUDIO_INFO_FRAME:
	case SALZCTRL_MSG_FUNC_SET_VALIDITY_BIT:
	case SALZCTRL_MSG_FUNC_SET_HBRA_INPUT_STATUS:
	case SALZCTRL_MSG_FUNC_SET_LAYOUT_BIT:
		if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState) {
			AUDIO_PATH_ST audioPath;
			u1HDMIEARCIsHDMIInput(&audioPath.bPath);
			/* TRUE is I2S. */
			if (audioPath.bPath) {
				setAudioInfo(&audioPath);
			}
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_ARC_STATUS:
		setEARCStatus(&pMsg->data.setArcStatus);
		break;
	case SALZCTRL_MSG_FUNC_SET_AUDIO_PATH:
		if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState && bIsEARCEDIDupdated) {
			setAudioInfo(&pMsg->data.setAudioPath);
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_AUDIO_LATENCY:
		if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState) {
			transition_func11();
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_TV_STATUS:
		if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState) {
			SALZCTRL_LOG_I("[SalzCtrl] update TVStatus\n");
			gTVStatus = pMsg->data.setTVStatus.bTVStatus;
			updateEDID();
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_BT_HP_STATUS:
		if (SALZCTRL_STATE_EARC_EARC_ENABLE == gState) {
			SALZCTRL_LOG_I("[SalzCtrl] update BtHpStatus\n");
			gBtHpStatus = pMsg->data.setBtHpStatus.bConnect;
			updateEDID();
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_ENABLE:
		setEARCModeEnable(&pMsg->data.setEnable);
		break;
	case SALZCTRL_MSG_FUNC_SET_HDMI_MUTE:
		if ((SALZCTRL_STATE_EARC_EARC_ENABLE == gState)
				&& (!pMsg->data.setHdmiMute.bMute)) {
			AUDIO_PATH_ST audioPath;
			u1HDMIEARCIsHDMIInput(&audioPath.bPath);
			/* TRUE is I2S. */
			if (audioPath.bPath) {
				setAudioInfo(&audioPath);
			}
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_AUDIO_MUTE:
		if ((SALZCTRL_STATE_EARC_EARC_ENABLE == gState)
				&& (!pMsg->data.setHdmiMute.bMute)) {
			AUDIO_PATH_ST audioPath;
			u1HDMIEARCIsHDMIInput(&audioPath.bPath);
			/* FALSE is SPDIF. */
			if (!audioPath.bPath) {
				setAudioInfo(&audioPath);
			}
		}
		break;
	case SALZCTRL_MSG_FUNC_SET_5V_STATUS:
		if ((!pMsg->data.set5VStatus.u15VStatus)
				&& (SALZCTRL_STATE_IDLE2_EARC_ENABLE == gState)) {
			setStatus(SALZCTRL_STATE_IDLE1_EARC_ENABLE);
			setARCMode(SII_ARC_MODE__ARC);
			notifyConnectChgToCEC(HDMI_EARC_WAITING);
			changeHPDStatus(0x0F);
			setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
		}
		break;
	default:
		break;
	}
}

/************************************************************
 * PUBLIC FUNCTIONS
 ************************************************************/
UINT8 u1SetChannelStatus(EARCChannelStatus_t *eStatus) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_CHANNEL_STATUS;
	msg.data.setChannelStatus.eStatus = eStatus;
	send_message(&msg);
	SALZCTRL_LOG_I("[SalzCtrl] Set Channel Status:\n", eStatus->u1Status,
			eStatus->u1Length);
	return 0;
}

UINT8 u1SetAudioInfoFrame(EARCAudioInfoFrame_t *eInfoFrame) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_AUDIO_INFO_FRAME;
	msg.data.setAudioInfoFrame.eInfoFrame = eInfoFrame;
	send_message(&msg);
	SALZCTRL_LOG_I("[SalzCtrl] Set Audio InfoFrame:\n", eInfoFrame->u1Info,
			eInfoFrame->u1Length);
	return 0;
}

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

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

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

UINT8 u1SetLayoutBit(BOOL bLayout) {
	SALZCTRL_MSG msg;
	msg.id = SALZCTRL_MSG_FUNC_SET_LAYOUT_BIT;
	msg.data.setLayoutBit.bLayout = bLayout;
	send_message(&msg);
	SALZCTRL_LOG_I("[SalzCtrl] Set Layout Bit:0x%x\n", bLayout);
	return 0;
}

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

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

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

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

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

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

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

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

EARCStatus_t eGetEARCStatus(UINT32 u4PortId) {
	EARCStatus_t ret;
	if (u4PortId == HDMI_PORT) {
		SALZCTRL_LOG_I("[SalzCtrl] Get EARC Status, Port:0x%x, Status:0x%x\n", HDMI_PORT, gEARCStatus);
		ret = gEARCStatus;
	} else {
		SALZCTRL_LOG_W("[SalzCtrl] Get EARC Status, Port:0x%x -> It's not eARC HDMI Port \n", u4PortId);
		ret = HDMI_EARC_WAITING;
	}
	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);
	u1SetLayoutBit,			//	UINT8 (*pfnSetLayoutBit)(BOOL bLayout);
	u1SetArcStatus,			//	UINT8 (*pfnSetArcStatus)(BOOL bOnOff);
	u1SetAudioPath,			//	UINT8 (*pfnSetAudioPath)(BOOL bPath);
	NULL,					//	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);
};

/* ==========================================================
 * SALZBURG LIBRARY CALLBACK
 * ==========================================================*/
void sii9438_callback(uint32_t *p_event_flags) {
	SALZCTRL_MSG msg;
	SALZCTRL_LOG_I("[SalzCtrl] call sii9438_callback(%x)\n", *p_event_flags);

	if (*p_event_flags & SII9438_EVENT_FLAGS__ASSERT) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : SII9438_EVENT_FLAGS__ASSERT\n");
		msg.id = SALZCTRL_MSG_CB_ASSERT;
		send_message(&msg);
	}

	if (*p_event_flags & SII9438_EVENT_FLAGS__EARC_LINK_CHNG) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : 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) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : 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) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : 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) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : 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) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : 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) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : 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) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : 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) {
		SALZCTRL_LOG_I("[SalzCtrl] INT : SII9438_EVENT_FLAGS__GPIO_CHNG\n");
		msg.id = SALZCTRL_MSG_CB_GPIO_CHNG;
		send_message(&msg);
	}
}

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

/* ==========================================================
 * LOCAL INITIALIZE FUNCTION FOR MAIN THREAD
 * ==========================================================*/
static 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 = TRUE;
	enum sii9438_mclk_div mclk_div = SII9438_MCLK_DIV__NO_DIV;
	EARCInitInfo_t eInitInfo;

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

	setStatus(SALZCTRL_STATE_INITIALIZE);

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

	if (OSR_OK
			!= x_sema_create(&pSii9438SemaphoreHdl, X_SEMA_TYPE_MUTEX,
					X_SEMA_STATE_UNLOCK)) {
		SALZCTRL_LOG_E("[SalzCtrl] x_sema_create(pSii9438SemaphoreHdl) is fail\n");
		return FALSE;
	}
	SALZCTRL_LOG_I("[SalzCtrl] x_sema_create(pSii9438SemaphoreHdl) is success\n");

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

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

	sii_platform_register_api(u4HDMIEARCHostBlockWrite, u4HDMIEARCHostBlockRead,
			vHDMIEARCResetSet, u4HDMIEARCMsecQuery, HDMIEARCLogPrint,
			vHDMIEARCSleepMsec);

	if (RET_SUCCESS != u1HDMIEARCGetInitialStatus(&eInitInfo)) {
		SALZCTRL_LOG_E("[SalzCtrl] u1HDMIEARCGetInitialStatus() is fail\n");
		return FALSE;
	}
	SALZCTRL_LOG_I(
			"[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);

	notifyConnectChgToCEC(HDMI_EARC_WAITING);

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

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

	sema_lock();
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_spdif_cs_passthru_enable_set(s_sii9438_tx_inst,
					&b_spdif_cs_passthru)) {
		SALZCTRL_LOG_E("[SalzCtrl] sii9438_spdif_cs_passthru_enable_set() is fail\n");
		sema_unlock();
		return FALSE;
	}
	SALZCTRL_LOG_I("[SalzCtrl] sii9438_spdif_cs_passthru_enable_set() is success\n");
	sema_unlock();

	sema_lock();
	if (SII_RETURN_VALUE__SUCCESS
			!= sii9438_mclk_div_set(s_sii9438_tx_inst, &mclk_div)) {
		SALZCTRL_LOG_E("[SalzCtrl] sii9438_mclk_div_set() is fail\n");
		sema_unlock();
		return FALSE;
	}
	SALZCTRL_LOG_I("[SalzCtrl] sii9438_mclk_div_set() is success\n");
	sema_unlock();

	setBEParam(0x00, 0x00); // eARC disconnect / SPDIF

	if (eInitInfo.bEnable) {
		SALZCTRL_LOG_I("[SalzCtrl] eARC enable\n");
		setStatus(SALZCTRL_STATE_DISC1DISC2_EARC_ENABLE);
	} else {
		SALZCTRL_LOG_I("[SalzCtrl] eARC disable\n");
		setARCMode(SII_ARC_MODE__ARC);
		notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);
		setStatus(SALZCTRL_STATE_IDLE2_EARC_DISABLE);
	}

	return TRUE;
}

/* ==========================================================
 * LOCAL FINALIZE FUNCTION FOR MAIN THREAD
 * ==========================================================*/
static 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)) {
			SALZCTRL_LOG_W("[SalzCtrl] sii9438_delete() is fail\n");
		} else {
			SALZCTRL_LOG_I("[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;
	}
	if (NULL_HANDLE != pSii9438SemaphoreHdl) {
		x_sema_unlock(pSii9438SemaphoreHdl);
		x_sema_delete(pSii9438SemaphoreHdl);
		pSii9438SemaphoreHdl = NULL_HANDLE;
	}
	pSii9438MainThreadHdl = NULL_HANDLE;
}

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

	if (TRUE != initialize((SALZ_INIT_SET_STATE *)args)) {
		SALZCTRL_LOG_E("[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)) {
			SALZCTRL_LOG_I("[SalzCtrl] Receive Message ID(%d). \n", msg.id);
			event_function(&msg);
			if (SALZCTRL_STATE_SUSPEND == gState)
				break;
		}
	}

	failure:

	finalize();

	SALZCTRL_LOG_I("[SalzCtrl] Exit Salz Ctrl Main Thread. \n");
}

/* ==========================================================
 * PUBLIC INIT FUNCTION
 * ==========================================================*/
BOOL Sii9438Init(void) {
	UINT8 u1Buf;
	SALZ_INIT_SET_STATE initSetState = SALZ_INIT_SET_ACON;

	SALZCTRL_LOG_I("[SalzCtrl] call Sii9438Init()\n");
	if (!fgHDMIEARCIsSupported()) {
		SALZCTRL_LOG_W("[SalzCtrl] Salzburg is not Supported\n");

		notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);

		u1Buf = 0x01;
		SIF_X_Write(0, 0x100,  0x60, 1, 0x2C, &u1Buf, 1);
		u1Buf = 0x85;
		SIF_X_Write(0, 0x100,  0x64, 1, 0x64, &u1Buf, 1);
		return FALSE;
	}
	SALZCTRL_LOG_I("[SalzCtrl] Salzburg is Supported\n");

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

/* ==========================================================
 * PUBLIC RESUME FUNCTION
 * ==========================================================*/
BOOL Sii9438Resume(void) {
	UINT8 u1Buf;
	SALZ_INIT_SET_STATE initSetState = SALZ_INIT_SET_RESUME;

	SALZCTRL_LOG_I("[SalzCtrl] call Sii9438Resume()\n");
	if (!fgHDMIEARCIsSupported()) {
		SALZCTRL_LOG_W("[SalzCtrl] Salzburg is not Supported\n");

		notifyConnectChgToCEC(HDMI_EARC_NOT_ENABLED);

		u1Buf = 0x01;
		SIF_X_Write(0, 0x100,  0x60, 1, 0x2C, &u1Buf, 1);
		u1Buf = 0x85;
		SIF_X_Write(0, 0x100,  0x64, 1, 0x64, &u1Buf, 1);
		return FALSE;
	}
	SALZCTRL_LOG_I("[SalzCtrl] Salzburg is Supported\n");

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

/* ==========================================================
 * PUBLIC SUSPEND FUNCTION
 * ==========================================================*/
void Sii9438Suspend(void) {
	SALZCTRL_MSG msg;

	SALZCTRL_LOG_I("[SalzCtrl] call Sii9438Suspend()\n");
	if (!fgHDMIEARCIsSupported()) {
		SALZCTRL_LOG_W("[SalzCtrl] Salzburg is not Supported\n");
		return;
	}
	SALZCTRL_LOG_I("[SalzCtrl] Salzburg is Supported\n");

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