/******************************************************************************
 *
 * 2017 (c) Lattice Semiconductor Corporation
 * 
 * This program is free software; you can redistribute it and/or
 * modify it only under the terms of version 2 of the GNU General Public License
 * as published by the Free Software Foundation, and not any later version.
 * 
 * This program is distributed "AS IS" WITHOUT ANY WARRANTY of any kind,
 * whether express or implied, including without limitation the implied warranty
 * of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE or NON-INFRINGEMENT.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * You may contact Lattice Semiconductor Corporation at 
 * 111 SW 5th Ave, Suite 700, Portland, OR 97204.
 *
 *****************************************************************************/

/**
 * @file sii9438_drv.c
 *
 * @brief Driver for SiI9438 (eARC/ARC transmitter)
 *
 *****************************************************************************/
#define SII_DEBUG	3

/***** #include statements ***************************************************/
#include "sii9438_api.h"
#include "sii9438_version.h"
#include "sii9438_drv.h"
#include "sii9438_regs.h"
#include "sii_platform_api.h"
#include "sii_system_api.h"
#include "sii_sys_obj_api.h"
#include "sii_sys_log_api.h"
#include "sii_sys_time_api.h"
#include "sii_sys_malloc_api.h"
#include "sii_sys_seq_api.h"
#include "sii_sys_assert_api.h"
#include "sii_lib_pipe_api.h"

/***** Register Module name **************************************************/
SII_MODULE_NAME_SET(Sii9438);

/***** local macro definitions ***********************************************/
#ifdef SII_SYS_ASSERT_ON
#define SII_ASSERT_CHECK()	{if (b_assert_status)\
					return SII_RETURN_VALUE_ASSERTION_ERR; }
#else
#define SII_ASSERT_CHECK()
#endif

#define SII9438_CHIP_ID		0x9438
#define SII9438_CHIP_REVISION	0x00

/* Maximum length of Software version string */
#define MAX_SW_VERSION_LENGTH		65

#define NUM_BKSV			5

#define SII_FREQ_44_1_KHZ		0x00		/* "44.1Khz" */
#define SII_FREQ_48_KHZ			0x02		/* "48Khz" */
#define SII_FREQ_32_KHZ			0x03		/* "32Khz" */
#define SII_FREQ_88_2_KHZ		0x08		/* "88.2Khz" */
#define SII_FREQ_96_KHZ			0x0A		/* "96Khz" */
#define SII_FREQ_176_4_KHZ		0x0C		/* "176.4Khz" */
#define SII_FREQ_192_KHZ		0x0E		/* "192Khz" */
#define SII_FREQ_4x192_KHZ		0x09		/* "4x192Khz" */

/* Complete channel status size in bytes */
#define SII9438_CHANNEL_STATUS_SIZE	9

/* HDMI HPD minimum low in ms */
#define	SII_HDMI_HPD_LOW_MIN		100

/* T_heartbeat_max time in ms */
#define	SII_HEARTBEAT_TIME_MAX		120

/* Wait time in ms to get the full channel status */
#define	SII_CHANNEL_STATUS_WAIT_TIME	100

/* Wait time to allow IEC audio format code to be received by eARC Rx */
#define	SII_RX_AUDIO_WAIT_TIME			300

/* Wait time in ms to get the full channel status */
#define	SII_MUTE_IN_PIN_POLL_TIME		100

/* Audio mute time in ms (two IEC 60958-1 blocks) */
#define	SII_MUTE_ASSERT_TIME			50

#define SII_SPEAKER_ALLOC_MASK				0xFF
#define BIT_MASK__I2S_CHST6__EARC_CA0_CA4		0xF8
#define BIT_MASK__I2S_CHST6__EARC_CA0_CA4_SHIFT		0x03

/***** local type definitions ************************************************/

struct sii9438_channel_status {
	uint8_t data[SII9438_CHANNEL_STATUS_SIZE];
};

enum sii9438_sm_state {
	SII9438_SM_STATE__IDLE1,
	SII9438_SM_STATE__DISC1,
	SII9438_SM_STATE__IDLE2,
	SII9438_SM_STATE__DISC2,
	SII9438_SM_STATE__ARC,
	SII9438_SM_STATE__EARC
};

const char *sm_states[] = {"IDLE1", "DISC1", "IDLE2", "DISC2", "ARC", "EARC"};

struct fs_table_struc {
	uint8_t		fs;
	char		*fsName;
};

static struct fs_table_struc fs_table[] = {
	{ 0x00, "44.1 KHz" },
	{ 0x02, "48 KHz" },
	{ 0x03, "32 KHz" },
	{ 0x08, "88.2 KHz" },
	{ 0x0A, "96 KHz" },
	{ 0x0C, "176.4 KHz" },
	{ 0x0E, "192 KHz" },
	{ 0x09, "4x192 KHz" }
};

struct obj_t {

	/* Hardware device instantiation identification number */
	uint32_t dev_id;

	sii9438_event_callback_func event_cb_func;

	uint32_t chip_id;

	uint8_t chip_version;

#ifdef SII_LOG_QUEUE
	/* Logging FIFO queue object */
	sii_inst_t log_fifo;
#endif

	uint32_t log_fifo_size;

	/* Controls which events will cause notification handler
	 * to be called */
	uint32_t event_flags_mask;

	/* Event Flags status*/
	uint32_t event_flags_status;

	/* Controls whether SiI9438 is in standby mode or
	 * full operational mode.
	 * In standby mode SiI9438 is configured for low power consumption
	 * and TMDS impedance termination is turned off */
	bool_t b_standby_mode;

	/* ARC mode status (NONE, Legacy ARC, or EARC) */
	enum sii_arc_mode arc_mode_status;

	/* v bit statuas (0, 1, Bypass) */
	enum sii9438_vbit vbit_status;

	/* ARC mode configured to */
	enum sii_arc_mode arc_mode_configured;

	/* State in State machine */
	enum sii9438_sm_state sm_state;

	/* channel status data (only applicable to SPDIF2, SPDIF8, I2S2, I2S8
	 * extraction modes). Set by API */
	struct sii_channel_status channel_status_input;

	/* channel status data (only applicable to SPDIF2, SPDIF8, I2S2, I2S8
	 * extraction modes). */
	struct sii9438_channel_status channel_status;

	/* SPDIF channel status pass through enable flag */
	bool_t spdif_cs_passthru;

	/* Mute Status for audio data. Set by API */
	bool_t audio_mute_set;

	/* GPIO Input/Output configuration */
	/*Each bit represents 1 GPIO pin based on the following assignment:
		Bit 0   : GPIO0
		Bit 1   : GPIO1
		Bit 2   : GPIO2
		Bit 3   : GPIO3
		Bit 4   : GPIO4
		Bit 5   : GPIO5
		Bit 6..7: unused */
	/*
	 * '0' enables GPIO to be an INPUT,
	 * '1' enables GPIO to be an OUTPUT
	 *  By default all GPIOs are configured as INPUT
	 * */
	uint16_t gpio_pin_io_config;

	/* Mask related to GPIO event control */
	uint16_t gpio_pin_event_mask;

	uint32_t mutex;

	uint8_t caps_ds[SII_EARC_CAPS_DS_MAX_LENGTH];

	bool_t b_caps_ds_ready;

	bool_t i2s_clock_status;

	bool_t b_earc_connected_status;

	bool_t b_earc_hpd;

	bool_t b_hb_stat_chng;

	/* HPD IN from GPIO4 */
	bool_t b_hpd_in;

	/* HPD IN from hpd_set() API */
	bool_t b_hpd_in_user;

	/* TX HPD OUT */
	bool_t b_hpd_out;

	/* HPD sent virtually through heartbeat */
	bool_t b_virtual_hdmi_hpd;

	/* Timer sequence instance */
	sii_inst_t seq_inst;

	sii_inst_t virtual_hdmi_hpd_timer;

	bool_t b_virtual_hdmi_hpd_pending;

	sii_inst_t ch_stat_wait_timer;

	sii_inst_t audio_wait_timer;

	sii_inst_t mute_pin_poll_timer;

	sii_inst_t audio_mute_timer;

	bool_t mute_in_pin_status;

	enum  sii9438_insertion_mode aud_mode;

	struct sii_audio_info audio_info;

	struct sii_erx_latency erx_latency;

	struct sii_erx_latency_req erx_latency_req;

	/* Config params from application */
	bool_t caps_ds_stress_test;

	bool_t is_input_channel_staus_valid;

	bool_t mute_state;

	bool_t b_input_ch_status_avail;
	bool_t b_input_audio_info_avail;

	bool_t b_input_signal_stable;

	bool_t b_ch_status_update;

	bool_t b_tx_diff_mode_reset;

	/* Configured MCLK divider.
	 * (DIV_BY1, DIV_BY2, DIV_BY4) Set by API */
	enum sii9438_mclk_div mclock_div;

	bool_t b_tx_pll_enable;

	bool_t b_erx_latency_read_pending;

	bool_t b_caps_ds_read_pending;
};


/***** local data objects ****************************************************/

static sii_inst_t inst_list = SII_INST_NULL;

/***** local prototypes ******************************************************/

static uint32_t s_int_handle(struct obj_t *p_obj);

static void s_gpio_config_update(struct obj_t *p_obj);

static void s_gpio_output_update(struct obj_t *p_obj,
		uint16_t *p_gpio_pin_mask, bool_t b_set);

static void s_sii9438_init(struct obj_t *p_obj,
		const struct sii9438_config *p_config);

static void s_user_notification_handler(struct obj_t *p_obj,
			uint32_t evt_flgs_notify);

static uint32_t s_audio_update(struct obj_t *p_obj);

static void s_gpio_event_mask_set(struct obj_t *p_obj);

static uint32_t s_update_channel_status(struct obj_t *p_obj);

static bool_t s_is_non_lpcm(struct obj_t *p_obj);

static bool_t s_is_2ch_lpcm(struct obj_t *p_obj);

static void s_standby_set(struct obj_t *p_obj, bool_t *p_on);

static void s_aud_format_set(struct obj_t *p_obj);

static void s_print_channel_status(struct obj_t *p_obj,
		struct sii9438_channel_status *p_ch_status,
		bool_t b_extra_bytes);

static void s_audio_mute(struct obj_t *p_obj, bool_t b_on);

static void s_print_mem_usage(struct obj_t *p_obj, const char *log_str);

static uint32_t s_arc_mode_update(struct obj_t *p_obj,
		enum sii_arc_mode arc_mode_status);

static uint32_t s_channel_status_input_valid(struct obj_t *p_obj);

static void s_hpd_out_update(struct obj_t *p_obj, bool_t b_earc_conn_stat);

static uint32_t s_interrupt_enable(struct obj_t *p_obj, bool_t enable);
static void s_tx_phy_fifo_reset(struct obj_t *p_obj, bool_t b_reset);
static void s_virtual_hdmi_hpd_set(struct obj_t *p_obj,
		bool_t b_hpd);
static void s_virtual_hdmi_hpd_timer_start(struct obj_t *p_obj);
static bool_t s_hpd_in_get(struct obj_t *p_obj);
static uint32_t s_channel_status_write(struct obj_t *p_obj);
static void s_ch_stat_wait_timer_start(struct obj_t *p_obj);
static void s_audio_wait_timer_start(struct obj_t *p_obj);
static void s_aud_info_set(struct obj_t *p_obj);
static void s_bsync_count_set(struct obj_t *p_obj,
		enum sii9438_insertion_mode ins_mode);
