/*
 * SiI8620 Linux Driver
 *
 * Copyright (C) 2013-2014 Silicon Image, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation version 2.
 * This program is distributed AS-IS WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; INCLUDING without the implied warranty
 * of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE or NON-INFRINGEMENT.
 * See the GNU General Public License for more details at
 * http://www.gnu.org/licenses/gpl-2.0.html.
 */

#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/hrtimer.h>
#include <linux/fs.h>
#include <linux/semaphore.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/stringify.h>
#include <linux/uaccess.h>

#include "si_fw_macros.h"
#include "si_infoframe.h"
#include "si_edid.h"
#include "si_mhl_defs.h"
#include "si_mhl2_edid_3d_api.h"
#include "si_mhl_tx_hw_drv_api.h"
#ifdef MEDIA_DATA_TUNNEL_SUPPORT
#include "si_mdt_inputdev.h"
#endif
#include "mhl_rcp_inputdev.h"
#if (INCLUDE_RBP == 1)
#include "mhl_rbp_inputdev.h"
#endif
#include "mhl_linux_tx.h"
#include "mhl_supp.h"
#include "platform.h"
#include "si_mhl_callback_api.h"
#include "si_8620_drv.h"

#define MHL_DRIVER_MINOR_MAX 1

/* Convert a value specified in milliseconds to nanoseconds */
#define MSEC_TO_NSEC(x)	(x * 1000000UL)

static char *white_space = "' ', '\t'";
static dev_t dev_num;

static struct class *mhl_class;

static void mhl_tx_destroy_timer_support(struct mhl_dev_context *dev_context);

/* Define SysFs attribute names */
#define SYS_ATTR_NAME_CONN			connection_state
#define SYS_ATTR_NAME_DSHPD			ds_hpd
#define SYS_ATTR_NAME_HDCP2			hdcp2_status

#define SYS_ATTR_NAME_SPAD			spad
#define SYS_ATTR_NAME_I2C_REGISTERS		i2c_registers
#define SYS_ATTR_NAME_DEBUG_LEVEL		debug_level
#define SYS_ATTR_NAME_REG_DEBUG_LEVEL		debug_reg_dump
#define SYS_ATTR_NAME_AKSV			aksv
#define SYS_ATTR_NAME_EDID			edid
#define SYS_ATTR_NAME_HEV_3D_DATA		hev_3d_data
#define SYS_ATTR_NAME_GPIO_INDEX		gpio_index
#define SYS_ATTR_NAME_GPIO_VALUE		gpio_value
#define SYS_ATTR_NAME_PP_16BPP			pp_16bpp

#ifdef DEBUG
#define SYS_ATTR_NAME_TX_POWER			tx_power
#define SYS_ATTR_NAME_STARK_CTL			set_stark_ctl
#endif

/* define SysFs object names */
#define		SYS_ATTR_NAME_IN		in
#define		SYS_ATTR_NAME_IN_STATUS		in_status
#define		SYS_ATTR_NAME_OUT		out
#define		SYS_ATTR_NAME_OUT_STATUS	out_status
#define		SYS_ATTR_NAME_INPUT_DEV		input_dev


#define SYS_OBJECT_NAME_BIST			bist
#define		SYS_ATTR_NAME_BIST_ECBUS_DUR	ecbus_duration
#define		SYS_ATTR_NAME_BIST_ECBUS_PAT	ecbus_pattern
#define		SYS_ATTR_NAME_BIST_ECBUS_FPAT	ecbus_fixed_pattern
#define		SYS_ATTR_NAME_BIST_AV_LINK_DR	av_link_data_rate
#define		SYS_ATTR_NAME_BIST_AV_LINK_PAT	av_link_pattern
#define		SYS_ATTR_NAME_BIST_AV_LINK_VM	av_link_video_mode
#define		SYS_ATTR_NAME_BIST_AV_LINK_DUR	av_link_duration
#define		SYS_ATTR_NAME_BIST_AV_LINK_FPAT	av_link_fixed_pattern
#define		SYS_ATTR_NAME_BIST_AV_LINK_RDZR	av_link_randomizer
#define		SYS_ATTR_NAME_BIST_AV_LINK_IMPM	impedance_mode
#define		SYS_ATTR_NAME_BIST_SETUP	setup
#define		SYS_ATTR_NAME_BIST_TRIGGER	trigger
#define		SYS_ATTR_NAME_BIST_STOP		stop
#define		SYS_ATTR_NAME_BIST_STATUS_REQ	status_req
#define		SYS_ATTR_NAME_T_BIST_MODE_DOWN	t_bist_mode_down


#define SYS_OBJECT_NAME_REG_ACCESS		reg_access
#define		SYS_ATTR_NAME_REG_ACCESS_PAGE	page
#define		SYS_ATTR_NAME_REG_ACCESS_OFFSET	offset
#define		SYS_ATTR_NAME_REG_ACCESS_LENGTH	length
#define		SYS_ATTR_NAME_REG_ACCESS_DATA	data

#define SYS_OBJECT_NAME_RAP			rap
#define		SYS_ATTR_NAME_RAP_IN		SYS_ATTR_NAME_IN
#define		SYS_ATTR_NAME_RAP_IN_STATUS	SYS_ATTR_NAME_IN_STATUS
#define		SYS_ATTR_NAME_RAP_OUT		SYS_ATTR_NAME_OUT
#define		SYS_ATTR_NAME_RAP_OUT_STATUS	SYS_ATTR_NAME_OUT_STATUS
#define		SYS_ATTR_NAME_RAP_INPUT_DEV	SYS_ATTR_NAME_INPUT_DEV

#define SYS_OBJECT_NAME_RCP			rcp
#define		SYS_ATTR_NAME_RCP_IN		SYS_ATTR_NAME_IN
#define		SYS_ATTR_NAME_RCP_IN_STATUS	SYS_ATTR_NAME_IN_STATUS
#define		SYS_ATTR_NAME_RCP_OUT		SYS_ATTR_NAME_OUT
#define		SYS_ATTR_NAME_RCP_OUT_STATUS	SYS_ATTR_NAME_OUT_STATUS
#define		SYS_ATTR_NAME_RCP_INPUT_DEV	SYS_ATTR_NAME_INPUT_DEV

#if (INCLUDE_RBP == 1)
#define SYS_OBJECT_NAME_RBP			rbp
#define		SYS_ATTR_NAME_RBP_IN		SYS_ATTR_NAME_IN
#define		SYS_ATTR_NAME_RBP_IN_STATUS	SYS_ATTR_NAME_IN_STATUS
#define		SYS_ATTR_NAME_RBP_OUT		SYS_ATTR_NAME_OUT
#define		SYS_ATTR_NAME_RBP_OUT_STATUS	SYS_ATTR_NAME_OUT_STATUS
#define		SYS_ATTR_NAME_RBP_INPUT_DEV	SYS_ATTR_NAME_INPUT_DEV
#endif

#define SYS_OBJECT_NAME_UCP			ucp
#define		SYS_ATTR_NAME_UCP_IN		SYS_ATTR_NAME_IN
#define		SYS_ATTR_NAME_UCP_IN_STATUS	SYS_ATTR_NAME_IN_STATUS
#define		SYS_ATTR_NAME_UCP_OUT		SYS_ATTR_NAME_OUT
#define		SYS_ATTR_NAME_UCP_OUT_STATUS	SYS_ATTR_NAME_OUT_STATUS
#define		SYS_ATTR_NAME_UCP_INPUT_DEV	SYS_ATTR_NAME_INPUT_DEV

#define SYS_OBJECT_NAME_DEVCAP				devcap
#define		SYS_ATTR_NAME_DEVCAP_LOCAL_OFFSET	local_offset
#define		SYS_ATTR_NAME_DEVCAP_LOCAL		local
#define		SYS_ATTR_NAME_DEVCAP_REMOTE_OFFSET	remote_offset
#define		SYS_ATTR_NAME_DEVCAP_REMOTE		remote

#define SYS_OBJECT_NAME_HDCP				hdcp
#define		SYS_ATTR_NAME_HDCP_CONTENT_TYPE		hdcp_content_type

#define SYS_OBJECT_NAME_VC				vc
#define		SYS_ATTR_NAME_VC_ASSIGN			vc_assign

/*
 * show_connection_state() - Handle read request to the connection_state
 *							attribute file.
 */
ssize_t show_connection_state(struct device *dev, struct device_attribute *attr,
			      char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);

	if (dev_context->mhl_flags & MHL_STATE_FLAG_CONNECTED)
		return scnprintf(buf, PAGE_SIZE, "connected");
	else
		return scnprintf(buf, PAGE_SIZE, "not connected");
}

/*
 * set_connection_state() - Handle write request to the connection_state
 *   attribute file.
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t set_connection_state(struct device *dev, struct device_attribute *attr,
			const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long new_connection_state = 0x100;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (buf != NULL) {
		/* BUGZILLA 27012 no prefix here, only on module parameter. */
		status = kstrtoul(buf, 0, &new_connection_state);
		if ((status != 0) || (new_connection_state > 0xFF)) {
			MHL_TX_DBG_ERR("Invalid connection_state: 0x%02lX\n",
				new_connection_state);
			goto err_exit;
		}
	} else {
		MHL_TX_DBG_ERR("Missing connection_state parameter\n");
		status = -EINVAL;
		goto err_exit;
	}

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto err_exit;
	}
	si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *)
	    &dev_context->drv_context, CM_NO_CONNECTION);
	status = count;

	MHL_TX_DBG_ERR("%sdisconnecting%s\n",
		ANSI_ESC_YELLOW_TEXT,
		ANSI_ESC_RESET_TEXT);

err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * show_ds_hpd_state() - Handle read request to the ds_hpd attribute file.
 */
ssize_t show_ds_hpd_state(struct device *dev, struct device_attribute *attr,
			      char *buf)
{
	int hpd_status = 0;
	int status;

	status = si_8620_get_hpd_status(&hpd_status);
	if (status)
		return status;
	else
		return scnprintf(buf, PAGE_SIZE, "%d", hpd_status);
}

/*
 * show_ds_hdcp2_status() - Handle read request to the ds_hpd attribute file.
 */
ssize_t show_hdcp2_status(struct device *dev, struct device_attribute *attr,
			      char *buf)
{
	uint32_t hdcp2_status = 0;
	int status;

	status = si_8620_get_hdcp2_status(&hdcp2_status);
	if (status)
		return status;
	else
		return scnprintf(buf, PAGE_SIZE, "%x", hdcp2_status);
}

/*
 * Wrapper for kstrtoul() that nul-terminates the input string at
 * the first non-digit character instead of returning an error.
 *
 * This function is destructive to the input string.
 *
 */
static int si_strtoul(char **str, int base, unsigned long *val)
{
	int tok_length, status, nul_offset;
	char *tstr = *str;

	nul_offset = 1;
	status = -EINVAL;
	if ((base == 0) && (tstr[0] == '0') && (tolower(tstr[1]) == 'x')) {
		tstr += 2;
		base = 16;
	}

	tok_length = strspn(tstr, "0123456789ABCDEFabcdef");
	if (tok_length) {
		if ((tstr[tok_length] == '\n') || (tstr[tok_length] == 0))
			nul_offset = 0;

		tstr[tok_length] = 0;
		status = kstrtoul(tstr, base, val);
		if (status == 0) {
			tstr = (tstr + tok_length) + nul_offset;
			*str = tstr;
		}
	}
	return status;
}

/*
 * send_scratch_pad() - Handle write request to the spad attribute file.
 *
 * This file is used to either initiate a write to the scratch pad registers
 * of an attached device, or to set the offset and byte count for a subsequent
 * read from the local scratch pad registers.
 *
 * The format of the string in buf must be:
 *	offset=<offset_value> length=<Length_value> \
 *	data=data_byte_0 ... data_byte_length-1
 * where: <offset_value> specifies the starting register offset to begin
 *			 read/writing within the scratch pad register space
 *	 <length_value> number of scratch pad registers to be written/read
 *	 data_byte	space separated list of <length_value> data bytes to be
 *			written.  If no data bytes are present then the write
 *			to this file will only be used to set the offset and
 *			length for a subsequent read from this file.
 */
ssize_t send_scratch_pad(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	unsigned long offset = 0x100;	/* initialize with invalid values */
	unsigned long length = 0x100;
	unsigned long value;
	u8 data[MAX_SCRATCH_PAD_TRANSFER_SIZE];
	unsigned int idx;
	char *str;
	int status = -EINVAL;
	struct bist_setup_burst *setup_burst;
	uint16_t burst_id;
	char *pinput = kmalloc(count, GFP_KERNEL);

	MHL_TX_DBG_INFO("received string(%d):%s\n", count, buf)

	if (pinput == 0) {

		MHL_TX_DBG_ERR("error exit\n");
		return -EINVAL;
	}
	memcpy(pinput, buf, count);

	/*
	 * Parse the input string and extract the scratch pad register selection
	 * parameters
	 */
	str = strnstr(pinput, "offset=", count);
	if (str != NULL) {
		str += 7;
		status = si_strtoul(&str, 0, &offset);
		if ((status != 0) || (offset > SCRATCH_PAD_SIZE)) {
			MHL_TX_DBG_ERR("Invalid offset value entered\n");
			goto err_exit_2;
		}
	} else {
		MHL_TX_DBG_ERR(
			"Invalid string format, can't find offset value\n");
		goto err_exit_2;
	}

	str = strstr(str, "length=");
	if (str != NULL) {
		str += 7;
		status = si_strtoul(&str, 0, &length);
		if ((status != 0) || (length > MAX_SCRATCH_PAD_TRANSFER_SIZE)) {
			MHL_TX_DBG_ERR("Transfer length too large\n");
			goto err_exit_2;
		}
	} else {
		MHL_TX_DBG_ERR(
			"Invalid string format, can't find length value\n");
		goto err_exit_2;
	}

	str = strstr(str, "data=");
	if (str != NULL) {
		str += 5;
		for (idx = 0; idx < length; idx++) {
			str += strspn(str, white_space);
			if (*str == 0) {
				MHL_TX_DBG_ERR(
					"Too few data values provided\n");
				goto err_exit_2;
			}

			status = si_strtoul(&str, 0, &value);
			if ((status != 0) || (value > 0xFF)) {
				MHL_TX_DBG_ERR(
					"Invalid scratch pad data detected\n");
				goto err_exit_2;
			}
			data[idx] = value;
		}
	} else {
		idx = 0;
	}

	if ((offset + length) > SCRATCH_PAD_SIZE) {
		MHL_TX_DBG_ERR("Invalid offset/length combination entered");
		goto err_exit_2;
	}

	dev_context->spad_offset = offset;
	dev_context->spad_xfer_length = length;
	if (idx == 0) {
		MHL_TX_DBG_INFO("No data specified, storing offset "
				"and length for subsequent scratch pad read\n");

		goto err_exit_2;
	}

	if (down_interruptible(&dev_context->isr_lock)) {
		status = -ERESTARTSYS;
		goto err_exit_2;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto err_exit_1;
	}

	/*
	 * Make sure there is an MHL connection and that the requested
	 * data transfer parameters don't exceed the address space of
	 * the scratch pad.  NOTE: The address space reserved for the
	 * Scratch Pad registers is 64 bytes but sources and sink devices
	 * are only required to implement the 1st 16 bytes.
	 */
	if (!(dev_context->mhl_flags & MHL_STATE_FLAG_CONNECTED) ||
	    (length < ADOPTER_ID_SIZE) ||
	    (offset > (SCRATCH_PAD_SIZE - ADOPTER_ID_SIZE)) ||
	    (offset + length > SCRATCH_PAD_SIZE)) {
		status = -EINVAL;
		goto err_exit_1;
	}

	dev_context->mhl_flags |= MHL_STATE_FLAG_SPAD_SENT;
	dev_context->spad_send_status = 0;

	setup_burst = (struct bist_setup_burst *)data;
	burst_id = setup_burst->burst_id_h << 8;
	burst_id |= setup_burst->burst_id_l;
	{
		enum scratch_pad_status scratch_pad_status;

		scratch_pad_status = si_mhl_tx_request_write_burst(dev_context,
			offset, length, data);
		switch (scratch_pad_status) {
		case SCRATCHPAD_SUCCESS:
			/* Return the number of bytes written to this file */
			status = count;
			break;

		case SCRATCHPAD_BUSY:
			status = -EAGAIN;
			break;

		default:
			status = -EFAULT;
			break;
		}
	}

err_exit_1:
	up(&dev_context->isr_lock);

err_exit_2:
	kfree(pinput);
	MHL_TX_DBG_ERR("status: %d\n", status);
	return status;
}

