5718 lines
152 KiB
C
5718 lines
152 KiB
C
/*
|
|
* 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[] = {
|
|
®_access_page_attr.attr,
|
|
®_access_offset_attr.attr,
|
|
®_access_length_attr.attr,
|
|
®_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, ¶m) != 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, ¶m) != 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, ¶m) != 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, ¶m) != 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, ¶m) != 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, ¶m) != 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, ¶m) != 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, ¶m);
|
|
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, ¶m) != 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, ¶m);
|
|
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, ¶m);
|
|
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, ¶m);
|
|
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,
|
|
®Value);
|
|
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, ¶m);
|
|
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,
|
|
®_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,
|
|
®_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 %p 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;
|
|
}
|