static void s_config_update(struct obj_t *p_obj);
static void s_sm_state_update(struct obj_t *p_obj, uint8_t *int_regs);
static void s_arc_change_sm_update(struct obj_t *p_obj);
static void s_state_change_sm_update(struct obj_t *p_obj);
static void s_mute_in_poll_timer_start(struct obj_t *p_obj);
static void s_vbit_update(struct obj_t *p_obj);
static char *s_getting_freq_name(uint8_t fs);
static bool_t s_is_hbra(struct obj_t *p_obj);
static void s_tx_pll_enable(struct obj_t *p_obj, bool_t b_enable);
static void s_caps_ds_read(struct obj_t *p_obj);

/***** call-back functions ***************************************************/

/* Timer value callback routine to be used by seq timers */
void sii_timer_value_callback(sii_inst_t inst,
	sii_sys_seq_time_ms_t *timer_value, bool_t b_set)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
				inst);
	SII_ASSERT(p_obj);
	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (!timer_value)
		return;

	if (b_set) {
		SII_LOG3A("", NULL,
			(" Timer value setting to - %d\n", *timer_value));
		sii9438_hal_timer_value_set(p_obj->dev_id, *timer_value);
	} else {
		sii9438_hal_timer_value_get(p_obj->dev_id, timer_value);
		SII_LOG3A("", NULL,
			(" Timer value retrieved - %d\n", *timer_value));
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);
}

/* Calllback routine for virtual HDMI HPD low timer */
void virtual_hdmi_hpd_timer_callback(sii_inst_t inst)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	SII_ASSERT(p_obj);

	/* Send pending virtual HDMI HPD on timer expiration */
	s_virtual_hdmi_hpd_set(p_obj, p_obj->b_virtual_hdmi_hpd_pending);
}

/* Callback routine for SPDIF channel status wait timer expiration */
void ch_stat_wait_timer_callback(sii_inst_t inst)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
				inst);
	struct sii_channel_status ch_stat;

	SII_ASSERT(p_obj);

	if (p_obj->spdif_cs_passthru &&
		(p_obj->aud_mode == SII9438_INSERTION_MODE__SPDIF2 ||
		p_obj->aud_mode == SII9438_INSERTION_MODE__SPDIF8)) {

		sii9438_hal_spdif_channel_status_query(p_obj->dev_id, &ch_stat);
		memcpy(p_obj->channel_status.data,
			ch_stat.data,
			SII_CHANNEL_STATUS_SIZE);

		SII_LOG3A("", NULL, ("SPDIF In Channel Status:\n"));
	} else {
		/* Check if input channel status and audio info is
		 * available. If not available, wait until input
		 * channel status and audio info is set by application */
		if (!(p_obj->b_input_ch_status_avail &&
				p_obj->b_input_audio_info_avail &&
				p_obj->b_ch_status_update)) {
			SII_LOG3A("", NULL,
				("Error: Ch Status Not Available\n"));
			return;
		}

		memcpy(p_obj->channel_status.data,
			p_obj->channel_status_input.data,
			SII_CHANNEL_STATUS_SIZE);

		SII_LOG3A("", NULL, ("Input Channel Status:\n"));
	}

	SII_MEMSET(&p_obj->channel_status.data[SII_CHANNEL_STATUS_SIZE], 0, 4);

	s_print_channel_status(p_obj, &p_obj->channel_status, FALSE);

	s_channel_status_write(p_obj);
}

/* Callback routine for MUTE in pin Low Poll timer expiration */
void mute_pin_poll_timer_callback(sii_inst_t inst)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
				inst);
	bool_t hw_mute_in_pin_status;

	SII_ASSERT(p_obj);

	sii9438_hal_tx_hw_mute_out_status_query(
			p_obj->dev_id,
			&hw_mute_in_pin_status);

	SII_LOG2A("", NULL,
		("Input HW MUTE Pin status:%s\n",
				hw_mute_in_pin_status ?
				"MUTED" : "UNMUTED"));
	if (!hw_mute_in_pin_status) {
		sii9438_hal_tx_mute_excep_det_int_clr(
				p_obj->dev_id);
		p_obj->mute_in_pin_status = FALSE;
		sii9438_hal_mute_excep_det_intr_enable(p_obj->dev_id,
							TRUE);
		SII_LOG2A("", NULL, ("HW Mute Out: UNMUTED\n"));
	} else {
		s_mute_in_poll_timer_start(p_obj);
	}
}

/* Callback routine for audio wait timer */
void audio_wait_timer_callback(sii_inst_t inst)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	bool_t b_unmute_audio = TRUE;
	SII_ASSERT(p_obj);

	/* For Non-LPCM, enable ECC after channel status is updated */
	if (s_is_non_lpcm(p_obj)) {
		bool_t b_ecc_on = FALSE;
		uint8_t stat_check_countdown = 200;

		/* For Non LPCM, enable ECC */
		SII_LOG2A("", NULL, ("Enabling ECC...\n"));
		sii9438_hal_ecc_enable(p_obj->dev_id, TRUE);
		do {
			sii9438_hal_ecc_status(p_obj->dev_id, &b_ecc_on);
			/* Update countdown value */
			stat_check_countdown--;
		} while (!b_ecc_on && stat_check_countdown);

		if (b_ecc_on) {
			SII_LOG2A("", NULL, ("ECC is turned ON\n"));
		} else {
			SII_LOG2A("", NULL,
				("ECC is NOT turned ON before timeout!\n"));
			b_unmute_audio = FALSE;
		}
	}

	if (b_unmute_audio)
		s_audio_mute(p_obj, FALSE);
}

/* Callback routine for audio mute timer */
void audio_mute_timer_callback(sii_inst_t inst)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
				inst);
	SII_ASSERT(p_obj);

	sii9438_hal_ecc_enable(p_obj->dev_id, FALSE);

	s_tx_phy_fifo_reset(p_obj, TRUE);

	sii9438_hal_tx_out_clk_en(p_obj->dev_id, FALSE);

	if (p_obj->i2s_clock_status && p_obj->b_earc_connected_status)
		p_obj->b_input_signal_stable = TRUE;
	else
		p_obj->b_input_signal_stable = FALSE;

	if (p_obj->b_input_signal_stable && p_obj->b_caps_ds_ready) {

		sii9438_hal_reset_aud_fifo(p_obj->dev_id);

		s_update_channel_status(p_obj);
	}
}

static void s_caps_ds_read(struct obj_t *p_obj)
{
	SII_ASSERT(p_obj);
	SII_MEMSET(p_obj->caps_ds, 0, SII_EARC_CAPS_DS_MAX_LENGTH);
	sii9438_hal_caps_ds_get(p_obj->dev_id, p_obj->caps_ds);
	p_obj->b_caps_ds_ready = TRUE;
	p_obj->b_ch_status_update = TRUE;
}

/***** public functions ******************************************************/

uint32_t sii9438_create(const struct sii9438_config *p_config,
			sii_inst_t *p_inst)

{
	struct obj_t *p_obj = NULL;
	sii_inst_t parent_inst = SII_INST_NULL;
	uint16_t i;

	SII_ASSERT_CHECK();
	SII_ASSERT(p_config);
	SII_ASSERT(p_inst);
	SII_ASSERT_CHECK();

	s_print_mem_usage(p_obj, "Before Sii9438 Create");

	/* Create instance list if not exist */
	if (!inst_list) {
		inst_list = sii_sys_obj_list_create(SII_MODULE_NAME_GET(),
				sizeof(struct obj_t));
		SII_ASSERT(inst_list);
	} else {
		/* Check if there is any instance existing with the same
		 * device ID */
		struct obj_t *p_obj_iter = sii_sys_obj_first_get(inst_list);

		while (p_obj_iter) {
			if (p_obj_iter->dev_id == p_config->dev_id)
				return SII_RETURN_VALUE__DEV_ID_ERR;

			p_obj_iter = sii_sys_obj_next_get(p_obj_iter);
		}
	}

	/* Create instance */
	p_obj = (struct obj_t *) sii_sys_obj_instance_create(inst_list,
			parent_inst,
			"TX");
	SII_ASSERT(p_obj);

	/* Initialise instance */
	p_obj->dev_id = p_config->dev_id;
	p_obj->event_cb_func = p_config->callback_func;
	p_obj->log_fifo_size = p_config->log_fifo_size;
	p_obj->chip_id = SII9438_CHIP_ID;
	p_obj->chip_version = SII9438_CHIP_REVISION;
	p_obj->event_flags_mask = 0xFFFFFFFF;
	p_obj->event_flags_status = 0;
	p_obj->b_standby_mode = FALSE;
	p_obj->b_earc_hpd = FALSE;
	p_obj->b_caps_ds_ready = FALSE;
	p_obj->b_hpd_in = FALSE;
	/* User set HPD is high by default so that HPD_IN are effective */
	p_obj->b_hpd_in_user = TRUE;
	p_obj->b_hpd_out = FALSE;
	p_obj->b_virtual_hdmi_hpd = FALSE;
	p_obj->b_virtual_hdmi_hpd_pending = FALSE;

	/* Default ARC mode is eARC */
	p_obj->arc_mode_status = SII_ARC_MODE__EARC;
	p_obj->arc_mode_configured = SII_ARC_MODE__EARC;

	p_obj->sm_state = SII9438_SM_STATE__IDLE2;

	for (i = 0; i < SII_CHANNEL_STATUS_SIZE; i++)
		p_obj->channel_status_input.data[i] = 0;

	for (i = 0; i < SII9438_CHANNEL_STATUS_SIZE; i++)
		p_obj->channel_status.data[i] = 0;

	p_obj->spdif_cs_passthru = TRUE;
	p_obj->gpio_pin_io_config = 0;
	p_obj->gpio_pin_event_mask = 0;

	p_obj->i2s_clock_status = 0;

	p_obj->caps_ds_stress_test = FALSE;
	p_obj->audio_mute_set = FALSE;
	p_obj->b_earc_connected_status = FALSE;
	p_obj->aud_mode = SII9438_INSERTION_MODE__NONE;
	p_obj->is_input_channel_staus_valid = FALSE;
	p_obj->b_input_ch_status_avail = FALSE;
	p_obj->b_input_audio_info_avail = FALSE;
	p_obj->b_input_signal_stable = FALSE;
	p_obj->b_ch_status_update = FALSE;
	p_obj->b_tx_diff_mode_reset = FALSE;
	p_obj->vbit_status = SII9438_VBIT_BYPASS;
	p_obj->mclock_div = SII9438_MCLK_DIV__NO_DIV;
	p_obj->b_tx_pll_enable = TRUE; /* Default value */
	p_obj->b_erx_latency_read_pending = FALSE;
	p_obj->b_caps_ds_read_pending = FALSE;

#ifdef SII_LOG_QUEUE
	p_obj->log_fifo = sii_lib_pipe_create("log_fifo",
			p_config->log_fifo_size, sizeof(char));
	sii_sys_log_set_pipe(p_obj->log_fifo);
#endif

	SII_MEMSET(&(p_obj->audio_info), 0, sizeof(struct sii_audio_info));

#if defined(SII943X_USER_DEF__MULTI_THREAD)
	/* Create/Initialise resources */
	/* Create mutex for multi-thread safe handling */
	p_obj->mutex = SII_OSAL_MUTEX_CREATE(dev_id);
	SII_ASSERT(p_obj->mutex);
#endif

	s_sii9438_init(p_obj, p_config);

	/* Create timer sequence to add timers */
	p_obj->seq_inst = sii_sys_seq_create((sii_inst_t) p_obj,
			sii_timer_value_callback);

	SII_ASSERT(p_obj->seq_inst);

	p_obj->virtual_hdmi_hpd_timer =
			sii_sys_seq_timer_create(p_obj->seq_inst,
				"Virtual_HDMI_HPD_Timer",
				virtual_hdmi_hpd_timer_callback, p_obj);

	p_obj->ch_stat_wait_timer =
			sii_sys_seq_timer_create(p_obj->seq_inst,
					"SPDIF_Ch_Stat_Wait_Timer",
				ch_stat_wait_timer_callback, p_obj);

	p_obj->audio_wait_timer =
			sii_sys_seq_timer_create(p_obj->seq_inst,
				"Rx_Audio_Wait_Timer",
				audio_wait_timer_callback, p_obj);

	p_obj->mute_pin_poll_timer =
			sii_sys_seq_timer_create(p_obj->seq_inst,
				"Rx_Mute_In_Poll_Timer",
				mute_pin_poll_timer_callback, p_obj);

	p_obj->audio_mute_timer =
			sii_sys_seq_timer_create(p_obj->seq_inst,
				"Audio_Mute_Timer",
				audio_mute_timer_callback, p_obj);

	*p_inst = (sii_inst_t) p_obj;

	sii9438_hal_chip_version_query(p_obj->dev_id,
					&(p_obj->chip_version));

	s_print_mem_usage(p_obj, "After Sii9438 Create");

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_delete(sii_inst_t inst)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
				inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT_CHECK();

	s_print_mem_usage(p_obj, "Before Sii9438 Delete");

#if defined(SII943X_USER_DEF__MULTI_THREAD)

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	/* Delete Mutex */
	SII_OSAL_MUTEX_DELETE(p_obj->mutex);
	p_obj->mutex = 0;
#endif

	/* Reset the chip */
	sii9438_hal_chip_reset(p_obj->dev_id);

	/* Delete timers in the reverse order they were created */
	sii_sys_seq_timer_delete(p_obj->audio_mute_timer);
	sii_sys_seq_timer_delete(p_obj->mute_pin_poll_timer);
	sii_sys_seq_timer_delete(p_obj->audio_wait_timer);
	sii_sys_seq_timer_delete(p_obj->ch_stat_wait_timer);
	sii_sys_seq_timer_delete(p_obj->virtual_hdmi_hpd_timer);

	/* Delete sequencer instance */
	sii_sys_seq_delete(p_obj->seq_inst);

#ifdef SII_LOG_QUEUE
	/* Delete the logging FIFO */
	if (p_obj->log_fifo)
		sii_lib_pipe_delete(p_obj->log_fifo);
#endif

	/* Delete the driver instance */
	if (p_obj) {
		sii_sys_obj_instance_delete(p_obj);
		p_obj = NULL;
	}

	/* Delete instance list if the list is empty*/
	if (inst_list) {
		if (NULL == sii_sys_obj_first_get(inst_list)) {
			sii_sys_obj_list_delete(inst_list);
			inst_list = SII_INST_NULL;
		}
	}

	s_print_mem_usage(p_obj, "After Sii9438 Delete");

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_handle(sii_inst_t inst)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	/* If there is any assertion failure, notify the event */
	if (b_assert_status) {
		SII_LOG1A("", NULL, ("Assertion error!\n"));
		s_user_notification_handler(p_obj, SII9438_EVENT_FLAGS__ASSERT);
	}

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	/* Handle notifications and interrupts */
	status = s_int_handle(p_obj);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return status;
}