/*
 * show_scratch_pad() - Handle read request to the spad attribute file.
 *
 * Reads from this file return one or more scratch pad register values
 * in hexadecimal string format.  The registers returned are specified
 * by the offset and length values previously written to this file.
 *
 * The return value is the number characters written to buf, or EAGAIN
 * if the driver is busy and cannot service the read request immediately.
 * If EAGAIN is returned the caller should wait a little and retry the
 * read.
 *
 * The format of the string returned in buf is:
 *	"offset:<offset> length:<lenvalue> data:<datavalues>
 *	where:	<offset> is the last scratch pad register offset
 *			 written to this file
 *		<lenvalue> is the last scratch pad register transfer length
 *			   written to this file
 *		<datavalue> space separated list of <lenvalue> scratch pad
 *			    register values in OxXX format
 */
ssize_t show_scratch_pad(struct device *dev, struct device_attribute *attr,
			 char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	u8 data[MAX_SCRATCH_PAD_TRANSFER_SIZE];
	u8 idx;
	enum scratch_pad_status scratch_pad_status;
	int status = -EINVAL;

	MHL_TX_DBG_INFO("called\n");

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto err_exit;
	}

	if (dev_context->mhl_flags & MHL_STATE_FLAG_CONNECTED) {

		scratch_pad_status = si_get_scratch_pad_vector(dev_context,
			dev_context->spad_offset,
			dev_context->spad_xfer_length, data);

		switch (scratch_pad_status) {
		case SCRATCHPAD_SUCCESS:
			status = scnprintf(buf, PAGE_SIZE, "offset:0x%02x "
					   "length:0x%02x data:",
					   dev_context->spad_offset,
					   dev_context->spad_xfer_length);

			for (idx = 0; idx < dev_context->spad_xfer_length;
			     idx++) {
				status +=
				    scnprintf(&buf[status], PAGE_SIZE,
					      "0x%02x ", data[idx]);
			}
			break;

		case SCRATCHPAD_BUSY:
			status = -EAGAIN;
			break;

		default:
			status = -EFAULT;
			break;
		}
	}

err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * set_reg_access_page() - Handle write request to set the
 *		reg access page value.
 *
 * The format of the string in buf must be:
 *	<pageaddr>
 * Where: <pageaddr> specifies the reg page of the register(s)
 *			to be written/read
 */
ssize_t set_reg_access_page(struct device *dev, struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	unsigned long address = 0x100;
	int status = -EINVAL;
	char my_buf[20];
	unsigned int i;

	MHL_TX_COMM_INFO("received string: %c%s%c\n", '"', buf, '"');
	if (count >= sizeof(my_buf)) {
		MHL_TX_DBG_ERR("string too long %c%s%c\n", '"', buf, '"');
		return status;
	}
	for (i = 0; i < count; ++i) {
		if ('\n' == buf[i]) {
			my_buf[i] = '\0';
			break;
		}
		if ('\t' == buf[i]) {
			my_buf[i] = '\0';
			break;
		}
		if (' ' == buf[i]) {
			my_buf[i] = '\0';
			break;
		}
		my_buf[i] = buf[i];
	}

	status = kstrtoul(my_buf, 0, &address);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
	} else if (address > 0xFF) {
		MHL_TX_DBG_ERR("address:0x%x buf:%s%s%s\n", address,
			ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->debug_i2c_address = address;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

/*
 * show_reg_access_page()	- Show the current page number to be used when
 *	reg_access/data is accessed.
 *
 */
ssize_t show_reg_access_page(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	MHL_TX_COMM_INFO("called\n");

	status = scnprintf(buf, PAGE_SIZE, "0x%02x",
		dev_context->debug_i2c_address);

	return status;
}

/*
 * set_reg_access_offset() - Handle write request to set the
 *		reg access page value.
 *
 * The format of the string in buf must be:
 *	<pageaddr>
 * Where: <pageaddr> specifies the reg page of the register(s)
 *			to be written/read
 */
ssize_t set_reg_access_offset(struct device *dev, struct device_attribute *attr,
	const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	unsigned long offset = 0x100;
	int status = -EINVAL;

	MHL_TX_COMM_INFO("received string: " "%s" "\n", buf);

	status = kstrtoul(buf, 0, &offset);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%c%s%c\n",
				status, '"', buf, '"');
	} else if (offset > 0xFF) {
		MHL_TX_DBG_ERR("offset:0x%x buf:%c%s%c\n",
				offset, '"', buf, '"');
	} else {

		if (down_interruptible(&dev_context->isr_lock))
			return -ERESTARTSYS;
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			status = -ENODEV;
		} else {
			dev_context->debug_i2c_offset = offset;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

/*
 * show_reg_access_offset()	- Show the current page number to be used when
 *	reg_access/data is accessed.
 *
 */
ssize_t show_reg_access_offset(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	MHL_TX_COMM_INFO("called\n");

	status = scnprintf(buf, PAGE_SIZE, "0x%02x",
		dev_context->debug_i2c_offset);

	return status;
}

/*
 * set_reg_access_length() - Handle write request to set the
 *		reg access page value.
 *
 * The format of the string in buf must be:
 *	<pageaddr>
 * Where: <pageaddr> specifies the reg page of the register(s)
 *			to be written/read
 */
ssize_t set_reg_access_length(struct device *dev, struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	unsigned long length = 0x100;
	int status = -EINVAL;

	MHL_TX_COMM_INFO("received string: " "%s" "\n", buf);

	status = kstrtoul(buf, 0, &length);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s'%s'%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%c%s%c\n",
				status, '"', buf, '"');
	} else if (length > 0xFF) {
		MHL_TX_DBG_ERR("length:0x%x buf:%c%s%c\n",
				length, '"', buf, '"');
	} else {

		if (down_interruptible(&dev_context->isr_lock))
			return -ERESTARTSYS;
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			status = -ENODEV;
		} else {
			dev_context->debug_i2c_xfer_length = length;
			status = count;
		}
		up(&dev_context->isr_lock);
	}


	return status;
}

/*
 * show_reg_access_length()	- Show the current page number to be used when
 *	reg_access/data is accessed.
 *
 */
ssize_t show_reg_access_length(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	MHL_TX_COMM_INFO("called\n");

	status = scnprintf(buf, PAGE_SIZE, "0x%02x",
		dev_context->debug_i2c_xfer_length);

	return status;
}

/*
 * set_reg_access_data() - Handle write request to the
 *	reg_access_data attribute file.
 *
 * This file is used to either perform a write to registers of the transmitter
 * or to set the address, offset and byte count for a subsequent from the
 * register(s) of the transmitter.
 *
 * The format of the string in buf must be:
 *	data_byte_0 ... data_byte_length-1
 * Where: data_byte is a space separated list of <length_value> data bytes
 *			to be written.  If no data bytes are present then
 *			the write to this file will only be used to set
 *			the  page address, offset and length for a
 *			subsequent read from this file.
 */
ssize_t set_reg_access_data(struct device *dev, struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	unsigned long value;
	u8 data[MAX_DEBUG_TRANSFER_SIZE];
	int i;
	char *str;
	int status = -EINVAL;
	char *pinput = kmalloc(count, GFP_KERNEL);

	MHL_TX_COMM_INFO("received string: %c%s%c\n", '"', buf, '"');

	if (pinput == 0)
		return -EINVAL;
	memcpy(pinput, buf, count);

	/*
	 * Parse the input string and extract the scratch pad register
	 * selection parameters
	 */
	str = pinput;
	for (i = 0; (i < MAX_DEBUG_TRANSFER_SIZE) && ('\0' != *str); i++) {

		status = si_strtoul(&str, 0, &value);
		if (-ERANGE == status) {
			MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
				ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
			goto exit_reg_access_data;
		} else if (-EINVAL == status) {
			MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
				ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
			goto exit_reg_access_data;
		} else if (status != 0) {
			MHL_TX_DBG_ERR("status:%d %s%s%s\n", status,
				ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
			goto exit_reg_access_data;
		} else if (value > 0xFF) {
			MHL_TX_DBG_ERR("value:0x%x str:%s%s%s\n", value,
				ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
			goto exit_reg_access_data;
		} else {
			data[i] = value;
		}
	}

	if (i == 0) {
		MHL_TX_COMM_INFO("No data specified\n");
		goto exit_reg_access_data;
	}

	if (down_interruptible(&dev_context->isr_lock)) {
		status = -ERESTARTSYS;
		goto exit_reg_access_data;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else {

		status = dev_context->drv_info->mhl_device_dbg_i2c_reg_xfer(
			&dev_context->drv_context,
			 dev_context->debug_i2c_address,
			 dev_context->debug_i2c_offset,
			 i, DEBUG_I2C_WRITE, data);
		if (status == 0)
			status = count;
	}

	up(&dev_context->isr_lock);

exit_reg_access_data:
	kfree(pinput);
	return status;
}

/*
 * show_reg_access_data()	- Handle read request to the
				reg_access_data attribute file.
 *
 * Reads from this file return one or more transmitter register values in
 * hexadecimal string format.  The registers returned are specified by the
 * address, offset and length values previously written to this file.
 *
 * The return value is the number characters written to buf, or an error
 * code if the I2C read fails.
 *
 * The format of the string returned in buf is:
 *	"address:<pageaddr> offset:<offset> length:<lenvalue> data:<datavalues>
 *	where:	<pageaddr>  is the last I2C register page address written
 *				to this file
 *		<offset>    is the last register offset written to this file
 *		<lenvalue>  is the last register transfer length written
 *				to this file
 *		<datavalue> space separated list of <lenvalue> register
 *				values in OxXX format
 */
ssize_t show_reg_access_data(struct device *dev, struct device_attribute *attr,
	char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	u8 data[MAX_DEBUG_TRANSFER_SIZE] = {0};
	u8 idx;
	int status = -EINVAL;

	MHL_TX_COMM_INFO("called\n");

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto no_dev;
	}

	status = dev_context->drv_info->mhl_device_dbg_i2c_reg_xfer(
		&dev_context->drv_context,
		dev_context->debug_i2c_address,
		dev_context->debug_i2c_offset,
		dev_context->debug_i2c_xfer_length,
		DEBUG_I2C_READ,
		data);
no_dev:
	up(&dev_context->isr_lock);

	if (status == 0) {
		status = scnprintf(buf, PAGE_SIZE,
			"0x%02x'0x%02x:",
			dev_context->debug_i2c_address,
			dev_context->debug_i2c_offset
			);

		for (idx = 0; idx < dev_context->debug_i2c_xfer_length;
			idx++) {
			status += scnprintf(&buf[status], PAGE_SIZE, " 0x%02x",
				data[idx]);
		}
	}

	return status;
}

static struct device_attribute reg_access_page_attr =
	__ATTR(SYS_ATTR_NAME_REG_ACCESS_PAGE, 0666,
		show_reg_access_page, set_reg_access_page);

static struct device_attribute reg_access_offset_attr =
	__ATTR(SYS_ATTR_NAME_REG_ACCESS_OFFSET, 0666,
		show_reg_access_offset, set_reg_access_offset);

static struct device_attribute reg_access_length_attr =
	__ATTR(SYS_ATTR_NAME_REG_ACCESS_LENGTH, 0666,
		show_reg_access_length, set_reg_access_length);

static struct device_attribute reg_access_data_attr =
	__ATTR(SYS_ATTR_NAME_REG_ACCESS_DATA, 0666,
		show_reg_access_data, set_reg_access_data);

static struct attribute *reg_access_attrs[] = {
	&reg_access_page_attr.attr,
	&reg_access_offset_attr.attr,
	&reg_access_length_attr.attr,
	&reg_access_data_attr.attr,
	NULL
};

static struct attribute_group reg_access_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_REG_ACCESS),
	.attrs = reg_access_attrs
};
/*
 * show_debug_level() - Handle read request to the debug_level attribute file.
 *
 * The return value is the number characters written to buf, or EAGAIN
 * if the driver is busy and cannot service the read request immediately.
 * If EAGAIN is returned the caller should wait a little and retry the
 * read.
 */
ssize_t get_debug_level(struct device *dev, struct device_attribute *attr,
			char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
		goto err_exit;
	}

	status = scnprintf(buf, PAGE_SIZE, "level=%d %s",
			   debug_level,
			   debug_reg_dump ? "(includes reg dump)" : "");
	MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');

err_exit:
	up(&dev_context->isr_lock);
/* this should be a separate sysfs!!!
	si_dump_important_regs((struct drv_hw_context *)&dev_context->
			       drv_context);
*/

	return status;
}

/*
 * set_debug_level() - Handle write request to the debug_level attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t set_debug_level(struct device *dev, struct device_attribute *attr,
			const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	long new_debug_level = 0x100;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (buf != NULL) {
		/* BUGZILLA 27012 no prefix here, only on module parameter. */
		status = kstrtol(buf, 0, &new_debug_level);
		if (status != 0) {
			MHL_TX_DBG_ERR("Invalid debug_level: 0x%02lX\n",
				new_debug_level);
			goto err_exit;
		}
	} else {
		MHL_TX_DBG_ERR("Missing debug_level parameter\n");
		status = -EINVAL;
		goto err_exit;
	}

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto err_exit;
	}
	debug_level = new_debug_level;
	status = count;

	MHL_TX_GENERIC_DBG_PRINT(debug_level,
		"new debug_level=%d\n", debug_level);

err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * show_debug_reg_dump()
 *
 * Handle read request to the debug_reg_dump attribute file.
 *
 * The return value is the number characters written to buf, or EAGAIN
 * if the driver is busy and cannot service the read request immediately.
 * If EAGAIN is returned the caller should wait a little and retry the
 * read.
 */
ssize_t get_debug_reg_dump(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
		goto err_exit;
	}

	status = scnprintf(buf, PAGE_SIZE, "%s",
			   debug_reg_dump ? "(includes reg dump)" : "");
	MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');

err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * set_debug_reg_dump()
 *
 * Handle write request to the debug_reg_dump attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t set_debug_reg_dump(struct device *dev, struct device_attribute *attr,
			    const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long new_debug_reg_dump = 0x100;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &new_debug_reg_dump);
	if (status != 0)
		goto err_exit2;

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto err_exit;
	}
	debug_reg_dump = (new_debug_reg_dump > 0) ? true : false;
	MHL_TX_DBG_ERR("debug dump %s\n", debug_reg_dump ? "ON" : "OFF");

	status = count;
err_exit:
	up(&dev_context->isr_lock);
err_exit2:
	return status;
}

/*
 * show_gpio_index() - Handle read request to the gpio_index attribute file.
 *
 * The return value is the number characters written to buf, or EAGAIN
 * if the driver is busy and cannot service the read request immediately.
 * If EAGAIN is returned the caller should wait a little and retry the
 * read.
 */