uint32_t sii9438_chip_id_query(sii_inst_t inst,
	uint32_t *p_chip_id)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_chip_id);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_chip_id = p_obj->chip_id;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_chip_version_query(sii_inst_t inst,
	uint8_t *p_chip_version)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_chip_version);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_chip_version = p_obj->chip_version;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_software_version_query(char *version_str, uint8_t size)
{
	char sw_version[MAX_SW_VERSION_LENGTH];
	uint32_t return_value = 0;

	SII_ASSERT_CHECK();
	SII_ASSERT(version_str);
	SII_ASSERT_CHECK();

	SII_STRCPY(sw_version, SII_VERSION, MAX_SW_VERSION_LENGTH);
	SII_STRCAT(sw_version, ".", MAX_SW_VERSION_LENGTH);
	SII_STRCAT(sw_version, SII_REVISION, MAX_SW_VERSION_LENGTH);
	if (SII_STRCMP(SII_REVISION, "unknown")) {
		if (0 != SII_STRLEN(SII_BRANCH)) {
			SII_STRCAT(sw_version, ".", MAX_SW_VERSION_LENGTH);
			SII_STRCAT(sw_version, SII_BRANCH,
			 MAX_SW_VERSION_LENGTH);
		}
	}
	if (0 != SII_STRLEN(SII_USERNAME)) {
		SII_STRCAT(sw_version, "_", MAX_SW_VERSION_LENGTH);
		SII_STRCAT(sw_version, SII_USERNAME, MAX_SW_VERSION_LENGTH);
	}

	SII_STRCPY(version_str, sw_version, size);
	if (size <= SII_STRLEN(sw_version))
		return_value = SII_STRLEN(sw_version) - size + 1;

	return return_value;
}

uint32_t sii9438_log_queue_query(sii_inst_t inst,
	char *log_str, uint32_t size)
{
#ifdef SII_LOG_QUEUE
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	uint32_t i;
	enum sii_lib_pipe_error error_code;

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(log_str);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	for (i = 0; i < size; i++) {
		error_code = sii_lib_pipe_read(p_obj->log_fifo, &log_str[i]);
		if (error_code == SII_LIB_PIPE_ERROR_EMPTY)
			break;
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

#endif

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_log_queue_size(sii_inst_t inst, uint32_t *p_size)
{
#ifdef SII_LOG_QUEUE
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_size);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_size = sii_lib_pipe_get_fill_size(p_obj->log_fifo);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);
#else
	SII_ASSERT(p_size);
	*p_size = 0;
#endif

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_assert_query(sii_inst_t inst, uint32_t *p_value)
{
	SII_ASSERT(p_value);

	if (!p_value) {
		SII_ASSERT_CHECK();
	} else {
		*p_value = assert_module_id;
		(*p_value) <<= 24;
		(*p_value) |= assert_line_no;
	}

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_event_flags_mask_set(sii_inst_t inst,
		uint32_t *p_flags)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_flags);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_obj->event_flags_mask = *p_flags;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_event_flags_mask_get(sii_inst_t inst,
		uint32_t *p_flags)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_flags);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_flags = p_obj->event_flags_mask;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_event_flags_status_clear(sii_inst_t inst,
	uint32_t *p_flags)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_flags);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_obj->event_flags_status &= (~(*p_flags));

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_event_flags_query(sii_inst_t inst,
	uint32_t *p_flags)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_flags);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_flags = p_obj->event_flags_status;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_standby_set(sii_inst_t inst, bool_t *p_on)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_on);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	s_standby_set(p_obj, p_on);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_standby_get(sii_inst_t inst, bool_t *p_on)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_on);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_on = p_obj->b_standby_mode;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_gpio_config_set(sii_inst_t inst,
	uint16_t *p_gpio_pin_mask)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_gpio_pin_mask);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_obj->gpio_pin_io_config = *p_gpio_pin_mask;
	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL,
			("Standby Mode: Skipping GPIO config set\n"));
	} else
		s_gpio_config_update(p_obj);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_gpio_config_get(sii_inst_t inst,
	uint16_t *p_gpio_pin_mask)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_gpio_pin_mask);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_gpio_pin_mask = p_obj->gpio_pin_io_config;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_gpio_output_set(sii_inst_t inst,
	uint16_t *p_gpio_pin_mask)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_gpio_pin_mask);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL,
			("Standby Mode: Skipping GPIO output set\n"));
	} else {
		/* Take output pins only from the mask */
		*p_gpio_pin_mask = (*p_gpio_pin_mask) &
				p_obj->gpio_pin_io_config;
		s_gpio_output_update(p_obj, p_gpio_pin_mask, TRUE);
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_gpio_output_clr(sii_inst_t inst,
	uint16_t *p_gpio_pin_mask)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_gpio_pin_mask);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL,
			("Standby Mode: Skipping GPIO output clear\n"));
	} else {
		/* Take output pins only from the mask */
		*p_gpio_pin_mask = (*p_gpio_pin_mask) &
				p_obj->gpio_pin_io_config;
		s_gpio_output_update(p_obj, p_gpio_pin_mask, FALSE);
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_gpio_event_mask_set(sii_inst_t inst,
	uint16_t *p_gpio_pin_mask)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_gpio_pin_mask);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_obj->gpio_pin_event_mask = *p_gpio_pin_mask;

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL,
			("Standby Mode: Skipping GPIO event mask set\n"));
	} else
		s_gpio_event_mask_set(p_obj);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_gpio_event_mask_get(sii_inst_t inst,
	uint16_t *p_gpio_pin_mask)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_gpio_pin_mask);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_gpio_pin_mask = p_obj->gpio_pin_event_mask;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_gpio_query(sii_inst_t inst, uint16_t *p_gpio_pin_mask)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_gpio_pin_mask);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	sii9438_hal_gpio_query(p_obj->dev_id, p_gpio_pin_mask);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_earc_caps_ds_query(sii_inst_t inst, uint16_t offset,
	uint8_t *p_data, uint16_t length)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_data);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	SII_MEMCPY(p_data, p_obj->caps_ds + offset, length);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_earc_hpd_query(sii_inst_t inst, bool_t *p_hpd)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_hpd);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_hpd = p_obj->b_earc_hpd;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_arc_mode_set(sii_inst_t inst,
	enum sii_arc_mode *p_arc_mode)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_arc_mode);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL, ("Standby Mode: Skipping ARC mode set\n"));
	} else if (p_obj->arc_mode_status != *p_arc_mode) {
		p_obj->arc_mode_status = *p_arc_mode;
		s_arc_change_sm_update(p_obj);
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return status;
}

uint32_t sii9438_arc_mode_get(sii_inst_t inst,
	enum sii_arc_mode *p_arc_mode)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_arc_mode);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_arc_mode = p_obj->arc_mode_status;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_earc_link_query(sii_inst_t inst, bool_t *p_on)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_on);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_on = p_obj->b_earc_connected_status;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_insertion_mode_set(sii_inst_t inst,
	enum sii9438_insertion_mode *p_mode)
{
	uint32_t status = SII_RETURN_VALUE__SUCCESS;
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_mode);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (p_obj->aud_mode != *p_mode) {
		p_obj->aud_mode = *p_mode;
		p_obj->b_ch_status_update =  TRUE;
		SII_LOG3A("", NULL, ("Insertion mode set to - %d\n", *p_mode));

		sii_hal_audio_mode_set(p_obj->dev_id,
					p_obj->aud_mode);

		status = s_audio_update(p_obj);
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return status;
}