ssize_t get_gpio_index(struct device *dev, struct device_attribute *attr,
		       char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
		goto err_exit;
	}

	status = scnprintf(buf, PAGE_SIZE, "%d", gpio_index);
	MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');

err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * set_gpio_index() - Handle write request to the gpio_index attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t set_gpio_index(struct device *dev, struct device_attribute *attr,
		       const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long new_gpio_index;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto err_exit;
	}
	if (kstrtoul(buf, 0, &new_gpio_index) != 0)
		goto err_exit;

	gpio_index = new_gpio_index;
	MHL_TX_DBG_INFO("gpio: %d\n", gpio_index);

	status = count;
err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * show_gpio_level() - Handle read request to the gpio_level attribute file.
 *
 * The return value is the number characters written to buf, or EAGAIN
 * if the driver is busy and cannot service the read request immediately.
 * If EAGAIN is returned the caller should wait a little and retry the
 * read.
 */
ssize_t get_gpio_level(struct device *dev, struct device_attribute *attr,
		       char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;
	int gpio_level;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
		goto err_exit;
	}
	gpio_level = gpio_get_value(gpio_index);
	status = scnprintf(buf, PAGE_SIZE, "%d", gpio_level);
	MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');

err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * set_gpio_level() - Handle write request to the gpio_level attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t set_gpio_level(struct device *dev, struct device_attribute *attr,
		       const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long gpio_level;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto err_exit;
	}
	if (kstrtoul(buf, 0, &gpio_level) != 0)
		goto err_exit;

	MHL_TX_DBG_INFO("gpio: %d<-%d\n", gpio_index, gpio_level);
	gpio_set_value(gpio_index, gpio_level);
	status = count;

err_exit:
	up(&dev_context->isr_lock);

	return status;
}

/*
 * show_pp_16bpp() - Handle read request to the gpio_level attribute file.
 *
 * The return value is the number characters written to buf, or EAGAIN
 * if the driver is busy and cannot service the read request immediately.
 * If EAGAIN is returned the caller should wait a little and retry the
 * read.
 */
ssize_t show_pp_16bpp(struct device *dev, struct device_attribute *attr,
		       char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;
	int pp_16bpp;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		pp_16bpp = si_mhl_tx_drv_get_pp_16bpp_override(dev_context);
		status = scnprintf(buf, PAGE_SIZE, "%d", pp_16bpp);
		MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');
	}

	up(&dev_context->isr_lock);

	return status;
}

/*
 * set_pp_16bpp() - Handle write request to the gpio_level attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t set_pp_16bpp(struct device *dev, struct device_attribute *attr,
		       const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;
	unsigned long pp_16bpp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &pp_16bpp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n"
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			si_mhl_tx_drv_set_pp_16bpp_override(dev_context,
				pp_16bpp);
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

/*
 * show_aksv()	- Handle read request to the aksv attribute file.
 *
 * Reads from this file return the 5 bytes of the transmitter's
 * Key Selection Vector (KSV). The registers are returned as a string
 * of space separated list of hexadecimal values formated as 0xXX.
 *
 * The return value is the number characters written to buf.
 */
ssize_t show_aksv(struct device *dev, struct device_attribute *attr,
		  char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	u8 data[5] = {0};
	u8 idx;
	int status = -EINVAL;

	MHL_TX_DBG_INFO("called\n");

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
		goto no_dev;
	}

	status = dev_context->drv_info->mhl_device_get_aksv(
		(struct drv_hw_context *)&dev_context->drv_context, data);
no_dev:
	up(&dev_context->isr_lock);

	if (status == 0) {

		for (idx = 0; idx < 5; idx++) {
			status += scnprintf(&buf[status], PAGE_SIZE, "0x%02x ",
					    data[idx]);
		}
	}

	return status;
}

/*
 * show_edid()	- Handle read request to the aksv attribute file.
 *
 * Reads from this file return the edid, as processed by this driver and
 *	presented upon the upstream DDC interface.
 *
 * The return value is the number characters written to buf.
 */
ssize_t show_edid(struct device *dev, struct device_attribute *attr,
		  char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	u8 edid_buffer[256] = {0};
	int status = -EINVAL;

	MHL_TX_DBG_INFO("called\n");

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else {
		status = si_mhl_tx_drv_sample_edid_buffer(
			(struct drv_hw_context *)&dev_context->drv_context,
			edid_buffer);
	}

	up(&dev_context->isr_lock);

	if (status == 0) {
		int idx, i;
		for (idx = 0, i = 0; i < 16; i++) {
			u8 j;
			for (j = 0; j < 16; ++j, ++idx) {
				status += scnprintf(&buf[status],
					PAGE_SIZE, "0x%02x ",
					edid_buffer[idx]);
			}
			status += scnprintf(&buf[status], PAGE_SIZE, "\n");
		}
	}

	return status;
}

/*
 * show_hev_3d()
 *
 * Reads from this file return the HEV_VIC, HEV_DTD,3D_VIC, and
 *	3D_DTD WRITE_BURST information presented in a format showing the
 *	respective associations.
 *
 * The return value is the number characters written to buf.
 */
ssize_t show_hev_3d(struct device *dev, struct device_attribute *attr,
		    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	MHL_TX_DBG_INFO("called\n");

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else {
		struct edid_3d_data_t *p_edid_data =
			dev_context->edid_parser_context;
		int i;
		status = 0;
		status += scnprintf(&buf[status], PAGE_SIZE, "HEV_DTD list:\n");
		for (i = 0; i < (int)p_edid_data->hev_dtd_info.num_items; ++i) {

			status += scnprintf(&buf[status], PAGE_SIZE,
				"%s %s %s 0x%02x 0x%04x 0x%04x 0x%04x 0x%04x "
				"0x%04x 0x%02x -- 0x%04x 0x%02x 0x%02x 0x%02x "
				"0x%02x 0x%02x\n",
				p_edid_data->hev_dtd_list[i]._3d_info.vdi_l.
					top_bottom ? "TB" : "--",
				p_edid_data->hev_dtd_list[i]._3d_info.vdi_l.
					left_right ? "LR" : "--",
				p_edid_data->hev_dtd_list[i]._3d_info.vdi_l.
					frame_sequential ? "FS" : "--",
				p_edid_data->hev_dtd_list[i].sequence_index,
				ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
					a.pixel_clock_in_MHz),
				ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
					a.h_active_in_pixels),
				ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
					a.h_blank_in_pixels),
				ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
					a.h_front_porch_in_pixels),
				ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
					a.h_sync_width_in_pixels),
				p_edid_data->hev_dtd_list[i].a.h_flags,
				ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
					b.v_total_in_lines),
				p_edid_data->hev_dtd_list[i].b.
					v_blank_in_lines,
				p_edid_data->hev_dtd_list[i].b.
					v_front_porch_in_lines,
				p_edid_data->hev_dtd_list[i].b.
					v_sync_width_in_lines,
				p_edid_data->hev_dtd_list[i].b.
					v_refresh_rate_in_fields_per_second,
				p_edid_data->hev_dtd_list[i].b.v_flags);
		}

		status += scnprintf(&buf[status], PAGE_SIZE, "HEV_VIC list:\n");
		for (i = 0; i < (int)p_edid_data->hev_vic_info.num_items; ++i) {
			status += scnprintf(&buf[status], PAGE_SIZE,
			"%s %s %s 0x%02x 0x%02x\n",
			p_edid_data->hev_vic_list[i]._3d_info.vdi_l.
				top_bottom ? "TB" : "--",
			p_edid_data->hev_vic_list[i]._3d_info.vdi_l.
				left_right ? "LR" : "--",
			p_edid_data->hev_vic_list[i]._3d_info.vdi_l.
				frame_sequential ? "FS" : "--",
			p_edid_data->hev_vic_list[i].mhl3_hev_vic_descriptor.
				vic_cea861f,
			p_edid_data->hev_vic_list[i].mhl3_hev_vic_descriptor.
				reserved);
		}

		status += scnprintf(&buf[status], PAGE_SIZE, "3D_DTD list:\n");
		for (i = 0; i < (int)p_edid_data->_3d_dtd_info.num_items; ++i) {
			status += scnprintf(&buf[status], PAGE_SIZE,
				/* pixel clk */
				"%s %s %s " "0x%02x 0x%02x "
				/* horizontal active and blanking */
				"0x%02x 0x%02x {0x%1x 0x%1x} "
				/* vertical active and blanking */
				"0x%02x 0x%02x {0x%1x 0x%1x} "
				/* sync pulse width and offset */
				"0x%02x 0x%02x {0x%1x 0x%1x} "
				"{0x%1x 0x%1x 0x%1x 0x%1x} "
				/* Image sizes */
				"0x%02x 0x%02x {0x%1x 0x%1x} "
				/* borders and flags */
				"0x%1x 0x%1x {0x%1x 0x%1x 0x%1x 0x%1x %s}\n",
				p_edid_data->_3d_dtd_list[i]._3d_info.vdi_l.
					top_bottom ? "TB" : "--",
				p_edid_data->_3d_dtd_list[i]._3d_info.vdi_l.
					left_right ? "LR" : "--",
				p_edid_data->_3d_dtd_list[i]._3d_info.vdi_l.
					frame_sequential ? "FS" : "--",
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					pixel_clock_low,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					pixel_clock_high,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_active_7_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_blanking_7_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_active_blanking_high.
					horz_blanking_11_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_active_blanking_high.
					horz_active_11_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					vert_active_7_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					vert_blanking_7_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					vert_active_blanking_high.
					vert_blanking_11_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					vert_active_blanking_high.
					vert_active_11_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_sync_offset_7_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_sync_pulse_width7_0,
				p_edid_data->_3d_dtd_list[i].
					dtd_cea_861.vert_sync_offset_width.
					vert_sync_pulse_width_3_0,
				p_edid_data->_3d_dtd_list[i].
					dtd_cea_861.vert_sync_offset_width.
					vert_sync_offset_3_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					hs_vs_offset_pulse_width.
					vert_sync_pulse_width_5_4,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					hs_vs_offset_pulse_width.
					vert_sync_offset_5_4,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					hs_vs_offset_pulse_width.
					horz_sync_pulse_width_9_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					hs_vs_offset_pulse_width.
					horz_sync_offset_9_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_image_size_in_mm_7_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					vert_image_size_in_mm_7_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					image_size_high.
					vert_image_size_in_mm_11_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					image_size_high.
					horz_image_size_in_mm_11_8,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					horz_border_in_lines,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.
					vert_border_in_pixels,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
					stereo_bit_0,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
					sync_signal_options,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
					sync_signal_type,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
					stereo_bits_2_1,
				p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
					interlaced ? "interlaced" :
					"progressive");
		}

		status += scnprintf(&buf[status], PAGE_SIZE, "3d_VIC list:\n");
		for (i = 0; i < (int)p_edid_data->_3d_vic_info.num_items; ++i) {
			status +=
			    scnprintf(&buf[status], PAGE_SIZE,
				      "%s %s %s %s 0x%02x\n",
				      p_edid_data->_3d_vic_list[i]._3d_info.
				      vdi_l.top_bottom ? "TB" : "--",
				      p_edid_data->_3d_vic_list[i]._3d_info.
				      vdi_l.left_right ? "LR" : "--",
				      p_edid_data->_3d_vic_list[i]._3d_info.
				      vdi_l.frame_sequential ? "FS" : "--",
				      p_edid_data->_3d_vic_list[i].svd.
				      native ? "N" : " ",
				      p_edid_data->_3d_vic_list[i].svd.VIC);
		}
	}

	up(&dev_context->isr_lock);
	return status;
}

#ifdef DEBUG
/*
 * set_tx_power() - Handle write request to the tx_power attribute file.
 *
 * Write the string "on" or "off" to this file to power on or off the
 * MHL transmitter.
 *
 * The return value is the number of characters in buf if successful or an
 * error code if not successful.
 */
ssize_t set_tx_power(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = 0;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else if (strnicmp("on", buf, count - 1) == 0) {
		status = si_8620_power_control(true);
	} else if (strnicmp("off", buf, count - 1) == 0) {
		status = si_8620_power_control(false);
	} else {
		MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
		status = -EINVAL;
	}

	if (status != 0)
		return status;
	else
		return count;
}

/*
 * set_stark_ctl() - Handle write request to the tx_power attribute file.
 *
 * Write the string "on" or "off" to this file to power on or off the
 * MHL transmitter.
 *
 * The return value is the number of characters in buf if successful or an
 * error code if not successful.
 */
ssize_t set_stark_ctl(struct device *dev, struct device_attribute *attr,
		      const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = 0;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else {
		if (strnicmp("on", buf, count - 1) == 0) {
			/*
			 * Set MHL/USB switch to USB
			 * NOTE: Switch control is implemented differently on
			 * each version of the starter kit.
			 */
			set_pin(X02_USB_SW_CTRL, 1);
		} else if (strnicmp("off", buf, count - 1) == 0) {
			/*
			 * Set MHL/USB switch to USB
			 * NOTE: Switch control is implemented differently on
			 * each version of the starter kit.
			 */
			set_pin(X02_USB_SW_CTRL, 0);
		} else {
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
		}
	}

	up(&dev_context->isr_lock);

	if (status != 0)
		return status;
	else
		return count;
}
#endif