uint32_t sii9438_insertion_mode_get(sii_inst_t inst,
	enum sii9438_insertion_mode *p_mode)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_mode);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_mode = p_obj->aud_mode;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_channel_status_set(sii_inst_t inst,
	struct sii_channel_status *p_channel_status)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_channel_status);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	memcpy(&p_obj->channel_status_input,
		p_channel_status,
		sizeof(struct sii_channel_status));
	p_obj->b_input_ch_status_avail = TRUE;

	SII_LOG2A("", NULL, ("New Channel Status Set\n"));

	if (!(p_obj->spdif_cs_passthru &&
		(p_obj->aud_mode == SII9438_INSERTION_MODE__SPDIF2 ||
		p_obj->aud_mode == SII9438_INSERTION_MODE__SPDIF8))) {
		p_obj->b_ch_status_update = TRUE;
		status = s_audio_update(p_obj);
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return status;
}

uint32_t sii9438_channel_status_get(sii_inst_t inst,
	struct sii_channel_status *p_channel_status)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_channel_status);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	memcpy(p_channel_status->data,
		p_obj->channel_status_input.data,
		SII_CHANNEL_STATUS_SIZE);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_spdif_cs_passthru_enable_set(sii_inst_t inst,
	bool_t *p_on)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_on);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (p_obj->spdif_cs_passthru != *p_on) {
		p_obj->spdif_cs_passthru = *p_on;

		if (p_obj->b_standby_mode) {
			SII_LOG1A("", NULL,
				("Standby Mode: Skipping cs passthru set\n"));
		} else {
			sii9438_hal_tx_spdif_ch_status_intr_enable(
				p_obj->dev_id, *p_on);
			SII_LOG3A("", NULL, ("CS Pass through: %s\n",
					*p_on ? "Enabled" : "Disabled"));
			p_obj->b_ch_status_update = TRUE;
			status = s_audio_update(p_obj);
		}
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return status;
}

uint32_t sii9438_spdif_cs_passthru_enable_get(sii_inst_t inst,
	bool_t *p_on)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_on);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_on = p_obj->spdif_cs_passthru;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_spdif_channel_status_query(sii_inst_t inst,
	struct sii_channel_status *p_channel_status)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_channel_status);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	sii9438_hal_spdif_channel_status_query(p_obj->dev_id, p_channel_status);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_mute_enable_set(sii_inst_t inst, bool_t *p_flag)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_flag);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (p_obj->audio_mute_set != *p_flag) {
		p_obj->audio_mute_set = *p_flag;
		p_obj->mute_state = *p_flag;

		if (p_obj->b_standby_mode) {
			SII_LOG1A("", NULL,
				("Standby Mode: Skipping mute enable set\n"));
		} else {
			/* Set Audio Mute */
			sii9438_hal_aud_mute_set(p_obj->dev_id, *p_flag);
		}
	}

	SII_LOG3A("", NULL, ("Audio mute setting to - %d\n", *p_flag));

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_mute_enable_get(sii_inst_t inst, bool_t *p_flag)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_flag);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_flag = p_obj->audio_mute_set;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_hpd_set(sii_inst_t inst, bool_t *p_hpd)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_hpd);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_obj->b_hpd_in_user = *p_hpd;

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL, ("Standby Mode: Skipping HPD Set\n"));
	} else
		s_virtual_hdmi_hpd_set(p_obj,
			p_obj->b_hpd_in_user && p_obj->b_hpd_in);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_audio_info_set(sii_inst_t inst,
		struct sii_audio_info *p_audio_info)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_audio_info);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	SII_MEMCPY(&(p_obj->audio_info), p_audio_info,
			sizeof(struct sii_audio_info));

	p_obj->b_input_audio_info_avail = TRUE;

	SII_LOG2A("", NULL, ("New Audio Info Set\n"));

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL, ("Standby Mode: Skipping audio update\n"));
	} else {
		p_obj->b_ch_status_update = TRUE;
		status = s_audio_update(p_obj);
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return status;
}

uint32_t sii9438_audio_info_get(sii_inst_t inst,
		struct sii_audio_info *p_audio_info)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_audio_info);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	SII_MEMCPY(p_audio_info, &(p_obj->audio_info),
			sizeof(struct sii_audio_info));

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_erx_latency_req_set(sii_inst_t inst,
				struct sii_erx_latency_req *p_erx_latency_req)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_erx_latency_req);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_obj->erx_latency_req.data = p_erx_latency_req->data;

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL,
			("Standby Mode: Skipping latency req set\n"));
	} else {
		/* Set latency */
		sii9438_hal_erx_latency_req_set(p_obj->dev_id,
				p_erx_latency_req->data);
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_erx_latency_req_get(sii_inst_t inst,
				struct sii_erx_latency_req *p_erx_latency_req)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_erx_latency_req);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_erx_latency_req->data = p_obj->erx_latency_req.data;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_erx_latency_query(sii_inst_t inst,
				struct sii_erx_latency *p_erx_latency)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_erx_latency);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_erx_latency->data = p_obj->erx_latency.data;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_vbit_set(sii_inst_t inst, enum sii9438_vbit *p_vbit)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_vbit);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	if (p_obj->vbit_status != *p_vbit) {
		p_obj->vbit_status = *p_vbit;
		if (p_obj->b_standby_mode) {
			SII_LOG1A("", NULL,
				("Standby Mode: Skipping v-bit set\n"));
		} else  {
			s_vbit_update(p_obj);
			SII_LOG3A("", NULL, ("V-bit Updated\n"));
		}
	}

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return status;
}

uint32_t sii9438_vbit_get(sii_inst_t inst, enum sii9438_vbit *p_vbit)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_vbit);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_vbit = p_obj->vbit_status;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_mclk_div_set(sii_inst_t inst,
	enum sii9438_mclk_div *p_div)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_div);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	p_obj->mclock_div = *p_div;
	if (p_obj->b_standby_mode) {
		SII_LOG1A("", NULL,
			("Standby Mode: Skipping MCLK mode set\n"));
	} else
		sii9438_hal_mclk_div_set(p_obj->dev_id, *p_div);

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

uint32_t sii9438_mclk_div_get(sii_inst_t inst,
	enum sii9438_mclk_div *p_div)
{
	struct obj_t *p_obj = (struct obj_t *)SII_SYS_OBJ_CHECK(inst_list,
			inst);

	SII_ASSERT_CHECK();
	SII_ASSERT(p_obj);
	SII_ASSERT(p_div);
	SII_ASSERT_CHECK();

	SII_OSAL_MUTEX_SET(p_obj->mutex);

	*p_div = p_obj->mclock_div;

	SII_OSAL_MUTEX_CLR(p_obj->mutex);

	return SII_RETURN_VALUE__SUCCESS;
}

/***** local functions *******************************************************/
static uint32_t s_int_handle(struct obj_t *p_obj)
{
	uint32_t evt_flgs_notify = 0;

	uint8_t int_regs[REG_TX__INT_NUM];
	uint8_t int_top_regs[REG_TOP__INT_NUM];
	uint8_t reg_top;
	uint8_t int_status;
	uint16_t gpio_values;
	uint32_t status = SII_RETURN_VALUE__SUCCESS;
	bool_t b_update_audio = FALSE;
	bool_t spdif_cs_ch_intr_en = FALSE;

#ifdef SII_LOG_QUEUE
	if (sii_lib_pipe_get_fill_size(p_obj->log_fifo))
		s_user_notification_handler(p_obj,
			SII9438_EVENT_FLAGS__LOG_FIFO_CHNG);
#endif

	sii9438_hal_top_int_get(p_obj->dev_id, &reg_top);

	if (reg_top & BIT_MSK__TOP_INTR_STATE__INTR_STATE_TOP) {

		SII_LOG3A("", NULL, ("__Processing 9438 interrupts__\n"));

		/* Disable all Interrupts */
		sii9438_hal_enable_all_int(p_obj->dev_id, FALSE);

		sii9438_hal_earc_block_int_get(p_obj->dev_id, &int_status);

		if (int_status & BIT_MSK__INTR_STATE__INTR_STATE) {

			sii9438_hal_earc_int_status_get(p_obj->dev_id,
							int_regs,
							REG_TX__INT_NUM);
			/*SII_LOG1A("", NULL,
			("eARC INTR status: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
					int_regs[REG_INTR_0],
					int_regs[REG_INTR_1],
					int_regs[REG_INTR_2],
					int_regs[REG_INTR_3]));
			SII_LOG1A("", NULL,
			("eARC INTR status: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
					int_regs[REG_INTR_4],
					int_regs[REG_INTR_5],
					int_regs[REG_INTR_6]));*/

			if (int_regs[REG_INTR_0] &
				BIT_MASK_TX__FIFO_FULL_ERR) {
				sii9438_hal_reset_aud_fifo(p_obj->dev_id);
				SII_LOG1A("", NULL,
				("audio fifo full err: 0x%02x\n",
						int_regs[REG_INTR_0]));
			}
		}

		/* Read all top interrupt status registers This function
		automatically clears the interrupts after read */
		sii9438_hal_top_int_status_get(p_obj->dev_id,
						int_top_regs,
						REG_TOP__INT_NUM);

		SII_LOG3A("", NULL,
				("Top INTR status: 0x%02x, 0x%02x, 0x%02x\n",
					int_top_regs[REG_INTR_TOP_1],
					int_top_regs[REG_INTR_TOP_2],
					int_top_regs[REG_INTR_TOP_3]));

		if (int_top_regs[REG_INTR_TOP_1] &
			BIT_MASK__TOP_INTR1_MASK__TOP_TIMER) {
			SII_LOG3A("", NULL, ("Timer interrupt received\n"));
			sii_sys_seq_handle(p_obj->seq_inst);
		}

		sii9438_hal_gpio_query(p_obj->dev_id, &gpio_values);
		SII_LOG2A("", NULL, ("GPIO values: 0x%02x\n",
			gpio_values));

		/* Check HPD-IN change from GPIO4 */
		if ((int_top_regs[REG_INTR_TOP_2] &
			BIT_MSK__TOP_INTR2__REG_TOP_INTR_STAT10) ||
			(int_top_regs[REG_INTR_TOP_3] &
			BIT_MSK__TOP_INTR3__REG_TOP_INTR_STAT16)) {

			p_obj->b_hpd_in = s_hpd_in_get(p_obj);

			SII_LOG3A("", NULL,
				("GPIO4 (TX HPD IN) changed: %s\n",
					p_obj->b_hpd_in ? "hi" : "low"));
			s_virtual_hdmi_hpd_set(p_obj,
				p_obj->b_hpd_in && p_obj->b_hpd_in_user);
		}

		if (int_status & BIT_MSK__INTR_STATE__INTR_STATE) {

			s_sm_state_update(p_obj, int_regs);

			if (int_regs[REG_INTR_6] &
				BIT_MASK_TX__EARC_CONN_CHANGE) {
				bool_t b_val;
				sii9438_hal_earc_conn_status_get(p_obj->dev_id,
								&b_val);
				if (b_val == TRUE) {
					SII_LOG2A("", NULL,
						("TX eARC Connected\n"));
					sii9438_hal_tx_out_clk_en(p_obj->dev_id,
							FALSE);
					s_tx_pll_enable(p_obj, TRUE);
					sii9438_hal_tx_out_clk_en(p_obj->dev_id,
							FALSE);
				} else {
					sii9438_hal_tx_out_clk_en(p_obj->dev_id,
							FALSE);
					SII_LOG2A("", NULL,
						("TX eARC Disconnected\n"));
					p_obj->b_caps_ds_ready = FALSE;
					sii9438_hal_cap_chng_conf_set(
							p_obj->dev_id, FALSE);
					sii9438_hal_stat_chng_conf_set(
							p_obj->dev_id, FALSE);
					s_tx_pll_enable(p_obj, FALSE);
				}

				evt_flgs_notify |=
					SII9438_EVENT_FLAGS__EARC_LINK_CHNG;

				s_hpd_out_update(p_obj, b_val);

				p_obj->b_earc_connected_status = b_val;

				s_audio_mute(p_obj, TRUE);
				b_update_audio = TRUE;
			}
			if (int_regs[REG_INTR_2] & BIT_MASK_TX__CLK_CHANGED) {
				sii9438_hal_i2s_clk_status_get(p_obj->dev_id,
						&(p_obj->i2s_clock_status));

				SII_LOG2A("", p_obj, ("i2s clk %s\n",
				p_obj->i2s_clock_status ? "hi" : "lo"));

				s_audio_mute(p_obj, TRUE);
				b_update_audio = TRUE;
			}
			if (int_regs[REG_INTR_5] & BIT_MASK_TX__EARC_HPD) {
				sii9438_hal_earc_hpd_query(p_obj->dev_id,
							&(p_obj->b_earc_hpd));
				SII_LOG2A("", p_obj,
					("eARC HPD Change Detect %s\n",
					p_obj->b_earc_hpd ? "hi" : "lo"));

				evt_flgs_notify |=
					SII9438_EVENT_FLAGS__EARC_HPD_CHNG;
			}

			if (int_regs[REG_INTR_3] &
				BIT_MASK_TX__HEARTBEAT_FAILED) {
				SII_LOG2A("", NULL,
					("TX heartbeat failed\n"));
			}

			if (int_regs[REG_INTR_3] &
				BIT_MASK_TX__HEARTBEAT_SUCCESS) {
				if (p_obj->b_caps_ds_read_pending) {
					SII_LOG2A("", NULL,
					("Reading Capability Data Struct\n"));
					sii9438_hal_hb_intr_mask_en(
							p_obj->dev_id,
							FALSE);
					s_caps_ds_read(p_obj);
					p_obj->b_caps_ds_read_pending = FALSE;
					s_audio_mute(p_obj, TRUE);
					b_update_audio = TRUE;
					evt_flgs_notify |=
					SII9438_EVENT_FLAGS__CAPS_DS_CHNG;
				}

				if (p_obj->b_erx_latency_read_pending) {
					SII_LOG2A("", NULL,
						("Reading ERX Latency\n"));
					sii9438_hal_hb_intr_mask_en(
								p_obj->dev_id,
								FALSE);

					sii9438_hal_erx_latency_query(
						p_obj->dev_id,
						&(p_obj->erx_latency.data));

					p_obj->b_erx_latency_read_pending =
							FALSE;

					evt_flgs_notify |=
					SII9438_EVENT_FLAGS__ERX_LATENCY_CHNG;
				}
			}

			if (int_regs[REG_INTR_4] &
					BIT_MASK_TX__HEARTBEAT_LOST) {
				SII_LOG2A("", p_obj,
					("TX heartbeat lost\n"));
			}

			if (int_regs[REG_INTR_5] &
					BIT_MASK_TX__STAT_CHNG) {
				sii9438_hal_rx_stat_chng_query(p_obj->dev_id,
						&(p_obj->b_hb_stat_chng));
				sii9438_hal_stat_chng_conf_set(p_obj->dev_id,
						p_obj->b_hb_stat_chng);
				/* if CAP_CHNG is 0, read Caps DS after
				 * one heartbeat time*/
				if (!(p_obj->b_hb_stat_chng)) {
					p_obj->b_erx_latency_read_pending =
							TRUE;
					/* Clear and enable heartbeat
					 * interrupt */
					sii9438_hal_hb_intr_clear(
							p_obj->dev_id);

					sii9438_hal_hb_intr_mask_en(
								p_obj->dev_id,
								TRUE);
				}

				SII_LOG2A("", p_obj,
					("eARC HB STAT CHNG to: %s\n",
					p_obj->b_hb_stat_chng ?
							"True" : "False"));
			}

			if (int_regs[REG_INTR_5] &
					BIT_MASK__INTR5_MASK__CAP_CHNG) {
				bool_t b_cap_chng;
				sii9438_hal_rx_cap_chng_query(p_obj->dev_id,
						&b_cap_chng);
				SII_LOG2A("", p_obj,
				("eARC HB CAP CHNG Req from Rx: %s\n",
						b_cap_chng ? "True" : "False"));

				/* Copy CAP_CHNG to CAP_CHNG_CONF */
				sii9438_hal_cap_chng_conf_set(p_obj->dev_id,
						b_cap_chng);

				/* if CAP_CHNG is 0, read Caps DS after
				 * one heartbeat time*/
				if (!b_cap_chng) {
					p_obj->b_caps_ds_read_pending = TRUE;

					/* Clear and enable heartbeat
					 * interrupt */
					sii9438_hal_hb_intr_clear(
								p_obj->dev_id);

					sii9438_hal_hb_intr_mask_en(
								p_obj->dev_id,
								TRUE);
				}
			}

			sii9438_hal_is_spdif_cs_ch_intr_en(p_obj->dev_id,
					&spdif_cs_ch_intr_en);
			if (spdif_cs_ch_intr_en) {
				if (int_regs[REG_INTR_4] &
				BIT_MASK__INTR4_MASK__CH_STATUS_CHANGED) {
					SII_LOG3A("", p_obj,
					("Input SPDIF Ch. Status Changed\n"));
					if (p_obj->spdif_cs_passthru) {
						p_obj->b_ch_status_update =
								TRUE;
						s_audio_mute(p_obj, TRUE);
						b_update_audio = TRUE;
					}
				}
			}
			if (int_regs[REG_INTR_6] &
				BIT_MASK__INT6_MASK__ECC_ERR) {
				SII_LOG1A("", NULL, ("ECC Errors detected\n"));

			}
			if (int_regs[REG_INTR_6] &
				BIT_MASK__INT6_MASK__ECC_ERR_FIXED) {
				SII_LOG1A("", NULL, ("ECC Errors fixed\n"));
			}

			if (int_regs[REG_INTR_3] &
				BIT_MASK_TX__TIMEOUT_NO_COMMA) {
				evt_flgs_notify |=
					SII9438_EVENT_FLAGS__EARC_DISC_TIMEOUT;
				SII_LOG2A("", NULL,
					("TX Discovery Timeout: No Comma\n"));
			}

			sii9438_hal_caps_ds_read_status_get(p_obj->dev_id,
							&int_status);
			if (int_status) {
				if (int_status &
				BIT_MSK__EDID_INTR_MASK__REG_INTR_MASK1) {
					SII_LOG1A("", NULL,
					("Caps DS read error int status\n"));
					sii9438_hal_reset_ddc_no_ack(p_obj->
								dev_id);
				}
				if (int_status &
				BIT_MSK__EDID_INTR_MASK__REG_INTR_MASK0) {
					SII_LOG2A("", NULL,
					("Caps DS read success int stat\n"));
				}
			}

			if (int_regs[REG_INTR_1] &
					BIT_MASK__INTR1_MASK__MUTE_EXCEP_DET) {
				sii9438_hal_tx_hw_mute_out_status_query(
						p_obj->dev_id,
						&(p_obj->mute_in_pin_status));
				SII_LOG2A("", NULL,
					("Input HW MUTE Pin status:%s\n",
						p_obj->mute_in_pin_status ?
							"MUTED" : "UNMUTED"));
				if (p_obj->mute_in_pin_status) {
					/*Disable interrupt Mask*/
					sii9438_hal_mute_excep_det_intr_enable(
							p_obj->dev_id,
							FALSE);
					/* start timer*/
					s_mute_in_poll_timer_start(p_obj);

					SII_LOG3A("", NULL,
					("Mute In Timer Poll started\n"));
				} else {
					sii9438_hal_tx_mute_excep_det_int_clr(
								p_obj->dev_id);
					p_obj->mute_in_pin_status = FALSE;
					sii9438_hal_mute_excep_det_intr_enable(
							p_obj->dev_id,
							TRUE);
				}
			}
		}

		/* Check GPIO status changes */
		/* If any one of the GPIO0 to GPIO3 status registers are not 0,
		 * there would be GPIO change event.
		 * GPIO4 and GPIO5 are reserved for HDP IN and HPD OUT
		 * respectively, so we don't check their status here */
		if ((int_top_regs[REG_INTR_TOP_2] &
			BIT_MASK__TOP_INTR2__REG_INTR_GEN_GPIO)|
			(int_top_regs[REG_INTR_TOP_3] &
			BIT_MASK__TOP_INTR3__REG_INTR_GEN_GPIO))
			evt_flgs_notify |= SII9438_EVENT_FLAGS__GPIO_CHNG;

		if (b_update_audio)
			status = s_audio_update(p_obj);

		/* enable all interrupts */
		sii9438_hal_enable_all_int(p_obj->dev_id, TRUE);
	}

	if (evt_flgs_notify) {
		SII_LOG3A("", p_obj,
			("Event Notification flags: 0x%02x\n",
			evt_flgs_notify));
		s_user_notification_handler(p_obj, evt_flgs_notify);
	}

#ifdef SII_LOG_QUEUE
	if (sii_lib_pipe_get_fill_size(p_obj->log_fifo))
		s_user_notification_handler(p_obj,
			SII9438_EVENT_FLAGS__LOG_FIFO_CHNG);
#endif

	return status;
}

static void s_sm_state_update(struct obj_t *p_obj, uint8_t *int_regs)
{
	uint8_t sm_state;
	if ((int_regs[REG_INTR_0] & BIT_MASK__INTR0_MASK__STATE_IDLE1)	||
		(int_regs[REG_INTR_1] & BIT_MASK__INTR1_MASK__STATE_DISC1) ||
		(int_regs[REG_INTR_1] & BIT_MASK__INTR1_MASK__STATE_IDLE2) ||
		(int_regs[REG_INTR_4] & BIT_MASK__INTR4_MASK__STATE_EARC)  ||
		(int_regs[REG_INTR_4] & BIT_MASK__INTR4_MASK__STATE_ARC)   ||
		(int_regs[REG_INTR_4] & BIT_MASK__INTR4_MASK__STATE_DISC2)) {

		sii9438_hal_sm_state_get(p_obj->dev_id, &sm_state);

		p_obj->sm_state = sm_state;

		SII_LOG3A("", p_obj, ("State machine state changed to %s",
				sm_states[sm_state]));

		s_state_change_sm_update(p_obj);
	}
}