ssize_t show_bist_ecbus_duration(struct device *dev,
			struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
				dev_context->sysfs_bist_setup.e_cbus_duration);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_ecbus_duration(struct device *dev,
			 struct device_attribute *attr,
			 const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.e_cbus_duration = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_ecbus_pattern(struct device *dev,
		 struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%04x\n",
				dev_context->sysfs_bist_setup.e_cbus_pattern);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_ecbus_pattern(struct device *dev,
		struct device_attribute *attr,  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.e_cbus_pattern = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;

}

ssize_t show_bist_ecbus_fixed_pattern(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%04x\n",
				dev_context->sysfs_bist_setup.e_cbus_fixed_pat);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_ecbus_fixed_pattern(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFFFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.e_cbus_fixed_pat = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_av_link_data_rate(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE,
				"avlink_data_rate:   0x%02x\n",
				dev_context->sysfs_bist_setup.avlink_data_rate);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_av_link_data_rate(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.avlink_data_rate = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_av_link_pattern(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
				dev_context->sysfs_bist_setup.avlink_pattern);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_av_link_pattern(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.avlink_pattern = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_av_link_video_mode(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			dev_context->sysfs_bist_setup.avlink_video_mode);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_av_link_video_mode(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.avlink_video_mode = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_av_link_duration(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
				dev_context->sysfs_bist_setup.avlink_duration);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_av_link_duration(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.avlink_duration = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_av_link_fixed_pattern(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%04x\n",
				dev_context->sysfs_bist_setup.avlink_fixed_pat);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_av_link_fixed_pattern(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFFFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.avlink_fixed_pat = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_av_link_randomizer(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			dev_context->sysfs_bist_setup.avlink_randomizer);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_av_link_randomizer(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.avlink_randomizer = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_impedance_mode(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
				dev_context->sysfs_bist_setup.impedance_mode);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_impedance_mode(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.impedance_mode = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t set_bist_setup(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			MHL_TX_DBG_ERR("\n");
			si_mhl_tx_execute_bist(dev_context,
				&dev_context->sysfs_bist_setup);
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t set_bist_trigger(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.bist_trigger_parm =
				(u8)temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t set_bist_stop(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			status = si_mhl_tx_bist_stop(dev_context);
			MHL_TX_DBG_ERR("stop status: %d\n", status);
			if (status == BIST_STATUS_NO_ERROR)
				status = count;
			else
				status = -EINVAL;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_bist_status(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "unimplemented");
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_bist_status_req(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.bist_stat_parm =
				(u8) temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

ssize_t show_t_bist_mode_down(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "%d",
				dev_context->sysfs_bist_setup.t_bist_mode_down);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_t_bist_mode_down(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long temp;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	status = kstrtoul(buf, 0, &temp);
	if (-ERANGE == status) {
		MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (-EINVAL == status) {
		MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (status != 0) {
		MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else if (temp > 0xFF) {
		MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
			ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
	} else {
		if (down_interruptible(&dev_context->isr_lock)) {
			MHL_TX_DBG_ERR("%scould not get mutex%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			return -ERESTARTSYS;
		}
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
			MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
				ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
			status = -ENODEV;
		} else {
			dev_context->sysfs_bist_setup.t_bist_mode_down = temp;
			status = count;
		}
		up(&dev_context->isr_lock);
	}

	return status;
}

static struct device_attribute bist_ecbus_duration_attr =
	__ATTR(SYS_ATTR_NAME_BIST_ECBUS_DUR,
		0666, show_bist_ecbus_duration, set_bist_ecbus_duration);
static struct device_attribute bist_ecbus_pattern_attr =
	__ATTR(SYS_ATTR_NAME_BIST_ECBUS_PAT,
		0666, show_bist_ecbus_pattern, set_bist_ecbus_pattern);
static struct device_attribute bist_ecbus_fixed_pattern_attr =
	__ATTR(SYS_ATTR_NAME_BIST_ECBUS_FPAT, 0666,
		show_bist_ecbus_fixed_pattern, set_bist_ecbus_fixed_pattern);
static struct device_attribute bist_av_link_data_rate_attr =
	__ATTR(SYS_ATTR_NAME_BIST_AV_LINK_DR, 0666,
		show_bist_av_link_data_rate, set_bist_av_link_data_rate);
static struct device_attribute bist_av_link_pattern_attr =
	__ATTR(SYS_ATTR_NAME_BIST_AV_LINK_PAT, 0666,
		show_bist_av_link_pattern, set_bist_av_link_pattern);
static struct device_attribute bist_av_link_video_mode_attr =
	__ATTR(SYS_ATTR_NAME_BIST_AV_LINK_VM, 0666,
		show_bist_av_link_video_mode, set_bist_av_link_video_mode);
static struct device_attribute bist_av_link_duration_attr =
	__ATTR(SYS_ATTR_NAME_BIST_AV_LINK_DUR, 0666,
		show_bist_av_link_duration, set_bist_av_link_duration);
static struct device_attribute bist_av_link_fixed_pattern_attr =
	__ATTR(SYS_ATTR_NAME_BIST_AV_LINK_FPAT, 0666,
		show_bist_av_link_fixed_pattern,
		set_bist_av_link_fixed_pattern);
static struct device_attribute bist_av_link_randomizer_attr =
	__ATTR(SYS_ATTR_NAME_BIST_AV_LINK_RDZR, 0666,
		show_bist_av_link_randomizer, set_bist_av_link_randomizer);
static struct device_attribute bist_impedance_mode_attr =
	__ATTR(SYS_ATTR_NAME_BIST_AV_LINK_IMPM, 0666,
		show_bist_impedance_mode, set_bist_impedance_mode);
static struct device_attribute bist_setup_attr =
	__ATTR(SYS_ATTR_NAME_BIST_SETUP, 0222,
		NULL, set_bist_setup);
static struct device_attribute bist_trigger_attr =
	__ATTR(SYS_ATTR_NAME_BIST_TRIGGER, 0222,
		NULL, set_bist_trigger);
static struct device_attribute bist_stop_attr =
	__ATTR(SYS_ATTR_NAME_BIST_STOP, 0222,
		NULL, set_bist_stop);
static struct device_attribute bist_status_attr =
	__ATTR(SYS_ATTR_NAME_BIST_STATUS_REQ, 0666,
		show_bist_status, set_bist_status_req);

static struct device_attribute t_bist_mode_down_attr =
	__ATTR(SYS_ATTR_NAME_T_BIST_MODE_DOWN, 0666,
	show_t_bist_mode_down, set_t_bist_mode_down);

static struct attribute *bist_attrs[] = {
	&bist_ecbus_duration_attr.attr,
	&bist_ecbus_pattern_attr.attr,
	&bist_ecbus_fixed_pattern_attr.attr,
	&bist_av_link_data_rate_attr.attr,
	&bist_av_link_pattern_attr.attr,
	&bist_av_link_video_mode_attr.attr,
	&bist_av_link_duration_attr.attr,
	&bist_av_link_fixed_pattern_attr.attr,
	&bist_av_link_randomizer_attr.attr,
	&bist_impedance_mode_attr.attr,
	&bist_setup_attr.attr,
	&bist_trigger_attr.attr,
	&bist_stop_attr.attr,
	&bist_status_attr.attr,
	&t_bist_mode_down_attr.attr,
	NULL
};

static struct attribute_group bist_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_BIST),
	.attrs = bist_attrs
};


#if (INCLUDE_SII6031 == 1)
static int mhl_tx_discover_mhl_device(void *mhl_ctx, int id,
	void (*mhl_notify_cb)(void *, int), void *motg)
{
	struct	mhl_dev_context *dev_context;
	int	remaining_time;

	dev_context = (struct mhl_dev_context *)mhl_ctx;
	dev_context->mhl_discovery_in_progress = true;
	init_completion(&dev_context->sem_mhl_discovery_complete);

	remaining_time = wait_for_completion_interruptible_timeout(
		&dev_context->sem_mhl_discovery_complete, 2 * HZ);
	dev_context->mhl_discovery_in_progress = false;
	if (dev_context->mhl_detected) {
		pr_debug("mhl driver detected mhl connection\n");
		return 1;
	} else
		return 0;
}

void mhl_tx_notify_otg(struct mhl_dev_context *dev_context, bool mhl_detected)
{
	dev_context->mhl_detected = mhl_detected;
	if (dev_context->mhl_discovery_in_progress)
		complete(&dev_context->sem_mhl_discovery_complete);
	otg_mhl_notify(dev_context, mhl_detected);
}

static void mhl_tx_register_mhl_discovery(struct mhl_dev_context *dev_context)
{
	if (otg_register_mhl_discovery((void *)dev_context,
		mhl_tx_discover_mhl_device))
		pr_debug("%s: USB callback registration failed\n", __func__);
}
#endif
#define MAX_EVENT_STRING_LEN 128
/*
 * Handler for event notifications from the MhlTx layer.
 *
 */
void mhl_event_notify(struct mhl_dev_context *dev_context, u32 event,
	u32 event_param, void *data)
{
	char event_string[MAX_EVENT_STRING_LEN];
	char *envp[] = { event_string, NULL };
	char *buf;
	u32 length;
	u32 count;
	int idx;

	MHL_TX_DBG_INFO("called, event: 0x%08x event_param: 0x%08x\n",
		event, event_param);

	switch (event) {

	case MHL_TX_EVENT_CONNECTION:
		dev_context->mhl_flags |= MHL_STATE_FLAG_CONNECTED;
		init_rcp_input_dev(dev_context);
		sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
			__stringify(SYS_ATTR_NAME_CONN));

		strncpy(event_string, "MHLEVENT=connected",
			MAX_EVENT_STRING_LEN);
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
			envp);
#if (INCLUDE_SII6031 == 1)
		mhl_tx_notify_otg(dev_context, true);
#endif
		break;

	case MHL_TX_EVENT_DISCONNECTION:
		dev_context->mhl_flags = 0;
#if (INCLUDE_RBP == 1)
		dev_context->rbp_in_button_code = 0;
		dev_context->rbp_out_button_code = 0;
		dev_context->rbp_err_code = 0;
		dev_context->rbp_send_status = 0;
#endif
		dev_context->rcp_in_key_code = 0;
		dev_context->rcp_out_key_code = 0;
		dev_context->rcp_err_code = 0;
		dev_context->rcp_send_status = 0;
		dev_context->ucp_in_key_code = 0;
		dev_context->ucp_out_key_code = 0;
		dev_context->ucp_err_code = 0;
		dev_context->spad_send_status = 0;

#if (INCLUDE_HID == 1)
		mhl3_hid_remove_all(dev_context);
#endif
#ifdef MEDIA_DATA_TUNNEL_SUPPORT
		mdt_destroy(dev_context);
#endif
#if (INCLUDE_SII6031 == 1)
		mhl_tx_notify_otg(dev_context, false);
#endif
		destroy_rcp_input_dev(dev_context);
		sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
			__stringify(SYS_ATTR_NAME_CONN));

		strncpy(event_string, "MHLEVENT=disconnected",
			MAX_EVENT_STRING_LEN);
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
			envp);
		break;

	case MHL_TX_EVENT_RCP_RECEIVED:
		if (input_dev_rcp) {
			int result;

			result = generate_rcp_input_event(dev_context,
				(uint8_t) event_param);
			if (0 == result)
				si_mhl_tx_rcpk_send(dev_context,
					(uint8_t) event_param);
			else
				si_mhl_tx_rcpe_send(dev_context,
					MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE);
		} else {
			dev_context->mhl_flags &= ~MHL_STATE_FLAG_RCP_SENT;
			dev_context->mhl_flags |= MHL_STATE_FLAG_RCP_RECEIVED;
			dev_context->rcp_in_key_code = event_param;

			sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
				__stringify(SYS_ATTR_NAME_RCP));

			snprintf(event_string, MAX_EVENT_STRING_LEN,
				"MHLEVENT=received_RCP key code=0x%02x",
				event_param);
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		}
		break;

	case MHL_TX_EVENT_RCPK_RECEIVED:
		if ((dev_context->mhl_flags & MHL_STATE_FLAG_RCP_SENT)
		    && (dev_context->rcp_out_key_code == event_param)) {

			if (!(dev_context->mhl_flags & MHL_STATE_FLAG_RCP_NAK))
					dev_context->rcp_err_code = 0;
			dev_context->mhl_flags |= MHL_STATE_FLAG_RCP_ACK;
			dev_context->mhl_flags &= ~MHL_STATE_FLAG_RCP_SENT;
			MHL_TX_DBG_INFO(
				"Generating RCPK rcvd event, keycode: 0x%02x\n",
				event_param);

			sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
				__stringify(SYS_ATTR_NAME_RCPK));

			snprintf(event_string, MAX_EVENT_STRING_LEN,
				"MHLEVENT=received_RCPK key code=0x%02x",
				event_param);
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		} else {
			MHL_TX_DBG_ERR(
				"Unexpected RCPK rcvd event, keycode: 0x%02x\n",
				event_param);
		}
		break;

	case MHL_TX_EVENT_RCPE_RECEIVED:
		if (input_dev_rcp) {
			/* do nothing */
		} else {
			if (dev_context->mhl_flags & MHL_STATE_FLAG_RCP_SENT) {

				dev_context->rcp_err_code = event_param;
				dev_context->mhl_flags |=
					MHL_STATE_FLAG_RCP_NAK;

				MHL_TX_DBG_INFO(
					"Generating RCPE received event, "
					"error code: 0x%02x\n", event_param);

				sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
					__stringify(SYS_ATTR_NAME_RCPK));

				snprintf(event_string, MAX_EVENT_STRING_LEN,
					"MHLEVENT=rcvd_RCPE error code=0x%02x",
					event_param);
				kobject_uevent_env(&dev_context->mhl_dev->kobj,
					KOBJ_CHANGE, envp);
			} else {
				MHL_TX_DBG_ERR(
					"Ignoring unexpected RCPE received "
					"event, error code: 0x%02x\n",
					event_param);
			}
		}
		break;

	case MHL_TX_EVENT_UCP_RECEIVED:
		if (input_dev_ucp) {
			if (event_param <= 0xFF)
				si_mhl_tx_ucpk_send(dev_context,
					(uint8_t) event_param);
			else
				si_mhl_tx_ucpe_send(dev_context,
					MHL_UCPE_STATUS_INEFFECTIVE_KEY_CODE);
		} else {
			dev_context->mhl_flags &= ~MHL_STATE_FLAG_UCP_SENT;
			dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_RECEIVED;
			dev_context->ucp_in_key_code = event_param;
			sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
				__stringify(SYS_ATTR_NAME_UCP));

			snprintf(event_string, MAX_EVENT_STRING_LEN,
				"MHLEVENT=received_UCP key code=0x%02x",
				event_param);
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		}
		break;

	case MHL_TX_EVENT_UCPK_RECEIVED:
		if ((dev_context->mhl_flags & MHL_STATE_FLAG_UCP_SENT) &&
			(dev_context->ucp_out_key_code == event_param)) {

			dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_ACK;

			MHL_TX_DBG_INFO("Generating UCPK received event, "
				"keycode: 0x%02x\n", event_param);

			sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
				__stringify(SYS_ATTR_NAME_UCPK));

			snprintf(event_string, MAX_EVENT_STRING_LEN,
				 "MHLEVENT=received_UCPK key code=0x%02x",
				 event_param);
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		} else {
			MHL_TX_DBG_ERR("Ignoring unexpected UCPK received "
				"event, keycode: 0x%02x\n", event_param);
		}
		break;

	case MHL_TX_EVENT_UCPE_RECEIVED:
		if (dev_context->mhl_flags & MHL_STATE_FLAG_UCP_SENT) {

			dev_context->ucp_err_code = event_param;
			dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_NAK;

			MHL_TX_DBG_INFO("Generating UCPE received event, "
				"error code: 0x%02x\n", event_param);

			sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
				__stringify(SYS_ATTR_NAME_UCPK));

			snprintf(event_string, MAX_EVENT_STRING_LEN,
				"MHLEVENT=received_UCPE error code=0x%02x",
				event_param);
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		} else {
			MHL_TX_DBG_ERR("Ignoring unexpected UCPE received "
				"event, error code: 0x%02x\n",
				event_param);
		}
		break;
#if (INCLUDE_RBP == 1)
	case MHL_TX_EVENT_RBP_RECEIVED:
		if (input_dev_rbp) {
			if (((event_param >= 0x01) && (event_param <= 0x07)) ||
			    ((event_param >= 0x20) && (event_param <= 0x21)) ||
			    ((event_param >= 0x30) && (event_param <= 0x35)))
				si_mhl_tx_rbpk_send(dev_context,
					(uint8_t) event_param);
			else
				si_mhl_tx_rbpe_send(dev_context,
					MHL_RBPE_STATUS_INEFFECTIVE_BUTTON_CODE
					);
		} else {
			dev_context->mhl_flags |= MHL_STATE_FLAG_RBP_RECEIVED;
			dev_context->rbp_in_button_code = event_param;

			sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
				__stringify(SYS_ATTR_NAME_RBP));

			snprintf(event_string, MAX_EVENT_STRING_LEN,
				"MHLEVENT=received_RBP button code=0x%02x",
				event_param);
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		}
		break;

	case MHL_TX_EVENT_RBPK_RECEIVED:
		MHL_TX_DBG_INFO("RBPK received\n");
		if ((dev_context->mhl_flags & MHL_STATE_FLAG_RBP_SENT)
		    && (dev_context->rbp_out_button_code == event_param)) {

			dev_context->mhl_flags |= MHL_STATE_FLAG_RBP_ACK;

			MHL_TX_DBG_INFO(
				"Generating RBPK rcvd event, button code: 0x%02x\n",
				event_param);

			sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
				__stringify(SYS_ATTR_NAME_RBPK));

			snprintf(event_string, MAX_EVENT_STRING_LEN,
				"MHLEVENT=received_RBPK button code=0x%02x",
				event_param);
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		} else {
			MHL_TX_DBG_ERR(
				"Unexpected RBPK rcvd event, button code: 0x%02x\n",
				event_param);
		}
		break;

	case MHL_TX_EVENT_RBPE_RECEIVED:
		MHL_TX_DBG_INFO("RBPE received\n");
		if (input_dev_rbp) {
			/* do nothing */
		} else {
			if (dev_context->mhl_flags & MHL_STATE_FLAG_RBP_SENT) {

				dev_context->rbp_err_code = event_param;
				dev_context->mhl_flags |=
					MHL_STATE_FLAG_RBP_NAK;

				MHL_TX_DBG_INFO(
					"Generating RBPE received event, "
					"error code: 0x%02x\n", event_param);

				sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
					__stringify(SYS_ATTR_NAME_RBPK));

				snprintf(event_string, MAX_EVENT_STRING_LEN,
					"MHLEVENT=rcvd_RBPE error code=0x%02x",
					event_param);
				kobject_uevent_env(&dev_context->mhl_dev->kobj,
					KOBJ_CHANGE, envp);
			} else {
				MHL_TX_DBG_ERR(
					"Ignoring unexpected RBPE received "
					"event, error code: 0x%02x\n",
					event_param);
			}
		}
		break;
#endif
	case MHL_TX_EVENT_SPAD_RECEIVED:
		length = event_param;
		buf = data;

		sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
			__stringify(SYS_ATTR_NAME_SPAD));

		idx = snprintf(event_string, MAX_EVENT_STRING_LEN,
			"MHLEVENT=SPAD_CHG length=0x%02x data=", length);

		count = 0;
		while (buf && idx < MAX_EVENT_STRING_LEN) {
			if (count >= length)
				break;

			idx += snprintf(&event_string[idx],
				MAX_EVENT_STRING_LEN - idx, "0x%02x ",
				buf[count]);
			count++;
		}

		if (idx < MAX_EVENT_STRING_LEN) {
			kobject_uevent_env(&dev_context->mhl_dev->kobj,
				KOBJ_CHANGE, envp);
		} else {
			MHL_TX_DBG_ERR(
				"Buffer too small for scratch pad data!\n");
		}
		break;

	case MHL_TX_EVENT_POW_BIT_CHG:
		MHL_TX_DBG_INFO("Generating VBUS power bit change "
				"event, POW bit is %s\n",
				event_param ? "ON" : "OFF");
		snprintf(event_string, MAX_EVENT_STRING_LEN,
			 "MHLEVENT=MHL VBUS power %s",
			 event_param ? "ON" : "OFF");
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
				   envp);
		break;

	case MHL_TX_EVENT_RAP_RECEIVED:
		MHL_TX_DBG_INFO("Generating RAP received event, "
				"action code: 0x%02x\n", event_param);

		sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
			     __stringify(SYS_ATTR_NAME_RAP_IN));

		snprintf(event_string, MAX_EVENT_STRING_LEN,
			 "MHLEVENT=received_RAP action code=0x%02x",
			 event_param);
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
				   envp);
		break;

	case MHL_TX_EVENT_BIST_READY_RECEIVED:
		MHL_TX_DBG_INFO("Received BIST_READY 0x%02X\n", event_param);
		snprintf(event_string, MAX_EVENT_STRING_LEN,
			 "MHLEVENT=received_BIST_READY code=0x%02x",
			 event_param);
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
				   envp);
		break;

	case MHL_TX_EVENT_BIST_TEST_DONE:
		MHL_TX_DBG_INFO("Triggered BIST test completed.\n");
		snprintf(event_string, MAX_EVENT_STRING_LEN,
			 "MHLEVENT=BIST_complete");
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
				   envp);
		break;

	case MHL_TX_EVENT_BIST_STATUS_RECEIVED:
		{
			struct bist_stat_info *stat =
			    (struct bist_stat_info *)data;
			MHL_TX_DBG_INFO("Received BIST_RETURN_STAT\n",
					event_param);
			if (stat != NULL) {
				MHL_TX_DBG_INFO
				    ("eCBUS Rx: 0x%04X,"
					" eCBUS Tx: 0x04X,"
					" AV_LINK: 0x%04X\n",
					stat->e_cbus_local_stat,
					stat->e_cbus_remote_stat,
					stat->avlink_stat);
				snprintf(event_string, MAX_EVENT_STRING_LEN,
					"MHLEVENT=received_BIST_STATUS "\
					"e_cbus_rx=0x%04x"
					"e_cbus_tx=0x%04x"
					" av_link=0x%04x",
					stat->e_cbus_local_stat,
					stat->e_cbus_remote_stat,
					stat->avlink_stat);
				kobject_uevent_env(&dev_context->
					mhl_dev->kobj, KOBJ_CHANGE, envp);
			} else {
				MHL_TX_DBG_INFO("\n");
			}
		}
		break;
	case MHL_TX_EVENT_T_RAP_MAX_EXPIRED:
		MHL_TX_DBG_INFO("T_RAP_MAX expired\n");
		snprintf(event_string, MAX_EVENT_STRING_LEN,
			 "MHLEVENT=T_RAP_MAX_EXPIRED");
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
				   envp);

		break;
	case MHL_EVENT_AUD_DELAY_RCVD:
		MHL_TX_DBG_INFO("Audio Delay burst received\n");
		snprintf(event_string, MAX_EVENT_STRING_LEN,
			 "MHLEVENT=AUD_DELAY_RCVD");
		kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
				   envp);

		break;
	default:
		MHL_TX_DBG_ERR("called with unrecognized event code!\n");
		break;
	}
}