static void s_vbit_update(struct obj_t *p_obj)
{
	sii9438_hal_vbit_set(p_obj->dev_id,
		p_obj->vbit_status,
		((p_obj->aud_mode == SII9438_INSERTION_MODE__SPDIF2) ||
		(p_obj->aud_mode == SII9438_INSERTION_MODE__SPDIF8)) ?
							TRUE : FALSE);
}
static void s_arc_change_sm_update(struct obj_t *p_obj)
{
	if (p_obj->arc_mode_status == SII_ARC_MODE__NONE) {
		/* If the ARC mode is NONE and state is not IDLE2,
		 * force state to IDLE2 and freeze it there */
		sii9438_hal_sm_state_set(p_obj->dev_id,
				SII9438_SM_STATE__IDLE2);
		sii9438_hal_sm_state_override_enable(p_obj->dev_id, TRUE);

		/* If the state is already in IDLE2, update with
		 * ARC_MODE__NONE configuration */
		if (p_obj->sm_state == SII9438_SM_STATE__IDLE2)
			s_arc_mode_update(p_obj, p_obj->arc_mode_status);

	} else if (p_obj->arc_mode_status == SII_ARC_MODE__ARC) {
		/* If the ARC mode is legacy ARC force the state to ARC
		 * freeze it there until ARC mode is changed */
		sii9438_hal_sm_state_set(p_obj->dev_id, SII9438_SM_STATE__ARC);
		sii9438_hal_sm_state_override_enable(p_obj->dev_id, TRUE);

	} else if (p_obj->arc_mode_status == SII_ARC_MODE__EARC) {
		/* If the current state is IDLE2, configure eARC and unfreeze */
		if (p_obj->sm_state == SII9438_SM_STATE__IDLE2) {
			if (p_obj->arc_mode_status == SII_ARC_MODE__EARC &&
				p_obj->arc_mode_configured !=
						SII_ARC_MODE__EARC)
				s_arc_mode_update(p_obj,
					p_obj->arc_mode_status);

			sii9438_hal_sm_state_override_enable(p_obj->dev_id,
					FALSE);
		} else if (p_obj->sm_state == SII9438_SM_STATE__ARC) {
			sii9438_hal_sm_state_override_enable(p_obj->dev_id,
				FALSE);
			/* If the current state is ARC, terminate it */
			sii9438_hal_arc_init_term(p_obj->dev_id, FALSE);
		} else if (p_obj->sm_state == SII9438_SM_STATE__IDLE1) {
			/* If the current state is in IDLE1 and if eARC is
			 * not configured yet, do it now */
			if (p_obj->arc_mode_configured != SII_ARC_MODE__EARC) {
				s_arc_mode_update(p_obj,
					p_obj->arc_mode_status);
			}
		}
	}
}

static void s_state_change_sm_update(struct obj_t *p_obj)
{
	if (p_obj->sm_state == SII9438_SM_STATE__IDLE2) {
		if (p_obj->arc_mode_status == SII_ARC_MODE__ARC)
			sii9438_hal_arc_init_term(p_obj->dev_id, TRUE);
		else if (p_obj->arc_mode_status ==  SII_ARC_MODE__EARC &&
			p_obj->arc_mode_configured != SII_ARC_MODE__EARC) {
			/* Update eARC configuration */
			s_arc_mode_update(p_obj, p_obj->arc_mode_status);
		} else if (p_obj->arc_mode_status == SII_ARC_MODE__NONE) {
			/* Update ARC_NONE configuration */
			s_arc_mode_update(p_obj, p_obj->arc_mode_status);
		}
	} else if (p_obj->sm_state == SII9438_SM_STATE__ARC) {
		/* Update ARC configuration */
		s_arc_mode_update(p_obj, p_obj->arc_mode_status);
	} else if (p_obj->sm_state == SII9438_SM_STATE__IDLE1) {
		/* Set eARC mode in IDLE1 state to cut audio path
		 * in case of S/PDIF or NONE case */
		sii9438_hal_tx_earc_mode_set(p_obj->dev_id, TRUE);

		/* In case state transition happened from ARC to IDLE1 on HPD
		 * low and ARC mode is set to eARC, configure eARC properly */
		if (p_obj->arc_mode_status == SII_ARC_MODE__EARC &&
			p_obj->arc_mode_configured != SII_ARC_MODE__EARC) {
			/* Update eARC configuration */
			s_arc_mode_update(p_obj, p_obj->arc_mode_status);
		}
	}
}

static void s_gpio_config_update(struct obj_t *p_obj)
{
	sii9438_hal_gpio_config(p_obj->dev_id, &p_obj->gpio_pin_io_config);
}

static void s_gpio_output_update(struct obj_t *p_obj,
		uint16_t *p_gpio_pin_mask, bool_t b_set)
{
	sii9438_hal_gpio_output_update(p_obj->dev_id, p_gpio_pin_mask, b_set);
}

static void s_gpio_event_mask_set(struct obj_t *p_obj)
{
	/* Get the event mask for GPIO inputs */
	uint8_t gpio_event_mask = 0x3F & p_obj->gpio_pin_event_mask &
				(~p_obj->gpio_pin_io_config);

	sii9438_hal_gpio_event_mask_set(p_obj->dev_id, &gpio_event_mask);
}

static void s_sii9438_init(struct obj_t *p_obj,
		const struct sii9438_config *p_config)
{
	uint8_t int_top_regs[REG_TOP__INT_NUM];

	sii9438_hal_chip_reset(p_obj->dev_id);
	sii9438_hal_initialize(p_obj->dev_id, p_config);
	s_tx_phy_fifo_reset(p_obj, TRUE);
	/* Disable output clock */
	s_tx_pll_enable(p_obj, FALSE);

	/* Read all top interrupt status registers This function
	automatically clears the interrupts after read
	This read is to clear any spurious interrupts*/
	sii9438_hal_top_int_status_get(p_obj->dev_id, int_top_regs,
							REG_TOP__INT_NUM);
	sii9438_hal_intr_enable(p_obj->dev_id, TRUE);

	sii9438_hal_tx_spdif_ch_status_intr_enable(p_obj->dev_id,
						p_obj->spdif_cs_passthru);
	/* eARC phase invert */
	sii9438_hal_norm_phase_set(p_obj->dev_id, FALSE);
	sii9438_hal_tx_out_clk_en(p_obj->dev_id, FALSE);

	p_obj->mute_state = FALSE;
	s_audio_mute(p_obj, TRUE);
}

static void s_user_notification_handler(struct obj_t *p_obj,
			uint32_t evt_flgs_notify)
{
	/* All unmasked events which are not notified to the callback
	 * function, will be added to event_flags_status. Do OR to prevent
	 * overriding existing flag which is uncleared yet by calling
	 * event_flags_status_clear API */
	p_obj->event_flags_status |= (evt_flgs_notify &
			~p_obj->event_flags_mask);

	/* Notify only events which are enabled in the mask */
	evt_flgs_notify &= p_obj->event_flags_mask;

	if (evt_flgs_notify) {
		if (p_obj->event_cb_func)
			p_obj->event_cb_func(&evt_flgs_notify);
	}
}

static void s_audio_mute(struct obj_t *p_obj, bool_t b_on)
{
	if (p_obj->mute_state != b_on) {
		p_obj->mute_state = b_on;
		/* Mute/unmute audio internally, only if the mute is
		 * not enabled from mute_enable_set() API */
		if (!p_obj->audio_mute_set)
			sii9438_hal_aud_mute_set(p_obj->dev_id, b_on);
	}
}

static bool_t s_is_non_lpcm(struct obj_t *p_obj)
{
	bool_t is_nonlpcm = FALSE;

	switch (p_obj->channel_status.data[0] &
			SII_CHANSTATUS_PATTERN__MASK) {
	case SII_CHANSTATUS_PATTERN__NON_PCM:
		is_nonlpcm = TRUE;
		break;
	default:
		break;
	}

	return is_nonlpcm;
}

static bool_t s_is_2ch_lpcm(struct obj_t *p_obj)
{
	if ((p_obj->channel_status.data[0] &
		SII_CHANSTATUS_PATTERN__MASK) ==
			SII_CHANSTATUS_PATTERN__2CH_LPCM)
		return TRUE;

	return FALSE;
}

static bool_t s_is_multi_channel(struct obj_t *p_obj)
{
	bool_t multi_chn = FALSE;

	switch (p_obj->channel_status.data[0] &
			SII_CHANSTATUS_PATTERN__MASK) {
	case SII_CHANSTATUS_PATTERN__MULTI_LPCM:
		multi_chn = TRUE;
		break;
	default:
		break;
	}

	SII_LOG2A("", NULL, ("is Multi channel: %s\n",
			(multi_chn) ? "TRUE" : "FALSE"));

	return multi_chn;
}

static void s_layout_set(struct obj_t *p_obj)
{
	/* For I2S, 2 ch audio, pattern should be multi-channel */
	switch (p_obj->aud_mode) {
	case SII9438_INSERTION_MODE__I2S2:
	case SII9438_INSERTION_MODE__I2S8:

		if (s_is_2ch_lpcm(p_obj)) {
			p_obj->channel_status.data[0] =
				(p_obj->channel_status.data[0] &
				~SII_CHANSTATUS_PATTERN__MASK) |
					SII_CHANSTATUS_PATTERN__MULTI_LPCM;
		}
		break;

	default:
		break;
	}

	/* Update layout (2ch, 8ch) for LPCM multi-channel */
	if (s_is_multi_channel(p_obj)) {
		if (p_obj->audio_info.speaker_alloc == 0) {
			p_obj->channel_status.data[5] =
				(p_obj->channel_status.data[5] &
				~SII_CHANSTATUS_LAYOUT__MASK) |
					SII_CHANSTATUS_LAYOUT__2CH;
		} else {
			p_obj->channel_status.data[5] =
				(p_obj->channel_status.data[5] &
				~SII_CHANSTATUS_LAYOUT__MASK) |
					SII_CHANSTATUS_LAYOUT__8CH;
		}
	} else if (s_is_non_lpcm(p_obj)) { /* Update layout for Non-LPCM */

		if (s_is_hbra(p_obj)) {
			p_obj->channel_status.data[5] =
				(p_obj->channel_status.data[5] &
				~SII_CHANSTATUS_LAYOUT__MASK) |
					SII_CHANSTATUS_COMPR_LAYOUT__A;

		} else {
			switch (p_obj->aud_mode) {
			/* For SPDIF insertion, use layout A */
			case SII9438_INSERTION_MODE__SPDIF2:
			case SII9438_INSERTION_MODE__SPDIF8:
				p_obj->channel_status.data[5] =
					(p_obj->channel_status.data[5] &
					~SII_CHANSTATUS_LAYOUT__MASK) |
						SII_CHANSTATUS_COMPR_LAYOUT__A;
				break;

			/* For I2S insertion, use layout B */
			case SII9438_INSERTION_MODE__I2S2:
			case SII9438_INSERTION_MODE__I2S8:
				p_obj->channel_status.data[5] =
					(p_obj->channel_status.data[5] &
					~SII_CHANSTATUS_LAYOUT__MASK) |
						SII_CHANSTATUS_COMPR_LAYOUT__B;
				break;

			default:
				p_obj->channel_status.data[5] =
					(p_obj->channel_status.data[5] &
					~SII_CHANSTATUS_LAYOUT__MASK) |
						SII_CHANSTATUS_COMPR_LAYOUT__B;
				break;
			}
		}
	} else {
		p_obj->channel_status.data[5] =
			(p_obj->channel_status.data[5] &
			~SII_CHANSTATUS_LAYOUT__MASK) |
				SII_CHANSTATUS_LAYOUT__2CH;
	}
}

static void s_bsync_count_set(struct obj_t *p_obj,
		enum sii9438_insertion_mode ins_mode)
{
	uint8_t samp_freq = p_obj->channel_status.data[3] & 0x0F;
	uint8_t bsync_cnt = 0x41;

	if (p_obj->chip_version == 1) {
		if (s_is_multi_channel(p_obj)) {
			switch (samp_freq) {
			case SII_FREQ_4x192_KHZ:
				bsync_cnt = 0x43;
				break;
			case SII_FREQ_192_KHZ:
			case SII_FREQ_176_4_KHZ:
				bsync_cnt = 0x41;
				break;
			case SII_FREQ_96_KHZ:
			case SII_FREQ_88_2_KHZ:
				bsync_cnt = 0x21;
				break;
			case SII_FREQ_32_KHZ:
			case SII_FREQ_48_KHZ:
			case SII_FREQ_44_1_KHZ:
				bsync_cnt = 0x11;
				break;
			default:
				break;
			}
		} else {
			switch (samp_freq) {
			case SII_FREQ_4x192_KHZ:
				bsync_cnt = 0x43;
				break;
			case SII_FREQ_192_KHZ:
			case SII_FREQ_176_4_KHZ:
				bsync_cnt = 0x13;
				break;
			case SII_FREQ_96_KHZ:
			case SII_FREQ_88_2_KHZ:
				bsync_cnt = 0x0B;
				break;
			case SII_FREQ_48_KHZ:
			case SII_FREQ_44_1_KHZ:
			case SII_FREQ_32_KHZ:
				bsync_cnt = 0x07;
				break;
			default:
				break;
			}
		}

	} else {

		switch (ins_mode) {
		case SII9438_INSERTION_MODE__SPDIF2:
		case SII9438_INSERTION_MODE__SPDIF8:
			bsync_cnt = 0x07;
			break;
		case SII9438_INSERTION_MODE__I2S2:
		case SII9438_INSERTION_MODE__I2S8:
			bsync_cnt = 0x41;
			break;
		case SII9438_INSERTION_MODE__NONE:
			break;
		default:
			break;
		}
	}

	SII_LOG2A("", NULL, ("Samp freq: %s\n",
			s_getting_freq_name(samp_freq)));

	SII_LOG2A("", NULL, ("bsync_cnt: 0x%x\n", bsync_cnt));

	sii_hal_bsync_count_set(p_obj->dev_id, bsync_cnt);
}

static void s_aud_format_set(struct obj_t *p_obj)
{
	switch (p_obj->aud_mode) {
	case SII9438_INSERTION_MODE__I2S2:
		break;
	case SII9438_INSERTION_MODE__I2S8:
	case SII9438_INSERTION_MODE__SPDIF8:
		if (p_obj->audio_info.speaker_alloc > 0 &&
				(!s_is_non_lpcm(p_obj))) {
			p_obj->channel_status.data[0] &=
				~SII_CHANSTATUS_PATTERN__MASK;
			p_obj->channel_status.data[0] |=
				BIT_MASK__MULTI_CHANNEL_LPCM;
		}
		break;
	case SII9438_INSERTION_MODE__SPDIF2:
	case SII9438_INSERTION_MODE__NONE:
		break;
	}
}

static bool_t s_is_freq(struct obj_t *p_obj, uint8_t freq)
{
	if ((p_obj->channel_status.data[3] & 0x0F) == freq)
		return TRUE;

	return FALSE;
}

static void s_update_freq_zone(struct obj_t *p_obj,
			enum sii9438_insertion_mode ins_mode)
{
	enum sii9438_freq_zone zone = SII_FREQ_ZONE_1X;

	switch (ins_mode) {
	case SII9438_INSERTION_MODE__SPDIF2:
	case SII9438_INSERTION_MODE__SPDIF8:
		if (s_is_non_lpcm(p_obj)) { /* Non-LPCM, Layout A */
			zone = SII_FREQ_ZONE_4X;
			if (s_is_freq(p_obj, SII_FREQ_176_4_KHZ) ||
					s_is_freq(p_obj, SII_FREQ_192_KHZ) ||
					s_is_freq(p_obj, SII_FREQ_4x192_KHZ))
				zone = SII_FREQ_ZONE_1X;
		} else { /* LPCM, Layout A and B */
			if (s_is_multi_channel(p_obj)) {
				zone = SII_FREQ_ZONE_1X;
				if (s_is_freq(p_obj, SII_FREQ_32_KHZ))
					zone = SII_FREQ_ZONE_4X;
			} else {
				zone = SII_FREQ_ZONE_4X;
				if (s_is_freq(p_obj, SII_FREQ_176_4_KHZ) ||
					s_is_freq(p_obj, SII_FREQ_192_KHZ) ||
					s_is_freq(p_obj, SII_FREQ_4x192_KHZ))
					zone = SII_FREQ_ZONE_1X;
			}
		}
		break;
	case SII9438_INSERTION_MODE__I2S2:
	case SII9438_INSERTION_MODE__I2S8:
		if (s_is_multi_channel(p_obj)) {
			zone = SII_FREQ_ZONE_1X;
			if (s_is_freq(p_obj, SII_FREQ_32_KHZ))
				zone = SII_FREQ_ZONE_4X;
		} else {
			zone = SII_FREQ_ZONE_4X;
			if (s_is_freq(p_obj, SII_FREQ_176_4_KHZ) ||
					s_is_freq(p_obj, SII_FREQ_192_KHZ) ||
					s_is_freq(p_obj, SII_FREQ_4x192_KHZ))
				zone = SII_FREQ_ZONE_1X;
		}
		break;
	case SII9438_INSERTION_MODE__NONE:
	default:
		break;
	}

	sii_hal_freq_zone_set(p_obj->dev_id, zone);

}

static uint32_t s_update_channel_status(struct obj_t *p_obj)
{
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	/* If channel status pass through is not enabled, use the
	 * channel status set from API */

	/* Start spdif channel status wait timer */
	s_ch_stat_wait_timer_start(p_obj);

	return status;
}

static void s_ch_stat_wait_timer_start(struct obj_t *p_obj)
{
	sii_sys_seq_timer_start(p_obj->ch_stat_wait_timer,
		SII_CHANNEL_STATUS_WAIT_TIME, 0);
}

static void s_mute_in_poll_timer_start(struct obj_t *p_obj)
{
	sii_sys_seq_timer_start(p_obj->mute_pin_poll_timer,
			SII_MUTE_IN_PIN_POLL_TIME, 0);
}

static void s_audio_mute_timer_start(struct obj_t *p_obj)
{
	sii_sys_seq_timer_start(p_obj->audio_mute_timer,
			SII_MUTE_ASSERT_TIME, 0);
}

static uint32_t s_channel_status_write(struct obj_t *p_obj)
{
	uint32_t status = s_channel_status_input_valid(p_obj);

	if (status) {
		SII_LOG1A("", p_obj, ("INVALID Input Channel Status\n"));
		return status;
	}

	/* Enable output */
	sii9438_hal_tx_out_clk_en(p_obj->dev_id, TRUE);

	s_layout_set(p_obj);
	s_aud_format_set(p_obj);
	s_aud_info_set(p_obj);

	s_update_freq_zone(p_obj, p_obj->aud_mode);

	s_bsync_count_set(p_obj, p_obj->aud_mode);

	s_vbit_update(p_obj);

	sii_hal_channel_status_update(p_obj->dev_id,
			 p_obj->channel_status.data,
			 SII9438_CHANNEL_STATUS_SIZE);

	SII_LOG3A("", NULL, ("Output Channel Status:\n"));
	s_print_channel_status(p_obj, &p_obj->channel_status, TRUE);

	p_obj->b_ch_status_update = FALSE;

	s_tx_phy_fifo_reset(p_obj, FALSE);

	s_audio_wait_timer_start(p_obj);

	return status;
}

static void s_aud_info_set(struct obj_t *p_obj)
{
	uint8_t val;
	uint8_t *p_ch_stat = p_obj->channel_status.data;
	struct sii_audio_info *p_audio_info = &p_obj->audio_info;

	p_ch_stat[7] = p_audio_info->speaker_alloc;

	val = (p_ch_stat[8] & ~BIT_MASK__I2S_CS_EXTRA2__EARC_LFE) |
			p_audio_info->lfepbl;
	val = (val & ~BIT_MASK__I2S_CS_EXTRA2__EARC_LSV) |
		(p_audio_info->lsv << BIT_MASK__I2S_CS_EXTRA2__EARC_LSV_SHIFT);

	val = (val & ~BIT_MASK__I2S_CS_EXTRA2__EARC_DM_INH) |
		(p_audio_info->dm_inh <<
				BIT_MASK__I2S_CS_EXTRA2__EARC_DM_INH_SHIFT);
	/* Add Mute bit in channel status buffer */
	val = (val & ~BIT_MASK__I2S_CS_EXTRA2__EARC_MUTE) |
			(p_obj->mute_state ?
				BIT_MASK__I2S_CS_EXTRA2__EARC_MUTE : 0);

	p_ch_stat[8] = val;

	SII_LOG2A("", NULL,
		("Speaker Alloc: 0x%x\n", p_audio_info->speaker_alloc));
	SII_LOG2A("", NULL,
		("dm_inh: %d, lsv: 0x%x, lfepbl: 0x%x\n",
				p_audio_info->dm_inh,
				p_audio_info->lsv,
				p_audio_info->lfepbl));
}

static void s_audio_wait_timer_start(struct obj_t *p_obj)
{
	sii_sys_seq_timer_start(p_obj->audio_wait_timer,
		SII_RX_AUDIO_WAIT_TIME, 0);
}

static uint32_t s_audio_update(struct obj_t *p_obj)
{
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	if (p_obj->b_standby_mode) {
		SII_LOG1A("", p_obj, ("Standby Mode: Skipping audio update\n"));
		p_obj->b_ch_status_update = FALSE;
		return status;
	}

	if (p_obj->arc_mode_status != SII_ARC_MODE__EARC)
		return status;

	if (sii_sys_seq_timer_running_is(p_obj->audio_mute_timer)) {
		SII_LOG3A("", p_obj,
		("In Audio Update already\n"));
		return status;
	}

	/* Stop the current running timers if a new audio update is to
	 * be processed */
	if (sii_sys_seq_timer_running_is(p_obj->ch_stat_wait_timer))
		sii_sys_seq_timer_stop(p_obj->ch_stat_wait_timer);

	if (sii_sys_seq_timer_running_is(p_obj->audio_wait_timer))
		sii_sys_seq_timer_stop(p_obj->audio_wait_timer);

	s_audio_mute(p_obj, TRUE);

	s_audio_mute_timer_start(p_obj);

	return status;
}

static void s_standby_set(struct obj_t *p_obj, bool_t *p_on)
{
	/* If the new standby mode setting is same as the current mode,
	 * skip processing and return */
	if (p_obj->b_standby_mode == *p_on) {
		SII_LOG2A("", p_obj,
		("No change in standby mode: %s\n",
			p_obj->b_standby_mode ? "ON" : "OFF"));
		return;
	}

	p_obj->b_standby_mode = *p_on;

	if (*p_on) {
		/* Disable interrupt mask */
		s_interrupt_enable(p_obj, FALSE);

		/* Mute output Audio */
		s_audio_mute(p_obj, TRUE);

		/* Disable OEN */
		s_tx_phy_fifo_reset(p_obj, TRUE);
		/* Disable output clock */
		s_tx_pll_enable(p_obj, FALSE);
	} else {
		/* Enable interrupt mask */
		s_interrupt_enable(p_obj, TRUE);

		s_config_update(p_obj);

		p_obj->b_ch_status_update = TRUE;
		s_audio_update(p_obj);
	}

	SII_LOG3A("", NULL, ("Standby mode is %s\n",
		(p_obj->b_standby_mode == TRUE) ? "enabled" : "disabled"));
}