/*
 *  File operations supported by the MHL driver
 */
static const struct file_operations mhl_fops = {
	.owner = THIS_MODULE
};

/*
 * Sysfs attribute files supported by this driver.
 */
struct device_attribute driver_attribs[] = {
	__ATTR(SYS_ATTR_NAME_CONN, 0666, show_connection_state,
		set_connection_state),
	__ATTR(SYS_ATTR_NAME_DSHPD, 0444, show_ds_hpd_state, NULL),
	__ATTR(SYS_ATTR_NAME_HDCP2, 0444, show_hdcp2_status, NULL),
	__ATTR(SYS_ATTR_NAME_SPAD, 0666, show_scratch_pad, send_scratch_pad),
	__ATTR(SYS_ATTR_NAME_DEBUG_LEVEL, 0666, get_debug_level,
	       set_debug_level),
	__ATTR(SYS_ATTR_NAME_REG_DEBUG_LEVEL, 0666, get_debug_reg_dump,
	       set_debug_reg_dump),
	__ATTR(SYS_ATTR_NAME_GPIO_INDEX, 0666, get_gpio_index, set_gpio_index),
	__ATTR(SYS_ATTR_NAME_GPIO_VALUE, 0666, get_gpio_level, set_gpio_level),
	__ATTR(SYS_ATTR_NAME_PP_16BPP  , 0666, show_pp_16bpp, set_pp_16bpp),
	__ATTR(SYS_ATTR_NAME_AKSV, 0444, show_aksv, NULL),
	__ATTR(SYS_ATTR_NAME_EDID, 0444, show_edid, NULL),
	__ATTR(SYS_ATTR_NAME_HEV_3D_DATA, 0444, show_hev_3d, NULL),
#ifdef DEBUG
	__ATTR(SYS_ATTR_NAME_TX_POWER, 0222, NULL, set_tx_power),
	__ATTR(SYS_ATTR_NAME_STARK_CTL, 0222, NULL, set_stark_ctl),
#endif

	__ATTR_NULL
};

ssize_t show_rap_in(struct device *dev, struct device_attribute *attr,
	char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
		return -ERESTARTSYS;
	}
	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			dev_context->rap_in_sub_command);
	}
	up(&dev_context->isr_lock);

	return status;
}

/*
 * set_rap_in_status() - Handle write request to the rap attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t set_rap_in_status(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			param = INT_MAX;
		switch (param) {
		case 0x00:
			dev_context->mhl_flags &=
			    ~MHL_STATE_APPLICATION_RAP_BUSY;
			break;
		case 0x03:
			dev_context->mhl_flags |=
			    MHL_STATE_APPLICATION_RAP_BUSY;
			break;
		default:
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
			break;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rap_out(struct device *dev, struct device_attribute *attr,
		     char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status =
		    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			      dev_context->rap_out_sub_command);
	}

	up(&dev_context->isr_lock);

	return status;
}

/*
 * send_rap_out() - Handle write request to the rap attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t send_rap_out(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			param = INT_MAX;
		switch (param) {
		case MHL_RAP_POLL:
		case MHL_RAP_CONTENT_ON:
		case MHL_RAP_CONTENT_OFF:
		case MHL_RAP_CBUS_MODE_DOWN:
		case MHL_RAP_CBUS_MODE_UP:
			if (!si_mhl_tx_rap_send(dev_context, param)) {
				MHL_TX_DBG_ERR("-EPERM\n");
				status = -EPERM;
			} else {
				dev_context->rap_out_sub_command = (u8) param;
			}
			break;
		default:
			dev_context->rap_out_status = MHL_RAPK_UNRECOGNIZED;
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
			break;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rap_out_status(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		/* todo: check for un-ack'ed RAP sub command
		 * and block until ack received.
		 */
		status =
		    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			      dev_context->rap_out_status);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rap_input_dev(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_rap);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_rap_input_dev(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			param = INT_MAX;
		switch (param) {
		case 0:
		case 1:
			input_dev_rap = (bool)param;
			break;
		default:
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
			break;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

static struct device_attribute rap_in_attr =
	__ATTR(SYS_ATTR_NAME_RAP_IN, 0444, show_rap_in, NULL);
static struct device_attribute rap_in_status_attr =
	__ATTR(SYS_ATTR_NAME_RAP_IN_STATUS, 0222, NULL, set_rap_in_status);
static struct device_attribute rap_out_attr =
	__ATTR(SYS_ATTR_NAME_RAP_OUT, 0666, show_rap_out, send_rap_out);
static struct device_attribute rap_out_status_attr =
	__ATTR(SYS_ATTR_NAME_RAP_OUT_STATUS, 0444, show_rap_out_status, NULL);
static struct device_attribute rap_input_dev =
	__ATTR(SYS_ATTR_NAME_RAP_INPUT_DEV, 0666, show_rap_input_dev,
	set_rap_input_dev);

static struct attribute *rap_attrs[] = {
	&rap_in_attr.attr,
	&rap_in_status_attr.attr,
	&rap_out_attr.attr,
	&rap_out_status_attr.attr,
	&rap_input_dev.attr,
	NULL
};

static struct attribute_group rap_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_RAP),
	.attrs = rap_attrs
};

ssize_t show_rcp_in(struct device *dev, struct device_attribute *attr,
		    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else if (input_dev_rcp) {
		MHL_TX_DBG_INFO("\n");
		status = scnprintf(buf, PAGE_SIZE, "rcp_input_dev\n");
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
				   dev_context->rcp_in_key_code);
	}

	up(&dev_context->isr_lock);

	return status;
}

/*
 * send_rcp_in_status() - Handle write request to the rcp attribute file.
 *
 * Writes to this file cause a RCP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t send_rcp_in_status(struct device *dev, struct device_attribute *attr,
			   const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else {
		unsigned long err_code;
		if (kstrtoul(buf, 0, &err_code) != 0)
			status = -EINVAL;
		else {
			status = count;
			if (err_code == 0) {
				if (!si_mhl_tx_rcpk_send(
				    dev_context,
				    dev_context->rcp_in_key_code)) {
					status = -ENOMEM;
				}
			} else if (!si_mhl_tx_rcpe_send(dev_context,
				(u8)err_code)) {
				status = -EINVAL;
			}
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rcp_out(struct device *dev, struct device_attribute *attr,
		     char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02X\n",
				   dev_context->rcp_out_key_code);
	}

	up(&dev_context->isr_lock);

	return status;
}

/*
 * send_rcp_out() - Handle write request to the rcp attribute file.
 *
 * Writes to this file cause a RCP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t send_rcp_out(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			status = -EINVAL;
		else {
			dev_context->mhl_flags &=
					~(MHL_STATE_FLAG_RCP_RECEIVED |
					MHL_STATE_FLAG_RCP_ACK |
					MHL_STATE_FLAG_RCP_NAK);
			dev_context->mhl_flags |= MHL_STATE_FLAG_RCP_SENT;
			dev_context->rcp_send_status = 0;
			dev_context->rcp_err_code = 0; /*reset error code*/
			if (!si_mhl_tx_rcp_send(dev_context, (u8) param)) {
				MHL_TX_DBG_ERR("-EPERM\n");
				status = -EPERM;
			} else {
				dev_context->rcp_out_key_code = param;
			}
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rcp_out_status(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		/* todo: check for un-ack'ed RCP sub command
		 * and block until ack received.
		 */
		if (dev_context->
		    mhl_flags & (MHL_STATE_FLAG_RCP_ACK |
				 MHL_STATE_FLAG_RCP_NAK)) {
			status =
			    scnprintf(buf, PAGE_SIZE, "0x%02X\n",
				      dev_context->rcp_err_code);
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rcp_input_dev(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_rcp);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_rcp_input_dev(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			param = INT_MAX;
		switch (param) {
		case 0:
		case 1:
			input_dev_rcp = (bool)param;
			break;
		default:
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
			break;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

static struct device_attribute rcp_in_attr =
	__ATTR(SYS_ATTR_NAME_RCP_IN, 0444, show_rcp_in, NULL);
static struct device_attribute rcp_in_status_attr =
	__ATTR(SYS_ATTR_NAME_RCP_IN_STATUS, 0222, NULL, send_rcp_in_status);
static struct device_attribute rcp_out_attr =
	__ATTR(SYS_ATTR_NAME_RCP_OUT, 0666, show_rcp_out, send_rcp_out);
static struct device_attribute rcp_out_status_attr =
	__ATTR(SYS_ATTR_NAME_RCP_OUT_STATUS, 0444, show_rcp_out_status, NULL);
static struct device_attribute rcp_input_dev =
	__ATTR(SYS_ATTR_NAME_RCP_INPUT_DEV, 0666, show_rcp_input_dev,
	set_rcp_input_dev);

static struct attribute *rcp_attrs[] = {
	&rcp_in_attr.attr,
	&rcp_in_status_attr.attr,
	&rcp_out_attr.attr,
	&rcp_out_status_attr.attr,
	&rcp_input_dev.attr,
	NULL
};

static struct attribute_group rcp_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_RCP),
	.attrs = rcp_attrs
};
#if (INCLUDE_RBP == 1)
ssize_t show_rbp_in(struct device *dev, struct device_attribute *attr,
		    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status =
		    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			      dev_context->rbp_in_button_code);
	}
	up(&dev_context->isr_lock);

	return status;
}

/*
 * send_rbp_in_status() - Handle write request to the rbp attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t send_rbp_in_status(struct device *dev, struct device_attribute *attr,
			   const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else {
		unsigned long err_code;
		status = kstrtoul(buf, 0, &err_code);
		if (status == 0) {
			status = count;
			if (err_code == 0) {
				if (!si_mhl_tx_rbpk_send(
					dev_context,
					dev_context->rbp_in_button_code)) {
					status = -ENOMEM;
				}
			} else if (!si_mhl_tx_rbpe_send(
					dev_context, (u8)err_code)) {
				status = -EINVAL;
			}
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

/*
 * send_rbp_out() - Handle write request to the rbp attribute file.
 *
 * Writes to this file cause a RBP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t send_rbp_out(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			status = -EINVAL;
		else {
			dev_context->mhl_flags &=
					~(MHL_STATE_FLAG_RBP_RECEIVED |
					MHL_STATE_FLAG_RBP_ACK |
					MHL_STATE_FLAG_RBP_NAK);
			dev_context->mhl_flags |= MHL_STATE_FLAG_RBP_SENT;
			dev_context->rbp_send_status = 0;
			if (!si_mhl_tx_rbp_send(dev_context, (u8) param)) {
				MHL_TX_DBG_ERR("-EPERM\n");
				status = -EPERM;
			} else {
				dev_context->rbp_out_button_code = param;
			}
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rbp_out(struct device *dev, struct device_attribute *attr,
		     char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02X\n",
				   dev_context->rbp_out_button_code);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_rbp_out_status(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		/* todo: check for un-ack'ed RBP sub command
		 * and block until ack received.
		 */
		if (dev_context->
		    mhl_flags & (MHL_STATE_FLAG_RBP_ACK |
				 MHL_STATE_FLAG_RBP_NAK)) {
			status =
			    scnprintf(buf, PAGE_SIZE, "0x%02X\n",
				      dev_context->rbp_err_code);
		}
	}

	up(&dev_context->isr_lock);

	return status;
}


ssize_t show_rbp_input_dev(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_rbp);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_rbp_input_dev(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			param = INT_MAX;
		switch (param) {
		case 0:
		case 1:
			input_dev_rbp = (bool)param;
			break;
		default:
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
			break;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

static struct device_attribute rbp_in_attr =
	__ATTR(SYS_ATTR_NAME_RBP_IN, 0444, show_rbp_in, NULL);
static struct device_attribute rbp_in_status_attr =
	__ATTR(SYS_ATTR_NAME_RBP_IN_STATUS, 0222, NULL, send_rbp_in_status);
static struct device_attribute rbp_out_attr =
	__ATTR(SYS_ATTR_NAME_RBP_OUT, 0666, show_rbp_out, send_rbp_out);
static struct device_attribute rbp_out_status_attr =
	__ATTR(SYS_ATTR_NAME_RBP_OUT_STATUS, 0444, show_rbp_out_status, NULL);
static struct device_attribute rbp_input_dev =
	__ATTR(SYS_ATTR_NAME_RBP_INPUT_DEV, 0666, show_rbp_input_dev,
	set_rbp_input_dev);

static struct attribute *rbp_attrs[] = {
	&rbp_in_attr.attr,
	&rbp_in_status_attr.attr,
	&rbp_out_attr.attr,
	&rbp_out_status_attr.attr,
	&rbp_input_dev.attr,
	NULL
};

static struct attribute_group rbp_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_RBP),
	.attrs = rbp_attrs
};
#endif
ssize_t show_ucp_in(struct device *dev, struct device_attribute *attr,
		    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
		return -ERESTARTSYS;
	}
	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status =
		    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			      dev_context->ucp_in_key_code);
	}
	up(&dev_context->isr_lock);

	return status;
}

/*
 * send_ucp_in_status() - Handle write request to the ucp attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t send_ucp_in_status(struct device *dev, struct device_attribute *attr,
			   const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock))
		return -ERESTARTSYS;

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		status = -ENODEV;
	} else if (input_dev_ucp) {
		MHL_TX_DBG_ERR("channel busy\n");
	} else {
		unsigned long err_code;
		status = kstrtoul(buf, 0, &err_code);
		if (status == 0) {
			status = count;
			if (err_code == 0) {
				if (!si_mhl_tx_ucpk_send(
					dev_context,
					dev_context->ucp_in_key_code)) {
					status = -ENOMEM;
				}
			} else if (!si_mhl_tx_ucpe_send(
					dev_context, (u8) err_code)) {
				status = -EINVAL;
			}
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_ucp_out(struct device *dev, struct device_attribute *attr,
		     char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status =
		    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			      dev_context->ucp_out_key_code);
	}

	up(&dev_context->isr_lock);

	return status;
}

/*
 * send_ucp_out() - Handle write request to the ucp attribute file.
 *
 * Writes to this file cause a RAP message with the specified action code
 * to be sent to the downstream device.
 */
ssize_t send_ucp_out(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		status = kstrtoul(buf, 0, &param);
		if (status == 0) {
			dev_context->mhl_flags &=
					~(MHL_STATE_FLAG_UCP_RECEIVED |
					MHL_STATE_FLAG_UCP_ACK |
					MHL_STATE_FLAG_UCP_NAK);
			dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_SENT;
			if (!si_mhl_tx_ucp_send(dev_context, (u8)param)) {
				MHL_TX_DBG_ERR("-EPERM\n");
				status = -EPERM;
			} else {
				dev_context->ucp_out_key_code = (u8)param;
				status = count;
			}
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_ucp_out_status(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		/* todo: check for un-ack'ed RAP sub command
		 * and block until ack received.
		 */
		if (dev_context->
		    mhl_flags & (MHL_STATE_FLAG_UCP_ACK |
				 MHL_STATE_FLAG_UCP_NAK)) {
			status =
			    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
				      dev_context->ucp_err_code);
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_ucp_input_dev(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_ucp);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_ucp_input_dev(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	/* Assume success */
	status = count;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		if (kstrtoul(buf, 0, &param) != 0)
			param = INT_MAX;
		switch (param) {
		case 0:
		case 1:
			input_dev_ucp = (bool)param;
			break;
		default:
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
			break;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

static struct device_attribute ucp_in_attr =
	__ATTR(SYS_ATTR_NAME_UCP_IN, 0444, show_ucp_in, NULL);
static struct device_attribute ucp_in_status_attr =
	__ATTR(SYS_ATTR_NAME_UCP_IN_STATUS, 0222, NULL, send_ucp_in_status);
static struct device_attribute ucp_out_attr =
	__ATTR(SYS_ATTR_NAME_UCP_OUT, 0666, show_ucp_out, send_ucp_out);
static struct device_attribute ucp_out_status_attr =
	__ATTR(SYS_ATTR_NAME_UCP_OUT_STATUS, 0444, show_ucp_out_status, NULL);

static struct device_attribute ucp_input_dev =
	__ATTR(SYS_ATTR_NAME_UCP_INPUT_DEV, 0666, show_ucp_input_dev,
	set_ucp_input_dev);

static struct attribute *ucp_attrs[] = {
	&ucp_in_attr.attr,
	&ucp_in_status_attr.attr,
	&ucp_out_attr.attr,
	&ucp_out_status_attr.attr,
	&ucp_input_dev.attr,
	NULL
};

static struct attribute_group ucp_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_UCP),
	.attrs = ucp_attrs
};

ssize_t show_devcap_local_offset(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status =
		    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			      dev_context->dev_cap_local_offset);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_devcap_local_offset(struct device *dev,
				struct device_attribute *attr, const char *buf,
				size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		status = kstrtoul(buf, 0, &param);
		if ((status != 0) || (param >= 16)) {
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
		} else {
			dev_context->dev_cap_local_offset = param;
			status = count;
		}
	}

	up(&dev_context->isr_lock);
	return status;
}

ssize_t show_devcap_local(struct device *dev, struct device_attribute *attr,
			  char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			dev_cap_values[dev_context->dev_cap_local_offset]);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_devcap_local(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		status = kstrtoul(buf, 0, &param);
		if ((status != 0) || (param > 0xFF)) {
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
		} else {
			dev_cap_values[dev_context->dev_cap_local_offset] =
			    param;
			status = count;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_devcap_remote_offset(struct device *dev,
				  struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status =
		    scnprintf(buf, PAGE_SIZE, "0x%02x\n",
			      dev_context->dev_cap_remote_offset);
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t set_devcap_remote_offset(struct device *dev,
				 struct device_attribute *attr,
				 const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		status = kstrtoul(buf, 0, &param);
		if ((status == 0) && (param < 16)) {
			dev_context->dev_cap_remote_offset = (u8)param;
			status = count;
		} else {
			MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
			status = -EINVAL;
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_devcap_remote(struct device *dev, struct device_attribute *attr,
			   char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		uint8_t regValue;
		status = si_mhl_tx_get_peer_dev_cap_entry(dev_context,
							  dev_context->
							  dev_cap_remote_offset,
							  &regValue);
		if (status != 0) {
			/*
			 * Driver is busy and cannot provide the requested
			 * DEVCAP register value right now so inform caller
			 * they need to try again later.
			 */
			status = -EAGAIN;
		} else {
			status = scnprintf(buf, PAGE_SIZE, "0x%02x", regValue);
		}
	}

	up(&dev_context->isr_lock);

	return status;
}

static struct device_attribute attr_devcap_local_offset =
	__ATTR(SYS_ATTR_NAME_DEVCAP_LOCAL_OFFSET, 0666,
	show_devcap_local_offset, set_devcap_local_offset);
static struct device_attribute attr_devcap_local =
	__ATTR(SYS_ATTR_NAME_DEVCAP_LOCAL, 0666, show_devcap_local,
	set_devcap_local);
static struct device_attribute attr_devcap_remote_offset =
	__ATTR(SYS_ATTR_NAME_DEVCAP_REMOTE_OFFSET, 0666,
	show_devcap_remote_offset, set_devcap_remote_offset);
static struct device_attribute attr_devcap_remote =
	__ATTR(SYS_ATTR_NAME_DEVCAP_REMOTE, 0444, show_devcap_remote, NULL);

static struct attribute *devcap_attrs[] = {
	&attr_devcap_local_offset.attr,
	&attr_devcap_local.attr,
	&attr_devcap_remote_offset.attr,
	&attr_devcap_remote.attr,
	NULL
};

static struct attribute_group devcap_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_DEVCAP),
	.attrs = devcap_attrs
};

ssize_t set_hdcp_force_content_type(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	unsigned long new_hdcp_content_type;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = kstrtoul(buf, 0, &new_hdcp_content_type);
		if (status == 0) {
			status = count;
			hdcp_content_type = new_hdcp_content_type;
		}
	}

	up(&dev_context->isr_lock);
	return status;
}

ssize_t show_hdcp_force_content_type(struct device *dev,
				     struct device_attribute *attr, char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "%d", hdcp_content_type);
	}

	up(&dev_context->isr_lock);

	return status;
}

static struct device_attribute attr_hdcp_force_content_type =
	__ATTR(SYS_ATTR_NAME_HDCP_CONTENT_TYPE, 0666,
	show_hdcp_force_content_type, set_hdcp_force_content_type);

static struct attribute *hdcp_attrs[] = {
	&attr_hdcp_force_content_type.attr,
	NULL
};

static struct attribute_group hdcp_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_HDCP),
	.attrs = hdcp_attrs
};

ssize_t set_vc_assign(struct device *dev, struct device_attribute *attr,
		      const char *buf, size_t count)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status;
	struct tdm_alloc_burst tdm_burst;
	uint8_t slots;

	MHL_TX_DBG_INFO("received string: %s\n", buf);

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		unsigned long param;
		enum cbus_mode_e cbus_mode;
		status = kstrtoul(buf, 0, &param);
		if (status != 0)
			goto input_err;
		status = count;

		cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context);
		switch (cbus_mode) {
		case CM_eCBUS_S:
			if (param > 24)
				goto input_err;
			break;
		case CM_eCBUS_D:
			if (param > 200)
				goto input_err;
			break;
		default:
input_err:
			MHL_TX_DBG_ERR(
				"Invalid number of eMSC slots specified\n");
			status = -EINVAL;
			goto done;
		}

		slots = (u8)param;

		memset(&tdm_burst, 0, sizeof(tdm_burst));
		tdm_burst.header.burst_id.high = burst_id_VC_ASSIGN >> 8;
		tdm_burst.header.burst_id.low = (uint8_t) burst_id_VC_ASSIGN;
		tdm_burst.header.sequence_index = 1;
		tdm_burst.header.total_entries = 2;
		tdm_burst.num_entries_this_burst = 2;
		tdm_burst.vc_info[0].vc_num = TDM_VC_E_MSC;
		tdm_burst.vc_info[0].feature_id = FEATURE_ID_E_MSC;
		tdm_burst.vc_info[0].req_resp.channel_size = slots;
		tdm_burst.vc_info[1].vc_num = TDM_VC_T_CBUS;
		tdm_burst.vc_info[1].feature_id = FEATURE_ID_USB;
		tdm_burst.vc_info[1].req_resp.channel_size = (u8) (24 - slots);
		tdm_burst.vc_info[2].vc_num = 0;
		tdm_burst.vc_info[2].feature_id = 0;
		tdm_burst.vc_info[2].req_resp.channel_size = 0;
		tdm_burst.header.checksum = 0;
		tdm_burst.header.checksum =
		    calculate_generic_checksum((uint8_t *) (&tdm_burst), 0,
					       sizeof(tdm_burst));

		dev_context->prev_virt_chan_slot_counts[TDM_VC_E_MSC] =
		    dev_context->virt_chan_slot_counts[TDM_VC_E_MSC];
		dev_context->prev_virt_chan_slot_counts[TDM_VC_T_CBUS] =
		    dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS];

		dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = slots;
		dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] =
		    (u8) (24 - slots);

		si_mhl_tx_request_write_burst(dev_context, 0, sizeof(tdm_burst),
					      (uint8_t *) &tdm_burst);
		status = count;
	}
done:
	up(&dev_context->isr_lock);

	return status;
}

ssize_t show_vc_assign(struct device *dev, struct device_attribute *attr,
		       char *buf)
{
	struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
	int status = -EINVAL;

	if (down_interruptible(&dev_context->isr_lock)) {
		MHL_TX_DBG_ERR("-ERESTARTSYS\n");
		return -ERESTARTSYS;
	}

	if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
		MHL_TX_DBG_ERR("-ENODEV\n");
		status = -ENODEV;
	} else {
		status = scnprintf(buf, PAGE_SIZE, "%d", 0);
	}

	up(&dev_context->isr_lock);

	return status;
}

static struct device_attribute attr_vc_assign =
	__ATTR(SYS_ATTR_NAME_VC_ASSIGN, 0666, show_vc_assign, set_vc_assign);

static struct attribute *vc_attrs[] = {
	&attr_vc_assign.attr,
	NULL
};

static struct attribute_group vc_attribute_group = {
	.name = __stringify(SYS_OBJECT_NAME_VC),
	.attrs = vc_attrs
};

/*
process_si_adopter_id
*/

void process_si_adopter_id(struct mhl_dev_context *dev_context,
			struct si_adopter_id_data *p_burst)
{
	uint8_t opcode = p_burst->hdr.op_code;
	uint8_t checksum;
	size_t total_size;
	total_size = SII_OFFSETOF(struct si_adopter_id_data, hdr.checksum)
			+  p_burst->hdr.remaining_length;
	checksum = calculate_generic_checksum(p_burst, 0, total_size);
	MHL_TX_DBG_INFO("total_size: 0x%02X"
		" id: %02X%02X"
		" rem_len: 0x%02X"
		" cksum: 0x%02X"
		" opcode: 0x%02X\n",
		total_size,
		p_burst->hdr.burst_id.high,
		p_burst->hdr.burst_id.low,
		p_burst->hdr.remaining_length,
		p_burst->hdr.checksum,
		p_burst->hdr.op_code)
	if (checksum) {
		MHL_TX_DBG_ERR("%schecksum error 0x%02X %s\n",
			ANSI_ESC_RED_TEXT,
			checksum,
			ANSI_ESC_RESET_TEXT)
	} else
	switch (opcode) {
	case EDID_BLOCK: {
		struct edid_3d_data_t *edid_context;
		edid_context = dev_context->edid_parser_context;
		MHL_TX_DBG_INFO("EDID_BLOCK "
			"blk_num: %02X\n",
			p_burst->opcode_data.edid_blk.block_num)
			process_emsc_edid_sub_payload(edid_context, p_burst);
		}
		break;
	case EDID_STOP:
		MHL_TX_DBG_ERR("EDID_STOP: %d\n", total_size)
		break;
	}

}