static char *s_getting_freq_name(uint8_t fs)
{
	uint8_t i;
	uint8_t length = 8;
	for (i = 0; i < length; i++) {
		if (fs_table[i].fs == fs)
			return fs_table[i].fsName;
	}
	return "Invalid Freq";
}

static void s_print_channel_status(struct obj_t *p_obj,
		struct sii9438_channel_status *p_ch_status,
		bool_t b_extra_bytes)
{
	if (b_extra_bytes) {
		SII_LOG3A("", p_obj,
			("  %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x\n",
			p_ch_status->data[0], p_ch_status->data[1],
			p_ch_status->data[2], p_ch_status->data[3],
			p_ch_status->data[4], p_ch_status->data[5],
			p_ch_status->data[6], p_ch_status->data[7],
			p_ch_status->data[8]));
	} else {
		SII_LOG3A("", p_obj,
			("  %02x.%02x.%02x.%02x.%02x\n",
			p_ch_status->data[0], p_ch_status->data[1],
			p_ch_status->data[2], p_ch_status->data[3],
			p_ch_status->data[4]));
	}
}

static void s_print_mem_usage(struct obj_t *p_obj, const char *log_str)
{
	SII_LOG3A("", p_obj, ("%s - Memory allocated in bytes: %d\n",
			log_str, sii_sys_malloc_bytes_allocated_get()));
}

static uint32_t s_arc_mode_update(struct obj_t *p_obj,
		enum sii_arc_mode arc_mode_status)
{
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	switch (arc_mode_status) {
	case SII_ARC_MODE__ARC:
		SII_LOG1A("", NULL, ("ARC Mode: Legacy ARC\n"));

		/* Mute audio output */
		s_audio_mute(p_obj, TRUE);

		/* Stop the current running timers if a new audio update is to
		 * be processed */
		if (sii_sys_seq_timer_running_is(
				p_obj->ch_stat_wait_timer))
			sii_sys_seq_timer_stop(p_obj->ch_stat_wait_timer);

		if (sii_sys_seq_timer_running_is(p_obj->audio_wait_timer))
			sii_sys_seq_timer_stop(p_obj->audio_wait_timer);

		s_tx_phy_fifo_reset(p_obj, TRUE);
		/* Disable output clock */
		s_tx_pll_enable(p_obj, FALSE);

		/* Disable the output and interrupts */
		s_interrupt_enable(p_obj, FALSE);

		/* Enable state change interrupt */
		sii9438_hal_sm_states_intr_enable(p_obj->dev_id, TRUE);

		/* Set Normal Phase*/
		sii9438_hal_norm_phase_set(p_obj->dev_id, TRUE);

		/* Change the ARC mode */
		sii9438_hal_arc_mode_set(p_obj->dev_id, arc_mode_status);

		s_hpd_out_update(p_obj, FALSE);

		p_obj->b_earc_connected_status = FALSE;

		/* UnMute audio output */
		s_audio_mute(p_obj, FALSE);

		p_obj->arc_mode_configured = SII_ARC_MODE__ARC;

		break;
	case SII_ARC_MODE__EARC:
		SII_LOG1A("", NULL, ("ARC Mode: eARC\n"));

		/* Mute audio output */
		s_audio_mute(p_obj, TRUE);

		/* Change the ARC mode */
		sii9438_hal_arc_mode_set(p_obj->dev_id, arc_mode_status);

		/* Enable the output and interrupts */
		s_interrupt_enable(p_obj, TRUE);

		/* Set Invert Phase*/
		sii9438_hal_norm_phase_set(p_obj->dev_id, FALSE);

		p_obj->b_ch_status_update = TRUE;
		status = s_audio_update(p_obj);

		p_obj->arc_mode_configured = SII_ARC_MODE__EARC;

		break;
	case SII_ARC_MODE__NONE:
		SII_LOG1A("", NULL, ("ARC Mode: NONE\n"));
		/* Stop the current running timers if a new audio update is to
		 * be processed */
		if (sii_sys_seq_timer_running_is(
				p_obj->ch_stat_wait_timer))
			sii_sys_seq_timer_stop(p_obj->ch_stat_wait_timer);

		if (sii_sys_seq_timer_running_is(p_obj->audio_wait_timer))
			sii_sys_seq_timer_stop(p_obj->audio_wait_timer);

		/* Mute output */
		s_audio_mute(p_obj, TRUE);
		/* Disable output */
		s_tx_phy_fifo_reset(p_obj, TRUE);
		/* Disable output clock */
		s_tx_pll_enable(p_obj, FALSE);

		/* Disable the interrupts */
		s_interrupt_enable(p_obj, FALSE);

		/* Enable state change interrupt */
		sii9438_hal_sm_states_intr_enable(p_obj->dev_id, TRUE);

		/* Change the ARC mode */
		sii9438_hal_arc_mode_set(p_obj->dev_id, arc_mode_status);

		s_hpd_out_update(p_obj, FALSE);

		p_obj->b_earc_connected_status = FALSE;

		p_obj->arc_mode_configured = SII_ARC_MODE__NONE;
		break;
	default:
		break;
	}

	return status;
}

static uint32_t s_interrupt_enable(struct obj_t *p_obj, bool_t enable)
{
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	status = sii9438_hal_intr_enable(p_obj->dev_id, enable);
	status = sii9438_hal_tx_spdif_ch_status_intr_enable(p_obj->dev_id,
			enable && p_obj->spdif_cs_passthru);

	return status;
}

static uint32_t s_channel_status_input_valid(struct obj_t *p_obj)
{
	uint32_t status = SII_RETURN_VALUE__SUCCESS;

	switch (p_obj->channel_status.data[0] &
			SII_CHANSTATUS_PATTERN__MASK) {
	case SII_CHANSTATUS_PATTERN__2CH_LPCM:
	case SII_CHANSTATUS_PATTERN__MULTI_LPCM:
	case SII_CHANSTATUS_PATTERN__NON_PCM:
		p_obj->is_input_channel_staus_valid = TRUE;
		status = SII_RETURN_VALUE__SUCCESS;
		break;
	default:
		p_obj->is_input_channel_staus_valid = FALSE;
		status = SII_RETURN_VALUE__CH_STATUS_ERR;
		break;
	}

	return status;
}

static void s_hpd_out_update(struct obj_t *p_obj, bool_t b_earc_conn_stat)
{
	bool_t b_tx_hpd_out = FALSE; /* HPD-OUT */

	/* If there is no change in eARC connection, there wont
	 * be any change in the hpd out state */
	if (b_earc_conn_stat == p_obj->b_earc_connected_status) {
		SII_LOG3A("", NULL,
			("No change in eARC status\n"));
		SII_LOG3A("", NULL,
			("No update for TX_HPD_OUT - %s\n",
				p_obj->b_hpd_out ? "hi" : "lo"));
		return;
	}

	/* If eARC is enabled */
	if (b_earc_conn_stat) {
		/* TX HPD OUT will be hi */
		b_tx_hpd_out = TRUE;
	} else {
		/* TX HPD OUT will be lo */
		b_tx_hpd_out = FALSE;
	}

	/* Update HPD Out if it is changed */
	if (p_obj->b_hpd_out != b_tx_hpd_out) {
		p_obj->b_hpd_out = b_tx_hpd_out;
		SII_LOG2A("", p_obj, ("TX HPD OUT changed - %s",
				p_obj->b_hpd_out ? "hi" : "lo"));
		sii9438_hal_hpd_out_set(p_obj->dev_id, p_obj->b_hpd_out);
	}
}

static void s_virtual_hdmi_hpd_set(struct obj_t *p_obj, bool_t b_hpd)
{
	p_obj->b_virtual_hdmi_hpd_pending = b_hpd;

	if (!sii_sys_seq_timer_running_is(p_obj->virtual_hdmi_hpd_timer)) {
		if (p_obj->b_virtual_hdmi_hpd == b_hpd)
			return;

		p_obj->b_virtual_hdmi_hpd = b_hpd;
		sii9438_hal_virtual_hdmi_hpd_set(p_obj->dev_id, b_hpd);
		SII_LOG2A("", NULL,
			("Virtual HDMI HPD Set to: %s\n", b_hpd ?
			"hi" : "low"));

		/* When eARC is established, if eARC HPD is set to low,
		 * start timer to maintain min low */
		if (p_obj->b_earc_connected_status &&
			!p_obj->b_virtual_hdmi_hpd)
			s_virtual_hdmi_hpd_timer_start(p_obj);
	}
}

static void s_virtual_hdmi_hpd_timer_start(struct obj_t *p_obj)
{
	/* Virtual HDMI HPD timer should run for
	 * hpd_low_min + T_heartbeat_max */
	sii_sys_seq_timer_start(p_obj->virtual_hdmi_hpd_timer,
		SII_HDMI_HPD_LOW_MIN + SII_HEARTBEAT_TIME_MAX, 0);
}

static bool_t s_hpd_in_get(struct obj_t *p_obj)
{
	uint16_t gpio_in_val;

	sii9438_hal_gpio_query(p_obj->dev_id, &gpio_in_val);
	return gpio_in_val & BIT__GPIO_NUM_4;
}

static void s_tx_phy_fifo_reset(struct obj_t *p_obj, bool_t b_reset)
{
	if (p_obj->b_tx_diff_mode_reset != b_reset) {
		p_obj->b_tx_diff_mode_reset = b_reset;
	SII_LOG2A("", p_obj, ("TX Output Diff Mode: %s",
			p_obj->b_tx_diff_mode_reset ? "DISABLED" : "ENABLED"));

		sii9438_hal_tx_phy_fifo_reset_set(p_obj->dev_id, b_reset);
	}
}

static void s_config_update(struct obj_t *p_obj)
{
	s_gpio_config_update(p_obj);
	s_gpio_event_mask_set(p_obj);
	sii9438_hal_tx_spdif_ch_status_intr_enable(
			p_obj->dev_id, p_obj->spdif_cs_passthru);
	s_virtual_hdmi_hpd_set(p_obj,
		p_obj->b_hpd_in_user && p_obj->b_hpd_in);
	s_vbit_update(p_obj);
}

static bool_t s_is_hbra(struct obj_t *p_obj)
{
	return ((p_obj->channel_status.data[3] & 0x0F) == SII_FREQ_4x192_KHZ) ?
		TRUE : FALSE;
}

static void s_tx_pll_enable(struct obj_t *p_obj, bool_t b_enable)
{
	if (p_obj->b_tx_pll_enable != b_enable) {
		p_obj->b_tx_pll_enable = b_enable;
		SII_LOG2A("", p_obj, ("TX PLL Clock Out: %s",
			p_obj->b_tx_pll_enable ? "ENABLED" : "DISABLED"));

		sii9438_hal_tx_pll_enable(p_obj->dev_id, b_enable);
	}
}

/***** end of file ***********************************************************/