/*
 * Called from the Titan interrupt handler to parse data
 * received from the eSMC BLOCK hardware. Process all waiting input
 * buffers. If multiple messages are sent in the same BLOCK Command buffer,
 * they are sorted out here also.
 */
static void si_mhl_tx_emsc_received(struct mhl_dev_context *context)
{
	struct drv_hw_context *hw_context =
		(struct drv_hw_context *)(&context->drv_context);
	int length, index, pass;
	uint16_t burst_id;
	uint8_t *prbuf, *pmsg;

	while (si_mhl_tx_drv_peek_block_input_buffer(context,
		&prbuf, &length) == 0) {
		index = 0;
		pass = 0;
/*		dump_array(DBG_MSG_LEVEL_INFO, "si_mhl_tx_emsc_received",
			prbuf, length); */
		do {
			struct MHL_burst_id_t *p_burst_id;
			pmsg = &prbuf[index];
			p_burst_id = (struct MHL_burst_id_t *)pmsg;
			burst_id = (((uint16_t)pmsg[0]) << 8) | pmsg[1];
			/* Move past the BURST ID */
			index += sizeof(*p_burst_id);

			switch (burst_id) {
			case burst_id_HID_PAYLOAD:
#if (INCLUDE_HID == 1)
				build_received_hid_message(context,
					&pmsg[3], pmsg[2]);
#endif
				index += (1 + pmsg[2]);
				break;

			case burst_id_BLK_RCV_BUFFER_INFO:
				hw_context->block_protocol.peer_blk_rx_buf_max
					= pmsg[3];
				hw_context->block_protocol.peer_blk_rx_buf_max
					<<= 8;
				hw_context->block_protocol.peer_blk_rx_buf_max
					|= pmsg[2];

				hw_context->block_protocol.
					peer_blk_rx_buf_avail =
					hw_context->block_protocol.
					peer_blk_rx_buf_max -
					(EMSC_RCV_BUFFER_DEFAULT -
						hw_context->block_protocol.
						peer_blk_rx_buf_avail);
				MHL_TX_DBG_INFO(
					"Total PEER Buffer Size: %d\n",
					hw_context->block_protocol.
					peer_blk_rx_buf_max);

				index += 2;
				break;

			case burst_id_BITS_PER_PIXEL_FMT:
				index += (4 + (2 * pmsg[5]));
				/*
				 * Inform the rest of the code of the
				 * acknowledgment
				 */
				break;
				/*
				 * Any BURST_ID reported by EMSC_SUPPORT can be
				 * sent using eMSC BLOCK
				 */
			case burst_id_HEV_DTDA:{
				struct MHL3_hev_dtd_a_data_t *p_dtda =
					(struct MHL3_hev_dtd_a_data_t *)
					p_burst_id;
				index += sizeof(*p_dtda) - sizeof(*p_burst_id);
				}
				break;
			case burst_id_HEV_DTDB:{
				struct MHL3_hev_dtd_b_data_t *p_dtdb =
				    (struct MHL3_hev_dtd_b_data_t *) p_burst_id;
				index +=
				    sizeof(*p_dtdb) -
				    sizeof(*p_burst_id);
				}
				/* for now, as a robustness excercise, adjust
				 * index according to the this burst field
				 */
				break;
			case burst_id_HEV_VIC:{
				struct MHL3_hev_vic_data_t *p_burst;
				p_burst = (struct MHL3_hev_vic_data_t *)
					p_burst_id;
				index += sizeof(*p_burst) -
					sizeof(*p_burst_id) +
					sizeof(p_burst->video_descriptors[0]) *
					p_burst->num_entries_this_burst;
				}
				break;
			case burst_id_3D_VIC:{
				struct MHL3_hev_vic_data_t *p_burst =
					(struct MHL3_hev_vic_data_t *)
					p_burst_id;
				index += sizeof(*p_burst) -
					sizeof(*p_burst_id) -
					sizeof(p_burst->video_descriptors) +
					sizeof(p_burst->video_descriptors[0]) *
					p_burst->num_entries_this_burst;
				}
				break;
			case burst_id_3D_DTD:{
				struct MHL2_video_format_data_t *p_burst =
					(struct MHL2_video_format_data_t *)
					p_burst_id;
				index += sizeof(*p_burst) -
					sizeof(*p_burst_id) -
					sizeof(p_burst->video_descriptors) +
					sizeof(p_burst->video_descriptors[0]) *
					p_burst->num_entries_this_burst;
				}
				break;
			case burst_id_VC_ASSIGN:{
				struct SI_PACK_THIS_STRUCT tdm_alloc_burst
					*p_burst;
				p_burst = (struct SI_PACK_THIS_STRUCT
					tdm_alloc_burst*)p_burst_id;
				index += sizeof(*p_burst) -
					sizeof(*p_burst_id) -
					sizeof(p_burst->reserved) -
					sizeof(p_burst->vc_info) +
					sizeof(p_burst->vc_info[0]) *
					p_burst->num_entries_this_burst;
				}
				break;
			case burst_id_VC_CONFIRM:{
				struct SI_PACK_THIS_STRUCT tdm_alloc_burst
					*p_burst;
				p_burst = (struct SI_PACK_THIS_STRUCT
					tdm_alloc_burst*)p_burst_id;
				index += sizeof(*p_burst) -
					sizeof(*p_burst_id) -
					sizeof(p_burst->reserved) -
					sizeof(p_burst->vc_info) +
					sizeof(p_burst->vc_info[0]) *
					p_burst->num_entries_this_burst;
				}
				break;
			case burst_id_AUD_DELAY:{
				struct MHL3_audio_delay_burst_t *p_burst =
					(struct MHL3_audio_delay_burst_t *)
					p_burst_id;
				index += sizeof(*p_burst) -
					sizeof(*p_burst_id) -
					sizeof(p_burst->reserved);
				}
				break;
			case burst_id_ADT_BURSTID:{
				struct MHL3_adt_data_t *p_burst =
					(struct MHL3_adt_data_t *)p_burst_id;
				index += sizeof(*p_burst) - sizeof(*p_burst_id);
				}
				break;
			case burst_id_BIST_SETUP:{
				struct SI_PACK_THIS_STRUCT
					bist_setup_burst * p_bist_setup;
				p_bist_setup = (struct SI_PACK_THIS_STRUCT
					bist_setup_burst*)p_burst_id;
				index += sizeof(*p_bist_setup) -
					sizeof(*p_burst_id);
				}
				break;
			case burst_id_BIST_RETURN_STAT:{
				struct SI_PACK_THIS_STRUCT
					bist_return_stat_burst
					*p_bist_return;
				p_bist_return = (struct SI_PACK_THIS_STRUCT
					bist_return_stat_burst*)p_burst_id;
				index += sizeof(*p_bist_return) -
					sizeof(*p_burst_id);
				}
				break;
			case burst_id_EMSC_SUPPORT:{
				/*
				 * For robustness, adjust index
				 * according to the num-entries this
				 * burst field
				 */
				struct MHL3_emsc_support_data_t *p_burst =
					(struct MHL3_emsc_support_data_t *)
					p_burst_id;
				int i;
				index += sizeof(*p_burst) -
					sizeof(*p_burst_id) -
					sizeof(p_burst->payload) +
					sizeof(p_burst->payload.burst_ids[0]) *
					p_burst->num_entries_this_burst;
				for (i = 0;
					i < p_burst->num_entries_this_burst;
					++i) {
					enum BurstId_e burst_id =
					BURST_ID(p_burst->payload.burst_ids[i]);
					MHL_TX_DBG_INFO("BURST_ID: %04X\n",
						burst_id)
					switch (burst_id) {
					case SILICON_IMAGE_ADOPTER_ID:
						context->sii_adopter_id = true;
						break;
					case burst_id_HID_PAYLOAD:
						break;
					default:
						MHL_TX_DBG_ERR("%sunexpected"
							" burst/adopter id:%04X%s\n",
							ANSI_ESC_RED_TEXT,
							burst_id,
							ANSI_ESC_RESET_TEXT)
					}
				}
				}
				break;
			case SILICON_IMAGE_ADOPTER_ID:
				{
				struct si_adopter_id_data *p_burst =
					(struct si_adopter_id_data *)
						p_burst_id;

				index +=
				sizeof(p_burst->hdr.remaining_length)
					+ p_burst->hdr.remaining_length;
				process_si_adopter_id(context, p_burst);

				}
				break;
			default:
				if ((MHL_TEST_ADOPTER_ID == burst_id) ||
					(burst_id >= adopter_id_RANGE_START)) {
					struct EMSC_BLK_ADOPT_ID_PAYLD_HDR*
					p_adopter_id = (struct
						EMSC_BLK_ADOPT_ID_PAYLD_HDR*)
						pmsg;
					MHL_TX_DBG_ERR(
						"Bad ADOPTER_ID: %04X\n",
						burst_id);
					index += p_adopter_id->remaining_length;
				} else {
					MHL_TX_DBG_ERR(
						"Bad BURST ID: %04X\n",
						burst_id);
					index = length;
				}
				break;
			}

			length -= index;
		} while (length > 0);
		si_mhl_tx_drv_free_block_input_buffer(context);
	}
}

static void check_drv_intr_flags(struct mhl_dev_context *dev_context)
{
	enum cbus_mode_e cbus_mode;
	cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context);
	if (dev_context->intr_info.flags & DRV_INTR_CBUS_ABORT)
		process_cbus_abort(dev_context);

	if (dev_context->intr_info.flags & DRV_INTR_WRITE_BURST)
		si_mhl_tx_process_write_burst_data(dev_context);

	/* sometimes SET_INT and WRITE_STAT come at the
	 * same time. Always process WRITE_STAT first.
	 */
	if (dev_context->intr_info.flags & DRV_INTR_WRITE_STAT)
		si_mhl_tx_got_mhl_status(dev_context,
			&dev_context->intr_info.dev_status);

	if (dev_context->intr_info.flags & DRV_INTR_SET_INT)
		si_mhl_tx_got_mhl_intr(dev_context,
			dev_context->intr_info.int_msg[0],
			dev_context->intr_info.int_msg[1]);

	if (dev_context->intr_info.flags & DRV_INTR_MSC_DONE)
		si_mhl_tx_msc_command_done(dev_context,
			dev_context->intr_info.msc_done_data);

	if (dev_context->intr_info.flags & DRV_INTR_EMSC_INCOMING)
		si_mhl_tx_emsc_received(dev_context);

	if (dev_context->intr_info.flags & DRV_INTR_HPD_CHANGE)
		si_mhl_tx_notify_downstream_hpd_change
		    (dev_context, dev_context->intr_info.hpd_status);

	if (dev_context->intr_info.flags & DRV_INTR_MSC_RECVD) {
		dev_context->msc_msg_arrived = true;
		dev_context->msc_msg_sub_command =
		    dev_context->intr_info.msc_msg[0];
		dev_context->msc_msg_data =
		    dev_context->intr_info.msc_msg[1];
		si_mhl_tx_process_events(dev_context);
	}

	if (DRV_INTR_COC_CAL & dev_context->intr_info.flags) {
		bool do_test_now = false;
		switch (cbus_mode) {
		case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST:
			if (0 == dev_context->bist_setup.avlink_duration)  {
				MHL_TX_DBG_ERR("infinite av_link duration\n")
				do_test_now = false;
			} else if (BIST_TRIGGER_ECBUS_AV_LINK_MASK &
				dev_context->bist_trigger_info) {
				MHL_TX_DBG_ERR("%sAV_LINK BIST start%s\n",
					ANSI_ESC_GREEN_TEXT,
					ANSI_ESC_RESET_TEXT)
				do_test_now = true;
			}

			if (BIST_TRIGGER_ECBUS_TX_RX_MASK &
				dev_context->bist_trigger_info) {
				MHL_TX_DBG_ERR("%seCBUS BIST start%s\n",
					ANSI_ESC_GREEN_TEXT,
					ANSI_ESC_RESET_TEXT)
				do_test_now = true;
			}
			if (!do_test_now)
				;
			else if (dev_context->misc_flags.flags.bist_role_TE)
				start_bist_initiator_test(dev_context);
			else
				initiate_bist_test(dev_context);
			break;
		default:
			;
		}
	}
	if (DRV_INTR_TDM_SYNC & dev_context->intr_info.flags) {
		switch (cbus_mode) {
		case CM_eCBUS_S_BIST:
		case CM_eCBUS_D_BIST:
			break;
		case CM_eCBUS_S_AV_BIST:
		case CM_eCBUS_D_AV_BIST:
			if (dev_context->bist_setup.avlink_duration)  {
				MHL_TX_DBG_ERR("finite av_link duration\n")
			} else if (BIST_TRIGGER_ECBUS_AV_LINK_MASK &
					dev_context->bist_trigger_info) {
				if (dev_context->
					misc_flags.flags.bist_role_TE) {
					MHL_TX_DBG_ERR("\n")
					start_bist_initiator_test(dev_context);
				} else {
					MHL_TX_DBG_ERR("\n")
					initiate_bist_test(dev_context);
				}
			} else {
				MHL_TX_DBG_ERR("infinite av_link duration\n")
			}
			break;
		case CM_eCBUS_S:
		case CM_eCBUS_D:
			/*
			   MHL3 spec requires the process of reading
			   the remainder of XDEVCAP and all of DEVCAP
			   to be deferred until after the first
			   transition to eCBUS mode.
			   Check DCAP_RDY, initiate XDEVCAP read here.
			 */
			if (MHL_STATUS_DCAP_RDY & dev_context->status_0)
				si_mhl_tx_ecbus_started(dev_context);
			if (dev_context->
				misc_flags.flags.have_complete_devcap) {
				if (dev_context->misc_flags.flags.mhl_hpd &&
				   !dev_context->edid_valid) {
					si_mhl_tx_initiate_edid_sequence(
					    dev_context->edid_parser_context);
				}
			}
			break;
		default:
			MHL_TX_DBG_ERR("%sunexpected CBUS mode%s:%s\n",
				ANSI_ESC_RED_TEXT,
				si_mhl_tx_drv_get_cbus_mode_str(cbus_mode),
				ANSI_ESC_RESET_TEXT)

		}
	}
}
/*
 * Interrupt handler for MHL transmitter interrupts.
 *
 * @irq:	The number of the asserted IRQ line that caused
 *		this handler to be called.
 * @data:	Data pointer passed when the interrupt was enabled,
 *		which in this case is a pointer to an mhl_dev_context struct.
 *
 * Always returns IRQ_HANDLED.
 */
static irqreturn_t mhl_irq_handler(int irq, void *data)
{
	struct mhl_dev_context *dev_context = (struct mhl_dev_context *)data;

	MHL_TX_DBG_INFO("called\n");

	if (!down_interruptible(&dev_context->isr_lock)) {
		int loop_limit = 5;
		if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN)
			goto irq_done;
		if (dev_context->dev_flags & DEV_FLAG_COMM_MODE)
			goto irq_done;

		do {
			memset(&dev_context->intr_info, 0,
				sizeof(*(&dev_context->intr_info)));

			dev_context->intr_info.edid_parser_context =
				dev_context->edid_parser_context;

			dev_context->drv_info->
				mhl_device_isr((struct drv_hw_context *)
				(&dev_context->drv_context),
				&dev_context->intr_info);

			/* post process events raised by interrupt handler */
			if (dev_context->intr_info.flags &
				DRV_INTR_DISCONNECT) {
				dev_context->misc_flags.flags.rap_content_on =
					false;
				dev_context->misc_flags.flags.mhl_rsen = false;
				dev_context->mhl_connection_event = true;
				dev_context->mhl_connected =
					MHL_TX_EVENT_DISCONNECTION;
				si_mhl_tx_process_events(dev_context);
			} else {
				if (dev_context->intr_info.flags &
					DRV_INTR_CONNECT) {
					dev_context->misc_flags.flags.
						rap_content_on = true;
					dev_context->rap_in_sub_command =
						MHL_RAP_CONTENT_ON;
					dev_context->misc_flags.flags.mhl_rsen =
						true;
					dev_context->mhl_connection_event =
						true;
					dev_context->mhl_connected =
						MHL_TX_EVENT_CONNECTION;
					si_mhl_tx_process_events(dev_context);
				}


				check_drv_intr_flags(dev_context);
			}
			/*
			 * Send any messages that may have been queued up
			 * as the result of interrupt processing.
			 */
			si_mhl_tx_drive_states(dev_context);
			if (si_mhl_tx_drv_get_cbus_mode(dev_context) >=
			    CM_eCBUS_S) {
				si_mhl_tx_push_block_transactions(dev_context);
			}
		} while ((--loop_limit > 0) && is_interrupt_asserted());
irq_done:
		up(&dev_context->isr_lock);
	}

	return IRQ_HANDLED;
}

/* APIs provided by the MHL layer to the lower level driver */

int mhl_handle_power_change_request(struct device *parent_dev, bool power_up)
{
	struct mhl_dev_context *dev_context;
	int status;

	dev_context = dev_get_drvdata(parent_dev);

	MHL_TX_DBG_INFO("\n");

	/* Power down the MHL transmitter hardware. */
	status = down_interruptible(&dev_context->isr_lock);
	if (status) {
		MHL_TX_DBG_ERR("failed to acquire ISR semaphore,"
			       "status: %d\n", status);
		goto done;
	}

	if (power_up)
		status = si_mhl_tx_initialize(dev_context);
	else
		status = si_mhl_tx_shutdown(dev_context);

	up(&dev_context->isr_lock);
done:
	return status;

}

int mhl_tx_init(struct mhl_drv_info const *drv_info, struct device *parent_dev)
{
	struct mhl_dev_context *dev_context;
	int status = 0;
	int ret;

	if (drv_info == NULL || parent_dev == NULL) {
		pr_err("Null parameter passed to %s\n", __func__);
		return -EINVAL;
	}

	if (drv_info->mhl_device_isr == NULL || drv_info->irq == 0) {
		dev_err(parent_dev, "No IRQ specified!\n");
		return -EINVAL;
	}

	dev_context = kzalloc(sizeof(*dev_context) + drv_info->drv_context_size,
			      GFP_KERNEL);
	if (!dev_context) {
		dev_err(parent_dev, "failed to allocate driver data\n");
		return -ENOMEM;
	}

	dev_context->signature = MHL_DEV_CONTEXT_SIGNATURE;
	dev_context->drv_info = drv_info;

	sema_init(&dev_context->isr_lock, 1);
	INIT_LIST_HEAD(&dev_context->timer_list);
	dev_context->timer_work_queue = create_workqueue(MHL_DRIVER_NAME);
	if (dev_context->timer_work_queue == NULL) {
		ret = -ENOMEM;
		goto free_mem;
	}

#if (INCLUDE_HID == 1)
	/*
	 * Create a single-threaded work queue so that no two queued
	 * items can run at the same time.
	 */
	dev_context->hid_work_queue = create_singlethread_workqueue(
		"MHL3_HID_WORK");
	if (dev_context->hid_work_queue == NULL) {
		ret = -ENOMEM;
		goto timer_cleanup;
	}
#endif

	if (mhl_class == NULL) {
		mhl_class = class_create(THIS_MODULE, "mhl");
		if (IS_ERR(mhl_class)) {
			ret = PTR_ERR(mhl_class);
			pr_info("class_create failed %d\n", ret);
			goto hid_cleanup;
		}

#if (LINUX_KERNEL_VER < 315)
		mhl_class->dev_attrs = driver_attribs;
#endif

		ret = alloc_chrdev_region(&dev_num,
			0, MHL_DRIVER_MINOR_MAX, MHL_DRIVER_NAME);

		if (ret) {
			pr_info("register_chrdev %s failed, error code: %d\n",
				MHL_DRIVER_NAME, ret);
			goto free_class;
		}

		cdev_init(&dev_context->mhl_cdev, &mhl_fops);
		dev_context->mhl_cdev.owner = THIS_MODULE;
		ret =
		    cdev_add(&dev_context->mhl_cdev, MINOR(dev_num),
			     MHL_DRIVER_MINOR_MAX);
		if (ret) {
			pr_info("cdev_add %s failed %d\n", MHL_DRIVER_NAME,
				ret);
			goto free_chrdev;
		}
	}

	dev_context->mhl_dev = device_create(mhl_class, parent_dev,
		dev_num, dev_context, "%s", MHL_DEVICE_NAME);
	if (IS_ERR(dev_context->mhl_dev)) {
		ret = PTR_ERR(dev_context->mhl_dev);
		pr_info("device_create failed %s %d\n", MHL_DEVICE_NAME, ret);
		goto free_cdev;
	}
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&reg_access_attribute_group);

	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
				       &bist_attribute_group);
	if (status) {
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n",
			       status);
	}
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&rap_attribute_group);
	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
#if (INCLUDE_RBP == 1)
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&rbp_attribute_group);
	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
#endif
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&rcp_attribute_group);
	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&ucp_attribute_group);
	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&devcap_attribute_group);
	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&hdcp_attribute_group);
	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
	status = sysfs_create_group(&dev_context->mhl_dev->kobj,
		&vc_attribute_group);
	if (status)
		MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);

	ret = request_threaded_irq(drv_info->irq, NULL,
		mhl_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
		MHL_DEVICE_NAME, dev_context);
	if (ret < 0) {
		dev_err(parent_dev,
			"request_threaded_irq failed, status: %d\n", ret);
		goto free_device;
	}

	/* Initialize the MHL transmitter hardware. */
	ret = down_interruptible(&dev_context->isr_lock);
	if (ret) {
		dev_err(parent_dev,
			"Failed to acquire ISR semaphore, status: %d\n", ret);
		goto free_irq_handler;
	}

	/* Initialize EDID parser module */
	dev_context->edid_parser_context = si_edid_create_context(dev_context,
		(struct drv_hw_context *)&dev_context->drv_context);
	rcp_input_dev_one_time_init(dev_context);

	ret = si_mhl_tx_reserve_resources(dev_context);
	if (ret)
		dev_err(parent_dev,
			"failed to reserve resources\n");
	else
		ret = si_mhl_tx_initialize(dev_context);

	up(&dev_context->isr_lock);

	if (ret)
		goto free_edid_context;

#if (INCLUDE_SII6031 == 1)
	mhl_tx_register_mhl_discovery(dev_context);
#endif
	MHL_TX_DBG_INFO("MHL transmitter successfully initialized\n");
	dev_set_drvdata(parent_dev, dev_context);

	return ret;

free_edid_context:
	MHL_TX_DBG_INFO("%sMHL transmitter initialization failed%s\n",
		ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
	status = down_interruptible(&dev_context->isr_lock);
	if (status) {
		dev_err(parent_dev,
			"Failed to acquire ISR semaphore, "
			"could not destroy EDID context. Status: %d\n",
			status);
		goto free_irq_handler;
	}
	if (dev_context->edid_parser_context)
		si_edid_destroy_context(dev_context->edid_parser_context);
	up(&dev_context->isr_lock);

free_irq_handler:
	if (dev_context->client)
		free_irq(dev_context->client->irq, dev_context);

free_device:
	device_destroy(mhl_class, dev_num);

free_cdev:
	cdev_del(&dev_context->mhl_cdev);

free_chrdev:
	unregister_chrdev_region(dev_num, MHL_DRIVER_MINOR_MAX);
	dev_num = 0;

free_class:
	class_destroy(mhl_class);

hid_cleanup:
#if (INCLUDE_HID == 1)
	destroy_workqueue(dev_context->hid_work_queue);
timer_cleanup:
#endif
	destroy_workqueue(dev_context->timer_work_queue);

free_mem:
	kfree(dev_context);

	return ret;
}

int mhl_tx_remove(struct device *parent_dev)
{
	struct mhl_dev_context *dev_context;
	int ret = 0;

	dev_context = dev_get_drvdata(parent_dev);

	if (dev_context != NULL) {
		MHL_TX_DBG_INFO("%x\n", dev_context);

		dev_context->dev_flags |= DEV_FLAG_SHUTDOWN;
		ret = down_interruptible(&dev_context->isr_lock);
		free_irq(dev_context->drv_info->irq, dev_context);
		up(&dev_context->isr_lock);

#if (INCLUDE_HID == 1)
		mhl3_hid_remove_all(dev_context);
		destroy_workqueue(dev_context->hid_work_queue);
		dev_context->hid_work_queue = NULL;
#endif
		ret = si_mhl_tx_shutdown(dev_context);

		mhl_tx_destroy_timer_support(dev_context);

		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&reg_access_attribute_group);
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&rap_attribute_group);
#if (INCLUDE_RBP == 1)
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&rbp_attribute_group);
#endif
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&rcp_attribute_group);
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&ucp_attribute_group);
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&devcap_attribute_group);
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&hdcp_attribute_group);
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&vc_attribute_group);
		sysfs_remove_group(&dev_context->mhl_dev->kobj,
			&bist_attribute_group);

		device_destroy(mhl_class, dev_num);
		cdev_del(&dev_context->mhl_cdev);
		unregister_chrdev_region(dev_num, MHL_DRIVER_MINOR_MAX);
		dev_num = 0;
		class_destroy(mhl_class);
		mhl_class = NULL;

#ifdef MEDIA_DATA_TUNNEL_SUPPORT
		mdt_destroy(dev_context);
#endif
		destroy_rcp_input_dev(dev_context);
		si_edid_destroy_context(dev_context->edid_parser_context);
#if (INCLUDE_SII6031 == 1)
		otg_unregister_mhl_discovery();
#endif
		kfree(dev_context);
	}
	return ret;
}
void mhl_tx_stop_all_timers(struct mhl_dev_context *dev_context)
{
	struct timer_obj *timer = NULL;

	list_for_each_entry(timer, &dev_context->timer_list, list_link) {
		mhl_tx_stop_timer(dev_context, timer);
	}
}
static void mhl_tx_destroy_timer_support(struct mhl_dev_context *dev_context)
{
	struct timer_obj *mhl_timer;

	/*
	 * Make sure all outstanding timer objects are canceled and the
	 * memory allocated for them is freed.
	 */
	while (!list_empty(&dev_context->timer_list)) {
		mhl_timer = list_first_entry(&dev_context->timer_list,
					     struct timer_obj, list_link);
		hrtimer_cancel(&mhl_timer->hr_timer);
		list_del(&mhl_timer->list_link);
		kfree(mhl_timer);
	}

	destroy_workqueue(dev_context->timer_work_queue);
	dev_context->timer_work_queue = NULL;
}

static void mhl_tx_timer_work_handler(struct work_struct *work)
{
	struct timer_obj *mhl_timer;

	mhl_timer = container_of(work, struct timer_obj, work_item);

	mhl_timer->flags |= TIMER_OBJ_FLAG_WORK_IP;
	if (!down_interruptible(&mhl_timer->dev_context->isr_lock)) {

		mhl_timer->timer_callback_handler(mhl_timer->callback_param);

		up(&mhl_timer->dev_context->isr_lock);
	}
	mhl_timer->flags &= ~TIMER_OBJ_FLAG_WORK_IP;

	if (mhl_timer->flags & TIMER_OBJ_FLAG_DEL_REQ) {
		/*
		 * Deletion of this timer was requested during the execution of
		 * the callback handler so go ahead and delete it now.
		 */
		kfree(mhl_timer);
	}
}

static enum hrtimer_restart mhl_tx_timer_handler(struct hrtimer *timer)
{
	struct timer_obj *mhl_timer;

	mhl_timer = container_of(timer, struct timer_obj, hr_timer);

	queue_work(mhl_timer->dev_context->timer_work_queue,
		   &mhl_timer->work_item);

	return HRTIMER_NORESTART;
}

static int is_timer_handle_valid(struct mhl_dev_context *dev_context,
				 void *timer_handle)
{
	struct timer_obj *timer = timer_handle; /* Set to avoid lint warning. */

	list_for_each_entry(timer, &dev_context->timer_list, list_link) {
		if (timer == timer_handle)
			break;
	}

	if (timer != timer_handle) {
		MHL_TX_DBG_WARN("Invalid timer handle %pK received\n",
				timer_handle);
		return -EINVAL;
	}
	return 0;
}

int mhl_tx_create_timer(void *context,
			void (*callback_handler) (void *callback_param),
			void *callback_param, void **timer_handle)
{
	struct mhl_dev_context *dev_context;
	struct timer_obj *new_timer;

	dev_context = get_mhl_device_context(context);

	if (callback_handler == NULL)
		return -EINVAL;

	if (dev_context->timer_work_queue == NULL)
		return -ENOMEM;

	new_timer = kmalloc(sizeof(*new_timer), GFP_KERNEL);
	if (new_timer == NULL)
		return -ENOMEM;

	new_timer->timer_callback_handler = callback_handler;
	new_timer->callback_param = callback_param;
	new_timer->flags = 0;

	new_timer->dev_context = dev_context;
	INIT_WORK(&new_timer->work_item, mhl_tx_timer_work_handler);

	list_add(&new_timer->list_link, &dev_context->timer_list);

	hrtimer_init(&new_timer->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	new_timer->hr_timer.function = mhl_tx_timer_handler;
	*timer_handle = new_timer;
	return 0;
}

int mhl_tx_delete_timer(void *context, void **timer_handle)
{
	struct mhl_dev_context *dev_context;
	struct timer_obj *timer;
	int status;

	dev_context = get_mhl_device_context(context);

	status = is_timer_handle_valid(dev_context, *timer_handle);
	if (status == 0) {
		timer = *timer_handle;

		list_del(&timer->list_link);

		hrtimer_cancel(&timer->hr_timer);

		if (timer->flags & TIMER_OBJ_FLAG_WORK_IP) {
			/*
			 * Request to delete timer object came from within the
			 * timer's callback handler. If we were to proceed with
			 * the timer deletion we would deadlock at
			 * cancel_work_sync(). Instead, just flag that the user
			 * wants the timer deleted. Later when the timer
			 * callback completes the timer's work handler will
			 * complete the process of deleting this timer.
			 */
			timer->flags |= TIMER_OBJ_FLAG_DEL_REQ;
		} else {
			cancel_work_sync(&timer->work_item);
			*timer_handle = NULL;
			kfree(timer);
		}
	}

	return status;
}

int mhl_tx_start_timer(void *context, void *timer_handle, uint32_t time_msec)
{
	struct mhl_dev_context *dev_context;
	struct timer_obj *timer;
	ktime_t timer_period;
	int status;

	dev_context = get_mhl_device_context(context);

	status = is_timer_handle_valid(dev_context, timer_handle);
	if (status == 0) {
		long secs = 0;
		timer = timer_handle;

		secs = time_msec / 1000;
		time_msec %= 1000;
		timer_period = ktime_set(secs, MSEC_TO_NSEC(time_msec));
		hrtimer_start(&timer->hr_timer, timer_period, HRTIMER_MODE_REL);
	}

	return status;
}

int mhl_tx_stop_timer(void *context, void *timer_handle)
{
	struct mhl_dev_context *dev_context;
	struct timer_obj *timer;
	int status;

	dev_context = get_mhl_device_context(context);

	status = is_timer_handle_valid(dev_context, timer_handle);
	if (status == 0) {
		timer = timer_handle;
		hrtimer_cancel(&timer->hr_timer);
	}
	return status;
}
