diff --git a/drivers/video/fbdev/msm/mhl3/Kconfig b/drivers/video/fbdev/msm/mhl3/Kconfig new file mode 100644 index 000000000000..59fd9ed192fe --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/Kconfig @@ -0,0 +1,17 @@ +config MEDIA_DATA_TUNNEL_SUPPORT + bool + prompt "MHL Media Data Tunneling Support" + default n + ---help--- + This option controls whether or not Silicon Image MHL transmitter + drivers include support for Media Data Tunneling (MDT). MDT + support enables HID events generated by HID devices attached to + an MDT capable MHL sink to be passed over the MHL connection + to the system. + +config SII8620_MHL_TX + tristate + prompt "Silicon Image 8620 HDMI to MHL Tx" + depends on I2C + default n + diff --git a/drivers/video/fbdev/msm/mhl3/Makefile b/drivers/video/fbdev/msm/mhl3/Makefile new file mode 100644 index 000000000000..77708958259f --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/Makefile @@ -0,0 +1,165 @@ +# +# Makefile for the Silicon Image 8620 MHL TX device driver +# +# example invocations: +# For regular Linux builds +# make ARCH=arm CROSS_COMPILE=arm-angstrom-linux-gnueabi- clean debug +# make ARCH=arm CROSS_COMPILE=arm-angstrom-linux-gnueabi- clean release +# make ARCH=arm CROSS_COMPILE=arm-angstrom-linux-gnueabi- clean clean +# +# For Android driver builds - Specify different tool-chain and kernel revision +# export PATH=~/rowboat-android/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin:$PATH +# make ARCH=arm KERNELPATH=~/rowboat-android/kernel CROSS_COMPILE=arm-eabi- clean debug +# make ARCH=arm KERNELPATH=~/rowboat-android/kernel CROSS_COMPILE=arm-eabi- clean release +# make ARCH=arm KERNELPATH=~/rowboat-android/kernel CROSS_COMPILE=arm-eabi- clean clean +# +# For Android 4.0.3 (ICS): +# export PATH=~/rowboat-android/TI-Android-ICS-4.0.3_AM37x_3.0.0/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin:$PATH +# make ARCH=arm KERNELPATH=~/rowboat-android/TI-Android-ICS-4.0.3_AM37x_3.0.0/kernel CROSS_COMPILE=arm-eabi- clean debug +# make ARCH=arm KERNELPATH=~/rowboat-android/TI-Android-ICS-4.0.3_AM37x_3.0.0/kernel CROSS_COMPILE=arm-eabi- clean release +# make ARCH=arm KERNELPATH=~/rowboat-android/TI-Android-ICS-4.0.3_AM37x_3.0.0/kernel CROSS_COMPILE=arm-eabi- clean clean +# +# For Android 4.2.2 (JB) Linaro release 13.04 for PandaBoard: +# export PATH=$PATH:~/Linaro-13.04/android/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7-linaro/bin +# make ARCH=arm KERNELPATH=~/Linaro-13.04/android/kernel/linaro/pandaboard CROSS_COMPILE=arm-eabi- clean debug +# make ARCH=arm KERNELPATH=~/Linaro-13.04/android/kernel/linaro/pandaboard CROSS_COMPILE=arm-eabi- clean release +# make ARCH=arm KERNELPATH=~/Linaro-13.04/android/kernel/linaro/pandaboard CROSS_COMPILE=arm-eabi- clean clean +# + +# Silicon Image uses DEVELOPER_BUILD_ID for sandbox build to be identified during testing. +ifneq ($(DEVELOPER_BUILD_ID),) +DEVELOPER_BUILD_COPY=cp sii$(MHL_PRODUCT_NUM)drv.ko sii$(MHL_PRODUCT_NUM)drv$(DEVELOPER_BUILD_ID).ko +endif + +ifneq ($(KERNELRELEASE),) +# kbuild part of makefile + +# +# color annotations to use instead of leading newline chars +ccflags-y += -DANSI_COLORS + +ccflags-y += -DBUILD_NUM_STRING=\"$(MHL_BUILD_NUM)$(DEVELOPER_BUILD_ID)\" +ccflags-y += -DMHL_PRODUCT_NUM=$(MHL_PRODUCT_NUM) +ccflags-y += -DMHL_DRIVER_NAME=\"sii$(MHL_PRODUCT_NUM)drv\" +ccflags-y += -DMHL_DEVICE_NAME=\"sii-$(MHL_PRODUCT_NUM)\" + +# Support Device Tree? +ifeq ($(DT_SUPPORT),1) +ccflags-y += -DSIMG_USE_DTS +endif + +# Kernel level supported +ccflags-y += -DLINUX_KERNEL_VER=$(LINUX_KERNEL_VER) +# +# FORCE_OCBUS_FOR_ECTS is used to identify code added for ECTS temporary fix that prohibits use of eCBUS. +# in addition module parameter force_ocbus_for_ects needs to be set as 1 to achieve ECTS operation. +ccflags-y += -DFORCE_OCBUS_FOR_ECTS +# +# PC_MODE_VIDEO_TIMING_SUPPORT is for cases where no VIC is available from either AVIF or VSIF. +ccflags-y += -DPC_MODE_VIDEO_TIMING_SUPPORT +# +# MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH is to clear all infoframes +# upon driving HPD high instead of when SCDT goes low. +ccflags-y += -DMANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH +# +# MEDIA_DATA_TUNNEL_SUPPORT +# Default is enabled. Comment next line to disable. +ccflags-y += -DMEDIA_DATA_TUNNEL_SUPPORT +# +# Include REMOTE BUTTON PROTOCOL code or not +ccflags-$(CONFIG_DEBUG_DRIVER) += -DINCLUDE_RBP=1 + +ccflags-$(CONFIG_DEBUG_DRIVER) += -DINCLUDE_HID=$(INCLUDE_HID) + +# Example of use of SiI6031 is wrapped under the following definition +# It also illustrates how 8620 driver may be integrated into MSM platform +# the flag should be disabled if not building for MSM +ccflags-$(CONFIG_DEBUG_DRIVER) += -DINCLUDE_SII6031=$(INCLUDE_SII6031) +# +# MANUAL_EDID_FETCH uses DDC master directly, instead of h/w automated method. +ccflags-y += -DMANUAL_EDID_FETCH + +# If CI2CA pin is pulled HIGH, you must define the following flag +#ccflags-y += -DALT_I2C_ADDR + +# PRINT_DDC_ABORTS enables logging of all DDC_ABORTs. Default "disabled" - helps MHL2 hot plug. +# Enable only if you must for debugging. +#ccflags-y += -DPRINT_DDC_ABORTS + +# CoC_FSM_MONITORING exports CoC state machine to GPIO pins +# Enable only if you must for debugging. +ccflags-y += -DCoC_FSM_MONITORING -DBIST_MONITORING + +#BIST_DONE_DEBUG adds register dump prior to RAP{CBUS_MODE_UP} +#ccflags-y += -DBIST_DONE_DEBUG + +# For si_emsc_hid-mt.c +ccflags-y += -Idrivers/hid + +# Optimzations and/or workaround +ccflags-y += -DDISABLE_SPI_DMA +ccflags-y += -DUSE_SPIOPTIMIZE +ccflags-y += -DGCS_QUIRKS_FOR_SIMG + +ccflags-$(CONFIG_DEBUG_DRIVER) += -DDEBUG + +# Enable VBUS sense and related operations +ccflags-$(CONFIG_DEBUG_DRIVER) += -DENABLE_VBUS_SENSE + +#support for DVI sources and sinks in MHL3 mode. +ccflags-$(CONFIG_DEBUG_DRIVER) += -DMHL3_DVI_SUPPORT + +#add HDMI VSDB to upstream EDID when downstream sink is DVI +#ccflags-$(CONFIG_DEBUG_DRIVER) += -DMHL3_DVI_SUPPORT_FORCE_HDMI +# +# the next lines are optional - they enable greater verbosity in debug output +#ccflags-$(CONFIG_DEBUG_DRIVER) += -DENABLE_EDID_DEBUG_PRINT +#ccflags-$(CONFIG_DEBUG_DRIVER) += -DENABLE_DUMP_INFOFRAME +# +# uncomment next line to prevent EDID parsing. Useful for debugging. +#ccflags-$(CONFIG_DEBUG_DRIVER) += -DEDID_PASSTHROUGH + +obj-$(CONFIG_SII$(MHL_PRODUCT_NUM)_MHL_TX) += sii$(MHL_PRODUCT_NUM)drv.o +sii$(MHL_PRODUCT_NUM)drv-objs += platform.o +sii$(MHL_PRODUCT_NUM)drv-objs += mhl_linux_tx.o +sii$(MHL_PRODUCT_NUM)drv-objs += mhl_rcp_inputdev.o +sii$(MHL_PRODUCT_NUM)drv-objs += mhl_rbp_inputdev.o +sii$(MHL_PRODUCT_NUM)drv-objs += mhl_supp.o +sii$(MHL_PRODUCT_NUM)drv-objs += si_8620_drv.o +sii$(MHL_PRODUCT_NUM)drv-objs += si_mhl2_edid_3d.o +sii$(MHL_PRODUCT_NUM)drv-objs += si_mdt_inputdev.o +ifeq ($(INCLUDE_HID),1) +sii$(MHL_PRODUCT_NUM)drv-objs += si_emsc_hid.o +sii$(MHL_PRODUCT_NUM)drv-objs += si_emsc_hid-mt.o +endif +else + +# Normal Makefile + +# If a kernel is not specified, default to the kernel used with Android Ice Cream Sandwich +ifneq ($(KERNELPATH),) +KERNELDIR=$(KERNELPATH) +else +KERNELDIR=~/src/linux-2.6.36 +#KERNELDIR=~/src/Android_ICS/kernel +endif +ARCH=arm + +PWD := $(shell pwd) + +.PHONY: clean + +release: + make -C $(KERNELDIR) M=$(PWD) CONFIG_SII$(MHL_PRODUCT_NUM)_MHL_TX=m CONFIG_MEDIA_DATA_TUNNEL_SUPPORT=y modules + $(CROSS_COMPILE)strip --strip-debug sii$(MHL_PRODUCT_NUM)drv.ko + $(DEVELOPER_BUILD_COPY) + +debug: + rm -f platform.o + make -C $(KERNELDIR) M=$(PWD) CONFIG_SII$(MHL_PRODUCT_NUM)_MHL_TX=m CONFIG_MEDIA_DATA_TUNNEL_SUPPORT=y CONFIG_DEBUG_DRIVER=y modules + $(DEVELOPER_BUILD_COPY) + +clean: + make -C $(KERNELDIR) M=$(PWD) CONFIG_SII$(MHL_PRODUCT_NUM)_MHL_TX=m clean + +endif diff --git a/drivers/video/fbdev/msm/mhl3/mhl_device_cfg.h b/drivers/video/fbdev/msm/mhl3/mhl_device_cfg.h new file mode 100644 index 000000000000..aa1c89c0a92a --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_device_cfg.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#if !defined(MHL_DEVICE_CFG_H) +#define MHL_DEVICE_CFG_H + +#include "si_app_devcap.h" + +/* + * This file contains SiI8620 driver and device configuration information + * + */ + + +/* + * Determine XDEVCAPS configurations allowed by this driver + */ + +#if (INCLUDE_HID == 1) +#define XDEVCAP_VAL_DEV_ROLES (MHL_XDC_DEV_HOST | MHL_XDC_HID_HOST) +#else +#define XDEVCAP_VAL_DEV_ROLES MHL_XDC_DEV_HOST +#endif + +#define XDEVCAP_VAL_LOG_DEV_MAPX MHL_XDC_LD_PHONE + +#endif /* if !defined(MHL_DEVICE_CFG_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/mhl_linux_tx.c b/drivers/video/fbdev/msm/mhl3/mhl_linux_tx.c new file mode 100644 index 000000000000..7a1645faa1f0 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_linux_tx.c @@ -0,0 +1,5717 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= length= \ + * data=data_byte_0 ... data_byte_length-1 + * where: specifies the starting register offset to begin + * read/writing within the scratch pad register space + * number of scratch pad registers to be written/read + * data_byte space separated list of 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 = strstr(pinput, "offset="); + 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: length: data: + * where: is the last scratch pad register offset + * written to this file + * is the last scratch pad register transfer length + * written to this file + * space separated list of 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: + * + * Where: 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: + * + * Where: 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: + * + * Where: 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 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: offset: length: data: + * where: is the last I2C register page address written + * to this file + * is the last register offset written to this file + * is the last register transfer length written + * to this file + * space separated list of 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 (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; +} diff --git a/drivers/video/fbdev/msm/mhl3/mhl_linux_tx.h b/drivers/video/fbdev/msm/mhl3/mhl_linux_tx.h new file mode 100644 index 000000000000..6ac5a003392e --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_linux_tx.h @@ -0,0 +1,455 @@ +/* + * 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. + */ + +#if !defined(MHL_LINUX_TX_H) +#define MHL_LINUX_TX_H + +#if (INCLUDE_HID == 1) +#include "si_emsc_hid.h" +#endif +#include "si_app_devcap.h" + +/* + * EMSC Block Transaction stuff + */ +#define EMSC_RCV_BUFFER_DEFAULT 256 +#define EMSC_BLK_MAX_LENGTH 256 +#define EMSC_BLK_STD_HDR_LEN 2 +#define EMSC_BLK_CMD_MAX_LEN (EMSC_BLK_MAX_LENGTH - \ + EMSC_BLK_STD_HDR_LEN) +#define LOCAL_BLK_RCV_BUFFER_SIZE 288 + +/* + * Event codes + * + */ +/* No event worth reporting */ +#define MHL_TX_EVENT_NONE 0x00 + +/* MHL connection has been lost */ +#define MHL_TX_EVENT_DISCONNECTION 0x01 + +/* MHL connection has been established */ +#define MHL_TX_EVENT_CONNECTION 0x02 + +/* Received an RCP key code */ +#define MHL_TX_EVENT_RCP_RECEIVED 0x04 + +/* Received an RCPK message */ +#define MHL_TX_EVENT_RCPK_RECEIVED 0x05 + +/* Received an RCPE message */ +#define MHL_TX_EVENT_RCPE_RECEIVED 0x06 + +/* Received an UTF-8 key code */ +#define MHL_TX_EVENT_UCP_RECEIVED 0x07 + +/* Received an UCPK message */ +#define MHL_TX_EVENT_UCPK_RECEIVED 0x08 + +/* Received an UCPE message */ +#define MHL_TX_EVENT_UCPE_RECEIVED 0x09 + +/* Scratch Pad Data received */ +#define MHL_TX_EVENT_SPAD_RECEIVED 0x0A + +/* Peer's power capability has changed */ +#define MHL_TX_EVENT_POW_BIT_CHG 0x0B + +/* Received a Request Action Protocol (RAP) message */ +#define MHL_TX_EVENT_RAP_RECEIVED 0x0C + +#if (INCLUDE_RBP == 1) +/* Received an RBP button code */ +#define MHL_TX_EVENT_RBP_RECEIVED 0x0D + +/* Received an RBPK message */ +#define MHL_TX_EVENT_RBPK_RECEIVED 0x0E + +/* Received an RBPE message */ +#define MHL_TX_EVENT_RBPE_RECEIVED 0x0F +#endif + +/* Received a BIST_READY message */ +#define MHL_TX_EVENT_BIST_READY_RECEIVED 0x10 + +/* A triggered BIST test has completed */ +#define MHL_TX_EVENT_BIST_TEST_DONE 0x11 + +/* Received a BIST_STATUS message */ +#define MHL_TX_EVENT_BIST_STATUS_RECEIVED 0x12 + +/* T_RAP_MAX expired */ +#define MHL_TX_EVENT_T_RAP_MAX_EXPIRED 0x13 + +/* peer sent AUD_DELAY burst */ +#define MHL_EVENT_AUD_DELAY_RCVD 0x14 + +#define ADOPTER_ID_SIZE 2 +#define MAX_SCRATCH_PAD_TRANSFER_SIZE 16 +#define SCRATCH_PAD_SIZE 64 + +#define SCRATCHPAD_SIZE 16 +union scratch_pad_u { + struct MHL2_video_format_data_t videoFormatData; + struct MHL3_hev_vic_data_t hev_vic_data; + struct MHL3_hev_dtd_a_data_t hev_dtd_a_data; + struct MHL3_hev_dtd_b_data_t hev_dtd_b_data; + uint8_t asBytes[SCRATCHPAD_SIZE]; +}; + +struct timer_obj { + struct list_head list_link; + struct work_struct work_item; + struct hrtimer hr_timer; + struct mhl_dev_context *dev_context; + uint8_t flags; +#define TIMER_OBJ_FLAG_WORK_IP 0x01 +#define TIMER_OBJ_FLAG_DEL_REQ 0x02 + void *callback_param; + void (*timer_callback_handler) (void *callback_param); +}; + +union misc_flags_u { + struct { + unsigned rcv_scratchpad_busy:1; + unsigned req_wrt_pending:1; + unsigned write_burst_pending:1; + unsigned have_complete_devcap:1; + + unsigned sent_dcap_rdy:1; + unsigned sent_path_en:1; + unsigned rap_content_on:1; + unsigned mhl_hpd:1; + + unsigned mhl_rsen:1; + unsigned edid_loop_active:1; + unsigned cbus_abort_delay_active:1; + unsigned have_complete_xdevcap:1; + + unsigned bist_role_TE:1; + + unsigned reserved:19; + } flags; + uint32_t as_uint32; +}; + +struct mhl_device_status { + uint8_t write_stat[3]; + uint8_t write_xstat[4]; +}; + +/* + * structure used by interrupt handler to return + * information about an interrupt. + */ +struct interrupt_info { + uint16_t flags; +/* Flags returned by low level driver interrupt handler */ +#define DRV_INTR_MSC_DONE 0x0001 /* message send done */ +#define DRV_INTR_MSC_RECVD 0x0002 /* MSC message received */ +#define DRV_INTR_MSC_NAK 0x0004 /* message send unsuccessful */ +#define DRV_INTR_WRITE_STAT 0x0008 /* write stat msg received */ +#define DRV_INTR_SET_INT 0x0010 /* set int message received */ +#define DRV_INTR_WRITE_BURST 0x0020 /* write burst received */ +#define DRV_INTR_HPD_CHANGE 0x0040 /* Hot plug detect change */ +#define DRV_INTR_CONNECT 0x0080 /* MHL connection established */ +#define DRV_INTR_DISCONNECT 0x0100 /* MHL connection lost */ +#define DRV_INTR_CBUS_ABORT 0x0200 /* CBUS msg transfer aborted */ +#define DRV_INTR_COC_CAL 0x0400 /* CoC Calibration done */ +#define DRV_INTR_TDM_SYNC 0x0800 /* TDM Sync Complete */ +#define DRV_INTR_EMSC_INCOMING 0x1000 + + void *edid_parser_context; + uint8_t msc_done_data; + uint8_t hpd_status; /* status of hot plug detect */ + /* received write stat data for CONNECTED_RDY and/or LINK_MODE, + * and/or MHL_VERSION_STAT + */ + struct mhl_device_status dev_status; + uint8_t msc_msg[2]; /* received msc message data */ + uint8_t int_msg[2]; /* received SET INT message data */ +}; + +enum tdm_vc_assignments { + TDM_VC_CBUS1 = 0, + TDM_VC_E_MSC = 1, + TDM_VC_T_CBUS = 2, + TDM_VC_MAX = TDM_VC_T_CBUS + 1 +}; + +/* allow for two WRITE_STAT, and one SET_INT immediately upon MHL_EST */ +#define NUM_CBUS_EVENT_QUEUE_EVENTS 16 + +#define MHL_DEV_CONTEXT_SIGNATURE \ + (('M' << 24) | ('H' << 16) | ('L' << 8) | ' ') + +struct mhl_dev_context { + uint32_t signature; /* identifies an instance of + this struct */ + struct mhl_drv_info const *drv_info; +#if (INCLUDE_SII6031 == 1) + struct completion sem_mhl_discovery_complete; + bool mhl_discovery_in_progress; + bool mhl_detected; + void (*notify_mhl)(int mhl_detected); + void *usb_ctxt; +#endif + struct i2c_client *client; + struct cdev mhl_cdev; + struct device *mhl_dev; + struct interrupt_info intr_info; + void *edid_parser_context; + u8 dev_flags; +#define DEV_FLAG_SHUTDOWN 0x01 /* Device is shutting down */ +#define DEV_FLAG_COMM_MODE 0x02 /* Halt INTR processing */ + + u16 mhl_flags; /* various state flags */ +#define MHL_STATE_FLAG_CONNECTED 0x0001 /* MHL connection + established */ +#define MHL_STATE_FLAG_RCP_SENT 0x0002 /* last RCP event was a key + send */ +#define MHL_STATE_FLAG_RCP_RECEIVED 0x0004 /* last RCP event was a key + code receive */ +#define MHL_STATE_FLAG_RCP_ACK 0x0008 /* last RCP key code sent was + ACK'd */ +#define MHL_STATE_FLAG_RCP_NAK 0x0010 /* last RCP key code sent was + NAK'd */ +#define MHL_STATE_FLAG_UCP_SENT 0x0020 /* last UCP event was a key + send */ +#define MHL_STATE_FLAG_UCP_RECEIVED 0x0040 /* last UCP event was a key + code receive */ +#define MHL_STATE_FLAG_UCP_ACK 0x0080 /* last UCP key code sent was + ACK'd */ +#define MHL_STATE_FLAG_UCP_NAK 0x0100 /* last UCP key code sent was + NAK'd */ +#if (INCLUDE_RBP == 1) +#define MHL_STATE_FLAG_RBP_RECEIVED 0x0200 /* last RBP event was a button + code receive */ +#define MHL_STATE_FLAG_RBP_ACK 0x0400 /* last RBP event was a button + code receive */ +#define MHL_STATE_FLAG_RBP_NAK 0x0800 /* last RBP event was a button + code receive */ +#define MHL_STATE_FLAG_RBP_SENT 0x1000 /* last RBP event was a button + send */ +#endif +#define MHL_STATE_FLAG_SPAD_SENT 0x2000 /* scratch pad send in + process */ +#define MHL_STATE_APPLICATION_RAP_BUSY 0x4000 /* application has indicated + that it is processing an + outstanding request */ + u8 dev_cap_local_offset; + u8 dev_cap_remote_offset; + u8 rap_in_sub_command; + u8 rap_in_status; + u8 rap_out_sub_command; + u8 rap_out_status; + u8 rcp_in_key_code; + u8 rcp_out_key_code; + u8 rcp_err_code; + u8 rcp_send_status; + u8 ucp_in_key_code; + u8 ucp_out_key_code; + u8 ucp_err_code; + u8 ucp_send_status; +#if (INCLUDE_RBP == 1) + u8 rbp_in_button_code; + u8 rbp_out_button_code; + u8 rbp_err_code; + u8 rbp_send_status; +#endif + u8 spad_offset; + u8 spad_xfer_length; + u8 spad_send_status; + u8 debug_i2c_address; + u8 debug_i2c_offset; + u8 debug_i2c_xfer_length; + +#ifdef MEDIA_DATA_TUNNEL_SUPPORT + struct mdt_inputdevs mdt_devs; +#endif + +#if (INCLUDE_HID == 1) + struct mhl3_hid_global_data mhl_ghid; + struct mhl3_hid_data *mhl_hid[16]; + struct workqueue_struct *hid_work_queue; +#endif + + u8 error_key; + struct input_dev *rcp_input_dev; +#if (INCLUDE_RBP == 1) + struct input_dev *rbp_input_dev; +#endif + struct semaphore isr_lock; /* semaphore used to prevent driver + * access from user mode from colliding + * with the threaded interrupt handler + */ + + u8 status_0; /* Received status from peer saved here */ + u8 status_1; + u8 peer_mhl3_version; + u8 xstatus_1; + u8 xstatus_3; + bool msc_msg_arrived; + u8 msc_msg_sub_command; + u8 msc_msg_data; + + u8 msc_msg_last_data; + u8 msc_save_rcp_key_code; +#if (INCLUDE_RBP == 1) + u8 msc_save_rbp_button_code; +#endif + u8 msc_save_ucp_key_code; + u8 link_mode; /* outgoing MHL LINK_MODE register value */ + bool mhl_connection_event; + u8 mhl_connected; + struct workqueue_struct *timer_work_queue; + struct list_head timer_list; + struct list_head cbus_queue; + struct list_head cbus_free_list; + struct cbus_req cbus_req_entries[NUM_CBUS_EVENT_QUEUE_EVENTS]; + struct cbus_req *current_cbus_req; + int sequence; + void *cbus_abort_timer; + void *dcap_rdy_timer; + void *dcap_chg_timer; + void *t_rap_max_timer; + union MHLDevCap_u dev_cap_cache; + union MHLXDevCap_u xdev_cap_cache; + u8 preferred_clk_mode; + union scratch_pad_u incoming_scratch_pad; + union scratch_pad_u outgoing_scratch_pad; + + uint8_t virt_chan_slot_counts[TDM_VC_MAX]; + uint8_t prev_virt_chan_slot_counts[TDM_VC_MAX]; + + void *cbus_mode_up_timer; + + void *bist_timer; + uint32_t bist_timeout_value; + uint32_t bist_timeout_total; + struct bist_setup_info bist_setup; + struct bist_setup_info sysfs_bist_setup; + struct bist_stat_info bist_stat; + uint8_t bist_trigger_info; + uint8_t bist_ready_status; + union misc_flags_u misc_flags; + + struct { + int sequence; + unsigned long local_blk_rx_buffer_size; + struct list_head queue; + struct list_head free_list; +#define NUM_BLOCK_QUEUE_REQUESTS 4 + struct block_req *marshalling_req; + struct block_req req_entries[NUM_BLOCK_QUEUE_REQUESTS]; + } block_protocol; + + bool sii_adopter_id; + bool edid_valid; + uint8_t numEdidExtensions; +#ifndef OLD_KEYMAP_TABLE + void *timer_T_press_mode; + void *timer_T_hold_maintain; +#endif + + void *drv_context; /* pointer aligned start of mhl + transmitter driver context area */ +}; + +#define PACKED_PIXEL_AVAILABLE(dev_context) \ + ((MHL_DEV_VID_LINK_SUPP_PPIXEL & \ + dev_context->dev_cap_cache.devcap_cache[DEVCAP_OFFSET_VID_LINK_MODE]) \ + && (MHL_DEV_VID_LINK_SUPP_PPIXEL & DEVCAP_VAL_VID_LINK_MODE)) + +#define _16_BPP_AVAILABLE(dev_context) \ + ((MHL_DEV_VID_LINK_SUPP_16BPP & \ + dev_context->dev_cap_cache.devcap_cache[DEVCAP_OFFSET_VID_LINK_MODE]) \ + && (MHL_DEV_VID_LINK_SUPP_16BPP & DEVCAP_VAL_VID_LINK_MODE)) + +enum scratch_pad_status { + SCRATCHPAD_FAIL = -4, + SCRATCHPAD_BAD_PARAM = -3, + SCRATCHPAD_NOT_SUPPORTED = -2, + SCRATCHPAD_BUSY = -1, + SCRATCHPAD_SUCCESS = 0 +}; + +struct drv_hw_context; + +struct mhl_drv_info { + int drv_context_size; + struct { + uint8_t major:4; + uint8_t minor:4; + } mhl_version_support; + int irq; + + /* APIs required to be supported by the low level MHL TX driver */ + int (*mhl_device_initialize) (struct drv_hw_context *hw_context); + void (*mhl_device_isr) (struct drv_hw_context *hw_context, + struct interrupt_info *intr_info); + int (*mhl_device_dbg_i2c_reg_xfer) (void *dev_context, u8 page, + u8 offset, u16 count, bool rw_flag, u8 *buffer); + int (*mhl_device_get_aksv) (struct drv_hw_context *hw_context, + u8 *buffer); +}; + +/* APIs provided by the Linux layer to the lower level driver */ +int mhl_handle_power_change_request(struct device *parent_dev, bool power_up); + +int mhl_tx_init(struct mhl_drv_info const *drv_info, struct device *parent_dev); + +int mhl_tx_remove(struct device *parent_dev); + +void mhl_event_notify(struct mhl_dev_context *dev_context, u32 event, + u32 event_param, void *data); + +struct mhl_dev_context *get_mhl_device_context(void *context); + +void *si_mhl_tx_get_drv_context(void *dev_context); + +int mhl_tx_create_timer(void *context, + void (*callback_handler) (void *callback_param), void *callback_param, + void **timer_handle); + +int mhl_tx_delete_timer(void *context, void **timer_handle); + +int mhl_tx_start_timer(void *context, void *timer_handle, uint32_t time_msec); + +int mhl_tx_stop_timer(void *context, void *timer_handle); +void mhl_tx_stop_all_timers(struct mhl_dev_context *dev_context); + +void si_mhl_tx_request_first_edid_block(struct mhl_dev_context *dev_context); +void si_mhl_tx_handle_atomic_hw_edid_read_complete(struct edid_3d_data_t + *mhl_edid_3d_data); + +/* APIs used within the Linux layer of the driver. */ +uint8_t si_mhl_tx_get_peer_dev_cap_entry(struct mhl_dev_context *dev_context, + uint8_t index, uint8_t *data); + +enum scratch_pad_status si_get_scratch_pad_vector(struct mhl_dev_context + *dev_context, uint8_t offset, uint8_t length, uint8_t *data); + +#if (INCLUDE_SII6031 == 1) +void mhl_tx_notify_otg(struct mhl_dev_context *dev_context, bool mhl_detected); + +int otg_register_mhl_discovery(void *mhl_ctx, int (*mhl_discover_device) + (void *, int, void (*)(void *, int online), void *)); +int otg_unregister_mhl_discovery(void); +void otg_mhl_notify(void *ctxt, int on); + +#endif +#endif /* if !defined(MHL_LINUX_TX_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.c b/drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.c new file mode 100644 index 000000000000..d3c4e3e696ce --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.c @@ -0,0 +1,350 @@ +/* + * 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. + */ +#if (INCLUDE_RBP == 1) +#include +#include +#include +#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_linux_tx.h" +#include "platform.h" +#include "mhl_rbp_inputdev.h" + +enum rbp_state_e { + ph0_idle, + ph3_press_and_hold_button, + ph8_hold_mode, + num_rbp_states +}; + +static char *state_strings[num_rbp_states] = { + "idle", + "press_and_hold_button", + "hold_mode" +}; + +enum rbp_event_e { + rbp_normal_button_press, + rbp_normal_button_press_same, + rbp_normal_button_release, + rbp_normal_button_release_same, + rbp_press_and_hold_button_press, + rbp_press_and_hold_button_press_same, + rbp_press_and_hold_button_release, + rbp_press_and_hold_button_release_same, + rbp_T_hold_maintain_expired, + rbp_T_press_mode_expired, + num_rbp_events +}; + +static char *event_strings[num_rbp_events] = { + "normal_button_press", + "normal_button_press_same", + "normal_button_release", + "normal_button_release_same", + "press_and_hold_button_press", + "press_and_hold_button_press_same", + "press_and_hold_button_release", + "press_and_hold_button_release_same", + "rbp_T_hold_maintain_expired", + "rbp_T_press_mode_expired" +}; + +enum rbp_state_e current_rbp_state = ph0_idle; +uint8_t rbp_previous_button = 0, rbp_current_button = 0; + +static int rbp_trigger_button_action(struct mhl_dev_context *dev_context, + uint8_t index, bool press_release) +{ + int status = -EINVAL; + + if (dev_context->rbp_input_dev) { + input_report_key(dev_context->rbp_input_dev, index, + press_release); + input_sync(dev_context->rbp_input_dev); + status = 0; + } + return status; +} + +static int handle_rbp_event(struct mhl_dev_context *dev_context, + uint8_t current_button, uint8_t prev_button, enum rbp_event_e event) +{ + int status = 0; + uint8_t current_index = current_button & MHL_RBP_BUTTON_ID_MASK; + uint8_t prev_index = prev_button & MHL_RBP_BUTTON_ID_MASK; + + MHL_TX_DBG_ERR("received 0x%02x: %s(%d) in state: %s(%d)\n", + current_button, event_strings[event], event, + state_strings[current_rbp_state], current_rbp_state); + /* now process the event according to the current state */ + switch (current_rbp_state) { + case ph0_idle: + switch (event) { + case rbp_normal_button_press: + case rbp_normal_button_press_same: + status = + rbp_trigger_button_action(dev_context, + current_index, 1); + /* no update for current_rbp_state */ + break; + case rbp_normal_button_release: + case rbp_normal_button_release_same: + status = + rbp_trigger_button_action(dev_context, + current_index, 0); + /* no update for current_rbp_state */ + break; + case rbp_press_and_hold_button_press: + case rbp_press_and_hold_button_press_same: + mhl_tx_start_timer(dev_context, + dev_context->timer_T_press_mode, + T_PRESS_MODE); + current_rbp_state = ph3_press_and_hold_button; + break; + + case rbp_press_and_hold_button_release: + case rbp_press_and_hold_button_release_same: + MHL_TX_DBG_ERR("unexpected %s(%d) in state: %s(%d)\n", + event_strings[event], event, + state_strings[current_rbp_state], + current_rbp_state); + break; + default: + MHL_TX_DBG_ERR("unexpected event: %d in state: %d\n", + event, current_rbp_state); + /* no update for current_rbp_state */ + status = -EINVAL; + } + break; + case ph3_press_and_hold_button: + switch (event) { + case rbp_normal_button_press: + case rbp_normal_button_press_same: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + rbp_trigger_button_action(dev_context, prev_index, 0); + /* OK to overwrite status */ + status = + rbp_trigger_button_action(dev_context, + current_index, 1); + current_rbp_state = ph0_idle; + break; + case rbp_normal_button_release: + case rbp_normal_button_release_same: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + rbp_trigger_button_action(dev_context, prev_index, 0); + rbp_trigger_button_action(dev_context, current_index, + 1); + status = + rbp_trigger_button_action(dev_context, + current_index, 0); + current_rbp_state = ph0_idle; + break; + case rbp_press_and_hold_button_press: + mhl_tx_start_timer(dev_context, + dev_context->timer_T_press_mode, + T_PRESS_MODE); + status = + rbp_trigger_button_action(dev_context, prev_index, + 1); + /* no update for current_rbp_state */ + break; + case rbp_press_and_hold_button_press_same: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + mhl_tx_start_timer(dev_context, + dev_context->timer_T_hold_maintain, + T_HOLD_MAINTAIN); + status = + rbp_trigger_button_action(dev_context, prev_index, + 1); + current_rbp_state = ph8_hold_mode; + break; + case rbp_press_and_hold_button_release: + case rbp_press_and_hold_button_release_same: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + status = + rbp_trigger_button_action(dev_context, prev_index, + 0); + current_rbp_state = ph0_idle; + break; + case rbp_T_press_mode_expired: + mhl_tx_start_timer(dev_context, + dev_context->timer_T_hold_maintain, + T_HOLD_MAINTAIN); + status = + rbp_trigger_button_action(dev_context, prev_index, + 0); + current_rbp_state = ph8_hold_mode; + break; + default: + MHL_TX_DBG_ERR("unexpected event: %d in state: %d\n", + event, current_rbp_state); + /* no update for current_rbp_state */ + status = -EINVAL; + } + break; + case ph8_hold_mode: + switch (event) { + case rbp_normal_button_press: + case rbp_normal_button_press_same: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + rbp_trigger_button_action(dev_context, prev_index, 0); + status = + rbp_trigger_button_action(dev_context, + current_index, 1); + current_rbp_state = ph0_idle; + break; + case rbp_normal_button_release: + case rbp_normal_button_release_same: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + rbp_trigger_button_action(dev_context, prev_index, 0); + rbp_trigger_button_action(dev_context, current_index, + 1); + status = + rbp_trigger_button_action(dev_context, + current_index, 0); + current_rbp_state = ph0_idle; + break; + case rbp_press_and_hold_button_press: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + mhl_tx_start_timer(dev_context, + dev_context->timer_T_press_mode, + T_PRESS_MODE); + status = + rbp_trigger_button_action(dev_context, prev_index, + 1); + current_rbp_state = ph3_press_and_hold_button; + break; + case rbp_press_and_hold_button_press_same: + mhl_tx_start_timer(dev_context, + dev_context->timer_T_hold_maintain, + T_HOLD_MAINTAIN); + status = + rbp_trigger_button_action(dev_context, prev_index, + 1); + /* no update for current_rbp_state */ + break; + case rbp_press_and_hold_button_release: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + rbp_trigger_button_action(dev_context, prev_index, 0); + rbp_trigger_button_action(dev_context, current_index, + 1); + status = + rbp_trigger_button_action(dev_context, + current_index, 0); + current_rbp_state = ph0_idle; + break; + case rbp_press_and_hold_button_release_same: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + status = + rbp_trigger_button_action(dev_context, prev_index, + 0); + current_rbp_state = ph0_idle; + break; + case rbp_T_hold_maintain_expired: + status = + rbp_trigger_button_action(dev_context, prev_index, + 0); + current_rbp_state = ph0_idle; + break; + default: + MHL_TX_DBG_ERR("unexpected event: %d in state: %d\n", + event, current_rbp_state); + /* no update for current_rbp_state */ + status = -EINVAL; + } + break; + default: + MHL_TX_DBG_ERR("irrational state value:%d\n", + current_rbp_state); + } + return status; +} + +int generate_rbp_input_event(struct mhl_dev_context *dev_context, + uint8_t rbp_buttoncode) +{ + /* + Since, in MHL, bit 7 == 1 indicates button release, + and, in Linux, zero means button release, + we use XOR (^) to invert the sense. + */ + enum rbp_event_e event; + int mhl_button_press; + int status = -EINVAL; + int index = rbp_buttoncode & MHL_RBP_BUTTON_ID_MASK; + + switch (index) { + case RBP_CALL_ANSWER: + case RBP_CALL_END: + case RBP_CALL_TOGGLE: + case RBP_CALL_MUTE: + case RBP_CALL_DECLINE: + case RBP_OCTOTHORPE: + case RBP_ASTERISK: + case RBP_ROTATE_CLKWISE: + case RBP_ROTATE_COUNTERCLKWISE: + case RBP_SCREEN_PAGE_NEXT: + case RBP_SCREEN_PAGE_PREV: + case RBP_SCREEN_PAGE_UP: + case RBP_SCREEN_PAGE_DN: + case RBP_SCREEN_PAGE_LEFT: + case RBP_SCREEN_PAGE_RIGHT: + break; + default: + return 1; + } + + mhl_button_press = + (rbp_buttoncode & MHL_RBP_BUTTON_RELEASED_MASK) ? 0 : 1; + + if (mhl_button_press) { + if (index == rbp_previous_button) + event = rbp_press_and_hold_button_press_same; + else + event = rbp_press_and_hold_button_press; + } else { + if (index == rbp_previous_button) + event = rbp_press_and_hold_button_release_same; + else + event = rbp_press_and_hold_button_release; + } + + status = handle_rbp_event(dev_context, rbp_buttoncode, + rbp_current_button, event); + + rbp_previous_button = rbp_current_button; + rbp_current_button = rbp_buttoncode; + + return status; +} +#endif + diff --git a/drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.h b/drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.h new file mode 100644 index 000000000000..e957e7ae9966 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef _MHL_RBP_INPUTDEV_H_ +#define _MHL_RBP_INPUTDEV_H_ + +struct mhl_dev_context; + +#define RBP_CALL_ANSWER 0x01 +#define RBP_CALL_END 0x02 +#define RBP_CALL_TOGGLE 0x03 +#define RBP_CALL_MUTE 0x04 +#define RBP_CALL_DECLINE 0x05 +#define RBP_OCTOTHORPE 0x06 +#define RBP_ASTERISK 0x07 +#define RBP_ROTATE_CLKWISE 0x20 +#define RBP_ROTATE_COUNTERCLKWISE 0x21 +#define RBP_SCREEN_PAGE_NEXT 0x30 +#define RBP_SCREEN_PAGE_PREV 0x31 +#define RBP_SCREEN_PAGE_UP 0x32 +#define RBP_SCREEN_PAGE_DN 0x33 +#define RBP_SCREEN_PAGE_LEFT 0x34 +#define RBP_SCREEN_PAGE_RIGHT 0x35 + +int generate_rbp_input_event(struct mhl_dev_context *dev_context, + uint8_t rbp_buttoncode); + +#endif /* #ifndef _MHL_RBP_INPUTDEV_H_ */ diff --git a/drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.c b/drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.c new file mode 100644 index 000000000000..15e349f6b319 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.c @@ -0,0 +1,845 @@ +/* + * 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 +#include +#include +#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_linux_tx.h" +#include "platform.h" +#include "mhl_rcp_inputdev.h" + +enum rcp_state_e { + PH0_IDLE, + PH3_PRESS_AND_HOLD_KEY, + ph8_hold_mode, + num_rcp_states +}; + +static char *state_strings[num_rcp_states] = { + "idle", + "press_and_hold_key", + "hold_mode" +}; + +enum rcp_event_e { + RCP_NORMAL_KEY_PRESS, + RCP_NORMAL_KEY_PRESS_SAME, + RCP_NORMAL_KEY_RELEASE, + RCP_NORMAL_KEY_RELEASE_SAME, + RCP_HOLD_KEY_PRESS, + RCP_HOLD_KEY_PRESS_SAME, + RCP_HOLD_KEY_RELEASE, + RCP_HOLD_KEY_RELEASE_SAME, + RCP_T_HOLD_MAINTAIN_EXPIRED, + RCP_T_PRESS_MODE_EXPIRED, + NUM_RCP_EVENTS +}; + +static char *event_strings[NUM_RCP_EVENTS] = { + "normal_key_press", + "normal_key_press_same", + "normal_key_release", + "normal_key_release_same", + "press_and_hold_key_press", + "press_and_hold_key_press_same", + "press_and_hold_key_release", + "press_and_hold_key_release_same", + "rcp_T_hold_maintain_expired", + "rcp_T_press_mode_expired" +}; + +enum rcp_state_e current_rcp_state = PH0_IDLE; +uint8_t rcp_previous_key = 0, rcp_current_key = 0; + +struct rcp_keymap_t rcpSupportTable[MHL_NUM_RCP_KEY_CODES] = { + {0, 0, 0, {KEY_SELECT, 0}, (MHL_DEV_LD_GUI)}, /* 0x00 */ + {0, 1, 0, {KEY_UP, 0}, (MHL_DEV_LD_GUI)}, /* 0x01 */ + {0, 1, 0, {KEY_DOWN, 0}, (MHL_DEV_LD_GUI)}, /* 0x02 */ + {0, 1, 0, {KEY_LEFT, 0}, (MHL_DEV_LD_GUI)}, /* 0x03 */ + {0, 1, 0, {KEY_RIGHT, 0}, (MHL_DEV_LD_GUI)}, /* 0x04 */ + + {1, 1, 0, {KEY_RIGHT, KEY_UP}, (MHL_DEV_LD_GUI)}, /* 0x05 */ + {1, 1, 0, {KEY_RIGHT, KEY_DOWN}, (MHL_DEV_LD_GUI)}, /* 0x06 */ + {1, 1, 0, {KEY_LEFT, KEY_UP}, (MHL_DEV_LD_GUI)}, /* 0x07 */ + {1, 1, 0, {KEY_LEFT, KEY_DOWN}, (MHL_DEV_LD_GUI)}, /* 0x08 */ + + {0, 0, 0, {KEY_MENU, 0}, (MHL_DEV_LD_GUI)}, /* 0x09 */ + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, /* 0x0A */ + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, /* 0x0B */ + {0, 0, 0, {KEY_BOOKMARKS, 0}, 0}, /* 0x0C */ + {0, 0, 0, {KEY_EXIT, 0}, (MHL_DEV_LD_GUI)}, /* 0x0D */ + + /* 0x0E - 0x1F Reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + + /* 0x20 Numeric 0 */ + {0, 0, 0, {KEY_NUMERIC_0, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x21 Numeric 1 */ + {0, 0, 0, {KEY_NUMERIC_1, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x22 Numeric 2 */ + {0, 0, 0, {KEY_NUMERIC_2, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x23 Numeric 3 */ + {0, 0, 0, {KEY_NUMERIC_3, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x24 Numeric 4 */ + {0, 0, 0, {KEY_NUMERIC_4, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x25 Numeric 5 */ + {0, 0, 0, {KEY_NUMERIC_5, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x26 Numeric 6 */ + {0, 0, 0, {KEY_NUMERIC_6, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x27 Numeric 7 */ + {0, 0, 0, {KEY_NUMERIC_7, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x28 Numeric 8 */ + {0, 0, 0, {KEY_NUMERIC_8, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x29 Numeric 9 */ + {0, 0, 0, {KEY_NUMERIC_9, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + + /* 0x2A Dot */ + {0, 0, 0, {KEY_DOT, 0}, 0}, + + /* 0x2B Enter */ + {0, 0, 0, {KEY_ENTER, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + /* 0x2C Clear */ + {0, 0, 0, {KEY_CLEAR, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | + MHL_DEV_LD_MEDIA | MHL_DEV_LD_TUNER)}, + + /* 0x2D - 0x2F Reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + + /* 0x30 Channel Up */ + {0, 1, 0, {KEY_CHANNELUP, 0}, (MHL_DEV_LD_TUNER)}, + /* 0x31 Channel Down */ + {0, 1, 0, {KEY_CHANNELDOWN, 0}, (MHL_DEV_LD_TUNER)}, + /* 0x32 Previous Channel */ + {0, 0, 0, {KEY_UNKNOWN, 0}, (MHL_DEV_LD_TUNER)}, + /* 0x33 Sound Select */ + {0, 0, 0, {KEY_SOUND, 0}, (MHL_DEV_LD_AUDIO)}, + /* 0x34 Input Select */ + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, + /* 0x35 Show Information */ + {0, 0, 0, {KEY_PROGRAM, 0}, 0}, + /* 0x36 Help */ + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, + /* 0x37 Page Up */ + {0, 1, 0, {KEY_PAGEUP, 0}, 0}, + /* 0x38 Page Down */ + {0, 1, 0, {KEY_PAGEDOWN, 0}, 0}, + + /* 0x39 - 0x40 Reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + + /* 0x41 Volume Up */ + {0, 1, 0, {KEY_VOLUMEUP, 0}, (MHL_DEV_LD_SPEAKER)}, + /* 0x42 Volume Down */ + {0, 1, 0, {KEY_VOLUMEDOWN, 0}, (MHL_DEV_LD_SPEAKER)}, + /* 0x43 Mute */ + {0, 0, 0, {KEY_MUTE, 0}, (MHL_DEV_LD_SPEAKER)}, + /* 0x44 Play */ + {0, 0, 0, {KEY_PLAY, 0}, (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO)}, + /* 0x45 Stop */ + {0, 0, 0, {KEY_STOP, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | MHL_DEV_LD_RECORD)}, + /* 0x46 Pause */ + {0, 0, 0, {KEY_PLAYPAUSE, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | MHL_DEV_LD_RECORD)}, + /* 0x47 Record */ + {0, 0, 0, {KEY_RECORD, 0}, (MHL_DEV_LD_RECORD)}, + /* 0x48 Rewind */ + {0, 1, 0, {KEY_REWIND, 0}, (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO)}, + /* 0x49 Fast Forward */ + {0, 1, 0, {KEY_FASTFORWARD, 0}, (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO)}, + /* 0x4A Eject */ + {0, 0, 0, {KEY_EJECTCD, 0}, (MHL_DEV_LD_MEDIA)}, + /* 0x4B Forward */ + {0, 1, 0, {KEY_NEXTSONG, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | MHL_DEV_LD_MEDIA)}, + /* 0x4C Backward */ + {0, 1, 0, {KEY_PREVIOUSSONG, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | MHL_DEV_LD_MEDIA)}, + + /* 0x4D - 0x4F Reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + + /* 0x50 = Angle */ + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, + /* 0x51 = Subpicture */ + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, + + /* 0x52 - 0x5F Reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + + /* 0x60 Play */ + {0, 0, 0, {KEY_PLAYPAUSE, 0}, (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO)}, + /* 0x60 = Pause the Play */ + {0, 0, 0, {KEY_PLAYPAUSE, 0}, (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO)}, + /* 0x62 = Record */ + {0, 0, 0, {KEY_RECORD, 0}, (MHL_DEV_LD_RECORD)}, + /* 0x63 = Pause the Record */ + {0, 0, 0, {KEY_PAUSE, 0}, (MHL_DEV_LD_RECORD)}, + /* 0x64 = Stop */ + {0, 0, 0, {KEY_STOP, 0}, + (MHL_DEV_LD_VIDEO | MHL_DEV_LD_AUDIO | MHL_DEV_LD_RECORD)}, + /* 0x65 = Mute */ + {0, 0, 0, {KEY_MUTE, 0}, (MHL_DEV_LD_SPEAKER)}, + /* 0x66 = Restore Mute */ + {0, 0, 0, {KEY_MUTE, 0}, (MHL_DEV_LD_SPEAKER)}, + + /* 0x67 - 0x68 Undefined */ + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, + {0, 0, 0, {KEY_UNKNOWN, 0}, 0}, + + /* 0x69 - 0x70 Reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + + /* 0x71 - 0x75 F1 - F5 */ + {0, 0, 0, {KEY_F1, 0}, 0}, + {0, 0, 0, {KEY_F2, 0}, 0}, + {0, 0, 0, {KEY_F3, 0}, 0}, + {0, 0, 0, {KEY_F4, 0}, 0}, + {0, 0, 0, {KEY_F5, 0}, 0}, + + /* 0x76 - 0x7D Reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + {0, 0, 0, {KEY_RESERVED, 0}, 0}, + + /* 0x7E Vendor */ + {0, 0, 0, {KEY_VENDOR, 0}, 0}, + + /* 0x7F reserved */ + {0, 0, 0, {KEY_RESERVED, 0}, 0} +}; + +static u16 rcp_def_keymap[MHL_NUM_RCP_KEY_CODES] +#ifdef OLD_KEYMAP_TABLE + = { + KEY_SELECT, + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_UNKNOWN, /* right-up */ + KEY_UNKNOWN, /* right-down */ + KEY_UNKNOWN, /* left-up */ + KEY_UNKNOWN, /* left-down */ + KEY_MENU, + KEY_UNKNOWN, /* setup */ + KEY_UNKNOWN, /* contents */ + KEY_UNKNOWN, /* favorite */ + KEY_EXIT, + KEY_RESERVED, /* 0x0e */ + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, /* 0x1F */ + KEY_NUMERIC_0, + KEY_NUMERIC_1, + KEY_NUMERIC_2, + KEY_NUMERIC_3, + KEY_NUMERIC_4, + KEY_NUMERIC_5, + KEY_NUMERIC_6, + KEY_NUMERIC_7, + KEY_NUMERIC_8, + KEY_NUMERIC_9, + KEY_DOT, + KEY_ENTER, + KEY_CLEAR, + KEY_RESERVED, /* 0x2D */ + KEY_RESERVED, + KEY_RESERVED, /* 0x2F */ + KEY_UNKNOWN, /* channel up */ + KEY_UNKNOWN, /* channel down */ + KEY_UNKNOWN, /* previous channel */ + KEY_UNKNOWN, /* sound select */ + KEY_UNKNOWN, /* input select */ + KEY_UNKNOWN, /* show information */ + KEY_UNKNOWN, /* help */ + KEY_UNKNOWN, /* page up */ + KEY_UNKNOWN, /* page down */ + KEY_RESERVED, /* 0x39 */ + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, /* 0x3F */ + KEY_RESERVED, /* 0x40 */ + KEY_UNKNOWN, /* volume up */ + KEY_UNKNOWN, /* volume down */ + KEY_UNKNOWN, /* mute */ + KEY_PLAY, + KEY_STOP, + KEY_PLAYPAUSE, + KEY_UNKNOWN, /* record */ + KEY_REWIND, + KEY_FASTFORWARD, + KEY_UNKNOWN, /* eject */ + KEY_NEXTSONG, + KEY_PREVIOUSSONG, + KEY_RESERVED, /* 0x4D */ + KEY_RESERVED, + KEY_RESERVED, /* 0x4F */ + KEY_UNKNOWN, /* angle */ + KEY_UNKNOWN, /* subtitle */ + KEY_RESERVED, /* 0x52 */ + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, /* 0x5F */ + KEY_PLAY, + KEY_PAUSE, + KEY_UNKNOWN, /* record_function */ + KEY_UNKNOWN, /* pause_record_function */ + KEY_STOP, + KEY_UNKNOWN, /* mute_function */ + KEY_UNKNOWN, /* restore_volume_function */ + KEY_UNKNOWN, /* tune_function */ + KEY_UNKNOWN, /* select_media_function */ + KEY_RESERVED, /* 0x69 */ + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, /* 0x70 */ + KEY_UNKNOWN, /* F1 */ + KEY_UNKNOWN, /* F2 */ + KEY_UNKNOWN, /* F3 */ + KEY_UNKNOWN, /* F4 */ + KEY_UNKNOWN, /* F5 */ + KEY_RESERVED, /* 0x76 */ + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, /* 0x7D */ + KEY_VENDOR, + KEY_RESERVED, /* 0x7F */ +} +#endif +; + +#ifdef OLD_KEYMAP_TABLE +int generate_rcp_input_event(struct mhl_dev_context *dev_context, + uint8_t rcp_keycode) +{ + int status = -EINVAL; + + if (dev_context->rcp_input_dev) { + if (rcp_keycode < ARRAY_SIZE(rcp_def_keymap) && + rcp_def_keymap[rcp_keycode] != KEY_UNKNOWN && + rcp_def_keymap[rcp_keycode] != KEY_RESERVED) { + + input_report_key(dev_context->rcp_input_dev, + rcp_keycode, 1); + input_report_key(dev_context->rcp_input_dev, + rcp_keycode, 0); + input_sync(dev_context->rcp_input_dev); + + status = 0; + } + } + return status; +} +#else + +static int rcp_trigger_key_action(struct mhl_dev_context *dev_context, + uint8_t index, bool press_release) +{ + int status = -EINVAL; + + index &= MHL_RCP_KEY_ID_MASK; + + if (dev_context->rcp_input_dev) { + input_report_key(dev_context->rcp_input_dev, + rcpSupportTable[index].map[0], press_release); + MHL_TX_DBG_ERR("input_report_key(0x%x,%d)\n", + rcpSupportTable[index].map[0], press_release) + if (rcpSupportTable[index].multicode) { + input_report_key(dev_context->rcp_input_dev, + rcpSupportTable[index].map[1], press_release); + MHL_TX_DBG_ERR("input_report_key(0x%x,%d)\n", + rcpSupportTable[index].map[1], press_release) + } + + input_sync(dev_context->rcp_input_dev); + status = 0; + } + return status; +} + +static int handle_rcp_event(struct mhl_dev_context *dev_context, + uint8_t current_key, uint8_t prev_key, enum rcp_event_e event) +{ + int status = 0; + uint8_t current_index = current_key & MHL_RCP_KEY_ID_MASK; + uint8_t prev_index = prev_key & MHL_RCP_KEY_ID_MASK; + + MHL_TX_DBG_ERR("received 0x%02x: %s(%d) in state: %s(%d)\n", + current_key, event_strings[event], event, + state_strings[current_rcp_state], current_rcp_state); + /* now process the event according to the current state */ + switch (current_rcp_state) { + case PH0_IDLE: + switch (event) { + case RCP_NORMAL_KEY_PRESS: + case RCP_NORMAL_KEY_PRESS_SAME: + status = + rcp_trigger_key_action(dev_context, current_index, + 1); + /* no update for current_rcp_state */ + break; + case RCP_NORMAL_KEY_RELEASE: + case RCP_NORMAL_KEY_RELEASE_SAME: + status = + rcp_trigger_key_action(dev_context, current_index, + 0); + /* no update for current_rcp_state */ + break; + case RCP_HOLD_KEY_PRESS: + case RCP_HOLD_KEY_PRESS_SAME: + status = + rcp_trigger_key_action(dev_context, current_index, + 1); + /* no break here */ + mhl_tx_start_timer(dev_context, + dev_context->timer_T_press_mode, + T_PRESS_MODE); + current_rcp_state = PH3_PRESS_AND_HOLD_KEY; + break; + + case RCP_HOLD_KEY_RELEASE: + case RCP_HOLD_KEY_RELEASE_SAME: + MHL_TX_DBG_ERR("unexpected %s(%d) in state: %s(%d)\n", + event_strings[event], event, + state_strings[current_rcp_state], + current_rcp_state); + break; + default: + MHL_TX_DBG_ERR("unexpected event: %d in state: %d\n", + event, current_rcp_state); + /* no update for current_rcp_state */ + status = -EINVAL; + } + break; + case PH3_PRESS_AND_HOLD_KEY: + switch (event) { + case RCP_NORMAL_KEY_PRESS: + case RCP_NORMAL_KEY_PRESS_SAME: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + rcp_trigger_key_action(dev_context, prev_index, 0); + /* OK to overwrite status */ + status = + rcp_trigger_key_action(dev_context, current_index, + 1); + current_rcp_state = PH0_IDLE; + break; + case RCP_NORMAL_KEY_RELEASE: + case RCP_NORMAL_KEY_RELEASE_SAME: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + rcp_trigger_key_action(dev_context, prev_index, 0); + rcp_trigger_key_action(dev_context, current_index, 1); + status = + rcp_trigger_key_action(dev_context, current_index, + 0); + current_rcp_state = PH0_IDLE; + break; + case RCP_HOLD_KEY_PRESS: + mhl_tx_start_timer(dev_context, + dev_context->timer_T_press_mode, + T_PRESS_MODE); + status = + rcp_trigger_key_action(dev_context, prev_index, 1); + /* no update for current_rcp_state */ + break; + case RCP_HOLD_KEY_PRESS_SAME: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + mhl_tx_start_timer(dev_context, + dev_context->timer_T_hold_maintain, + T_HOLD_MAINTAIN); + status = + rcp_trigger_key_action(dev_context, prev_index, 1); + current_rcp_state = ph8_hold_mode; + break; + case RCP_HOLD_KEY_RELEASE: + case RCP_HOLD_KEY_RELEASE_SAME: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_press_mode); + status = + rcp_trigger_key_action(dev_context, prev_index, 0); + current_rcp_state = PH0_IDLE; + break; + case RCP_T_PRESS_MODE_EXPIRED: + mhl_tx_start_timer(dev_context, + dev_context->timer_T_hold_maintain, + T_HOLD_MAINTAIN); + status = + rcp_trigger_key_action(dev_context, prev_index, 0); + current_rcp_state = ph8_hold_mode; + break; + default: + MHL_TX_DBG_ERR("unexpected event: %d in state: %d\n", + event, current_rcp_state); + /* no update for current_rcp_state */ + status = -EINVAL; + } + break; + case ph8_hold_mode: + switch (event) { + case RCP_NORMAL_KEY_PRESS: + case RCP_NORMAL_KEY_PRESS_SAME: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + rcp_trigger_key_action(dev_context, prev_index, 0); + status = + rcp_trigger_key_action(dev_context, current_index, + 1); + current_rcp_state = PH0_IDLE; + break; + case RCP_NORMAL_KEY_RELEASE: + case RCP_NORMAL_KEY_RELEASE_SAME: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + rcp_trigger_key_action(dev_context, prev_index, 0); + rcp_trigger_key_action(dev_context, current_index, 1); + status = + rcp_trigger_key_action(dev_context, current_index, + 0); + current_rcp_state = PH0_IDLE; + break; + case RCP_HOLD_KEY_PRESS: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + mhl_tx_start_timer(dev_context, + dev_context->timer_T_press_mode, + T_PRESS_MODE); + status = + rcp_trigger_key_action(dev_context, prev_index, 1); + current_rcp_state = PH3_PRESS_AND_HOLD_KEY; + break; + case RCP_HOLD_KEY_PRESS_SAME: + mhl_tx_start_timer(dev_context, + dev_context->timer_T_hold_maintain, + T_HOLD_MAINTAIN); + status = + rcp_trigger_key_action(dev_context, prev_index, 1); + /* no update for current_rcp_state */ + break; + case RCP_HOLD_KEY_RELEASE: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + rcp_trigger_key_action(dev_context, prev_index, 0); + rcp_trigger_key_action(dev_context, current_index, 1); + status = + rcp_trigger_key_action(dev_context, current_index, + 0); + current_rcp_state = PH0_IDLE; + break; + case RCP_HOLD_KEY_RELEASE_SAME: + mhl_tx_stop_timer(dev_context, + dev_context->timer_T_hold_maintain); + status = + rcp_trigger_key_action(dev_context, prev_index, 0); + current_rcp_state = PH0_IDLE; + break; + case RCP_T_HOLD_MAINTAIN_EXPIRED: + status = + rcp_trigger_key_action(dev_context, prev_index, 0); + current_rcp_state = PH0_IDLE; + break; + default: + MHL_TX_DBG_ERR("unexpected event: %d in state: %d\n", + event, current_rcp_state); + /* no update for current_rcp_state */ + status = -EINVAL; + } + break; + default: + MHL_TX_DBG_ERR("irrational state value:%d\n", + current_rcp_state); + } + return status; +} + +static void timer_callback_T_hold_maintain_handler(void *param) +{ + struct mhl_dev_context *dev_context = (struct mhl_dev_context *)param; + handle_rcp_event(dev_context, rcp_current_key, rcp_previous_key, + RCP_T_HOLD_MAINTAIN_EXPIRED); +} + +static void timer_callback_T_press_mode_handler(void *param) +{ + struct mhl_dev_context *dev_context = (struct mhl_dev_context *)param; + handle_rcp_event(dev_context, rcp_current_key, rcp_previous_key, + RCP_T_PRESS_MODE_EXPIRED); +} + +int generate_rcp_input_event(struct mhl_dev_context *dev_context, + uint8_t rcp_keycode) +{ + /* + Since, in MHL, bit 7 == 1 indicates key release, + and, in Linux, zero means key release, + we use XOR (^) to invert the sense. + */ + int status = -EINVAL; + int index = rcp_keycode & MHL_RCP_KEY_ID_MASK; + + if (rcp_def_keymap[index] != KEY_UNKNOWN && + rcp_def_keymap[index] != KEY_RESERVED) { + + enum rcp_event_e event; + int mhl_key_press = + (rcp_keycode & MHL_RCP_KEY_RELEASED_MASK) ? 0 : 1; + + if (mhl_key_press) { + if (rcpSupportTable[index].press_and_hold_key) { + if (index == rcp_previous_key) + event = RCP_HOLD_KEY_PRESS_SAME; + else + event = RCP_HOLD_KEY_PRESS; + } else { + if (index == rcp_previous_key) + event = RCP_NORMAL_KEY_PRESS_SAME; + else + event = RCP_NORMAL_KEY_PRESS; + } + } else { + if (rcpSupportTable[index].press_and_hold_key) { + if (index == rcp_previous_key) + event = RCP_HOLD_KEY_RELEASE_SAME; + else + event = RCP_HOLD_KEY_RELEASE; + } else { + if (index == rcp_previous_key) + event = RCP_NORMAL_KEY_RELEASE_SAME; + else + event = RCP_NORMAL_KEY_RELEASE; + } + } + status = + handle_rcp_event(dev_context, rcp_keycode, rcp_current_key, + event); + } + + rcp_previous_key = rcp_current_key; + rcp_current_key = rcp_keycode; + + return status; +} +#endif + +int init_rcp_input_dev(struct mhl_dev_context *dev_context) +{ + unsigned int i; + struct input_dev *rcp_input_dev; + int ret; + + if (dev_context->rcp_input_dev != NULL) { + MHL_TX_DBG_INFO("RCP input device already exists!\n"); + return 0; + } + + rcp_input_dev = input_allocate_device(); + if (!rcp_input_dev) { + MHL_TX_DBG_ERR("Failed to allocate RCP input device\n"); + return -ENOMEM; + } + + set_bit(EV_KEY, rcp_input_dev->evbit); + + rcp_input_dev->name = "MHL Remote Control"; + rcp_input_dev->keycode = rcp_def_keymap; + rcp_input_dev->keycodesize = sizeof(u16); + rcp_input_dev->keycodemax = ARRAY_SIZE(rcp_def_keymap); + + for (i = 0; i < ARRAY_SIZE(rcp_def_keymap); i++) { +#ifdef OLD_KEYMAP_TABLE + u16 keycode = rcp_def_keymap[i]; +#else + u16 keycode = rcpSupportTable[i].map[0]; + rcp_def_keymap[i] = keycode; +#endif + if (keycode != KEY_UNKNOWN && keycode != KEY_RESERVED) + set_bit(keycode, rcp_input_dev->keybit); + } + + rcp_input_dev->id.bustype = BUS_VIRTUAL; + + ret = input_register_device(rcp_input_dev); + if (ret) { + MHL_TX_DBG_ERR("Failed to register device\n"); + input_free_device(rcp_input_dev); + return ret; + } + ret = mhl_tx_create_timer(dev_context, + timer_callback_T_press_mode_handler, + dev_context, &dev_context->timer_T_press_mode); + if (ret != 0) { + MHL_TX_DBG_ERR("failed in created timer_T_press_mode!\n"); + } else { + ret = mhl_tx_create_timer(dev_context, + timer_callback_T_hold_maintain_handler, + dev_context, &dev_context->timer_T_hold_maintain); + if (ret != 0) { + MHL_TX_DBG_ERR + ("failed to create timer_T_hold_maintain!\n"); + } else { + MHL_TX_DBG_INFO("device created\n"); + dev_context->rcp_input_dev = rcp_input_dev; + return 0; + } + mhl_tx_delete_timer(dev_context, + &dev_context->timer_T_press_mode); + } + return ret; +} + +void destroy_rcp_input_dev(struct mhl_dev_context *dev_context) +{ + if (dev_context->timer_T_press_mode) { + mhl_tx_delete_timer(dev_context, + &dev_context->timer_T_press_mode); + } + if (dev_context->timer_T_hold_maintain) { + mhl_tx_delete_timer(dev_context, + &dev_context->timer_T_hold_maintain); + } + if (dev_context->rcp_input_dev) { + input_unregister_device(dev_context->rcp_input_dev); + dev_context->rcp_input_dev = NULL; + } +} + +void rcp_input_dev_one_time_init(struct mhl_dev_context *dev_context) +{ + int i; + for (i = 0; i < MHL_NUM_RCP_KEY_CODES; ++i) + rcp_def_keymap[i] = rcpSupportTable[i].map[0]; +} diff --git a/drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.h b/drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.h new file mode 100644 index 000000000000..c4b8ffc1ee73 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef _MHL_RCP_INPUTDEV_H_ +#define _MHL_RCP_INPUTDEV_H_ + +struct mhl_dev_context; + +struct rcp_keymap_t { + unsigned multicode:1; + unsigned press_and_hold_key:1; + unsigned reserved:6; + uint16_t map[2]; + uint8_t rcp_support; +}; + +#define MHL_LOGICAL_DEVICE_MAP (MHL_DEV_LD_AUDIO | MHL_DEV_LD_VIDEO | \ + MHL_DEV_LD_MEDIA | MHL_DEV_LD_GUI) + +#define MHL_NUM_RCP_KEY_CODES 0x80 +extern struct rcp_keymap_t rcpSupportTable[MHL_NUM_RCP_KEY_CODES]; + +int generate_rcp_input_event(struct mhl_dev_context *dev_context, + uint8_t rcp_keycode); + +int init_rcp_input_dev(struct mhl_dev_context *dev_context); +void destroy_rcp_input_dev(struct mhl_dev_context *dev_context); +void rcp_input_dev_one_time_init(struct mhl_dev_context *dev_context); + +#endif /* #ifndef _MHL_RCP_INPUTDEV_H_ */ diff --git a/drivers/video/fbdev/msm/mhl3/mhl_supp.c b/drivers/video/fbdev/msm/mhl3/mhl_supp.c new file mode 100644 index 000000000000..133c6f2d411a --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_supp.c @@ -0,0 +1,4700 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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_8620_internal_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 "si_app_devcap.h" +#include "platform.h" +#include "si_mhl_callback_api.h" +#include "si_8620_drv.h" + +/* + We choose 150 ms as the sample period due to hardware + taking 140 ms to distinguish between an error + and a disconnection. + We will keep the last three samples and discard + most recent two. +*/ +#define LOCAL_eCBUS_ERR_SAMPLE_PERIOD 150 + +static void si_mhl_tx_refresh_peer_devcap_entries_impl( + struct mhl_dev_context *dev_context, const char *function, int line); +#define si_mhl_tx_refresh_peer_devcap_entries(context) \ + si_mhl_tx_refresh_peer_devcap_entries_impl(context, __func__, __LINE__) + +static void cbus_abort_timer_callback(void *callback_param); +static void bist_timer_callback(void *callback_param); +static void cbus_dcap_rdy_timeout_callback(void *callback_param); +static void cbus_dcap_chg_timeout_callback(void *callback_param); +static void cbus_mode_up_timeout_callback(void *callback_param); + +uint16_t plim_table[] = { + 500, + 900, + 1500, + 100, + 2000, + 0, + 0, + 0 +}; +#ifdef DEBUG +static char *get_cbus_command_string(int command) +{ +#define CBUS_COMMAND_CASE(command) case command: return #command; + switch (command) { + CBUS_COMMAND_CASE(MHL_ACK) + CBUS_COMMAND_CASE(MHL_NACK) + CBUS_COMMAND_CASE(MHL_ABORT) + CBUS_COMMAND_CASE(MHL_WRITE_STAT) + CBUS_COMMAND_CASE(MHL_SET_INT) + CBUS_COMMAND_CASE(MHL_READ_DEVCAP_REG) + CBUS_COMMAND_CASE(MHL_READ_XDEVCAP_REG) + CBUS_COMMAND_CASE(MHL_GET_STATE) + CBUS_COMMAND_CASE(MHL_GET_VENDOR_ID) + CBUS_COMMAND_CASE(MHL_SET_HPD) + CBUS_COMMAND_CASE(MHL_CLR_HPD) + CBUS_COMMAND_CASE(MHL_SET_CAP_ID) + CBUS_COMMAND_CASE(MHL_GET_CAP_ID) + CBUS_COMMAND_CASE(MHL_MSC_MSG) + CBUS_COMMAND_CASE(MHL_GET_SC1_ERRORCODE) + CBUS_COMMAND_CASE(MHL_GET_DDC_ERRORCODE) + CBUS_COMMAND_CASE(MHL_GET_MSC_ERRORCODE) + CBUS_COMMAND_CASE(MHL_WRITE_BURST) + CBUS_COMMAND_CASE(MHL_GET_SC3_ERRORCODE) + CBUS_COMMAND_CASE(MHL_WRITE_XSTAT) + CBUS_COMMAND_CASE(MHL_READ_DEVCAP) + CBUS_COMMAND_CASE(MHL_READ_XDEVCAP) + CBUS_COMMAND_CASE(MHL_READ_EDID_BLOCK) + CBUS_COMMAND_CASE(MHL_SEND_3D_REQ_OR_FEAT_REQ) + } + return "unknown"; +} +#else +#define get_cbus_command_string(command) "" +#endif + +static char *rapk_error_code_string[] = { + "NO_ERROR", + "UNRECOGNIZED_ACTION_CODE", + "UNSUPPORTED_ACTION_CODE", + "RESPONDER_BUSY" +}; + +struct mhl_dev_context *get_mhl_device_context(void *context) +{ + struct mhl_dev_context *dev_context = context; + + if (dev_context->signature != MHL_DEV_CONTEXT_SIGNATURE) + dev_context = container_of(context, + struct mhl_dev_context, drv_context); + return dev_context; +} + +void init_cbus_queue(struct mhl_dev_context *dev_context) +{ + struct cbus_req *entry; + int idx; + + INIT_LIST_HEAD(&dev_context->cbus_queue); + INIT_LIST_HEAD(&dev_context->cbus_free_list); + + dev_context->current_cbus_req = NULL; + + /* Place pre-allocated CBUS queue entries on the free list */ + for (idx = 0; idx < NUM_CBUS_EVENT_QUEUE_EVENTS; idx++) { + + entry = &dev_context->cbus_req_entries[idx]; + memset(entry, 0, sizeof(struct cbus_req)); + list_add(&entry->link, &dev_context->cbus_free_list); + } +} + +static struct cbus_req *get_free_cbus_queue_entry_impl( + struct mhl_dev_context *dev_context, const char *function, int line) +{ + struct cbus_req *req; + struct list_head *entry; + + if (list_empty(&dev_context->cbus_free_list)) { + int i; + MHL_TX_GENERIC_DBG_PRINT(-1, + "No free cbus queue entries available %s:%d\n", + function, line); + list_for_each(entry, &dev_context->cbus_queue) { + req = list_entry(entry, struct cbus_req, link); + MHL_TX_GENERIC_DBG_PRINT(-1, + "cbus_queue entry %d called from %s:%d\n\t%s " + "0x%02x 0x%02x\n", + req->sequence, req->function, req->line, + get_cbus_command_string(req->command), + req->reg, req->reg_data); + } + for (i = 0; i < ARRAY_SIZE(dev_context->cbus_req_entries); + ++i) { + req = &dev_context->cbus_req_entries[i]; + MHL_TX_GENERIC_DBG_PRINT(-1, + "%d cbus_req_entries[%d] called from %s:%d\n", + req->sequence, i, req->function, req->line); + } + return NULL; + } + + entry = dev_context->cbus_free_list.next; + list_del(entry); + req = list_entry(entry, struct cbus_req, link); + + /* Start clean */ + req->status.flags.cancel = 0; + req->completion = NULL; + + req->function = function; + req->line = line; + req->sequence = dev_context->sequence++; + /*MHL_TX_DBG_ERR(,"q %d get:0x%p %s:%d\n", + req->sequence,req,function,line); */ + return req; +} + +#define get_free_cbus_queue_entry(context) \ + get_free_cbus_queue_entry_impl(context, __func__, __LINE__) + +static void return_cbus_queue_entry_impl(struct mhl_dev_context *dev_context, + struct cbus_req *pReq, + const char *function, int line) +{ + /* MHL_TX_DBG_ERR(,"q ret:0x%p %s:%d\n",pReq,function,line); */ + list_add(&pReq->link, &dev_context->cbus_free_list); + +} + +#define return_cbus_queue_entry(context, req) \ + return_cbus_queue_entry_impl(context, req, __func__, __LINE__) + +void queue_cbus_transaction(struct mhl_dev_context *dev_context, + struct cbus_req *pReq) +{ + MHL_TX_DBG_INFO("0x%02x 0x%02x 0x%02x\n", + pReq->command, + (MHL_MSC_MSG == pReq->command) ? + pReq->msg_data[0] : pReq->reg, + (MHL_MSC_MSG == pReq->command) ? + pReq->msg_data[1] : pReq->reg_data); + + list_add_tail(&pReq->link, &dev_context->cbus_queue); + /* try to send immediately, if possible */ + si_mhl_tx_drive_states(dev_context); +} + +void queue_priority_cbus_transaction(struct mhl_dev_context *dev_context, + struct cbus_req *req) +{ + MHL_TX_DBG_INFO("0x%02x 0x%02x 0x%02x\n", + req->command, + (MHL_MSC_MSG == req->command) ? + req->msg_data[0] : req->reg, + (MHL_MSC_MSG == req->command) ? + req->msg_data[1] : req->reg_data); + + list_add(&req->link, &dev_context->cbus_queue); +} + +struct cbus_req *peek_next_cbus_transaction(struct mhl_dev_context *dev_context) +{ + struct list_head *entry; + struct cbus_req *req; + if (list_empty(&dev_context->cbus_queue)) { + MHL_TX_DBG_INFO("Queue empty\n"); + return NULL; + } + entry = dev_context->cbus_queue.next; + req = list_entry(entry, struct cbus_req, link); + return req; +} + +struct cbus_req *get_next_cbus_transaction(struct mhl_dev_context *dev_context) +{ + struct cbus_req *req; + struct list_head *entry; + enum cbus_mode_e cbus_mode; + + if (list_empty(&dev_context->cbus_queue)) { + MHL_TX_DBG_INFO("Queue empty\n"); + return NULL; + } + + if (dev_context->misc_flags.flags.cbus_abort_delay_active) { + MHL_TX_DBG_INFO("CBUS abort delay in progress " + "can't send any messages\n"); + return NULL; + } + + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + switch (cbus_mode) { + case CM_NO_CONNECTION: + case CM_TRANSITIONAL_TO_eCBUS_S_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S: + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + case CM_TRANSITIONAL_TO_eCBUS_D_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D: + case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: + case CM_eCBUS_S_BIST: + case CM_eCBUS_D_BIST: + case CM_BIST_DONE_PENDING_DISCONNECT: + MHL_TX_DBG_INFO("CBUS not available\n"); + return NULL; + default: + break; + } + + entry = dev_context->cbus_queue.next; + req = list_entry(entry, struct cbus_req, link); + + list_del(entry); + + MHL_TX_DBG_INFO("0x%02x 0x%02x 0x%02x\n", + req->command, + (MHL_MSC_MSG == req->command) ? + req->msg_data[0] : req->reg, + (MHL_MSC_MSG == req->command) ? + req->msg_data[1] : req->reg_data); + + return req; +} + +#ifdef DEBUG +static char *get_block_id_string(int id) +{ +#define BURST_ID_CASE(id) case id: return #id; + switch (id) { + BURST_ID_CASE(MHL_TEST_ADOPTER_ID) + BURST_ID_CASE(burst_id_3D_VIC) + BURST_ID_CASE(burst_id_3D_DTD) + BURST_ID_CASE(burst_id_HEV_VIC) + BURST_ID_CASE(burst_id_HEV_DTDA) + BURST_ID_CASE(burst_id_HEV_DTDB) + BURST_ID_CASE(burst_id_VC_ASSIGN) + BURST_ID_CASE(burst_id_VC_CONFIRM) + BURST_ID_CASE(burst_id_AUD_DELAY) + BURST_ID_CASE(burst_id_ADT_BURSTID) + BURST_ID_CASE(burst_id_BIST_SETUP) + BURST_ID_CASE(burst_id_BIST_RETURN_STAT) + BURST_ID_CASE(burst_id_EMSC_SUPPORT) + BURST_ID_CASE(burst_id_HID_PAYLOAD) + BURST_ID_CASE(burst_id_BLK_RCV_BUFFER_INFO) + BURST_ID_CASE(burst_id_BITS_PER_PIXEL_FMT) + BURST_ID_CASE(LOCAL_ADOPTER_ID) + } + return "unknown"; +} +#else +#define get_block_id_string(command) "" +#endif + +static struct block_req *start_new_block_marshalling_req_impl( + struct mhl_dev_context *dev_context, const char *function, int line) +{ + struct block_req *req; + struct list_head *entry; + union SI_PACK_THIS_STRUCT emsc_payload_t *payload; + + if (list_empty(&dev_context->block_protocol.free_list)) { + int i; + MHL_TX_DBG_ERR("No free block queue entries available %s:%d\n", + function, line); + list_for_each(entry, &dev_context->block_protocol.queue) { + req = list_entry(entry, struct block_req, link); + MHL_TX_DBG_ERR("block_protocol.queue entry %d called " + "from %s:%d\n\t%s 0x%04x\n", + req->sequence, req->function, req->line, + get_block_id_string(BURST_ID(req->payload-> + hdr_and_burst_id.burst_id)), + BURST_ID(req->payload-> + hdr_and_burst_id.burst_id)); + } + for (i = 0; + i < ARRAY_SIZE(dev_context->block_protocol.req_entries); + ++i) { + req = &dev_context->block_protocol.req_entries[i]; + MHL_TX_DBG_ERR("%d block_protocol.req_entries[%d] " + "called from %s:%d\n", req->sequence, i, + req->function, req->line); + } + return NULL; + } + + entry = dev_context->block_protocol.free_list.next; + list_del(entry); + req = list_entry(entry, struct block_req, link); + + payload = req->payload; + req->function = function; + req->line = line; + req->sequence = dev_context->block_protocol.sequence++; + req->sub_payload_size = 0; + req->space_remaining = + sizeof(payload->as_bytes) - + sizeof(struct SI_PACK_THIS_STRUCT standard_transport_header_t); + dev_context->block_protocol.marshalling_req = req; + MHL_TX_DBG_WARN("q %d get:0x%p %s:%d\n", req->sequence, req, function, + line); + return req; +} + +#define start_new_block_marshalling_req(context) \ + start_new_block_marshalling_req_impl(context, __func__, __LINE__) + +static void return_block_queue_entry_impl(struct mhl_dev_context *dev_context, + struct block_req *pReq, + const char *function, int line) +{ + /* MHL_TX_DBG_ERR(,"q ret:0x%p %s:%d\n",pReq,function,line); */ + list_add(&pReq->link, &dev_context->block_protocol.free_list); + +} + +#define return_block_queue_entry(context, req) \ + return_block_queue_entry_impl(context, req, __func__, __LINE__) + +struct block_req *get_next_block_transaction(struct mhl_dev_context + *dev_context) +{ + struct block_req *req; + struct list_head *entry; + + if (list_empty(&dev_context->block_protocol.queue)) { + MHL_TX_DBG_INFO("Queue empty\n"); + return NULL; + } + + entry = dev_context->block_protocol.queue.next; + list_del(entry); + req = list_entry(entry, struct block_req, link); + + MHL_TX_DBG_INFO("0x%04x\n", req->payload->hdr_and_burst_id.burst_id); + + return req; +} + +void si_mhl_tx_push_block_transactions(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)(&dev_context->drv_context); + struct block_req *req; + struct list_head *entry; + uint16_t ack_byte_count; + + /* + Send the requests out, starting with those in the queue + */ + ack_byte_count = hw_context->block_protocol.received_byte_count; + req = dev_context->block_protocol.marshalling_req; + if (NULL == req) { + MHL_TX_DBG_ERR("%s wayward pointer%s\n", ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + return; + } + /* Need to send the unload count even if no other payload. */ + /* If there is no payload and 2 or less unload bytes, don't bother -- + * they will be unloaded with the next payload write. + * This could violate the MHL3 eMSC block transfer protocol requirement + * to ACK within 50ms, but it can also cause an CK feedback loop + * between the two peer devices. If the unload count is larger, + * go ahead and send it even if it does cause an extra response + * from the other side. + */ + if ((ack_byte_count > EMSC_BLK_STD_HDR_LEN) || req->sub_payload_size) { + /* don't use queue_block_transaction here */ + list_add_tail(&req->link, &dev_context->block_protocol.queue); + dev_context->block_protocol.marshalling_req = NULL; + } + + while (!list_empty(&dev_context->block_protocol.queue)) { + uint16_t payload_size; + entry = dev_context->block_protocol.queue.next; + req = list_entry(entry, struct block_req, link); + payload_size = sizeof(req->payload->hdr_and_burst_id.tport_hdr) + + req->sub_payload_size; + + MHL_TX_DBG_INFO( + "=== sub_payload_size: %d, ack count: %d\n", + req->sub_payload_size, + hw_context->block_protocol.received_byte_count); + + if (hw_context->block_protocol.peer_blk_rx_buf_avail < + payload_size) { + /* not enough space in peer's receive buffer, + so wait to send until later + */ + MHL_TX_DBG_ERR("==== not enough space in peer's " + "receive buffer, send later payload_size:0x%x," + "blk_rx_buffer_avail:0x%x sub-payload size:" + "0x%x tport_hdr size:0x%x\n", + payload_size, + hw_context->block_protocol. + peer_blk_rx_buf_avail, + req->sub_payload_size, + sizeof(req->payload-> + hdr_and_burst_id.tport_hdr)); + break; + } + list_del(entry); + hw_context->block_protocol.peer_blk_rx_buf_avail -= + payload_size; + MHL_TX_DBG_WARN("PEER Buffer Available After Write: %d\n", + hw_context->block_protocol. + peer_blk_rx_buf_avail); + + req->payload->hdr_and_burst_id.tport_hdr.length_remaining = + req->sub_payload_size; + req->count = payload_size; + /* The driver layer will fill in the rx_unload_ack field */ + mhl_tx_drv_send_block((struct drv_hw_context *) + (&dev_context->drv_context), req); + /* return request to free list */ + return_block_queue_entry(dev_context, req); + + } + if (NULL == dev_context->block_protocol.marshalling_req) { + /* now start a new marshalling request */ + req = start_new_block_marshalling_req(dev_context); + if (NULL == req) { + MHL_TX_DBG_ERR("%sblock free list exhausted!%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + } +} + +void *si_mhl_tx_get_sub_payload_buffer(struct mhl_dev_context *dev_context, + uint8_t size) +{ + void *buffer; + struct block_req *req; + union emsc_payload_t *payload; + req = dev_context->block_protocol.marshalling_req; + if (NULL == req) { + /* this can only happen if we run out of free requests */ + /* TODO: Lee - can't call this here, because the first thing + * it does is check to see if + * dev_context->block_protocol.marshalling_req == NULL, + * which we know it is, and if it finds NULL, it prints an + * error and returns. So why bother? + */ + si_mhl_tx_push_block_transactions(dev_context); + req = dev_context->block_protocol.marshalling_req; + if (NULL == req) { + MHL_TX_DBG_ERR("%sblock free list exhausted!%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return NULL; + } + } + if (size > req->space_remaining) { + MHL_TX_DBG_INFO("0x%04x\n", + req->payload->hdr_and_burst_id.burst_id); + + list_add_tail(&req->link, &dev_context->block_protocol.queue); + si_mhl_tx_push_block_transactions(dev_context); + + req = start_new_block_marshalling_req(dev_context); + if (NULL == req) { + MHL_TX_DBG_ERR("%sblock free list exhausted!%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return NULL; + } + } + if (size > EMSC_BLK_CMD_MAX_LEN) + return NULL; + + payload = req->payload; + buffer = &payload->as_bytes[sizeof(payload->as_bytes) - + req->space_remaining]; + req->space_remaining -= size; + req->sub_payload_size += size; + return buffer; +} + +/* + * Send the BLK_RCV_BUFFER_INFO BLOCK message. This must be the first BLOCK + * message sent, but we will wait until the XDEVCAPs have been read to allow + * time for each side to initialize their eMSC message handling. + */ + +void si_mhl_tx_send_blk_rcv_buf_info(struct mhl_dev_context *context) +{ + uint16_t rcv_buffer_size; + struct SI_PACK_THIS_STRUCT block_rcv_buffer_info_t *buf_info; + struct SI_PACK_THIS_STRUCT MHL3_emsc_support_data_t *emsc_supp; + size_t total_size; + + total_size = sizeof(*buf_info) + + sizeof(*emsc_supp) + - sizeof(emsc_supp->payload.burst_ids) + + sizeof(emsc_supp->payload.burst_ids[0]); + + buf_info = si_mhl_tx_get_sub_payload_buffer(context, total_size); + if (NULL == buf_info) { + MHL_TX_DBG_ERR("%ssi_mhl_tx_get_sub_payload_buffer failed%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } else { + /* next byte after blk_rcv_buf_info */ + emsc_supp = + (struct SI_PACK_THIS_STRUCT MHL3_emsc_support_data_t *) + (buf_info + 1); + buf_info->burst_id.low = + (uint8_t)(burst_id_BLK_RCV_BUFFER_INFO & 0xFF); + buf_info->burst_id.high = + (uint8_t)(burst_id_BLK_RCV_BUFFER_INFO >> 8); + + rcv_buffer_size = si_mhl_tx_drv_get_blk_rcv_buf_size(); + buf_info->blk_rcv_buffer_size_low = + (uint8_t)(rcv_buffer_size & 0xFF); + buf_info->blk_rcv_buffer_size_high = + (uint8_t)(rcv_buffer_size >> 8); + + emsc_supp->header.burst_id.high = (uint8_t) + (burst_id_EMSC_SUPPORT >> 8); + emsc_supp->header.burst_id.low = (uint8_t) + (burst_id_EMSC_SUPPORT & 0xFF); + emsc_supp->header.checksum = 0; + emsc_supp->header.total_entries = 1; + emsc_supp->header.sequence_index = 1; + emsc_supp->num_entries_this_burst = 1; + emsc_supp->payload.burst_ids[0].high = (uint8_t) + (SILICON_IMAGE_ADOPTER_ID >> 8); + emsc_supp->payload.burst_ids[0].low = (uint8_t) + (SILICON_IMAGE_ADOPTER_ID & 0xFF); + + emsc_supp->header.checksum = + calculate_generic_checksum(emsc_supp, 0, + total_size - sizeof(*buf_info)); + + MHL_TX_DBG_INFO( + "blk_rcv_buffer_info: id:0x%02X%02X\n" + " sz: 0x%02X%02X\n" + " emsc: 0x%02X%02X\n" + " emsc- cksum: 0x%02X\n" + " emsc- tot_ent: 0x%02X\n" + " emsc- seq_idx: 0x%02X\n" + " emsc-this_bst: 0x%02X\n" + " emsc- high: 0x%02X\n" + " emsc- low: 0x%02X\n", + buf_info->burst_id.high, + buf_info->burst_id.low, + buf_info->blk_rcv_buffer_size_high, + buf_info->blk_rcv_buffer_size_low, + emsc_supp->header.burst_id.high, + emsc_supp->header.burst_id.low, + emsc_supp->header.checksum, + emsc_supp->header.total_entries, + emsc_supp->header.sequence_index, + emsc_supp->num_entries_this_burst, + emsc_supp->payload.burst_ids[0].high, + emsc_supp->payload.burst_ids[0].low); + } + +} + +void si_mhl_tx_initialize_block_transport(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)(&dev_context->drv_context); + struct block_req *req; + uint8_t buffer_size; + int idx; + struct block_buffer_info_t block_buffer_info; + + si_mhl_tx_platform_get_block_buffer_info(&block_buffer_info); + + hw_context->block_protocol.peer_blk_rx_buf_avail = + EMSC_RCV_BUFFER_DEFAULT; + hw_context->block_protocol.peer_blk_rx_buf_max = + EMSC_RCV_BUFFER_DEFAULT; + + INIT_LIST_HEAD(&dev_context->block_protocol.queue); + INIT_LIST_HEAD(&dev_context->block_protocol.free_list); + + /* Place pre-allocated BLOCK queue entries on the free list */ + for (idx = 0; idx < NUM_BLOCK_QUEUE_REQUESTS; idx++) { + + req = &dev_context->block_protocol.req_entries[idx]; + memset(req, 0, sizeof(*req)); + req->platform_header = + block_buffer_info.buffer + + block_buffer_info.req_size * idx; + req->payload = (union SI_PACK_THIS_STRUCT emsc_payload_t *) + (req->platform_header + + block_buffer_info.payload_offset); + list_add(&req->link, &dev_context->block_protocol.free_list); + } + buffer_size = + sizeof(struct SI_PACK_THIS_STRUCT standard_transport_header_t) + + sizeof(struct SI_PACK_THIS_STRUCT block_rcv_buffer_info_t); + if (buffer_size != 6) { + MHL_TX_DBG_ERR("%scheck structure packing%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + /* we just initialized the free list, this call cannot fail */ + start_new_block_marshalling_req(dev_context); + +} + +int si_mhl_tx_get_num_block_reqs(void) +{ + return NUM_BLOCK_QUEUE_REQUESTS; +} + +uint8_t si_get_peer_mhl_version(struct mhl_dev_context *dev_context) +{ + uint8_t ret_val = dev_context->dev_cap_cache.mdc.mhl_version; + + if (0 == dev_context->dev_cap_cache.mdc.mhl_version) { + /* If we come here it means we have not read devcap and + * VERSION_STAT must have placed the version asynchronously + * in peer_mhl3_version + */ + ret_val = dev_context->peer_mhl3_version; + } + return ret_val; +} + +uint8_t calculate_generic_checksum(void *info_frame_data_parm, uint8_t checksum, + uint8_t length) +{ + uint8_t i; + uint8_t *info_frame_data = (uint8_t *) info_frame_data_parm; + + for (i = 0; i < length; i++) + checksum += info_frame_data[i]; + + checksum = 0x100 - checksum; + + return checksum; +} + +static struct cbus_req *write_stat_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1); +/* + * si_mhl_tx_set_status + * + * Set MHL defined STATUS bits in peer's register set. + * + * xstat true for XSTATUS bits + * register MHL register to write + * value data to write to the register + */ +bool si_mhl_tx_set_status(struct mhl_dev_context *dev_context, + bool xstat, uint8_t reg_to_write, uint8_t value) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO("called\n"); + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + if (xstat) + req->command = MHL_WRITE_XSTAT; + else { + req->command = MHL_WRITE_STAT; + req->completion = write_stat_done; + } + + req->reg = reg_to_write; + req->reg_data = value; + + queue_cbus_transaction(dev_context, req); + + return true; +} + +/* + * si_mhl_tx_send_3d_req_hawb + * Send SET_INT(3D_REQ) as an atomic command. + * completion is defined as finishing the 3D_DTD/3D_REQ + * This function returns true if operation was successfully performed. + * + */ +bool si_mhl_tx_send_3d_req_or_feat_req(struct mhl_dev_context *dev_context) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO + ("%sQueue 3D_REQ(MHL2.x) or FEAT_REQ(MHL 3.x) %s. MHL %02x\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT, + si_get_peer_mhl_version(dev_context)); + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + req->command = MHL_SEND_3D_REQ_OR_FEAT_REQ; + req->reg = MHL_RCHANGE_INT; + if (si_get_peer_mhl_version(dev_context) >= 0x30) { + req->reg_data = MHL3_INT_FEAT_REQ; + } else if (si_get_peer_mhl_version(dev_context) >= 0x20) { + req->reg_data = MHL2_INT_3D_REQ; + } else { + /* Code must not come here. This is just a trap so look + * for the following message in log + */ + MHL_TX_DBG_ERR("%sMHL 1 does not support 3D\n"); + return false; + } + queue_cbus_transaction(dev_context, req); + return true; +} + +static struct cbus_req *set_int_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1); +/* + * si_mhl_tx_set_int + * Set MHL defined INTERRUPT bits in peer's register set. + * This function returns true if operation was successfully performed. + * + * regToWrite Remote interrupt register to write + * mask the bits to write to that register + * + * priority 0: add to head of CBusQueue + * 1: add to tail of CBusQueue + */ +bool si_mhl_tx_set_int(struct mhl_dev_context *dev_context, + uint8_t reg_to_write, uint8_t mask, + uint8_t priority_level) +{ + struct cbus_req *req; + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + req->command = MHL_SET_INT; + req->reg = reg_to_write; + req->reg_data = mask; + req->completion = set_int_done; + + if (priority_level) + queue_cbus_transaction(dev_context, req); + else + queue_priority_cbus_transaction(dev_context, req); + + return true; +} + +static struct cbus_req *write_burst_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1); + +bool si_mhl_tx_send_write_burst(struct mhl_dev_context *dev_context, + void *buffer) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO("%sQueue WRITE_BURST%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 1; + req->command = MHL_WRITE_BURST; + req->length = MHL_SCRATCHPAD_SIZE; + req->burst_offset = 0; + req->completion = write_burst_done; + memcpy(req->msg_data, buffer, MHL_SCRATCHPAD_SIZE); + + queue_cbus_transaction(dev_context, req); + return true; +} + +static void si_mhl_tx_reset_states(struct mhl_dev_context *dev_context) +{ + /* + * Make sure that these timers do not start prematurely + */ + MHL_TX_DBG_INFO("stopping timers for DCAP_RDY and DCAP_CHG\n"); + mhl_tx_stop_timer(dev_context, dev_context->dcap_rdy_timer); + mhl_tx_stop_timer(dev_context, dev_context->dcap_chg_timer); + mhl_tx_stop_timer(dev_context, dev_context->t_rap_max_timer); + + /* + * Make sure that this timer does not start prematurely + */ + MHL_TX_DBG_INFO("stopping timer for CBUS_MODE_UP\n"); + mhl_tx_stop_timer(dev_context, dev_context->cbus_mode_up_timer); + + init_cbus_queue(dev_context); + + dev_context->mhl_connection_event = false; + dev_context->edid_valid = false; + dev_context->mhl_connected = MHL_TX_EVENT_DISCONNECTION; + + dev_context->msc_msg_arrived = false; + dev_context->status_0 = 0; + dev_context->status_1 = 0; + dev_context->link_mode = MHL_STATUS_CLK_MODE_NORMAL; + /* dev_context->preferred_clk_mode can be overridden by the application + * calling si_mhl_tx_set_preferred_pixel_format() + */ + dev_context->preferred_clk_mode = MHL_STATUS_CLK_MODE_NORMAL; + { + /* preserve BIST role as DUT or TE over disconnection + */ + bool temp = dev_context->misc_flags.flags.bist_role_TE; + dev_context->misc_flags.as_uint32 = 0; + dev_context->misc_flags.flags.bist_role_TE = temp ? 1 : 0; + } + dev_context->bist_timeout_total = 0; + +#ifdef MEDIA_DATA_TUNNEL_SUPPORT + memset(dev_context->mdt_devs.is_dev_registered, + INPUT_WAITING_FOR_REGISTRATION, MDT_TYPE_COUNT); + dev_context->mdt_devs.x_max = X_MAX; + dev_context->mdt_devs.x_screen = SCALE_X_SCREEN; + dev_context->mdt_devs.x_raw = SCALE_X_RAW; + dev_context->mdt_devs.x_shift = X_SHIFT; + dev_context->mdt_devs.y_max = Y_MAX; + dev_context->mdt_devs.y_screen = SCALE_Y_SCREEN; + dev_context->mdt_devs.y_raw = SCALE_Y_RAW; + dev_context->mdt_devs.y_shift = Y_SHIFT; + dev_context->mdt_devs.swap_xy = SWAP_XY; + dev_context->mdt_devs.swap_updown = SWAP_UPDOWN; + dev_context->mdt_devs.swap_leftright = SWAP_LEFTRIGHT; +#endif + + dev_context->peer_mhl3_version = 0; + memset(&dev_context->dev_cap_cache, 0, + sizeof(dev_context->dev_cap_cache)); + memset(&dev_context->xdev_cap_cache, 0, + sizeof(dev_context->xdev_cap_cache)); + +#if (INCLUDE_HID == 1) + mhl3_hid_remove_all(dev_context); +#endif + mhl_tx_stop_timer(dev_context, dev_context->bist_timer); +} + +static void t_rap_max_timer_callback(void *callback_param) +{ + struct mhl_dev_context *dev_context = callback_param; + mhl_event_notify(dev_context, MHL_TX_EVENT_T_RAP_MAX_EXPIRED, + 0x00, NULL); +} + +int si_mhl_tx_reserve_resources(struct mhl_dev_context *dev_context) +{ + int ret; + + MHL_TX_DBG_INFO("called\n"); + ret = mhl_tx_create_timer(dev_context, cbus_abort_timer_callback, + dev_context, &dev_context->cbus_abort_timer); + if (ret != 0) { + MHL_TX_DBG_ERR("Failed to allocate CBUS abort timer!\n"); + return ret; + } + + ret = mhl_tx_create_timer(dev_context, bist_timer_callback, + dev_context, &dev_context->bist_timer); + if (ret != 0) { + MHL_TX_DBG_ERR("Failed to allocate BIST timer!\n"); + return ret; + } + + ret = + mhl_tx_create_timer(dev_context, cbus_dcap_rdy_timeout_callback, + dev_context, + &dev_context->dcap_rdy_timer); + if (ret != 0) { + MHL_TX_DBG_ERR("Failed to allocate dcap_rdy timeout timer!\n"); + return ret; + } + ret = + mhl_tx_create_timer(dev_context, cbus_dcap_chg_timeout_callback, + dev_context, + &dev_context->dcap_chg_timer); + if (ret != 0) { + MHL_TX_DBG_ERR("Failed to allocate dcap_chg timeout timer!\n"); + return ret; + } + ret = mhl_tx_create_timer(dev_context, cbus_mode_up_timeout_callback, + dev_context, + &dev_context->cbus_mode_up_timer); + if (ret != 0) { + MHL_TX_DBG_ERR + ("Failed to allocate cbus_mode_up timeout timer!\n"); + return ret; + } + ret = mhl_tx_create_timer(dev_context, t_rap_max_timer_callback, + dev_context, + &dev_context->t_rap_max_timer); + if (ret != 0) { + MHL_TX_DBG_ERR + ("Failed to allocate cbus_mode_up timeout timer!\n"); + return ret; + } + return ret; +} + +int si_mhl_tx_initialize(struct mhl_dev_context *dev_context) +{ + MHL_TX_DBG_INFO("called\n"); + + si_mhl_tx_reset_states(dev_context); + + dev_context->bist_setup.t_bist_mode_down = T_BIST_MODE_DOWN_MAX; + + return dev_context->drv_info->mhl_device_initialize( + (struct drv_hw_context *) + (&dev_context->drv_context)); + +} + +static void cbus_abort_timer_callback(void *callback_param) +{ + struct mhl_dev_context *dev_context = callback_param; + + MHL_TX_DBG_INFO("CBUS abort timer expired, enable CBUS messaging\n"); + dev_context->misc_flags.flags.cbus_abort_delay_active = false; + si_mhl_tx_drive_states(dev_context); +} + +void si_mhl_tx_bist_cleanup(struct mhl_dev_context *dev_context) +{ + mhl_tx_stop_timer(dev_context, dev_context->bist_timer); + MHL_TX_DBG_ERR("BIST duration elapsed\n"); + msleep(dev_context->bist_setup.t_bist_mode_down); + if (BIST_TRIGGER_ECBUS_TX_RX_MASK & + dev_context->bist_trigger_info) { + si_mhl_tx_drv_stop_ecbus_bist((struct drv_hw_context *) + &dev_context->drv_context, + &dev_context->bist_setup); + } + + if (BIST_TRIGGER_ECBUS_AV_LINK_MASK & + dev_context->bist_trigger_info) { + si_mhl_tx_drv_stop_avlink_bist( + (struct drv_hw_context *)&dev_context->drv_context); + dev_context->bist_stat.avlink_stat = 0; + } + + if (BIST_TRIGGER_IMPEDANCE_TEST & + dev_context->bist_trigger_info) { + si_mhl_tx_drv_stop_impedance_bist((struct drv_hw_context *) + &dev_context->drv_context, + &dev_context->bist_setup); + } else { + + enum cbus_mode_e cbus_mode; + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + if (cbus_mode > CM_oCBUS_PEER_IS_MHL3) { + /* allow the other end some time to + inspect their error count registers */ + MHL_TX_DBG_ERR("T_bist_mode_down\n") + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *)&dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_STAT); + } + } + mhl_event_notify(dev_context, MHL_TX_EVENT_BIST_TEST_DONE, 0x00, NULL); + si_mhl_tx_drive_states(dev_context); +} + +static void bist_timer_callback(void *callback_param) +{ + struct mhl_dev_context *dev_context = callback_param; + uint8_t test_sel; + uint32_t bist_timeout_value = dev_context->bist_timeout_value; + uint8_t ecbus_rx_run_done = false; + uint8_t ecbus_tx_run_done = false; + enum cbus_mode_e cbus_mode; + + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + + test_sel = dev_context->bist_trigger_info; + dev_context->bist_timeout_total += LOCAL_eCBUS_ERR_SAMPLE_PERIOD; + + MHL_TX_DBG_INFO("%s\n", si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)) + if (CM_BIST_DONE_PENDING_DISCONNECT == cbus_mode) { + MHL_TX_DBG_ERR("%s Peer disconnected before" + "T_BIST_MODE_DOWN expired%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) + return; + } else if (dev_context->bist_timeout_total < bist_timeout_value) { + int32_t temp; + int32_t err_cnt = -1; + + temp = si_mhl_tx_drv_get_ecbus_bist_status(dev_context, + &ecbus_rx_run_done, &ecbus_tx_run_done); + /* sample the error counter as appropriate */ + if (dev_context->misc_flags.flags.bist_role_TE) { + if (BIST_TRIGGER_E_CBUS_TX & test_sel) { + err_cnt = temp; + MHL_TX_DBG_WARN( + "local eCBUS error count: %d\n", + err_cnt) + } + } else { + if (BIST_TRIGGER_E_CBUS_RX & test_sel) { + err_cnt = temp; + MHL_TX_DBG_WARN( + "local eCBUS error count: %d\n", + err_cnt) + } + } + if (CM_NO_CONNECTION_BIST_STAT == cbus_mode) { + MHL_TX_DBG_ERR("%s Peer disconnected before" + " bist timeout expired%s: %d\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, + dev_context->bist_timeout_total); + ; + } else if (si_mhl_tx_drv_ecbus_connected(dev_context)) { + /* accept the error count + only if we're still connected + + Since we can not distinguish between + "real" errors and errors that happen + as part of a disconnection, we keep track + of the last three results and discard the + two most recent. + */ + mhl_tx_start_timer(dev_context, dev_context->bist_timer, + LOCAL_eCBUS_ERR_SAMPLE_PERIOD); + dev_context->bist_stat.e_cbus_prev_local_stat = + dev_context->bist_stat.e_cbus_local_stat; + dev_context->bist_stat.e_cbus_local_stat = + dev_context->bist_stat.e_cbus_next_local_stat; + dev_context->bist_stat.e_cbus_next_local_stat = err_cnt; + + return; + } + } else if (0 == dev_context->bist_timeout_value) { + MHL_TX_DBG_INFO("infinite duration AV BIST\n") + mhl_tx_start_timer(dev_context, dev_context->bist_timer, + LOCAL_eCBUS_ERR_SAMPLE_PERIOD); + return; + } + si_mhl_tx_bist_cleanup(dev_context); +} + +static void cbus_dcap_rdy_timeout_callback(void *callback_param) +{ + struct mhl_dev_context *dev_context = callback_param; + enum cbus_mode_e cbus_mode; + + MHL_TX_DBG_ERR("%sCBUS DCAP_RDY timer expired%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + mhl_tx_stop_timer(dev_context, dev_context->dcap_rdy_timer); + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + if (CM_oCBUS_PEER_VERSION_PENDING == cbus_mode) { + MHL_TX_DBG_ERR("%s%signoring lack of DCAP_RDY%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_YELLOW_BG, + ANSI_ESC_RESET_TEXT); + /* + Initialize registers to operate in oCBUS mode + */ + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *)&dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL1_2); + + si_mhl_tx_refresh_peer_devcap_entries(dev_context); + si_mhl_tx_drive_states(dev_context); + } +} + +static void cbus_dcap_chg_timeout_callback(void *callback_param) +{ + struct mhl_dev_context *dev_context = callback_param; + enum cbus_mode_e cbus_mode; + + MHL_TX_DBG_ERR("%sCBUS DCAP_CHG timer expired%s\n", ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + mhl_tx_stop_timer(dev_context, dev_context->dcap_chg_timer); + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + if (CM_oCBUS_PEER_IS_MHL1_2 == cbus_mode) { + MHL_TX_DBG_ERR("%s%signoring lack of DCAP_CHG%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_YELLOW_BG, + ANSI_ESC_RESET_TEXT); + si_mhl_tx_refresh_peer_devcap_entries(dev_context); + si_mhl_tx_drive_states(dev_context); + } +} + +static void cbus_mode_up_timeout_callback(void *callback_param) +{ + struct mhl_dev_context *dev_context = callback_param; + bool status; + + MHL_TX_DBG_INFO("CBUS_MODE_UP timer expired\n"); + + status = si_mhl_tx_rap_send(dev_context, MHL_RAP_CBUS_MODE_UP); + if (status) + si_mhl_tx_drive_states(dev_context); +} + +void process_cbus_abort(struct mhl_dev_context *dev_context) +{ + struct cbus_req *req; + + /* + * Place the CBUS message that errored back on + * transmit queue if it has any retries left. + */ + if (dev_context->current_cbus_req != NULL) { + req = dev_context->current_cbus_req; + dev_context->current_cbus_req = NULL; + if (req->retry_count) { + req->retry_count -= 1; + queue_priority_cbus_transaction(dev_context, req); + } else { + return_cbus_queue_entry(dev_context, req); + } + } + + /* Delay the sending of any new CBUS messages for 2 seconds */ + dev_context->misc_flags.flags.cbus_abort_delay_active = true; + + mhl_tx_start_timer(dev_context, dev_context->cbus_abort_timer, 2000); +} + +/* + * si_mhl_tx_drive_states + * + * This function is called by the interrupt handler in the driver layer. + * to move the MSC engine to do the next thing before allowing the application + * to run RCP APIs. + */ +void si_mhl_tx_drive_states(struct mhl_dev_context *dev_context) +{ + struct cbus_req *req; + struct cbus_req *peek; + + MHL_TX_DBG_INFO("\n"); + + peek = peek_next_cbus_transaction(dev_context); + if (NULL == peek) { + /* nothing to send */ + return; + } + switch (si_mhl_tx_drv_get_cbus_mode(dev_context)) { + case CM_NO_CONNECTION: + case CM_TRANSITIONAL_TO_eCBUS_S_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S: + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + case CM_TRANSITIONAL_TO_eCBUS_D_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D: + case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: + case CM_eCBUS_S_BIST: + case CM_eCBUS_D_BIST: + case CM_BIST_DONE_PENDING_DISCONNECT: + MHL_TX_DBG_ERR + ("CBUS transactions forbidden in transitional state" + " command:0x%x\n", peek->command); + return; + default: + break; + } + + req = dev_context->current_cbus_req; + if (req != NULL) { + char *command_string = get_cbus_command_string(req->command); + switch (req->command) { + case MHL_WRITE_BURST: + if (MHL_WRITE_BURST == peek->command) { + /* pending and next transactions + * are both WRITE_BURST + */ + if (si_mhl_tx_drv_hawb_xfifo_avail + (dev_context)) { + /* it's OK to send WRITE_BURSTs */ + break; + } + } + MHL_TX_DBG_INFO("WRITE_BURST in progress\n"); + return; + default: + MHL_TX_DBG_INFO("%s[0x%02x]=0x%02x in progress\n", + command_string, + dev_context->current_cbus_req->reg, + dev_context->current_cbus_req-> + reg_data); + return; + } + } + + if (MHL_WRITE_BURST != peek->command) { + if (si_mhl_tx_drv_get_pending_hawb_write_status(dev_context)) { + /* hawb still pending */ + return; + } + } + + /* process queued CBus transactions */ + req = get_next_cbus_transaction(dev_context); + if (req == NULL) + return; + + MHL_TX_DBG_INFO("req: %p\n", req); + /* coordinate write burst requests and grants. */ + if (MHL_MSC_MSG == req->command) { + dev_context->msc_msg_last_data = req->msg_data[1]; + } + + else if (MHL_SET_INT == req->command) { + if (MHL_RCHANGE_INT == req->reg) { + if (MHL_INT_GRT_WRT == req->reg_data) { + dev_context->misc_flags.flags. + rcv_scratchpad_busy = true; + } + } + } + + MHL_TX_DBG_INFO("req: %p\n", req); + if (req) { + uint8_t ret_val; + dev_context->current_cbus_req = req; + switch (req->command) { + case MHL_WRITE_BURST: + do { + struct cbus_req *next_req; + ret_val = si_mhl_tx_drv_send_cbus_command( + (struct drv_hw_context *) + (&dev_context->drv_context), req); + if (0 == ret_val) { + /* WB XMIT level not available */ + MHL_TX_DBG_INFO("\n"); + break; + } + + next_req = + peek_next_cbus_transaction(dev_context); + + /* process queued CBus transactions */ + if (next_req == NULL) { + MHL_TX_DBG_INFO("\n"); + break; /* out of the do-while loop */ + } + + if (MHL_WRITE_BURST != next_req->command) { + MHL_TX_DBG_INFO("\n"); + break; + } + + next_req = + get_next_cbus_transaction(dev_context); + if (ret_val) { + return_cbus_queue_entry(dev_context, + req); + req = next_req; + dev_context->current_cbus_req = + next_req; + } + + } while (ret_val); + break; + case MHL_MSC_MSG: + if (MHL_MSC_MSG_RAP == req->msg_data[0]) { + MHL_TX_DBG_INFO("sending RAP\n"); + mhl_tx_start_timer(dev_context, + dev_context->t_rap_max_timer, 1000); + } + goto case_default; + + default: +case_default: + ret_val = si_mhl_tx_drv_send_cbus_command( + (struct drv_hw_context *) + (&dev_context->drv_context), req); + + if (ret_val) { + MHL_TX_DBG_INFO("current command: %s0x%02x%s\n", + ANSI_ESC_YELLOW_TEXT, ret_val, + ANSI_ESC_RESET_TEXT); + req->command = ret_val; + } else { + return_cbus_queue_entry(dev_context, req); + dev_context->current_cbus_req = NULL; + if (MHL_READ_EDID_BLOCK == req->command) { + dev_context->misc_flags.flags. + edid_loop_active = 0; + MHL_TX_DBG_INFO + ("tag: EDID active: %d\n", + dev_context->misc_flags.flags. + edid_loop_active); + } + } + break; + } + } +} + +enum scratch_pad_status si_mhl_tx_request_write_burst(struct mhl_dev_context + *dev_context, + uint8_t burst_offset, + uint8_t length, + uint8_t *data) +{ + enum scratch_pad_status status = SCRATCHPAD_BUSY; + + if ((si_get_peer_mhl_version(dev_context) < 0x20) && + !(dev_context->dev_cap_cache.mdc.featureFlag + & MHL_FEATURE_SP_SUPPORT)) { + MHL_TX_DBG_ERR("failed SCRATCHPAD_NOT_SUPPORTED\n"); + status = SCRATCHPAD_NOT_SUPPORTED; + + } else if ((burst_offset + length) > SCRATCHPAD_SIZE) { + MHL_TX_DBG_ERR("invalid offset + length\n"); + status = SCRATCHPAD_BAD_PARAM; + + } else { + MHL_TX_DBG_ERR("Sending WB\n"); + si_mhl_tx_send_write_burst(dev_context, data); + status = SCRATCHPAD_SUCCESS; + } + + return status; +} + +/* + * si_mhl_tx_send_msc_msg + * + * This function sends a MSC_MSG command to the peer. + * It returns true if successful in doing so. + */ +bool si_mhl_tx_send_msc_msg(struct mhl_dev_context *dev_context, + uint8_t command, uint8_t cmdData, + struct cbus_req *(*completion)(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) + ) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO("called\n"); + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + req->command = MHL_MSC_MSG; + req->msg_data[0] = command; + req->msg_data[1] = cmdData; + req->completion = completion; + + queue_cbus_transaction(dev_context, req); + + return true; +} + +struct cbus_req *rapk_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, + uint8_t data1) +{ + if (MHL_RAP_CBUS_MODE_DOWN == dev_context->rap_in_sub_command) { + MHL_TX_DBG_ERR("%sRAPK complete%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context + *)&dev_context-> + drv_context, + CM_oCBUS_PEER_IS_MHL3); + } else if (MHL_RAP_CBUS_MODE_UP == dev_context->rap_in_sub_command) { + MHL_TX_DBG_ERR("%sRAPK complete%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *)&dev_context->drv_context, + CM_eCBUS_S); + } + return req; +} +/* + * si_mhl_rapk_send + * This function sends RAPK to the peer device. + */ +static bool si_mhl_rapk_send(struct mhl_dev_context *dev_context, + uint8_t status) +{ + return si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RAPK, status, + rapk_done); +} + +struct cbus_req *rcpe_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, + uint8_t data1) +{ + /* + * RCPE is always followed by an RCPK with + * original key code received. + */ + si_mhl_tx_rcpk_send(dev_context, dev_context->msc_save_rcp_key_code); + return req; +} +/* + * si_mhl_tx_rcpe_send + * + * The function will return a value of true if it could successfully send the + * RCPE subcommand. Otherwise false. + * + * When successful, mhl_tx internally sends RCPK with original (last known) + * keycode. + */ +bool si_mhl_tx_rcpe_send(struct mhl_dev_context *dev_context, + uint8_t rcpe_error_code) +{ + bool status; + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RCPE, + rcpe_error_code, rcpe_done); + if (status) + si_mhl_tx_drive_states(dev_context); + + return status; +} + +/* + * si_mhl_tx_bist_setup + * + * This function sends a BIST_SETUP WRITE_BURST to the MHL3 peer. + * See Section 15 of the MHL3 specification + * + * This function returns the status of the command sent. + * + * This function is called only when Titan is the BIST initiator, + * i.e. test equipment. This function can only be called when the CBUS is + * in oCBUS mode. If the CBUS is in any other mode, the WRITE_BURST will not + * be sent to the MHL3 peer and this function will return failure. + */ +enum bist_cmd_status si_mhl_tx_bist_setup(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup) +{ + enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; + + /* Validate current cbus mode and bist_setup_info */ + if (si_mhl_tx_drv_get_cbus_mode(dev_context) != + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP) { + ret_status = BIST_STATUS_NOT_IN_OCBUS; + } else if (setup->e_cbus_duration == 0x00) { + ret_status = BIST_STATUS_INVALID_SETUP; + } else if ((setup->e_cbus_pattern == BIST_ECBUS_PATTERN_UNSPECIFIED) || + (setup->e_cbus_pattern > BIST_ECBUS_PATTERN_MAX)) { + ret_status = BIST_STATUS_INVALID_SETUP; + /*} if setup->e_cbus_pattern is Fixed10 + * with no support for eCBUS-D { + */ + /* ret_status = BIST_STATUS_INVALID_SETUP; */ + } else + if ((setup->avlink_data_rate == BIST_AVLINK_DATA_RATE_UNSPECIFIED) + || (setup->avlink_pattern > BIST_AVLINK_DATA_RATE_MAX)) { + ret_status = BIST_STATUS_INVALID_SETUP; + } else if ((setup->avlink_pattern == BIST_AVLINK_PATTERN_UNSPECIFIED) || + (setup->avlink_pattern > BIST_AVLINK_PATTERN_MAX)) { + ret_status = BIST_STATUS_INVALID_SETUP; + /*} validate video mode { */ + /* ret_status = BIST_STATUS_INVALID_SETUP; */ + } else if ((setup->impedance_mode == BIST_IMPEDANCE_MODE_RESERVED_1) || + (setup->impedance_mode == BIST_IMPEDANCE_MODE_RESERVED_2) || + (setup->impedance_mode > BIST_IMPEDANCE_MODE_MAX)) { + ret_status = BIST_STATUS_INVALID_SETUP; + } else { + /* Build BIST_SETUP WRITE_BURST */ + struct bist_setup_burst burst; + burst.burst_id_h = HIGH_BYTE_16(burst_id_BIST_SETUP); + burst.burst_id_l = LOW_BYTE_16(burst_id_BIST_SETUP); + burst.checksum = 0x00; + burst.e_cbus_duration = setup->e_cbus_duration; + burst.e_cbus_pattern = setup->e_cbus_pattern; + burst.e_cbus_fixed_h = HIGH_BYTE_16(setup->e_cbus_fixed_pat); + burst.e_cbus_fixed_l = LOW_BYTE_16(setup->e_cbus_fixed_pat); + burst.avlink_data_rate = setup->avlink_data_rate; + burst.avlink_pattern = setup->avlink_pattern; + burst.avlink_video_mode = setup->avlink_video_mode; + burst.avlink_duration = setup->avlink_duration; + burst.avlink_fixed_h = HIGH_BYTE_16(setup->avlink_fixed_pat); + burst.avlink_fixed_l = LOW_BYTE_16(setup->avlink_fixed_pat); + burst.avlink_randomizer = setup->avlink_randomizer; + burst.impedance_mode = setup->impedance_mode; + + /* calculate checksum */ + burst.checksum = + calculate_generic_checksum((uint8_t *) &burst, 0, + sizeof(burst)); + + /* Send WRITE_BURST */ + si_mhl_tx_request_write_burst(dev_context, 0, sizeof(burst), + (uint8_t *) &burst); + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *)&dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT); + } + + return ret_status; +} + +void si_mhl_tx_set_bist_timer_impl(struct mhl_dev_context *dev_context, + const char *caller, int line_num) +{ + MHL_TX_DBG_ERR("BIST timeout (%d,%d) from %s:%d\n", + dev_context->bist_timeout_value, + LOCAL_eCBUS_ERR_SAMPLE_PERIOD, caller, line_num) + + dev_context->bist_timeout_total = 0; + mhl_tx_start_timer(dev_context, dev_context->bist_timer, + LOCAL_eCBUS_ERR_SAMPLE_PERIOD); +} + +static bool determine_bist_timeout_value(struct mhl_dev_context *dev_context) +{ + uint32_t bist_timeout = 0; + uint32_t av_link_timeout = 0; + uint8_t test_sel; + + MHL_TX_DBG_INFO("\n"); + + test_sel = dev_context->bist_trigger_info; + dev_context->bist_timeout_value = 0; + + if (test_sel & BIST_TRIGGER_IMPEDANCE_TEST) { + MHL_TX_DBG_INFO("\n") + bist_timeout = + dev_context->bist_setup.e_cbus_duration * 1000; + dev_context->bist_timeout_value = bist_timeout; + si_mhl_tx_set_bist_timer(dev_context); + } else { + if (test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK) { + MHL_TX_DBG_INFO("Initiate eCBUS BIST\n"); + bist_timeout = + dev_context->bist_setup.e_cbus_duration * 1000; + dev_context->bist_timeout_value = bist_timeout; + } + if (test_sel & BIST_TRIGGER_ECBUS_AV_LINK_MASK) { + MHL_TX_DBG_INFO("\n") + av_link_timeout = + dev_context->bist_setup.avlink_duration; + if (dev_context->bist_setup.avlink_pattern <= + BIST_AVLINK_PATTERN_FIXED_8) { + MHL_TX_DBG_INFO("\n") + /* ~17ms. per frame */ + av_link_timeout *= 32 * 17; + } else { + MHL_TX_DBG_INFO("\n") + av_link_timeout *= 1000; + } + /* + * Run the test for the longer of either the + * eCBUS test time or the AV_LINK test time. + */ + if (av_link_timeout > bist_timeout) { + MHL_TX_DBG_INFO("\n") + dev_context->bist_timeout_value = + av_link_timeout; + } + if (!(test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK)) { + if (0 == av_link_timeout) { + MHL_TX_DBG_ERR("indefinite\n") + dev_context->bist_timeout_value = 0; + } + } + } + dev_context->bist_setup.t_bist_mode_down = + (dev_context->bist_timeout_value * 10)/100 + + T_BIST_MODE_DOWN_MIN; + MHL_TX_DBG_ERR("%d\n", dev_context->bist_timeout_value) + } + if (!dev_context->bist_timeout_value) + MHL_TX_DBG_ERR("No BIST timeout - wait for BIST_STOP\n"); + return true; +} + +struct cbus_req *bist_trigger_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, + uint8_t data1) +{ + /* We just requested a BIST test + * so now set up for it. + */ + MHL_TX_DBG_ERR("\n") + if (BIST_TRIGGER_IMPEDANCE_TEST == dev_context->bist_trigger_info) { + MHL_TX_DBG_ERR("\n") + start_bist_initiator_test(dev_context); + } else if (dev_context->bist_trigger_info) { + enum cbus_mode_e mode = + dev_context->msc_msg_data & BIST_TRIGGER_TEST_E_CBUS_D ? + CM_eCBUS_D_BIST : CM_eCBUS_S_BIST; + MHL_TX_DBG_ERR("\n") + determine_bist_timeout_value(dev_context); + if (1 == si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *)&dev_context->drv_context, mode)) + MHL_TX_DBG_ERR("wait TDM\n"); + + } + return req; +} +/* + * si_mhl_tx_bist_trigger + * + * This function sends a BIST_TRIGGER MSC_MSG to the MHL3 peer. + * See Section 15 of the MHL3 specification + * + * This function returns the status of the command sent. + * + * This function is called only when Titan is the BIST initiator, + * i.e. test equipment. This function can only be called when the CBUS is + * in oCBUS mode. If the CBUS is in any other mode, the MSC_MSG will not + * be sent to the MHL3 peer and this function will return failure. + */ +enum bist_cmd_status si_mhl_tx_bist_trigger(struct mhl_dev_context *dev_context, + uint8_t trigger_operand) +{ + enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; + + trigger_operand &= BIST_TRIGGER_OPERAND_VALID_MASK; + + /* Validate current cbus mode and trigger_operand */ + if (si_mhl_tx_drv_get_cbus_mode(dev_context) != + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY) { + MHL_TX_DBG_ERR("%sBIST_STATUS_NOT_IN_OCBUS%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) + ret_status = BIST_STATUS_DUT_NOT_READY; + /*} else if (trigger_operand & BIST_TRIGGER_AVLINK_TX) { + MHL_TX_DBG_ERR("%sBIST_STATUS_INVALID_TRIGGER%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) + ret_status = BIST_STATUS_INVALID_TRIGGER; + } else if ((trigger_operand & BIST_TRIGGER_OPERAND_SELECT_eCBUS_D) && + (eCBUS_D not supported)) { + */ + } else if ((trigger_operand & BIST_TRIGGER_IMPEDANCE_TEST) && + ((trigger_operand & ~BIST_TRIGGER_IMPEDANCE_TEST) != 0x00)) { + MHL_TX_DBG_ERR("%sBIST_STATUS_INVALID_TRIGGER%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT) + ret_status = BIST_STATUS_INVALID_TRIGGER; + } else { + dev_context->bist_trigger_info = trigger_operand; + + /* Send BIST_TRIGGER */ + si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_BIST_TRIGGER, + dev_context->bist_trigger_info, bist_trigger_done); + si_mhl_tx_drive_states(dev_context); + } + + return ret_status; +} +struct cbus_req *bist_stop_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + /* bist timer always runs, even for + bist_timeout_value == 0 (infinite) + */ + si_mhl_tx_bist_cleanup(dev_context); + return req; +} +/* + * si_mhl_tx_bist_stop + * + * This function sends a BIST_STOP MSC_MSG to the MHL3 peer. + * See Section 15 of the MHL3 specification + * + * This function returns the status of the command sent. + * + * This function is called only when Titan is the BIST initiator, + * i.e. test equipment. This function can only be called when the CBUS is + * in eCBUS mode. If the CBUS is in any other mode, the MSC_MSG will not + * be sent to the MHL3 peer and this function will return failure. + */ +enum bist_cmd_status si_mhl_tx_bist_stop(struct mhl_dev_context *dev_context) +{ + enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; + + /* Validate current cbus mode */ + if (si_mhl_tx_drv_get_cbus_mode(dev_context) < CM_eCBUS_S) + ret_status = BIST_STATUS_NOT_IN_ECBUS; + + /* Send BIST_STOP */ + si_mhl_tx_send_msc_msg(dev_context, + MHL_MSC_MSG_BIST_STOP, 0x00, bist_stop_done); + si_mhl_tx_drive_states(dev_context); + + return ret_status; +} +struct cbus_req *bist_request_stat_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} +/* + * si_mhl_tx_bist_request_stat + * + * This function sends a BIST_REQUEST_STAT MSC_MSG to the MHL3 peer. + * See Section 15 of the MHL3 specification + * + * This function returns the status of the command sent. + * + * This function is called only when Titan is the BIST initiator, + * i.e. test equipment. This function can only be called when the CBUS is + * in oCBUS mode. If the CBUS is in any other mode, the MSC_MSG will not + * be sent to the MHL3 peer and this function will return failure. + */ +enum bist_cmd_status si_mhl_tx_bist_request_stat(struct mhl_dev_context + *dev_context, + uint8_t request_operand) +{ + enum bist_cmd_status ret_status = BIST_STATUS_NO_ERROR; + + /* Validate current cbus mode */ + if (si_mhl_tx_drv_get_cbus_mode(dev_context) != + CM_oCBUS_PEER_IS_MHL3_BIST_STAT) + ret_status = BIST_STATUS_NOT_IN_OCBUS; + + /* verify operand 0x00 or 0x01 */ + + /* Send BIST_REQUEST_STAT */ + si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_BIST_REQUEST_STAT, + request_operand, bist_request_stat_done); + si_mhl_tx_drive_states(dev_context); + + return ret_status; +} + +void send_bist_status(struct mhl_dev_context *dev_context) +{ + struct bist_return_stat_burst bist_status; + uint16_t ecbus_local_stat; + + memset(&bist_status, 0, sizeof(bist_status)); + bist_status.burst_id_h = burst_id_BIST_RETURN_STAT >> 8; + bist_status.burst_id_l = burst_id_BIST_RETURN_STAT; + bist_status.avlink_stat_h = dev_context->bist_stat.avlink_stat >> 8; + bist_status.avlink_stat_l = dev_context->bist_stat.avlink_stat; + if (dev_context->bist_stat.e_cbus_prev_local_stat < 0) + ecbus_local_stat = 0xFFFF; + else if (dev_context->bist_stat.e_cbus_prev_local_stat > 0xFFFF) + ecbus_local_stat = 0xFFFE; + else + ecbus_local_stat = + (uint16_t)dev_context->bist_stat.e_cbus_prev_local_stat; + + bist_status.e_cbus_stat_h = ecbus_local_stat >> 8; + bist_status.e_cbus_stat_l = ecbus_local_stat; + bist_status.checksum = calculate_generic_checksum((uint8_t + *) (&bist_status), 0, + sizeof(bist_status)); + + si_mhl_tx_request_write_burst(dev_context, 0, sizeof(bist_status), + (uint8_t *) (&bist_status)); +} + +bool invalid_bist_parms(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup_info) +{ + uint8_t test_sel; + bool e_cbus_d_sel = false; + + MHL_TX_DBG_ERR("\n") + + test_sel = setup_info->bist_trigger_parm; + e_cbus_d_sel = test_sel & BIST_TRIGGER_TEST_E_CBUS_D ? true : false; + test_sel &= ~BIST_TRIGGER_E_CBUS_TYPE_MASK; + + if (test_sel > BIST_TRIGGER_IMPEDANCE_TEST) { + MHL_TX_DBG_ERR("Impedance test cannot be run " + "concurrently with other tests!\n"); + return true; + } + + if (BIST_TRIGGER_ECBUS_AV_LINK_MASK & test_sel) { + switch (setup_info->avlink_video_mode) { + case 4: /* 1280 X 720 (720P) */ + break; + case 3: /* 720 X 480 (480P) */ + break; + default: + MHL_TX_DBG_ERR("Unsupported VIC received!\n"); + return true; + } + switch (setup_info->avlink_data_rate) { + case 1: /* 1.5 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 1.5Gbps\n"); + break; + case 2: /* 3.0 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 3.0Gbps\n"); + break; + case 3: /* 6.0 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 6.0Gbps\n"); + break; + default: + MHL_TX_DBG_ERR("%sUnsupported " + "AVLINK_DATA_RATE %02X%s\n", + ANSI_ESC_RED_TEXT, + setup_info->avlink_data_rate, + ANSI_ESC_RESET_TEXT); + return true; + } + switch (setup_info->avlink_pattern) { + case BIST_AVLINK_PATTERN_UNSPECIFIED: + case BIST_AVLINK_PATTERN_PRBS: + MHL_TX_DBG_ERR("AV LINK_PATTERN %sPRBS%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) + break; + + case BIST_AVLINK_PATTERN_FIXED_8: + MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed8%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + break; + + case BIST_AVLINK_PATTERN_FIXED_10: + MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed10%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + break; + + default: + MHL_TX_DBG_ERR("%sUnrecognized test " + "pattern detected!%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return true; + } + } + if (BIST_TRIGGER_ECBUS_TX_RX_MASK & test_sel) { + MHL_TX_DBG_ERR("\n") + if (e_cbus_d_sel) { + MHL_TX_DBG_ERR("Testing of eCBUS-D not supported yet\n") + dev_context->bist_stat.e_cbus_prev_local_stat = 0xFFFF; + dev_context->bist_stat.e_cbus_local_stat = 0xFFFF; + dev_context->bist_stat.e_cbus_next_local_stat = 0xFFFF; + dev_context->bist_stat.e_cbus_remote_stat = 0xFFFF; + test_sel &= ~BIST_TRIGGER_ECBUS_TX_RX_MASK; + return true; + } + } + return false; +} + +bool invalid_bist_te_parms(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup_info) +{ + uint8_t test_sel; + test_sel = setup_info->bist_trigger_parm; + if (invalid_bist_parms(dev_context, setup_info)) { + MHL_TX_DBG_ERR("\n") + return true; + } else if (test_sel & BIST_TRIGGER_AVLINK_TX) { + MHL_TX_DBG_ERR("%sinvalid test_sel%s:0x%02x\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT, + test_sel) + /* Invalid test for MHL transmitter TE */ + dev_context->bist_stat.avlink_stat = 0xFFFF; + return true; + } + + return false; +} + +bool invalid_bist_dut_parms(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup_info) +{ + uint8_t test_sel; + test_sel = setup_info->bist_trigger_parm; + if (invalid_bist_parms(dev_context, setup_info)) { + MHL_TX_DBG_ERR("\n") + return true; + } else if (test_sel & BIST_TRIGGER_AVLINK_RX) { + /* Invalid test for MHL transmitter DUT */ + MHL_TX_DBG_ERR("%sInvalid test:0x%02X for MHL Tx DUT%s\n", + ANSI_ESC_RED_TEXT, + test_sel, + ANSI_ESC_RESET_TEXT) + dev_context->bist_stat.avlink_stat = 0xFFFF; + return true; + } + + return false; +} + +void initiate_bist_test(struct mhl_dev_context *dev_context) +{ + uint8_t test_sel; + enum cbus_mode_e cbus_mode; + bool e_cbus_d_sel = false; + + MHL_TX_DBG_ERR("\n") + + if (invalid_bist_dut_parms(dev_context, &dev_context->bist_setup)) { + MHL_TX_DBG_ERR("\n") + si_mhl_tx_bist_cleanup(dev_context); + return; + } + test_sel = dev_context->bist_trigger_info; + e_cbus_d_sel = test_sel & BIST_TRIGGER_TEST_E_CBUS_D ? true : false; + test_sel &= ~BIST_TRIGGER_E_CBUS_TYPE_MASK; + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + if (test_sel == 0) { + MHL_TX_DBG_ERR("%sNo test selected%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT) + si_mhl_tx_bist_cleanup(dev_context); + return; + } + + if (CM_oCBUS_PEER_IS_MHL3_BIST_STAT == cbus_mode) { + MHL_TX_DBG_ERR("another BIST_TRIGGER\n") + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *)&dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); + dev_context->bist_stat.avlink_stat = 0; + dev_context->bist_stat.e_cbus_prev_local_stat = 0; + dev_context->bist_stat.e_cbus_local_stat = 0; + dev_context->bist_stat.e_cbus_next_local_stat = 0; + dev_context->bist_stat.e_cbus_remote_stat = 0; + } else if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY != cbus_mode) { + dev_context->bist_stat.avlink_stat = 0; + dev_context->bist_stat.e_cbus_prev_local_stat = 0; + dev_context->bist_stat.e_cbus_local_stat = 0; + dev_context->bist_stat.e_cbus_next_local_stat = 0; + dev_context->bist_stat.e_cbus_remote_stat = 0; + } else { + MHL_TX_DBG_ERR("BIST test requested without prior " + "valid BIST setup command\n"); + dev_context->bist_stat.avlink_stat = 0xFFFF; + dev_context->bist_stat.e_cbus_prev_local_stat = 0xFFFF; + dev_context->bist_stat.e_cbus_local_stat = 0xFFFF; + dev_context->bist_stat.e_cbus_next_local_stat = 0xFFFF; + dev_context->bist_stat.e_cbus_remote_stat = 0xFFFF; + si_mhl_tx_bist_cleanup(dev_context); + return; + } + + if (test_sel & BIST_TRIGGER_IMPEDANCE_TEST) { + if (cbus_mode != CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY) { + si_mhl_tx_bist_cleanup(dev_context); + return; + } + if (0 == si_mhl_tx_drv_start_impedance_bist( + (struct drv_hw_context *) + &dev_context->drv_context, + &dev_context->bist_setup)) { + MHL_TX_DBG_ERR("\n") + + } + } else { + if (cbus_mode < CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST) { + MHL_TX_DBG_ERR + ("%sCannot initiate eCBUS-S BIST when " + "CBUS mode is%s %s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, + si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)); + si_mhl_tx_bist_cleanup(dev_context); + return; + } + + if (e_cbus_d_sel && + cbus_mode < CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST) { + MHL_TX_DBG_ERR + ("%sCannot initiate eCBUS-S BIST when " + "CBUS mode is%s %s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, + si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)); + si_mhl_tx_bist_cleanup(dev_context); + return; + } + if (test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK) { + MHL_TX_DBG_INFO("Initiate eCBUS BIST\n"); + si_mhl_tx_drv_start_ecbus_bist( + (struct drv_hw_context *) + &dev_context->drv_context, + &dev_context->bist_setup); + } + if (test_sel & BIST_TRIGGER_AVLINK_TX) { + MHL_TX_DBG_ERR("total: %d value:%d\n", + dev_context->bist_timeout_total, + dev_context->bist_timeout_value) + si_mhl_tx_drv_start_avlink_bist(dev_context, + &dev_context->bist_setup); + } + } +} + +void start_bist_initiator_test(struct mhl_dev_context *dev_context) +{ + uint8_t test_sel; + enum cbus_mode_e cbus_mode; + bool e_cbus_d_sel = false; + + MHL_TX_DBG_ERR("\n"); + + if (invalid_bist_te_parms(dev_context, &dev_context->bist_setup)) { + MHL_TX_DBG_ERR("\n") + si_mhl_tx_bist_cleanup(dev_context); + return; + } + test_sel = dev_context->bist_trigger_info; + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + if (test_sel == 0) { + MHL_TX_DBG_ERR("%sNo test selected%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT) + si_mhl_tx_bist_cleanup(dev_context); + return; + } + + if (test_sel & BIST_TRIGGER_IMPEDANCE_TEST) { + MHL_TX_DBG_ERR("\n") + if (0 == + si_mhl_tx_drv_start_impedance_bist((struct drv_hw_context *) + &dev_context-> + drv_context, + &dev_context-> + bist_setup)) { + MHL_TX_DBG_ERR("\n") + } + } else { + MHL_TX_DBG_ERR("\n") + if (cbus_mode < CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST) { + MHL_TX_DBG_ERR + ("Cannot initiate eCBUS-S BIST when CBUS mode is" + " %s\n", + si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)) + si_mhl_tx_bist_cleanup(dev_context); + return; + } + if (e_cbus_d_sel && + cbus_mode < CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST) { + MHL_TX_DBG_ERR + ("Cannot initiate eCBUS-D BIST when CBUS mode is" + " %s\n", + si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)) + si_mhl_tx_bist_cleanup(dev_context); + return; + } + if (test_sel & BIST_TRIGGER_ECBUS_TX_RX_MASK) { + MHL_TX_DBG_ERR("active\n") + si_mhl_tx_drv_start_ecbus_bist( + (struct drv_hw_context *) + &dev_context->drv_context, + &dev_context->bist_setup); + } + if (test_sel & BIST_TRIGGER_AVLINK_RX) { + MHL_TX_DBG_ERR("active\n") + si_mhl_tx_drv_start_avlink_bist(dev_context, + &dev_context->bist_setup); + } + } +} + +/* +API for CTS tester. + si_mhl_tx_execute_bist +Parameters: + dev_context: pointer to device context. + setup_info: pointer to a structure containing + a complete abstraction of a BIST_SETUP + BURST_ID packet along with parameters + for BIST_TRIGGER and BIST_REQUEST_STAT and + a run-time adjustable value for T_BIST_MODE_DOWN + whose default is the maximum value of 5 seconds. + Parameters for BIST_SETUP are converted from the + abstraction to the BIST_SETUP WRITE_BURST format + just before sending the WRITE_BURST. +*/ +void si_mhl_tx_execute_bist(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup_info) +{ + dev_context->misc_flags.flags.bist_role_TE = 1; + if (invalid_bist_te_parms(dev_context, setup_info)) { + MHL_TX_DBG_ERR("\n"); + } else { + dev_context->bist_setup = *setup_info; + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP); + } +} +/* + * si_mhl_tx_process_events + * This internal function is called at the end of interrupt processing. It's + * purpose is to process events detected during the interrupt. Some events + * are internally handled here but most are handled by a notification to + * interested applications. + */ +void si_mhl_tx_process_events(struct mhl_dev_context *dev_context) +{ + uint8_t rapk_status; + enum cbus_mode_e cbus_mode; + + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + /* Make sure any events detected during the interrupt are processed. */ + si_mhl_tx_drive_states(dev_context); + + if (dev_context->mhl_connection_event) { + MHL_TX_DBG_INFO("mhl_connection_event\n"); + + /* Consume the message */ + dev_context->mhl_connection_event = false; + + /* + * Let interested apps know about the connection state change + */ + mhl_event_notify(dev_context, dev_context->mhl_connected, + dev_context->dev_cap_cache.mdc.featureFlag, NULL); + + /* If connection has been lost, reset all state flags. */ + if (MHL_TX_EVENT_DISCONNECTION == dev_context->mhl_connected) { + MHL_TX_DBG_WARN("MHL Disconnect Event. Reset states\n"); + si_mhl_tx_reset_states(dev_context); + } + + else if (MHL_TX_EVENT_CONNECTION == + dev_context->mhl_connected) { + + /* queue up all three in this order to + indicate MHL 3.0 version according to spec. */ + +#ifdef FORCE_OCBUS_FOR_ECTS + /* This compile option is always enabled. + * It is intended to help identify code deletion by + * adopters who do not need this feauture. The control + * for forcing oCBUS works by using module parameter + * below. Peer version is forced to 2.0 allowing 8620 + * to treat the sink as if it is MHL 2.0 device and as + * a result never switch cbus to MHL3 eCBUS. + */ + if (force_ocbus_for_ects) { + MHL_TX_DBG_ERR("%sQueue DCAP_RDY, DCAP_CHG%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_CONNECTED_RDY, + MHL_STATUS_DCAP_RDY); + si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, + MHL_INT_DCAP_CHG, 1); + } else { + MHL_TX_DBG_WARN("%sQueue VERSION_STAT,DCAP_RDY" + ", DCAP_CHG%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_VERSION_STAT, + MHL_VERSION); + si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_CONNECTED_RDY, + MHL_STATUS_DCAP_RDY | + MHL_STATUS_XDEVCAPP_SUPP | + ((DEVCAP_VAL_DEV_CAT & + (MHL_DEV_CATEGORY_PLIM2_0 | + MHL_DEV_CATEGORY_POW_BIT)) >> 2) + ); + si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, + MHL_INT_DCAP_CHG, 1); + } +#else + MHL_TX_DBG_ERR + ("%sQueue VERSION_STAT, DCAP_RDY, DCAP_CHG%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_VERSION_STAT, + MHL_VERSION); + si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_CONNECTED_RDY, + MHL_STATUS_DCAP_RDY | + MHL_STATUS_XDEVCAPP_SUPP | + ((DEVCAP_VAL_DEV_CAT & + (MHL_DEV_CATEGORY_PLIM2_0 | + MHL_DEV_CATEGORY_POW_BIT) + ) >> 2) + ); + si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, + MHL_INT_DCAP_CHG, 1); +#endif + /* + * Start timer here to circumvent issue of not getting + * DCAP_RDY. Use timeout durations of 7 seconds or more + * to distinguish between non-compliant dongles and the + * CBUS CTS tester. + */ + switch (cbus_mode) { + case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: + case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: + /* stop rather than start the timer + for these cases + */ + mhl_tx_stop_timer(dev_context, + dev_context->dcap_rdy_timer); + mhl_tx_stop_timer(dev_context, + dev_context->dcap_chg_timer); + break; + default: + mhl_tx_start_timer(dev_context, + dev_context->dcap_rdy_timer, 7000); + } + } + } else if (dev_context->msc_msg_arrived) { + + MHL_TX_DBG_INFO("MSC MSG <%02X, %02X>\n", + dev_context->msc_msg_sub_command, + dev_context->msc_msg_data); + + /* Consume the message */ + dev_context->msc_msg_arrived = false; + + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + + /* + * Map MSG sub-command to an event ID + */ + switch (dev_context->msc_msg_sub_command) { + case MHL_MSC_MSG_RAP: + /* + * RAP messages are fully handled here. + */ + if (dev_context-> + mhl_flags & MHL_STATE_APPLICATION_RAP_BUSY) { + rapk_status = MHL_RAPK_BUSY; + } else { + rapk_status = MHL_RAPK_NO_ERR; + } + dev_context->rap_in_sub_command = + dev_context->msc_msg_data; + + if (MHL_RAP_POLL == dev_context->msc_msg_data) { + /* just do the ack */ + } else if (MHL_RAP_CONTENT_ON == + dev_context->msc_msg_data) { + MHL_TX_DBG_ERR("Got RAP{CONTENT_ON}\n"); + dev_context->misc_flags.flags.rap_content_on = + true; + si_mhl_tx_drv_content_on( + (struct drv_hw_context *) + &dev_context->drv_context); + } else if (MHL_RAP_CONTENT_OFF == + dev_context->msc_msg_data) { + MHL_TX_DBG_ERR("Got RAP{CONTENT_OFF}\n"); + if (dev_context->misc_flags.flags. + rap_content_on) { + dev_context->misc_flags.flags. + rap_content_on = false; + si_mhl_tx_drv_content_off( + (struct drv_hw_context *) + &dev_context->drv_context); + } + } else if (MHL_RAP_CBUS_MODE_DOWN == + dev_context->msc_msg_data) { + MHL_TX_DBG_ERR("Got RAP{CBUS_MODE_DOWN}\n"); + } else if (MHL_RAP_CBUS_MODE_UP == + dev_context->msc_msg_data) { + MHL_TX_DBG_ERR("Got RAP{CBUS_MODE_UP}\n"); + mhl_tx_stop_timer(dev_context, + dev_context-> + cbus_mode_up_timer); + } else { + MHL_TX_DBG_ERR("Unrecognized RAP code: 0x%02x " + "received\n", + dev_context->msc_msg_data); + rapk_status = MHL_RAPK_UNRECOGNIZED; + } + + /* Always RAPK to the peer */ + si_mhl_rapk_send(dev_context, rapk_status); + + if (rapk_status == MHL_RAPK_NO_ERR) + mhl_event_notify(dev_context, + MHL_TX_EVENT_RAP_RECEIVED, + dev_context->msc_msg_data, + NULL); + break; + + case MHL_MSC_MSG_RCP: + /* + * If we get a RCP key that we do NOT support, + * send back RCPE. Do not notify app layer. + */ + if (rcpSupportTable + [dev_context->msc_msg_data & MHL_RCP_KEY_ID_MASK]. + rcp_support & MHL_LOGICAL_DEVICE_MAP) { + mhl_event_notify(dev_context, + MHL_TX_EVENT_RCP_RECEIVED, + dev_context->msc_msg_data, + NULL); + } else { + /* Save keycode to send a RCPK after RCPE. */ + dev_context->msc_save_rcp_key_code = + dev_context->msc_msg_data; + si_mhl_tx_rcpe_send(dev_context, + RCPE_INEFFECTIVE_KEY_CODE); + } + break; + + case MHL_MSC_MSG_RCPK: + mhl_event_notify(dev_context, + MHL_TX_EVENT_RCPK_RECEIVED, + dev_context->msc_msg_data, NULL); + break; + + case MHL_MSC_MSG_RCPE: + mhl_event_notify(dev_context, + MHL_TX_EVENT_RCPE_RECEIVED, + dev_context->msc_msg_data, NULL); + break; + + case MHL_MSC_MSG_UCP: + /* + * Save key code so that we can send an UCPE message in + * case the UCP key code is rejected by the host + * application. + */ + dev_context->msc_save_ucp_key_code = + dev_context->msc_msg_data; + mhl_event_notify(dev_context, MHL_TX_EVENT_UCP_RECEIVED, + dev_context->msc_save_ucp_key_code, + NULL); + break; + + case MHL_MSC_MSG_UCPK: + mhl_event_notify(dev_context, + MHL_TX_EVENT_UCPK_RECEIVED, + dev_context->msc_msg_data, NULL); + break; + + case MHL_MSC_MSG_UCPE: + mhl_event_notify(dev_context, + MHL_TX_EVENT_UCPE_RECEIVED, + dev_context->msc_msg_data, NULL); + break; +#if (INCLUDE_RBP == 1) + case MHL_MSC_MSG_RBP: + /* + * Save button code so that we can send an RBPE message + * in case the RBP button code is rejected by the host + * application. + */ + dev_context->msc_save_rbp_button_code = + dev_context->msc_msg_data; + mhl_event_notify(dev_context, MHL_TX_EVENT_RBP_RECEIVED, + dev_context->msc_save_rbp_button_code, + NULL); + break; + + case MHL_MSC_MSG_RBPK: + mhl_event_notify(dev_context, + MHL_TX_EVENT_RBPK_RECEIVED, + dev_context->msc_msg_data, NULL); + break; + + case MHL_MSC_MSG_RBPE: + mhl_event_notify(dev_context, + MHL_TX_EVENT_RBPE_RECEIVED, + dev_context->msc_msg_data, NULL); + break; +#endif + case MHL_MSC_MSG_RAPK: + mhl_tx_stop_timer(dev_context, + dev_context->t_rap_max_timer); + /* the only RAP commands that we send are + * CBUS_MODE_UP and CBUS_MODE_DOWN. + */ + if (MHL_RAP_CBUS_MODE_DOWN == + dev_context->msc_msg_last_data) { + MHL_TX_DBG_ERR + ("Got RAPK{%s} for RAP{CBUS_MODE_DOWN}\n", + rapk_error_code_string[dev_context-> + msc_msg_data & + 0x03]); + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + + } else if (MHL_RAP_CBUS_MODE_UP == + dev_context->msc_msg_last_data) { + enum cbus_mode_e cbus_mode; + cbus_mode = + si_mhl_tx_drv_get_cbus_mode(dev_context); + MHL_TX_DBG_ERR + ("Got RAPK{%s}\n", + rapk_error_code_string[dev_context-> + msc_msg_data & + 0x03]); + + if (MHL_RAPK_NO_ERR == + dev_context->msc_msg_data) { + + /* CBUS Mode switch to eCBUS */ + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, + CM_eCBUS_S); + + } else if (MHL_RAPK_BUSY == + dev_context->msc_msg_data) { + if (CM_oCBUS_PEER_IS_MHL3 == + cbus_mode) { + MHL_TX_DBG_ERR( + "starting timer for " + "CBUS_MODE_UP\n") + mhl_tx_start_timer(dev_context, + dev_context-> + cbus_mode_up_timer, + 100); + } + + } else { + /* + * Nothing to do for + * MHL_RAPK_UNRECOGNIZED, + * MHL_RAPK_UNSUPPORTED + */ + } + + } else { + MHL_TX_DBG_ERR("Got RAPK for RAP " + "cmd: %02x,err_code: %02x\n", + dev_context->msc_msg_last_data, + dev_context->msc_msg_data); + /* post status */ + dev_context->rap_out_status = + dev_context->msc_msg_data; + } + break; + + case MHL_MSC_MSG_RHID: + case MHL_MSC_MSG_RHIDK: +#if (INCLUDE_HID == 1) + MHL_TX_DBG_WARN("Received MSC_MSG_RHID/K from sink\n"); + mhl_tx_hid_host_negotiation(dev_context); +#endif + break; + + case MHL_MSC_MSG_BIST_READY: + MHL_TX_DBG_INFO("Got BIST_READY\n"); + if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT == + cbus_mode) { + bool status; + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY + ); + mhl_event_notify(dev_context, + MHL_TX_EVENT_BIST_READY_RECEIVED, + dev_context->msc_msg_data, + NULL); + status = si_mhl_tx_bist_trigger( + dev_context, dev_context-> + bist_setup.bist_trigger_parm); + if (BIST_STATUS_NO_ERROR != status) { + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_STAT); + + } + } + break; + + case MHL_MSC_MSG_BIST_TRIGGER: + if (CM_oCBUS_PEER_IS_MHL3_BIST_STAT == cbus_mode) { + MHL_TX_DBG_ERR("another BIST_TRIGGER\n"); + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); + } else if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY + != cbus_mode) { + MHL_TX_DBG_ERR + ("Got BIST_TRIGGER when CBUS mode is %s\n", + si_mhl_tx_drv_get_cbus_mode_str + (cbus_mode)); + break; + } + + MHL_TX_DBG_ERR("Got BIST_TRIGGER\n"); + dev_context->bist_setup.bist_trigger_parm = + dev_context->bist_trigger_info = + dev_context->msc_msg_data; + if (dev_context->msc_msg_data == + BIST_TRIGGER_IMPEDANCE_TEST) { + initiate_bist_test(dev_context); + } else if (dev_context->msc_msg_data != 0) { + cbus_mode = dev_context->msc_msg_data & + BIST_TRIGGER_TEST_E_CBUS_D ? + CM_eCBUS_D_BIST : CM_eCBUS_S_BIST; + MHL_TX_DBG_ERR("\n") + determine_bist_timeout_value(dev_context); + if (1 == si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, cbus_mode)) + MHL_TX_DBG_ERR("Wait CoC Cal\n") + } + break; + + case MHL_MSC_MSG_BIST_STOP: + if (cbus_mode < CM_eCBUS_S_AV_BIST) { + MHL_TX_DBG_ERR + ("Got BIST_STOP when CBUS mode is %s\n", + si_mhl_tx_drv_get_cbus_mode_str + (cbus_mode)); + break; + } + + MHL_TX_DBG_INFO("Got BIST_STOP\n"); + + mhl_tx_stop_timer(dev_context, + dev_context->bist_timer); + dev_context->bist_stat.avlink_stat = 0; + /* bist timer always runs, even for + bist_timeout_value == 0 (infinite) + */ + si_mhl_tx_bist_cleanup(dev_context); + break; + + case MHL_MSC_MSG_BIST_REQUEST_STAT: + if (cbus_mode != CM_oCBUS_PEER_IS_MHL3_BIST_STAT) { + MHL_TX_DBG_ERR("Got BIST_REQUEST_STAT when " + "CBUS mode is %s\n", + si_mhl_tx_drv_get_cbus_mode_str + (cbus_mode)); + break; + } + + MHL_TX_DBG_ERR("Got BIST_REQUEST_STAT\n"); + + if (dev_context->msc_msg_data) { + MHL_TX_DBG_ERR("Send BIST status\n"); + send_bist_status(dev_context); + } + break; + + default: + MHL_TX_DBG_WARN("Unexpected MSC message " + "sub-command code: 0x%02x received!\n", + dev_context->msc_msg_sub_command); + break; + } + } +} + +static struct cbus_req *read_devcap_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1); + +bool si_mhl_tx_read_devcap(struct mhl_dev_context *dev_context) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO("called\n"); + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + req->command = MHL_READ_DEVCAP; + req->reg = 0; + req->reg_data = 0; /* do this to avoid confusion */ + req->completion = read_devcap_done; + + queue_cbus_transaction(dev_context, req); + + return true; +} + +bool si_mhl_tx_read_devcap_reg(struct mhl_dev_context *dev_context, + uint8_t offset) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO("called\n"); + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + req->command = MHL_READ_DEVCAP_REG; + req->reg = offset; + req->reg_data = 0; /* do this to avoid confusion */ + + queue_cbus_transaction(dev_context, req); + + return true; +} + +static struct cbus_req *read_xdevcap_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1); + +bool si_mhl_tx_read_xdevcap_impl(struct mhl_dev_context *dev_context, + const char *func_name, int line_num) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO("called from %s:%d\n", func_name, line_num); + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + req->command = MHL_READ_XDEVCAP; + req->reg = 0; + req->reg_data = 0; /* do this to avoid confusion */ + req->completion = read_xdevcap_done; + + queue_cbus_transaction(dev_context, req); + + return true; +} + +static struct cbus_req *read_xdevcap_reg_done( + struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1); +#define si_mhl_tx_read_xdevcap(dev_context) \ + si_mhl_tx_read_xdevcap_impl(dev_context, __func__, __LINE__) + +bool si_mhl_tx_read_xdevcap_reg_impl(struct mhl_dev_context *dev_context, + uint8_t reg_addr, const char *func_name, int line_num) +{ + struct cbus_req *req; + + MHL_TX_DBG_WARN("called from %s:%d\n", func_name, line_num); + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_GENERIC_DBG_PRINT(-1, "CBUS free queue exhausted\n") + return false; + } + + req->retry_count = 2; + req->command = MHL_READ_XDEVCAP_REG; + req->reg = reg_addr; + req->reg_data = 0; /* avoid confusion */ + req->completion = read_xdevcap_reg_done; + queue_cbus_transaction(dev_context, req); + + return true; +} + +#define si_mhl_tx_read_xdevcap_reg(dev_context, offset) \ + si_mhl_tx_read_xdevcap_reg_impl(dev_context, offset, __func__, \ + __LINE__) + +struct cbus_req *rcpk_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} + +bool si_mhl_tx_rcpk_send(struct mhl_dev_context *dev_context, + uint8_t rcp_key_code) +{ + bool status; + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RCPK, + rcp_key_code, rcpk_done); + if (status) + si_mhl_tx_drive_states(dev_context); + + return status; +} + +static struct cbus_req *read_edid_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1); +/* + * si_mhl_tx_request_first_edid_block + * + * This function initiates a CBUS command to read the specified EDID block. + * Returns true if the command was queued successfully. + */ +void si_mhl_tx_request_first_edid_block(struct mhl_dev_context *dev_context) +{ + MHL_TX_DBG_INFO("tag: EDID active: %d\n", + dev_context->misc_flags.flags.edid_loop_active); + if (!dev_context->misc_flags.flags.edid_loop_active) { + struct cbus_req *req; + + req = get_free_cbus_queue_entry(dev_context); + if (req == NULL) { + MHL_TX_DBG_INFO("couldn't get free cbus req\n"); + } else { + dev_context->misc_flags.flags.edid_loop_active = 1; + MHL_TX_DBG_INFO("tag: EDID active: %d\n", + dev_context->misc_flags.flags. + edid_loop_active); + + /* Send MHL_READ_EDID_BLOCK command */ + req->retry_count = 2; + req->command = MHL_READ_EDID_BLOCK; + req->burst_offset = 0; /* block number */ + req->msg_data[0] = 0; /* avoid confusion */ + req->completion = read_edid_done; + + queue_cbus_transaction(dev_context, req); + + si_mhl_tx_drive_states(dev_context); + } + } +} + +/* + si_mhl_tx_ecbus_speeds_done +*/ + +static void si_mhl_tx_ecbus_speeds_done(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + enum cbus_mode_e cbus_mode; + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + /* + * Set default eCBUS virtual channel slot assignments + * based on the capabilities of both the transmitter + * and receiver. + */ + if ((dev_context->xdev_cap_cache.mxdc.ecbus_speeds & + MHL_XDC_ECBUS_D_150) && + si_mhl_tx_drv_support_e_cbus_d( + (struct drv_hw_context *) + &dev_context->drv_context)) { + dev_context->virt_chan_slot_counts[TDM_VC_CBUS1] = 1; + dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = 39; + dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = 160; + } else { + dev_context->virt_chan_slot_counts[TDM_VC_CBUS1] = 1; + dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = 4; + dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = 20; + } + memcpy(hw_context->tdm_virt_chan_slot_counts, + dev_context->virt_chan_slot_counts, + sizeof(hw_context->tdm_virt_chan_slot_counts)); + + + if (cbus_mode <= CM_oCBUS_PEER_IS_MHL3) { + bool status; + enum bist_cmd_status bcs; + bool send_bist_setup = true; + status = si_mhl_tx_set_status( + dev_context, true, + MHL_XSTATUS_REG_CBUS_MODE, + MHL_XDS_ECBUS_S | + MHL_XDS_SLOT_MODE_8BIT); + + switch (cbus_mode) { + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + MHL_TX_DBG_ERR("\n") + if (BIST_TRIGGER_ECBUS_AV_LINK_MASK & + dev_context->bist_setup.bist_trigger_parm) { + MHL_TX_DBG_ERR("\n") + if (dev_context-> + misc_flags.flags.bist_role_TE) { + MHL_TX_DBG_ERR("\n") + send_bist_setup = false; + setup_sans_cbus1(dev_context); + } + } + if (send_bist_setup) { + MHL_TX_DBG_ERR( + "%sissuing BIST_SETUP%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + bcs = si_mhl_tx_bist_setup(dev_context, + &dev_context->bist_setup); + } + MHL_TX_DBG_ERR("status: %d\n", status); + break; + case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: + + if (dev_context->misc_flags.flags.bist_role_TE) { + if ((BIST_TRIGGER_ECBUS_AV_LINK_MASK | + BIST_TRIGGER_E_CBUS_RX | + BIST_TRIGGER_IMPEDANCE_TEST) & + dev_context->bist_trigger_info) { + MHL_TX_DBG_ERR( + "%sissuing BIST_REQUEST_STAT%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + status = si_mhl_tx_bist_request_stat( + dev_context, + dev_context-> + bist_setup.bist_stat_parm); + MHL_TX_DBG_ERR("status: %d\n", status); + break; + } + MHL_TX_DBG_ERR + ("%sLocal BIST results%s" + " eCBUS TX:%s0x%06x%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT, + ANSI_ESC_GREEN_TEXT, + dev_context->bist_stat. + e_cbus_prev_local_stat, + ANSI_ESC_RESET_TEXT) + } else if (BIST_TRIGGER_E_CBUS_RX & + dev_context->bist_trigger_info) { + MHL_TX_DBG_ERR("%swaiting for " + "BIST_REQUEST_STAT " + "from sink TE%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + break; + } + + dev_context->bist_trigger_info = 0x00; + /* fall through here to do cbus_mode_up */ + default: + /* issue RAP(CBUS_MODE_UP) + * RAP support is required for MHL3 and + * later devices + */ + MHL_TX_DBG_ERR( + "%sissuing CBUS_MODE_UP%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + #ifdef BIST_DONE_DEBUG + si_dump_important_regs( + (struct drv_hw_context *) + &dev_context->drv_context); + #endif + status = si_mhl_tx_rap_send( + dev_context, + MHL_RAP_CBUS_MODE_UP); + if (status) + si_mhl_tx_drive_states(dev_context); + } + } +} + +static struct cbus_req *read_xdevcap_reg_done( + struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_INFO("mhl: peer XDEVCAP[0x%02x] data1:0x%02x\n", + req->reg, data1); + dev_context->xdev_cap_cache. + xdevcap_cache[XDEVCAP_OFFSET(req->reg)] = data1; + + if (XDEVCAP_ADDR_ECBUS_SPEEDS == req->reg) + si_mhl_tx_ecbus_speeds_done(dev_context); + + return req; +} + + +static struct cbus_req *read_xdevcap_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + int i; + union MHLXDevCap_u old_xdevcap, xdevcap_changes; + uint8_t roles; + + old_xdevcap = dev_context->xdev_cap_cache; + MHL_TX_DBG_INFO("mhl: peer XDEVCAP data1:0x%02x\n", data1); + si_mhl_tx_read_xdevcap_fifo((struct drv_hw_context *) + &dev_context->drv_context, + &dev_context->xdev_cap_cache); + + /* + * Generate a change mask between the old and new devcaps + */ + for (i = 0; i < XDEVCAP_OFFSET(XDEVCAP_LIMIT); ++i) { + xdevcap_changes.xdevcap_cache[i] + = dev_context->xdev_cap_cache.xdevcap_cache[i] + ^ old_xdevcap.xdevcap_cache[i]; + if (xdevcap_changes.xdevcap_cache[i]) { + MHL_TX_DBG_INFO("XDEVCAP[%d] changed from " + "0x%02x to 0x%02x\n", i, + old_xdevcap.xdevcap_cache[i], + dev_context->xdev_cap_cache. + xdevcap_cache[i]); + } + } + + roles = XDEVCAP_OFFSET(XDEVCAP_ADDR_ECBUS_DEV_ROLES); + MHL_TX_DBG_INFO + ("mhl: xdevcap_changes.xdevcap_cache[%d]= %02X\n", roles, + xdevcap_changes.xdevcap_cache[roles]); + if (xdevcap_changes.xdevcap_cache[roles]) { + roles = + dev_context->xdev_cap_cache.xdevcap_cache[roles]; + MHL_TX_DBG_INFO + ("mhl: XDEVCAP_ADDR_ECBUS_DEV_ROLES= %02X\n", + roles); + /* + * If sink supports HID_DEVICE, + * tell it we want to be the host + */ +#if (INCLUDE_HID) + if (roles & MHL_XDC_HID_DEVICE) { + MHL_TX_DBG_INFO("mhl: calling " + "mhl_tx_hid_host_role_request()\n"); + mhl_tx_hid_host_role_request(dev_context, + MHL_RHID_REQUEST_HOST); + } +#endif + } + + si_mhl_tx_read_devcap(dev_context); + + return req; +} + +static struct cbus_req *read_devcap_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + uint8_t temp; + int i; + union MHLDevCap_u old_devcap, devcap_changes; + + old_devcap = dev_context->dev_cap_cache; + si_mhl_tx_read_devcap_fifo((struct drv_hw_context *) + &dev_context->drv_context, + &dev_context->dev_cap_cache); + + if (dev_context->peer_mhl3_version < 0x30) { + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + dev_context->peer_mhl3_version = + dev_context->dev_cap_cache.mdc.mhl_version; + hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL1_2; + si_set_cbus_mode_leds(CM_oCBUS_PEER_IS_MHL1_2); +#ifdef FORCE_OCBUS_FOR_ECTS + /* This compile option is always enabled. + * It is intended to help identify code deletion by + * adopters who do not need this feauture. The control + * for forcing oCBUS works by using module parameter + * below. Peer version is forced to 2.0 allowing 8620 + * to treat the sink as if it is MHL 2.0 device and as + * a result never switch cbus to MHL3 eCBUS. + */ + { + if (force_ocbus_for_ects) { + /* todo: what if the peer is MHL 1.x? */ + dev_context->peer_mhl3_version = 0x20; + } + } +#endif + } + + MHL_TX_DBG_WARN("DEVCAP->MHL_VER = %02x\n", + dev_context->peer_mhl3_version); + /* + * Generate a change mask between the old and new devcaps + */ + for (i = 0; i < sizeof(old_devcap); ++i) { + devcap_changes.devcap_cache[i] + = dev_context->dev_cap_cache.devcap_cache[i] + ^ old_devcap.devcap_cache[i]; + } + /* look for a change in the pow bit */ + if (MHL_DEV_CATEGORY_POW_BIT & devcap_changes.mdc. + deviceCategory) { + uint8_t param; + uint8_t index; + index = dev_context->dev_cap_cache.mdc.deviceCategory + & MHL_DEV_CATEGORY_PLIM2_0; + index >>= 5; + param = dev_context->dev_cap_cache.mdc.deviceCategory + & MHL_DEV_CATEGORY_POW_BIT; + + if (param) { + MHL_TX_DBG_WARN("%ssink drives VBUS%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + /* limit incoming current */ + mhl_tx_vbus_control(VBUS_OFF); + mhl_tx_vbus_current_ctl(plim_table[index]); + } else { + MHL_TX_DBG_WARN("%ssource drives VBUS%s\n", + ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); + mhl_tx_vbus_control(VBUS_ON); + } + + /* Inform interested Apps of the MHL power change */ + mhl_event_notify(dev_context, MHL_TX_EVENT_POW_BIT_CHG, + param, NULL); + } + + /* indicate that the DEVCAP cache is up to date. */ + dev_context->misc_flags.flags.have_complete_devcap = true; + + /* + * Check to see if any other bits besides POW_BIT have changed + */ + devcap_changes.mdc.deviceCategory &= + ~(MHL_DEV_CATEGORY_POW_BIT | MHL_DEV_CATEGORY_PLIM2_0); + temp = 0; + for (i = 0; i < sizeof(devcap_changes); ++i) + temp |= devcap_changes.devcap_cache[i]; + + if (temp) { + if (dev_context->misc_flags.flags.mhl_hpd) { + MHL_TX_DBG_INFO("Have HPD\n"); + si_mhl_tx_initiate_edid_sequence( + dev_context->edid_parser_context); + } else { + MHL_TX_DBG_INFO("No HPD\n"); + } + } + return req; +} + +static struct cbus_req *read_edid_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + si_mhl_tx_drive_states(dev_context); + MHL_TX_GENERIC_DBG_PRINT(data1 ? -1 : 1, "data1: %d\n", data1) + if (0 == data1) { + dev_context->edid_valid = true; + si_mhl_tx_handle_atomic_hw_edid_read_complete + (dev_context->edid_parser_context); + } + dev_context->misc_flags.flags.edid_loop_active = 0; + MHL_TX_DBG_INFO("tag: EDID active: %d\n", + dev_context->misc_flags.flags.edid_loop_active); + return req; +} + +static struct cbus_req *write_stat_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_INFO("Sent WRITE_STAT[0x%02x]->0x%02x" + " miscFlags: %08X\n", req->reg, req->reg_data, + dev_context->misc_flags.as_uint32); + if (MHL_STATUS_REG_CONNECTED_RDY == req->reg) { + if (MHL_STATUS_DCAP_RDY & req->reg_data) { + dev_context->misc_flags.flags.sent_dcap_rdy = + true; + + MHL_TX_DBG_INFO("%sSent DCAP_RDY%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + } + } else if (MHL_STATUS_REG_LINK_MODE == req->reg) { + if (MHL_STATUS_PATH_ENABLED & req->reg_data) { + dev_context->misc_flags.flags.sent_path_en = + true; + MHL_TX_DBG_INFO("FLAGS_SENT_PATH_EN\n"); + } + } + return req; +} + +static struct cbus_req *write_burst_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + struct tdm_alloc_burst *tdm_burst; + tdm_burst = (struct tdm_alloc_burst *) + req->msg_data; + MHL_TX_DBG_INFO("MHL_WRITE_BURST\n"); + if (burst_id_VC_CONFIRM + == BURST_ID(tdm_burst->header.burst_id)) { + int i; + int can_reassign = 1; + MHL_TX_DBG_ERR("VC_CONFIRM done\n"); + for (i = 0; i < VC_MAX; ++i) { + if (VC_RESPONSE_ACCEPT != tdm_burst->vc_info[i]. + req_resp.channel_size) { + can_reassign = 0; + } + } + /* changing slot allocations may result in a loss of data; however, + * the link will self-synchronize + */ + if ((can_reassign) + && (0 == si_mhl_tx_drv_set_tdm_slot_allocation( + (struct drv_hw_context *)&dev_context->drv_context, + dev_context->virt_chan_slot_counts, true))) { + MHL_TX_DBG_ERR("Slots reassigned.\n"); + } + } else if (burst_id_BIST_RETURN_STAT + == BURST_ID(tdm_burst->header.burst_id)) { + bool status; + + MHL_TX_DBG_ERR("%sBIST DONE%s\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT) + /* we already know that were in MHL_BIST */ + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + + /* issue RAP(CBUS_MODE_UP) + * RAP support is required for MHL3 and + * later devices + */ + MHL_TX_DBG_ERR( + "%sissuing CBUS_MODE_UP%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + #ifdef BIST_DONE_DEBUG + si_dump_important_regs( + (struct drv_hw_context *) + &dev_context->drv_context); + #endif + status = si_mhl_tx_rap_send( + dev_context, + MHL_RAP_CBUS_MODE_UP); + if (status) { + si_mhl_tx_drive_states( + dev_context); + } + } + return req; +} + + +static struct cbus_req *set_int_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_INFO("MHL_SET_INT\n"); + if (MHL_RCHANGE_INT == req->reg) { + MHL_TX_DBG_INFO("%sSent MHL_RCHANGE_INT%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + + if (MHL3_INT_FEAT_COMPLETE == req->reg_data) { + MHL_TX_DBG_WARN("%sSent FEAT_COMPLETE%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + } + } + return req; +} + +/* + * si_mhl_tx_msc_command_done + * + * This function is called by the driver to notify completion of last command. + * + * It is called in interrupt context to meet some MHL specified timings. + * Therefore, it should not call app layer and do negligible processing, no + * printfs. + */ + +void si_mhl_tx_msc_command_done(struct mhl_dev_context *dev_context, + uint8_t data1) +{ + struct cbus_req *req; + + MHL_TX_DBG_INFO("data1 = %02X\n", data1); + + req = dev_context->current_cbus_req; + if (req == NULL) { + MHL_TX_DBG_ERR("No message to associate with " + "completion notification\n"); + return; + } + MHL_TX_DBG_INFO("current command:0x%02x\n", req->command); + + dev_context->current_cbus_req = NULL; + + if (req->status.flags.cancel == true) { + MHL_TX_DBG_INFO("Canceling request with command 0x%02x\n", + req->command); + + } else if (MHL_MSC_MSG == req->command) { + if (dev_context->intr_info.flags & DRV_INTR_MSC_NAK) { + + msleep(1000); + MHL_TX_DBG_INFO("MSC_NAK, re-trying...\n"); + /* + * Request must be retried, so place it back + * on the front of the queue. + */ + req->status.as_uint8 = 0; + queue_priority_cbus_transaction(dev_context, req); + req = NULL; + } else if (req->completion) { + MHL_TX_DBG_INFO + ("MHL_MSC_MSG sub cmd: 0x%02x data: 0x%02x\n", + req->msg_data[0], req->msg_data[1]); + req = req->completion(dev_context, req, data1); + } else { + MHL_TX_DBG_INFO("default\n" + "\tcommand: 0x%02X\n" + "\tmsg_data: 0x%02X " + "msc_msg_last_data: 0x%02X\n", + req->command, + req->msg_data[0], + dev_context->msc_msg_last_data); + } + } else if (req->completion) { + req = req->completion(dev_context, req, data1); +#if 0 + } else if (MHL_READ_XDEVCAP == req->command) { + req = read_xdevcap_done(dev_context, req, data1); + + } else if (MHL_READ_XDEVCAP_REG == req->command) { + req = read_xdevcap_reg_done(dev_context, req, data1); + } else if (MHL_READ_DEVCAP == req->command) { + req = read_devcap_done(dev_context, req, data1); + } else if (MHL_READ_EDID_BLOCK == req->command) { + req = read_edid_done(dev_context, req, data1); + } else if (MHL_WRITE_STAT == req->command) { + req = write_stat_done(dev_context, req, data1); +#endif +#if 0 + } else if (MHL_WRITE_BURST == req->command) { + req = write_burst_done(dev_context, req, data1); + } else if (MHL_SET_INT == req->command) { + req = set_int_done(dev_context, req, data1); +#endif + } else if (MHL_SEND_3D_REQ_OR_FEAT_REQ == req->command) { + MHL_TX_DBG_ERR("3D_REQ or FEAT_REQ completed\n"); + } else { + MHL_TX_DBG_INFO("default\n" + "\tcommand: 0x%02X reg: 0x%02x reg_data: " + "0x%02x burst_offset: 0x%02x msg_data[0]: " + "0x%02x msg_data[1]: 0x%02x\n", + req->command, + req->reg, req->reg_data, + req->burst_offset, + req->msg_data[0], req->msg_data[1]); + } + + if (req != NULL) + return_cbus_queue_entry(dev_context, req); + +} + +void si_mhl_tx_process_vc_assign_burst(struct mhl_dev_context *dev_context, + uint8_t *write_burst_data) +{ + struct tdm_alloc_burst *tdm_burst; + uint8_t idx = 0; + int8_t save_burst_TDM_VC_E_MSC_idx = -1; + int8_t save_burst_TDM_VC_T_CBUS_idx = -1; + + tdm_burst = (struct tdm_alloc_burst *)write_burst_data; + + if (0 != calculate_generic_checksum(write_burst_data, 0, 16)) { + uint8_t i; + for (i = 0; i < 16; i++) + MHL_TX_DBG_ERR("0x%02X\n", write_burst_data[i]); + + MHL_TX_DBG_ERR("Bad checksum in virtual channel assign\n"); + return; + } + + /* The virtual channel assignment in the WRITE_BURST may contain one, + * two or three channel allocations + */ + if (tdm_burst->num_entries_this_burst > 3) { + MHL_TX_DBG_ERR("Bad number of assignment requests in " + "virtual channel assign\n"); + return; + } + + for (idx = 0; idx < VC_MAX; idx++) { + dev_context->prev_virt_chan_slot_counts[idx] = + dev_context->virt_chan_slot_counts[idx]; + } + + for (idx = 0; idx < tdm_burst->num_entries_this_burst; idx++) { + switch (tdm_burst->vc_info[idx].feature_id) { + case FEATURE_ID_E_MSC: + dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = + tdm_burst->vc_info[idx].req_resp.channel_size; + save_burst_TDM_VC_E_MSC_idx = idx; + break; + case FEATURE_ID_USB: + dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = + tdm_burst->vc_info[idx].req_resp.channel_size; + save_burst_TDM_VC_T_CBUS_idx = idx; + break; + default: + tdm_burst->vc_info[idx].req_resp.channel_size = + VC_RESPONSE_BAD_FEATURE_ID; + break; + } + } + + if (si_mhl_tx_drv_set_tdm_slot_allocation + ((struct drv_hw_context *)&dev_context->drv_context, + dev_context->virt_chan_slot_counts, false)) { + + MHL_TX_DBG_INFO("Source will reject request to assign " + "CBUS virtual channels\n"); + + for (idx = 0; idx < VC_MAX; idx++) + dev_context->virt_chan_slot_counts[idx] = + dev_context->prev_virt_chan_slot_counts[idx]; + + if (save_burst_TDM_VC_E_MSC_idx >= 0) + tdm_burst->vc_info[save_burst_TDM_VC_E_MSC_idx]. + req_resp.channel_size = + VC_RESPONSE_BAD_CHANNEL_SIZE; + if (save_burst_TDM_VC_T_CBUS_idx >= 0) + tdm_burst->vc_info[save_burst_TDM_VC_T_CBUS_idx]. + req_resp.channel_size = + VC_RESPONSE_BAD_CHANNEL_SIZE; + + } else { + if (save_burst_TDM_VC_E_MSC_idx >= 0) + tdm_burst->vc_info[save_burst_TDM_VC_E_MSC_idx]. + req_resp.channel_size = VC_RESPONSE_ACCEPT; + if (save_burst_TDM_VC_T_CBUS_idx >= 0) + tdm_burst->vc_info[save_burst_TDM_VC_T_CBUS_idx]. + req_resp.channel_size = VC_RESPONSE_ACCEPT; + + } + + /* Respond back to requester to indicate acceptance or rejection */ + tdm_burst->header.burst_id.high = burst_id_VC_CONFIRM >> 8; + tdm_burst->header.burst_id.low = (uint8_t) burst_id_VC_CONFIRM; + tdm_burst->header.checksum = 0; + tdm_burst->header.checksum = + calculate_generic_checksum((uint8_t *) (tdm_burst), 0, + sizeof(*tdm_burst)); + + si_mhl_tx_request_write_burst(dev_context, 0, sizeof(*tdm_burst), + (uint8_t *) (tdm_burst)); + /* the actual assignment will occur when the + write_burst is completed*/ +} + +void si_mhl_tx_process_vc_confirm_burst(struct mhl_dev_context *dev_context, + uint8_t *write_burst_data) +{ + struct tdm_alloc_burst *tdm_burst; + bool assign_ok = true; + + tdm_burst = (struct tdm_alloc_burst *)write_burst_data; + + if (0 != calculate_generic_checksum(write_burst_data, 0, 16)) { + uint8_t i; + for (i = 0; i < 16; i++) + MHL_TX_DBG_ERR("0x%02X\n", write_burst_data[i]); + + MHL_TX_DBG_ERR("Bad checksum in virtual channel confirm\n"); + return; + } + + if (tdm_burst->vc_info[TDM_VC_E_MSC - 1].req_resp.response != + VC_RESPONSE_ACCEPT) { + MHL_TX_DBG_WARN("Sink rejected request to assign" + "%d slots to E_MSC virtual channel with status: 0x%02x\n", + dev_context-> + virt_chan_slot_counts[TDM_VC_E_MSC], + tdm_burst->vc_info[TDM_VC_E_MSC - + 1].req_resp.response); + assign_ok = false; + } + + if (tdm_burst->vc_info[TDM_VC_T_CBUS - 1].req_resp.response != + VC_RESPONSE_ACCEPT) { + MHL_TX_DBG_WARN("Sink rejected request to assign" + "%d slots to T_CBUS virtual channel with status: 0x%02x\n", + dev_context-> + virt_chan_slot_counts[TDM_VC_T_CBUS], + tdm_burst->vc_info[TDM_VC_T_CBUS - + 1].req_resp.response); + assign_ok = false; + } + + if (assign_ok) { + si_mhl_tx_drv_set_tdm_slot_allocation((struct drv_hw_context *) + &dev_context->drv_context, + dev_context-> + virt_chan_slot_counts, + true); + MHL_TX_DBG_ERR + ("Sink accepted requested virtual channel assignments\n"); + } else { + dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = + dev_context->prev_virt_chan_slot_counts[TDM_VC_E_MSC]; + dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] = + dev_context->prev_virt_chan_slot_counts[TDM_VC_T_CBUS]; + } +} + +struct cbus_req *bist_ready_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} + +void send_bist_ready(struct mhl_dev_context *dev_context) +{ + si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_BIST_READY, + dev_context->bist_ready_status, bist_ready_done); +} +void si_mhl_tx_process_bist_setup_burst(struct mhl_dev_context *dev_context, + uint8_t *write_burst_data) +{ + struct bist_setup_burst *bist_setup; + enum cbus_mode_e cbus_mode; + uint8_t tmp; + uint8_t setup_status = 0; + + MHL_TX_DBG_INFO("\n"); + + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + switch (cbus_mode) { + case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *)&dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); + break; + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + case CM_oCBUS_PEER_IS_MHL3: + + break; + default: + MHL_TX_DBG_ERR("BIST_SETUP received while CBUS mode is %s\n", + si_mhl_tx_drv_get_cbus_mode_str(cbus_mode)); + return; + } + bist_setup = (struct bist_setup_burst *)write_burst_data; + + dev_context->misc_flags.flags.bist_role_TE = 0; + /* + * Validate the received BIST setup info and if it checks out + * save it for use later when a BIST test is requested. + */ + tmp = calculate_generic_checksum(write_burst_data, 0, + sizeof(*bist_setup)); + if (tmp != 0) { + MHL_TX_DBG_ERR("Bad checksum (0x%02x) in BIST setup burst\n", + tmp); + return; + } + + mhl_tx_stop_timer(dev_context, dev_context->cbus_mode_up_timer); + + dev_context->bist_setup.avlink_data_rate = bist_setup->avlink_data_rate; + dev_context->bist_setup.avlink_duration = bist_setup->avlink_duration; + dev_context->bist_setup.avlink_fixed_pat = + (bist_setup->avlink_fixed_h << 8) | bist_setup->avlink_fixed_l; + dev_context->bist_setup.avlink_pattern = bist_setup->avlink_pattern; + dev_context->bist_setup.avlink_randomizer = + bist_setup->avlink_randomizer; + dev_context->bist_setup.avlink_video_mode = + bist_setup->avlink_video_mode; + dev_context->bist_setup.e_cbus_duration = bist_setup->e_cbus_duration; + dev_context->bist_setup.e_cbus_fixed_pat = + (bist_setup->e_cbus_fixed_h << 8) | bist_setup->e_cbus_fixed_l; + dev_context->bist_setup.e_cbus_pattern = bist_setup->e_cbus_pattern; + dev_context->bist_setup.impedance_mode = bist_setup->impedance_mode; + + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY); + switch (bist_setup->impedance_mode) { + case BIST_IMPEDANCE_MODE_AVLINK_TX_LOW: + case BIST_IMPEDANCE_MODE_AVLINK_TX_HIGH: + case BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW: + case BIST_IMPEDANCE_MODE_ECBUS_S_TX_HIGH: + break; + case BIST_IMPEDANCE_MODE_ECBUS_D_TX_LOW: + case BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH: + if (si_mhl_tx_drv_support_e_cbus_d( + (struct drv_hw_context *)&dev_context->drv_context)) { + break; + } + default: + MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " + "IMPEDANCE_MODE field\n", + bist_setup->impedance_mode); + setup_status |= BIST_READY_TERM_ERROR; + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + break; + } + + if (bist_setup->e_cbus_duration == 0) { + MHL_TX_DBG_ERR("Invalid value 0x00 specified in " + "eCBUS_DURATION field\n"); + setup_status |= BIST_READY_E_CBUS_ERROR | BIST_READY_TERM_ERROR; + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + } + + if (bist_setup->e_cbus_pattern > BIST_ECBUS_PATTERN_MAX) { + MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " + "eCBUS_PATTERN field\n", + bist_setup->e_cbus_pattern); + setup_status |= BIST_READY_E_CBUS_ERROR; + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + } + + if (bist_setup->avlink_pattern > BIST_AVLINK_PATTERN_MAX) { + MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " + "AVLINK_PATTERN field\n", + bist_setup->avlink_pattern); + setup_status |= BIST_READY_AVLINK_ERROR; + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + } + + if (!(bist_setup->avlink_video_mode == 3 || + bist_setup->avlink_video_mode == 4)) { + MHL_TX_DBG_ERR("Invalid value specified in " + "AVLINK_VIDEO_MODE field\n"); + setup_status |= BIST_READY_AVLINK_ERROR; + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + } + + if (bist_setup->avlink_randomizer > 1) { + MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " + "AVLINK_RANDOMIZER field\n", + bist_setup->avlink_randomizer); + setup_status |= BIST_READY_AVLINK_ERROR; + si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + } + + /* + * Make sure the specified AV data rate is valid and supported by the + * transmitter. + */ + if (bist_setup->avlink_data_rate == 0 || + bist_setup->avlink_data_rate > 3) { + MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " + "AVLINK_PATTERN field\n", + bist_setup->avlink_data_rate); + setup_status |= BIST_READY_AVLINK_ERROR; + } + + if (!(setup_status & BIST_READY_E_CBUS_ERROR)) + setup_status |= BIST_READY_E_CBUS_READY; + if (!(setup_status & BIST_READY_AVLINK_ERROR)) + setup_status |= BIST_READY_AVLINK_READY; + if (!(setup_status & BIST_READY_TERM_ERROR)) + setup_status |= BIST_READY_TERM_READY; + + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + if (CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY == cbus_mode) { + mhl_tx_stop_timer(dev_context, dev_context->cbus_mode_up_timer); + MHL_TX_DBG_ERR("\n") + dev_context->bist_ready_status = setup_status; + setup_sans_cbus1(dev_context); + } + +} + +void si_mhl_tx_process_write_burst_data(struct mhl_dev_context *dev_context) +{ + int ret_val = 0; + enum BurstId_e burst_id; + enum cbus_mode_e cbus_mode; + + MHL_TX_DBG_INFO("\n"); + cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context); + + /* continue else statement to support 3D along with MDT */ + ret_val = si_mhl_tx_drv_get_scratch_pad( + (struct drv_hw_context *)(&dev_context->drv_context), + 0, + dev_context->incoming_scratch_pad.asBytes, + sizeof(dev_context->incoming_scratch_pad)); + if (ret_val < 0) { + MHL_TX_DBG_ERR("scratch pad failure 0x%x\n", ret_val); + } else { + burst_id = + BURST_ID(dev_context->incoming_scratch_pad.videoFormatData. + header.burst_id); + + MHL_TX_DBG_WARN("Got WRITE_BURST {0x%04x}\n", burst_id); + + switch (burst_id) { + case burst_id_3D_VIC: + si_mhl_tx_process_3d_vic_burst( + dev_context->edid_parser_context, + &dev_context->incoming_scratch_pad. + videoFormatData); + break; + + case burst_id_3D_DTD: + si_mhl_tx_process_3d_dtd_burst( + dev_context->edid_parser_context, + &dev_context->incoming_scratch_pad. + videoFormatData); + break; + case burst_id_HEV_VIC: + si_mhl_tx_process_hev_vic_burst( + dev_context->edid_parser_context, + &dev_context->incoming_scratch_pad. + hev_vic_data); + break; + + case burst_id_HEV_DTDA: + si_mhl_tx_process_hev_dtd_a_burst( + dev_context->edid_parser_context, + &dev_context->incoming_scratch_pad. + hev_dtd_a_data); + break; + case burst_id_HEV_DTDB: + si_mhl_tx_process_hev_dtd_b_burst( + dev_context->edid_parser_context, + &dev_context->incoming_scratch_pad. + hev_dtd_b_data); + break; + + case burst_id_VC_ASSIGN: + si_mhl_tx_process_vc_assign_burst(dev_context, + (uint8_t *)(&dev_context-> + incoming_scratch_pad)); + break; + + case burst_id_VC_CONFIRM: + si_mhl_tx_process_vc_confirm_burst(dev_context, + (uint8_t *)(&dev_context-> + incoming_scratch_pad)); + break; + case burst_id_AUD_DELAY: + + mhl_event_notify(dev_context, + MHL_EVENT_AUD_DELAY_RCVD, 0x00, + &dev_context->incoming_scratch_pad); + break; + + case burst_id_ADT_BURSTID: + break; + + case burst_id_BIST_SETUP: + si_mhl_tx_process_bist_setup_burst(dev_context, + (uint8_t *)(&dev_context-> + incoming_scratch_pad)); + break; + + case burst_id_BIST_RETURN_STAT: + { + struct bist_return_stat_burst *bist_status; + bist_status = (struct bist_return_stat_burst *) + dev_context->incoming_scratch_pad.asBytes; + if (0 != calculate_generic_checksum( + (uint8_t *)bist_status, 0, + sizeof(*bist_status))) { + MHL_TX_DBG_ERR + ("BIST_RETURN_STAT received " + "with bad checksum\n"); + } else { + uint16_t e_cbus_remote_stat; + uint16_t av_link_stat; + bool status; + e_cbus_remote_stat = + bist_status->e_cbus_stat_h << 8; + e_cbus_remote_stat |= + bist_status->e_cbus_stat_l; + av_link_stat = bist_status->avlink_stat_h << 8; + av_link_stat |= bist_status->avlink_stat_l; + dev_context->bist_stat.e_cbus_remote_stat = + e_cbus_remote_stat; + dev_context->bist_stat.avlink_stat = + av_link_stat; + MHL_TX_DBG_ERR + ("BIST_RETURN_STAT received eCBUS_STAT" + " remote:%s0x%04x%s" + " local:%s0x%06x%s" + " AV_LINK_STAT %s0x%04x%s\n", + ANSI_ESC_GREEN_TEXT, + e_cbus_remote_stat, + ANSI_ESC_RESET_TEXT, + ANSI_ESC_GREEN_TEXT, + dev_context->bist_stat. + e_cbus_prev_local_stat, + ANSI_ESC_RESET_TEXT, + ANSI_ESC_GREEN_TEXT, + av_link_stat, + ANSI_ESC_RESET_TEXT); + mhl_event_notify(dev_context, + MHL_TX_EVENT_BIST_STATUS_RECEIVED, + 0x00, &(dev_context->bist_stat)); + /* + * BIST run completed + */ + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + MHL_TX_DBG_ERR( + "%sissuing CBUS_MODE_UP%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + #ifdef BIST_DONE_DEBUG + si_dump_important_regs( + (struct drv_hw_context *) + &dev_context->drv_context); + #endif + status = si_mhl_tx_rap_send( + dev_context, + MHL_RAP_CBUS_MODE_UP); + if (status) { + si_mhl_tx_drive_states( + dev_context); + } + } + } + break; + + case burst_id_BIST_DISCARD: + { + struct bist_discard_burst *bist_discard; + bist_discard = (struct bist_discard_burst *) + dev_context->incoming_scratch_pad.asBytes; + MHL_TX_DBG_ERR("BIST_DISCARD:0x%x\n", + bist_discard->hdr.remaining_length) + } + break; + + case burst_id_BIST_ECHO_REQUEST: + { + struct bist_echo_request_burst *echo_request; + struct bist_echo_response_burst echo_response; + int i; + echo_request = (struct bist_echo_request_burst *) + dev_context->incoming_scratch_pad.asBytes; + echo_response.hdr.burst_id.high = + HIGH_BYTE_16(burst_id_BIST_ECHO_RESPONSE); + echo_response.hdr.burst_id.low = + LOW_BYTE_16(burst_id_BIST_ECHO_RESPONSE); + echo_response.hdr.remaining_length = + echo_request->hdr.remaining_length; + for (i = 0; + (i < echo_request->hdr.remaining_length) + && (i < sizeof(echo_response.payload)); + ++i) { + echo_response.payload[i] = + echo_request->payload[i]; + } + si_mhl_tx_request_write_burst( + dev_context, 0, sizeof(echo_response), + (uint8_t *)&echo_response); + } + break; + + case burst_id_BIST_ECHO_RESPONSE: + break; + + case burst_id_EMSC_SUPPORT: + break; + + case burst_id_HID_PAYLOAD: + break; + + case burst_id_BLK_RCV_BUFFER_INFO: + break; + + case burst_id_BITS_PER_PIXEL_FMT: + break; + case LOCAL_ADOPTER_ID: +#ifdef MEDIA_DATA_TUNNEL_SUPPORT + case MHL_TEST_ADOPTER_ID: + if (CM_oCBUS_PEER_IS_MHL3_BIST_STAT == cbus_mode) { + MHL_TX_DBG_ERR("MHL_TEST_ID 0x%04x\n", + burst_id); + si_mhl_tx_drv_switch_cbus_mode( + (struct drv_hw_context *) + &dev_context->drv_context, + CM_oCBUS_PEER_IS_MHL3); + } else { + si_mhl_tx_mdt_process_packet(dev_context, + (void *)&dev_context->incoming_scratch_pad. + asBytes); + } +#else + /* + * Cause a notification event to be raised to allow + * interested applications a chance to process the + * received write burst data. + */ + mhl_event_notify(dev_context, + MHL_TX_EVENT_SPAD_RECEIVED, + sizeof(dev_context->incoming_scratch_pad), + dev_context->incoming_scratch_pad.asBytes); +#endif + break; + + default: + MHL_TX_DBG_ERR("Dropping write burst with " + "invalid adopter id: 0x%04x\n", + burst_id); + break; + } + } +} + +static bool si_mhl_tx_set_path_en(struct mhl_dev_context *dev_context) +{ + MHL_TX_DBG_INFO("called\n"); + si_mhl_tx_drv_enable_video_path((struct drv_hw_context *) + (&dev_context->drv_context)); + dev_context->link_mode |= MHL_STATUS_PATH_ENABLED; + return si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_LINK_MODE, + dev_context->link_mode); +} + +static bool si_mhl_tx_clr_path_en(struct mhl_dev_context *dev_context) +{ + MHL_TX_DBG_INFO("called\n"); + si_mhl_tx_drv_disable_video_path((struct drv_hw_context *) + (&dev_context->drv_context)); + dev_context->link_mode &= ~MHL_STATUS_PATH_ENABLED; + return si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_LINK_MODE, + dev_context->link_mode); +} + +static void si_mhl_tx_refresh_peer_devcap_entries_impl(struct mhl_dev_context + *dev_context, + const char *function, + int line) +{ + MHL_TX_DBG_INFO("from %s:%d\n", function, line); + MHL_TX_DBG_INFO("DCAP_RDY DEVCAP: %s\n", + dev_context->misc_flags.flags. + have_complete_devcap ? "current" : "stale"); + + /* + * If there is a DEV CAP read operation in progress + * cancel it and issue a new DEV CAP read to make sure + * we pick up all the DEV CAP register changes. + */ + if (dev_context->current_cbus_req != NULL) { + if (dev_context->current_cbus_req->command == MHL_READ_DEVCAP) { + dev_context->current_cbus_req->status.flags.cancel = + true; + MHL_TX_DBG_ERR("%s cancelling MHL_READ_DEVCAP%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + } + si_mhl_tx_read_devcap(dev_context); + + MHL_TX_DBG_INFO("mhl_version:0x%x\n", + dev_context->dev_cap_cache.mdc.mhl_version); +} + +/* + * si_mhl_tx_got_mhl_intr + * + * This function is called to inform of the arrival + * of an MHL INTERRUPT message. + */ +void si_mhl_tx_got_mhl_intr(struct mhl_dev_context *dev_context, + uint8_t intr_0, uint8_t intr_1) +{ + MHL_TX_DBG_INFO("INTERRUPT Arrived. %02X, %02X\n", intr_0, intr_1); + + /* Handle DCAP_CHG INTR here */ + if (MHL_INT_DCAP_CHG & intr_0) { + MHL_TX_DBG_WARN("got DCAP_CHG stopping timer...\n"); + mhl_tx_stop_timer(dev_context, dev_context->dcap_chg_timer); + if (MHL_STATUS_DCAP_RDY & dev_context->status_0) { + MHL_TX_DBG_WARN("got DCAP_CHG & DCAP_RDY\n"); + if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) { + if (si_mhl_tx_drv_get_cbus_mode(dev_context) < + CM_eCBUS_S) { + si_mhl_tx_read_xdevcap_reg(dev_context, + XDEVCAP_ADDR_ECBUS_SPEEDS); + } else { + si_mhl_tx_read_xdevcap(dev_context); + } + } else { + si_mhl_tx_refresh_peer_devcap_entries + (dev_context); + } + } + } + + if (MHL_INT_DSCR_CHG & intr_0) { + /* remote WRITE_BURST is complete */ + dev_context->misc_flags.flags.rcv_scratchpad_busy = false; + si_mhl_tx_process_write_burst_data(dev_context); + } + + if (MHL_INT_REQ_WRT & intr_0) { + /* Scratch pad write request from the sink device. */ + if (dev_context->misc_flags.flags.rcv_scratchpad_busy) { + /* + * Use priority 1 to defer sending grant until + * local traffic is done + */ + si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, + MHL_INT_GRT_WRT, 1); + } else { + /* use priority 0 to respond immediately */ + si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, + MHL_INT_GRT_WRT, 0); + } + } + + if (MHL3_INT_FEAT_REQ & intr_0) { + /* Send write bursts for all features that we support */ + + struct MHL3_emsc_support_data_t emsc_support = { + {ENCODE_BURST_ID(burst_id_EMSC_SUPPORT), + 0, /* checksum starts at 0 */ + 1, /* total entries */ + 1 /* sequence id */ + }, + 1, /* num entries this burst */ + { + { + ENCODE_BURST_ID(burst_id_HID_PAYLOAD), + ENCODE_BURST_ID(0), + ENCODE_BURST_ID(0), + ENCODE_BURST_ID(0), + ENCODE_BURST_ID(0) + } + } + }; + struct MHL3_adt_data_t adt_burst = { + { + ENCODE_BURST_ID(burst_id_ADT_BURSTID), + 0, /* checksum starts at 0 */ + 1, /* total entries */ + 1 /* sequence id */ + }, + { + 0, /* format flags */ + .descriptors = { + .short_descs = {0, 0, 0, 0, 0, 0, 0, 0, 0} +#if 0 + .spkr_alloc_db = { + .cea861f_spkr_alloc = {0, 0, 0}, + .cea861f_spkr_alloc = {0, 0, 0}, + .cea861f_spkr_alloc = {0, 0, 0} + } +#endif + } + } + }; + MHL_TX_DBG_WARN("%sGot FEAT_REQ%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + emsc_support.header.checksum = + calculate_generic_checksum((uint8_t *) &emsc_support, 0, + sizeof(emsc_support)); + adt_burst.header.checksum = + calculate_generic_checksum((uint8_t *) &adt_burst, 0, + sizeof(adt_burst)); + + /* Step 6: Store HEV_VIC in Burst Out-Queue. */ + if (false == + si_mhl_tx_send_write_burst(dev_context, &emsc_support)) { + MHL_TX_DBG_ERR("%scbus queue flooded%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } else if (false == + si_mhl_tx_send_write_burst(dev_context, + &adt_burst)) { + MHL_TX_DBG_ERR("%scbus queue flooded%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + /* send with normal priority */ + si_mhl_tx_set_int(dev_context, MHL_RCHANGE_INT, + MHL3_INT_FEAT_COMPLETE, 1); + } + + if (MHL3_INT_FEAT_COMPLETE & intr_0) { + /* sink is finished sending write bursts in response to + * MHL3_INT_FEAT_REQ + */ + si_mhl_tx_display_timing_enumeration_end(dev_context-> + edid_parser_context); + } + + if (MHL_INT_EDID_CHG & intr_1) { + + dev_context->edid_valid = false; + MHL_TX_DBG_INFO("MHL_INT_EDID_CHG\n"); + si_edid_reset(dev_context->edid_parser_context); + if (dev_context->misc_flags.flags.have_complete_devcap) { + if (dev_context->misc_flags.flags.mhl_hpd) { + MHL_TX_DBG_INFO("tag: EDID_CHG\n"); + si_mhl_tx_initiate_edid_sequence( + dev_context->edid_parser_context); + } + } + } +} + +/* + * si_si_mhl_tx_check_av_link_status + */ +static void si_mhl_tx_check_av_link_status(struct mhl_dev_context *dev_context) +{ + /* is TMDS normal */ + if (MHL_XDS_LINK_STATUS_TMDS_NORMAL & dev_context->xstatus_1) { + MHL_TX_DBG_WARN("AV LINK_MODE_STATUS is NORMAL.\n"); + if (dev_context->misc_flags.flags.edid_loop_active) { + MHL_TX_DBG_ERR("EDID busy\n"); + } else if (!dev_context->misc_flags.flags.mhl_hpd) { + MHL_TX_DBG_ERR( + "No HPD when receiving AV LINK_MODE_STATUS\n"); + } else { + si_mhl_tx_drv_start_cp(dev_context); + } + } else { + MHL_TX_DBG_WARN("AV LINK_MODE_STATUS not NORMAL\n"); + si_mhl_tx_drv_shut_down_HDCP2((struct drv_hw_context *) + (&dev_context->drv_context)); + } +} + +/* + * si_mhl_tx_got_mhl_status + * + * This function is called by the driver to inform of arrival of a MHL STATUS. + */ +void si_mhl_tx_got_mhl_status(struct mhl_dev_context *dev_context, + struct mhl_device_status *dev_status) +{ + uint8_t status_change_bit_mask_0; + uint8_t status_change_bit_mask_1; + uint8_t xstatus_change_bit_mask_1; + uint8_t xstatus_change_bit_mask_3; + + MHL_TX_DBG_WARN + ("STATUS Arrived. %02X, %02X %02X : %02X %02X %02X %02X\n", + dev_status->write_stat[0], dev_status->write_stat[1], + dev_status->write_stat[2], dev_status->write_xstat[0], + dev_status->write_xstat[1], dev_status->write_xstat[2], + dev_status->write_xstat[3]); + /* + * Handle DCAP_RDY STATUS here itself + */ + status_change_bit_mask_0 = + dev_status->write_stat[0] ^ dev_context->status_0; + status_change_bit_mask_1 = + dev_status->write_stat[1] ^ dev_context->status_1; + xstatus_change_bit_mask_1 = + dev_status->write_xstat[1] ^ dev_context->xstatus_1; + xstatus_change_bit_mask_3 = + dev_status->write_xstat[3] ^ dev_context->xstatus_3; + + /* + * Remember the event. (other code checks the saved values, + * so save the values early, but not before the XOR operations above) + */ + dev_context->status_0 = dev_status->write_stat[0]; + dev_context->status_1 = dev_status->write_stat[1]; + dev_context->xstatus_1 = dev_status->write_xstat[1]; + dev_context->xstatus_3 = dev_status->write_xstat[3]; + + if (0xFF & dev_status->write_stat[2]) { + if (0 == dev_context->peer_mhl3_version) { + dev_context->peer_mhl3_version = + dev_status->write_stat[2]; + } + MHL_TX_DBG_WARN("Got VERSION_STAT->MHL_VER = %02x\n", + dev_context->peer_mhl3_version); + } + + if (MHL_STATUS_DCAP_RDY & status_change_bit_mask_0) { + MHL_TX_DBG_WARN("DCAP_RDY changed\n"); + if (MHL_STATUS_DCAP_RDY & dev_status->write_stat[0]) { + if (dev_context->peer_mhl3_version >= 0x30) { + MHL_TX_DBG_INFO("Assuming minimum MHL3.x" + " feature support\n"); + dev_context->dev_cap_cache.mdc.featureFlag |= + MHL_FEATURE_RCP_SUPPORT | + MHL_FEATURE_RAP_SUPPORT | + MHL_FEATURE_SP_SUPPORT; + } + mhl_tx_stop_timer(dev_context, + dev_context->dcap_rdy_timer); + /* some dongles send DCAP_RDY, but not DCAP_CHG */ + mhl_tx_start_timer(dev_context, + dev_context->dcap_chg_timer, 2000); + if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) + MHL_TX_DBG_WARN("waiting for DCAP_CHG\n"); + } + } + /* look for a change in the pow bit */ + if (MHL_STATUS_POW_STAT & status_change_bit_mask_0) { + uint8_t sink_drives_vbus = + (MHL_STATUS_POW_STAT & dev_status->write_stat[0]); + uint8_t param = 0; + + if (sink_drives_vbus) { + uint8_t index; + + index = dev_status->write_stat[0]; + index &= MHL_STATUS_PLIM_STAT_MASK; + index >>= 3; + param = MHL_DEV_CATEGORY_POW_BIT; + /* + * Since downstream device is supplying VBUS power, + * turn off our VBUS power here. If the platform + * application can control VBUS power it should turn + * off it's VBUS power now. + */ + MHL_TX_DBG_WARN("%ssink drives VBUS%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + /* limit incoming current */ + mhl_tx_vbus_control(VBUS_OFF); + mhl_tx_vbus_current_ctl(plim_table[index]); + } else { + MHL_TX_DBG_WARN("%ssource drives VBUS" + " according to PLIM_STAT%s\n", + ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); + /* limit outgoing current */ + mhl_tx_vbus_control(VBUS_ON); + } + /* we only get POW_STAT if the sink is MHL3 or newer, + * so update the POW bit + */ + dev_context->dev_cap_cache.mdc.deviceCategory &= + ~MHL_DEV_CATEGORY_POW_BIT; + dev_context->dev_cap_cache.mdc.deviceCategory |= param; + /* Inform interested Apps of the MHL power change */ + mhl_event_notify(dev_context, MHL_TX_EVENT_POW_BIT_CHG, + param, NULL); + } + + /* did PATH_EN change? */ + if (MHL_STATUS_PATH_ENABLED & status_change_bit_mask_1) { + MHL_TX_DBG_INFO("PATH_EN changed\n"); + if (MHL_STATUS_PATH_ENABLED & dev_status->write_stat[1]) + si_mhl_tx_set_path_en(dev_context); + else + si_mhl_tx_clr_path_en(dev_context); + } + + if (xstatus_change_bit_mask_1) { + MHL_TX_DBG_WARN("link mode status changed %02X\n", + xstatus_change_bit_mask_1); + si_mhl_tx_check_av_link_status(dev_context); + } +} +struct cbus_req *rcp_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} +/* + * si_mhl_tx_rcp_send + * + * This function checks if the peer device supports RCP and sends rcpKeyCode. + * The function will return a value of true if it could successfully send the + * RCP subcommand and the key code. Otherwise false. + * + */ +bool si_mhl_tx_rcp_send(struct mhl_dev_context *dev_context, + uint8_t rcpKeyCode) +{ + bool status; + + MHL_TX_DBG_INFO("called\n"); + + /* + * Make sure peer supports RCP + */ + if (dev_context->dev_cap_cache.mdc.featureFlag & + MHL_FEATURE_RCP_SUPPORT) { + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RCP, + rcpKeyCode, rcp_done); + if (status) + si_mhl_tx_drive_states(dev_context); + } else { + MHL_TX_DBG_ERR("failed, peer feature flag = 0x%X\n", + dev_context->dev_cap_cache.mdc.featureFlag); + status = false; + } + + return status; +} + +struct cbus_req *ucp_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} + +/* + * si_mhl_tx_ucp_send + * + * This function is (indirectly) called by a host application to send + * a UCP key code to the downstream device. + * + * Returns true if the key code can be sent, false otherwise. + */ +bool si_mhl_tx_ucp_send(struct mhl_dev_context *dev_context, + uint8_t ucp_key_code) +{ + bool status; + MHL_TX_DBG_INFO("called key code: 0x%02x\n", ucp_key_code); + + /* + * Make sure peer supports UCP and that the connection is + * in a state where a UCP message can be sent. + */ + if (dev_context->dev_cap_cache.mdc.featureFlag & + MHL_FEATURE_UCP_RECV_SUPPORT) { + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_UCP, + ucp_key_code, ucp_done); + if (status) + si_mhl_tx_drive_states(dev_context); + } else { + MHL_TX_DBG_ERR("failed\n"); + status = false; + } + return status; +} + +struct cbus_req *ucpk_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + + return req; +} +/* + * si_mhl_tx_ucp_send + * + * This function is (indirectly) called by a host application to send + * a UCP acknowledge message for a received UCP key code message. + * + * Returns true if the message can be sent, false otherwise. + */ +bool si_mhl_tx_ucpk_send(struct mhl_dev_context *dev_context, + uint8_t ucp_key_code) +{ + bool status; + MHL_TX_DBG_INFO("called key code: 0x%02x\n", ucp_key_code); + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_UCPK, + ucp_key_code, ucpk_done); + return status; +} + +struct cbus_req *ucpe_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + /* + * UCPE is always followed by an UCPK with + * original key code received. + */ + si_mhl_tx_ucpk_send(dev_context, dev_context->msc_save_ucp_key_code); + return req; +} +/* + * si_mhl_tx_ucpe_send + * + * This function is (indirectly) called by a host application to send a + * UCP negative acknowledgment message for a received UCP key code message. + * + * Returns true if the message can be sent, false otherwise. + * + * When successful, mhl_tx internally sends UCPK with original (last known) + * UCP keycode. + */ +bool si_mhl_tx_ucpe_send(struct mhl_dev_context *dev_context, + uint8_t ucpe_error_code) +{ + MHL_TX_DBG_INFO("called\n"); + + return si_mhl_tx_send_msc_msg + (dev_context, MHL_MSC_MSG_UCPE, ucpe_error_code, ucpe_done); +} + +#if (INCLUDE_RBP == 1) +struct cbus_req *rbp_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} + +/* + * si_mhl_tx_rbp_send + * + * This function checks if the peer device supports RBP and sends rbpButtonCode. + * The function will return a value of true if it could successfully send the + * RBP subcommand and the button code. Otherwise false. + * + */ +bool si_mhl_tx_rbp_send(struct mhl_dev_context *dev_context, + uint8_t rbpButtonCode) +{ + bool status; + + MHL_TX_DBG_INFO("called\n"); + + /* + * Make sure peer supports RBP + */ + if (dev_context->dev_cap_cache.mdc.featureFlag & + MHL_FEATURE_RBP_SUPPORT) { + + status = + si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RBP, + rbpButtonCode, rbp_done); + if (status) + si_mhl_tx_drive_states(dev_context); + } else { + MHL_TX_DBG_ERR("failed, peer feature flag = 0x%X\n", + dev_context->dev_cap_cache.mdc.featureFlag); + status = false; + } + + return status; +} + +struct cbus_req *rbpk_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} +/* + * si_mhl_tx_rbpk_send + * + * This function is (indirectly) called by a host application to send + * a RBP acknowledge message for a received RBP button code message. + * + * Returns true if the message can be sent, false otherwise. + */ +bool si_mhl_tx_rbpk_send(struct mhl_dev_context *dev_context, + uint8_t rbp_button_code) +{ + bool status; + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RBPK, + rbp_button_code, rbpk_done); + if (status) + si_mhl_tx_drive_states(dev_context); + + return status; +} + +struct cbus_req *rbpe_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + /* + * RBPE is always followed by an RBPK with + * original button code received. + */ + MHL_TX_DBG_ERR("\n") + si_mhl_tx_rbpk_send(dev_context, dev_context->msc_save_rbp_button_code); + return req; +} +/* + * si_mhl_tx_rbpe_send + * + * The function will return a value of true if it could successfully send the + * RBPE subcommand. Otherwise false. + * + * When successful, mhl_tx internally sends RBPK with original (last known) + * button code. + */ +bool si_mhl_tx_rbpe_send(struct mhl_dev_context *dev_context, + uint8_t rbpe_error_code) +{ + bool status; + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RBPE, + rbpe_error_code, rbpe_done); + if (status) + si_mhl_tx_drive_states(dev_context); + + return status; +} +#endif + +char *rap_strings_high[4] = { + "POLL", + "CONTENT_", + "CBUS_MODE_", + "out of range" +}; + +char *rap_strings_low[4][2] = { + {"", ""}, + {"ON", "OFF"}, + {"DOWN", "UP"}, + {"OOR", "OOR"}, +}; + +struct cbus_req *rap_done(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + /* the only RAP commands that we send are + * CBUS_MODE_UP AND CBUS_MODE_DOWN. + */ + /* + if (MHL_RAP_CBUS_MODE_DOWN == dev_context->msc_msg_last_data) { + } else if (MHL_RAP_CBUS_MODE_UP == dev_context->msc_msg_last_data) { + } + */ + return req; +} + +/* + * si_mhl_tx_rap_send + * + * This function sends the requested RAP action code message if RAP + * is supported by the downstream device. + * + * The function returns true if the message can be sent, false otherwise. + */ +bool si_mhl_tx_rap_send(struct mhl_dev_context *dev_context, + uint8_t rap_action_code) +{ + bool status; + + MHL_TX_DBG_ERR("%sSend RAP{%s%s}%s\n", ANSI_ESC_GREEN_TEXT, + rap_strings_high[(rap_action_code>>4)&3], + rap_strings_low[(rap_action_code>>4)&3][rap_action_code & 1], + ANSI_ESC_RESET_TEXT); + /* + * Make sure peer supports RAP and that the connection is + * in a state where a RAP message can be sent. + */ + if (si_get_peer_mhl_version(dev_context) >= 0x30) { + /* + * MHL3.0 Requires RAP support, + * no need to check if sink is MHL 3.0 + */ + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RAP, + rap_action_code, rap_done); + + } else + if (dev_context->dev_cap_cache.mdc. + featureFlag & MHL_FEATURE_RAP_SUPPORT) { + + status = si_mhl_tx_send_msc_msg(dev_context, MHL_MSC_MSG_RAP, + rap_action_code, rap_done); + } else { + MHL_TX_DBG_ERR("%sERROR in sending RAP{CBUS_MODE_%s}%s\n", + ANSI_ESC_RED_TEXT, + (rap_action_code == 0x21) ? "UP" : "DOWN", + ANSI_ESC_RESET_TEXT); + status = false; + } + + return status; +} + +/* + * si_mhl_tx_notify_downstream_hpd_change + * + * Handle the arrival of SET_HPD or CLEAR_HPD messages. + * + * Turn the content off or on based on what we got. + */ +void si_mhl_tx_notify_downstream_hpd_change(struct mhl_dev_context *dev_context, + uint8_t downstream_hpd) +{ + + MHL_TX_DBG_INFO("HPD = %s\n", downstream_hpd ? "HIGH" : "LOW"); + + if (0 == downstream_hpd) { + struct cbus_req *req = dev_context->current_cbus_req; + dev_context->misc_flags.flags.mhl_hpd = false; + dev_context->edid_valid = false; + if (req) { + if (MHL_READ_EDID_BLOCK == req->command) { + + return_cbus_queue_entry(dev_context, req); + dev_context->current_cbus_req = NULL; + dev_context->misc_flags.flags.edid_loop_active = + 0; + MHL_TX_DBG_INFO("tag: EDID active: %d\n", + dev_context->misc_flags.flags. + edid_loop_active); + } + } + si_edid_reset(dev_context->edid_parser_context); + } else { + dev_context->misc_flags.flags.mhl_hpd = true; + + /* + * possible EDID read is complete here + * see MHL spec section 5.9.1 + */ + if (dev_context->misc_flags.flags.have_complete_devcap) { + /* Devcap refresh is complete */ + MHL_TX_DBG_INFO("tag:\n"); + si_mhl_tx_initiate_edid_sequence( + dev_context->edid_parser_context); + } + } +} + +/* + * si_mhl_tx_get_peer_dev_cap_entry + * + * index -- the devcap index to get + * *data pointer to location to write data + * + * returns + * 0 -- success + * 1 -- busy. + */ +uint8_t si_mhl_tx_get_peer_dev_cap_entry(struct mhl_dev_context *dev_context, + uint8_t index, uint8_t *data) +{ + if (!dev_context->misc_flags.flags.have_complete_devcap) { + /* update is in progress */ + return 1; + } else { + *data = dev_context->dev_cap_cache.devcap_cache[index]; + return 0; + } +} + +/* + * si_get_scratch_pad_vector + * + * offset + * The beginning offset into the scratch pad from which to fetch entries. + * length + * The number of entries to fetch + * *data + * A pointer to an array of bytes where the data should be placed. + * + * returns: + * scratch_pad_status see si_mhl_tx_api.h for details +*/ +enum scratch_pad_status si_get_scratch_pad_vector(struct mhl_dev_context + *dev_context, uint8_t offset, + uint8_t length, + uint8_t *data) +{ + if (!(dev_context->dev_cap_cache.mdc.featureFlag + & MHL_FEATURE_SP_SUPPORT)) { + + MHL_TX_DBG_INFO("failed SCRATCHPAD_NOT_SUPPORTED\n"); + return SCRATCHPAD_NOT_SUPPORTED; + + } else if (dev_context->misc_flags.flags.rcv_scratchpad_busy) { + return SCRATCHPAD_BUSY; + + } else if ((offset >= sizeof(dev_context->incoming_scratch_pad)) || + (length > + (sizeof(dev_context->incoming_scratch_pad) - offset))) { + return SCRATCHPAD_BAD_PARAM; + } else { + uint8_t *scratch_pad = + dev_context->incoming_scratch_pad.asBytes; + + scratch_pad += offset; + memcpy(data, scratch_pad, length); + } + return SCRATCHPAD_SUCCESS; +} + +#ifdef ENABLE_DUMP_INFOFRAME + +#define AT_ROW_END(i, length) ((i & (length-1)) == (length-1)) + +void DumpIncomingInfoFrameImpl(char *pszId, char *pszFile, int iLine, + info_frame_t *pInfoFrame, uint8_t length) +{ + uint8_t j; + uint8_t *pData = (uint8_t *) pInfoFrame; + + printk(KERN_DEFAULT "mhl_tx: %s: length:0x%02x -- ", pszId, length); + for (j = 0; j < length; j++) { + printk(KERN_DEFAULT "%02X ", pData[j]); + if (AT_ROW_END(j, 32)) + printk("\n"); + } + printk(KERN_DEFAULT "\n"); +} +#endif + +void *si_mhl_tx_get_drv_context(void *context) +{ + struct mhl_dev_context *dev_context = context; + + if (dev_context->signature == MHL_DEV_CONTEXT_SIGNATURE) + return &dev_context->drv_context; + else + return context; +} + +int si_peer_supports_packed_pixel(void *dev_context) +{ + struct mhl_dev_context *dev_context_ptr = + (struct mhl_dev_context *)dev_context; + return PACKED_PIXEL_AVAILABLE(dev_context_ptr); +} + +int si_mhl_tx_shutdown(struct mhl_dev_context *dev_context) +{ + MHL_TX_DBG_INFO("SiI8620 Driver Shutdown.\n"); + mhl_tx_stop_all_timers(dev_context); + si_mhl_tx_drv_shutdown((struct drv_hw_context *)&dev_context-> + drv_context); + return 0; +} + +int si_mhl_tx_ecbus_started(struct mhl_dev_context *dev_context) +{ + MHL_TX_DBG_INFO("eCBUS started\n"); + /* queue up both of these */ + si_mhl_tx_read_xdevcap(dev_context); + return 0; +} diff --git a/drivers/video/fbdev/msm/mhl3/mhl_supp.h b/drivers/video/fbdev/msm/mhl3/mhl_supp.h new file mode 100644 index 000000000000..fa08fb7a0028 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/mhl_supp.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#if !defined(MHL_SUPP_H) +#define MHL_SUPP_H + +/* APIs exported from mhl_supp.c */ + +int si_mhl_tx_get_num_block_reqs(void); +int si_mhl_tx_initialize(struct mhl_dev_context *dev_context); +int si_mhl_tx_reserve_resources(struct mhl_dev_context *dev_context); +void si_mhl_tx_initialize_block_transport(struct mhl_dev_context *dev_context); +void process_cbus_abort(struct mhl_dev_context *dev_context); +void si_mhl_tx_drive_states(struct mhl_dev_context *dev_context); +void si_mhl_tx_push_block_transactions(struct mhl_dev_context *dev_context); + +bool invalid_bist_te_parms(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup_info); + +void initiate_bist_test(struct mhl_dev_context *dev_context); +void si_mhl_tx_process_events(struct mhl_dev_context *dev_context); +uint8_t si_mhl_tx_set_preferred_pixel_format(struct mhl_dev_context + *dev_context, uint8_t clkMode); +void si_mhl_tx_process_write_burst_data(struct mhl_dev_context *dev_context); +void si_mhl_tx_msc_command_done(struct mhl_dev_context *dev_context, + uint8_t data1); +void si_mhl_tx_notify_downstream_hpd_change(struct mhl_dev_context *dev_context, + uint8_t downstream_hpd); +void si_mhl_tx_got_mhl_status(struct mhl_dev_context *dev_context, + struct mhl_device_status *write_stat); +void si_mhl_tx_got_mhl_intr(struct mhl_dev_context *dev_context, + uint8_t intr_0, uint8_t intr_1); +void si_mhl_tx_bist_cleanup(struct mhl_dev_context *dev_context); +enum scratch_pad_status si_mhl_tx_request_write_burst( + struct mhl_dev_context *dev_context, uint8_t reg_offset, + uint8_t length, uint8_t *data); +bool si_mhl_tx_send_msc_msg(struct mhl_dev_context *dev_context, + uint8_t command, uint8_t cmdData, + struct cbus_req *(*completion)(struct mhl_dev_context *dev_context, + struct cbus_req *req, uint8_t data1)); +bool si_mhl_tx_rcp_send(struct mhl_dev_context *dev_context, + uint8_t rcpKeyCode); +bool si_mhl_tx_rcpk_send(struct mhl_dev_context *dev_context, + uint8_t rcp_key_code); +bool si_mhl_tx_rcpe_send(struct mhl_dev_context *dev_context, + uint8_t rcpe_error_code); +bool si_mhl_tx_rbp_send(struct mhl_dev_context *dev_context, + uint8_t rbpButtonCode); +bool si_mhl_tx_rbpk_send(struct mhl_dev_context *dev_context, + uint8_t rbp_button_code); +bool si_mhl_tx_rbpe_send(struct mhl_dev_context *dev_context, + uint8_t rbpe_error_code); +bool si_mhl_tx_ucp_send(struct mhl_dev_context *dev_context, + uint8_t ucp_key_code); +bool si_mhl_tx_ucpk_send(struct mhl_dev_context *dev_context, + uint8_t ucp_key_code); +bool si_mhl_tx_ucpe_send(struct mhl_dev_context *dev_context, + uint8_t ucpe_error_code); +bool si_mhl_tx_rap_send(struct mhl_dev_context *dev_context, + uint8_t rap_action_code); +struct cbus_req *peek_next_cbus_transaction(struct mhl_dev_context + *dev_context); +bool si_mhl_tx_send_3d_req_or_feat_req(struct mhl_dev_context *dev_context); +void send_bist_ready(struct mhl_dev_context *dev_context); +enum bist_cmd_status { + BIST_STATUS_NO_ERROR, + BIST_STATUS_NOT_IN_OCBUS, + BIST_STATUS_NOT_IN_ECBUS, + BIST_STATUS_INVALID_SETUP, + BIST_STATUS_INVALID_TRIGGER, + BIST_STATUS_DUT_NOT_READY, + BIST_STATUS_NO_RESOURCES, + BIST_STATUS_TEST_IP, + BIST_STATUS_NO_TEST_IP +}; +enum bist_cmd_status si_mhl_tx_bist_setup(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup); +enum bist_cmd_status si_mhl_tx_bist_trigger(struct mhl_dev_context *dev_context, + uint8_t trigger_operand); +void si_mhl_tx_execute_bist(struct mhl_dev_context *dev_context, + struct bist_setup_info *setup_info); +void start_bist_initiator_test(struct mhl_dev_context *dev_context); +enum bist_cmd_status si_mhl_tx_bist_stop(struct mhl_dev_context *dev_context); +enum bist_cmd_status si_mhl_tx_bist_request_stat(struct mhl_dev_context + *dev_context, + uint8_t request_operand); +int si_mhl_tx_shutdown(struct mhl_dev_context *dev_context); +int si_mhl_tx_ecbus_started(struct mhl_dev_context *dev_context); +void si_mhl_tx_send_blk_rcv_buf_info(struct mhl_dev_context *dev_context); +void si_mhl_tx_initialize_block_transport(struct mhl_dev_context *dev_context); + +void si_mhl_tx_set_bist_timer_impl(struct mhl_dev_context *dev_context, + const char *caller, int line_num); +#define si_mhl_tx_set_bist_timer(dev_context) \ + si_mhl_tx_set_bist_timer_impl(dev_context, __func__, __LINE__) +#endif /* #if !defined(MHL_SUPP_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/platform.c b/drivers/video/fbdev/msm/mhl3/platform.c new file mode 100644 index 000000000000..017960c9ebc5 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/platform.c @@ -0,0 +1,2181 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include "si_mdt_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" +#include "si_8620_regs.h" + +#ifdef SIMG_USE_DTS +#include +#endif + + +/* + * Platform resources (I2C port, GPIOs, ...) needed to control the + * MHL starter kit. These default values are used to interface with + * PandaBoard which does not (currently) use device tree. Device tree + * capable systems should modify the device tree to declare the platform + * resources assigned to the MHL starter kit. If compiled with device tree + * support (-DSIMG_USE_DTS), this driver will override these default + * values with those from the device tree. + */ +#define GPIO_MHL_INT 138 /* W_INT */ +#define GPIO_BB_RESET 140 /* P_BB_RST# */ +#define I2C_ADAPTER 4 + +#define GPIO_EXP_ADDR 0x40 + +#define RESET_PULSE_WIDTH 1 /* In ms */ + +/* + * NOTE: The following GPIO expander register type address + * offsets are all defined with the address auto-increment + * bit set (0x80) + */ +#define GPIO_EXP_INPUT_REGS_OFFSET 0x80 +#define GPIO_EXP_OUTPUT_REGS_OFFSET 0x88 +#define GPIO_EXP_POL_INVERT_REGS_OFFSET 0x90 +#define GPIO_EXP_IO_CONFIG_REGS_OFFSET 0x98 +#define GPIO_EXP_INTR_MASK_REGS_OFFSET 0xA0 + +#define GPIO_EXP_BANK_2_OUTPUT_DEFAULT 0xFF +#define GPIO_EXP_BANK_2_3D (0x01 << 0) +#define GPIO_EXP_BANK_2_PKD_PXL (0x01 << 1) +#define GPIO_EXP_BANK_2_HDCP_ON (0x01 << 2) +#define GPIO_EXP_BANK_2_N_TCODE (0x01 << 3) +#define GPIO_EXP_BANK_2_LED_USB_MODE (0x01 << 4) +#define GPIO_EXP_BANK_2_SPR_LED2 (0x01 << 5) +#define GPIO_EXP_BANK_2_SPR_LED3 (0x01 << 6) +#define GPIO_EXP_BANK_2_SPR_LED4 (0x01 << 7) + +#define GPIO_EXP_BANK_3_OUTPUT_DEFAULT 0x67 + +#define GPIO_EXP_BANK_3_MHL_TX_RST_B (0x01 << 0) +#define GPIO_EXP_BANK_3_FW_WAKE_A (0x01 << 1) +#define GPIO_EXP_BANK_3_CHG_DET (0x01 << 2) +#define GPIO_EXP_BANK_3_XO3_SINK_VBUS_SENSE (0x01 << 3) +#define GPIO_EXP_BANK_3_12V_PS_SENSE (0x01 << 4) +#define GPIO_EXP_BANK_3_EEPROM_WR_EN (0x01 << 5) +#define GPIO_EXP_BANK_3_TX2MHLRX_PWR_A (0x01 << 6) +#define GPIO_EXP_BANK_3_M2U_VBUS_CTRL_A (0x01 << 7) + +#define GPIO_EXP_BANK_4_OUTPUT_DEFAULT 0xF0 +#define GPIO_EXP_BANK_4_DSW9 (0x01 << 0) +#define GPIO_EXP_BANK_4_DSW10 (0x01 << 1) +#define GPIO_EXP_BANK_4_DSW11 (0x01 << 2) +#define GPIO_EXP_BANK_4_DSW12 (0x01 << 3) +#define GPIO_EXP_BANK_4_USB_SW_CTRL0 (0x01 << 4) +#define GPIO_EXP_BANK_4_USB_SW_CTRL1 (0x01 << 5) +#define GPIO_EXP_BANK_4_LED15_AMBER (0x01 << 6) +#define GPIO_EXP_BANK_4_LED15_GREEN (0x01 << 7) + +#define GET_FROM_MODULE_PARAM -1 +#define GPIO_ON_EXPANDER -2 + +#define REG_PCA_950x_PORT_0_INPUT 0x00 +#define REG_PCA_950x_PORT_1_INPUT 0x01 +#define REG_PCA_950x_PORT_2_INPUT 0x02 +#define REG_PCA_950x_PORT_3_INPUT 0x03 +#define REG_PCA_950x_PORT_4_INPUT 0x04 + +#define REG_PCA_950x_PORT_0_OUTPUT 0x08 +#define REG_PCA_950x_PORT_1_OUTPUT 0x09 +#define REG_PCA_950x_PORT_2_OUTPUT 0x0A +#define REG_PCA_950x_PORT_3_OUTPUT 0x0B +#define REG_PCA_950x_PORT_4_OUTPUT 0x0C + +u8 gpio_exp_bank2_output; +u8 gpio_exp_bank3_output; +u8 gpio_exp_bank4_output; + +static char *buildTime = "Built " __DATE__ "-" __TIME__; +static char *buildVersion = "1.03." BUILD_NUM_STRING; + +struct semaphore platform_lock; +static uint32_t platform_flags; +bool probe_fail; + +static struct spi_device *spi_dev; +#define SPI_BUS_NUM 1 +#define SPI_CHIP_SEL 0 +#define SPI_TRANSFER_MODE SPI_MODE_0 +#define SPI_BUS_SPEED 1000000 + +enum si_spi_opcodes { + spi_op_disable = 0x04, + spi_op_enable = 0x06, + spi_op_clear_status = 0x07, + spi_op_reg_read = 0x60, + spi_op_reg_write = 0x61, + spi_op_ddc_reg_read = 0x62, + spi_op_emsc_read = 0x80, + spi_op_emsc_write = 0x81, + spi_op_slow_cbus_read = 0x90, + spi_op_slow_cbus_write = 0x91 +}; + +#define MAX_SPI_PAYLOAD_SIZE LOCAL_BLK_RCV_BUFFER_SIZE +#define MAX_SPI_CMD_SIZE 3 +#define EMSC_WRITE_SPI_CMD_SIZE 1 +#define EMSC_READ_SPI_CMD_SIZE 1 +#define MAX_SPI_DUMMY_XFER_BYTES 20 +#define MAX_SPI_XFER_BUFFER_SIZE (MAX_SPI_CMD_SIZE + \ + MAX_SPI_DUMMY_XFER_BYTES + MAX_SPI_PAYLOAD_SIZE) +#define MAX_SPI_EMSC_BLOCK_SIZE (MAX_SPI_CMD_SIZE + MAX_SPI_PAYLOAD_SIZE) + +#define MAX_I2C_PAYLOAD_SIZE LOCAL_BLK_RCV_BUFFER_SIZE +#define MAX_I2C_CMD_SIZE 0 + +#define MAX_I2C_EMSC_BLOCK_SIZE (MAX_I2C_CMD_SIZE + MAX_I2C_PAYLOAD_SIZE) + +struct spi_xfer_mem { + u8 *tx_buf; + u8 *rx_buf; + /* block commands are asynchronous to normal cbus traffic + and CANNOT share a buffer. + */ + uint8_t *block_tx_buffers; + struct spi_transfer spi_xfer[2]; + struct spi_message spi_cmd; +} spi_mem; + +struct i2c_xfer_mem { + uint8_t *block_tx_buffers; +} i2c_mem; + +static int gpio_expander_transfer(u8 offset, u16 count, u8 *values, bool write); + +#if defined(SIMG_USE_DTS) +static u32 i2c_adapter_num = I2C_ADAPTER; +#endif +static u32 spi_bus_num = SPI_BUS_NUM; +static struct i2c_adapter *i2c_bus_adapter; + +struct i2c_dev_info { + uint8_t dev_addr; + struct i2c_client *client; +}; + +#define I2C_DEV_INFO(addr) \ + {.dev_addr = addr >> 1, .client = NULL} + +static struct i2c_dev_info device_addresses[] = { + I2C_DEV_INFO(SA_TX_PAGE_0), + I2C_DEV_INFO(SA_TX_PAGE_1), + I2C_DEV_INFO(SA_TX_PAGE_2), + I2C_DEV_INFO(SA_TX_PAGE_3), + I2C_DEV_INFO(SA_TX_PAGE_6), + I2C_DEV_INFO(SA_TX_CBUS), + I2C_DEV_INFO(GPIO_EXP_ADDR), +}; + +int debug_level; +bool debug_reg_dump; +bool input_dev_rap = 1; +bool input_dev_rcp = 1; +bool input_dev_ucp = 1; +#if (INCLUDE_RBP == 1) +bool input_dev_rbp = 1; +#endif +int hdcp_content_type; +bool use_spi; /* Default to i2c (0). */ +int crystal_khz = 19200; /* SiI8620 SK has 19.2MHz crystal */ +int use_heartbeat; + +bool wait_for_user_intr; +int tmds_link_speed; +#ifdef FORCE_OCBUS_FOR_ECTS +bool force_ocbus_for_ects; +#endif +int gpio_index = 138; + +module_param(debug_reg_dump, bool, S_IRUGO); +module_param(debug_level, int, S_IRUGO); + +module_param(input_dev_rap, bool, S_IRUGO); +module_param(input_dev_rcp, bool, S_IRUGO); +module_param(input_dev_ucp, bool, S_IRUGO); +#if (INCLUDE_RBP == 1) +module_param(input_dev_rbp, bool, S_IRUGO); +#endif +module_param(hdcp_content_type, int, S_IRUGO); +module_param(use_spi, bool, S_IRUGO); +module_param(crystal_khz, int, S_IRUGO); +module_param(use_heartbeat, int, S_IRUGO); +module_param(wait_for_user_intr, bool, S_IRUGO); +module_param(tmds_link_speed, int, S_IRUGO); +#ifdef FORCE_OCBUS_FOR_ECTS +module_param(force_ocbus_for_ects, bool, S_IRUGO); +#endif + +module_param_named(debug_msgs, debug_level, int, S_IRUGO); + +struct platform_signals_list platform_signals[] = { + {.name = "TX_HW_RESET", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_MHL_TX_RST_B, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "TX_FW_WAKE", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_FW_WAKE_A, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "CHG_DET", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_CHG_DET, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "XO3_SINK_VBUS_SENSE", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_INPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_XO3_SINK_VBUS_SENSE, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "TWELVE_VOLT_PS_SENSE", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_12V_PS_SENSE, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "EEPROM_WR_EN", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_EEPROM_WR_EN, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "TX2MHLRX_PWR", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_TX2MHLRX_PWR_A, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "M2U_VBUS_CTRL", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_3_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_3_M2U_VBUS_CTRL_A, + .gpio_bank_value = &gpio_exp_bank3_output, + .param = NULL}, + {.name = "LED_3D", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_2_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_2_3D, + .gpio_bank_value = &gpio_exp_bank2_output, + .param = NULL}, + {.name = "LED_PACKED_PIXEL", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_2_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_2_PKD_PXL, + .gpio_bank_value = &gpio_exp_bank2_output, + .param = NULL}, + {.name = "LED_HDCP", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_2_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_2_HDCP_ON, + .gpio_bank_value = &gpio_exp_bank2_output, + .param = NULL}, + {.name = "LED_USB_MODE", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_2_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_2_LED_USB_MODE, + .gpio_bank_value = &gpio_exp_bank2_output, + .param = NULL}, + {.name = "LED_SPARE_2", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_2_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_2_SPR_LED2, + .gpio_bank_value = &gpio_exp_bank2_output, + .param = NULL}, + {.name = "LED_SPARE_3", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_2_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_2_SPR_LED3, + .gpio_bank_value = &gpio_exp_bank2_output, + .param = NULL}, + {.name = "LED_SPARE_4", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_2_OUTPUT}, + .gpio_mask_PCA950x = GPIO_EXP_BANK_2_SPR_LED4, + .gpio_bank_value = &gpio_exp_bank2_output, + .param = NULL}, + {.name = "X02_USB_SW_CTRL", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_4_OUTPUT}, + .gpio_mask_PCA950x = (GPIO_EXP_BANK_4_USB_SW_CTRL0 | + GPIO_EXP_BANK_4_USB_SW_CTRL1), + .gpio_bank_value = &gpio_exp_bank4_output, + .param = NULL}, + {.name = "X02_USB_SW_CTRL0", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_4_OUTPUT}, + .gpio_mask_PCA950x = (GPIO_EXP_BANK_4_USB_SW_CTRL0), + .gpio_bank_value = &gpio_exp_bank4_output, + .param = NULL}, + {.name = "X02_USB_SW_CTRL1", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_4_OUTPUT}, + .gpio_mask_PCA950x = (GPIO_EXP_BANK_4_USB_SW_CTRL1), + .gpio_bank_value = &gpio_exp_bank4_output, + .param = NULL}, + {.name = "X02_LED15_AMBER", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_4_OUTPUT}, + .gpio_mask_PCA950x = (GPIO_EXP_BANK_4_LED15_AMBER), + .gpio_bank_value = &gpio_exp_bank4_output, + .param = NULL}, + {.name = "X02_LED15_GREEN", + .gpio_number = GPIO_ON_EXPANDER, + .gpio_reg_PCA950x = {GPIO_EXP_ADDR, REG_PCA_950x_PORT_4_OUTPUT}, + .gpio_mask_PCA950x = (GPIO_EXP_BANK_4_LED15_GREEN), + .gpio_bank_value = &gpio_exp_bank4_output, + .param = NULL} +}; + +#define MHL_INT_INDEX 0 +#define MHL_RESET_INDEX 1 +static struct gpio starter_kit_control_gpios[] = { + /* + * GPIO signals needed for the starter kit board. + */ + {GPIO_MHL_INT, GPIOF_IN, "MHL_intr"}, + {GPIO_BB_RESET, GPIOF_OUT_INIT_HIGH, "MHL_reset"}, +}; + +static inline int platform_read_i2c_block(struct i2c_adapter *i2c_bus, u8 page, + u8 offset, u16 count, u8 *values) +{ + struct i2c_msg msg[2]; + + msg[0].flags = 0; + msg[0].addr = page >> 1; + msg[0].buf = &offset; + msg[0].len = 1; + + msg[1].flags = I2C_M_RD; + msg[1].addr = page >> 1; + msg[1].buf = values; + msg[1].len = count; + + return i2c_transfer(i2c_bus, msg, 2); +} + +static inline int platform_write_i2c_block(struct i2c_adapter *i2c_bus, u8 page, + u8 offset, u16 count, u8 *values) +{ + struct i2c_msg msg; + u8 *buffer; + int ret; + + buffer = kmalloc(count + 1, GFP_KERNEL); + if (!buffer) { + printk(KERN_ERR "%s:%d buffer allocation failed\n", + __func__, __LINE__); + return -ENOMEM; + } + + buffer[0] = offset; + memmove(&buffer[1], values, count); + + msg.flags = 0; + msg.addr = page >> 1; + msg.buf = buffer; + msg.len = count + 1; + + ret = i2c_transfer(i2c_bus, &msg, 1); + + kfree(buffer); + + if (ret != 1) { + printk(KERN_ERR "%s:%d I2c write failed 0x%02x:0x%02x\n", + __func__, __LINE__, page, offset); + ret = -EIO; + } else { + ret = 0; + } + + return ret; +} + +static int platform_read_i2c_reg(struct i2c_adapter *bus_adapter_i2c, u8 page, + u8 offset) +{ + int ret; + u8 byte_read; + + ret = platform_read_i2c_block(bus_adapter_i2c, page, offset, 1, + &byte_read); + MHL_TX_DBG_INFO("\tGI2C_R %2x:%2x = %2x\n", page, offset, ret); + if (ret != 2) { + printk(KERN_ERR "%s:%d I2c read failed, 0x%02x:0x%02x\n", + __func__, __LINE__, page, offset); + ret = -EIO; + } else { + ret = 0; + } + return ret ? ret : byte_read; +} + +static int platform_write_i2c_reg(struct i2c_adapter *bus_adapter_i2c, u8 page, + u8 offset, u8 value) +{ + MHL_TX_DBG_INFO("\tGI2C_W %2x:%2x <- %2x\n", page, offset, value); + return platform_write_i2c_block(bus_adapter_i2c, page, offset, 1, + &value); +} + +uint32_t platform_get_flags(void) +{ + return platform_flags; +} + +static void toggle_reset_n(void) +{ + MHL_TX_DBG_INFO("Toggle MHL_RST_B/RESET_N pin. Resets 8620 only.\n"); + gpio_exp_bank3_output &= ~GPIO_EXP_BANK_3_MHL_TX_RST_B; + gpio_expander_transfer(GPIO_EXP_OUTPUT_REGS_OFFSET + 3, + 1, &gpio_exp_bank3_output, true); + + /* Without this, we see a 500ns reset pulse. Enforce 1ms.*/ + msleep(RESET_PULSE_WIDTH); + + gpio_exp_bank3_output |= GPIO_EXP_BANK_3_MHL_TX_RST_B; + gpio_expander_transfer(GPIO_EXP_OUTPUT_REGS_OFFSET + 3, + 1, &gpio_exp_bank3_output, true); + +} + +static void toggle_BB_RST(int reset_period) +{ + MHL_TX_DBG_INFO("Toggle BB_RST# pin. Resets GPIO expander AND 8620\n"); + gpio_set_value(GPIO_BB_RESET, 0); + msleep(reset_period); + gpio_set_value(GPIO_BB_RESET, 1); + +} + +int is_interrupt_asserted(void) +{ + return gpio_get_value(starter_kit_control_gpios[MHL_INT_INDEX].gpio) + ? 0 : 1; +} + +int get_config(void *dev_context, int config_idx) +{ + int pin_state = 0; + + if (config_idx < ARRAY_SIZE(platform_signals)) { + switch (platform_signals[config_idx].gpio_number) { + case GET_FROM_MODULE_PARAM: + pin_state = *(platform_signals[config_idx].param); + break; + case GPIO_ON_EXPANDER: + pin_state = + (platform_read_i2c_reg + (i2c_bus_adapter, + platform_signals[config_idx].gpio_reg_PCA950x. + slave_addr, + platform_signals[config_idx].gpio_reg_PCA950x. + offset) + & platform_signals[config_idx]. + gpio_mask_PCA950x) ? 1 : 0; + break; + default: + pin_state = + gpio_get_value(platform_signals[config_idx]. + gpio_number); + break; + } + } + return pin_state; +} + +void set_pin_impl(int pin_idx, int value, + const char *function_name, int line_num) +{ + uint8_t bank_value; + + if (pin_idx < ARRAY_SIZE(platform_signals)) { + + MHL_TX_DBG_INFO("set_pin(%s,%d)\n", + platform_signals[pin_idx].name, value); + switch (platform_signals[pin_idx].gpio_number) { + case GET_FROM_MODULE_PARAM: + break; + case GPIO_ON_EXPANDER: + bank_value = + *(platform_signals[pin_idx].gpio_bank_value); + if (value) + bank_value |= platform_signals[pin_idx]. + gpio_mask_PCA950x; + else + bank_value &= ~platform_signals[pin_idx]. + gpio_mask_PCA950x; + + *(platform_signals[pin_idx].gpio_bank_value) = + bank_value; + platform_write_i2c_reg(i2c_bus_adapter, + platform_signals[pin_idx]. + gpio_reg_PCA950x.slave_addr, + platform_signals[pin_idx]. + gpio_reg_PCA950x.offset, + bank_value); + break; + default: + gpio_set_value(platform_signals[pin_idx].gpio_number, + value); + break; + } + } +} + +void platform_mhl_tx_hw_reset(uint32_t reset_period, uint32_t reset_delay) +{ + /* then reset the chip */ + toggle_reset_n(); + + if (reset_delay) + msleep(reset_delay); + + if (use_spi) { + u8 cmd = spi_op_enable; + spi_write(spi_dev, &cmd, 1); + } +} + +void mhl_tx_vbus_control(enum vbus_power_state power_state) +{ + struct device *parent_dev; + struct mhl_dev_context *dev_context; + + if (use_spi) + parent_dev = &spi_dev->dev; + else + parent_dev = &device_addresses[0].client->dev; + + dev_context = dev_get_drvdata(parent_dev); + + switch (power_state) { + case VBUS_OFF: + set_pin(M2U_VBUS_CTRL, 0); + set_pin(TX2MHLRX_PWR, 1); + break; + + case VBUS_ON: + set_pin(TX2MHLRX_PWR, 0); + set_pin(M2U_VBUS_CTRL, 1); + break; + + default: + dev_err(dev_context->mhl_dev, + "%s: Invalid power state %d received!\n", + __func__, power_state); + break; + } +} + +void mhl_tx_vbus_current_ctl(uint16_t max_current_in_milliamps) +{ + /* + Starter kit does not have a PMIC. + Implement VBUS input current limit control here. + */ + +} + +int si_device_dbg_i2c_reg_xfer(void *dev_context, u8 page, u8 offset, + u16 count, bool rw_flag, u8 *buffer) +{ + u16 address = (page << 8) | offset; + + if (rw_flag == DEBUG_I2C_WRITE) + return mhl_tx_write_reg_block(dev_context, address, count, + buffer); + else + return mhl_tx_read_reg_block(dev_context, address, count, + buffer); +} + +#define MAX_DEBUG_MSG_SIZE 1024 + +#if defined(DEBUG) + +/* + * Return a pointer to the file name part of the + * passed path spec string. + */ +char *find_file_name(const char *path_spec) +{ + char *pc; + + for (pc = (char *)&path_spec[strlen(path_spec)]; + pc != path_spec; --pc) { + if ('\\' == *pc) { + ++pc; + break; + } + if ('/' == *pc) { + ++pc; + break; + } + } + return pc; +} + +void print_formatted_debug_msg(char *file_spec, const char *func_name, + int line_num, char *fmt, ...) +{ + uint8_t *msg = NULL; + uint8_t *msg_offset; + char *file_spec_sep = NULL; + int remaining_msg_len = MAX_DEBUG_MSG_SIZE; + int len; + va_list ap; + + if (fmt == NULL) + return; + + msg = kmalloc(remaining_msg_len, GFP_KERNEL); + if (msg == NULL) + return; + + msg_offset = msg; + + len = scnprintf(msg_offset, remaining_msg_len, "mhl: "); + msg_offset += len; + remaining_msg_len -= len; + + /* Only print the file name, not the path */ + if (file_spec != NULL) + file_spec = find_file_name(file_spec); + + if (file_spec != NULL) { + if (func_name != NULL) + file_spec_sep = "->"; + else if (line_num != -1) + file_spec_sep = ":"; + } + + if (file_spec) { + len = scnprintf(msg_offset, remaining_msg_len, "%s", file_spec); + msg_offset += len; + remaining_msg_len -= len; + } + + if (file_spec_sep) { + len = + scnprintf(msg_offset, remaining_msg_len, "%s", + file_spec_sep); + msg_offset += len; + remaining_msg_len -= len; + } + + if (func_name) { + len = scnprintf(msg_offset, remaining_msg_len, "%s", func_name); + msg_offset += len; + remaining_msg_len -= len; + } + + if (line_num != -1) { + if ((file_spec != NULL) || (func_name != NULL)) + len = + scnprintf(msg_offset, remaining_msg_len, ":%d ", + line_num); + else + len = + scnprintf(msg_offset, remaining_msg_len, "%d ", + line_num); + + msg_offset += len; + remaining_msg_len -= len; + } + + va_start(ap, fmt); + len = vscnprintf(msg_offset, remaining_msg_len, fmt, ap); + va_end(ap); + + printk(msg); + + kfree(msg); +} + +void dump_transfer(enum tx_interface_types if_type, + u8 page, u8 offset, u16 count, u8 *values, bool write) +{ + if (debug_reg_dump != 0) { + int buf_size = 64; + u16 idx; + int buf_offset; + char *buf; + char *if_type_msg; + + switch (if_type) { + case TX_INTERFACE_TYPE_I2C: + if_type_msg = "I2C"; + break; + case TX_INTERFACE_TYPE_SPI: + if_type_msg = "SPI"; + break; + default: + return; + }; + + if (count > 1) { + /* 3 chars per byte displayed */ + buf_size += count * 3; + /* plus per display row overhead */ + buf_size += ((count / 16) + 1) * 8; + } + + buf = kmalloc(buf_size, GFP_KERNEL); + if (!buf) + return; + + if (count == 1) { + + scnprintf(buf, buf_size, " %s %02X.%02X %s %02X\n", + if_type_msg, + page, offset, write ? "W" : "R", values[0]); + } else { + idx = 0; + buf_offset = + scnprintf(buf, buf_size, "%s %02X.%02X %s(%d)", + if_type_msg, page, offset, + write ? "W" : "R", count); + + for (idx = 0; idx < count; idx++) { + if (0 == (idx & 0x0F)) + buf_offset += + scnprintf(&buf[buf_offset], + buf_size - buf_offset, + "\n%04X: ", idx); + + buf_offset += scnprintf(&buf[buf_offset], + buf_size - buf_offset, + "%02X ", values[idx]); + } + buf_offset += + scnprintf(&buf[buf_offset], buf_size - buf_offset, + "\n"); + } + + print_formatted_debug_msg(NULL, NULL, -1, buf); + kfree(buf); + } +} +#endif /* #if defined(DEBUG) */ + +static struct mhl_drv_info drv_info = { + .drv_context_size = sizeof(struct drv_hw_context), + .mhl_device_initialize = si_mhl_tx_chip_initialize, + .mhl_device_isr = si_mhl_tx_drv_device_isr, + .mhl_device_dbg_i2c_reg_xfer = si_device_dbg_i2c_reg_xfer, + .mhl_device_get_aksv = si_mhl_tx_drv_get_aksv +}; + +int mhl_tx_write_reg_block_i2c(void *drv_context, u8 page, u8 offset, + u16 count, u8 *values) +{ + DUMP_I2C_TRANSFER(page, offset, count, values, true); + + return platform_write_i2c_block(i2c_bus_adapter, page, offset, count, + values); +} + +int mhl_tx_write_reg_i2c(void *drv_context, u8 page, u8 offset, u8 value) +{ + return mhl_tx_write_reg_block_i2c(drv_context, page, offset, 1, &value); +} + +int mhl_tx_read_reg_block_i2c(void *drv_context, u8 page, u8 offset, + u16 count, u8 *values) +{ + int ret; + + if (count == 0) { + MHL_TX_DBG_ERR("Tried to read 0 bytes\n"); + return -EINVAL; + } + + ret = platform_read_i2c_block(i2c_bus_adapter, page, offset, count, + values); + if (ret != 2) { + MHL_TX_DBG_ERR("I2c read failed, 0x%02x:0x%02x\n", page, + offset); + ret = -EIO; + } else { + ret = 0; + DUMP_I2C_TRANSFER(page, offset, count, values, false); + } + + return ret; +} + +int mhl_tx_read_reg_i2c(void *drv_context, u8 page, u8 offset) +{ + u8 byte_read; + int status; + + status = mhl_tx_read_reg_block_i2c(drv_context, page, offset, + 1, &byte_read); + + return status ? status : byte_read; +} + +static int i2c_addr_to_spi_cmd(void *drv_context, bool write, u8 *page, + u8 *opcode, u8 *dummy_bytes) +{ + if (write) { + *opcode = spi_op_reg_write; + *dummy_bytes = 0; + } else { + *opcode = spi_op_reg_read; + *dummy_bytes = 5; + } + + switch (*page) { + case SA_TX_PAGE_0: + *page = 0; + break; + case SA_TX_PAGE_1: + *page = 1; + break; + case SA_TX_PAGE_2: + *page = 2; + break; + case SA_TX_PAGE_3: + *page = 3; + break; + case SA_TX_PAGE_4: + *page = 4; + break; + case SA_TX_CBUS: + *page = 5; + break; + case SA_TX_PAGE_6: + *page = 6; + break; + case SA_TX_PAGE_7: + *page = 7; + break; + case SA_TX_PAGE_8: + *page = 8; + break; + default: + MHL_TX_DBG_ERR("Called with unknown page 0x%02x\n", *page); + return -EINVAL; + } + return 0; +} + +inline uint8_t reg_page(uint16_t address) +{ + return (uint8_t)((address >> 8) & 0x00FF); +} + +inline uint8_t reg_offset(uint16_t address) +{ + return (uint8_t)(address & 0x00FF); +} + +static int mhl_tx_write_reg_block_spi(void *drv_context, u8 page, u8 offset, + u16 count, u8 *values) +{ + u8 opcode; + u8 dummy_bytes; + u16 length = count + 3; + int ret; + + DUMP_SPI_TRANSFER(page, offset, count, values, true); + + ret = i2c_addr_to_spi_cmd(drv_context, true, &page, &opcode, + &dummy_bytes); + if (ret != 0) + return ret; + + length = 3 + count + dummy_bytes; + + if (length > MAX_SPI_XFER_BUFFER_SIZE) { + MHL_TX_DBG_ERR("Transfer count (%d) is too large!\n", count); + return -EINVAL; + } + + spi_mem.tx_buf[0] = opcode; + spi_mem.tx_buf[1] = page; + spi_mem.tx_buf[2] = offset; + if (dummy_bytes) + memset(&spi_mem.tx_buf[3], 0, dummy_bytes); + + memmove(&spi_mem.tx_buf[dummy_bytes + 3], values, count); + + ret = spi_write(spi_dev, spi_mem.tx_buf, length); + + if (ret != 0) { + MHL_TX_DBG_ERR("SPI write block failed, " + "page: 0x%02x, register: 0x%02x\n", + page, offset); + ret = -EIO; + } else { + ret = 0; + } + + return ret; +} + +static int mhl_tx_write_reg_spi(void *drv_context, u8 page, u8 offset, u8 value) +{ + return mhl_tx_write_reg_block_spi(drv_context, page, offset, 1, &value); +} + +static int mhl_tx_read_reg_block_spi(void *drv_context, u8 page, u8 offset, + u16 count, u8 *values) +{ + u8 page_num = page; + u8 opcode; + u8 dummy_bytes; + u16 length; + int ret = 0; + + if (count > MAX_SPI_PAYLOAD_SIZE) { + MHL_TX_DBG_ERR("Requested transfer count (%d) is too large\n", + count); + return -EINVAL; + } + + ret = i2c_addr_to_spi_cmd(drv_context, false, &page_num, &opcode, + &dummy_bytes); + if (ret != 0) + return ret; + + if ((reg_page(REG_DDC_DATA) == page) && + (reg_offset(REG_DDC_DATA) == offset)) { + dummy_bytes = 11; + opcode = spi_op_ddc_reg_read; + } + + length = 3 + count + dummy_bytes; + if (length > MAX_SPI_XFER_BUFFER_SIZE) { + MHL_TX_DBG_ERR("Requested transfer total (%d) is too large\n", + length); + return -EINVAL; + } + + spi_message_init(&spi_mem.spi_cmd); + memset(&spi_mem.spi_xfer, 0, sizeof(spi_mem.spi_xfer)); + spi_mem.tx_buf[0] = opcode; + spi_mem.tx_buf[1] = page_num; + spi_mem.tx_buf[2] = offset; + + spi_mem.spi_xfer[0].tx_buf = spi_mem.tx_buf; + spi_mem.spi_xfer[0].len = 3 + dummy_bytes; +#ifdef USE_SPIOPTIMIZE + memset(&spi_mem.tx_buf[3], 0, dummy_bytes + count); + spi_mem.spi_xfer[0].len += count; + + spi_mem.spi_xfer[0].rx_buf = spi_mem.rx_buf; + spi_message_add_tail(&spi_mem.spi_xfer[0], &spi_mem.spi_cmd); + ret = spi_sync(spi_dev, &spi_mem.spi_cmd); +#else + spi_message_add_tail(&spi_mem.spi_xfer[0], &spi_mem.spi_cmd); + + spi_mem.spi_xfer[1].rx_buf = spi_mem.rx_buf; + spi_mem.spi_xfer[1].len = count; + spi_mem.spi_xfer[1].cs_change = 1; + spi_message_add_tail(&spi_mem.spi_xfer[1], &spi_mem.spi_cmd); + + ret = spi_sync(spi_dev, &spi_mem.spi_cmd); +#endif + + if (ret != 0) { + MHL_TX_DBG_ERR("SPI read block failed, " + "page: 0x%02x, register: 0x%02x\n", + page, offset); + } else { +#ifdef USE_SPIOPTIMIZE + memcpy(values, &spi_mem.rx_buf[3 + dummy_bytes], count); +#else + memcpy(values, spi_mem.rx_buf, count); +#endif + DUMP_SPI_TRANSFER(page, offset, count, values, false); + } + + return ret; +} + +static int mhl_tx_read_reg_block_spi_emsc(void *drv_context, u16 count, + u8 *values) +{ + u8 dummy_bytes = 1; + u16 length; + int ret; + + if (count > MAX_SPI_PAYLOAD_SIZE) { + MHL_TX_DBG_ERR("Requested transfer count (%d) is too large\n", + count); + return -EINVAL; + } + + length = EMSC_READ_SPI_CMD_SIZE + dummy_bytes + count; + if (length > MAX_SPI_XFER_BUFFER_SIZE) { + MHL_TX_DBG_ERR("Requested transfer total (%d) is too large\n", + length); + return -EINVAL; + } + + spi_message_init(&spi_mem.spi_cmd); + memset(&spi_mem.spi_xfer, 0, sizeof(spi_mem.spi_xfer)); + spi_mem.tx_buf[0] = spi_op_emsc_read; + + spi_mem.spi_xfer[0].tx_buf = spi_mem.tx_buf; + spi_mem.spi_xfer[0].len = EMSC_READ_SPI_CMD_SIZE + dummy_bytes; + +#ifdef USE_SPIOPTIMIZE + memset(&spi_mem.tx_buf[EMSC_READ_SPI_CMD_SIZE], 0, dummy_bytes + count); + spi_mem.spi_xfer[0].rx_buf = spi_mem.rx_buf; + spi_mem.spi_xfer[0].len += count; + spi_message_add_tail(&spi_mem.spi_xfer[0], &spi_mem.spi_cmd); +#else + spi_message_add_tail(&spi_mem.spi_xfer[0], &spi_mem.spi_cmd); + + spi_mem.spi_xfer[1].rx_buf = spi_mem.rx_buf; + spi_mem.spi_xfer[1].len = count; + spi_mem.spi_xfer[1].cs_change = 1; + spi_message_add_tail(&spi_mem.spi_xfer[1], &spi_mem.spi_cmd); +#endif + ret = spi_sync(spi_dev, &spi_mem.spi_cmd); + + if (ret != 0) { + MHL_TX_DBG_ERR("SPI eMSC read block failed "); + } else { +#ifdef USE_SPIOPTIMIZE + memcpy(values, + &spi_mem.rx_buf[EMSC_READ_SPI_CMD_SIZE + dummy_bytes], + count); +#else + memcpy(values, spi_mem.rx_buf, count); +#endif + /* DUMP_SPI_TRANSFER(page, offset, count, values, false); */ + } + + return ret; +} + +int mhl_tx_read_spi_emsc(void *drv_context, u16 count, u8 *values) +{ + u8 *dptr; + u16 length, block; + int ret = 0; + + /* + * Don't read more than 128 bytes at a time to reduce + * possible problems with SPI hardware. + */ + dptr = values; + length = count; + while (length > 0) { + block = (length < 128) ? length : 128; + ret = mhl_tx_read_reg_block_spi_emsc(drv_context, block, dptr); + if (ret != 0) + break; + length -= block; + dptr += block; + } + + return ret; +} + +static int mhl_tx_read_reg_spi(void *drv_context, u8 page, u8 offset) +{ + u8 byte_read = 0; + int status; + + status = mhl_tx_read_reg_block_spi(drv_context, page, offset, 1, + &byte_read); + + return status ? status : byte_read; +} + +void mhl_tx_clear_emsc_read_err(void *drv_context) +{ + + if (use_spi) { + spi_mem.tx_buf[0] = spi_op_clear_status; + spi_mem.tx_buf[1] = 0x02; + spi_write(spi_dev, spi_mem.tx_buf, 2); + } +} + +int mhl_tx_write_reg_block(void *drv_context, u16 address, u16 count, + u8 *values) +{ + u8 page = (u8)(address >> 8); + u8 offset = (u8)address; + + if (use_spi) + return mhl_tx_write_reg_block_spi(drv_context, page, offset, + count, values); + else + return mhl_tx_write_reg_block_i2c(drv_context, page, offset, + count, values); +} + +void si_mhl_tx_platform_get_block_buffer_info(struct block_buffer_info_t + *block_buffer_info) +{ + if (use_spi) { + block_buffer_info->buffer = spi_mem.block_tx_buffers; + block_buffer_info->req_size = MAX_SPI_EMSC_BLOCK_SIZE; + block_buffer_info->payload_offset = EMSC_WRITE_SPI_CMD_SIZE; + } else { + block_buffer_info->buffer = i2c_mem.block_tx_buffers; + block_buffer_info->req_size = MAX_I2C_EMSC_BLOCK_SIZE; + block_buffer_info->payload_offset = MAX_I2C_CMD_SIZE; + } +} + +int mhl_tx_write_block_spi_emsc(void *drv_context, struct block_req *req) +{ + u16 length; + int ret; + + /* DUMP_SPI_TRANSFER(page, offset, req->count, req->payload->as_bytes, + * true); + */ + + /* dummy bytes will always be zero */ + length = EMSC_WRITE_SPI_CMD_SIZE + req->count; + + if (length > MAX_SPI_EMSC_BLOCK_SIZE) { + MHL_TX_DBG_ERR("Transfer count (%d) is too large!\n", + req->count); + return -EINVAL; + } + + req->platform_header[0] = spi_op_emsc_write; + + ret = spi_write(spi_dev, req->platform_header, length); + + if (ret != 0) { + MHL_TX_DBG_ERR("SPI write block failed\n"); + ret = -EIO; + } else { + ret = 0; + } + + return ret; +} + +int mhl_tx_write_reg(void *drv_context, u16 address, u8 value) +{ + u8 page = (u8)(address >> 8); + u8 offset = (u8)address; + + if (use_spi) + return mhl_tx_write_reg_spi(drv_context, page, offset, value); + else + return mhl_tx_write_reg_i2c(drv_context, page, offset, value); +} + +int mhl_tx_read_reg_block(void *drv_context, u16 address, u16 count, u8 *values) +{ + u8 page = (u8)(address >> 8); + u8 offset = (u8)address; + + if (use_spi) + return mhl_tx_read_reg_block_spi(drv_context, page, offset, + count, values); + else + return mhl_tx_read_reg_block_i2c(drv_context, page, offset, + count, values); +} + +int mhl_tx_read_reg(void *drv_context, u16 address) +{ + u8 page = (u8)(address >> 8); + u8 offset = (u8)address; + + if (use_spi) + return mhl_tx_read_reg_spi(drv_context, page, offset); + else + return mhl_tx_read_reg_i2c(drv_context, page, offset); +} + +int mhl_tx_modify_reg(void *drv_context, u16 address, u8 mask, u8 value) +{ + int reg_value; + int write_status; + + reg_value = mhl_tx_read_reg(drv_context, address); + if (reg_value < 0) + return reg_value; + + reg_value &= ~mask; + reg_value |= mask & value; + + write_status = mhl_tx_write_reg(drv_context, address, reg_value); + + if (write_status < 0) + return write_status; + else + return reg_value; +} + +/* + * Return a value indicating how upstream HPD is + * implemented on this platform. + */ +enum hpd_control_mode platform_get_hpd_control_mode(void) +{ + return HPD_CTRL_PUSH_PULL; +} + +static int gpio_expander_transfer(u8 offset, u16 count, u8 *values, bool write) +{ + struct i2c_msg msg[2]; + u8 buf[8]; + int msg_count; + int ret; + + if ((count + 1) > ARRAY_SIZE(buf)) + return -1; + + if (write) { + buf[0] = offset; + memmove(&buf[1], values, count); + + msg[0].flags = 0; + msg[0].addr = GPIO_EXP_ADDR >> 1; + msg[0].buf = buf; + msg[0].len = count + 1; + + msg_count = 1; + + } else { + + msg[0].flags = 0; + msg[0].addr = GPIO_EXP_ADDR >> 1; + msg[0].buf = &offset; + msg[0].len = 1; + + msg[1].flags = I2C_M_RD; + msg[1].addr = GPIO_EXP_ADDR >> 1; + msg[1].buf = values; + msg[1].len = count; + msg_count = 2; + } + + ret = i2c_transfer(i2c_bus_adapter, msg, msg_count); + if (ret != msg_count) { + MHL_TX_DBG_ERR("I2c %s failed ret:%d, page: 0x%02x, " + "register: 0x%02x count:0x%x\n", + write ? "write" : "read", ret, GPIO_EXP_ADDR, + offset, count); + ret = -EIO; + } else { + ret = 0; + /*DUMP_I2C_TRANSFER(GPIO_EXP_ADDR, offset, count, values, + * write); + */ + } + + return ret; +} + +static int gpio_expander_init(void) +{ + u8 gpio_exp_mask_init[] = { GPIO_EXP_INTR_MASK_REGS_OFFSET, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + u8 gpio_exp_pol_invert_init[] = { GPIO_EXP_POL_INVERT_REGS_OFFSET, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + u8 gpio_exp_output_init[] = { GPIO_EXP_OUTPUT_REGS_OFFSET, 0x00, 0x00, + GPIO_EXP_BANK_2_OUTPUT_DEFAULT, + GPIO_EXP_BANK_3_OUTPUT_DEFAULT, + GPIO_EXP_BANK_4_OUTPUT_DEFAULT + }; + u8 gpio_exp_io_config_init[] = { GPIO_EXP_IO_CONFIG_REGS_OFFSET, + 0xFF, 0xFF, 0x00, 0x18, 0x0F + }; + int ret; + + /* First reset GPIO Expander (and 8620) */ + toggle_BB_RST(10); + + ret = gpio_expander_transfer(gpio_exp_mask_init[0], + ARRAY_SIZE(gpio_exp_mask_init) - 1, + &gpio_exp_mask_init[1], true); + if (ret != 0) { + pr_err("%s():%d gpio_expander_transfer failed, error code %d\n", + __func__, __LINE__, ret); + return ret; + } + + ret = gpio_expander_transfer(gpio_exp_pol_invert_init[0], + ARRAY_SIZE(gpio_exp_pol_invert_init) - 1, + &gpio_exp_pol_invert_init[1], true); + if (ret != 0) { + pr_err("%s():%d gpio_expander_transfer failed, error code %d\n", + __func__, __LINE__, ret); + return ret; + } + ret = gpio_expander_transfer(gpio_exp_output_init[0], + ARRAY_SIZE(gpio_exp_output_init) - 1, + &gpio_exp_output_init[1], true); + gpio_exp_bank2_output = GPIO_EXP_BANK_2_OUTPUT_DEFAULT; + gpio_exp_bank3_output = GPIO_EXP_BANK_3_OUTPUT_DEFAULT; + gpio_exp_bank4_output = GPIO_EXP_BANK_4_OUTPUT_DEFAULT; + + if (ret != 0) { + pr_err("%s():%d gpio_expander_transfer failed, error code %d\n", + __func__, __LINE__, ret); + return ret; + } + ret = gpio_expander_transfer(gpio_exp_io_config_init[0], + ARRAY_SIZE(gpio_exp_io_config_init) - 1, + &gpio_exp_io_config_init[1], true); + if (ret != 0) { + pr_err("%s():%d gpio_expander_transfer failed, error code %d\n", + __func__, __LINE__, ret); + } + return ret; +} + +#if (LINUX_KERNEL_VER >= 308) +static int starter_kit_init(void) +#else +static int __devinit starter_kit_init(void) +#endif +{ + int ret; + + /* Acquire the GPIO pins needed to control the starter kit. */ + ret = gpio_request_array(starter_kit_control_gpios, + ARRAY_SIZE(starter_kit_control_gpios)); + if (ret < 0) { + pr_err("%s(): gpio_request_array failed, error code %d\n", + __func__, ret); + } else { + ret = gpio_expander_init(); + if (ret != 0) { + gpio_free_array(starter_kit_control_gpios, + ARRAY_SIZE(starter_kit_control_gpios)); + } + } + return ret; +} + +#if defined(SIMG_USE_DTS) +static int si_8620_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int value; + + value = of_get_named_gpio_flags(np, "simg,reset-gpio", 0, NULL); + if (value >= 0) + starter_kit_control_gpios[MHL_RESET_INDEX].gpio = value; + + value = of_get_named_gpio_flags(np, "simg,irq-gpio", 0, NULL); + if (value >= 0) + starter_kit_control_gpios[MHL_INT_INDEX].gpio = value; + + /* + * Need this for I/O expander in case we're using SPI as + * the register I/O. + */ + if (!of_property_read_u32(np, "simg,i2c_port#", &value)) + i2c_adapter_num = value; + + MHL_TX_DBG_INFO("Resources assigned to driver...\n"); + MHL_TX_DBG_ERR(" Reset GPIO = %d\n", + starter_kit_control_gpios[MHL_RESET_INDEX].gpio); + MHL_TX_DBG_ERR(" Interrupt GPIO = %d\n", + starter_kit_control_gpios[MHL_INT_INDEX].gpio); + MHL_TX_DBG_ERR(" I2C adapter = %d\n", i2c_adapter_num); + if (use_spi) + MHL_TX_DBG_ERR(" SPI adapter = %d\n", spi_bus_num); + + return 0; +} +#endif + +#if (LINUX_KERNEL_VER >= 308) +static int si_8620_mhl_tx_i2c_probe(struct i2c_client *client, +#else +static int __devinit si_8620_mhl_tx_i2c_probe(struct i2c_client *client, +#endif + const struct i2c_device_id *id) +{ + int ret; + + pr_info("%s(), i2c_device_id = %p\n", __func__, id); + +#if defined(SIMG_USE_DTS) + /* + * Modify default driver platform parameters with those + * specified in the device tree. + */ + if (client->dev.of_node) { + ret = si_8620_parse_dt(&client->dev); + if (ret) + goto done; + } +#endif + i2c_bus_adapter = to_i2c_adapter(client->dev.parent); + device_addresses[0].client = client; + + if (!i2c_bus_adapter || + !i2c_check_functionality(i2c_bus_adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + MHL_TX_DBG_ERR("[ERROR] i2c function check failed\n"); + ret = -EIO; + goto done; + } + i2c_mem.block_tx_buffers = kmalloc(MAX_I2C_EMSC_BLOCK_SIZE * + NUM_BLOCK_QUEUE_REQUESTS, GFP_KERNEL); + if (NULL == i2c_mem.block_tx_buffers) { + ret = -ENOMEM; + goto done; + } + + ret = starter_kit_init(); + if (ret >= 0) { + drv_info.irq = gpio_to_irq( + starter_kit_control_gpios[MHL_INT_INDEX].gpio); + ret = mhl_tx_init(&drv_info, &client->dev); + if (ret) { + MHL_TX_DBG_ERR("mhl_tx_init failed, error code %d\n", + ret); + gpio_free_array(starter_kit_control_gpios, + ARRAY_SIZE(starter_kit_control_gpios)); + } + } +done: + if (ret != 0) { + kfree(i2c_mem.block_tx_buffers); + probe_fail = true; + } + return ret; +} + +#if (LINUX_KERNEL_VER >= 308) +static int si_8620_mhl_tx_remove(struct i2c_client *client) +#else +static int __devexit si_8620_mhl_tx_remove(struct i2c_client *client) +#endif +{ + if (!use_spi) + kfree(i2c_mem.block_tx_buffers); + + gpio_free_array(starter_kit_control_gpios, + ARRAY_SIZE(starter_kit_control_gpios)); + return 0; +} + +int si_8620_pm_suspend(struct device *dev) +{ + int status = -EINVAL; + + if (dev == 0) + goto done; + + status = down_interruptible(&platform_lock); + if (status) + goto done; + + status = mhl_handle_power_change_request(dev, false); + /* + * 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); + + up(&platform_lock); +done: + return status; +} + +int si_8620_pm_resume(struct device *dev) +{ + int status = -EINVAL; + + if (dev == 0) + goto done; + + status = down_interruptible(&platform_lock); + if (status) + goto done; + + set_pin(X02_USB_SW_CTRL, 1); + status = mhl_handle_power_change_request(dev, true); + + up(&platform_lock); +done: + return status; +} + +int si_8620_power_control(bool power_up) +{ + struct device *dev = NULL; + int status; + + if (use_spi) + dev = &spi_dev->dev; + else + dev = &device_addresses[0].client->dev; + + if (power_up) + status = si_8620_pm_resume(dev); + else + status = si_8620_pm_suspend(dev); + return status; +} +EXPORT_SYMBOL_GPL(si_8620_power_control); + +int si_8620_get_hpd_status(int *hpd_status) +{ + struct device *dev = NULL; + int status = 0; + struct mhl_dev_context *dev_context; + + if (use_spi) + dev = &spi_dev->dev; + else + dev = &device_addresses[0].client->dev; + + dev_context = dev_get_drvdata(dev); + + if (down_interruptible(&dev_context->isr_lock)) { + MHL_TX_DBG_ERR("%scouldn't acquire 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("%sshutting down%s\n", + ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); + status = -ENODEV; + } else { + *hpd_status = si_mhl_tx_drv_get_hpd_status(dev_context); + MHL_TX_DBG_INFO("%HPD status: %s%d%s\n", + ANSI_ESC_YELLOW_TEXT, + *hpd_status, + ANSI_ESC_RESET_TEXT); + } + up(&dev_context->isr_lock); + return status; +} +EXPORT_SYMBOL_GPL(si_8620_get_hpd_status); + +int si_8620_get_hdcp2_status(uint32_t *hdcp2_status) +{ + struct device *dev = NULL; + int status = 0; + struct mhl_dev_context *dev_context; + + if (use_spi) + dev = &spi_dev->dev; + else + dev = &device_addresses[0].client->dev; + + dev_context = dev_get_drvdata(dev); + + if (down_interruptible(&dev_context->isr_lock)) { + MHL_TX_DBG_ERR("%scouldn't acquire 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("%sshutting down%s\n", + ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); + status = -ENODEV; + } else { + *hdcp2_status = si_mhl_tx_drv_get_hdcp2_status(dev_context); + MHL_TX_DBG_INFO("%HDCP2 status: %s0x%8x%s\n", + ANSI_ESC_YELLOW_TEXT, + *hdcp2_status, + ANSI_ESC_RESET_TEXT); + } + up(&dev_context->isr_lock); + return status; +} +EXPORT_SYMBOL_GPL(si_8620_get_hdcp2_status); + +static const struct i2c_device_id si_8620_mhl_tx_id[] = { + {MHL_DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, si_8620_mhl_tx_id); + +static const struct dev_pm_ops si_8620_tx_pm_ops = { + .runtime_suspend = si_8620_pm_suspend, + .runtime_resume = si_8620_pm_resume, +}; + +#ifdef SIMG_USE_DTS +static struct of_device_id si_8620_of_match_table[] = { + { + .compatible = "simg,sii-8620", + }, + {} +}; +#endif + +static struct i2c_driver si_8620_mhl_tx_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = MHL_DRIVER_NAME, +#ifdef SIMG_USE_DTS + .of_match_table = si_8620_of_match_table, +#endif + .pm = &si_8620_tx_pm_ops, + }, + .id_table = si_8620_mhl_tx_id, + .probe = si_8620_mhl_tx_i2c_probe, + +#if (LINUX_KERNEL_VER >= 308) + .remove = si_8620_mhl_tx_remove, +#else + .remove = __devexit_p(si_8620_mhl_tx_remove), +#endif + .command = NULL, +}; + +#ifndef SIMG_USE_DTS +static struct i2c_board_info __initdata si_8620_i2c_boardinfo[] = { + { + I2C_BOARD_INFO(MHL_DEVICE_NAME, (SA_TX_PAGE_0 >> 1)), + .flags = I2C_CLIENT_WAKE, + .irq = -1 + } +}; +#endif + +#if (LINUX_KERNEL_VER >= 308) +static int si_8620_mhl_tx_spi_probe(struct spi_device *spi) +#else +static int __devinit si_8620_mhl_tx_spi_probe(struct spi_device *spi) +#endif +{ + int ret; + + pr_info("%s(), spi = %p\n", __func__, spi); + spi->bits_per_word = 8; + spi_dev = spi; + spi_bus_num = spi->master->bus_num; + +#if defined(SIMG_USE_DTS) + /* + * Modify default driver platform parameters with those + * specified in the device tree. + */ + if (spi_dev->dev.of_node) { + ret = si_8620_parse_dt(&spi_dev->dev); + if (ret) + goto failed; + } + + /* + * Because we're using SPI for register access, we need to + * obtain separate i2c access for the I/O expander, so we + * use the I2C adapter number we got from the device tree. + */ + i2c_bus_adapter = i2c_get_adapter(i2c_adapter_num); + if (i2c_bus_adapter == NULL) { + pr_err("%s() failed to get i2c adapter %d\n", __func__, + i2c_adapter_num); + ret = -EFAULT; + goto failed; + } + + if (!i2c_check_functionality(i2c_bus_adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + MHL_TX_DBG_ERR("[ERROR] i2c function check failed\n"); + ret = -EIO; + goto failed; + } +#endif + + spi_mem.tx_buf = kmalloc(MAX_SPI_XFER_BUFFER_SIZE, GFP_KERNEL); + spi_mem.rx_buf = kmalloc(MAX_SPI_XFER_BUFFER_SIZE, GFP_KERNEL); + spi_mem.block_tx_buffers = kmalloc(MAX_SPI_EMSC_BLOCK_SIZE * + NUM_BLOCK_QUEUE_REQUESTS, GFP_KERNEL); + if (!spi_mem.tx_buf || !spi_mem.rx_buf || !spi_mem.block_tx_buffers) { + ret = -ENOMEM; + goto mem_cleanup; + } + ret = starter_kit_init(); + if (ret >= 0) { + drv_info.irq = gpio_to_irq( + starter_kit_control_gpios[MHL_INT_INDEX].gpio); + ret = mhl_tx_init(&drv_info, &spi_dev->dev); + if (ret) { + pr_err("%s(): mhl_tx_init failed, error code %d\n", + __func__, ret); + gpio_free_array(starter_kit_control_gpios, + ARRAY_SIZE(starter_kit_control_gpios)); + goto mem_cleanup; + } + goto done; + } + +mem_cleanup: + kfree(spi_mem.tx_buf); + spi_mem.tx_buf = NULL; + + kfree(spi_mem.rx_buf); + spi_mem.rx_buf = NULL; + + kfree(spi_mem.block_tx_buffers); + spi_mem.block_tx_buffers = NULL; + +#if defined(SIMG_USE_DTS) +failed: +#endif + probe_fail = true; + +done: + return ret; +} + +#if (LINUX_KERNEL_VER >= 308) +static int si_8620_mhl_spi_remove(struct spi_device *spi_dev) +#else +static int __devexit si_8620_mhl_spi_remove(struct spi_device *spi_dev) +#endif +{ + pr_info("%s() called\n", __func__); + + gpio_free_array(starter_kit_control_gpios, + ARRAY_SIZE(starter_kit_control_gpios)); + kfree(spi_mem.tx_buf); + kfree(spi_mem.rx_buf); + kfree(spi_mem.block_tx_buffers); + return 0; +} + +static struct spi_driver si_86x0_mhl_tx_spi_driver = { + .driver = { + .owner = THIS_MODULE, + .name = MHL_DRIVER_NAME, +#ifdef SIMG_USE_DTS + .of_match_table = si_8620_of_match_table, +#endif + .pm = &si_8620_tx_pm_ops, + }, + .probe = si_8620_mhl_tx_spi_probe, +#if (LINUX_KERNEL_VER >= 308) + .remove = si_8620_mhl_spi_remove, +#else + .remove = __devexit_p(si_8620_mhl_spi_remove), +#endif +}; + +#ifndef SIMG_USE_DTS +static struct spi_board_info __initdata si_86x0_spi_board_info = { + .modalias = MHL_DRIVER_NAME, + .max_speed_hz = SPI_BUS_SPEED, + .bus_num = SPI_BUS_NUM, + .chip_select = SPI_CHIP_SEL, + .mode = SPI_TRANSFER_MODE, + .irq = -1 +}; + +static int __init add_spi_device_to_bus(void) +{ + struct spi_master *spi_master; + int status = 0; + + pr_info("add_spi_device_to_bus called\n"); + + spi_master = spi_busnum_to_master(SPI_BUS_NUM); + if (spi_master == NULL) { + pr_err("spi_busnum_to_master(%d) returned NULL\n", SPI_BUS_NUM); + return -1; + } + + spi_dev = spi_new_device(spi_master, &si_86x0_spi_board_info); + if (spi_dev == NULL || probe_fail) { + pr_err("spi_new_device() failed\n"); + if (spi_dev) + spi_unregister_device(spi_dev); + status = -1; + goto exit; + } + +exit: + put_device(&spi_master->dev); + return status; +} +#endif + +static int __init spi_init(void) +{ + int status; + + status = spi_register_driver(&si_86x0_mhl_tx_spi_driver); + if (status < 0) { + pr_err("[ERROR] %s():%d failed !\n", __func__, __LINE__); + goto done; + } + +#ifndef SIMG_USE_DTS + status = add_spi_device_to_bus(); + if (status < 0) + MHL_TX_DBG_ERR("Failed to add MHL transmitter as SPI device\n"); +#endif + + if (probe_fail || status < 0) { + spi_unregister_driver(&si_86x0_mhl_tx_spi_driver); + status = -ENODEV; + } +done: + return status; +} + +static int __init i2c_init(void) +{ +#ifndef SIMG_USE_DTS + struct i2c_client *client; + int idx; +#endif + int ret = -ENODEV; + +#ifndef SIMG_USE_DTS + /* "Hotplug" the MHL transmitter device onto the 2nd I2C bus */ + i2c_bus_adapter = i2c_get_adapter(I2C_ADAPTER); + if (i2c_bus_adapter == NULL) + goto done; + + for (idx = 0; idx < ARRAY_SIZE(device_addresses); idx++) { + if (idx == 0) { + client = + i2c_new_device(i2c_bus_adapter, + &si_8620_i2c_boardinfo[idx]); + device_addresses[idx].client = client; + } else { + device_addresses[idx].client = + i2c_new_dummy(i2c_bus_adapter, + device_addresses[idx].dev_addr); + } + if (device_addresses[idx].client == NULL) { + MHL_TX_DBG_INFO("returning %d\n", ret); + goto err_exit; + } + } +#endif + ret = i2c_add_driver(&si_8620_mhl_tx_i2c_driver); + if (ret < 0 || probe_fail) { + if (ret == 0) + i2c_del_driver(&si_8620_mhl_tx_i2c_driver); + MHL_TX_DBG_INFO("failed !\n\nCHECK POWER AND CONNECTION " + "TO CP8620 Starter Kit.\n\n"); + goto err_exit; + } + + goto done; + +err_exit: +#ifndef SIMG_USE_DTS + for (idx = 0; idx < ARRAY_SIZE(device_addresses); idx++) { + if (device_addresses[idx].client != NULL) + i2c_unregister_device(device_addresses[idx].client); + } +#endif + +done: + if (probe_fail) + ret = -ENODEV; + + MHL_TX_DBG_INFO("returning %d\n", ret); + return ret; +} + +static int __init si_8620_init(void) +{ + int ret; + + pr_info("mhl: Starting SiI%d Driver v%s\n", MHL_PRODUCT_NUM, + buildVersion); + pr_info("mhl: %s\n", buildTime); +#if (INCLUDE_HID == 1) + pr_info("mhl: Supports MHL3 HID\n"); +#endif + + sema_init(&platform_lock, 1); + + platform_flags &= ~PLATFORM_FLAG_HEARTBEAT_MASK; + switch (use_heartbeat) { + case 0: + /* don't do anything with heatbeat */ + break; + case 1: + platform_flags |= PLATFORM_VALUE_ISSUE_HEARTBEAT; + break; + case 2: + platform_flags |= PLATFORM_VALUE_DISCONN_HEARTBEAT; + break; + default: + MHL_TX_DBG_ERR("%sinvalid use_heartbeat parameter%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + + if (tmds_link_speed == 15) + platform_flags |= PLATFORM_FLAG_1_5GBPS; + else if (tmds_link_speed == 3) + platform_flags |= PLATFORM_FLAG_3GBPS; + else if (tmds_link_speed == 6) + platform_flags |= PLATFORM_FLAG_6GBPS; + + /* + * NOTE: Even if the user selects to communicate with the MHL + * transmitter via SPI we still need I2C to communicate with + * other devices on the starter kit board. + */ +#ifndef SIMG_USE_DTS + i2c_bus_adapter = i2c_get_adapter(I2C_ADAPTER); + if (i2c_bus_adapter == NULL) { + pr_err("%s() failed to get i2c adapter %d\n", __func__, + (int)I2C_ADAPTER); + return -EFAULT; + } +#endif + if (use_spi) + ret = spi_init(); + else + ret = i2c_init(); + + return ret; +} + +static void __exit si_8620_exit(void) +{ +#ifndef SIMG_USE_DTS + int idx; +#endif + + pr_info("si_8620_exit called\n"); + + si_8620_power_control(false); + + if (use_spi) { + mhl_tx_remove(&spi_dev->dev); + spi_unregister_driver(&si_86x0_mhl_tx_spi_driver); +#ifndef SIMG_USE_DTS + spi_unregister_device(spi_dev); +#endif + } else { + if (device_addresses[0].client != NULL) { + mhl_tx_remove(&device_addresses[0].client->dev); + MHL_TX_DBG_INFO("client removed\n"); + } + i2c_del_driver(&si_8620_mhl_tx_i2c_driver); + MHL_TX_DBG_INFO("i2c driver deleted from context\n"); + +#ifndef SIMG_USE_DTS + for (idx = 0; idx < ARRAY_SIZE(device_addresses); idx++) { + MHL_TX_DBG_INFO("\n"); + if (device_addresses[idx].client != NULL) { + MHL_TX_DBG_INFO("unregistering device:%p\n", + device_addresses[idx].client); + i2c_unregister_device(device_addresses[idx]. + client); + } + } +#endif + } + MHL_TX_DBG_ERR("driver unloaded.\n"); +} + +static int debug_level_stack[15]; +static unsigned int debug_level_stack_ptr; + +void push_debug_level(int new_verbosity) +{ + if (debug_level_stack_ptr < ARRAY_SIZE(debug_level_stack)) { + /* stack is initially empty */ + debug_level_stack[debug_level_stack_ptr++] = debug_level; + debug_level = new_verbosity; + } else { + MHL_TX_DBG_ERR("%sdebug_level_stack overflowed%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } +} + +void pop_debug_level(void) +{ + if (debug_level_stack_ptr > 0) { + if (debug_level_stack_ptr > ARRAY_SIZE(debug_level_stack)) { + MHL_TX_DBG_ERR("%sdebug_level_stack overflowed%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } else { + debug_level = + debug_level_stack[--debug_level_stack_ptr]; + } + } +} + +int si_8620_register_callbacks(struct si_mhl_callback_api_t *p_callbacks) +{ + struct device *dev = NULL; + int status = 0; + struct mhl_dev_context *dev_context; + struct drv_hw_context *hw_context; + + if (use_spi) + dev = &spi_dev->dev; + else + dev = &device_addresses[0].client->dev; + + dev_context = dev_get_drvdata(dev); + hw_context = (struct drv_hw_context *)&dev_context->drv_context; + + if (down_interruptible(&dev_context->isr_lock)) + return -ERESTARTSYS; + + if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) { + status = -ENODEV; + } else { + if (NULL != p_callbacks) { + if (p_callbacks->context) + hw_context-> + callbacks.context = + p_callbacks->context; + if (p_callbacks->display_timing_enum_begin) + hw_context-> + callbacks.display_timing_enum_begin = + p_callbacks->display_timing_enum_begin; + if (p_callbacks->display_timing_enum_item) + hw_context-> + callbacks.display_timing_enum_item = + p_callbacks->display_timing_enum_item; + if (p_callbacks->display_timing_enum_end) + hw_context-> + callbacks.display_timing_enum_end = + p_callbacks->display_timing_enum_end; + if (p_callbacks->hpd_driven_low) + hw_context-> + callbacks.hpd_driven_low = + p_callbacks->hpd_driven_low; + if (p_callbacks->hpd_driven_high) + hw_context-> + callbacks.hpd_driven_high = + p_callbacks->hpd_driven_high; + } + } + + up(&dev_context->isr_lock); + return status; +} +EXPORT_SYMBOL(si_8620_register_callbacks); + +int si_8620_info_frame_change(enum hpd_high_callback_status mode_parm, + union avif_or_cea_861_dtd_u *p_avif_or_dtd, + size_t avif_or_dtd_max_length, union vsif_mhl3_or_hdmi_u *p_vsif, + size_t vsif_max_length) +{ + struct device *dev = NULL; + int status; + struct mhl_dev_context *dev_context; + struct drv_hw_context *hw_context; + + if (use_spi) + dev = &spi_dev->dev; + else + dev = &device_addresses[0].client->dev; + + dev_context = dev_get_drvdata(dev); + hw_context = (struct drv_hw_context *)&dev_context->drv_context; + + if (down_interruptible(&dev_context->isr_lock)) + return -ERESTARTSYS; + + if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) { + status = -ENODEV; + } else { + size_t xfer_size; + + memset(&hw_context->vsif_mhl3_or_hdmi_from_callback, 0, + sizeof(hw_context->vsif_mhl3_or_hdmi_from_callback)); + memset(&hw_context->avif_or_dtd_from_callback, 0, + sizeof(hw_context->avif_or_dtd_from_callback)); + + if (sizeof(hw_context->vsif_mhl3_or_hdmi_from_callback) < + vsif_max_length) { + xfer_size = sizeof( + hw_context->vsif_mhl3_or_hdmi_from_callback); + } else { + xfer_size = vsif_max_length; + } + memcpy(&hw_context->vsif_mhl3_or_hdmi_from_callback, p_vsif, + xfer_size); + + if (sizeof(hw_context->avif_or_dtd_from_callback) < + avif_or_dtd_max_length) { + xfer_size = sizeof( + hw_context->avif_or_dtd_from_callback); + } else { + xfer_size = avif_or_dtd_max_length; + } + memcpy(&hw_context->avif_or_dtd_from_callback, p_avif_or_dtd, + xfer_size); + + status = si_mhl_tx_drv_set_display_mode(dev_context, mode_parm); + } + + up(&dev_context->isr_lock); + return status; +} +EXPORT_SYMBOL(si_8620_info_frame_change); + +module_init(si_8620_init); +module_exit(si_8620_exit); + +MODULE_DESCRIPTION("Silicon Image MHL Transmitter driver"); +MODULE_AUTHOR("Silicon Image "); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/msm/mhl3/platform.h b/drivers/video/fbdev/msm/mhl3/platform.h new file mode 100644 index 000000000000..52d3e5d952e8 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/platform.h @@ -0,0 +1,236 @@ +/* + * 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. + */ + +#if !defined(PLATFORM_H) +#define PLATFORM_H + +#define DEVICE_ID_8620 0x8620 + +#define DEBUG_I2C_WRITE 1 +#define DEBUG_I2C_READ 0 +#define MAX_DEBUG_TRANSFER_SIZE 32 + +enum dbg_msg_level { + DBG_MSG_LEVEL_ERR, + DBG_MSG_LEVEL_WARN, + DBG_MSG_LEVEL_INFO, + DBG_MSG_LEVEL_GPIO, + DBG_MSG_LEVEL_EDID_INFO, + DBG_MSG_LEVEL_COMM_INFO +}; + +enum tx_interface_types { + TX_INTERFACE_TYPE_I2C, + TX_INTERFACE_TYPE_SPI +}; + +#if defined(DEBUG) + +void print_formatted_debug_msg(char *file_spec, const char *func_name, + int line_num, char *fmt, ...); + +void dump_transfer(enum tx_interface_types if_type, + u8 page, u8 offset, u16 count, u8 *values, bool write); + +#define MHL_TX_PROXY_DBG_PRINT(level, function, iLine, ...) \ +{ \ + if ((level) <= debug_level) \ + print_formatted_debug_msg(NULL, function, iLine, __VA_ARGS__); \ +} + +#define MHL_TX_GENERIC_DBG_PRINT(level, ...) \ +{ \ + if ((level) <= debug_level) \ + print_formatted_debug_msg(NULL, __func__, __LINE__, \ + __VA_ARGS__); \ +} + +#define MHL_TX_COMM_INFO(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_COMM_INFO, __VA_ARGS__) + +#define MHL_TX_EDID_INFO(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_EDID_INFO, __VA_ARGS__) + +#define MHL_TX_DBG_GPIO(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_GPIO, __VA_ARGS__) + +#define MHL_TX_DBG_INFO(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_INFO, __VA_ARGS__) + +#define MHL_TX_DBG_WARN(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_WARN, __VA_ARGS__) + +#define MHL_TX_DBG_ERR(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_ERR, __VA_ARGS__) + +#define DUMP_I2C_TRANSFER(page, offset, count, values, write_flag) \ + dump_transfer(TX_INTERFACE_TYPE_I2C, page, offset, \ + count, values, write_flag); + +#define DUMP_SPI_TRANSFER(page, offset, count, values, write_flag) \ + dump_transfer(TX_INTERFACE_TYPE_SPI, page, offset, \ + count, values, write_flag); +#else + +#define MHL_TX_PROXY_DBG_PRINT(level, function, iLine, ...) +#define MHL_TX_GENERIC_DBG_PRINT(level, ...) +#define MHL_TX_COMM_INFO(fmt, ...) +#define MHL_TX_EDID_INFO(fmt, ...) +#define MHL_TX_DBG_GPIO(fmt, ...) +#define MHL_TX_DBG_INFO(fmt, ...) +#define MHL_TX_DBG_WARN(fmt, ...) +#define MHL_TX_DBG_ERR(fmt, ...) + +#define DUMP_I2C_TRANSFER(page, offset, count, values, write_flag) +#define DUMP_SPI_TRANSFER(page, offset, count, values, write_flag) + +#endif + +void push_debug_level(int new_verbosity); +void pop_debug_level(void); + +enum vbus_power_state { + VBUS_OFF, + VBUS_ON +}; + +enum hpd_control_mode { + HPD_CTRL_MODE_ERROR = -1, + HPD_CTRL_OPEN_DRAIN, + HPD_CTRL_PUSH_PULL +}; + +enum hpd_control_mode platform_get_hpd_control_mode(void); +void platform_mhl_tx_hw_reset(uint32_t reset_period, uint32_t reset_delay); +void mhl_tx_vbus_control(enum vbus_power_state power_state); +void mhl_tx_vbus_current_ctl(uint16_t max_current_in_milliamps); + +struct platform_reg_pair { + uint8_t slave_addr; + uint8_t offset; +}; + +struct platform_signals_list { + char *name; + int16_t gpio_number; + struct platform_reg_pair gpio_reg_PCA950x; + uint8_t gpio_mask_PCA950x; + uint8_t *gpio_bank_value; + bool *param; +}; +struct block_buffer_info_t { + uint8_t *buffer; + uint8_t payload_offset; + size_t req_size; +}; +void si_mhl_tx_platform_get_block_buffer_info( + struct block_buffer_info_t *block_buffer_info); +int mhl_tx_write_block_spi_emsc(void *drv_context, struct block_req *req); +int mhl_tx_read_spi_emsc(void *drv_context, u16 count, u8 *values); +void mhl_tx_clear_emsc_read_err(void *drv_context); + +int mhl_tx_write_reg(void *drv_context, u16 address, u8 value); +int mhl_tx_read_reg(void *drv_context, u16 address); +int mhl_tx_write_reg_block(void *drv_context, u16 address, u16 count, + u8 *values); +int mhl_tx_read_reg_block(void *drv_context, u16 address, u16 count, + u8 *values); +int mhl_tx_modify_reg(void *drv_context, u16 address, u8 mask, u8 value); + +#ifdef DEBUG +int si_8620_power_control(bool power_up); +#endif + +uint32_t platform_get_flags(void); +#define PLATFORM_FLAG_HEARTBEAT_MASK 0x00000030 +#define PLATFORM_VALUE_ISSUE_HEARTBEAT 0x00000010 +#define PLATFORM_VALUE_DISCONN_HEARTBEAT 0x00000020 +#define PLATFORM_FLAG_LINK_SPEED 0x000000C0 +#define PLATFORM_FLAG_6GBPS 0x000000C0 +#define PLATFORM_FLAG_3GBPS 0x00000080 +#define PLATFORM_FLAG_1_5GBPS 0x00000040 + +int is_interrupt_asserted(void); +int get_config(void *dev_context, int config_idx); + +#define GPIO_LED_ON 0 +#define GPIO_LED_OFF 1 +void set_pin_impl(/*void *dev_context,*/ int pin_idx, int value, + const char *function_name, int line_num); +#define set_pin(/*dev_context,*/ pin_idx, value) \ + set_pin_impl(/*dev_context,*/ pin_idx, value, __func__, __LINE__) + +extern bool source_vbus_on; +extern bool bpp_on_wb; +extern bool use_spi; +extern bool wait_for_user_intr; +extern int debug_level; +extern bool debug_reg_dump; +extern bool force_ocbus_for_ects; +extern int crystal_khz; +extern int gpio_index; +extern int hdcp_content_type; +extern bool input_dev_rap; +#if (INCLUDE_RBP == 1) +extern bool input_dev_rbp; +#endif +extern bool input_dev_rcp; +extern bool input_dev_ucp; + +/* Starter kit board signal control index definitions */ +#define TX_HW_RESET 0 +#define TX_FW_WAKE 1 +#define CHG_DET 2 +#define XO3_SINK_VBUS_SENSE 3 +#define TWELVE_VOLT_PS_SENSE 4 +#define EEPROM_WR_EN 5 +#define TX2MHLRX_PWR 6 +#define M2U_VBUS_CTRL 7 +#define LED_3D 8 +#define LED_PACKED_PIXEL 9 +#define LED_HDCP 10 +#define LED_USB_MODE 11 +#define LED_SPARE_2 12 +#define LED_SPARE_3 13 +#define LED_SPARE_4 14 +#define X02_USB_SW_CTRL 15 +#define X02_USB_SW_CTRL0 16 +#define X02_USB_SW_CTRL1 17 +#define X02_USB_LED15_AMBER 18 +#define X02_USB_LED15_GREEN 19 + +#ifdef ANSI_COLORS +#define ANSI_ESC_RESET_TEXT "\x1b[0m" +#define ANSI_ESC_YELLOW_BG "\x1b[43m" +#define ANSI_ESC_WHITE_BG "\x1b[47m" +#define ANSI_ESC_RED_TEXT "\x1b[31m" +#define ANSI_ESC_YELLOW_TEXT "\x1b[33m" +#define ANSI_ESC_GREEN_TEXT "\x1b[32m" +#define ANSI_ESC_BLACK_TEXT "\1b[30m" +#define ANSI_ESC_WHITE_TEXT "\x1b[37m\x1b[1m" +#define ANSI_ESC_MAGENTA_TEXT "\x1b[35m" +#define ANSI_ESC_CYAN_TEXT "\x1b[36m" +#else +#define ANSI_ESC_RESET_TEXT "" +#define ANSI_ESC_WHITE_BG "" +#define ANSI_ESC_RED_TEXT "\n\n" +#define ANSI_ESC_YELLOW_TEXT "\n\n" +#define ANSI_ESC_GREEN_TEXT "\n\n" +#define ANSI_ESC_BLACK_TEXT "" +#define ANSI_ESC_WHITE_TEXT "" +#define ANSI_ESC_MAGENTA_TEXT "" +#define ANSI_ESC_CYAN_TEXT "" +#endif +#endif /* if !defined(PLATFORM_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/si_8620.lnt b/drivers/video/fbdev/msm/mhl3/si_8620.lnt new file mode 100644 index 000000000000..37a3659c13cb --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_8620.lnt @@ -0,0 +1 @@ +-DMHL_BUILD_NUM=$BUILD_NUM -DI2C_BUS_NUM=4 -DMHL_PRODUCT_NUM=8620 mhl_linux_tx.c mhl_rcp_inputdev.c mhl_supp.c platform.c si_8620_drv.c si_mdt_inputdev.c si_mhl2_edid_3d.c \ No newline at end of file diff --git a/drivers/video/fbdev/msm/mhl3/si_8620_drv.c b/drivers/video/fbdev/msm/mhl3/si_8620_drv.c new file mode 100644 index 000000000000..b5b69c4f4473 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_8620_drv.c @@ -0,0 +1,7638 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "si_fw_macros.h" +#include "si_app_devcap.h" +#include "si_infoframe.h" +#include "si_edid.h" +#include "si_mhl_defs.h" +#include "si_mhl2_edid_3d_api.h" +#include "si_8620_internal_api.h" +#include "si_mhl_tx_hw_drv_api.h" +#ifdef MEDIA_DATA_TUNNEL_SUPPORT +#include "si_mdt_inputdev.h" +#endif +#include "si_mhl_callback_api.h" +#include "si_8620_drv.h" +#include "mhl_linux_tx.h" +#include "si_8620_regs.h" +#include "mhl_supp.h" +#include "platform.h" + +#include "mhl_device_cfg.h" + + +#define SLEEP_10MS 10 +#define RFIFO_FILL_DELAY 10 + +#define MHL_STREAM_ID 0 +#define CONTENT_TYPE_DEFAULT 0 + +/* Local functions */ +#ifdef USE_HW_TIMER +static int int_1_isr(struct drv_hw_context *hw_context, uint8_t int_1_status); +#endif +static int int_2_isr(struct drv_hw_context *hw_context, uint8_t int_2_status); +static int int_3_isr(struct drv_hw_context *hw_context, uint8_t int_3_status); +static int int_4_isr(struct drv_hw_context *hw_context, uint8_t int_4_status); +static int int_5_isr(struct drv_hw_context *hw_context, uint8_t int_5_status); +static int int_8_isr(struct drv_hw_context *hw_context, uint8_t intr_8_status); +static int int_9_isr(struct drv_hw_context *hw_context, uint8_t int_9_status); +static int hdcp_isr(struct drv_hw_context *hw_context, uint8_t intr_status); +static int hdcp2_isr(struct drv_hw_context *hw_context, uint8_t intr_status); +static int g2wb_err_isr(struct drv_hw_context *hw_context, uint8_t intr_stat); +static int g2wb_isr(struct drv_hw_context *hw_context, uint8_t intr_stat); +static int mhl_cbus_isr(struct drv_hw_context *hw_context, uint8_t cbus_int); +static int mhl_cbus_err_isr(struct drv_hw_context *hw_context, + uint8_t cbus_err_int); +static int mhl3_block_isr(struct drv_hw_context *hw_context, uint8_t status); +static int coc_isr(struct drv_hw_context *hw_context, uint8_t coc_int_status); +static int tdm_isr(struct drv_hw_context *hw_context, uint8_t intr_status); +static int int_link_trn_isr(struct drv_hw_context *hw_context, + uint8_t intr_status); +static void enable_intr(struct drv_hw_context *hw_context, uint8_t intr_num, + uint8_t intr_mask); +static void switch_to_idle(struct drv_hw_context *hw_context, + bool do_interrupt_clear); +static void disconnect_mhl(struct drv_hw_context *hw_context, + bool do_interrupt_clear); +static void start_hdcp(struct drv_hw_context *hw_context); +static void start_hdcp_content_type(struct drv_hw_context *hw_context); +static void stop_video(struct drv_hw_context *hw_context); +static int get_device_rev(struct drv_hw_context *hw_context); +static void unmute_video(struct drv_hw_context *hw_context); +static int set_hdmi_params(struct mhl_dev_context *dev_context); +static void hsic_init(struct drv_hw_context *hw_context); + +static void si_mhl_tx_drv_enable_emsc_block(struct drv_hw_context *hw_context); +static void disable_gen2_write_burst_rcv(struct drv_hw_context *hw_context); +static void disable_gen2_write_burst_xmit(struct drv_hw_context *hw_context); +static void si_mhl_tx_drv_start_gen2_write_burst( + struct drv_hw_context *hw_context); + +static void si_mhl_tx_drv_set_lowest_tmds_link_speed( + struct mhl_dev_context *dev_context, uint32_t pixel_clock_frequency, + uint8_t bits_per_pixel); + +static int start_video(struct drv_hw_context *hw_context); + +/* Local data */ +#define FIELD_RATE_MEASUREMENT_INTERVAL 50 + +#define DDC_ABORT_THRESHOLD 10 +static int ddc_abort_count; + +#define MSC_ABORT_THRESHOLD 10 +static int msc_abort_count; + +struct intr_tbl { + uint8_t aggr_stat_index; + uint8_t aggr_stat_id_bit; + uint8_t mask; + uint16_t mask_addr; + uint16_t stat_addr; + int (*isr) (struct drv_hw_context *, uint8_t status); + char *name; +}; + +enum l1_intr_stat_enums_t { + FAST_INTR_STAT, + L1_INTR_STAT_0, + L1_INTR_STAT_1, + L1_INTR_STAT_2, + L1_INTR_STAT_3, + L1_INTR_STAT_4, + L1_INTR_STAT_5, + /* this one MUST be last */ + NUM_AGGREGATED_INTR_REGS +}; + +struct intr_tbl g_intr_tbl[] = { + {L1_INTR_STAT_2, 0x10, 0, REG_CBUS_DISC_INTR0_MASK, + REG_CBUS_DISC_INTR0, int_4_isr, "DISC"}, + {L1_INTR_STAT_1, 0x08, 0, REG_MDT_INT_1_MASK, + REG_MDT_INT_1, g2wb_err_isr, "G2WB"}, + {L1_INTR_STAT_1, 0x04, 0, REG_MDT_INT_0_MASK, + REG_MDT_INT_0, g2wb_isr, "G2WB"}, + {L1_INTR_STAT_5, 0x08, 0, REG_COC_INTR_MASK, REG_COC_INTR, + coc_isr, "COC"}, + {L1_INTR_STAT_4, 0x04, 0, REG_TRXINTMH, REG_TRXINTH, + tdm_isr, "TDM"}, + {L1_INTR_STAT_1, 0x01, 0, REG_CBUS_INT_0_MASK, + REG_CBUS_INT_0, mhl_cbus_isr, "MSC"}, + {L1_INTR_STAT_1, 0x02, 0, REG_CBUS_INT_1_MASK, + REG_CBUS_INT_1, mhl_cbus_err_isr, "MERR"}, + {L1_INTR_STAT_2, 0x40, 0, REG_EMSCINTRMASK, REG_EMSCINTR, + mhl3_block_isr, "BLOCK"}, + {L1_INTR_STAT_2, 0x80, 0, REG_EMSCINTRMASK1, + REG_EMSCINTR1, int_link_trn_isr, "LTRN"}, + {L1_INTR_STAT_0, 0x20, 0, REG_INTR8_MASK, REG_INTR8, + int_8_isr, "INFR"}, + {L1_INTR_STAT_0, 0x80, 0, REG_TPI_INTR_EN, + REG_TPI_INTR_ST0, hdcp_isr, "HDCP"}, + {L1_INTR_STAT_3, 0x01, 0, REG_HDCP2X_INTR0_MASK, + REG_HDCP2X_INTR0, hdcp2_isr, "HDCP2"}, + {L1_INTR_STAT_0, 0x40, 0, REG_INTR9_MASK, REG_INTR9, + int_9_isr, "EDID"}, + {L1_INTR_STAT_0, 0x04, 0, REG_INTR3_MASK, REG_INTR3, + int_3_isr, "DDC"}, + {L1_INTR_STAT_0, 0x08, 0, REG_INTR5_MASK, REG_INTR5, + int_5_isr, "SCDT"}, + {L1_INTR_STAT_0, 0x02, 0, REG_INTR2_MASK, REG_INTR2, + int_2_isr, "INT2"}, +#ifdef USE_HW_TIMER + {L1_INTR_STAT_0, 0x01, 0, REG_INTR1_MASK, REG_INTR1, + int_1_isr, "TIMR"}, +#endif +}; + +enum intr_nums_t { + INTR_DISC, + INTR_G2WB_ERR, + INTR_G2WB, + INTR_COC, + INTR_TDM, + INTR_MSC, + INTR_MERR, + INTR_BLOCK, + INTR_LINK_TRAINING, + INTR_INFR, + INTR_HDCP, + INTR_HDCP2, + INTR_EDID, + INTR_DDC, + INTR_SCDT, + INTR_USER, +#ifdef USE_HW_TIMER + INTR_TIMR, +#endif + MAX_INTR +}; + +#define BIT_RGND_READY_INT \ + BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT6 +#define BIT_CBUS_MHL12_DISCON_INT \ + BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT5 +#define BIT_CBUS_MHL3_DISCON_INT \ + BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT4 +#define BIT_NOT_MHL_EST_INT \ + BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT3 +#define BIT_MHL_EST_INT \ + BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT2 +#define BIT_MHL3_EST_INT \ + BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT1 + +#define BIT_RGND_READY_INT_MASK \ + BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK6 +#define BIT_CBUS_MHL12_DISCON_INT_MASK \ + BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK5 +#define BIT_CBUS_MHL3_DISCON_INT_MASK \ + BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK4 +#define BIT_NOT_MHL_EST_INT_MASK \ + BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK3 +#define BIT_MHL_EST_INT_MASK \ + BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK2 +#define BIT_MHL3_EST_INT_MASK \ + BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK1 + +#ifdef USE_HW_TIMER +#define BIT_HW_TIMER_POP BIT_INTR1_STAT7 +#endif + +#define BIT_DDC_CMD_DONE BIT_INTR3_STAT3 + +#define BIT_INTR_SCDT_CHANGE BIT_INTR5_STAT0 + +#define BIT_CEA_NEW_VSI BIT_INTR8_MASK2 +#define BIT_CEA_NEW_AVI BIT_INTR8_MASK1 + +#define BIT_VID_OVRRD_ENABLE_AUTO_LINK_MODE_UPDATE 0x08 + +#define BIT_MDT_RFIFO_DATA_RDY BIT_MDT_INT_0_MDT_INT_0_0 +#define BIT_MDT_IDLE_AFTER_HAWB_DISABLE BIT_MDT_INT_0_MDT_INT_0_2 +#define BIT_MDT_XFIFO_EMPTY BIT_MDT_INT_0_MDT_INT_0_3 + +#define BIT_MDT_RCV_TIMEOUT BIT_MDT_INT_1_MDT_INT_1_0 +#define BIT_MDT_RCV_SM_ABORT_PKT_RCVD BIT_MDT_INT_1_MDT_INT_1_1 +#define BIT_MDT_RCV_SM_ERROR BIT_MDT_INT_1_MDT_INT_1_2 + +#define BIT_MDT_XMIT_TIMEOUT BIT_MDT_INT_1_MDT_INT_1_5 +#define BIT_MDT_XMIT_SM_ABORT_PKT_RCVD BIT_MDT_INT_1_MDT_INT_1_6 +#define BIT_MDT_XMIT_SM_ERROR BIT_MDT_INT_1_MDT_INT_1_7 + +#define BIT_CBUS_DDC_PEER_ABORT BIT_DDC_ABORT_INT_DDC_ABORT_INT_STAT7 + +#define BIT_CBUS_MSC_MT_DONE_NACK \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK7 +#define BIT_CBUS_MSC_MR_SET_INT \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK6 +#define BIT_CBUS_MSC_MR_WRITE_BURST \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK5 +#define BIT_CBUS_MSC_MR_MSC_MSG \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK4 +#define BIT_CBUS_MSC_MR_WRITE_STAT \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK3 +#define BIT_CBUS_HPD_CHG \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK2 +#define BIT_CBUS_MSC_MT_DONE \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK1 +#define BIT_CBUS_CNX_CHG \ + BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK0 + +#define BIT_CBUS_CMD_ABORT BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK6 +#define BIT_CBUS_MSC_ABORT_RCVD BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK3 +#define BIT_CBUS_DDC_ABORT BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK2 +#define BIT_CBUS_CEC_ABORT BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK1 + +#define BIT_INTR9_EDID_ERROR BIT_INTR9_STAT6 +#define BIT_INTR9_EDID_DONE BIT_INTR9_STAT5 +#define BIT_INTR9_DEVCAP_DONE BIT_INTR9_STAT4 +#define BIT_INTR9_EDID_ERROR_MASK BIT_INTR9_MASK6 +#define BIT_INTR9_EDID_DONE_MASK BIT_INTR9_MASK5 +#define BIT_INTR9_DEVCAP_DONE_MASK BIT_INTR9_MASK4 + +#define BIT_TDM_INTR_SYNC_DATA BIT_TRXINTH_TRX_INTR8 +#define BIT_TDM_INTR_SYNC_WAIT BIT_TRXINTH_TRX_INTR9 + +#define BIT_TDM_INTR_SYNC_DATA_MASK BIT_TRXINTMH_TRX_INTRMASK8 +#define BIT_TDM_INTR_SYNC_WAIT_MASK BIT_TRXINTMH_TRX_INTRMASK9 + +#define BIT_HDCP2_INTR_AUTH_DONE \ + BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT0 +#define BIT_HDCP2_INTR_AUTH_FAIL \ + BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT1 +#define BIT_HDCP2_INTR_RPTR_RCVID_CHANGE \ + BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT4 + +#define BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR 0x04 +#define BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE 0x08 + +#define BIT_CBUS_MSC_MT_ABORT_INT_MSC_MT_PEER_ABORT \ + BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT7 +#define BIT_PAGE_CBUS_REG_MSC_MT_ABORT_INT_STAT5 \ + BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT5 +#define BIT_CBUS_MSC_MT_ABORT_INT_UNDEF_CMD \ + BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT3 +#define BIT_CBUS_MSC_MT_ABORT_INT_TIMEOUT \ + BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT2 +#define BIT_CBUS_MSC_MT_ABORT_INT_PROTO_ERR \ + BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT1 +#define BIT_CBUS_MSC_MT_ABORT_INT_MAX_FAIL \ + BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT0 + +#define REG_CBUS_MHL_SCRPAD_BASE 0x40 + +#define REG_RX_HDMI_CTRL0_DEFVAL_DVI 0x14 +#define REG_RX_HDMI_CTRL0_DEFVAL_HDMI 0x1C +#define REG_RX_HDMI_CTRL0_DEFVAL_HW_CTRL 0x10 + +#define REG_RX_HDMI_CTRL2_DEFVAL_DVI 0x30 +#define REG_RX_HDMI_CTRL2_DEFVAL_HDMI 0x38 + +#define TX_HW_RESET_PERIOD 10 +#define TX_HW_RESET_DELAY 100 + +#define MHL_FLOW_FLAG_NONE 0x0000 +#define MHL_FLOW_FLAG_MHL_IMPEDANCE 0x0001 +#define MHL_FLOW_FLAG_MHL_ESTABLISHED 0x0002 +#define MHL_FLOW_FLAG_DCAP_READY 0x0004 +#define MHL_FLOW_FLAG_DCAP_CHANGE 0x0008 +#define MHL_FLOW_FLAG_PATH_ENABLE 0x0010 +#define MHL_FLOW_FLAG_RAP_CONTENT_ON 0x0020 +#define MHL_FLOW_FLAG_CBUS_SET_HPD 0x0040 +#define MHL_FLOW_FLAG_EDID_RTP 0x0080 +#define MHL_FLOW_FLAG_INPUT_CLOCK_STABLE 0x0100 +#define MHL_FLOW_FLAG_INFOFRAME_RECEIVED 0x0200 +#define MHL_FLOW_FLAG_CP_AVAILABLE 0x0400 +#define MHL_FLOW_FLAG_CP_AUTHENTICATED 0x0800 + +#define BIT_COC_PLL_LOCK_STATUS_CHANGE 0x01 +#define BIT_COC_CALIBRATION_DONE 0x02 + +#define MSK_TDM_SYNCHRONIZED 0xC0 +#define VAL_TDM_SYNCHRONIZED 0x80 + +#define BIT_COC_STAT_6_CALIBRATION_DONE 0x80 +#define BITS_ES0_0_COC_STAT_0_FSM_STATE_MASK 0x0F +#define BITS_ES0_0_COC_STAT_F_CALIBRATION_STATE_2 0x02 +#define BITS_ES1_0_COC_STAT_0_CALIBRATION_MASK 0x8F +#define BITS_ES1_0_COC_STAT_0_CALIBRATION_STATE_2 0x02 +#define BITS_ES1_0_COC_STAT_0_PLL_LOCKED 0x80 + +#define VAL_M3_CTRL_MHL1_2_VALUE (BIT_M3_CTRL_SW_MHL3_SEL | \ + BIT_M3_CTRL_ENC_TMDS) + +#if defined(DEBUG) +static char *rgnd_value_string[] = { + "open", + "2k", + "1k", + "short" +}; +#endif + +#define IN_MHL3_MODE(hw_context) \ + (hw_context->cbus_mode > CM_oCBUS_PEER_IS_MHL1_2) + +static uint8_t colorSpaceTranslateInfoFrameToHw[] = { + VAL_INPUT_FORMAT_RGB, + VAL_INPUT_FORMAT_YCBCR422, + VAL_INPUT_FORMAT_YCBCR444, + VAL_INPUT_FORMAT_INTERNAL_RGB +}; + +/* + Based on module parameter "crystal_khz=xxxxx" program registers. +*/ +static void program_ext_clock_regs(struct drv_hw_context *hw_context, + int crystal_khz) +{ + /* preset to default crystal on SK - 19.2 MHz */ + int reg_fb_div_ctl_main = 0x04; + int reg_hdcp2x_tp1 = 0x5E; + + switch (crystal_khz) { + case 38400: + reg_fb_div_ctl_main = 0x0C; + reg_hdcp2x_tp1 = 0xBC; + break; + case 30000: + reg_fb_div_ctl_main = 0x06; + reg_hdcp2x_tp1 = 0x92; + break; + case 24000: + reg_fb_div_ctl_main = 0x05; + reg_hdcp2x_tp1 = 0x75; + break; + case 20000: + reg_fb_div_ctl_main = 0x04; + reg_hdcp2x_tp1 = 0x62; + break; + case 19200: + default: +/* reg_fb_div_ctl_main = 0x04; + reg_hdcp2x_tp1 = 0x5E;*/ + break; + } + mhl_tx_write_reg(hw_context, REG_DIV_CTL_MAIN, + reg_fb_div_ctl_main); + mhl_tx_write_reg(hw_context, REG_HDCP2X_TP1, reg_hdcp2x_tp1); + MHL_TX_DBG_INFO("Set ext clock regs: 3F2 = %02X, 3B4 = %02X\n", + reg_fb_div_ctl_main, reg_hdcp2x_tp1); +} + +static uint8_t ok_to_proceed_with_ddc(struct drv_hw_context *hw_context) +{ + int cbus_status = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); + if (cbus_status < 0) { + MHL_TX_DBG_INFO("I2C error: 0x%02x\n", cbus_status); + return 0; + } + if (!(BIT_CBUS_STATUS_CBUS_HPD & cbus_status)) + return 0; + if (!(BIT_CBUS_STATUS_CBUS_CONNECTED & cbus_status)) + return 0; + return cbus_status & (BIT_CBUS_STATUS_CBUS_HPD | + BIT_CBUS_STATUS_CBUS_CONNECTED); +} + +uint8_t si_mhl_tx_drv_ecbus_connected(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + int cbus_status = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); + if (cbus_status < 0) { + MHL_TX_DBG_INFO("I2C error: 0x%02x\n", cbus_status); + return 0; + } + + return cbus_status & BIT_CBUS_STATUS_CBUS_CONNECTED; +} +#if 0 +static void enable_heartbeat(struct drv_hw_context *hw_context) +{ + /* + * turn on disconnection based on heartbeat (as well as RSEN) - + * This is no longer the default behavior. + * use_heartbeat=2 is required to enable disconnection. + */ + switch (platform_get_flags() & PLATFORM_FLAG_HEARTBEAT_MASK) { + case PLATFORM_VALUE_DISCONN_HEARTBEAT: + MHL_TX_DBG_INFO + ("Disconnection on heartbeat failure is enabled\n"); + mhl_tx_modify_reg(hw_context, REG_DISC_CTRL1, + BIT_DISC_CTRL1_HB_ONLY, + BIT_DISC_CTRL1_HB_ONLY); + /* intentionally fall through */ + case PLATFORM_VALUE_ISSUE_HEARTBEAT: + /* Turn on heartbeat polling */ + MHL_TX_DBG_WARN("Heartbeat polling is enabled\n"); + mhl_tx_write_reg(hw_context, REG_MSC_HEARTBEAT_CONTROL, + 0xA7); + break; + default: + MHL_TX_DBG_INFO + ("Disconnection on heartbeat failure is disabled\n"); + MHL_TX_DBG_INFO("heartbeat entirely disabled for compliance\n"); + mhl_tx_modify_reg(hw_context, REG_DISC_CTRL1, + BIT_DISC_CTRL1_HB_ONLY, 0x00); + mhl_tx_write_reg(hw_context, REG_MSC_HEARTBEAT_CONTROL, + 0x27); + break; + } +} +#endif + +static void disable_heartbeat(struct drv_hw_context *hw_context) +{ + /* Disable MSC heartbeat */ + mhl_tx_write_reg(hw_context, REG_MSC_HEARTBEAT_CONTROL, 0x27); +} + +/* + For MHL 3 the auto_zone bits must be cleared. +*/ +static void clear_auto_zone_for_mhl_3(struct drv_hw_context *hw_context) +{ + /* Clear auto zone */ + mhl_tx_write_reg(hw_context, REG_TX_ZONE_CTL1, 0x0); + + /* Program PLL for 1X and clock from HSIC */ + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, + (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | + BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL | + BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); +} + +/* + For MHL 1/2 we should use auto_zone. +*/ +static void set_auto_zone_for_mhl_1_2(struct drv_hw_context *hw_context) +{ + /* Enable AUTO ZONE for MHL1/2 */ + mhl_tx_write_reg(hw_context, + REG_TX_ZONE_CTL1, + VAL_TX_ZONE_CTL1_TX_ZONE_CTRL_MODE); + + /* Program PLL for 1X and clock from HDMI */ + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, + (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | + BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); +} + +int si_mhl_tx_drv_set_tdm_slot_allocation(struct drv_hw_context *hw_context, + uint8_t *vc_slot_counts, + bool program) +{ + int status = -EINVAL; + uint16_t slot_total = 0; + uint8_t idx; + + /* To the extent we can sanity check the slot allocation request */ + if (vc_slot_counts[VC_CBUS1] != 1) + goto done; + for (idx = 0; idx < VC_MAX; idx++) { + slot_total += vc_slot_counts[idx]; + if (vc_slot_counts[idx] == 0) + goto done; + } + + switch (hw_context->cbus_mode) { + case CM_eCBUS_S: + case CM_eCBUS_S_AV_BIST: + if (slot_total != 25) + goto done; + break; + case CM_eCBUS_D: + case CM_eCBUS_D_AV_BIST: + if (slot_total != 200) + goto done; + break; + default: + goto done; + } + status = 0; + + if (program) { + mhl_tx_write_reg(hw_context, REG_TTXSPINUMS, + vc_slot_counts[VC_E_MSC]); + mhl_tx_write_reg(hw_context, REG_TTXHSICNUMS, + vc_slot_counts[VC_T_CBUS]); + mhl_tx_write_reg(hw_context, REG_TRXSPINUMS, + vc_slot_counts[VC_E_MSC]); + mhl_tx_write_reg(hw_context, REG_TRXHSICNUMS, + vc_slot_counts[VC_T_CBUS]); + memcpy(hw_context->tdm_virt_chan_slot_counts, + vc_slot_counts, + sizeof(hw_context->tdm_virt_chan_slot_counts)); + + } + +done: + return status; +} + +static int block_input_buffer_available(struct drv_hw_context *hw_context) +{ + uint16_t head, tail; + head = hw_context->block_protocol.head; + tail = hw_context->block_protocol.tail; + if (head == (tail + 1)) { + /* full case */ + return 0; + } + return 1; +} + +static int alloc_block_input_buffer(struct drv_hw_context *hw_context, + uint8_t **pbuffer) +{ + uint16_t head, tail; + int index; + head = hw_context->block_protocol.head; + tail = hw_context->block_protocol.tail; + + if (!block_input_buffer_available(hw_context)) { + /* full case */ + return -1; + } + index = tail; + + if (++tail >= NUM_BLOCK_INPUT_BUFFERS) + tail = 0; + + hw_context->block_protocol.tail = tail; + + *pbuffer = &hw_context->block_protocol.input_buffers[index][0]; + return index; +} + +static void set_block_input_buffer_length(struct drv_hw_context *hw_context, + int block, int length) +{ + if ((block < 0) || (block >= NUM_BLOCK_INPUT_BUFFERS)) + return; + + hw_context->block_protocol.input_buffer_lengths[block] = length; +} + +/* call this from mhl_supp.c to during processing of + * DRV_INTR_FLAG_EMSC_INCOMING + */ +int si_mhl_tx_drv_peek_block_input_buffer(struct mhl_dev_context *dev_context, + uint8_t **buffer, int *length) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + uint16_t head, tail, index; + head = hw_context->block_protocol.head; + tail = hw_context->block_protocol.tail; + if (head == tail) + return -1; + + index = head; + + *buffer = &hw_context->block_protocol.input_buffers[index][0]; + *length = hw_context->block_protocol.input_buffer_lengths[index]; + return 0; +} + +/* Call this from mhl_supp.c during processing of + * DRV_INTR_FLAG_EMSC_INCOMING + */ +void si_mhl_tx_drv_free_block_input_buffer(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + uint16_t head; + head = hw_context->block_protocol.head; + if (++head >= NUM_BLOCK_INPUT_BUFFERS) + head = 0; + + hw_context->block_protocol.head = head; +} + +#ifdef DUMP_ALL_REGS +void dump_all_registers(struct drv_hw_context *hw_context) +{ + uint8_t pages[] = { + SA_TX_PAGE_0, + SA_TX_PAGE_1, + SA_TX_PAGE_2, + SA_TX_PAGE_3, + SA_TX_PAGE_4, + SA_TX_CBUS, + SA_TX_PAGE_6, + SA_TX_PAGE_7, + SA_TX_PAGE_8 + }; + int i; + for (i = 0; i < sizeof(pages); ++i) { + int j; + for (j = 0; j < 256; ++j) { + int dummy; + dummy = mhl_tx_read_reg(hw_context, pages[i], j); + } + } +} +#endif + +static int get_emsc_fifo_count(struct drv_hw_context *hw_context, + uint16_t *fifo_count) +{ + int status; + uint8_t rfifo_size[2]; + uint16_t count; + + status = mhl_tx_read_reg_block(hw_context, REG_EMSCRFIFOBCNTL, + sizeof(rfifo_size), rfifo_size); + if (status < 0) { + MHL_TX_DBG_ERR("%sSPI Read error: %d%s\n", + ANSI_ESC_RED_TEXT, status, ANSI_ESC_RESET_TEXT); + return status; + } + count = ((uint16_t)rfifo_size[1] << 8) | (uint16_t)rfifo_size[0]; + if (count > LOCAL_BLK_RCV_BUFFER_SIZE) { + MHL_TX_DBG_ERR("%sFIFO count too large (%d)%s\n", + ANSI_ESC_RED_TEXT, count, ANSI_ESC_RESET_TEXT); + return -EINVAL; + } + + *fifo_count = count; + return 0; +} + +#define EMSC_HEADER_SIZE (1 + STD_TRANSPORT_HDR_SIZE) +static int mhl3_block_isr(struct drv_hw_context *hw_context, uint8_t int_status) +{ + int unload_cnt, remaining, avail; + int block_index, status; + uint8_t emsc_int, req_size; + uint16_t data_len; + uint8_t *pbit_bucket; + bool payload_encountered = false; + + /* If eMSC not in normal mode, all bets are off. */ + status = mhl_tx_read_reg(hw_context, REG_SPIBURSTSTAT); + if (0 == (BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE & status)) + return 0; + + if (BIT_EMSCINTR_EMSC_RFIFO_READ_ERR & int_status) { + MHL_TX_DBG_ERR("%seMSC read error! status:%x%s\n", + ANSI_ESC_RED_TEXT, int_status, ANSI_ESC_RESET_TEXT); + mhl_tx_clear_emsc_read_err(hw_context); + } + if ((BIT_EMSCINTR_SPI_DVLD & int_status) == 0) + return 0; + + do { + uint8_t *buffer = NULL; + uint8_t header[STD_TRANSPORT_HDR_SIZE]; + + emsc_int = mhl_tx_read_reg(hw_context, REG_EMSCINTR); + if (emsc_int & BIT_EMSCINTR_EMSC_RFIFO_READ_ERR) { + MHL_TX_DBG_WARN("%seMSC read error! status:%x%s\n", + ANSI_ESC_RED_TEXT, emsc_int, + ANSI_ESC_RESET_TEXT); + mhl_tx_clear_emsc_read_err(hw_context); + } + if ((emsc_int & BIT_EMSCINTR_SPI_DVLD) == 0) + break; + if (!block_input_buffer_available(hw_context)) + break; + + status = get_emsc_fifo_count(hw_context, &data_len); + if (status < 0) + break; + MHL_TX_DBG_INFO("EMSCINTR_SPI_DVLD, payload length: %d\n", + data_len); + + /* + * Has to be at least enough for a standard header. If not, + * it should be by the next time this interrupt is handled. + */ + if (data_len < EMSC_HEADER_SIZE) + break; + + /* TODO: Possible bug handled here */ + if (data_len == LOCAL_BLK_RCV_BUFFER_SIZE) + goto drain_rfifo; + + /* + * Get the request size. Even though the RFIFO size (data_len) + * is big enough, the req_size may be shorter than the STD + * header size, so we only get the one byte. + */ + if (use_spi) { + mhl_tx_read_spi_emsc(hw_context, 1, &req_size); + } else { + mhl_tx_read_reg_block(hw_context, + REG_EMSC_RCV_READ_PORT, 1, &req_size); + } + data_len--; + + /* Get the STD header if enough data, and go from there. */ + if (req_size < 1) { + MHL_TX_DBG_ERR("%sHeader Error: Command size less than " + "STD Transaction Header Size: %d%s\n", + ANSI_ESC_RED_TEXT, + req_size, + ANSI_ESC_RESET_TEXT); + goto done_emsc; + } + if (use_spi) { + mhl_tx_read_spi_emsc(hw_context, + STD_TRANSPORT_HDR_SIZE, header); + } else { + mhl_tx_read_reg_block(hw_context, + REG_EMSC_RCV_READ_PORT, + STD_TRANSPORT_HDR_SIZE, header); + } + req_size--; /* Read two bytes, so correct count. */ + data_len -= STD_TRANSPORT_HDR_SIZE; + + unload_cnt = header[0]; + remaining = header[1]; + + /* Validate STD header */ + if (req_size < remaining) { + MHL_TX_DBG_ERR("%sHeader Error: " + "RFIFO_LEN: %d REQ_LEN: %d REMAINING: %d%s\n", + ANSI_ESC_YELLOW_TEXT, + data_len, req_size, remaining, + ANSI_ESC_RESET_TEXT); + + req_size = (req_size < data_len) ? req_size : data_len; + goto drain_req; + } + if ((unload_cnt + remaining) == 0) { + MHL_TX_DBG_ERR("%sHeader Error: Unload and Remaining " + "counts are 0!%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + goto drain_req; + } + + avail = hw_context->block_protocol.peer_blk_rx_buf_avail; + if ((avail + unload_cnt) > + hw_context->block_protocol.peer_blk_rx_buf_max) { + MHL_TX_DBG_ERR( + "%seMSC Header Error: unload count too large:" + "%d + %d > %d%s\n", + ANSI_ESC_YELLOW_TEXT, + unload_cnt, avail, + hw_context->block_protocol.peer_blk_rx_buf_max, + ANSI_ESC_RESET_TEXT); + avail = hw_context->block_protocol.peer_blk_rx_buf_max; + } else { + avail += unload_cnt; + } + hw_context->block_protocol.peer_blk_rx_buf_avail = avail; + + /* + * FIFO data count MUST be at LEAST enough + * for the current transport header remaining byte count. + */ + if (data_len < remaining) { + MHL_TX_DBG_WARN("eMSC FIFO data count < remaining " + "byte count: %d < %d\n", + data_len, remaining); + msleep(RFIFO_FILL_DELAY); + status = get_emsc_fifo_count(hw_context, &data_len); + if (status < 0) + break; + if (data_len < remaining) { + status = -EINVAL; + break; + } + } + + if (remaining > 0) { + payload_encountered = true; + block_index = alloc_block_input_buffer(hw_context, + &buffer); + + if (use_spi) { + mhl_tx_read_spi_emsc(hw_context, + remaining, buffer); + } else { + mhl_tx_read_reg_block(hw_context, + REG_EMSC_RCV_READ_PORT, + remaining, buffer); + } + set_block_input_buffer_length(hw_context, block_index, + remaining); + } + hw_context->block_protocol.received_byte_count += + (remaining + STD_TRANSPORT_HDR_SIZE); + + data_len -= remaining; + } while (data_len > 0); + + if (payload_encountered) + hw_context->intr_info->flags |= DRV_INTR_EMSC_INCOMING; + goto done_emsc; + +drain_req: + if (req_size == 0) + goto done_emsc; + data_len = req_size; +drain_rfifo: + MHL_TX_DBG_ERR("Draining %d bytes from RFIFO\n", data_len); + + pbit_bucket = kmalloc(data_len, GFP_KERNEL); + if (use_spi) { + mhl_tx_read_spi_emsc(hw_context, data_len, pbit_bucket); + } else { + mhl_tx_read_reg_block(hw_context, + REG_EMSC_RCV_READ_PORT, data_len, pbit_bucket); + } + get_emsc_fifo_count(hw_context, &data_len); + MHL_TX_DBG_INFO("New RFIFO length: %d\n", data_len); + kfree(pbit_bucket); + +done_emsc: + return 0; +} + +static int coc_isr(struct drv_hw_context *hw_context, uint8_t coc_int_status) +{ + int ret_val = 0; + + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + if (BIT_COC_PLL_LOCK_STATUS_CHANGE & coc_int_status) + MHL_TX_DBG_INFO("COC PLL lock status change\n"); + + if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) { + + if (BIT_COC_CALIBRATION_DONE & coc_int_status) { + int calibration_stat; + uint8_t calibrated_value; + MHL_TX_DBG_INFO("Calibration done\n"); + + calibration_stat = mhl_tx_read_reg(hw_context, + REG_COC_STAT_0); + calibration_stat &= + BITS_COC_STAT_0_CALIBRATION_MASK; + calibrated_value = + BITS_COC_STAT_0_PLL_LOCKED | + BITS_COC_STAT_0_CALIBRATION_STATE_2; + + if (calibrated_value == calibration_stat) { + /* disable timeout */ + mhl_tx_write_reg(hw_context, + REG_COC_CTLB, 0x00); + MHL_TX_DBG_ERR("CoC in calibrated state\n"); + + switch (hw_context->cbus_mode) { + case CM_TRANSITIONAL_TO_eCBUS_S: + enable_intr(hw_context, INTR_TDM, + BIT_TDM_INTR_SYNC_DATA_MASK | + BIT_TDM_INTR_SYNC_WAIT_MASK); + hw_context->cbus_mode = + CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED; + si_set_cbus_mode_leds( + hw_context->cbus_mode); + break; + case CM_TRANSITIONAL_TO_eCBUS_S_BIST: + si_mhl_tx_set_bist_timer(dev_context); + if (!(BIST_TRIGGER_ECBUS_TX_RX_MASK & + dev_context->bist_trigger_info)) + enable_intr(hw_context, + INTR_TDM, + BIT_TDM_INTR_SYNC_DATA_MASK | + BIT_TDM_INTR_SYNC_WAIT_MASK); + hw_context->cbus_mode = + CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST; + si_set_cbus_mode_leds( + hw_context->cbus_mode); + break; + default: + ; + } + hw_context->intr_info->flags |= + DRV_INTR_COC_CAL; + } else { + MHL_TX_DBG_ERR("calibration state: 0x%02X\n", + calibration_stat); + } + } + } + return ret_val; +} + +static int tdm_isr(struct drv_hw_context *hw_context, uint8_t intr_status) +{ + int ret_val = 0; + uint8_t tdm_status; + + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) { + + if (BIT_TDM_INTR_SYNC_DATA & intr_status) { + + MHL_TX_DBG_INFO("TDM in SYNC_DATA state.\n"); + tdm_status = mhl_tx_read_reg(hw_context, REG_TRXSTA2); + + if ((tdm_status & MSK_TDM_SYNCHRONIZED) == + VAL_TDM_SYNCHRONIZED) { + + MHL_TX_DBG_ERR("TDM is synchronized %s\n", + si_mhl_tx_drv_get_cbus_mode_str( + hw_context->cbus_mode)); + + if (hw_context->cbus_mode < CM_eCBUS_S) { + hw_context->intr_info->flags |= + DRV_INTR_TDM_SYNC; + hw_context->block_protocol. + received_byte_count = 0; + + si_mhl_tx_send_blk_rcv_buf_info( + dev_context); +#ifdef EARLY_HSIC + /* + * todo hsic_init configures the + * transmitter for USB host mode. So + * really this call should be deferred + * until the driver has negotiated with + * the sink to take over the host role. + * The call is placed here for test. + */ + hsic_init(hw_context); +#endif + } + switch (hw_context->cbus_mode) { + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + hw_context-> + cbus_mode = CM_eCBUS_S_AV_BIST; + si_set_cbus_mode_leds( + hw_context->cbus_mode); + break; + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + hw_context->cbus_mode = CM_eCBUS_S; + si_set_cbus_mode_leds( + hw_context->cbus_mode); + break; + case CM_eCBUS_S: + case CM_eCBUS_S_AV_BIST: + /* do nothing in this case */ + break; + default: + MHL_TX_DBG_ERR( + "%sunexpected CBUS mode %s: %s\n", + ANSI_ESC_RED_TEXT, + si_mhl_tx_drv_get_cbus_mode_str( + hw_context->cbus_mode), + ANSI_ESC_RESET_TEXT) + } + } else { + switch (hw_context->cbus_mode) { + case CM_eCBUS_S_BIST: + MHL_TX_DBG_ERR("TDM not " + "synchronized," + " NOT retrying\n"); + break; + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + if (BIST_TRIGGER_ECBUS_TX_RX_MASK & + dev_context->bist_trigger_info) { + MHL_TX_DBG_ERR("TDM not " + "synchronized," + " NOT retrying\n"); + break; + } + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + MHL_TX_DBG_ERR("TDM not synchronized," + " retrying\n"); + mhl_tx_write_reg(hw_context, + REG_MHL_PLL_CTL2, 0x00); + mhl_tx_write_reg(hw_context, + REG_MHL_PLL_CTL2, 0x80); + break; + default: + ; + } + } + } + + if (BIT_TDM_INTR_SYNC_WAIT & intr_status) + MHL_TX_DBG_ERR("TDM in SYNC_WAIT state.\n"); + } + + return ret_val; +} + +static int int_link_trn_isr(struct drv_hw_context *hw_context, + uint8_t intr_status) +{ + if (IN_MHL3_MODE(hw_context)) { + if (BIT_EMSCINTR1_EMSC_TRAINING_COMMA_ERR & intr_status) { + MHL_TX_DBG_ERR("%sTraining comma " + "error COC_STAT_0:0x%02x%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + + } else { + MHL_TX_DBG_ERR("%sCOC_STAT_0:0x%02x%s\n", + ANSI_ESC_GREEN_TEXT, + mhl_tx_read_reg(hw_context, REG_COC_STAT_0), + ANSI_ESC_RESET_TEXT); + } + return 0; + } else { + return 0; + } +} + +static char *cbus_mode_strings[NUM_CM_MODES] = { + "CM_NO_CONNECTION", + "CM_NO_CONNECTION_BIST_SETUP", + "CM_NO_CONNECTION_BIST_STAT", + "CM_oCBUS_PEER_VERSION_PENDING", + "CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP", + "CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT", + "CM_oCBUS_PEER_IS_MHL1_2", + "CM_oCBUS_PEER_IS_MHL3_BIST_SETUP", + "CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT", + "CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY", + "CM_oCBUS_PEER_IS_MHL3_BIST_STAT", + "CM_oCBUS_PEER_IS_MHL3", + "CM_TRANSITIONAL_TO_eCBUS_S_BIST", + "CM_TRANSITIONAL_TO_eCBUS_D_BIST", + "CM_TRANSITIONAL_TO_eCBUS_S", + "CM_TRANSITIONAL_TO_eCBUS_D", + "CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST", + "CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST", + "CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED", + "CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED", + "CM_eCBUS_S_BIST", + "CM_eCBUS_D_BIST", + "CM_BIST_DONE_PENDING_DISCONNECT", + "CM_eCBUS_S", + "CM_eCBUS_D", + "CM_eCBUS_S_AV_BIST", + "CM_eCBUS_D_AV_BIST", +}; +void si_set_cbus_mode_leds_impl(enum cbus_mode_e mode_sel, + const char *func_name, + int line_num) +{ + if (mode_sel < NUM_CM_MODES) { + MHL_TX_PROXY_DBG_PRINT(DBG_MSG_LEVEL_WARN, + func_name, line_num, + "CBUS MODE: %s%s%s\n", + ANSI_ESC_CYAN_TEXT, + cbus_mode_strings[mode_sel], + ANSI_ESC_RESET_TEXT) + } + switch (mode_sel) { + case CM_NO_CONNECTION: + case CM_NO_CONNECTION_BIST_SETUP: + case CM_NO_CONNECTION_BIST_STAT: + set_pin(X02_USB_LED15_AMBER, 1); + set_pin(X02_USB_LED15_GREEN, 1); + set_pin(LED_USB_MODE, 0); + break; + case CM_oCBUS_PEER_VERSION_PENDING: + case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: + case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: + set_pin(X02_USB_LED15_AMBER, 0); + set_pin(X02_USB_LED15_GREEN, 0); + set_pin(LED_USB_MODE, 1); + break; + case CM_oCBUS_PEER_IS_MHL1_2: + set_pin(X02_USB_LED15_AMBER, 0); + set_pin(X02_USB_LED15_GREEN, 1); + set_pin(LED_USB_MODE, 1); + break; + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: + case CM_oCBUS_PEER_IS_MHL3: + case CM_TRANSITIONAL_TO_eCBUS_S_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S: + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + case CM_TRANSITIONAL_TO_eCBUS_D: + case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: + case CM_eCBUS_S_BIST: + case CM_eCBUS_D_BIST: + case CM_BIST_DONE_PENDING_DISCONNECT: + case CM_eCBUS_S: + case CM_eCBUS_D: + case CM_eCBUS_S_AV_BIST: + case CM_eCBUS_D_AV_BIST: + set_pin(X02_USB_LED15_AMBER, 1); + set_pin(X02_USB_LED15_GREEN, 0); + set_pin(LED_USB_MODE, 1); + break; + default: + MHL_TX_DBG_INFO("CBUS MODE: %02X\n", mode_sel); + break; + } +} + +static void leave_ecbus_s_mode(struct drv_hw_context *hw_context) +{ + mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x40); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0x07); + /* bugzilla 33456 */ + mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x06); +} +static void force_cbus_high_z(struct drv_hw_context *hw_context) +{ + int disc_ctrl5; + disc_ctrl5 = mhl_tx_read_reg(hw_context, REG_DISC_CTRL5); + mhl_tx_write_reg(hw_context, REG_DISC_CTRL5, + disc_ctrl5 & ~MSK_DISC_CTRL5_CBUSMHL_PUP_SEL); + msleep(50); + mhl_tx_write_reg(hw_context, REG_DISC_CTRL5, disc_ctrl5); + +} + +static void ecbus_s_bist_prep(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info) +{ + uint8_t start_cmd = 0x01; + memset(hw_context->prev_bist_coc_status, 0, + ARRAY_SIZE(hw_context->prev_bist_coc_status)); + /* Clear eCBUS-S BIST error counter and status + and remain in default CoC working mode. + */ + mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x66); + /* Clear eCBUS-S BIST error counter and status */ + mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x60); + + switch (test_info->e_cbus_pattern) { + case BIST_ECBUS_PATTERN_UNSPECIFIED: + case BIST_ECBUS_PATTERN_PRBS: + start_cmd = 0x01; + break; + + case BIST_ECBUS_PATTERN_FIXED_8: + mhl_tx_write_reg(hw_context, REG_COC_CTL9, + (uint8_t) test_info->e_cbus_fixed_pat); + mhl_tx_write_reg(hw_context, REG_COC_CTLA, + (uint8_t) test_info->e_cbus_fixed_pat); + start_cmd = 0x02; + break; + + case BIST_ECBUS_PATTERN_FIXED_10: + /* Fixed10 is not supported by eCBUS-S + * Verify this command option is properly refused. + * For now just fall through to default. + */ + + default: + MHL_TX_DBG_ERR("Unrecognized test pattern detected!\n"); + } + mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x60 | start_cmd); + mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x05); + mhl_tx_modify_reg(hw_context, REG_COC_CTL0, + BIT_COC_CTL0_COC_CONTROL0_2, 0x00); + +} + +static void ecbus_d_bist_prep(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info) +{ + uint8_t start_cmd = 0x03; + /* Clear eCBUS-D BIST error counter and status */ + mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x60); + + switch (test_info->e_cbus_pattern) { + case BIST_ECBUS_PATTERN_UNSPECIFIED: + case BIST_ECBUS_PATTERN_PRBS: + start_cmd = 0x01; + break; + + case BIST_ECBUS_PATTERN_FIXED_8: + mhl_tx_write_reg(hw_context, REG_DOC_CTL9, + (uint8_t) test_info->e_cbus_fixed_pat); + + mhl_tx_write_reg(hw_context, REG_DOC_CTLA, + (uint8_t) test_info->e_cbus_fixed_pat); + start_cmd = 0x02; + break; + + case BIST_ECBUS_PATTERN_FIXED_10: + mhl_tx_write_reg(hw_context, REG_DOC_CTL9, + (uint8_t) test_info->e_cbus_fixed_pat); + mhl_tx_write_reg(hw_context, REG_DOC_CTL8, + ((uint8_t) + (test_info->e_cbus_fixed_pat >> 8)) & 0x03); + + start_cmd = + (uint8_t) ((test_info-> + e_cbus_fixed_pat & 0x0300) >> 6); + if (test_info->e_cbus_fixed_pat & 0x8000) { + mhl_tx_write_reg(hw_context, REG_DOC_CTLA, + ~(uint8_t) test_info->e_cbus_fixed_pat); + mhl_tx_modify_reg(hw_context, REG_DOC_CTL8, 0x0C, + ~start_cmd); + } else { + mhl_tx_write_reg(hw_context, REG_DOC_CTLA, + (uint8_t) test_info->e_cbus_fixed_pat); + mhl_tx_modify_reg(hw_context, REG_DOC_CTL8, 0x0C, + start_cmd); + } + start_cmd = 0x03; + break; + default: + MHL_TX_DBG_ERR("Unrecognized test pattern detected!\n"); + start_cmd = 0x03; + } + mhl_tx_write_reg(hw_context, REG_DOC_CTL7, start_cmd); + +} +/* + * Configure the CBUS link for the requested operating mode. + * Returns: + * -1 = Requested mode not available. + * 0 = Requested mode change is complete. + * 1 = Requested mode change is in process. In this case completion + * is indicated by the interrupt flag DRV_INTR_FLAG_TDM_SYNC. + */ +int si_mhl_tx_drv_switch_cbus_mode(struct drv_hw_context *hw_context, + enum cbus_mode_e mode_sel) +{ + int status = 1; + uint8_t slot_total; + int coc_stat_0; + int i = 0; + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + MHL_TX_DBG_WARN("Switch cbus_mode from %x to %x\n", + hw_context->cbus_mode, mode_sel); + + if (hw_context->cbus_mode < CM_oCBUS_PEER_VERSION_PENDING) + return -1; + + switch (mode_sel) { + case CM_NO_CONNECTION: + case CM_NO_CONNECTION_BIST_SETUP: + case CM_NO_CONNECTION_BIST_STAT: + switch (hw_context->cbus_mode) { + case CM_NO_CONNECTION: + case CM_NO_CONNECTION_BIST_SETUP: + case CM_NO_CONNECTION_BIST_STAT: + /* already disconnected */ + break; + case CM_oCBUS_PEER_VERSION_PENDING: + case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: + case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: + case CM_oCBUS_PEER_IS_MHL1_2: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: + case CM_oCBUS_PEER_IS_MHL3: + force_cbus_high_z(hw_context); + break; + case CM_eCBUS_D_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D: + case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: + case CM_eCBUS_D: + case CM_eCBUS_D_AV_BIST: + /* todo: either implement or remove eCBUS-D support */ + break; + case CM_eCBUS_S_BIST: + case CM_eCBUS_S_AV_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + hw_context->cbus_mode = CM_BIST_DONE_PENDING_DISCONNECT; + si_set_cbus_mode_leds(hw_context->cbus_mode); + case CM_TRANSITIONAL_TO_eCBUS_S: + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + leave_ecbus_s_mode(hw_context); + force_cbus_high_z(hw_context); + /* let disconnect_mhl handle this transition */ + mode_sel = hw_context->cbus_mode; + break; + case CM_eCBUS_S: + leave_ecbus_s_mode(hw_context); + force_cbus_high_z(hw_context); + mode_sel = CM_NO_CONNECTION; + break; + default: + MHL_TX_DBG_ERR("%sinvalid cbus mode%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + } + hw_context->cbus_mode = mode_sel; + si_set_cbus_mode_leds(hw_context->cbus_mode); + break; + case CM_oCBUS_PEER_IS_MHL1_2: + MHL_TX_DBG_ERR("Switch to MHL1/2 oCBUS mode\n"); +#define BIT_CBUS_MSC_COMPATIBILITY_CONTROL_ENABLE_XDEVCAP 0x80 + mhl_tx_write_reg(hw_context, + REG_CBUS_MSC_COMPATIBILITY_CONTROL, + 0x02); + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_MHL1_2_VALUE); + /* + * disable BIT_DPD_PWRON_HSIC + */ + mhl_tx_write_reg(hw_context, REG_DPD, + BIT_DPD_PWRON_PLL | + BIT_DPD_PDNTX12 | + BIT_DPD_OSC_EN); + enable_intr(hw_context, INTR_COC, 0); + set_auto_zone_for_mhl_1_2(hw_context); + + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xBC); + + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL1, 0xFE); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL3, 0x48); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x39); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2A); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL6, 0x2A); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL7, 0x08); + hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL1_2; + si_set_cbus_mode_leds(hw_context->cbus_mode); + break; + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: + case CM_oCBUS_PEER_IS_MHL3: + switch (hw_context->cbus_mode) { + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: + case CM_oCBUS_PEER_IS_MHL3: + break; + case CM_TRANSITIONAL_TO_eCBUS_S_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + case CM_eCBUS_S_BIST: + case CM_eCBUS_S_AV_BIST: + case CM_BIST_DONE_PENDING_DISCONNECT: + hw_context->cbus_mode = CM_BIST_DONE_PENDING_DISCONNECT; + case CM_TRANSITIONAL_TO_eCBUS_S: + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + /* let disconnect_mhl handle this transition */ + mode_sel = hw_context->cbus_mode; + default: + leave_ecbus_s_mode(hw_context); + /* when a graceful transition to oCBUS mode can + be tested, remove the following line */ + force_cbus_high_z(hw_context); + } + hw_context->cbus_mode = mode_sel; + si_set_cbus_mode_leds(hw_context->cbus_mode); + break; + + case CM_eCBUS_S_BIST: + case CM_eCBUS_S: +#ifdef CoC_FSM_MONITORING +#ifndef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_6, BIT_CTRL1_GPIO_I_6); +#endif +#endif + si_mhl_tx_initialize_block_transport(dev_context); + si_mhl_tx_drv_enable_emsc_block(hw_context); + + MHL_TX_DBG_WARN("hpd status: 0x%02x\n", + mhl_tx_read_reg(hw_context, + REG_CBUS_STATUS)); + switch (mode_sel) { + case CM_eCBUS_S_BIST: + hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_S_BIST; + si_set_cbus_mode_leds(hw_context->cbus_mode); + + if (BIST_TRIGGER_ECBUS_TX_RX_MASK & + dev_context->bist_trigger_info) + ecbus_s_bist_prep(hw_context, + &dev_context->bist_setup); + break; + case CM_eCBUS_S: + hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_S; + break; + default: + ; + } + + mhl_tx_write_reg(hw_context, REG_TTXSPINUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_E_MSC]); + mhl_tx_write_reg(hw_context, REG_TRXSPINUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_E_MSC]); + slot_total = hw_context->tdm_virt_chan_slot_counts[VC_E_MSC]; + mhl_tx_write_reg(hw_context, REG_TTXHSICNUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_T_CBUS]); + mhl_tx_write_reg(hw_context, REG_TRXHSICNUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_T_CBUS]); + slot_total += hw_context->tdm_virt_chan_slot_counts[VC_T_CBUS]; + mhl_tx_write_reg(hw_context, REG_TTXTOTNUMS, 24); + mhl_tx_write_reg(hw_context, REG_TRXTOTNUMS, 24); + + /* begin reset */ + mhl_tx_write_reg(hw_context, REG_PWD_SRST, 0xA0); + + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0xBC); + + /* release sw-reset */ + mhl_tx_write_reg(hw_context, REG_PWD_SRST, 0x20); + + /* Enable timeout */ + mhl_tx_write_reg(hw_context, REG_COC_CTLB, 0x01); + + mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x5C); + mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x03); + mhl_tx_write_reg(hw_context, REG_COC_CTL15, 0x80); +#ifdef CoC_FSM_MONITORING + #ifndef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_7, BIT_CTRL1_GPIO_I_7); + #endif +#endif +#define STATE_3_TRAP_LIMIT 6 +#define TRAP_WAIT_SLEEP 1 + for (i = 0; i < STATE_3_TRAP_LIMIT; ++i) { + int temp; + temp = mhl_tx_read_reg(hw_context, REG_EMSCINTR1); + coc_stat_0 = mhl_tx_read_reg(hw_context, + REG_COC_STAT_0); + if (0x03 == (BITS_ES0_0_COC_STAT_0_FSM_STATE_MASK & + coc_stat_0)) { +#ifdef CoC_FSM_MONITORING + #ifndef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_7, 0); + #endif +#endif + msleep(20); +#ifdef CoC_FSM_MONITORING + #ifndef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_7, BIT_CTRL1_GPIO_I_7); + #endif +#endif + break; + } + + if (0x02 == (BITS_ES0_0_COC_STAT_0_FSM_STATE_MASK & + coc_stat_0)) { + break; + } + + if (!(BITS_ES1_0_COC_STAT_0_PLL_LOCKED & coc_stat_0)) { + MHL_TX_DBG_ERR( + "%slost PLL_LOCK coc_stat_0:0x%02x%s\n", + ANSI_ESC_RED_TEXT, + coc_stat_0, + ANSI_ESC_RESET_TEXT); + i = STATE_3_TRAP_LIMIT; + break; + } + msleep(TRAP_WAIT_SLEEP); + } + + mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x00); + mhl_tx_write_reg(hw_context, REG_COC_CTL15, 0x80); + + if (i < STATE_3_TRAP_LIMIT) { + + mhl_tx_write_reg(hw_context, REG_CBUS3_CNVT, 0x85); + } else { + MHL_TX_DBG_ERR("%stimed out waiting for trap%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + status = -1; + + disconnect_mhl(hw_context, true); + switch_to_idle(hw_context, false); + mode_sel = CM_NO_CONNECTION; + } +#ifdef CoC_FSM_MONITORING + #ifndef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_7 | BIT_CTRL1_GPIO_I_6, 0); + #endif +#endif + + break; + + case CM_eCBUS_D_BIST: + case CM_eCBUS_D: + hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_D; + + si_mhl_tx_initialize_block_transport(dev_context); + si_mhl_tx_drv_enable_emsc_block(hw_context); + + switch (mode_sel) { + case CM_eCBUS_D_BIST: + hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_D_BIST; + si_set_cbus_mode_leds(hw_context->cbus_mode); + if (BIST_TRIGGER_ECBUS_TX_RX_MASK & + dev_context->bist_trigger_info) + ecbus_d_bist_prep(hw_context, + &dev_context->bist_setup); + break; + case CM_eCBUS_D: + hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_D; + si_set_cbus_mode_leds(hw_context->cbus_mode); + break; + default: + ; + } + mhl_tx_write_reg(hw_context, REG_TTXSPINUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_E_MSC]); + mhl_tx_write_reg(hw_context, REG_TRXSPINUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_E_MSC]); + slot_total = hw_context->tdm_virt_chan_slot_counts[VC_E_MSC]; + mhl_tx_write_reg(hw_context, REG_TTXHSICNUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_T_CBUS]); + mhl_tx_write_reg(hw_context, REG_TRXHSICNUMS, + hw_context-> + tdm_virt_chan_slot_counts[VC_T_CBUS]); + slot_total += hw_context->tdm_virt_chan_slot_counts[VC_T_CBUS]; + mhl_tx_write_reg(hw_context, REG_TTXTOTNUMS, 199); + mhl_tx_write_reg(hw_context, REG_TRXTOTNUMS, 199); + + break; + + default: + MHL_TX_DBG_ERR("Invalid or unsupported CBUS mode specified\n"); + status = -EINVAL; + break; + } + return status; +} + +enum cbus_mode_e si_mhl_tx_drv_get_cbus_mode(struct mhl_dev_context + *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + return hw_context->cbus_mode; +} + +char *si_mhl_tx_drv_get_cbus_mode_str(enum cbus_mode_e cbus_mode) +{ + if (cbus_mode >= NUM_CM_MODES) + return "invalid cbus mode"; + + return cbus_mode_strings[cbus_mode]; +} + +static void disable_gen2_write_burst_rcv(struct drv_hw_context *hw_context) +{ + if (hw_context->gen2_write_burst_rcv) { + /* disable Gen2 Write Burst to allow normal CBUS traffic */ + mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, 0); + mhl_tx_write_reg(hw_context, REG_MDT_RCV_CONTROL, 0); + /* BIT_MDT_RCV_CONTROL_MDT_RCV_EN); */ + /* TODO: Review with John */ + /* BIT_MDT_RCV_CONTROL_MDT_DISABLE); */ + MHL_TX_DBG_INFO("%sdisabled GEN2%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + hw_context->gen2_write_burst_rcv = false; + } +} + +static void disable_gen2_write_burst_xmit(struct drv_hw_context *hw_context) +{ + if (hw_context->gen2_write_burst_xmit) { + /* disable Gen2 Write Burst to allow normal CBUS traffic */ + mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, 0); + MHL_TX_DBG_INFO("%sdisabled GEN2%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + hw_context->gen2_write_burst_xmit = false; + } +} + +static void enable_gen2_write_burst_rcv(struct drv_hw_context *hw_context) +{ + /* enable Gen2 Write Burst interrupt, MSC and EDID interrupts. */ + if (!hw_context->gen2_write_burst_rcv) { + /* 2 second timeout */ + mhl_tx_write_reg(hw_context, REG_MDT_RCV_TIMEOUT, 100); + mhl_tx_write_reg(hw_context, REG_MDT_RCV_CONTROL, + BIT_MDT_RCV_CONTROL_MDT_RCV_EN | + hw_context->delayed_hawb_enable_reg_val); + + MHL_TX_DBG_INFO("%senabled GEN2%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + hw_context->gen2_write_burst_rcv = true; + } +} + +static void enable_gen2_write_burst_xmit(struct drv_hw_context *hw_context) +{ + /* enable Gen2 Write Burst interrupt, MSC and EDID interrupts. */ + if (!hw_context->gen2_write_burst_xmit) { + mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, + BIT_MDT_XMIT_CONTROL_MDT_XMIT_EN | + BIT_MDT_XMIT_CONTROL_MDT_XMIT_FIXED_BURST_LEN); + + MHL_TX_DBG_INFO("%senabled GEN2%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + hw_context->gen2_write_burst_xmit = true; + } +} + +#ifndef MANUAL_EDID_FETCH +static void freeze_MHL_connect(struct drv_hw_context *hw_context) +{ + mhl_tx_write_reg(hw_context, REG_DISC_STAT1, 0x08); + mhl_tx_modify_reg(hw_context, REG_DISC_CTRL5, + BIT_DISC_CTRL5_DSM_OVRIDE, + BIT_DISC_CTRL5_DSM_OVRIDE); +} + +static void unfreeze_MHL_connect(struct drv_hw_context *hw_context) +{ + mhl_tx_modify_reg(hw_context, REG_DISC_CTRL5, + BIT_DISC_CTRL5_DSM_OVRIDE, 0); +} +#endif + +void si_mhl_tx_drv_shut_down_HDCP2(struct drv_hw_context *hw_context) +{ + int ddcm_status; + int count = 0; + /* Disable HDCP 2.2 */ + enable_intr(hw_context, INTR_HDCP2, 0x00); + + /* Disable HDCP2 DDC polling */ + hw_context->hdcp2_started = false; + mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); + + while (0 <= + (ddcm_status = + mhl_tx_read_reg(hw_context, REG_HDCP2X_DDCM_STS))) { + if (0 == + (MSK_HDCP2X_DDCM_STS_HDCP2X_DDCM_CTL_CS_3_0 & + ddcm_status)) { + break; + } + if (++count > 256) + break; + + MHL_TX_DBG_WARN("shutting down HDCP\n"); + } + + /* disable encryption */ + mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x82); + + MHL_TX_DBG_WARN("HDCP2 Off; Last HDCP2X_DDCM Status %02X;\n", + ddcm_status); + + mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_HDCP_EN, 0x00); + + /* clear any leftover hdcp2 interrupts */ + mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP2].stat_addr, 0xff); +} + +static bool issue_edid_read_request(struct drv_hw_context *hw_context, + uint8_t block_number) +{ + if (ok_to_proceed_with_ddc(hw_context)) { + int ddc_status; + ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); + if (BIT_DDC_STATUS_DDC_BUS_LOW & ddc_status) { + int lm_ddc; + lm_ddc = mhl_tx_read_reg(hw_context, REG_LM_DDC); + /* disable TPI mode */ + mhl_tx_write_reg(hw_context, REG_LM_DDC, + lm_ddc | + VAL_LM_DDC_SW_TPI_EN_DISABLED); + /* clear out the ddc bus low bit */ + mhl_tx_write_reg(hw_context, REG_DDC_STATUS, + ddc_status & + ~BIT_DDC_STATUS_DDC_BUS_LOW); + + /* restore TPI mode state */ + mhl_tx_write_reg(hw_context, REG_LM_DDC, lm_ddc); + } + MHL_TX_DBG_INFO("\n\tRequesting EDID block:%d\n" + "\tcurrentEdidRequestBlock:%d\n" + "\tedidFifoBlockNumber:%d" + "ddc_status:0x%02x\n", + block_number, + hw_context->current_edid_req_blk, + hw_context->edid_fifo_block_number, ddc_status); + /* Setup auto increment and kick off read */ + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | + VAL_EDID_CTRL_DEVCAP_SELECT_EDID | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE + | ((block_number & 0x01) << 2) + | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + +#ifndef MANUAL_EDID_FETCH +#define SWWA_BZ30759 +#endif +#ifdef SWWA_BZ30759 + freeze_MHL_connect(hw_context); +#endif + /* Setup which block to read */ + if (0 == block_number) { + /* Enable EDID interrupt */ + enable_intr(hw_context, INTR_EDID, + (BIT_INTR9_DEVCAP_DONE_MASK + | BIT_INTR9_EDID_DONE_MASK + | BIT_INTR9_EDID_ERROR)); + mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, + BIT_TPI_CBUS_START_GET_EDID_START_0); + } else { + uint8_t param = (1 << (block_number - 1)); + MHL_TX_DBG_INFO("EDID HW Assist: Programming " + "Reg %02X:%02X to %02X\n", + REG_EDID_START_EXT, param); + mhl_tx_write_reg(hw_context, REG_EDID_START_EXT, + param); + } + + return true; + } else { + MHL_TX_DBG_INFO("\n\tNo HPD for EDID block request:%d\n" + "\tcurrentEdidRequestBlock:%d\n" + "\tedidFifoBlockNumber:%d\n", + block_number, + hw_context->current_edid_req_blk, + hw_context->edid_fifo_block_number); + return false; + } +} + +/* + * si_mhl_tx_drv_send_block + */ +void mhl_tx_drv_send_block(struct drv_hw_context *hw_context, + struct block_req *req) +{ + uint8_t count; + + count = req->sub_payload_size; + MHL_TX_DBG_INFO(" req->sub_payload_size: %d req->count: %d\n", count, + req->count); + + MHL_TX_DBG_WARN("total bytes to ack: %d\n", + hw_context->block_protocol.received_byte_count); + if (hw_context->block_protocol.received_byte_count >= 256) { + + /* + * Can't represent numbers >= 256 in 8 bits, + * so ack as much as possible + */ + req->payload->hdr_and_burst_id.tport_hdr.rx_unload_ack = 255; + hw_context->block_protocol.received_byte_count -= 255; + } else { + req->payload->hdr_and_burst_id.tport_hdr.rx_unload_ack = + (uint8_t) hw_context->block_protocol.received_byte_count; + hw_context->block_protocol.received_byte_count = 0; + } + + MHL_TX_DBG_WARN("rx_unload_ack: %d\n", + req->payload->hdr_and_burst_id.tport_hdr.rx_unload_ack); +/* dump_array(0, "eMSC BLOCK message", &req->payload->as_bytes[0], + req->count); */ + if (use_spi) { + mhl_tx_write_block_spi_emsc(hw_context, req); + } else { + mhl_tx_write_reg_block(hw_context, REG_EMSC_XMIT_WRITE_PORT, + req->count, &req->payload->as_bytes[0]); + } +} + +/* + pending hawb write burst status +*/ +uint8_t si_mhl_tx_drv_get_pending_hawb_write_status(struct mhl_dev_context + *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + return hw_context->hawb_write_pending; +} + +/* +si_mhl_tx_drv_hawb_xfifo_avail +*/ + +uint8_t si_mhl_tx_drv_hawb_xfifo_avail(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + + return MSK_MDT_XFIFO_STAT_MDT_XFIFO_LEVEL_AVAIL + & mhl_tx_read_reg(hw_context, REG_MDT_XFIFO_STAT); +} + +#ifdef MANUAL_EDID_FETCH +static uint8_t fetch_edid_block(struct drv_hw_context *hw_context, + uint8_t *buffer, uint8_t block_num, + bool trigger_on_last) +{ + int lm_ddc, ddc_cmd, ddc_status, ddc_address, ddc_limit, step, dout_cnt, + intr3_status, cbus_status; + cbus_status = ok_to_proceed_with_ddc(hw_context); + lm_ddc = mhl_tx_read_reg(hw_context, REG_LM_DDC); + ddc_cmd = mhl_tx_read_reg(hw_context, REG_DDC_CMD); + ddc_cmd &= ~MSK_DDC_CMD_DDC_CMD; + /* Disable EDID interrupt */ + enable_intr(hw_context, INTR_EDID, 0); + + hw_context->hdcp2_started = false; + /* disable auto edid function */ + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE); + /* Disable HDCP2 DDC polling */ + mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); + + /* disable encryption */ + mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x02); + + /* disable TPI mode */ + mhl_tx_write_reg(hw_context, REG_LM_DDC, + lm_ddc | VAL_LM_DDC_SW_TPI_EN_DISABLED); + + for (step = 0; step < 256; ++step) { + ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); + if (0 == (BIT_DDC_STATUS_DDC_I2C_IN_PROG & ddc_status)) + break; + + mhl_tx_write_reg(hw_context, REG_DDC_STATUS, + BIT_DDC_STATUS_DDC_FIFO_EMPTY); + } + /* set DDC slave address to EDID */ + + mhl_tx_write_reg(hw_context, REG_DDC_ADDR, 0xA0); + step = 16; + ddc_limit = (block_num << 7) + EDID_BLOCK_SIZE; + for (ddc_address = block_num << 7; ddc_address < ddc_limit; + ddc_address += step) { + ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); + mhl_tx_write_reg(hw_context, REG_DDC_CMD, + ddc_cmd | VAL_DDC_CMD_DDC_CMD_ABORT); + mhl_tx_write_reg(hw_context, REG_DDC_CMD, + ddc_cmd | + VAL_DDC_CMD_DDC_CMD_CLEAR_FIFO); + mhl_tx_write_reg(hw_context, REG_DDC_STATUS, + BIT_DDC_STATUS_DDC_FIFO_EMPTY); + + /* synchronize by making sure that any stale interrupt + * is cleared + */ + intr3_status = mhl_tx_read_reg(hw_context, REG_INTR3); + mhl_tx_write_reg(hw_context, REG_INTR3, intr3_status); + + mhl_tx_write_reg(hw_context, REG_DDC_SEGM, + HIGH_BYTE_16(ddc_address)); + mhl_tx_write_reg(hw_context, REG_DDC_OFFSET, + LOW_BYTE_16(ddc_address)); + mhl_tx_write_reg(hw_context, REG_DDC_DIN_CNT1, + LOW_BYTE_16(step)); + mhl_tx_write_reg(hw_context, REG_DDC_DIN_CNT2, + HIGH_BYTE_16(step)); + + mhl_tx_write_reg(hw_context, REG_DDC_CMD, + ddc_cmd | + VAL_DDC_CMD_ENH_DDC_READ_NO_ACK); + + do { + intr3_status = + mhl_tx_read_reg(hw_context, REG_INTR3); + cbus_status = ok_to_proceed_with_ddc(hw_context); + if (BIT_DDC_CMD_DONE & intr3_status) + break; + } while (cbus_status); + if (!cbus_status) + break; + + ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); + dout_cnt = mhl_tx_read_reg(hw_context, REG_DDC_DOUT_CNT); + + if (use_spi) { + int k; + for (k = 0; k < step; ++k) { + buffer[(ddc_address + k) % EDID_BLOCK_SIZE] = + (uint8_t) mhl_tx_read_reg(hw_context, + REG_DDC_DATA); + } + } else { + mhl_tx_read_reg_block(hw_context, REG_DDC_DATA, + step, + &buffer[ddc_address % + EDID_BLOCK_SIZE]); + } + + if (0 == ddc_address) { + struct EDID_block0_t *p_EDID_block_0 = + (struct EDID_block0_t *) buffer; + if (!si_mhl_tx_check_edid_header + (hw_context->intr_info->edid_parser_context, + p_EDID_block_0)) { +#if defined(DEBUG) + int start = ddc_address % EDID_BLOCK_SIZE; +#endif + /* back-up by one step to retry */ + MHL_TX_DBG_ERR("%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + buffer[start + 0], buffer[start + 1], + buffer[start + 2], buffer[start + 3], + buffer[start + 4], buffer[start + 5], + buffer[start + 6], buffer[start + 7] + ); + ddc_address -= step; + } + } + + if ((ddc_address + step) >= ddc_limit) { + /* make sure that done is triggered for sinks with + * only 1 EDID block (DVI) + */ + if (0 == block_num) { + struct EDID_block0_t *p_EDID_block_0 = + (struct EDID_block0_t *) buffer; + if (0 == p_EDID_block_0->extension_flag) + trigger_on_last = true; + } + + if (trigger_on_last) { + enable_intr(hw_context, INTR_DDC, + BIT_DDC_CMD_DONE); + /* let int_3_isr or si_mhl_tx_drv_device_isr + * clear the interrupt + */ + } else { + mhl_tx_write_reg(hw_context, REG_INTR3, + intr3_status); + } + } else { + mhl_tx_write_reg(hw_context, REG_INTR3, + intr3_status); + } + } + + /* restore TPI mode state */ + mhl_tx_write_reg(hw_context, REG_LM_DDC, lm_ddc); + return cbus_status; +} +#endif +/* + * si_mhl_tx_drv_send_cbus_command + * + * Write the specified Sideband Channel command to the CBUS. + * such as READ_DEVCAP, SET_INT, WRITE_STAT, etc. + * Command can be a MSC_MSG command (RCP/RAP/RCPK/RCPE/RAPK), or another + * Parameters: + * req - Pointer to a cbus_req_t structure containing the command to write + * + * Returns: + * for WRITE_BURST, if an MDT XFIFO level is available, + * it returns non-zero, otherwise zero. + * for MHL_READ_EDID_BLOCK it returns either the command type, + * or 0 if downstream HPD is low. + * for all other commands, the return value is the command type. + * + */ +uint8_t si_mhl_tx_drv_send_cbus_command(struct drv_hw_context *hw_context, + struct cbus_req *req) +{ + uint8_t ret_val = req->command; + uint8_t block_write_buffer[3]; + int success; + uint8_t cbus_status; + + switch (req->command) { + case MHL_WRITE_BURST: + break; + default: + /* Disable h/w automation of WRITE_BURST until + * this command completes + */ + disable_gen2_write_burst_rcv(hw_context); + /* TODO: Review with John */ + disable_gen2_write_burst_xmit(hw_context); + } + + hw_context->current_cbus_req = *req; + switch (req->command) { + case MHL_SEND_3D_REQ_OR_FEAT_REQ: + /* DO NOT RE-ORDER THIS CASE */ + /* + * Do a complete reset of HAWB + */ + /*mhl_tx_write_reg(hw_context, REG_MDT_RCV_CONTROL, + BIT_MDT_RCV_CONTROL_MDT_DISABLE); */ + /* TODO: Review with John */ + /* Insert disable of both rcv and xmit engine */ + /* Enable h/w automation of WRITE_BURST receive */ + hw_context->delayed_hawb_enable_reg_val = + BIT_MDT_RCV_CONTROL_MDT_DELAY_RCV_EN; + enable_gen2_write_burst_rcv(hw_context); + hw_context->cbus1_state = CBUS1_MSC_PEND_DLY_RCV_EN; + + mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, + req->reg); + mhl_tx_write_reg(hw_context, REG_MSC_1ST_TRANSMIT_DATA, + req->reg_data); + mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_MSC_WRITE_STAT_CMD); + break; + + case MHL_SET_INT: + case MHL_WRITE_STAT: + case MHL_WRITE_XSTAT: + MHL_TX_DBG_INFO("->reg: 0x%02x data: 0x%02x\n", + req->reg, req->reg_data); +#ifdef WRITE_STAT_SET_INT_COALESCE + mhl_tx_write_reg_block(hw_context, REG_MSC_CMD_OR_OFFSET, + 2, &req->reg); +#else + mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, + req->reg); + mhl_tx_write_reg(hw_context, REG_MSC_1ST_TRANSMIT_DATA, + req->reg_data); +#endif + mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_MSC_WRITE_STAT_CMD); + if (MHL_RCHANGE_INT == hw_context->current_cbus_req.reg) { + + if (MHL2_INT_3D_REQ & hw_context->current_cbus_req. + reg_data) { + MHL_TX_DBG_WARN("3D_REQ sent\n"); + } + + if (MHL3_INT_FEAT_REQ & hw_context->current_cbus_req. + reg_data) { + MHL_TX_DBG_WARN("FEAT_REQ sent\n"); + } + + if (MHL_INT_GRT_WRT & hw_context->current_cbus_req. + reg_data) { + MHL_TX_DBG_WARN("GRT_WRT sent\n"); + } + } + break; + + case MHL_READ_DEVCAP: + MHL_TX_DBG_WARN("Read DEVCAP\n"); + /* Enable DEVCAP_DONE interrupt */ + enable_intr(hw_context, INTR_EDID, BIT_INTR9_DEVCAP_DONE); + + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | + VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE + | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + + /* read the entire DEVCAP array in one command */ + mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, + BIT_TPI_CBUS_START_GET_DEVCAP_START); + break; + case MHL_READ_DEVCAP_REG: + MHL_TX_DBG_INFO("Trigger DEVCAP_REG Read\n"); + MHL_TX_DBG_INFO("Read DEVCAP (0x%02x)\n", req->reg); + mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, + req->reg); + mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_MSC_READ_DEVCAP_CMD); + break; + + case MHL_READ_XDEVCAP: + MHL_TX_DBG_INFO("Trigger XDEVCAP Read\n"); + /* Enable DEVCAP_DONE interrupt */ + enable_intr(hw_context, INTR_EDID, BIT_INTR9_DEVCAP_DONE); + + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | + BIT_EDID_CTRL_XDEVCAP_EN | + VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE + | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + /* read the entire DEVCAP array in one command */ + mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, + BIT_TPI_CBUS_START_GET_DEVCAP_START); + break; + case MHL_READ_XDEVCAP_REG: + MHL_TX_DBG_INFO("Read XDEVCAP_REG (0x%02x)\n", req->reg); + mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, + req->reg); + mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_MSC_READ_DEVCAP_CMD); + break; + + case MHL_READ_EDID_BLOCK: + hw_context->current_edid_req_blk = 0; +#ifdef MANUAL_EDID_FETCH +#define HPD_AND_CONNECTED (BIT_CBUS_STATUS_CBUS_HPD | \ + BIT_CBUS_STATUS_CBUS_CONNECTED) + + success = 1; + cbus_status = + fetch_edid_block(hw_context, hw_context->edid_block, + hw_context->current_edid_req_blk, false); + if (cbus_status == (BIT_CBUS_STATUS_CBUS_HPD | + BIT_CBUS_STATUS_CBUS_CONNECTED)) { + int num_extensions; + + num_extensions = + si_mhl_tx_get_num_cea_861_extensions + (hw_context->intr_info->edid_parser_context, + hw_context->current_edid_req_blk); + if (num_extensions < 0) { + success = 0; + } else { + for (hw_context->current_edid_req_blk = 1; + hw_context->current_edid_req_blk <= + num_extensions; + hw_context->current_edid_req_blk++) { + cbus_status = + fetch_edid_block(hw_context, + hw_context->edid_block, + hw_context-> + current_edid_req_blk, + (hw_context-> + current_edid_req_blk == + num_extensions) ? + true : false); + if (cbus_status != HPD_AND_CONNECTED) { + success = 0; + break; + } + num_extensions = + si_mhl_tx_get_num_cea_861_extensions + (hw_context->intr_info-> + edid_parser_context, + hw_context->current_edid_req_blk); + if (num_extensions < 0) { + MHL_TX_DBG_ERR( + "edid problem:%d\n", + num_extensions); + success = 0; + break; + } + } + } + } +#else + success = issue_edid_read_request(hw_context, + hw_context->current_edid_req_blk); +#endif + ret_val = success ? ret_val : 0; + break; + + case MHL_GET_STATE: + case MHL_GET_VENDOR_ID: /* for vendor id */ + case MHL_SET_HPD: /* Set Hot Plug Detect */ + case MHL_CLR_HPD: /* Clear Hot Plug Detect */ + case MHL_GET_SC1_ERRORCODE: /* Get channel 1 command error code */ + case MHL_GET_DDC_ERRORCODE: /* Get DDC channel command err code */ + case MHL_GET_MSC_ERRORCODE: /* Get MSC command error code */ + case MHL_GET_SC3_ERRORCODE: /* Get channel 3 command error code */ + MHL_TX_DBG_INFO("Sending MSC command %02x, %02x, %02x\n", + req->command, req->reg, req->reg_data); + mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, + req->command); + mhl_tx_write_reg(hw_context, REG_MSC_1ST_TRANSMIT_DATA, + req->reg_data); + mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_MSC_PEER_CMD); + break; + + case MHL_MSC_MSG: + MHL_TX_DBG_INFO("MHL_MSC_MSG sub cmd: 0x%02x data: 0x%02x\n", + req->msg_data[0], req->msg_data[1]); + block_write_buffer[0] = req->command; + block_write_buffer[1] = req->msg_data[0]; + block_write_buffer[2] = req->msg_data[1]; + + if (MHL_MSC_MSG_BIST_REQUEST_STAT == req->msg_data[0]) { + hw_context->delayed_hawb_enable_reg_val = + BIT_MDT_RCV_CONTROL_MDT_DELAY_RCV_EN; + enable_gen2_write_burst_rcv(hw_context); + hw_context->cbus1_state = CBUS1_MSC_PEND_DLY_RCV_EN; + } + mhl_tx_write_reg_block(hw_context, REG_MSC_CMD_OR_OFFSET, + 3, block_write_buffer); + mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_MSC_MSC_MSG_CMD); + break; + + case MHL_WRITE_BURST: + MHL_TX_DBG_INFO + ("MHL_WRITE_BURST offset: 0x%02x length: 0x%02x\n", + req->burst_offset, req->length); + hw_context->hawb_write_pending = true; + enable_gen2_write_burst_xmit(hw_context); + mhl_tx_write_reg_block(hw_context, + REG_MDT_XMIT_WRITE_PORT, + req->length, req->msg_data); + ret_val = + (MSK_MDT_XFIFO_STAT_MDT_XFIFO_LEVEL_AVAIL & + mhl_tx_read_reg(hw_context, REG_MDT_XFIFO_STAT) + ); + break; + + default: + MHL_TX_DBG_ERR("Unsupported command 0x%02x detected!\n", + req->command); + ret_val = 0; + break; + } + + return ret_val; +} + +void si_mhl_tx_drv_set_3d_mode(struct drv_hw_context *hw_context, bool do_3D, + enum _3D_structure_e three_d_mode) +{ + if (do_3D) { + if (tdsFramePacking == three_d_mode) { + MHL_TX_DBG_INFO("using frame packing\n"); + + mhl_tx_write_reg(hw_context, REG_VID_OVRRD, + BIT_VID_OVRRD_PP_AUTO_DISABLE | + VAL_VID_OVRRD_3DCONV_EN_FRAME_PACK); + } else { + MHL_TX_DBG_INFO("NOT using frame packing\n"); + mhl_tx_write_reg(hw_context, REG_VID_OVRRD, + BIT_VID_OVRRD_PP_AUTO_DISABLE); + } + } else { + MHL_TX_DBG_INFO("NOT using frame packing\n"); + mhl_tx_write_reg(hw_context, REG_VID_OVRRD, + BIT_VID_OVRRD_PP_AUTO_DISABLE); + } +} + +struct SI_PACK_THIS_STRUCT si_incoming_hw_timing_t { + uint8_t h_total_low; + uint8_t h_total_high; + uint8_t v_total_low; + uint8_t v_total_high; + uint8_t iadjust; + uint8_t pol_detect; + uint8_t columns_low; + uint8_t columns_high; + uint8_t rows_low; + uint8_t rows_high; + uint8_t field_rate_low; + uint8_t field_rate_high; +}; + +uint16_t si_mhl_tx_drv_get_incoming_timing(struct drv_hw_context *hw_context, + struct si_incoming_timing_t *p_timing) +{ + uint16_t ret_val; + struct si_incoming_hw_timing_t hw_timing; + + ret_val = + mhl_tx_read_reg_block(hw_context, REG_H_RESL, + sizeof(hw_timing), (uint8_t *) &hw_timing); + p_timing->h_total = (((uint16_t) hw_timing.h_total_high) << 8) + | (uint16_t) hw_timing.h_total_low; + p_timing->v_total = (((uint16_t) hw_timing.v_total_high) << 8) + | (uint16_t) hw_timing.v_total_low; + p_timing->columns = (((uint16_t) hw_timing.columns_high) << 8) + | (uint16_t) hw_timing.columns_low; + p_timing->rows = (((uint16_t) hw_timing.rows_high) << 8) + | (uint16_t) hw_timing.rows_low; + p_timing->field_rate = (((uint16_t) hw_timing.field_rate_high) << 8) + | (uint16_t) hw_timing.field_rate_low; + return ret_val; +} + +int si_mhl_tx_drv_get_aksv(struct drv_hw_context *hw_context, uint8_t * buffer) +{ + memcpy(buffer, hw_context->aksv, 5); + return 0; +} + +void si_mhl_tx_drv_skip_to_next_edid_block(struct drv_hw_context *hw_context) +{ + hw_context->edid_fifo_block_number++; +} + +int si_mhl_tx_drv_get_edid_fifo_partial_block(struct drv_hw_context *hw_context, + uint8_t start, uint8_t length, uint8_t *edid_buf) +{ + int ret_val; + uint8_t offset; + + offset = EDID_BLOCK_SIZE * (hw_context->edid_fifo_block_number & 0x01); + offset += start; + + MHL_TX_DBG_INFO("%x %x\n", (unsigned int)hw_context, + (unsigned int)edid_buf); + if (EDID_BLOCK_SIZE == (offset + length)) + hw_context->edid_fifo_block_number++; + +#ifdef MANUAL_EDID_FETCH + memcpy(edid_buf, &hw_context->edid_block[start], length); +#else + mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, offset); + + ret_val = mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, + length, edid_buf); +#endif + + DUMP_EDID_BLOCK(0, edid_buf, length); + + ret_val = ok_to_proceed_with_ddc(hw_context); + if (!ret_val) { + MHL_TX_DBG_INFO("No HPD ret_val:0x%02x\n", ret_val); + return ne_NO_HPD; + } else { + MHL_TX_DBG_INFO("EDID block read complete. ret_val:0x%02x\n", + ret_val); + return ne_SUCCESS; + } +} + +int si_mhl_tx_drv_get_edid_fifo_next_block(struct drv_hw_context *hw_context, + uint8_t *edid_buf) +{ + int ret_val; + uint8_t offset; + + offset = EDID_BLOCK_SIZE * (hw_context->edid_fifo_block_number & 0x01); + + MHL_TX_DBG_INFO("%x %x\n", (unsigned int)hw_context, + (unsigned int)edid_buf); + hw_context->edid_fifo_block_number++; + +#ifdef MANUAL_EDID_FETCH + memcpy(edid_buf, hw_context->edid_block, EDID_BLOCK_SIZE); +#else + mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, offset); + + ret_val = mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, + EDID_BLOCK_SIZE, edid_buf); +#endif + + DUMP_EDID_BLOCK(0, edid_buf, EDID_BLOCK_SIZE); + + ret_val = ok_to_proceed_with_ddc(hw_context); + if (!ret_val) { + MHL_TX_DBG_INFO("No HPD ret_val:0x%02x\n", ret_val); + return ne_NO_HPD; + } else { + MHL_TX_DBG_ERR("EDID block read complete. ret_val:0x%02x\n", + ret_val); + return ne_SUCCESS; + } +} + +int si_mhl_tx_drv_get_scratch_pad(struct drv_hw_context *hw_context, + uint8_t start_reg, uint8_t *data, + uint8_t length) +{ + if ((start_reg + length) > (int)MHL_SCRATCHPAD_SIZE) + return -1; + + memcpy(data, &hw_context->write_burst_data[start_reg], length); + + return 0; +} + +static bool packed_pixel_available(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context; + hw_context = (struct drv_hw_context *)&dev_context->drv_context; + if (hw_context->cbus_mode >= CM_eCBUS_S) { + if ((MHL_DEV_VID_LINK_SUPP_16BPP & DEVCAP_VAL_VID_LINK_MODE) && + (dev_context->dev_cap_cache.mdc.vid_link_mode & + MHL_DEV_VID_LINK_SUPP_16BPP)) { + + return true; + } + } else { + if ((MHL_DEV_VID_LINK_SUPP_PPIXEL & DEVCAP_VAL_VID_LINK_MODE) && + (dev_context->dev_cap_cache.mdc.vid_link_mode & + MHL_DEV_VID_LINK_SUPP_PPIXEL)) { + + return true; + } + } + return false; +} + +#define SIZE_AVI_INFOFRAME 14 +static uint8_t calculate_avi_info_frame_checksum( + union hw_avi_payload_t *payload) +{ + uint8_t checksum; + + checksum = 0x82 + 0x02 + 0x0D; /* these are set by the hardware */ + return calculate_generic_checksum(payload->ifData, checksum, + SIZE_AVI_INFOFRAME); +} + +static int is_valid_avi_info_frame(struct mhl_dev_context *dev_context, + struct avi_info_frame_t *avif) +{ + uint8_t checksum; + + checksum = + calculate_generic_checksum((uint8_t *) avif, 0, sizeof(*avif)); + if (0 != checksum) { + MHL_TX_DBG_ERR("AVI info frame checksum is: 0x%02x " + "should be 0\n", checksum); + return 0; + + } else if (0x82 != avif->header.type_code) { + MHL_TX_DBG_ERR("Invalid AVI type code: 0x%02x\n", + avif->header.type_code); + return 0; + + } else if (0x02 != avif->header.version_number) { + MHL_TX_DBG_ERR("Invalid AVI version: 0x%02x\n", + avif->header.version_number); + return 0; + + } else if (0x0D != avif->header.length) { + return 0; + + } else { + return 1; + } +} + +#define IEEE_OUI(x) ((uint32_t)x[0] | \ + (((uint32_t)x[1]) << 8) | \ + (((uint32_t)x[2]) << 16)) + +static int is_valid_vsif(struct mhl_dev_context *dev_context, + union vsif_mhl3_or_hdmi_u *vsif) +{ + uint8_t checksum; + + checksum = calculate_generic_checksum((uint8_t *) vsif, 0, + sizeof(vsif->common.header) + + vsif->common.header.length); + if (0 != checksum) { + MHL_TX_DBG_WARN("VSIF info frame checksum is: 0x%02x " + "should be 0\n", checksum); + /* + Try again, assuming that the header includes the checksum. + */ + checksum = calculate_generic_checksum((uint8_t *) vsif, 0, + sizeof(vsif->common.header) + + vsif->common.header.length + + sizeof(vsif->common.checksum)); + if (0 != checksum) { + MHL_TX_DBG_ERR("VSIF info frame checksum (adjusted " + "for checksum itself) is: 0x%02x " + "should be 0\n", checksum); + return 0; + + } + } + if (0x81 != vsif->common.header.type_code) { + MHL_TX_DBG_ERR("Invalid VSIF type code: 0x%02x\n", + vsif->common.header.type_code); + return 0; + + } else { + uint32_t ieee_oui = IEEE_OUI(vsif->common.ieee_oui); + switch (ieee_oui) { + case IEEE_OUI_HDMI: + if (0x01 == vsif->common.header.version_number) + return 1; + MHL_TX_DBG_ERR("Invalid VSIF version: 0x%02x\n", + vsif->common.header.version_number); + break; + case IEEE_OUI_MHL: + if (0x03 == vsif->common.header.version_number) + return 1; + MHL_TX_DBG_ERR("Invalid VSIF version: 0x%02x\n", + vsif->common.header.version_number); + break; + default: + MHL_TX_DBG_ERR("Invalid IEEE OUI: 0x%06x\n", ieee_oui); + } + + } + return 0; +} + +static void print_vic_modes_impl(struct drv_hw_context *hw_context, + uint8_t vic, const char *function, int iLine) +{ + int i; + struct vic_name { + uint8_t vic; + char name[15]; + } vic_name_table[] = { + { 2, "480p"}, + { 4, "720p60"}, + { 5, "1080i60"}, + { 6, "480i"}, + { 16, "1080p60"}, + { 17, "576p50"}, + { 19, "720p50"}, + { 20, "1080i50"}, + { 21, "576i50"}, + { 31, "1080p50"}, + { 32, "1080p24"}, + { 33, "1080p25"}, + { 34, "1080p30"}, + { 60, "720p24"}, + { 61, "720p25"}, + { 62, "720p30"}, + { 63, "1080p120"}, + { 64, "1080p100"}, + { 86, "2560x1080p24w"}, + { 87, "2560x1080p25w"}, + { 89, "2560x1080p50w"}, + { 93, "2160p24"}, + { 94, "2160p25"}, + { 95, "2160p30"}, + { 98, "4096x2160p24"}, + { 99, "4096x2160p25"}, + {100, "4096x2160p30"}, + /* to handle the case where VIC is not found in the table */ + {0, ""} + }; + +#define NUM_VIC_NAMES (sizeof(vic_name_table)/sizeof(vic_name_table[0])) + /* stop before the terminator */ + for (i = 0; i < (NUM_VIC_NAMES - 1); i++) { + if (vic == vic_name_table[i].vic) + break; + } + if (vic) { + MHL_TX_PROXY_DBG_PRINT(0, function, iLine, + "VIC = %s%d%s (%s)\n", ANSI_ESC_GREEN_TEXT, vic, + ANSI_ESC_RESET_TEXT, vic_name_table[i].name); + } else { + MHL_TX_PROXY_DBG_PRINT(0, function, iLine, + "VIC:%s%d%s\n", ANSI_ESC_YELLOW_TEXT, vic, + ANSI_ESC_RESET_TEXT); + } +} +#define print_vic_modes(ctx, vic) \ + print_vic_modes_impl(ctx, vic, __func__, __LINE__) + +#define DUMP_INFO_FRAMES +#ifdef DUMP_INFO_FRAMES +static void dump_avif_vsif_impl(struct drv_hw_context *hw_context, + const char *function, int line_num) +{ + if (debug_level >= DBG_MSG_LEVEL_WARN) { + struct hdmi_vsif_t vsif; + struct avi_info_frame_t avif; + int i; + unsigned char *c; + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, + hw_context->rx_hdmi_ctrl2_defval | + VAL_RX_HDMI_CTRL2_VSI_MON_SEL_VSI); + + mhl_tx_read_reg_block(hw_context, + REG_RX_HDMI_MON_PKT_HEADER1, sizeof(vsif), + (uint8_t *) &vsif); + + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, + hw_context->rx_hdmi_ctrl2_defval | + VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); + + mhl_tx_read_reg_block(hw_context, + REG_RX_HDMI_MON_PKT_HEADER1, sizeof(avif), + (uint8_t *) &avif); + + print_formatted_debug_msg(NULL, function, line_num, "VSIF:"); + for (i = 0, c = (unsigned char *)&vsif; i < sizeof(vsif); + ++i, ++c) { + printk(KERN_CONT " %02x", *c); + } + printk("\n"); + print_formatted_debug_msg(NULL, function, line_num, "AVIF:"); + for (i = 0, c = (unsigned char *)&avif; i < sizeof(avif); + ++i, ++c) { + printk(KERN_CONT " %02x", *c); + } + printk(KERN_CONT "\n"); + } +} + +#define DUMP_AVIF_VSIF(hw_context) \ + dump_avif_vsif_impl(hw_context, __func__, __LINE__); +#else +#define DUMP_AVIF_VSIF(hw_context) /* nothing */ +#endif + +enum timing_info_basis_e { + use_avi_vic, + use_hdmi_vic, + use_mhl3_sequence_index, + use_hardware_totals +}; + +static bool process_hdmi_vsif(struct drv_hw_context *hw_context, + struct avi_info_frame_data_byte_4_t *p_input_video_code, + enum timing_info_basis_e *p_timing_info_basis, + enum mhl_vid_fmt_e *p_vid_fmt, uint32_t *p_threeDPixelClockRatio, + uint8_t *p_fp_3d_mode, enum mhl_3d_fmt_type_e *p_3d_fmt_type) +{ + uint8_t input_VIC = (uint8_t) (*p_input_video_code).VIC; + /* + * HDMI spec. v1.4: + * "When transmitting any 2D video format of section 6.3 above, an + * HDMI Source shall set the VIC field to the Video Code for that + * format. See CEA-861-D section 6.4 for detatils. The additional VIC + * values from 60 to 64 are defined in Table 8-4. When transmitting any + * 3D video format using the 3D_Structure field in the HDMI Vendor + * Specific InfoFrame, an HDMI Source shall set the AVI InfoFrame VIC + * field to satisfy the relation described in section 8.2.3.2. + * When transmitting any extended video format indicated through use of + * the HDMI_VIC field in the HDMI Vendor Specific InfoFrame or any + * other format which is not described in the above cases, and HDMI + * Source shall set the AVI InfoFrame VIC field to zero." + */ + MHL_TX_DBG_WARN("valid HDMI VSIF\n"); + print_vic_modes(hw_context, input_VIC); + if (0 == input_VIC) { + if (hvfExtendedResolutionFormatPresent == + hw_context->current_vsif.hdmi.payLoad.pb4. + HDMI_Video_Format) { +#if defined(DEBUG) + uint8_t vic = + hw_context->current_vsif.hdmi.payLoad.pb5. + HDMI_VIC; +#endif + /* HDMI_VIC should contain one of 0x01 through 0x04 */ + MHL_TX_DBG_ERR("HDMI extended resolution %d\n", vic); + *p_timing_info_basis = use_hdmi_vic; + } else { +#ifdef PC_MODE_VIDEO_TIMING_SUPPORT + MHL_TX_DBG_WARN("AVI VIC is zero!!!\n"); + *p_timing_info_basis = use_hardware_totals; +#else + /* + * Instead of no video, let us attempt HDCP and if + * possible show video. If hdcp fails due to clock + * difference on input (which we don't know about + * clearly), after 5 attempts it will anyways stabilize + * and use an infoframe interrupt if that arrives with + * a good vic. + * + * TODO: Flag arrival of an infoframe from the time + * this path was executed and abort HDCP a bit earlier. + */ + MHL_TX_DBG_ERR("AVI VIC is zero!!!\n"); + return false; +#endif + } + } + /* + * From VSIF bytes, figure out if we need to perform + * frame packing or not. This helps decide if packed pixel + * (16-bit) is required or not in conjunction with the VIC. + */ + + if (hvf3DFormatIndicationPresent == + hw_context->current_vsif.hdmi.payLoad.pb4.HDMI_Video_Format) { + + *p_vid_fmt = mhl_vid_fmt_3d_fmt_present; + + MHL_TX_DBG_INFO("VSIF indicates 3D\n"); + switch (hw_context->current_vsif.hdmi.payLoad.pb5. + ThreeDStructure.threeDStructure) { + case tdsFramePacking: + MHL_TX_DBG_INFO("mhl_tx: tdsFramePacking\n"); + *p_threeDPixelClockRatio = 2; + *p_fp_3d_mode |= + VAL_VID_OVRRD_3DCONV_EN_FRAME_PACK; + *p_3d_fmt_type = MHL_3D_FMT_TYPE_FS; + break; + case tdsTopAndBottom: + *p_3d_fmt_type = MHL_3D_FMT_TYPE_TB; + break; + case tdsSideBySide: + *p_3d_fmt_type = MHL_3D_FMT_TYPE_LR; + break; + default: + break; + } + } + + return true; +} + +/* + Find pixel clock from closest match to input timing parameters +*/ + +static uint32_t find_pixel_clock_from_closest_match_timing( + struct mhl_dev_context *dev_context, + struct si_incoming_timing_t *p_timing) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + uint32_t pixels_per_second = 0; + uint32_t pixel_clock_frequency = 0; + uint32_t pixels_per_line, lines_per_field, fields_per_second; + uint32_t lines_per_second; + int ret_val; + + pixel_clock_frequency = 0; + + /* Measure the HTOTAL and VTOTAL and look them up in a table */ + /* also consider display enable periods and field rates */ + ret_val = si_mhl_tx_drv_get_incoming_timing(hw_context, p_timing); + + pixels_per_line = (uint32_t) p_timing->h_total; + lines_per_field = (uint32_t) p_timing->v_total; + fields_per_second = (uint32_t) p_timing->field_rate; + + lines_per_second = lines_per_field * fields_per_second; + pixels_per_second = pixels_per_line * lines_per_second; + + /* did we get a valid pixel clock? */ + if (pixels_per_second) { + p_timing->calculated_pixel_clock = 0; + pixel_clock_frequency = + si_mhl_tx_find_timings_from_totals( + dev_context->edid_parser_context, p_timing); + + MHL_TX_DBG_WARN + ("{%d,%d,%dx%d-%dHz, %s%d.%d%s MHz, %d.%d MHz}\n", + p_timing->h_total, p_timing->v_total, p_timing->columns, + p_timing->rows, p_timing->field_rate, ANSI_ESC_YELLOW_TEXT, + p_timing->calculated_pixel_clock / 1000000, + p_timing->calculated_pixel_clock % 1000000, + ANSI_ESC_RESET_TEXT, pixels_per_second / 1000000, + pixels_per_second % 1000000); + + } + + if (0 == pixel_clock_frequency) { + uint8_t mask; + uint8_t idx; + uint8_t limit; + int32_t total; + int32_t average; + int32_t epsilon; + limit = ARRAY_SIZE(hw_context->pixel_clock_history); + mask = limit - 1; + idx = hw_context->idx_pixel_clock_history++ & mask; + hw_context->pixel_clock_history[idx] = pixels_per_second; + + /* emphasize modes that are not in the table */ + MHL_TX_DBG_ERR("%s{%4d,%4d,%5d,%4d,%3d,%9d,??," + "{0,0},\"%dx%d-%2d\"}%s\n", + (0 != pixel_clock_frequency) ? + ANSI_ESC_GREEN_TEXT : ANSI_ESC_YELLOW_TEXT, + p_timing->h_total, p_timing->v_total, + p_timing->columns, p_timing->rows, p_timing->field_rate, + pixels_per_second, p_timing->columns, p_timing->rows, + p_timing->field_rate, + ANSI_ESC_RESET_TEXT); + for (idx = 0, total = 0; idx < limit; ++idx) { + MHL_TX_DBG_INFO("total: %d\n", total) + total += hw_context->pixel_clock_history[idx]; + } + average = total / limit; + epsilon = pixels_per_second - average; + /* Ignore field rates less than 24 FPS */ + if (p_timing->field_rate >= 24) { + if (0 == epsilon) { + MHL_TX_DBG_ERR("stable pixel clock: %d\n", + pixels_per_second) + pixel_clock_frequency = pixels_per_second; + } + } + } else { + MHL_TX_DBG_ERR("%s%dx%d-%dHz@%d.%dMHz%s\n", ANSI_ESC_GREEN_TEXT, + p_timing->columns, p_timing->rows, + p_timing->field_rate, + pixel_clock_frequency / 1000000, + pixel_clock_frequency % 1000000, + ANSI_ESC_RESET_TEXT); + } + return pixel_clock_frequency; +} + +#define ENABLE_FP VAL_VID_OVRRD_3DCONV_EN_FRAME_PACK + +static void process_mhl3_vsif(struct drv_hw_context *hw_context, + enum mhl_3d_fmt_type_e *p_3d_fmt_type, + enum timing_info_basis_e *p_timing_info_basis, + uint32_t *p_threeDPixelClockRatio, + uint8_t *p_fp_3d_mode) +{ + switch (PB4_MASK_MHL_VID_FMT & + hw_context->vsif_mhl3_or_hdmi_from_callback.mhl3.pb4) { + case mhl_vid_fmt_no_additional: + break; + case mhl_vid_fmt_3d_fmt_present: + *p_3d_fmt_type = PB4_MASK_MHL_3D_FMT_TYPE & + hw_context->vsif_mhl3_or_hdmi_from_callback.mhl3.pb4; + switch (*p_3d_fmt_type) { + case MHL_3D_FMT_TYPE_TB: + case MHL_3D_FMT_TYPE_LR: + case MHL_3D_FMT_TYPE_TBLR: + break; + case MHL_3D_FMT_TYPE_FS: + case MHL_3D_FMT_TYPE_FS_TB: + case MHL_3D_FMT_TYPE_FS_LR: + *p_threeDPixelClockRatio = 2; + *p_fp_3d_mode |= ENABLE_FP; + break; + } + break; + case mhl_vid_fmt_multi_view: + break; + case mhl_vid_fmt_dual_3d: + break; + } + switch (PB6_MASK_MHL_HEV_FMT & + hw_context->vsif_mhl3_or_hdmi_from_callback.mhl3.pb6) { + case mhl_hev_fmt_hev_present: + *p_timing_info_basis = use_mhl3_sequence_index; + break; + case mhl_hev_fmt_no_additional: + case mhl_hev_fmt_reserved_2: + case mhl_hev_fmt_reserved_3: + /* nothing to do in these cases */ + break; + } +} +/* + * This function must not be called for DVI mode. + */ +static int set_hdmi_params(struct mhl_dev_context *dev_context) +{ + uint32_t pixel_clock_frequency = 0; + uint8_t tpi_output = 0; + uint8_t tpi_input = 0; + uint32_t threeDPixelClockRatio; + uint8_t packedPixelNeeded = 0; + uint8_t fp_3d_mode; + enum AviColorSpace_e input_clr_spc = acsRGB; + enum avi_quant_range_e input_quant_range = aqr_default; + enum avi_quant_range_e output_quant_range = aqr_default; + uint8_t output_clr_spc = acsRGB; + struct avi_info_frame_data_byte_4_t input_video_code; + struct avi_info_frame_data_byte_4_t output_video_code; + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + enum timing_info_basis_e timing_info_basis = use_avi_vic; + /* default values for MHL3 VSIF, which we will always send */ + enum mhl_vid_fmt_e vid_fmt = mhl_vid_fmt_no_additional; + enum mhl_3d_fmt_type_e _3d_fmt_type = MHL_3D_FMT_TYPE_FS; + enum mhl_sep_audio_e sep_aud = mhl_sep_audio_not_available; + enum mhl_hev_fmt_e hev_fmt = mhl_hev_fmt_no_additional; + uint16_t hev_fmt_type = 0; + uint32_t delay_sync = 0; + enum mhl_av_delay_dir_e delay_dir = mhl_av_delay_dir_audio_earlier; + + /* Extract VIC from incoming AVIF */ + input_video_code = + hw_context->current_avi_info_frame.payLoad.hwPayLoad. + namedIfData.ifData_u.bitFields.VIC; + threeDPixelClockRatio = 1; + fp_3d_mode = + REG_VID_OVRRD_DEFVAL | BIT_VID_OVRRD_M1080P_OVRRD; + + /* did we get an MHL3 vsif from the callback API? */ + if (hw_context->valid_vsif) { + uint32_t ieee_oui; + + ieee_oui = IEEE_OUI(hw_context->current_vsif.common.ieee_oui); + + switch (hw_context->hpd_high_callback_status) { + case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON: + case HH_FMT_HDMI_VSIF_MHL3: + case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_RPT: + case HH_FMT_HDMI_VSIF_MHL3_NOT_RPT: + MHL_TX_DBG_WARN("MHL3 VSIF from callback\n"); + process_mhl3_vsif(hw_context, + &_3d_fmt_type, + &timing_info_basis, + &threeDPixelClockRatio, + &fp_3d_mode); + break; + case HH_FMT_HDMI_VSIF_HDMI: + case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON: + case HH_FMT_HDMI_VSIF_HDMI_NOT_RPT: + case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON_NOT_RPT: + MHL_TX_DBG_WARN + ("HDMI VSIF from callback\n"); + if (false == + process_hdmi_vsif(hw_context, + &input_video_code, + &timing_info_basis, + &vid_fmt, + &threeDPixelClockRatio, + &fp_3d_mode, + &_3d_fmt_type)) + return false; + break; + default: + MHL_TX_DBG_WARN("VSIF from data islands\n") + switch (ieee_oui) { + case IEEE_OUI_HDMI: + if (false == + process_hdmi_vsif(hw_context, + &input_video_code, + &timing_info_basis, + &vid_fmt, + &threeDPixelClockRatio, + &fp_3d_mode, + &_3d_fmt_type)) { + return false; + } + break; + case IEEE_OUI_MHL: + process_mhl3_vsif(hw_context, + &_3d_fmt_type, + &timing_info_basis, + &threeDPixelClockRatio, + &fp_3d_mode); + break; + } + break; + } + } else { /* no incoming HDMI VSIF */ + if (0 == input_video_code.VIC) { + DUMP_AVIF_VSIF(hw_context) +#ifdef PC_MODE_VIDEO_TIMING_SUPPORT + timing_info_basis = use_hardware_totals; +#else + /* + * This routine will not be called until we know + * (from the downstream EDID)that the sink is HDMI. + * We do not support DVI only sources. The + * upstream source is expected to choose between + * HDMI and DVI based upon the EDID that we present + * upstream. The other information in the infoframe + * is not helpful for determining the pixel clock + * frequency. So we try to infer the pixel clock + * from the HTOTAL and VTOTAL registers. This is + * the case for PC modes on HDMI (valid AVIF, + * no VSIF, VIC==0); + */ + MHL_TX_DBG_ERR + ("no VSIF and AVI VIC (offset 0x%x) is zero!!! " + "trying HTOTAL/VTOTAL\n", + (size_t) &hw_context->current_avi_info_frame. + payLoad.hwPayLoad.namedIfData.ifData_u.bitFields. + VIC - + (size_t) &hw_context->current_avi_info_frame); + /* Return error to avoid further processing. */ + return false; +#endif + } else { + print_vic_modes(hw_context, + (uint8_t) input_video_code.VIC); + } + } + mhl_tx_write_reg(hw_context, REG_VID_OVRRD, fp_3d_mode); +#ifndef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH + /* Do not remember previous VSIF */ + hw_context->valid_vsif = false; +#endif + + /* make a copy of avif */ + hw_context->outgoingAviPayLoad = + hw_context->current_avi_info_frame.payLoad.hwPayLoad; + switch (timing_info_basis) { + case use_avi_vic: + + /* compute pixel frequency */ + pixel_clock_frequency = + si_edid_find_pixel_clock_from_AVI_VIC( + dev_context->edid_parser_context, + input_video_code.VIC); + output_video_code = input_video_code; + break; + case use_hdmi_vic: + output_video_code.VIC = + hw_context->current_vsif.hdmi.payLoad.pb5.HDMI_VIC; + pixel_clock_frequency = + si_edid_find_pixel_clock_from_HDMI_VIC( + dev_context->edid_parser_context, + output_video_code.VIC); + output_video_code.VIC = + si_edid_map_hdmi_vic_to_mhl3_vic( + dev_context->edid_parser_context, + output_video_code.VIC); + output_video_code.futureMustBeZero = 0; + print_vic_modes(hw_context, output_video_code.VIC); + break; + case use_mhl3_sequence_index: + output_video_code.VIC = 0; + output_video_code.futureMustBeZero = 0; + pixel_clock_frequency = + si_edid_find_pixel_clock_from_HEV_DTD( + dev_context->edid_parser_context, + hw_context->vsif_mhl3_or_hdmi_from_callback. + mhl3.mhl_hev_fmt_type); + break; + case use_hardware_totals: + output_video_code = input_video_code; + { + struct si_incoming_timing_t timing; + /* initialize this here */ + memset((void *)&timing, 0, sizeof(timing)); + pixel_clock_frequency = + find_pixel_clock_from_closest_match_timing + (dev_context, &timing); + if (0 == pixel_clock_frequency) { + MHL_TX_DBG_WARN( + "%sVIC==0 and totals not supported%s\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT); + mhl_tx_start_timer(dev_context, + hw_context-> + input_field_rate_measurement_timer, + FIELD_RATE_MEASUREMENT_INTERVAL); + return false; + } else { + MHL_TX_DBG_INFO("MHL3 vic:%d\n", + timing.mhl3_vic); + /* This value will be zero for modes that + * don't have an MHL3 VIC + */ + output_video_code.VIC = timing.mhl3_vic; + print_vic_modes(hw_context, + output_video_code.VIC); + } + } + break; + default: + MHL_TX_DBG_ERR( + "%s'shouldn't get here (invalid switch value)%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + + output_video_code.VIC = 0; + output_video_code.futureMustBeZero = 0; + break; + } + + mhl_tx_stop_timer(dev_context, + hw_context->input_field_rate_measurement_timer); + hw_context->outgoingAviPayLoad.namedIfData.ifData_u.bitFields.VIC = + output_video_code; + /* extract input color space */ + input_clr_spc = hw_context->current_avi_info_frame.payLoad.hwPayLoad. + namedIfData.ifData_u.bitFields.pb1.colorSpace; + + input_quant_range = + hw_context->current_avi_info_frame.payLoad.hwPayLoad. + namedIfData.ifData_u.bitFields.pb3.RGBQuantizationRange; + output_quant_range = input_quant_range; + + MHL_TX_DBG_INFO("input_clr_spc = %02X infoData[0]:%02X\n", + input_clr_spc, + hw_context->current_avi_info_frame.payLoad.hwPayLoad. + namedIfData.ifData_u.infoFrameData[0]); + + /* + * decide about packed pixel mode + */ + pixel_clock_frequency *= threeDPixelClockRatio; + MHL_TX_DBG_INFO("pixel clock:%u\n", pixel_clock_frequency); + + if (qualify_pixel_clock_for_mhl(dev_context->edid_parser_context, + pixel_clock_frequency, 24)) { + MHL_TX_DBG_INFO("OK for 24 bit pixels\n"); + } else { + /* not enough bandwidth for uncompressed video */ + if (si_edid_sink_supports_YCbCr422( + dev_context->edid_parser_context)) { + MHL_TX_DBG_INFO("Sink supports YCbCr422\n"); + + if (qualify_pixel_clock_for_mhl( + dev_context->edid_parser_context, + pixel_clock_frequency, 16)) { + /* enough for packed pixel */ + packedPixelNeeded = 1; + } else { + MHL_TX_DBG_ERR("unsupported video mode." + "pixel clock too high %s\n", + si_peer_supports_packed_pixel + (dev_context) ? "" : + "(peer does not support packed pixel)." + ); + return false; + } + } else { + MHL_TX_DBG_ERR("unsupported video mode." + "Sink doesn't support 4:2:2.\n"); + return false; + } + } + + /* Does output color space need to be 4:2:2 or same as input */ + output_clr_spc = input_clr_spc; +#define MHL3_PACKED_PIXEL_MODE VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED + switch (hw_context->pp_16bpp_override) { + case pp_16bpp_automatic: + case pp_16bpp_override_24bpp: + break; + case pp_16bpp_override_16bpp: + packedPixelNeeded = 1; + break; + } + + if (packedPixelNeeded) { + if (packed_pixel_available(dev_context)) { + + dev_context->link_mode = + MHL_STATUS_PATH_ENABLED | + MHL_STATUS_CLK_MODE_PACKED_PIXEL; + /* enforcing 4:2:2 if packed pixel. */ + output_clr_spc = BIT_EDID_FIELD_FORMAT_YCbCr422; + +/* begin BZ 32674 ( */ +#if 1 + if (aqr_default != input_quant_range) { + /* do nothing */ + } else if (acsRGB == input_clr_spc) { + input_quant_range = aqr_limited_range; +#if 0 + output_quant_range = aqr_full_range; + hw_context->outgoingAviPayLoad.namedIfData. + ifData_u.bitFields.pb3. + RGBQuantizationRange = aqr_full_range; +#endif + } +#endif +#if 1 + tpi_output |= BIT_TPI_OUTPUT_CSCMODE709; + + /* indicate ITU BT709 */ + hw_context->outgoingAviPayLoad.namedIfData. + ifData_u.bitFields. + colorimetryAspectRatio.Colorimetry = 2; +#endif +/* end BZ 32674 ) */ + + if (IN_MHL3_MODE(hw_context)) { + MHL_TX_DBG_INFO("setting 16BPP mode\n"); + mhl_tx_modify_reg(hw_context, + REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE, + MHL3_PACKED_PIXEL_MODE); + } else { + MHL_TX_DBG_INFO("setting packed pixel mode\n"); + mhl_tx_write_reg(hw_context, + REG_VID_MODE, + VAL_VID_MODE_M1080P_ENABLE); + mhl_tx_write_reg(hw_context, + REG_MHL_TOP_CTL, 0x41); + + mhl_tx_write_reg(hw_context, + REG_MHLTX_CTL6, 0x60); + } + + } else { + MHL_TX_DBG_ERR( + "Unsupported video mode. Packed Pixel not " + "available. Sink's link mode = 0x%02x\n", + dev_context->dev_cap_cache.mdc.vid_link_mode); + return false; + } + } else { + + dev_context->link_mode = + MHL_STATUS_PATH_ENABLED | MHL_STATUS_CLK_MODE_NORMAL; + + if (IN_MHL3_MODE(hw_context)) { + MHL_TX_DBG_INFO("setting 24BPP Mode\n"); + mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE, + VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_NORMAL); + } else { + MHL_TX_DBG_INFO( + "normal Mode, Packed Pixel mode disabled\n"); + mhl_tx_write_reg(hw_context, REG_VID_MODE, + VAL_VID_MODE_M1080P_DISABLE); + mhl_tx_write_reg(hw_context, REG_MHL_TOP_CTL, + 0x01); + mhl_tx_write_reg(hw_context, REG_MHLTX_CTL6, + 0xA0); + } + } + + tpi_input = colorSpaceTranslateInfoFrameToHw[input_clr_spc]; + tpi_input |= input_quant_range << 2; + + /* Set input color space */ + mhl_tx_write_reg(hw_context, REG_TPI_INPUT, tpi_input); + + /* Set output color space */ + tpi_output |= colorSpaceTranslateInfoFrameToHw[output_clr_spc]; + tpi_output |= input_quant_range << 2; + + mhl_tx_write_reg(hw_context, REG_TPI_OUTPUT, tpi_output); + + if (IN_MHL3_MODE(hw_context)) { + struct MHL_bits_per_pixel_fmt_data_t bpp_fmt; + struct MHL_bits_per_pixel_fmt_data_t *p_buffer; + size_t xfer_size; + /* only one descriptor to send */ + xfer_size = sizeof(bpp_fmt) - sizeof(p_buffer->descriptors) + + sizeof(p_buffer->descriptors[0]); + p_buffer = + si_mhl_tx_get_sub_payload_buffer(dev_context, + xfer_size); + + if (p_buffer) { + p_buffer->header.burst_id.low = + LOW_BYTE_16(burst_id_BITS_PER_PIXEL_FMT); + p_buffer->header.burst_id.high = + HIGH_BYTE_16(burst_id_BITS_PER_PIXEL_FMT); + p_buffer->header.checksum = 0; + p_buffer->header.total_entries = 1; + p_buffer->header.sequence_index = 1; + p_buffer->num_entries_this_burst = 1; + p_buffer->descriptors[0].stream_id = 0; + switch (dev_context-> + link_mode & MHL_STATUS_CLK_MODE_MASK) { + case MHL_STATUS_CLK_MODE_PACKED_PIXEL: + p_buffer->descriptors[0].stream_pixel_format = + VIEW_PIX_FMT_16BPP; + break; + case MHL_STATUS_CLK_MODE_NORMAL: + p_buffer->descriptors[0].stream_pixel_format = + VIEW_PIX_FMT_24BPP; + break; + } + p_buffer->header.checksum = + calculate_generic_checksum(p_buffer, 0, + xfer_size); + si_mhl_tx_push_block_transactions(dev_context); + } + /* enable TMDS */ + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF0); + + /* fill in values for MHL3 VSIF */ + hw_context->outgoing_mhl3_vsif.header.type_code = + MHL3_VSIF_TYPE; + hw_context->outgoing_mhl3_vsif.header.version_number = + MHL3_VSIF_VERSION; + hw_context->outgoing_mhl3_vsif.header.length = + sizeof(hw_context->outgoing_mhl3_vsif); + hw_context->outgoing_mhl3_vsif.checksum = 0; + hw_context->outgoing_mhl3_vsif.ieee_oui[0] = + (uint8_t) (IEEE_OUI_MHL & 0xFF); + hw_context->outgoing_mhl3_vsif.ieee_oui[1] = + (uint8_t) ((IEEE_OUI_MHL >> 8) & 0xFF); + hw_context->outgoing_mhl3_vsif.ieee_oui[2] = + (uint8_t) ((IEEE_OUI_MHL >> 16) & 0xFF); + hw_context->outgoing_mhl3_vsif.pb4 = + MHL3_VSIF_PB4(vid_fmt, _3d_fmt_type, sep_aud); + hw_context->outgoing_mhl3_vsif.pb5_reserved = 0; + hw_context->outgoing_mhl3_vsif.pb6 = MHL3_VSIF_PB6(hev_fmt); + hw_context->outgoing_mhl3_vsif.mhl_hev_fmt_type.high = + HIGH_BYTE_16(hev_fmt_type); + hw_context->outgoing_mhl3_vsif.mhl_hev_fmt_type.low = + LOW_BYTE_16(hev_fmt_type); + hw_context->outgoing_mhl3_vsif.pb9 = + MHL3_VSIF_PB9(delay_sync, delay_dir); + hw_context->outgoing_mhl3_vsif.av_delay_sync.high = + HIGH_BYTE_16(delay_sync); + hw_context->outgoing_mhl3_vsif.av_delay_sync.low = + LOW_BYTE_16(delay_sync); + + hw_context->outgoing_mhl3_vsif.checksum = + calculate_generic_checksum( + &hw_context->outgoing_mhl3_vsif, + 0, hw_context->outgoing_mhl3_vsif.header. + length); + + /* + * Program TMDS link speeds + */ + switch (dev_context->link_mode & MHL_STATUS_CLK_MODE_MASK) { + case MHL_STATUS_CLK_MODE_PACKED_PIXEL: + si_mhl_tx_drv_set_lowest_tmds_link_speed(dev_context, + pixel_clock_frequency, 16); + break; + case MHL_STATUS_CLK_MODE_NORMAL: + si_mhl_tx_drv_set_lowest_tmds_link_speed(dev_context, + pixel_clock_frequency, 24); + break; + } + } else { + /* + * MSC WRITE_STATUS is required to prepare sink for new mode + */ + si_mhl_tx_set_status(dev_context, false, + MHL_STATUS_REG_LINK_MODE, dev_context->link_mode); + } + /* + * Prepare outgoing AVIF for later programming the registers + * + * the checksum itself is included in the calculation. + */ + hw_context->outgoingAviPayLoad.namedIfData.checksum = 0; + hw_context->outgoingAviPayLoad.namedIfData.ifData_u.bitFields.pb1. + colorSpace = output_clr_spc; + hw_context->outgoingAviPayLoad.ifData[1] &= 0x7F; /* Clear PB1[7] */ + hw_context->outgoingAviPayLoad.ifData[4] &= 0x7F; /* Clear PB4[7] */ + hw_context->outgoingAviPayLoad.namedIfData.checksum = + calculate_avi_info_frame_checksum( + &hw_context->outgoingAviPayLoad); + + return true; +} + +/* + * process_info_frame_change + * called by the MHL Tx driver when a + * new AVI info frame is received from upstream + * OR + * called by customer's SOC video driver when a mode change is desired. + */ + +void process_info_frame_change(struct drv_hw_context *hw_context, + union vsif_mhl3_or_hdmi_u *vsif, + struct avi_info_frame_t *avif) +{ + bool mode_change = false; + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, struct mhl_dev_context, + drv_context); + if (NULL != vsif) { + if (is_valid_vsif(dev_context, vsif)) { + hw_context->current_vsif = *vsif; + hw_context->valid_vsif = 1; + mode_change = true; + } + } + if (NULL != avif) { + if (is_valid_avi_info_frame(dev_context, avif)) { + hw_context->current_avi_info_frame = *avif; + mode_change = true; + } + } + if (mode_change) { + int cstat_p3; + int bits_of_interest; + cstat_p3 = + mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); + bits_of_interest = + cstat_p3 & (BIT_TMDS_CSTAT_P3_SCDT | + BIT_TMDS_CSTAT_P3_CKDT); + + if ((BIT_TMDS_CSTAT_P3_SCDT | + VAL_TMDS_CSTAT_P3_CKDT_DETECTED) + == bits_of_interest) { + start_video(hw_context); + } + } +} + +#define dump_edid_fifo(hw_context, block_number) /* do nothing */ + +#define RX_DPD_BITS (BIT_DPD_PDNRX12 \ + | BIT_DPD_PDIDCK_N \ + | BIT_DPD_PD_MHL_CLK_N) +static int init_rx_regs(struct drv_hw_context *hw_context) +{ + /* power up the RX */ + mhl_tx_modify_reg(hw_context, REG_DPD, RX_DPD_BITS, RX_DPD_BITS); + + /* TODO: add to PR. Default for 2A4 is 0x0f */ + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL3, 0x00); + + /* + Before exposing the EDID upstream, setup to drop all packets. + This ensures we do not get Packet Overflow interrupt. + We still get the AVIF interrupts which is crucial. + Packet filters must be disabled until after TMDS is enabled. + */ + mhl_tx_write_reg(hw_context, REG_PKT_FILTER_0, 0xFF); + mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, 0xFF); + + mhl_tx_write_reg(hw_context, REG_ALICE0_BW_I2C, 0x06); + + mhl_tx_modify_reg(hw_context, REG_RX_HDMI_CLR_BUFFER, + BIT_RX_HDMI_CLR_BUFFER_VSI_CLR_EN, + VAL_RX_HDMI_CLR_BUFFER_VSI_CLR_EN_CLEAR); + + return 0; +} + +#ifdef USE_HW_TIMER +static void start_hw_timer(struct drv_hw_context *hw_context) +{ + MHL_TX_DBG_INFO("Start HW Timer.\n"); + mhl_tx_write_reg(hw_context, REG_SYS_CTRL3, + BIT_SYS_CTRL3_SYS_CNTR); +} + +static void stop_hw_timer(struct drv_hw_context *hw_context) +{ + MHL_TX_DBG_INFO("Stop HW Timer.\n"); + mhl_tx_write_reg(hw_context, REG_SYS_CTRL3, 0x00); +} + +/* +Time (ms) Reg value + 5000 2710 + 3000 1770 + 2000 0FA0 + 1500 0BB8 +*/ +static void setup_hw_timer(struct drv_hw_context *hw_context, uint16_t time_ms) +{ + MHL_TX_DBG_INFO("Setup HW Timer for %dms.\n", time_ms); + + /* Max time is 32,767ms. */ + time_ms &= 0x7FFF; + + /* Divide by 0.5 for register value. */ + time_ms <<= 1; + + mhl_tx_write_reg(hw_context, REG_SYS_CNTR_0, + (uint8_t) (time_ms & 0x00FF)); + mhl_tx_write_reg(hw_context, REG_SYS_CNTR_1, + (uint8_t) ((time_ms >> 8) & 0x00FF)); +} +#endif + +/* This function is exported from the driver */ +int si_mhl_tx_drv_set_display_mode(struct mhl_dev_context *dev_context, + enum hpd_high_callback_status status) +{ + struct drv_hw_context *hw_context; + hw_context = (struct drv_hw_context *)&dev_context->drv_context; + + hw_context->hpd_high_callback_status = status; + /* did the client begin sending video? */ + if (status >= 0) { + switch (status) { + case HH_FMT_DVI_HDCP_ON: + case HH_FMT_DVI: + case HH_FMT_DVI_HDCP_ON_NOT_RPT: + case HH_FMT_DVI_NOT_RPT: + /* + * output video here for DVI + */ + start_video(hw_context); + break; + case HH_FMT_HDMI_VSIF_NONE_HDCP_ON: + case HH_FMT_HDMI_VSIF_NONE: + case HH_FMT_HDMI_VSIF_NONE_HDCP_ON_NOT_RPT: + case HH_FMT_HDMI_VSIF_NONE_NOT_RPT: + process_info_frame_change(hw_context, NULL, + &hw_context-> + avif_or_dtd_from_callback. + avif); + break; + + case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON: + case HH_FMT_HDMI_VSIF_MHL3: + case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_RPT: + case HH_FMT_HDMI_VSIF_MHL3_NOT_RPT: +#ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH + hw_context->valid_vsif = true; +#endif + /* set_hdmi_params uses MHL3 vsif in the hw_context */ + process_info_frame_change(hw_context, NULL, + &hw_context-> + avif_or_dtd_from_callback. + avif); + break; + case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON: + case HH_FMT_HDMI_VSIF_HDMI: + case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON_NOT_RPT: + case HH_FMT_HDMI_VSIF_HDMI_NOT_RPT: +#ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH + hw_context->valid_vsif = true; +#endif + process_info_frame_change(hw_context, + &hw_context->vsif_mhl3_or_hdmi_from_callback, + &hw_context->avif_or_dtd_from_callback. + avif); + break; + default: + /* other values are not applicable */ + break; + } + } + return 0; +} + +static void do_hpd_driven_high_callback(struct drv_hw_context *hw_context, + uint8_t *edid, uint16_t length) +{ + int status; + struct mhl_dev_context *dev_context = + container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + struct edid_3d_data_t *p_edid_data = + dev_context->edid_parser_context; + + status = hw_context->callbacks.hpd_driven_high( + hw_context->callbacks.context, + edid, + length, + p_edid_data->p_edid_emsc, + p_edid_data->num_edid_emsc_blocks * EDID_BLOCK_SIZE, + p_edid_data->hev_dtd_list, + p_edid_data->hev_dtd_info.num_items, + p_edid_data->hev_vic_list, + p_edid_data->hev_vic_info.num_items, + p_edid_data->_3d_dtd_list, + p_edid_data->_3d_dtd_info.num_items, + p_edid_data->_3d_vic_list, + p_edid_data->_3d_vic_info.num_items, + &hw_context->avif_or_dtd_from_callback, + sizeof(hw_context->avif_or_dtd_from_callback), + &hw_context->vsif_mhl3_or_hdmi_from_callback, + sizeof(hw_context-> + vsif_mhl3_or_hdmi_from_callback) + ); + si_mhl_tx_drv_set_display_mode(dev_context, status); +} + +#define BIT_0_DISABLED 0x01 +#define BIT_0_HIGH 0x02 +#define BIT_1_DISABLED 0x04 +#define BIT_1_HIGH 0x08 + +#define BITS_GPIO_01_HPD_HIGH (BIT_0_HIGH | BIT_1_HIGH) +#define BITS_GPIO_01_HPD_LOW 0 + +#define BITS_HPD_CTRL_OPEN_DRAIN_HIGH (BITS_GPIO_01_HPD_HIGH | 0x70) +#define BITS_HPD_CTRL_PUSH_PULL_HIGH (BITS_GPIO_01_HPD_HIGH | 0x30) + +#define BITS_HPD_CTRL_OPEN_DRAIN_LOW (BITS_GPIO_01_HPD_LOW | 0x50) +#define BITS_HPD_CTRL_PUSH_PULL_LOW (BITS_GPIO_01_HPD_LOW | 0x10) +/* + * drive_hpd_high -- sets upstream HPD high + * returns the value of REG_TMDS_CSTAT_P3 +*/ +static int drive_hpd_high(struct drv_hw_context *hw_context, uint8_t *edid, + uint16_t length) +{ + enum hpd_control_mode mode; + int ret_val = -1; + int cstat_p3; + + mode = platform_get_hpd_control_mode(); + + /* sample REG_TMDS_CSTAT_P3 before driving upstream HDP high */ + cstat_p3 = mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); + + /* disable auto-clear */ + cstat_p3 |= BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR; +#ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH + cstat_p3 |= BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE; +#endif + mhl_tx_write_reg(hw_context, REG_TMDS_CSTAT_P3, cstat_p3); + + if (HPD_CTRL_OPEN_DRAIN == mode) + ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, + BITS_HPD_CTRL_OPEN_DRAIN_HIGH | + (IN_MHL3_MODE(hw_context) ? + BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); + else if (HPD_CTRL_PUSH_PULL == mode) + ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, + BITS_HPD_CTRL_PUSH_PULL_HIGH | + (IN_MHL3_MODE(hw_context) ? + BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); + + if (edid) { + MHL_TX_DBG_INFO("\n") + do_hpd_driven_high_callback(hw_context, edid, length); + } + if (ret_val >= 0) + return cstat_p3; + + return ret_val; +} + +static int drive_hpd_low(struct drv_hw_context *hw_context) +{ + enum hpd_control_mode mode; + int ret_val = -1; + + ddc_abort_count = 0; + mode = platform_get_hpd_control_mode(); + + mhl_tx_modify_reg(hw_context, REG_EDID_CTRL, + BIT_EDID_CTRL_EDID_PRIME_VALID, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE); + + if (HPD_CTRL_OPEN_DRAIN == mode) + ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, + BITS_HPD_CTRL_OPEN_DRAIN_LOW | + (IN_MHL3_MODE(hw_context) ? + BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); + else if (HPD_CTRL_PUSH_PULL == mode) + ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, + BITS_HPD_CTRL_PUSH_PULL_LOW | + (IN_MHL3_MODE(hw_context) ? + BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); + + enable_intr(hw_context, INTR_INFR, 0x00); + hw_context->hpd_high_callback_status = HH_VIDEO_NOT_RDY; + hw_context->callbacks.hpd_driven_low(hw_context->callbacks.context); + return ret_val; +} + +#ifdef SWWA_BZ30759 +/* we are done with the EDID for now. We now expect to start doing HDCP, which + * can destroy the contents of our EDID buffer, so do another EDID read, which + * we know will fail, but that will reset the DDC fifo in such a way as to + * leave the buffer contents alone + */ +void edid_hw_sm_clean_up(struct drv_hw_context *hw_context) +{ + mhl_tx_write_reg(hw_context, REG_PAGE_8_HDCP1X_LB_BIST, + BIT_PAGE_8_HDCP1X_LB_BIST_HDCP1X_LB_BIST_EN); + mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, + BIT_DDC_MANUAL_MAN_DDC); + mhl_tx_write_reg(hw_context, REG_INTR9, 0xFF); + + /* Disable EDID interrupt */ + enable_intr(hw_context, INTR_EDID, 0); + + /* Trigger EDID to generate an error to reset state machine */ + mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, + BIT_TPI_CBUS_START_GET_EDID_START_0); + + mhl_tx_write_reg(hw_context, REG_INTR9, 0xFF); + mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, 0x00); + mhl_tx_write_reg(hw_context, REG_PAGE_8_HDCP1X_LB_BIST, 0x00); + +} +#endif + +int si_mhl_tx_drv_set_upstream_edid(struct drv_hw_context *hw_context, + uint8_t *edid, uint16_t length) +{ + uint8_t reg_val; + + if (MHL_SEND_3D_REQ_OR_FEAT_REQ == + hw_context->current_cbus_req.command) { + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + MHL_TX_DBG_WARN("3D_REQ or FEAT_REQ completed\n"); + hw_context->current_cbus_req.command = 0x00; + si_mhl_tx_msc_command_done(dev_context, 0x00); + } + + if (!ok_to_proceed_with_ddc(hw_context)) + return -1; + + MHL_TX_DBG_INFO("presenting EDID upstream\n"); + + init_rx_regs(hw_context); + +#ifdef SWWA_BZ30759 + /* we are done with the EDID for now. We now expect to start doing + * HDCP, which can destroy the contents of our EDID buffer, so do + * another EDID read, which we know will fail, but that will reset the + * DDC fifo in such a way as to leave the buffer contents alone + */ + edid_hw_sm_clean_up(hw_context); +#endif + /* choose EDID instead of devcap to appear at the FIFO */ + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | + VAL_EDID_CTRL_DEVCAP_SELECT_EDID | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | + VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + + mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); + mhl_tx_write_reg_block(hw_context, REG_EDID_FIFO_WR_DATA, length, + edid); + + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_ENABLE | + VAL_EDID_CTRL_DEVCAP_SELECT_EDID | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | + VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + + /* Enable SCDT interrupt to detect stable incoming clock */ + enable_intr(hw_context, INTR_SCDT, BIT_INTR_SCDT_CHANGE); + + /* Disable EDID interrupt */ + enable_intr(hw_context, INTR_EDID, 0); + +#ifndef EARLY_HSIC + if (IN_MHL3_MODE(hw_context)) { + /* + * todo hsic_init configures the transmitter for USB host mode. + * This call should be deferred until the driver has negotiated + * with the sink to take over the host role. + * The call is placed here for test purposes. + */ + hsic_init(hw_context); + } +#endif + MHL_TX_DBG_ERR("Expose EDID\n"); + + /* HPD was held low all this time. Now we send an HPD high */ + reg_val = drive_hpd_high(hw_context, edid, length); + + /* If SCDT is already high, then we will not get an interrupt */ + if (BIT_TMDS_CSTAT_P3_SCDT & reg_val) { + MHL_TX_DBG_INFO("SCDT status is already HIGH. " + "Simulate int_5: %s0x%02x%s\n", + ANSI_ESC_YELLOW_TEXT, + mhl_tx_read_reg(hw_context, REG_RX_HDMI_CTRL0), + ANSI_ESC_RESET_TEXT); + int_5_isr(hw_context, BIT_INTR_SCDT_CHANGE); + } + return 0; +} + +static void hsic_init(struct drv_hw_context *hw_context) +{ + MHL_TX_DBG_INFO("Initialize USB Tunneling\n"); + + /* Enable USB flow control and select host mode */ + mhl_tx_write_reg(hw_context, REG_FCGC, 0x03); + + /* + * strobe_stay_then_reset + * bit0: host=1, device=0 + * bit1: status_en + */ + mhl_tx_modify_reg(hw_context, REG_HRXCTRL3, + BIT_HRXCTRL3_HRX_STAY_RESET | + BIT_HRXCTRL3_STATUS_EN, + BIT_HRXCTRL3_HRX_STAY_RESET | + BIT_HRXCTRL3_STATUS_EN); + + /* tdm_tx_num_of_bits_per_symbol bit[2:0]: 4=8-bit */ + mhl_tx_modify_reg(hw_context, REG_TTXNUMB, + MSK_TTXNUMB_TTX_NUMBPS_2_0, 4); + + /* tdm_rx_from_se_coc bit3:doc=0, coc=1 */ + mhl_tx_modify_reg(hw_context, REG_TRXCTRL, 0x08, 0x08); + + /* hsic_tx_respect_tdm_evn_if_rx_busy bit2:host=0, device=1 */ + mhl_tx_modify_reg(hw_context, REG_HTXCTRL, 0x02, 0x00); + + /* keeper_mode bit[1:0]:host=0, device=2 */ + mhl_tx_modify_reg(hw_context, REG_KEEPER, 0x03, 0x00); + + mhl_tx_write_reg(hw_context, REG_TDMLLCTL, 0x00); + + /* Reset USB Tunneling module: hsic_rx/hsic_tx/hsic_fc/keeper */ + mhl_tx_write_reg(hw_context, REG_UTSRST, + BIT_UTSRST_HRX_SRST | + BIT_UTSRST_HTX_SRST | + BIT_UTSRST_KEEPER_SRST | + BIT_UTSRST_FC_SRST); + mhl_tx_write_reg(hw_context, REG_UTSRST, + BIT_UTSRST_HRX_SRST | + BIT_UTSRST_HTX_SRST); + + /* Interrupt clear + * todo Not sure these are necessary. Need to look into removing + * them once HSIC is working. + */ + mhl_tx_write_reg(hw_context, REG_HRXINTL, 0xFF); /* hsic_rx */ + mhl_tx_write_reg(hw_context, REG_HRXINTH, 0xFF); /* hsic_rx */ + mhl_tx_write_reg(hw_context, REG_TTXINTL, 0xFF); /* tdm_tx */ + mhl_tx_write_reg(hw_context, REG_TTXINTH, 0xFF); /* tdm_tx */ + mhl_tx_write_reg(hw_context, REG_TRXINTL, 0xFF); /* tdm_rx */ + mhl_tx_write_reg(hw_context, REG_TRXINTH, 0xFF); /* tdm_rx */ + mhl_tx_write_reg(hw_context, REG_HTXINTL, 0xFF); /* hsic_tx */ + mhl_tx_write_reg(hw_context, REG_HTXINTH, 0xFF); /* hsic_tx */ + mhl_tx_write_reg(hw_context, REG_FCINTR0, 0xFF); /* hsic_fc */ + mhl_tx_write_reg(hw_context, REG_FCINTR1, 0xFF); /* hsic_fc */ + mhl_tx_write_reg(hw_context, REG_FCINTR2, 0xFF); /* hsic_fc */ + mhl_tx_write_reg(hw_context, REG_FCINTR3, 0xFF); /* hsic_fc */ + mhl_tx_write_reg(hw_context, REG_FCINTR4, 0xFF); /* hsic_fc */ + mhl_tx_write_reg(hw_context, REG_FCINTR5, 0xFF); /* hsic_fc */ + mhl_tx_write_reg(hw_context, REG_FCINTR6, 0xFF); /* hsic_fc */ + mhl_tx_write_reg(hw_context, REG_FCINTR7, 0xFF); /* hsic_fc */ +} + +#define MHL_LOGICAL_DEVICE_MAP (MHL_DEV_LD_AUDIO | MHL_DEV_LD_VIDEO | \ + MHL_DEV_LD_MEDIA | MHL_DEV_LD_GUI) +#define DEVCAP_REG(x) (REG_MHL_DEVCAP_0 | DEVCAP_OFFSET_##x) +#define XDEVCAP_REG(x) (REG_MHL_EXTDEVCAP_0 | \ + XDEVCAP_OFFSET(XDEVCAP_ADDR_##x)) + +/* Local devcap values. Populated at init_regs time from si_app_devcap.h */ +uint8_t dev_cap_values[16] = { + DEVCAP_VAL_DEV_STATE, + DEVCAP_VAL_MHL_VERSION, + DEVCAP_VAL_DEV_CAT, + DEVCAP_VAL_ADOPTER_ID_H, + DEVCAP_VAL_ADOPTER_ID_L, + DEVCAP_VAL_VID_LINK_MODE, + DEVCAP_VAL_AUD_LINK_MODE, + DEVCAP_VAL_VIDEO_TYPE, + DEVCAP_VAL_LOG_DEV_MAP, + DEVCAP_VAL_BANDWIDTH, + DEVCAP_VAL_FEATURE_FLAG, + 0, + 0, + DEVCAP_VAL_SCRATCHPAD_SIZE, + DEVCAP_VAL_INT_STAT_SIZE, + DEVCAP_VAL_RESERVED +}; + +/* Local xdevcap values. Populated at init_regs time from si_app_devcap.h */ +uint8_t xdev_cap_values[4] = { + XDEVCAP_VAL_ECBUS_SPEEDS, + XDEVCAP_VAL_TMDS_SPEEDS, + XDEVCAP_VAL_DEV_ROLES, + XDEVCAP_VAL_LOG_DEV_MAPX +}; + +static void mhl3_specific_init(struct drv_hw_context *hw_context) +{ + /* Even in MHL3 mode, TPI:1A[0] controls DVI vs. HDMI */ + mhl_tx_write_reg(hw_context, REG_SYS_CTRL1, + BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD | + BIT_SYS_CTRL1_TX_CONTROL_HDMI); + enable_intr(hw_context, INTR_LINK_TRAINING, + BIT_EMSCINTR1_EMSC_TRAINING_COMMA_ERR); +} + +static int init_regs(struct drv_hw_context *hw_context) +{ + int ret_val = 0; + + MHL_TX_DBG_INFO("called\n"); + + /* disable and clear uninteresting interrupts for MHL 1/2 */ + enable_intr(hw_context, INTR_HDCP2, 0x00); + enable_intr(hw_context, INTR_LINK_TRAINING, 0x00); + mhl_tx_write_reg(hw_context, REG_HDCP2X_INTR0, 0xFF); + mhl_tx_write_reg(hw_context, REG_INTR1, 0xFF); + mhl_tx_write_reg(hw_context, REG_SYS_CTRL1, + BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD | + BIT_SYS_CTRL1_TX_CONTROL_HDMI); + + /* default values for flags */ + hw_context->video_ready = false; + hw_context->video_path = 1; + + hw_context->rx_hdmi_ctrl2_defval = REG_RX_HDMI_CTRL2_DEFVAL_DVI; + /* Drop the Hot Plug to upstream and hide EDID */ + drive_hpd_low(hw_context); + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE); + + /* + * DO NOT enable wake pulses or discovery pulses until RGND == 1K + * No OTG, + */ + /* this is extraneous here, this register is properly set elsewhere */ + mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, + BIT_DISC_CTRL9_WAKE_DRVFLT | + BIT_DISC_CTRL9_WAKE_PULSE_BYPASS); + + mhl_tx_write_reg(hw_context, REG_TMDS0_CCTRL1, 0x90); + + /* Enable TxPLL Clock */ + mhl_tx_write_reg(hw_context, REG_TMDS_CLK_EN, 0x01); + + /* Enable Tx Clock Path & Equalizer */ + mhl_tx_write_reg(hw_context, REG_TMDS_CH_EN, 0x11); + mhl_tx_write_reg(hw_context, REG_BGR_BIAS, 0x87); + + mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xE8); + mhl_tx_write_reg(hw_context, REG_ALICE0_MODE_CTRL, 0x04); + + /* Enable TPI */ + ret_val = mhl_tx_read_reg(hw_context, REG_LM_DDC); + ret_val &= ~BIT_LM_DDC_SW_TPI_EN; + ret_val |= VAL_LM_DDC_SW_TPI_EN_ENABLED; + mhl_tx_write_reg(hw_context, REG_LM_DDC, ret_val); + +/* mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, + VAL_TPI_COPP_PROTLEVEL_MIN);*/ + + /* + * TODO: TPI:0xBB has bad default. Need to document in the PR + * If this initialization is not changed, we have unstable HDCP + * (while testing 9612 as source) + */ + mhl_tx_write_reg(hw_context, REG_TPI_HW_OPT3, 0x76); + + /* TMDS should be enabled for both MHL1/2 and MHL3 cases. + * MHL3 needs it for CoC calibration. + */ + mhl_tx_write_reg(hw_context, REG_TMDS_CCTRL, + BIT_TMDS_CCTRL_TMDS_OE); + + /* set time base for one second to be 60Hz/4*5 + 4 */ + mhl_tx_write_reg(hw_context, REG_TPI_DTD_B2, 79); + + /* setup local DEVCAP and few more CBUS registers. */ + + /* + * Fill-in DEVCAP device ID values with those read + * from the transmitter. + */ + dev_cap_values[DEVCAP_OFFSET_DEVICE_ID_L] = + (uint8_t) hw_context->chip_device_id; + dev_cap_values[DEVCAP_OFFSET_DEVICE_ID_H] = + (uint8_t) (hw_context->chip_device_id >> 8); + + /* Setup local DEVCAP registers */ + mhl_tx_write_reg_block(hw_context, DEVCAP_REG(DEV_STATE), + ARRAY_SIZE(dev_cap_values), dev_cap_values); + + /* + * Adjust XDEVCAP values to match capabilities of the transmitter. + */ + xdev_cap_values[XDEVCAP_OFFSET(XDEVCAP_ADDR_ECBUS_SPEEDS)] = + MHL_XDC_ECBUS_S_075 | MHL_XDC_ECBUS_S_8BIT; + if (si_mhl_tx_drv_support_e_cbus_d(hw_context)) + xdev_cap_values[XDEVCAP_OFFSET(XDEVCAP_ADDR_ECBUS_SPEEDS)] |= + MHL_XDC_ECBUS_D_150 | MHL_XDC_ECBUS_D_8BIT; + + mhl_tx_write_reg_block(hw_context, XDEVCAP_REG(ECBUS_SPEEDS), + ARRAY_SIZE(xdev_cap_values), xdev_cap_values); + + /* + * Make sure MDT registers are initialized and the MDT + * transmit/receive are both disabled. + */ + mhl_tx_write_reg(hw_context, REG_MDT_XMIT_TIMEOUT, 100); + + /* Clear transmit FIFOs and make sure MDT transmit is disabled */ + mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, 0x03); + + /* Disable MDT transmit preemptive handshake option */ + mhl_tx_write_reg(hw_context, REG_MDT_XFIFO_STAT, 0x00); + + mhl_tx_write_reg(hw_context, REG_MDT_RCV_TIMEOUT, 100); + + /* 2013-03-01 bugzilla 27180 */ + mhl_tx_write_reg(hw_context, REG_CBUS_LINK_CONTROL_8, 0x1D); + + si_mhl_tx_drv_start_gen2_write_burst(hw_context); + + mhl_tx_write_reg(hw_context, REG_BIST_CTRL, 0x00); + + /* Setup MHL3 specs parameters in CoC registers */ + mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x10); + mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x18); + mhl_tx_write_reg(hw_context, REG_COC_CTLF, 0x07); + /* set eCBUS BIST hw duration to infinite 7:4 == 0xF */ + mhl_tx_write_reg(hw_context, REG_COC_CTL11, 0xF8); + mhl_tx_write_reg(hw_context, REG_COC_CTL17, 0x61); + mhl_tx_write_reg(hw_context, REG_COC_CTL18, 0x46); + mhl_tx_write_reg(hw_context, REG_COC_CTL19, 0x15); + mhl_tx_write_reg(hw_context, REG_COC_CTL1A, 0x01); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL3, + BIT_MHL_COC_CTL3_COC_AECHO_EN); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL4, 0x2D); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL5, 0xF9); + + disable_heartbeat(hw_context); + disable_gen2_write_burst_rcv(hw_context); + disable_gen2_write_burst_xmit(hw_context); + + return ret_val; +} + +static void si_mhl_tx_drv_start_gen2_write_burst(struct drv_hw_context + *hw_context) +{ + enable_intr(hw_context, INTR_G2WB_ERR, + BIT_MDT_RCV_TIMEOUT | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | + BIT_MDT_RCV_SM_ERROR | BIT_MDT_XMIT_TIMEOUT | + BIT_MDT_XMIT_SM_ABORT_PKT_RCVD | BIT_MDT_XMIT_SM_ERROR); + enable_intr(hw_context, INTR_G2WB, + BIT_MDT_XFIFO_EMPTY | BIT_MDT_IDLE_AFTER_HAWB_DISABLE | + BIT_MDT_RFIFO_DATA_RDY); + + /* Want HAWB RCV on at all times except + * when sending a non-WB transaction + */ + hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; + hw_context->delayed_hawb_enable_reg_val = 0; + enable_gen2_write_burst_rcv(hw_context); +} + +void si_mhl_tx_drv_disable_video_path(struct drv_hw_context *hw_context) +{ + /* If video was already being output */ + if (hw_context->video_ready + && (0 == + (VAL_TPI_SC_TPI_AV_MUTE_MUTED & + mhl_tx_read_reg(hw_context, REG_TPI_SC)))) { + + /* stop hdcp and video and remember */ + stop_video(hw_context); + hw_context->video_path = 0; + } +} + +void si_mhl_tx_drv_enable_video_path(struct drv_hw_context *hw_context) +{ + uint8_t mask = + (BIT_TPI_SC_REG_TMDS_OE | BIT_TPI_SC_TPI_AV_MUTE); + uint8_t reg; + + /* if a path_en = 0 had stopped the video, + * restart it unless done already. + */ + if (hw_context->video_ready && (0 == hw_context->video_path)) { + /* remember ds has enabled our path */ + hw_context->video_path = 1; + + reg = mhl_tx_read_reg(hw_context, REG_TPI_SC); + + if (mask == (mask & reg)) + start_video(hw_context); + } +} + +void si_mhl_tx_drv_content_off(struct drv_hw_context *hw_context) +{ + MHL_TX_DBG_INFO("RAP CONTENT_OFF video %sready\n", + hw_context->video_ready ? "" : "NOT "); + /* If video was already being output */ + if (hw_context->video_ready + && (0 == + (VAL_TPI_SC_TPI_AV_MUTE_MUTED & + mhl_tx_read_reg(hw_context, REG_TPI_SC)))) { + + MHL_TX_DBG_INFO("RAP CONTENT_OFF\n"); + /* stop hdcp and video and remember */ + stop_video(hw_context); + } +} + +void si_mhl_tx_drv_content_on(struct drv_hw_context *hw_context) +{ + uint8_t mask = + (BIT_TPI_SC_REG_TMDS_OE | BIT_TPI_SC_TPI_AV_MUTE); + uint8_t reg; + + /* if a path_en = 0 had stopped the video, + * restart it unless done already. + */ + if (hw_context->video_ready) { + + reg = mhl_tx_read_reg(hw_context, REG_TPI_SC); + + if (mask == (mask & reg)) + start_video(hw_context); + } +} + +static void unmute_video(struct drv_hw_context *hw_context) +{ + MHL_TX_DBG_INFO("AV unmuted\n"); + + if (!IN_MHL3_MODE(hw_context)) { + if (hdcp_content_type == 0) { + if (si_edid_sink_is_hdmi + (hw_context->intr_info->edid_parser_context)) { + mhl_tx_write_reg(hw_context, REG_TPI_SC, + VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + } else { + mhl_tx_write_reg(hw_context, REG_TPI_SC, + VAL_TPI_SC_TPI_OUTPUT_MODE_0_DVI); + } + } else { + MHL_TX_DBG_INFO + ("HDCP Content Type 1, AV remain muted\n"); + } + } + + /* Now we can entertain PATH_EN */ + hw_context->video_ready = 1; +} + +/* + Sequence of operations in this function is important. + 1. Turn off HDCP interrupt + 2. Turn of TMDS output + 3. Turn off HDCP engine/encryption + 4. Clear any leftover HDCP interrupt +*/ +#define OUTPUT_OFF_HDMI (VAL_TPI_SC_REG_TMDS_OE_POWER_DOWN | \ + VAL_TPI_SC_TPI_AV_MUTE_MUTED | \ + VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI) + +#define OUTPUT_OFF_DVI (VAL_TPI_SC_REG_TMDS_OE_POWER_DOWN | \ + VAL_TPI_SC_TPI_AV_MUTE_MUTED | \ + VAL_TPI_SC_TPI_OUTPUT_MODE_0_DVI) + +static void stop_video(struct drv_hw_context *hw_context) +{ + if (IN_MHL3_MODE(hw_context)) { + + MHL_TX_DBG_WARN("for >= MHL3.0\n"); + + /* disable TMDS early */ + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xC0); + + /* reset H2M here */ + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_MHL3_VALUE | + BIT_M3_CTRL_H2M_SWRST); + + si_mhl_tx_drv_shut_down_HDCP2(hw_context); + + } else { + + /* MHL 2/1 with HDCP 1.x */ + MHL_TX_DBG_WARN(" for MHL1/2.x\n"); + + /* Turn off HDCP interrupt */ + enable_intr(hw_context, INTR_HDCP, 0x00); + enable_intr(hw_context, INTR_HDCP2, 0x00); + + /* stop hdcp engine */ + mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, 0); + + /* clear any leftover hdcp interrupt */ + mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP].stat_addr, + 0xFF); + + MHL_TX_DBG_WARN("hw_context = %x; ->intr_info = %x\n", + hw_context, hw_context->intr_info); + + if (NULL != hw_context->intr_info) { + if (NULL != + hw_context->intr_info->edid_parser_context) { + /* We must maintain the output bit (bit 0) to + * allow just one bit change later when BKSV + * read is triggered. This programming is + * required for HDCP CTS 1.x to pass + */ + if (si_edid_sink_is_hdmi( + hw_context->intr_info-> + edid_parser_context)) { + + mhl_tx_write_reg(hw_context, + REG_TPI_SC, + OUTPUT_OFF_HDMI); + } else { + mhl_tx_write_reg(hw_context, + REG_TPI_SC, + OUTPUT_OFF_DVI); + } + } + } + } +} + +void si_mhl_tx_drv_start_cp(struct mhl_dev_context *dev_context) +{ + /* if hdcp is already running, shut it down here and + * wait for the next "NOT normal"/"normal" message pair from the sink. + */ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)(&dev_context->drv_context); + if (hw_context->hdcp2_started) { + MHL_TX_DBG_ERR("%salready started%s\n", ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + si_mhl_tx_drv_shut_down_HDCP2(hw_context); + } else + start_hdcp(hw_context); +} + +static void start_hdcp(struct drv_hw_context *hw_context) +{ + if (IN_MHL3_MODE(hw_context)) { + MHL_TX_DBG_ERR("Start HDCP 2.2\n"); + + start_hdcp_content_type(hw_context); + + /* Unmask HDCP2 INTs */ + enable_intr(hw_context, INTR_HDCP2, BIT_HDCP2_INTR_AUTH_DONE | + BIT_HDCP2_INTR_AUTH_FAIL | + BIT_HDCP2_INTR_RPTR_RCVID_CHANGE); + + /* Enable HDCP 2.2 */ + mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_HDCP_EN, + BIT_M3_P0CTRL_MHL3_P0_HDCP_EN); + + /* enable encryption */ + mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x83); + + /* Enable HDCP2 DDC polling */ + hw_context->hdcp2_started = true; + mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x70); + + mhl_tx_write_reg(hw_context, REG_HDCP2X_RPT_SMNG_K, 1); + + mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_1, 0x01); + mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_1, 0x00); + } else { + /* First stop hdcp and video */ + stop_video(hw_context); + /* Enable HDCP interrupts + * Clear interrupt first. + */ + MHL_TX_DBG_ERR("Start HDCP 1.x\n"); + mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP].stat_addr, + 0xFF); + + if (ok_to_proceed_with_ddc(hw_context)) { + /* Enable HDCP interrupt */ + enable_intr(hw_context, INTR_HDCP, + (BIT_TPI_INTR_ST0_TPI_AUTH_CHNGE_STAT | + BIT_TPI_INTR_ST0_TPI_COPP_CHNGE_STAT | +/*#define KSV_FIFO_RDY_INTERRUPT*/ +#ifdef KSV_FIFO_RDY_INTERRUPT + BIT_TPI_INTR_ST0_KSV_FIFO_FIRST_STAT | +#endif + BIT_TPI_INTR_ST0_READ_BKSV_BCAPS_DONE_STAT | + BIT_TPI_INTR_ST0_READ_BKSV_ERR_STAT)); + + msleep(250); + /* + * Chip requires only bit 4 to change for BKSV read + * No other bit should change. + */ + mhl_tx_modify_reg(hw_context, REG_TPI_SC, + BIT_TPI_SC_REG_TMDS_OE, + VAL_TPI_SC_REG_TMDS_OE_ACTIVE); + } + } +} + +static void start_hdcp_content_type(struct drv_hw_context *hw_context) +{ + uint8_t misc_ctrl; + uint8_t index; + uint8_t msg[2] = {MHL_STREAM_ID, CONTENT_TYPE_DEFAULT}; + + if (hdcp_content_type == 1) + msg[1] = 0x01; + + MHL_TX_DBG_INFO("HDCP Content Type = %d\n", msg[1]); + + misc_ctrl = mhl_tx_read_reg(hw_context, REG_HDCP2X_MISC_CTRL); + + /* + * Toggle SMNG_WR_START + */ + mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, misc_ctrl | + BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_SMNG_WR_START); + mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, misc_ctrl); + + /* Write message */ + for (index = 0; index < 2; index++) { + mhl_tx_write_reg(hw_context, REG_HDCP2X_RPT_SMNG_IN, + msg[index]); + + mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, + misc_ctrl | + BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_SMNG_WR); + mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, + misc_ctrl); + } +} + +/* + video_sans_cbus1 +*/ +static void video_sans_cbus1(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)(&dev_context->drv_context); + struct bist_setup_info *bist_setup; + uint32_t pixel_clock_frequency; + bist_setup = &dev_context->bist_setup; + pixel_clock_frequency = si_edid_find_pixel_clock_from_AVI_VIC( + dev_context->edid_parser_context, + bist_setup->avlink_video_mode); + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, + REG_RX_HDMI_CTRL2_DEFVAL_HDMI | + VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); + mhl_tx_write_reg(hw_context, REG_VID_OVRRD, + BIT_VID_OVRRD_PP_AUTO_DISABLE | + BIT_VID_OVRRD_M1080P_OVRRD); + mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_HDCP_EN | + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE | + BIT_M3_P0CTRL_MHL3_P0_PORT_EN, + 0x00 | + VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_NORMAL | + BIT_M3_P0CTRL_MHL3_P0_PORT_EN); + mhl_tx_write_reg(hw_context, REG_TPI_INPUT, 0x01); + mhl_tx_write_reg(hw_context, REG_TPI_OUTPUT, 0x01); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF0); + si_mhl_tx_drv_set_lowest_tmds_link_speed(dev_context, + pixel_clock_frequency, 24); + /* Assert H2M reset */ + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_MHL3_VALUE | + BIT_M3_CTRL_H2M_SWRST); + + /* Deassert H2M reset */ + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_MHL3_VALUE); + mhl_tx_write_reg(hw_context, REG_PKT_FILTER_0, 0xA5); + mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, 0x83); + enable_intr(hw_context, INTR_HDCP2, 0x00); + mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); + mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x02); + + mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP2].stat_addr, 0xff); + + mhl_tx_write_reg(hw_context, REG_TPI_SC, + VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + + switch (bist_setup->avlink_randomizer) { + case 0: + mhl_tx_modify_reg(hw_context, REG_M3_SCTRL, + BIT_M3_SCTRL_MHL3_SCRAMBLER_EN, + 0); + break; + case 1: + mhl_tx_modify_reg(hw_context, REG_M3_SCTRL, + BIT_M3_SCTRL_MHL3_SCRAMBLER_EN, + BIT_M3_SCTRL_MHL3_SCRAMBLER_EN); + break; + default: + MHL_TX_DBG_ERR("%s incoherent AV_LINK_RANDOMIZER:%d%s\n", + ANSI_ESC_RED_TEXT, + bist_setup->avlink_randomizer, + ANSI_ESC_RESET_TEXT); + } + switch (bist_setup->avlink_pattern) { + case BIST_AVLINK_PATTERN_UNSPECIFIED: + case BIST_AVLINK_PATTERN_PRBS: + case BIST_AVLINK_PATTERN_FIXED_8: + MHL_TX_DBG_ERR("Start Fixed 8/PRBS!\n"); + mhl_tx_modify_reg(hw_context, REG_BIST_CTRL, + BIT_BIST_START_BIT, BIT_BIST_START_BIT); + break; + + case BIST_AVLINK_PATTERN_FIXED_10: + MHL_TX_DBG_ERR("Start Fixed 10!\n"); + mhl_tx_write_reg(hw_context, + REG_TX_IP_BIST_CNTLSTA, + BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN | + BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL); + break; + + default: + MHL_TX_DBG_ERR("Unrecognized test pattern\n"); + } +#ifdef CoC_FSM_MONITORING + #ifdef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_6, BIT_CTRL1_GPIO_I_6); + #endif +#endif +} +/* + * start_video + * + * + */ +static int start_video(struct drv_hw_context *hw_context) +{ + struct mhl_dev_context *dev_context; + dev_context = get_mhl_device_context(hw_context); + + if (!IN_MHL3_MODE(hw_context)) { + /* + * stop hdcp and video + * this kills any hdcp thread going on already + */ + stop_video(hw_context); + } + + /* + * if path has been disabled by PATH_EN = 0 return with error; + * When enabled, this function will be called again. + * if downstream connection has been lost (CLR_HPD), return with error. + */ + if ((0 == hw_context->video_path) + || (!ok_to_proceed_with_ddc(hw_context)) + || (false == dev_context->misc_flags.flags.rap_content_on) + ) { + return false; + } + + if ( +#ifdef MHL3_DVI_SUPPORT + IN_MHL3_MODE(hw_context) || +#endif + si_edid_sink_is_hdmi( + hw_context->intr_info->edid_parser_context)) { + /* + * setup registers for packed pixel, 3D, colors and AVIF + * using incoming info frames. VSIF sent out by the h/w. + */ + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, + hw_context->rx_hdmi_ctrl2_defval = + REG_RX_HDMI_CTRL2_DEFVAL_HDMI | + VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); + if (false == set_hdmi_params(dev_context)) { + /* Do not disrupt video for bad incoming infoframes */ + return false; + } + + if (IN_MHL3_MODE(hw_context)) { + /* Assert H2M reset */ + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_MHL3_VALUE | + BIT_M3_CTRL_H2M_SWRST); + + /* Deassert H2M reset */ + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_MHL3_VALUE); + } else { + set_auto_zone_for_mhl_1_2(hw_context); + + start_hdcp(hw_context); + } + /* + * TODO: Check if the following block has to be performed + * again at HDCP restart. + * + * Start sending out AVIF now. Also ensure other appropriate + * InfoFrames are being forwarded or dropped. + */ + /* + * Send infoframe out + */ + mhl_tx_write_reg_block(hw_context, REG_TPI_AVI_CHSUM, + sizeof(hw_context->outgoingAviPayLoad.ifData), + hw_context->outgoingAviPayLoad.ifData); + /* + * Change packet filters to drop AIF and GCP. + * TODO: Describe this in the PR appropriately. + * TODO: for MHL3 mode, drop VSI packets and convert + * HDMI VSIF to MHL3 VSIF. + */ + mhl_tx_write_reg(hw_context, REG_PKT_FILTER_0, 0xA5); + /* Value written does not match comment above per values below + * BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT (0x80) + * BIT_PKT_FILTER_0_DROP_MPEG_PKT (0x20) + * BIT_PKT_FILTER_0_DROP_AVI_PKT (0x04) + * BIT_PKT_FILTER_0_DROP_GCP_PKT (0x01) + */ + + if (IN_MHL3_MODE(hw_context)) { + enum info_sel_e { + info_sel_avi = + 0, info_sel_spd, info_sel_audio, + info_sel_mpeg, info_sel_generic, + info_sel_generic2, info_sel_vsi, + info_sel_reserved + }; + uint8_t vsif_buffer[31]; + /* + * disable HDMI to MHL VSIF conversion (do it in + * software) (set bit 7)and drop generic infoframes + * (set bit 1) + */ + mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, + BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | + BIT_PKT_FILTER_1_DROP_GEN_PKT | + BIT_PKT_FILTER_1_DROP_VSIF_PKT); + + mhl_tx_write_reg(hw_context, REG_TPI_INFO_FSEL, + BIT_TPI_INFO_FSEL_TPI_INFO_EN | + BIT_TPI_INFO_FSEL_TPI_INFO_RPT | + info_sel_vsi); + + /* hardware takes effect on the write to TPI_INFO_B30, + and checksum is calculated on just the first part, + so pad the remainder of the buffer + */ + memset(vsif_buffer, 0, sizeof(vsif_buffer)); + switch (hw_context->hpd_high_callback_status) { + case HH_FMT_HDMI_VSIF_MHL3: + case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON: + case HH_FMT_HDMI_VSIF_MHL3_NOT_RPT: + case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_RPT: + memcpy(vsif_buffer, + (uint8_t *) &hw_context-> + vsif_mhl3_or_hdmi_from_callback.mhl3, + sizeof(hw_context-> + vsif_mhl3_or_hdmi_from_callback.mhl3)); + break; + default: + memcpy(vsif_buffer, + (uint8_t *) &hw_context-> + outgoing_mhl3_vsif, + sizeof(hw_context-> + outgoing_mhl3_vsif)); + } + mhl_tx_write_reg_block(hw_context, + REG_TPI_INFO_B0, + sizeof(vsif_buffer), + (uint8_t *) vsif_buffer); + } else { + /* + * enable HDMI to MHL VSIF conversion (clear bit 7) + * and drop generic infoframes (set bit 1) + */ + mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, + BIT_PKT_FILTER_1_DROP_GEN_PKT); + } + } else { + /* For DVI, output video w/o infoframe. + * No video settings are changed. + */ + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, + hw_context->rx_hdmi_ctrl2_defval = + REG_RX_HDMI_CTRL2_DEFVAL_DVI | + VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); + + MHL_TX_DBG_ERR("DVI - Start HDCP\n"); + start_hdcp(hw_context); + } + + return true; +} + +static int hdcp_isr(struct drv_hw_context *hw_context, uint8_t intr_status) +{ + uint8_t query_data; + + query_data = mhl_tx_read_reg(hw_context, REG_TPI_COPP_DATA1); + MHL_TX_DBG_INFO("R3D= %02x R29= %02x\n", intr_status, query_data); + + if (BIT_TPI_INTR_ST0_READ_BKSV_BCAPS_DONE_STAT & intr_status) { + + /* + See 32702 why 200ms was added before enabling + authentication. + */ + msleep(200); + + if (BIT_TPI_COPP_DATA1_COPP_PROTYPE & query_data) { + /* + * If the downstream device is a repeater, enforce a + * 5-second delay to pass HDCP CTS 1B-03. + * TODO: Describe this in the PR. + * Allow the enforcement to exit early if the + * KSV_FIFO_LAST bit gets set, which, implies that + * BCAPS[5] (KSV_FIFO_RDY) is set. This fixes the + * occasional "Inactivity Timer Expired"/"Not Judged" + * result in 1B-02. + */ + if (BIT_TPI_COPP_DATA1_COPP_HDCP_REP & + query_data) { +#ifdef KSV_FIFO_RDY_INTERRUPT + /* Start authentication here */ + mhl_tx_write_reg(hw_context, + REG_TPI_COPP_DATA2, + BIT_TPI_COPP_DATA2_KSV_FORWARD | + VAL_TPI_COPP_PROTLEVEL_MAX); +#else + msleep(SLEEP_10MS); + /* Start authentication here */ + mhl_tx_write_reg(hw_context, + REG_TPI_COPP_DATA2, + VAL_TPI_COPP_PROTLEVEL_MAX); +#endif + } else { + /* Start authentication here */ + mhl_tx_write_reg(hw_context, + REG_TPI_COPP_DATA2, + VAL_TPI_COPP_PROTLEVEL_MAX); + } + + } +#ifdef KSV_FIFO_RDY_INTERRUPT + } else if (BIT_TPI_INTR_ST0_KSV_FIFO_FIRST_STAT & intr_status) { + /* this will happen in repeater cases */ + int ksv_fifo_stat; + int cbus_connected_state; + /* Read out the KSV list */ + do { + int ksv_fifo_bytes; + int dummy = + mhl_tx_read_reg(hw_context, + REG_TPI_DS_BCAPS); + ksv_fifo_stat = + mhl_tx_read_reg(hw_context, + REG_TPI_KSV_FIFO_STAT); + ksv_fifo_bytes = + ksv_fifo_stat & + MSK_TPI_KSV_FIFO_STAT_KSV_FIFO_BYTES; + if (ksv_fifo_bytes) { + int ksv; + ksv = + mhl_tx_read_reg(hw_context, + REG_TPI_KSV_FIFO_FORW); + } + if (BIT_TPI_KSV_FIFO_STAT_KSV_FIFO_LAST & + ksv_fifo_stat) { + break; + } + cbus_connected_state = + mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); + cbus_connected_state &= + (BIT_CBUS_STATUS_CBUS_CONNECTED | + BIT_CBUS_STATUS_CBUS_HPD); + } while ((BIT_CBUS_STATUS_CBUS_CONNECTED | + BIT_CBUS_STATUS_CBUS_HPD) == + cbus_connected_state); + +#endif + } else if (BIT_TPI_INTR_ST0_READ_BKSV_ERR_STAT & intr_status) { + MHL_TX_DBG_WARN("BKSV ERROR - Start HDCP\n"); + start_hdcp(hw_context); + } else if (BIT_TPI_INTR_ST0_TPI_COPP_CHNGE_STAT & intr_status) { + int link_status; + + link_status = + query_data & MSK_TPI_COPP_DATA1_COPP_LINK_STATUS; + + switch (link_status) { + case VAL_TPI_COPP_LINK_STATUS_NORMAL: + /* + * Device asserts this status when authentication is + * turned off. + * Nothing should be done here. + */ + break; + + case VAL_TPI_COPP_LINK_STATUS_LINK_LOST: + MHL_TX_DBG_ERR("LINK LOST - Start HDCP\n"); + start_hdcp(hw_context); + break; + case VAL_TPI_COPP_LINK_STATUS_RENEGOTIATION_REQ: + MHL_TX_DBG_INFO("tpi BSTATUS2: 0x%x\n", + mhl_tx_read_reg(hw_context, + REG_TPI_BSTATUS2) + ); + /* + * Device asserts this status for Ri mismatch and starts + * BKSV READ by itself. All we need to do is turn off + * HDCP Authentication control. + * Once BKSV READ (and BCAPS) concludes we will get + * another interrupt and start authentication. + */ + mhl_tx_modify_reg(hw_context, REG_TPI_SC, + BIT_TPI_SC_TPI_AV_MUTE, + VAL_TPI_SC_TPI_AV_MUTE_MUTED); + + mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, + 0); + break; + case VAL_TPI_COPP_LINK_STATUS_LINK_SUSPENDED: + MHL_TX_DBG_ERR("LINK suspended - Start HDCP\n"); + start_hdcp(hw_context); + break; + } + } else if (BIT_TPI_INTR_ST0_TPI_AUTH_CHNGE_STAT & intr_status) { + uint8_t new_link_prot_level; + + new_link_prot_level = (uint8_t) + (query_data & (BIT_TPI_COPP_DATA1_COPP_GPROT | + BIT_TPI_COPP_DATA1_COPP_LPROT)); + + switch (new_link_prot_level) { + case (VAL_TPI_COPP_GPROT_NONE | VAL_TPI_COPP_LPROT_NONE): + MHL_TX_DBG_ERR("?PROT_NONE - Start HDCP\n"); + start_hdcp(hw_context); + break; + + /* + Device asserts both LPROT and GPROT if DS is a repeater. + We should unmute the video only once. + */ + case VAL_TPI_COPP_GPROT_SECURE: + break; + /* + When both bits are simultaneously set. + */ + case (VAL_TPI_COPP_GPROT_SECURE | VAL_TPI_COPP_LPROT_SECURE): + case VAL_TPI_COPP_LPROT_SECURE: + MHL_TX_DBG_ERR("HDCP 1.x Authentication Done\n"); + unmute_video(hw_context); + break; + } + } + return 0; +} + +static int hdcp2_isr(struct drv_hw_context *hw_context, uint8_t intr_status) +{ + uint8_t rcvr_info[3]; + uint8_t rcvr_id_list[5]; + + if (intr_status & BIT_HDCP2_INTR_AUTH_DONE) { + MHL_TX_DBG_ERR("HDCP2.2 Authentication Done.\n"); + + /* Enable high-value content / disable mute */ + } + + if (intr_status & BIT_HDCP2_INTR_AUTH_FAIL) { + uint8_t ro_gp0; + uint8_t ro_auth[2]; + + /* Disable high-value content / enable mute */ + + ro_gp0 = mhl_tx_read_reg(hw_context, REG_HDCP2X_GP_OUT0); + mhl_tx_read_reg_block(hw_context, REG_HDCP2X_AUTH_STAT, + sizeof(ro_auth), ro_auth); + + MHL_TX_DBG_ERR("HDCP2.2 Authentication Failed" + " - gp0 %02X, status %02X %02X\n", + ro_gp0, ro_auth[0], ro_auth[1]); + } + + if (intr_status & BIT_HDCP2_INTR_RPTR_RCVID_CHANGE) { + MHL_TX_DBG_ERR("HDCP2.2 RCV_ID Changed.\n"); + + /* Read RCVR INFO and RCVR ID LIST */ + mhl_tx_read_reg_block(hw_context, + REG_HDCP2X_RPT_RCVID_OUT, + sizeof(rcvr_info), rcvr_info); + mhl_tx_read_reg_block(hw_context, + REG_HDCP2X_RPT_RCVR_ID0, + sizeof(rcvr_id_list), rcvr_id_list); + } + + mhl_tx_write_reg(hw_context, REG_HDCP2X_INTR0, intr_status); + + return intr_status; +} + +static int int_8_isr(struct drv_hw_context *hw_context, uint8_t intr_8_status) +{ + union vsif_mhl3_or_hdmi_u vsif; + struct avi_info_frame_t avif; + + memset(&vsif, 0, sizeof(vsif)); + /* Clear interrupt status immediately */ + mhl_tx_write_reg(hw_context, REG_INTR8, intr_8_status); + + if (hw_context->hpd_high_callback_status >= 0) { + /* Video is already started. + * Set this flag so that we catch mode changes + */ + + hw_context->hpd_high_callback_status = HH_VIDEO_NOT_RDY; + return intr_8_status; + } + + /* Resolution change interrupt (NEW_AVIF or NEW_VSIF) */ + if ((BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI) & intr_8_status) { + MHL_TX_DBG_WARN("got NEW_VSI\n"); + + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, + hw_context-> + rx_hdmi_ctrl2_defval | + VAL_RX_HDMI_CTRL2_VSI_MON_SEL_VSI); + + mhl_tx_read_reg_block(hw_context, + REG_RX_HDMI_MON_PKT_HEADER1, + sizeof(vsif), (uint8_t *)&vsif); + MHL_TX_DBG_WARN( + "Got vsif: {%02x %02x %02x} %02x %02x%02x%02x %02x %02x\n", + vsif.common.header.type_code, + vsif.common.header.version_number, + vsif.common.header.length, + vsif.common.checksum, + vsif.common.ieee_oui[0], + vsif.common.ieee_oui[1], + vsif.common.ieee_oui[2], + *((uint8_t *) &vsif.hdmi.payLoad.pb4), + vsif.hdmi.payLoad.pb5.HDMI_VIC); +#ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH + /* we rely on this interrupt to tell us when VSIF has gone away + * as well as when it arrives. process_info_frame_change will + * set valid_vsif according to the contents acquired here. + */ + hw_context->valid_vsif = false; +#endif + } + + if (BIT_CEA_NEW_AVI & intr_8_status) { + + MHL_TX_DBG_WARN("got NEW_AVIF\n"); + + /* Read AVIF packet */ + + mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, + hw_context-> + rx_hdmi_ctrl2_defval | + VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); + + mhl_tx_read_reg_block(hw_context, + REG_RX_HDMI_MON_PKT_HEADER1, + sizeof(avif), + (uint8_t *) &avif); + + if (0 == avif.header.type_code) { + MHL_TX_DBG_ERR( + "bogus AVIF: avif.header.type_code is 0\n"); + return intr_8_status; + } + } + + switch (intr_8_status & + (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI)) { + case BIT_CEA_NEW_VSI: + process_info_frame_change(hw_context, &vsif, NULL); + break; + case BIT_CEA_NEW_AVI: + process_info_frame_change(hw_context, NULL, &avif); + break; + case (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI): + process_info_frame_change(hw_context, &vsif, &avif); + break; + } + + return intr_8_status; +} + +static int int_3_isr(struct drv_hw_context *hw_context, uint8_t int_3_status) +{ + if (BIT_DDC_CMD_DONE & int_3_status) { + /* Inform MHL module of manual EDID read completion */ + hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = 0; + /* ensure that no other DDC interrupts occur */ + enable_intr(hw_context, INTR_DDC, 0); + } + return 0; +} + +static int int_9_isr(struct drv_hw_context *hw_context, uint8_t int_9_status) +{ + if (int_9_status) { + mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_EDID].stat_addr, + int_9_status); + if (BIT_INTR9_DEVCAP_DONE & int_9_status) { + hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = 0; + } + + if (BIT_INTR9_EDID_DONE & int_9_status) { + int ddcStatus; + ddcStatus = + mhl_tx_read_reg(hw_context, REG_DDC_STATUS); +#ifdef SWWA_BZ30759 + unfreeze_MHL_connect(hw_context); +#endif + if (BIT_DDC_STATUS_DDC_NO_ACK & ddcStatus) { + + /* Restart EDID read from block 0 */ + hw_context->current_edid_req_blk = 0; + if (!issue_edid_read_request(hw_context, + hw_context->current_edid_req_blk)) { + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = + 1; + MHL_TX_DBG_ERR + ("%sedid request problem%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + } + } else { + int num_extensions; + + MHL_TX_DBG_INFO("EDID block read complete\n"); + num_extensions = + si_mhl_tx_get_num_cea_861_extensions + (hw_context->intr_info->edid_parser_context, + hw_context->current_edid_req_blk); + if (num_extensions < 0) { + MHL_TX_DBG_ERR("edid problem:%d\n", + num_extensions); + if (ne_NO_HPD == num_extensions) { + /* no HPD, so start over */ + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info-> + msc_done_data = 1; + MHL_TX_DBG_ERR("%shpd low%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + } else { + /* + * Restart EDID read + * from block 0 + */ + hw_context-> + current_edid_req_blk = 0; + if (!issue_edid_read_request( + hw_context, + hw_context-> + current_edid_req_blk)) { + /* Notify the component + * layer with error + */ + hw_context->intr_info-> + flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info-> + msc_done_data = 1; + MHL_TX_DBG_ERR("%sedid" + " request problem%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + } + } + } else if (hw_context->current_edid_req_blk < + num_extensions) { + /* EDID read next block */ + if (!issue_edid_read_request( + hw_context, ++hw_context-> + current_edid_req_blk)) { + /* Notify the MHL module with + * error + */ + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info-> + msc_done_data = 1; + MHL_TX_DBG_ERR("%sedid request" + " problem%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + } + } else { + /* Inform MHL module of EDID read MSC + * command completion + */ + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = + 0; + } + } + } + + if (BIT_INTR9_EDID_ERROR & int_9_status) { +#ifdef SWWA_BZ30759 + unfreeze_MHL_connect(hw_context); +#endif + + MHL_TX_DBG_INFO("EDID read error, retrying\n"); + + hw_context->edid_fifo_block_number = 0; + hw_context->current_edid_req_blk = 0; + + if (ok_to_proceed_with_ddc(hw_context)) { + + /* Restart EDID read from block 0 */ + hw_context->current_edid_req_blk = 0; + if (!issue_edid_read_request(hw_context, + hw_context->current_edid_req_blk)) { + /* Notify the MHL module of error */ + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = + 1; + } + } else { + /* Notify the MHL module with error */ + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = 1; + } + } + } + return int_9_status; +} + +void si_mhl_tx_read_devcap_fifo(struct drv_hw_context *hw_context, + union MHLDevCap_u *dev_cap_buf) +{ + MHL_TX_DBG_INFO("called\n"); + + /* Enable EDID interrupt */ + enable_intr(hw_context, INTR_EDID, + (BIT_INTR9_DEVCAP_DONE_MASK + | BIT_INTR9_EDID_DONE_MASK | BIT_INTR9_EDID_ERROR)); + + /* choose devcap instead of EDID to appear at the FIFO */ + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | + VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | + VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); + + /* Retrieve the DEVCAP register values from the FIFO */ + mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, + DEVCAP_SIZE, dev_cap_buf->devcap_cache); + MHL_TX_DBG_INFO("%sgot DEVCAP%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); +} + +void si_mhl_tx_read_xdevcap_fifo(struct drv_hw_context *hw_context, + union MHLXDevCap_u *xdev_cap_buf) +{ + MHL_TX_DBG_INFO("called\n"); + + /* choose XDEVCAP instead of EDID to appear at the FIFO */ + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | + BIT_EDID_CTRL_XDEVCAP_EN | + VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | + VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); + + /* Retrieve the XDEVCAP register values from the FIFO */ + mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, + XDEVCAP_OFFSET(XDEVCAP_LIMIT), + xdev_cap_buf->xdevcap_cache); + + MHL_TX_DBG_INFO("%sgot XDEVCAP%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); +} + +static int mhl_cbus_err_isr(struct drv_hw_context *hw_context, + uint8_t cbus_err_int) +{ + int ret_val = 0; + uint8_t msc_abort_reason = 0; + + /* + * Three possible errors could be asserted. + * 94[2] = DDC_ABORT + * 94[3] = ABORT while receiving a command - MSC_RCV_ERROR + * The ABORT reasons are in 9A + * 94[6] = ABORT while sending a command - MSC_SEND_ERROR + * The ABORT reasons are in 9C + */ +#ifdef PRINT_DDC_ABORTS + if (cbus_err_int & BIT_CBUS_DDC_ABORT) { + uint8_t ddc_abort_reason = 0; + /* + * For DDC ABORTs, options are + * 1. reset DDC block. This will hamper a broken HDCP or EDID. + * 2. if error persists, reset the chip. In any case video is + * not working. So it should not cause blinks. + * In either case, call an API to let SoC know what happened. + * + * Only option 1 has been implemented here. + */ + + ddc_abort_reason = mhl_tx_read_reg(hw_context, + REG_DDC_ABORT_INT); + + MHL_TX_DBG_INFO("CBUS DDC ABORT. Reason = %02X\n", + ddc_abort_reason); + + if (DDC_ABORT_THRESHOLD < ++ddc_abort_count) { + ddc_abort_count = 0; + } else if (MHL_READ_EDID_BLOCK == + hw_context->current_cbus_req.command) { + hw_context->intr_info->flags |= DRV_INTR_FLAG_MSC_DONE; + /* setting 1 indicates there was an error */ + hw_context->intr_info->msc_done_data = 1; + } + } +#endif + if (cbus_err_int & BIT_CBUS_MSC_ABORT_RCVD) { + /* + * For MSC Receive time ABORTs + * Defer submission of new commands by 2 seconds per MHL spec. + * This is not even worth reporting to SoC. + * Action is in the hands of peer. + */ + hw_context->intr_info->flags |= DRV_INTR_CBUS_ABORT; + + msc_abort_reason = mhl_tx_read_reg(hw_context, + REG_MSC_MR_ABORT_INT); + + ++msc_abort_count; + + MHL_TX_DBG_ERR("#%d: ABORT during MSC RCV. Reason = %02X\n", + msc_abort_count, msc_abort_reason); + } + + if (cbus_err_int & BIT_CBUS_CMD_ABORT) { + /* + * Defer submission of new commands by 2 seconds per MHL spec + * + * For API operations such as RCP/UCP etc., report the + * situation to SoC and let the decision be there. Internal + * retries have been already done. + */ + hw_context->intr_info->flags |= DRV_INTR_CBUS_ABORT; + + msc_abort_reason = mhl_tx_read_reg(hw_context, + REG_MSC_MT_ABORT_INT); + + MHL_TX_DBG_ERR("CBUS ABORT during MSC SEND. Reason = %02X\n", + msc_abort_reason); + + mhl_tx_write_reg(hw_context, REG_MSC_MT_ABORT_INT, + msc_abort_reason); + } + /* + * Print the reason for information + */ + if (msc_abort_reason) { + if (BIT_CBUS_MSC_MT_ABORT_INT_MAX_FAIL & msc_abort_reason) + MHL_TX_DBG_ERR("Retry threshold exceeded\n"); + if (BIT_CBUS_MSC_MT_ABORT_INT_PROTO_ERR & msc_abort_reason) + MHL_TX_DBG_ERR("Protocol Error\n"); + if (BIT_CBUS_MSC_MT_ABORT_INT_TIMEOUT & msc_abort_reason) + MHL_TX_DBG_ERR("Translation layer timeout\n"); + if (BIT_CBUS_MSC_MT_ABORT_INT_UNDEF_CMD & msc_abort_reason) + MHL_TX_DBG_ERR("Undefined opcode\n"); + if (BIT_CBUS_MSC_MT_ABORT_INT_MSC_MT_PEER_ABORT & + msc_abort_reason) + MHL_TX_DBG_ERR("MSC Peer sent an ABORT\n"); + } + return ret_val; +} + +static void process_mhl3_dcap_rdy(struct drv_hw_context *hw_context) +{ + bool peer_is_mhl3 = 0; + /* + * Peer device is MHL3.0 or + * newer. Enable DEVCAP_X and + * STATUS_X operations + */ + switch (hw_context->cbus_mode) { + case CM_oCBUS_PEER_VERSION_PENDING: + hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL3; + peer_is_mhl3 = 1; + break; + case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: + hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL3_BIST_SETUP; + + peer_is_mhl3 = 1; + break; + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + peer_is_mhl3 = 1; + break; + case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: + hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL3_BIST_STAT; + + peer_is_mhl3 = 1; + break; + default: + break; + } + if (peer_is_mhl3) { + MHL_TX_DBG_ERR("%sdownstream device supports MHL3.0+%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + + mhl_tx_write_reg(hw_context, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); + si_set_cbus_mode_leds(hw_context->cbus_mode); + /* + * Now that we have positively + * identified the MHL version + * of the peer, we re-evaluate + * our settings. + */ + mhl3_specific_init(hw_context); + } + +} +/* + * mhl_cbus_isr + * + * Only when MHL connection has been established. This is where we have the + * first looks on the CBUS incoming commands or returned data bytes for the + * previous outgoing command. + * + * It simply stores the event and allows application to pick up the event + * and respond at leisure. + * + * return values: + * 0 - MHL interrupts (all of them) have been cleared + * - calling routine should exit + * 1 - MHL interrupts (at least one of them) may not have been cleared + * - calling routine should proceed with interrupt processing. + */ +static int mhl_cbus_isr(struct drv_hw_context *hw_context, uint8_t cbus_int) +{ + + /* some times a sink will send a CBUS1 message before we get + the TDM_SYNC interrupt. If we were in the calibrated + state, then ignore this interrupt until we are calibrated. + */ + switch (hw_context->cbus_mode) { + case CM_eCBUS_S_AV_BIST: + case CM_eCBUS_D_AV_BIST: + /* + * Only allow BIST_STOP (MSC_MSG) and command done + * in BIST mode + */ + mhl_tx_write_reg(hw_context, REG_CBUS_INT_0, + cbus_int & + ~(BIT_CBUS_MSC_MR_MSC_MSG | BIT_CBUS_MSC_MT_DONE)); + cbus_int &= (BIT_CBUS_MSC_MR_MSC_MSG | BIT_CBUS_MSC_MT_DONE); + break; + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: + case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: + MHL_TX_DBG_ERR("%sCBUS1 message received cbus_int:0x%02x " + "hpd status: 0x%02x%s\n", + ANSI_ESC_YELLOW_TEXT, + cbus_int, + mhl_tx_read_reg(hw_context, REG_CBUS_STATUS), + ANSI_ESC_RESET_TEXT); + return -1; + default: + break; + } + + if (CBUS1_IDLE_RCV_PEND == hw_context->cbus1_state) { + /* don't process these interrupts until we exit + the cbus1_idle_rcv_pending state */ + return -1; + } + + if (cbus_int & ~BIT_CBUS_HPD_CHG) { + /* bugzilla 27396 + * Logic to detect missed HPD interrupt. + * Do not clear BIT_CBUS_INT_0_CBUS_INT_0_STAT2 yet. + */ + mhl_tx_write_reg(hw_context, REG_CBUS_INT_0, + cbus_int & ~BIT_CBUS_HPD_CHG); + } + + if (BIT_CBUS_HPD_CHG & cbus_int) { + uint8_t cbus_status; + uint8_t status; + + /* Check if a SET_HPD came from the downstream device. */ + cbus_status = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); + status = cbus_status & BIT_CBUS_STATUS_CBUS_HPD; + + if (BIT_CBUS_STATUS_CBUS_HPD & + (hw_context->cbus_status ^ cbus_status)) { + /* bugzilla 27396 + * No HPD interrupt has been missed yet. + * Clear BIT_CBUS_INT_0_CBUS_INT_0_STAT2. + */ + mhl_tx_write_reg(hw_context, REG_CBUS_INT_0, + BIT_CBUS_INT_0_CBUS_INT_0_STAT2); + MHL_TX_DBG_INFO("HPD change\n"); + } else { + MHL_TX_DBG_ERR("missed HPD change\n"); + + /* leave the BIT_CBUS_INT_0_CBUS_INT_0_STAT2 + * interrupt uncleared, so that we get another interrupt + */ + /* whatever was missed is the inverse of what we got */ + status ^= BIT_CBUS_STATUS_CBUS_HPD; + cbus_status ^= BIT_CBUS_STATUS_CBUS_HPD; + } + + MHL_TX_DBG_INFO("DS HPD changed to %02X\n", status); + + hw_context->intr_info->flags |= DRV_INTR_HPD_CHANGE; + hw_context->intr_info->hpd_status = status; + + if (0 == status) { + struct mhl_dev_context *dev_context; + dev_context = get_mhl_device_context(hw_context); + mhl_tx_stop_timer(dev_context, + hw_context->input_field_rate_measurement_timer); + MHL_TX_DBG_ERR("%sgot CLR_HPD%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + /* HW edid fifo state DOES NOT get reset on CLR_HPD */ + if (MHL_SEND_3D_REQ_OR_FEAT_REQ == + hw_context->current_cbus_req.command) { + + hw_context->current_cbus_req.command = 0x00; + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = 1; + } else if (MHL_READ_EDID_BLOCK == + hw_context->current_cbus_req.command) { + hw_context->current_cbus_req.command = 0x00; + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + /* setting 1 indicates there was an error */ + hw_context->intr_info->msc_done_data = 1; + } + /* todo: investigate if this call should be after the + * call to stop_video() + */ + drive_hpd_low(hw_context); + hw_context->current_edid_req_blk = 0; + + /* default values for video */ + hw_context->video_ready = false; + hw_context->video_path = 1; + + /* + * This cannot wait for the upper layer to notice + * DRV_INTR_FLAG_HPD_CHANGE. + * stop_video relies on the result. + */ + si_edid_reset(dev_context->edid_parser_context); + + /* if DS sent CLR_HPD ensure video is not there */ + stop_video(hw_context); + } else { + MHL_TX_DBG_ERR("%sGot SET_HPD%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + } + + hw_context->cbus_status = cbus_status; + } + + if (BIT_CBUS_MSC_MT_DONE_NACK & cbus_int) { + MHL_TX_DBG_ERR("%sGot MSC_MT_DONE_NACK%s\n", ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + hw_context->intr_info->flags |= DRV_INTR_MSC_NAK; + } + + if (BIT_CBUS_MSC_MR_WRITE_STAT & cbus_int) { + /* read status bytes */ + mhl_tx_read_reg_block(hw_context, REG_MHL_STAT_0, + ARRAY_SIZE(hw_context->intr_info->dev_status. + write_stat), + hw_context->intr_info->dev_status.write_stat); + /* read xstatus bytes */ + mhl_tx_read_reg_block(hw_context, REG_MHL_EXTSTAT_0, + ARRAY_SIZE(hw_context->intr_info->dev_status. + write_xstat), + hw_context->intr_info->dev_status.write_xstat); + MHL_TX_DBG_WARN( + "%sGot WRITE_STAT: " + "%02x %02x %02x : %02x %02x %02x %02x%s\n", + ANSI_ESC_GREEN_TEXT, + hw_context->intr_info->dev_status.write_stat[0], + hw_context->intr_info->dev_status.write_stat[1], + hw_context->intr_info->dev_status.write_stat[2], + hw_context->intr_info->dev_status.write_xstat[0], + hw_context->intr_info->dev_status.write_xstat[1], + hw_context->intr_info->dev_status.write_xstat[2], + hw_context->intr_info->dev_status.write_xstat[3], + ANSI_ESC_RESET_TEXT); + if (hw_context->intr_info->dev_status.write_stat[2] >= 0x30) { + hw_context->mhl_peer_version_stat = + hw_context->intr_info->dev_status.write_stat[2]; + } +#ifdef FORCE_OCBUS_FOR_ECTS + /* This compile option is always enabled. + * It is intended to help identify code deletion by adopters + * who do not need this feauture. The control for forcing oCBUS + * works by using module parameter below. Peer version is forced + * to 2.0 allowing 8620 to treat the sink as if it is MHL 2.0 + * device and as a result never switch cbus to MHL3 eCBUS. + */ + { + /* todo: what if the sink/dongle is MHL1.x? */ + if (force_ocbus_for_ects) { + hw_context->mhl_peer_version_stat = + hw_context->intr_info->dev_status. + write_stat[2] = 0x20; + } + } +#endif + if (MHL_STATUS_DCAP_RDY & hw_context->intr_info->dev_status. + write_stat[0]) { + MHL_TX_DBG_INFO("DCAP_RDY in effect\n"); + + if (hw_context->mhl_peer_version_stat >= 0x30) { + if (MHL_STATUS_XDEVCAPP_SUPP & + hw_context->intr_info->dev_status. + write_stat[0]) + process_mhl3_dcap_rdy(hw_context); + } + + /* after the above does not indicate MHL3, + * then it's MHL1.x or MHL2.x + */ + if (CM_oCBUS_PEER_VERSION_PENDING == + hw_context->cbus_mode) { + /* + * Initialize registers to operate in oCBUS mode + */ + hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL1_2; + si_mhl_tx_drv_switch_cbus_mode(hw_context, + CM_oCBUS_PEER_IS_MHL1_2); + si_set_cbus_mode_leds(hw_context->cbus_mode); + } + + /* Enable EDID interrupt */ + enable_intr(hw_context, INTR_EDID, + (BIT_INTR9_DEVCAP_DONE_MASK | + BIT_INTR9_EDID_DONE_MASK | + BIT_INTR9_EDID_ERROR)); + } + /* + * Save received write_stat info for later + * post interrupt processing + */ + hw_context->intr_info->flags |= DRV_INTR_WRITE_STAT; + } + + if ((BIT_CBUS_MSC_MR_MSC_MSG & cbus_int)) { + /* + * Save received MSC message info for later + * post interrupt processing + */ + hw_context->intr_info->flags |= DRV_INTR_MSC_RECVD; + mhl_tx_read_reg_block(hw_context, + REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, + ARRAY_SIZE(hw_context->intr_info->msc_msg), + hw_context->intr_info->msc_msg); + + MHL_TX_DBG_INFO("MSC MSG: %02X %02X\n", + hw_context->intr_info->msc_msg[0], + hw_context->intr_info->msc_msg[1]); + } + + /* + * don't do anything for a scratch pad write received interrupt. + * instead wait for the DSCR_CHG interrupt + */ + if (BIT_CBUS_MSC_MR_SET_INT & cbus_int) { + MHL_TX_DBG_WARN("MHL INTR Received\n"); + + /* + * Save received SET INT message info for later + * post interrupt processing + */ + hw_context->intr_info->flags |= DRV_INTR_SET_INT; + mhl_tx_read_reg_block(hw_context, REG_MHL_INT_0, + ARRAY_SIZE(hw_context->intr_info->int_msg), + hw_context->intr_info->int_msg); + /* clear the individual SET_INT bits */ + mhl_tx_write_reg_block(hw_context, REG_MHL_INT_0, + ARRAY_SIZE(hw_context->intr_info->int_msg), + hw_context->intr_info->int_msg); + + if (MHL_INT_EDID_CHG & hw_context->intr_info->int_msg[1]) { + int reg_val; + + MHL_TX_DBG_INFO("%sgot EDID_CHG%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + + /* clear this bit so that + * BIT_TPI_INFO_FSEL_TPI_INFO_EN + * will get cleared by the h/w + */ + mhl_tx_modify_reg(hw_context, REG_TPI_INFO_FSEL, + BIT_TPI_INFO_FSEL_TPI_INFO_RPT, 0); + + /* MHL module will re-read EDID */ + drive_hpd_low(hw_context); + stop_video(hw_context); + + /* prevent HDCP interrupts from coming in */ + reg_val = mhl_tx_read_reg(hw_context, REG_LM_DDC); + MHL_TX_DBG_INFO("REG_LM_DDC:%02x\n", reg_val); + reg_val &= ~BIT_LM_DDC_SW_TPI_EN; + reg_val |= VAL_LM_DDC_SW_TPI_EN_DISABLED; + mhl_tx_write_reg(hw_context, REG_LM_DDC, reg_val); + + /* SetTPIMode */ + MHL_TX_DBG_INFO("REG_LM_DDC:%02x\n", reg_val); + reg_val &= ~BIT_LM_DDC_SW_TPI_EN; + reg_val |= VAL_LM_DDC_SW_TPI_EN_ENABLED; + mhl_tx_write_reg(hw_context, REG_LM_DDC, reg_val); + + /* + * Clear HDCP interrupt. + * Due to TPI enable, we may get one. + * TODO: Document this in the PR. + */ + mhl_tx_write_reg(hw_context, + g_intr_tbl[INTR_HDCP].stat_addr, 0xFF); + } else if (MHL_INT_DSCR_CHG & + hw_context->intr_info->int_msg[0]) { + MHL_TX_DBG_WARN("got DSCR_CHG\n"); + if (hw_context->gen2_write_burst_rcv) { + /* this is NOT expected */ + MHL_TX_DBG_ERR( + "Ignored DSCR_CHG " + "since MDT is enabled\n"); + } else { + mhl_tx_read_reg_block(hw_context, + REG_MHL_SCRPAD_0, + ARRAY_SIZE(hw_context-> + write_burst_data), + hw_context->write_burst_data); + } + } else if (MHL_INT_DCAP_CHG & + hw_context->intr_info->int_msg[0]) { + MHL_TX_DBG_WARN("got DCAP_CHG\n"); + } else if (MHL_INT_REQ_WRT & + hw_context->intr_info->int_msg[0]) { + MHL_TX_DBG_WARN("got REQ_WRT\n"); + } + } + if (BIT_CBUS_MSC_MT_DONE & cbus_int) { + bool completed = true; + MHL_TX_DBG_INFO("MSC_REQ_DONE,0x%02x(0x%02x,0x%02x)\n", + hw_context->current_cbus_req.command, + hw_context->current_cbus_req.reg, + hw_context->current_cbus_req.reg_data); + + if (MHL_SET_INT == hw_context->current_cbus_req.command) { + if (MHL_RCHANGE_INT == + hw_context->current_cbus_req.reg) { + if (MHL2_INT_3D_REQ & hw_context-> + current_cbus_req.reg_data) { + + MHL_TX_DBG_WARN("3D_REQ complete\n"); + hw_context->cbus1_state = + CBUS1_IDLE_RCV_ENABLED; + } + if (MHL_INT_GRT_WRT & hw_context-> + current_cbus_req.reg_data) { + + MHL_TX_DBG_WARN("GRT_WRT complete\n"); + hw_context->cbus1_state = + CBUS1_IDLE_RCV_ENABLED; + } + } + } else if (MHL_SEND_3D_REQ_OR_FEAT_REQ == + hw_context->current_cbus_req.command) { + /* if this command completes before we can enable HAWB, + * control will come here. We want to hold off other + * CBUS activity until the peer finishes sending 3D_DTD + * and 3D_VIC bursts. si_mhl_tx_msc_command_done will + * be called from si_mhl_tx_drv_set_upstream_edid which + * is called by the upper layer. + */ + completed = false; + hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; + } + if (completed) { + hw_context->current_cbus_req.command = 0x00; + hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = + mhl_tx_read_reg(hw_context, + REG_MSC_MT_RCVD_DATA0); + } + } + return -1; +} + +static int int_5_isr(struct drv_hw_context *hw_context, uint8_t int_5_status) +{ + int ret_val = 0; + + if (int_5_status & BIT_INTR_SCDT_CHANGE) { + uint8_t temp; + temp = mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); + + if (BIT_TMDS_CSTAT_P3_SCDT & temp) { + enum bist_cmd_status bcs; + struct mhl_dev_context *dev_context; + dev_context = get_mhl_device_context(hw_context); + MHL_TX_DBG_ERR("%sGot SCDT HIGH%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + switch (hw_context->cbus_mode) { + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + + MHL_TX_DBG_ERR("\n") + if (dev_context-> + misc_flags.flags.bist_role_TE) { + MHL_TX_DBG_ERR( + "%sissuing BIST_SETUP%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + bcs = si_mhl_tx_bist_setup(dev_context, + &dev_context->bist_setup); + } else { + MHL_TX_DBG_ERR( + "%sissuing BIST_READY%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + send_bist_ready(dev_context); + } + break; + default: + MHL_TX_DBG_INFO("%s\n", + si_mhl_tx_drv_get_cbus_mode_str( + hw_context->cbus_mode)) +#ifdef MHL3_DVI_SUPPORT +#else + /* + * enable infoframe interrupt + */ + enable_intr(hw_context, INTR_INFR, + BIT_CEA_NEW_AVI | BIT_CEA_NEW_VSI); +#endif + + if (IN_MHL3_MODE(hw_context) || + si_edid_sink_is_hdmi(hw_context-> + intr_info->edid_parser_context)) { +#ifdef MHL3_DVI_SUPPORT + uint8_t src_signal_hdmi; +#endif + + mhl_tx_write_reg(hw_context, REG_TPI_SC, + VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + +#ifdef MHL3_DVI_SUPPORT +#define HDMI_MODE BIT_RX_HDMI_CTRL0_RX_HDMI_HDMI_MODE + /* query the Rx for DVI vs. HDMI */ + src_signal_hdmi = HDMI_MODE & + mhl_tx_read_reg(hw_context, + REG_RX_HDMI_CTRL0); + if (src_signal_hdmi) { + MHL_TX_DBG_ERR( + "source signal HDMI\n"); +#endif + /* + * enable infoframe interrupt + */ + enable_intr(hw_context, + INTR_INFR, + BIT_CEA_NEW_AVI | + BIT_CEA_NEW_VSI); +#ifdef MHL3_DVI_SUPPORT + } else { + MHL_TX_DBG_ERR( + "source signal DVI\n"); + start_video(hw_context); + } +#endif + } else { + if (hw_context-> + hpd_high_callback_status >= 0) { + /* Video is already started. + * Set this flag so that we + * catch mode changes + */ + + hw_context-> + hpd_high_callback_status = + HH_VIDEO_NOT_RDY; + return ret_val; + } + /* + * output video here for DVI or + * if a VIC is in hand for HDMI + */ + start_video(hw_context); + } + MHL_TX_DBG_INFO("actual_mode: %s0x%02x%s\n", + ANSI_ESC_YELLOW_TEXT, + mhl_tx_read_reg(hw_context, + REG_RX_HDMI_CTRL0), + ANSI_ESC_RESET_TEXT); + } + } else { + struct mhl_dev_context *dev_context; + dev_context = get_mhl_device_context(hw_context); + mhl_tx_stop_timer(dev_context, + hw_context->input_field_rate_measurement_timer); + MHL_TX_DBG_WARN("%sGot SCDT LOW%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + + /* Clear all InfoFrame info which is now stale. */ + mhl_tx_write_reg(hw_context, REG_TMDS_CSTAT_P3, + BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE | + BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR); + +#ifndef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH + hw_context->valid_vsif = false; + + memset(&hw_context->current_vsif, 0, + sizeof(hw_context->current_vsif)); + memset(&hw_context->current_avi_info_frame, 0, + sizeof(hw_context->current_avi_info_frame)); +#endif + + stop_video(hw_context); + /* Disable infoframe interrupt */ + enable_intr(hw_context, INTR_INFR, 0); + } + } + return ret_val; +} + +#ifdef USE_HW_TIMER +static int int_1_isr(struct drv_hw_context *hw_context, uint8_t int_1_status) +{ + if (int_1_status & BIT_HW_TIMER_POP) { + MHL_TX_DBG_INFO("Timer Pop\n"); + + /* Check timer flag(s) and process them here */ + } + + mhl_tx_write_reg(hw_context, REG_INTR1, 0x80); + + return 0; +} +#endif +#define BIT_0072_SW_INTR 0x04 +static int int_2_isr(struct drv_hw_context *hw_context, uint8_t int_2_status) +{ + if (BIT_0072_SW_INTR & int_2_status) { + MHL_TX_DBG_ERR("enabling RGND\n"); + /* Keep only RGND interrupt enabled for 8620 */ + enable_intr(hw_context, INTR_DISC, BIT_RGND_READY_INT); + wait_for_user_intr = 0; + } + return 0; +} + +/* + get_device_id + returns chip Id + */ +int get_device_id(struct drv_hw_context *hw_context) +{ + int ret_val; + uint16_t number; + + ret_val = mhl_tx_read_reg(hw_context, REG_DEV_IDH); + if (ret_val < 0) { + MHL_TX_DBG_ERR("I2C error 0x%x\n", ret_val); + return ret_val; + } + number = ret_val << 8; + + ret_val = mhl_tx_read_reg(hw_context, REG_DEV_IDL); + if (ret_val < 0) { + MHL_TX_DBG_ERR("I2C error 0x%x\n", ret_val); + return ret_val; + } + ret_val |= number; + + return ret_val; +} + +/* + get_device_rev + returns chip revision + */ +static int get_device_rev(struct drv_hw_context *hw_context) +{ + int ret_val; + + ret_val = mhl_tx_read_reg(hw_context, REG_DEV_REV); + if (ret_val < 0) { + MHL_TX_DBG_ERR("I2C error\n"); + ret_val = -1; + } + + return ret_val; +} + +/* + * clear_and_disable_on_disconnect + */ +static void clear_and_disable_on_disconnect(struct drv_hw_context *hw_context) +{ + uint8_t intr_num; + struct mhl_dev_context *dev_context; + dev_context = get_mhl_device_context(hw_context); + mhl_tx_stop_timer(dev_context, + hw_context->input_field_rate_measurement_timer); + /* clear and mask all interrupts */ + for (intr_num = 0; intr_num < MAX_INTR; intr_num++) { + if (INTR_DISC == intr_num) { + /* clear discovery interrupts except MHL_EST */ + mhl_tx_write_reg(hw_context, + g_intr_tbl[INTR_DISC].stat_addr, + ~BIT_RGND_READY_INT); + if (wait_for_user_intr) { + /* Keep only USER interrupt enabled */ + enable_intr(hw_context, INTR_DISC, 0); + } else { + /* Keep only RGND interrupt enabled */ + enable_intr(hw_context, INTR_DISC, + BIT_RGND_READY_INT); + } + } else { + /* Clear and disable all other interrupts */ + mhl_tx_write_reg(hw_context, + g_intr_tbl[intr_num].stat_addr, 0xFF); + if (INTR_USER == intr_num) { + if (wait_for_user_intr) { + /* Keep only USER interrupt enabled */ + enable_intr(hw_context, INTR_USER, + BIT_0072_SW_INTR); + } else { + enable_intr(hw_context, INTR_USER, + 0x00); + } + } else { + enable_intr(hw_context, intr_num, 0x00); + } + } + } +} + +/* + * switch_to_idle + * This function performs s/w as well as h/w state transitions. + */ +static void switch_to_idle(struct drv_hw_context *hw_context, + bool do_interrupt_clear) +{ + if (do_interrupt_clear) + clear_and_disable_on_disconnect(hw_context); + + /* + Adjust RGND vbias threshold and calibration resistance. + CBUS driver strength is maintained at POR default of "Strong". + */ + mhl_tx_write_reg(hw_context, REG_MHL_CBUS_CTL0, + (VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_STRONG | + VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_734)); + + mhl_tx_write_reg(hw_context, REG_MHL_CBUS_CTL1, + VAL_MHL_CBUS_CTL1_1115_OHM); + /* + * Leave just enough of the transmitter powered up + * to detect connections. + * i.e. disable the following: + * BIT_DPD_PDNRX12, BIT_DPD_PWRON_HSIC, + * BIT_DPD_PDIDCK_N, BIT_DPD_PD_MHL_CLK_N + */ + mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL + | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); +} + +static void cbus_reset(struct drv_hw_context *hw_context) +{ + MHL_TX_DBG_WARN("Perform CBUS reset to clean MHL STAT values\n"); + mhl_tx_write_reg(hw_context, REG_PWD_SRST, + BIT_PWD_SRST_CBUS_RST | + BIT_PWD_SRST_CBUS_RST_SW_EN); + mhl_tx_write_reg(hw_context, REG_PWD_SRST, + BIT_PWD_SRST_CBUS_RST_SW_EN); + +#ifdef SWWA_BZ30759 + /* switch away from connector wires using 6051 */ + set_pin(X02_USB_SW_CTRL, 0); + platform_mhl_tx_hw_reset(TX_HW_RESET_PERIOD, TX_HW_RESET_DELAY); + /* Power up the device */ + mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL + | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); + + /* Based on module parameter "crystal_khz=xxxxx" program registers. */ + program_ext_clock_regs(hw_context, crystal_khz); + + /* switch back to connector wires using 6051 */ + set_pin(X02_USB_SW_CTRL, 1); +#endif +#ifdef CoC_FSM_MONITORING + /* Begin enable CoC FSM monitoring */ + { +#define REG_COC_MISC_CTL0 (TX_PAGE_7 | 0x28) + mhl_tx_modify_reg(hw_context, REG_COC_MISC_CTL0, 0x80, 0x80); + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_7 | BIT_CTRL1_GPIO_OEN_7 | + BIT_CTRL1_GPIO_I_6 | BIT_CTRL1_GPIO_OEN_6, + 0); + + } + /* End enable CoC FSM monitoring */ +#endif +} + +/* + * disconnect_mhl + * This function performs s/w as well as h/w state transitions. + */ +static void disconnect_mhl(struct drv_hw_context *hw_context, + bool do_interrupt_clear) +{ + disable_gen2_write_burst_rcv(hw_context); + disable_gen2_write_burst_xmit(hw_context); + + stop_video(hw_context); + MHL_TX_DBG_WARN("STOP_VIDEO DONE\n"); + + mhl_tx_vbus_control(VBUS_OFF); + mhl_tx_vbus_current_ctl(I_VBUS_PRE_DISCOVERY); + /* Meet an MHL CTS timing - Tsrc:cbus_float. + * Must do before resetting CBUS + * Fixes failing cases 3.3.14.1, 3.3.14.3, 3.3.22.2 + */ + msleep(50); + cbus_reset(hw_context); + clear_auto_zone_for_mhl_3(hw_context); + + mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x40); + mhl_tx_write_reg(hw_context, REG_CBUS3_CNVT, 0x84); + mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x00); + mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x40); + mhl_tx_write_reg(hw_context, REG_HRXCTRL3, 0x07); +/* mhl_tx_write_reg(hw_context, REG_CBUS3_CNVT, 0x84); */ + + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, + (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | + BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL | + BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); + + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xC0); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL1, 0xBB); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL3, 0x48); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3F); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2F); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL6, 0x2A); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL7, 0x08); + + MHL_TX_DBG_WARN("cbus_mode: %s\n", + si_mhl_tx_drv_get_cbus_mode_str(hw_context->cbus_mode)) + switch (hw_context->cbus_mode) { + case CM_NO_CONNECTION_BIST_STAT: + case CM_eCBUS_S_BIST: + case CM_eCBUS_S_AV_BIST: + case CM_eCBUS_D_BIST: + case CM_eCBUS_D_AV_BIST: + case CM_BIST_DONE_PENDING_DISCONNECT: + hw_context->cbus_mode = CM_NO_CONNECTION_BIST_STAT; + break; + case CM_NO_CONNECTION_BIST_SETUP: + case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: + case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: + hw_context->cbus_mode = CM_NO_CONNECTION_BIST_SETUP; + break; + case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: + case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: + default: + hw_context->cbus_mode = CM_NO_CONNECTION; + } + /* restore default value after BIST */ + mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x06); + hw_context->mhl_peer_version_stat = 0; + si_set_cbus_mode_leds(hw_context->cbus_mode); + + /* Pull the upstream HPD line low to prevent EDID and HDCP activity. */ + drive_hpd_low(hw_context); + + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_PEER_VERSION_PENDING_VALUE); + + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0x07); + + /* For proper MHL discovery and tolerance of impedance + * measurement, set 5E3[7:4]=0x01 - this turns off the + * CBUS pull up for discovery states and 20K for IDLE + * state before impedance is measured. Fixes CBUS CTS + * cases that measure +/-20% tolerance of 1K MHL + * impedance, such as 3.3.5.1, 3.3.5.5, 3.3.6.4 + */ + mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, 0x10); + mhl_tx_write_reg(hw_context, REG_DISC_CTRL8, 0x00); + + /* DO NOT enable wake/discovery pulses until successful RGND == 1K */ + mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, + BIT_DISC_CTRL9_WAKE_DRVFLT | + BIT_DISC_CTRL9_WAKE_PULSE_BYPASS); + + /* Disable MSC heartbeat */ + disable_heartbeat(hw_context); + + /* Set up discovery registers to attempt oCBUS discovery */ + mhl_tx_write_reg(hw_context, REG_DISC_CTRL1, 0x25); + if (do_interrupt_clear) + clear_and_disable_on_disconnect(hw_context); + + /* clear this flag to fix DS hot plug issue */ + hw_context->cbus_status = 0; + hw_context->current_cbus_req.command = 0x00; + hw_context->hawb_write_pending = false; + hw_context->cbus1_state = CBUS1_IDLE_RCV_DISABLED; +} + +/* + * int_4_isr + * MHL device discovery interrupt handler + * 1. When impedance is measured as 1k, RGND interrupt is asserted. + * 2. Chip sends out wake pulses and discovery pulses. + * Then asserts MHL_EST if CBUS stays high to meet MHL timings. + * 3. If discovery fails, NON_MHL_EST is asserted. + * 4. If MHL cable is removed, CBUS_DIS is asserted. + * (Need to check this bit all the time) + */ +static int int_4_isr(struct drv_hw_context *hw_context, uint8_t int_4_status) +{ + int ret_val = 0; + + MHL_TX_DBG_WARN("cbus_mode: %s\n", + si_mhl_tx_drv_get_cbus_mode_str(hw_context->cbus_mode)) + if ((BIT_CBUS_MHL12_DISCON_INT & int_4_status) || + (BIT_CBUS_MHL3_DISCON_INT & int_4_status) || + (BIT_NOT_MHL_EST_INT & int_4_status)) { + + MHL_TX_DBG_ERR("%sGot CBUS_DIS. MHL disconnection%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + switch (hw_context->cbus_mode) { + case CM_TRANSITIONAL_TO_eCBUS_S_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D_BIST: + case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: + case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: + case CM_eCBUS_S_BIST: + case CM_eCBUS_D_BIST: + case CM_BIST_DONE_PENDING_DISCONNECT: + case CM_eCBUS_S_AV_BIST: + case CM_eCBUS_D_AV_BIST: + { + struct mhl_dev_context *dev_context; + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + si_mhl_tx_bist_cleanup(dev_context); + } + break; + default: + ; + } + /* For proper MHL discovery and tolerance of impedance + * measurement, set 5E3[7:4]=0x01 - this turns off the + * CBUS pull up for discovery states and 20K for IDLE + * state before impedance is measured. Fixes CBUS CTS + * cases that measure +/-20% tolerance of 1K MHL + * impedance, such as 3.3.5.1, 3.3.5.5, 3.3.6.4 + */ + mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, 0x10); + + /* Setup termination etc. */ + hw_context->intr_info->flags |= DRV_INTR_DISCONNECT; + if (BIT_CBUS_MHL12_DISCON_INT & int_4_status) { + disconnect_mhl(hw_context, true); + switch_to_idle(hw_context, false); + } else { /* must be BIT_NOT_MHL_EST_INT */ + disconnect_mhl(hw_context, false); + switch_to_idle(hw_context, true); + } + ret_val = 0xFF; /* INTR already cleared in disconnect_mhl */ + + if (hw_context->current_cbus_req.command) { + hw_context->current_cbus_req.command = 0x00; + hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = 0; + } + + } else if (int_4_status & BIT_RGND_READY_INT) { + int disc_stat2; + + disc_stat2 = + mhl_tx_read_reg(hw_context, + REG_DISC_STAT2) & + MSK_DISC_STAT2_RGND; + MHL_TX_DBG_ERR("Cable (RGND) impedance measured (%s)\n", + rgnd_value_string[disc_stat2]); + + if (VAL_RGND_1K == disc_stat2) { + MHL_TX_DBG_WARN("Cable impedance = 1k (MHL Device)\n"); + + mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, + BIT_DISC_CTRL9_WAKE_DRVFLT | + BIT_DISC_CTRL9_DISC_PULSE_PROCEED); + +#ifdef ENABLE_VBUS_SENSE /* BZ31845 */ + /* disconnect_mhl sets VBUS_OFF, so we know, + * at this point, that if XO3_SINK_VBUS_SENSE + * indicates high, then we must be getting + * VBUS power from the sink/dongle + */ + msleep(MHL_T_src_vbus_cbus_stable_min); + if (get_config(hw_context, XO3_SINK_VBUS_SENSE)) { + MHL_TX_DBG_WARN("%ssink drives VBUS%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + /* limit incoming current */ + mhl_tx_vbus_control(VBUS_OFF); + mhl_tx_vbus_current_ctl(I_VBUS_PRE_DISCOVERY); + } else { + MHL_TX_DBG_WARN("%ssource drives VBUS%s\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT); + mhl_tx_vbus_control(VBUS_ON); + } +#endif + /* For proper MHL discovery and tolerance of + * impedance measurement, set 5E3[7:4]=0x90. + * This sets the CBUS pull up for discovery + * states as 5k and 20K for IDLE state after + * impedance has been measured. Fixes CBUS + * CTS cases that measure +/-20% tolerance of + * 1K MHL impedance. Such as 3.3.5.1, 3.3.5.5, + * 3.3.6.4 + */ + mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, + 0x90); + + /* speculatively enable the MHL3 clock so + * that by the time the wake/discover + * sequence is complete, the MHL3 clock + * will be stabilized LONG BEFORE the + * transition to eCBUS mode. + */ + + /* enable HSIC earlier to enhance stability */ + + /* enable remaining discovery interrupts */ + enable_intr(hw_context, INTR_DISC, + BIT_MHL3_EST_INT_MASK + | BIT_MHL_EST_INT_MASK + | BIT_NOT_MHL_EST_INT_MASK + | BIT_CBUS_MHL3_DISCON_INT_MASK + | BIT_CBUS_MHL12_DISCON_INT_MASK + | BIT_RGND_READY_INT_MASK); + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, + (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | + BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL | + BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); + + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xC0); + mhl_tx_write_reg(hw_context, REG_M3_CTRL, + VAL_M3_CTRL_PEER_VERSION_PENDING_VALUE); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL1, 0xA2); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x03); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL3, 0x35); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x02); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL6, 0x02); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL7, 0x08); + mhl_tx_write_reg(hw_context, REG_COC_CTLC, 0xFF); + mhl_tx_write_reg(hw_context, REG_DPD, + BIT_DPD_PWRON_PLL | + BIT_DPD_PDNTX12 | + BIT_DPD_OSC_EN | + BIT_DPD_PWRON_HSIC); + enable_intr(hw_context, INTR_COC, + BIT_COC_PLL_LOCK_STATUS_CHANGE | + BIT_COC_CALIBRATION_DONE); + /* Enable MSC interrupt to handle initial exchanges */ + enable_intr(hw_context, INTR_MERR, ( +#ifdef PRINT_DDC_ABORTS + BIT_CBUS_DDC_ABORT | +#endif + BIT_CBUS_MSC_ABORT_RCVD | + BIT_CBUS_CMD_ABORT)); + enable_intr(hw_context, INTR_MSC, + (BIT_CBUS_MSC_MT_DONE | BIT_CBUS_HPD_CHG | + BIT_CBUS_MSC_MR_WRITE_STAT | + BIT_CBUS_MSC_MR_MSC_MSG | + BIT_CBUS_MSC_MR_WRITE_BURST | + BIT_CBUS_MSC_MR_SET_INT | + BIT_CBUS_MSC_MT_DONE_NACK)); + } else { + mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, + BIT_DISC_CTRL9_WAKE_DRVFLT | + BIT_DISC_CTRL9_NOMHL_EST | + BIT_DISC_CTRL9_WAKE_PULSE_BYPASS); + /* enable remaining discovery INTRs, except MHL_EST */ +#if (INCLUDE_SII6031 == 1) + { + struct mhl_dev_context *dev_context; + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + mhl_tx_notify_otg(dev_context, false); + } +#endif + enable_intr(hw_context, INTR_DISC, + BIT_NOT_MHL_EST_INT_MASK + | BIT_CBUS_MHL3_DISCON_INT_MASK + | BIT_CBUS_MHL12_DISCON_INT_MASK + | BIT_RGND_READY_INT_MASK); + } + } else if (int_4_status & BIT_MHL_EST_INT) { + uint8_t msc_compat = + BIT_CBUS_MSC_COMPATIBILITY_CONTROL_ENABLE_XDEVCAP; + /* + * ENABLE_DISCOVERY ensures wake up / discovery pulses ar sent + * and as result sink/dongle would respond CBUS high. + * 8620: ENABLE_DISCOVERY is always set. On successful + * discovery, 8620 will assert MHL_EST. + */ + + switch (hw_context->cbus_mode) { + case CM_NO_CONNECTION: + hw_context->cbus_mode = CM_oCBUS_PEER_VERSION_PENDING; + break; + case CM_NO_CONNECTION_BIST_SETUP: + hw_context->cbus_mode = + CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP; + break; + case CM_NO_CONNECTION_BIST_STAT: + hw_context->cbus_mode = + CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT; + break; + default: + ; + } + si_set_cbus_mode_leds(hw_context->cbus_mode); + /* For proper MHL discovery and tolerance of impedance + * measurement, set 5E3[7:4]=0x01 - this turns off the + * CBUS pull up for discovery states and 20K for IDLE + * state after MHL sink has been discovered. Fixes + * CBUS CTS cases that measure +/-20% tolerance of 1K + * MHL impedance, Such as 3.3.5.1, 3.3.5.5, 3.3.6.4 + */ + mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, 0x10); + + /* speculatively enable XDEVCAP reads + * so that MHL 3.0 sinks and dongles can + * read xdevcap registers in oCBUS mode + */ + mhl_tx_write_reg(hw_context, + REG_CBUS_MSC_COMPATIBILITY_CONTROL, + msc_compat); + + init_regs(hw_context); + + /* + * Setting this flag triggers sending DCAP_RDY. + * Tested RK-9296 - it works fine. + */ + hw_context->intr_info->flags |= DRV_INTR_CONNECT; + } + + return ret_val; +} + +static int g2wb_err_isr(struct drv_hw_context *hw_context, uint8_t intr_stat) +{ + if (intr_stat) { + if (BIT_MDT_RCV_TIMEOUT & intr_stat) { + MHL_TX_DBG_WARN("%sBIT_MDT_RCV_TIMEOUT%s\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT); + } + + if (BIT_MDT_RCV_SM_ABORT_PKT_RCVD & intr_stat) { + MHL_TX_DBG_ERR("%sBIT_MDT_RCV_SM_ABORT_PKT_RCVD%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + + if (BIT_MDT_RCV_SM_ERROR & intr_stat) { + MHL_TX_DBG_ERR("%sBIT_MDT_RCV_SM_ERROR%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + + if (BIT_MDT_XMIT_TIMEOUT & intr_stat) { + MHL_TX_DBG_ERR("%sBIT_MDT_XMIT_TIMEOUT %s\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT); + } + + if (BIT_MDT_XMIT_SM_ABORT_PKT_RCVD & intr_stat) { + MHL_TX_DBG_ERR + ("%sBIT_MDT_XMIT_SM_ABORT_PKT_RCVD " + "- status: 0x%02x%s\n", + ANSI_ESC_RED_TEXT, + mhl_tx_read_reg(hw_context, + REG_MDT_SM_STAT), + ANSI_ESC_RESET_TEXT); + } + + if (BIT_MDT_XMIT_SM_ERROR & intr_stat) { + MHL_TX_DBG_ERR("%sBIT_MDT_XMIT_SM_ERROR%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + } + return 0; +} + +static void check_bist_request_stat_pending(struct drv_hw_context *hw_context) +{ + struct cbus_req *cur_req = &hw_context->current_cbus_req; + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + if (MHL_MSC_MSG == cur_req->command) { + if (MHL_MSC_MSG_BIST_REQUEST_STAT == cur_req->msg_data[0]) { + MHL_TX_DBG_WARN("BIST_REQUEST_STAT completed\n") + cur_req->command = 0x00; + si_mhl_tx_msc_command_done(dev_context, 0x00); + } + } +} +static int g2wb_isr(struct drv_hw_context *hw_context, uint8_t intr_stat) +{ + int ret_val = 0; + if (intr_stat) { + if (BIT_MDT_XFIFO_EMPTY & intr_stat) { + struct cbus_req *peek_req; + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + MHL_TX_DBG_WARN( + "%sHAWB XFIFO empty%s XFIFO_STAT: 0x%02x\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT, + mhl_tx_read_reg(hw_context, + REG_MDT_XFIFO_STAT)); + + peek_req = peek_next_cbus_transaction(dev_context); + if (NULL == peek_req) { + disable_gen2_write_burst_xmit(hw_context); + disable_gen2_write_burst_rcv(hw_context); + } else if (MHL_WRITE_BURST != peek_req->command) { + disable_gen2_write_burst_xmit(hw_context); + disable_gen2_write_burst_rcv(hw_context); + } + } + if (BIT_MDT_RFIFO_DATA_RDY & intr_stat) { + uint8_t length; + uint8_t mdt_buffer[20]; + int rfifo_stat; + + /* Read all bytes */ + mhl_tx_read_reg_block(hw_context, + REG_MDT_RCV_READ_PORT, 17, mdt_buffer); + + MHL_TX_DBG_WARN + ("%sgot G2WB incoming 0x%02x%02x%s = %02X\n", + ANSI_ESC_GREEN_TEXT, mdt_buffer[1], mdt_buffer[2], + ANSI_ESC_RESET_TEXT, intr_stat); + + /* first byte contains the length of data */ + length = mdt_buffer[0]; + /* + * There is no way to know how much + * of the scratch pad was written so read + * it all. The app. will have to parse + * the data to know how much of it is valid. + */ + memcpy(hw_context->write_burst_data, + &mdt_buffer[1], 16); + + /* Signal upper layer of this arrival */ + hw_context->intr_info->flags |= DRV_INTR_WRITE_BURST; + + /* + * Clear current level in the FIFO. + * Moves pointer to the next keep RSM enabled + */ + mhl_tx_write_reg(hw_context, + REG_MDT_RCV_CONTROL, + BIT_MDT_RCV_CONTROL_MDT_RFIFO_CLR_CUR | + BIT_MDT_RCV_CONTROL_MDT_RCV_EN | + hw_context->delayed_hawb_enable_reg_val); + /* don't let the caller clear + * BIT_MDT_RFIFO_DATA_RDY until + * the receive buffer is empty + */ + rfifo_stat = mhl_tx_read_reg(hw_context, + REG_MDT_RFIFO_STAT); + if (rfifo_stat & MSK_MDT_RFIFO_STAT_MDT_RFIFO_CNT) { + ret_val = BIT_MDT_RFIFO_DATA_RDY; + } else { + switch (hw_context->cbus1_state) { + case CBUS1_IDLE_RCV_ENABLED: + case CBUS1_IDLE_RCV_DISABLED: + break; + case CBUS1_IDLE_RCV_PEND: + hw_context->cbus1_state = + CBUS1_IDLE_RCV_ENABLED; + break; + case CBUS1_MSC_PEND_DLY_RCV_EN: + MHL_TX_DBG_ERR( + "%sdelayed HAWB recv%s\n", + ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT) + check_bist_request_stat_pending( + hw_context); + break; + case CBUS1_MSC_PEND_DLY_RCV_DIS: + break; + case CBUS1_XMIT_PEND_XMIT_RCV_EN: + break; + case CBUS1_XMIT_PEND_XMIT_RCV_PEND: + hw_context->cbus1_state = + CBUS1_XMIT_PEND_XMIT_RCV_EN; + break; + } + } + } + if (BIT_MDT_IDLE_AFTER_HAWB_DISABLE & intr_stat) { + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_WARN, + "%shawb_idle%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) + if (MHL_WRITE_BURST == + hw_context->current_cbus_req.command) { + + hw_context->current_cbus_req.command = 0x00; + hw_context->intr_info->flags |= + DRV_INTR_MSC_DONE; + hw_context->intr_info->msc_done_data = 0; + hw_context->hawb_write_pending = false; + hw_context->cbus1_state = + CBUS1_IDLE_RCV_ENABLED; + } + } + } + return ret_val; +} + +static void enable_intr(struct drv_hw_context *hw_context, + uint8_t intr_num, uint8_t intr_mask) +{ + g_intr_tbl[intr_num].mask = intr_mask; + mhl_tx_write_reg(hw_context, g_intr_tbl[intr_num].mask_addr, intr_mask); +} + +/* + * Enable interrupts and turn on the engine + */ +static void si_mhl_tx_drv_enable_emsc_block(struct drv_hw_context *hw_context) +{ + uint8_t intStatus; + MHL_TX_DBG_INFO("Enabling EMSC and EMSC interrupts\n"); + + mhl_tx_modify_reg(hw_context, REG_SPIBURSTSTAT, + BIT_SPIBURSTSTAT_SPI_SRST, BIT_SPIBURSTSTAT_SPI_SRST); + mhl_tx_modify_reg(hw_context, REG_GENCTL, + BIT_GENCTL_EMSC_EN | + BIT_GENCTL_CLR_EMSC_RFIFO | + BIT_GENCTL_CLR_EMSC_XFIFO, + BIT_GENCTL_EMSC_EN | + BIT_GENCTL_CLR_EMSC_RFIFO | + BIT_GENCTL_CLR_EMSC_XFIFO); + mhl_tx_modify_reg(hw_context, REG_GENCTL, + BIT_GENCTL_CLR_EMSC_RFIFO | + BIT_GENCTL_CLR_EMSC_XFIFO, 0); + mhl_tx_modify_reg(hw_context, REG_COMMECNT, + BIT_COMMECNT_I2C_TO_EMSC_EN, + use_spi ? 0 : BIT_COMMECNT_I2C_TO_EMSC_EN); + intStatus = mhl_tx_read_reg(hw_context, REG_EMSCINTR); + mhl_tx_write_reg(hw_context, REG_EMSCINTR, intStatus); + enable_intr(hw_context, INTR_BLOCK, BIT_EMSCINTR_SPI_DVLD); +} + +void si_mhl_tx_drv_device_isr(struct drv_hw_context *hw_context, + struct interrupt_info *intr_info) +{ + uint8_t intr_num; + uint8_t aggregated_intr_status[NUM_AGGREGATED_INTR_REGS]; + + hw_context->intr_info = intr_info; + + MHL_TX_DBG_INFO("%sgot INTR COC_STAT_0:0x%02x%s\n", + ANSI_ESC_GREEN_TEXT, + mhl_tx_read_reg(hw_context, REG_COC_STAT_0), + ANSI_ESC_RESET_TEXT); + + MHL_TX_DBG_INFO("%sdiv_ctl_main:0x%02x hdcp2x_tp1:0x%02x%s\n", + ANSI_ESC_GREEN_TEXT, + mhl_tx_read_reg(hw_context, REG_DIV_CTL_MAIN), + mhl_tx_read_reg(hw_context, REG_HDCP2X_TP1), + ANSI_ESC_RESET_TEXT); + + mhl_tx_read_reg_block(hw_context, REG_FAST_INTR_STAT, + sizeof(aggregated_intr_status), + aggregated_intr_status); + MHL_TX_DBG_INFO("%s aggr intr status:" + " %02x %02x %02x %02x %02x %02x %02x%s\n", + ANSI_ESC_GREEN_TEXT, + aggregated_intr_status[FAST_INTR_STAT], + aggregated_intr_status[L1_INTR_STAT_0], + aggregated_intr_status[L1_INTR_STAT_1], + aggregated_intr_status[L1_INTR_STAT_2], + aggregated_intr_status[L1_INTR_STAT_3], + aggregated_intr_status[L1_INTR_STAT_4], + aggregated_intr_status[L1_INTR_STAT_5], + ANSI_ESC_RESET_TEXT); + + /* Skip checking interrupts if GPIO pin is not asserted anymore */ + for (intr_num = 0; + (intr_num < MAX_INTR) && (is_interrupt_asserted()); intr_num++) { + if (g_intr_tbl[intr_num].mask) { + uint8_t aggregated_index, aggregated_id_bit, + aggregated_status; + aggregated_index = g_intr_tbl[intr_num].aggr_stat_index; + aggregated_id_bit = + g_intr_tbl[intr_num].aggr_stat_id_bit; + aggregated_status = + aggregated_intr_status[aggregated_index]; + if (aggregated_status & aggregated_id_bit) { + int reg_value; + uint8_t intr_stat; + + reg_value = mhl_tx_read_reg(hw_context, + g_intr_tbl + [intr_num]. + stat_addr); + + if (reg_value < 0) + return; + + intr_stat = (uint8_t) reg_value; + + /* Process enabled interrupts. Ignore others */ + intr_stat = + intr_stat & g_intr_tbl[intr_num].mask; + if (intr_stat) { + int already_cleared; + + MHL_TX_DBG_INFO("INTR-%s = %02X\n", + g_intr_tbl[intr_num]. + name, intr_stat); + already_cleared = + g_intr_tbl[intr_num].isr(hw_context, + intr_stat); + if (already_cleared >= 0) { + intr_stat &= ~already_cleared; + if (intr_stat) { + mhl_tx_write_reg + (hw_context, + g_intr_tbl + [intr_num]. + stat_addr, + intr_stat); + } + } + } + } + } + } +} + +/* default callbacks */ +int cb_enum_begin(void *context) +{ + MHL_TX_DBG_INFO("%senum begin%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + return 0; +} + +int cb_enum_item(void *context, uint16_t columns, uint16_t rows, + uint8_t bits_per_pixel, uint32_t vertical_refresh_rate_in_milliHz, + uint16_t burst_id, union video_burst_descriptor_u *p_descriptor) +{ + MHL_TX_DBG_INFO("%senum item%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + return 0; +} + +int cb_enum_end(void *context) +{ + MHL_TX_DBG_INFO("%senum end%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); + return 0; +} + +void cb_hpd_driven_low(void *context) +{ + MHL_TX_DBG_WARN("%sdriven_low%s\n", ANSI_ESC_GREEN_TEXT, + ANSI_ESC_RESET_TEXT); +} + +enum hpd_high_callback_status cb_hpd_driven_high(void *context, + uint8_t *p_edid, size_t edid_length, + uint8_t *p_emsc_edid, size_t emsc_edid_length, + struct MHL3_hev_dtd_item_t *p_hev_dtd, size_t num_hev_dtds, + struct MHL3_hev_vic_item_t *p_hev_vic, size_t num_hev_vics, + struct MHL3_3d_dtd_item_t *p_3d_dtd_items, size_t num_3d_dtds, + struct MHL3_3d_vic_item_t *p_3d_vic, size_t num_3d_vics, + union avif_or_cea_861_dtd_u *p_avif_or_dtd, + size_t avif_or_dtd_max_length, union vsif_mhl3_or_hdmi_u *p_vsif, + size_t vsif_max_length) +{ + MHL_TX_DBG_WARN("%sdriven_high%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + return HH_VIDEO_NOT_RDY; +} + +static void input_field_rate_measurement_callback(void *callback_param) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)callback_param; + start_video(hw_context); +} + +/* + * si_mhl_tx_chip_initialize + * + * Chip specific initialization. + * This function resets and initializes the transmitter. + * MHL Detection interrupt setups up the chip for video. + */ +int si_mhl_tx_chip_initialize(struct drv_hw_context *hw_context) +{ + int ret_val; + int status = -1; + + hw_context->pp_16bpp_override = pp_16bpp_automatic; + set_pin(TX_FW_WAKE, 0); /* inverter on board */ + platform_mhl_tx_hw_reset(TX_HW_RESET_PERIOD, TX_HW_RESET_DELAY); + + /* Power up the device. Keep HSIC and Rx cores powered down. */ + mhl_tx_write_reg(hw_context, REG_DPD, + BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); + if (!use_spi) { + /* + * SWWA BZ35174: Duplicate write to ensure proper + * I2C interface functionality. Additional reset added to + * address a fault in some application processor i2c driver's + * ability to recover from an initial i2c failure. + */ + platform_mhl_tx_hw_reset(TX_HW_RESET_PERIOD, TX_HW_RESET_DELAY); + mhl_tx_write_reg(hw_context, REG_DPD, + BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); + } + ret_val = get_device_rev(hw_context); + hw_context->chip_rev_id = (uint8_t) ret_val; + if ((0 == hw_context->chip_rev_id) || (ret_val == -1)) { + MHL_TX_DBG_ERR("%sDevice missing or revision not supported%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return status; + } + /* SWWA BZ35298: Dummy write to TX DDC to ensure proper + * DDC interface functionality. + */ + mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, + BIT_DDC_MANUAL_MAN_DDC | 0x03); + mhl_tx_write_reg(hw_context, REG_HDCP1X_LB_BIST, BIT_HDCP1X_LB_BIST_EN); + mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, 0x03); + mhl_tx_write_reg(hw_context, REG_HDCP1X_LB_BIST, 0x00); + + + MHL_TX_DBG_ERR("72:06 -- 0x%02x\n", + mhl_tx_read_reg(hw_context, REG_OTP_DBYTE510)) + si_set_cbus_mode_leds(CM_NO_CONNECTION); + + ret_val = get_device_id(hw_context); + if (ret_val > 0) { + struct mhl_dev_context *dev_context = + container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + hw_context->chip_device_id = (uint16_t) ret_val; + + MHL_TX_DBG_ERR("%x%s: Found SiI%04X rev: %01X.%01X%s\n", + hw_context, + ANSI_ESC_GREEN_TEXT, + hw_context->chip_device_id, + hw_context->chip_rev_id >> 4, + (hw_context->chip_rev_id & 0x0F), + ANSI_ESC_RESET_TEXT); +#ifndef SWWA_BZ30759 + /* Based on module parameter "crystal_khz=xxxxx". */ + program_ext_clock_regs(hw_context, crystal_khz); +#endif + /* + * Store transmitter's KSV in case it's requested by + * someone else (probably the video source driver) + */ + mhl_tx_read_reg_block(hw_context, + REG_AKSV_1, 5, hw_context->aksv); + + /* Initialize these before calling disconnect_mhl() */ + hw_context->callbacks.display_timing_enum_begin = cb_enum_begin; + hw_context->callbacks.display_timing_enum_item = cb_enum_item; + hw_context->callbacks.display_timing_enum_end = cb_enum_end; + hw_context->callbacks.hpd_driven_low = cb_hpd_driven_low; + hw_context->callbacks.hpd_driven_high = cb_hpd_driven_high; + + /* Move to disconnected state. + * Let RGND/MHL connection event start the driver + */ + disconnect_mhl(hw_context, true); + switch_to_idle(hw_context, false); + status = mhl_tx_create_timer(dev_context, + input_field_rate_measurement_callback, + hw_context, + &hw_context->input_field_rate_measurement_timer); + if (status != 0) { + MHL_TX_DBG_ERR( + "Failed to allocate FIELD_RATE timer\n"); + } else { + /* Don't allow timer to start prematurely */ + MHL_TX_DBG_INFO( + "stopping timer for FIELD_RATE measurement\n"); + mhl_tx_stop_timer(dev_context, + hw_context->input_field_rate_measurement_timer); + } + } + + return status; +} + +void si_mhl_tx_drv_shutdown(struct drv_hw_context *hw_context) +{ + set_pin(TX_FW_WAKE, 1); +} + +int si_mhl_tx_drv_connection_is_mhl3(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + return IN_MHL3_MODE(hw_context) ? 1 : 0; +} + +int si_mhl_tx_drv_get_highest_tmds_link_speed(struct mhl_dev_context + *dev_context) +{ + int link_speed = MHL_XDC_TMDS_000; + + if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & MHL_XDC_TMDS_600) { + link_speed = MHL_XDC_TMDS_600; + } else if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & + MHL_XDC_TMDS_300) { + link_speed = MHL_XDC_TMDS_300; + } else if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & + MHL_XDC_TMDS_150) { + link_speed = MHL_XDC_TMDS_150; + } + + return link_speed; +} + +static void si_mhl_tx_drv_set_lowest_tmds_link_speed(struct mhl_dev_context + *dev_context, uint32_t pixel_clock_frequency, uint8_t bits_per_pixel) +{ + uint32_t link_clock_frequency; + uint32_t top_clock_frequency = 147000000; /* init to lowest max */ + uint8_t reg_val = 0; + bool found_fit = false; + bool fits_1_5, fits_3_0, fits_6_0; + uint8_t av_link_param = 0; + + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + + link_clock_frequency = + pixel_clock_frequency * ((uint32_t) (bits_per_pixel >> 3)); + fits_1_5 = false; + fits_3_0 = false; + fits_6_0 = false; + + /* Find lowest fit by finding all link speeds into which + * this mode will fit, highest first. + * Apply override if we can. + */ + + /* If BIST is in progress, use the parameters in BIST_SETUP */ + switch (hw_context->cbus_mode) { + case CM_eCBUS_S_BIST: + case CM_eCBUS_S_AV_BIST: + switch (dev_context->bist_setup.avlink_data_rate) { + case 1: /* 1.5 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 1.5Gbps\n"); + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; + break; + case 2: /* 3.0 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 3.0Gbps\n"); + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; + break; + case 3: /* 6.0 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 6.0Gbps\n"); + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; + break; + default: + MHL_TX_DBG_ERR("Unsupported AVLINK_DATA_RATE %02X\n", + (dev_context->bist_setup.avlink_data_rate)); + return ; + } + break; + default: + if (link_clock_frequency <= 600000000) { + MHL_TX_DBG_WARN( + "Mode fit TMDS Link Speed = 6.0Gbps (%d)\n", + link_clock_frequency); + if (dev_context->xdev_cap_cache.mxdc.tmds_speeds + & MHL_XDC_TMDS_600) { + MHL_TX_DBG_INFO( + "XDEVCAP TMDS Link Speed = 6.0Gbps is " + "supported\n"); + found_fit = true; + fits_6_0 = true; + /* 6000000 * 98; */ + top_clock_frequency = 588000000; + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; + } + } + if (link_clock_frequency <= 300000000) { + MHL_TX_DBG_WARN( + "Mode fits TMDS Link Speed = 3.0Gbps (%d)\n", + link_clock_frequency); + if (dev_context->xdev_cap_cache.mxdc.tmds_speeds + & MHL_XDC_TMDS_300) { + MHL_TX_DBG_INFO( + "XDEVCAP TMDS Link Speed = 3.0Gbps is " + "supported\n"); + found_fit = true; + fits_3_0 = true; + /* 3000000 * 98; */ + top_clock_frequency = 294000000; + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; + } + } + if (link_clock_frequency <= 150000000) { + MHL_TX_DBG_WARN( + "Mode fits TMDS Link Speed = 1.5Gbps (%d)\n", + link_clock_frequency); + if (dev_context->xdev_cap_cache.mxdc.tmds_speeds + & MHL_XDC_TMDS_150) { + MHL_TX_DBG_INFO( + "XDEVCAP TMDS Link Speed = 1.5Gbps is " + "supported\n"); + found_fit = true; + fits_1_5 = true; + /* 1500000 * 98; */ + top_clock_frequency = 147000000; + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; + + } + } + if (!found_fit) { + MHL_TX_DBG_ERR( + "Cannot fit mode to any supported TMDS Link " + "Speeds\n"); + MHL_TX_DBG_INFO("Forcing TMDS Link Speed = 6.0Gbps\n"); + /* 6000000 * 98; */ + top_clock_frequency = 588000000; + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; + } + switch (platform_get_flags() & PLATFORM_FLAG_LINK_SPEED) { + case PLATFORM_FLAG_1_5GBPS: + if (fits_1_5) { + MHL_TX_DBG_WARN("Module parameter forcing " + "TMDS Link Speed = 1.5Gbps\n"); + /* 1500000 * 98; */ + top_clock_frequency = 147000000; + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; + } + break; + case PLATFORM_FLAG_3GBPS: + if (fits_3_0) { + MHL_TX_DBG_WARN("Module parameter forcing " + "TMDS Link Speed = 3.0Gbps\n"); + /* 3000000 * 98; */ + top_clock_frequency = 294000000; + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; + } + break; + case PLATFORM_FLAG_6GBPS: + if (fits_6_0) { + MHL_TX_DBG_WARN("Module parameter forcing " + "TMDS Link Speed = 6.0Gbps\n"); + /* 6000000 * 98; */ + top_clock_frequency = 588000000; + reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; + } + break; + } + } + + /* set tmds link speed */ + mhl_tx_write_reg(hw_context, REG_MHL3_TX_ZONE_CTL, reg_val); + + /* set unlimited packet size only if not enough overhead */ + MHL_TX_DBG_WARN("lcf = %d, tcf = %d\n", link_clock_frequency, + top_clock_frequency); + if (link_clock_frequency >= top_clock_frequency) { + MHL_TX_DBG_ERR("3E1[3] <- 1 UNLIMITED MODE ON\n"); + mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN | + BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN | + BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN_ON); + } else { + MHL_TX_DBG_ERR("3E1[3] <- 0 UNLIMITED MODE OFF\n"); + mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN | + BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN | + BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN_OFF); + } + + /* set write stat indicating this change */ + /* 0b000 = 1.5Gbps, 0b001 = 3Gbps, 0b010 = 6.0Gbps */ + switch (reg_val) { + case VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS: + mhl_tx_modify_reg(dev_context, REG_M3_POSTM, + MSK_M3_POSTM_RRP_DECODE, 0x40); + av_link_param = 0x02; + break; + case VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS: + mhl_tx_modify_reg(dev_context, REG_M3_POSTM, + MSK_M3_POSTM_RRP_DECODE, 0x40); + av_link_param = 0x01; + break; + case VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS: + mhl_tx_modify_reg(dev_context, REG_M3_POSTM, + MSK_M3_POSTM_RRP_DECODE, 0x38); + av_link_param = 0x00; + break; + } + switch (hw_context->cbus_mode) { + case CM_eCBUS_S_BIST: + case CM_eCBUS_D_BIST: + case CM_eCBUS_S_AV_BIST: + case CM_eCBUS_D_AV_BIST: + break; + default: + si_mhl_tx_set_status(dev_context, true, + MHL_STATUS_REG_AV_LINK_MODE_CONTROL, av_link_param); + } +} + +bool si_mhl_tx_drv_support_e_cbus_d(struct drv_hw_context *hw_context) +{ + return false; +} + +int si_mhl_tx_drv_cbus_ready_for_edid(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + switch (hw_context->cbus_mode) { + case CM_oCBUS_PEER_IS_MHL1_2: + case CM_eCBUS_S: + case CM_eCBUS_D: + case CM_eCBUS_S_AV_BIST: + case CM_eCBUS_D_AV_BIST: + return 1; + case CM_eCBUS_S_BIST: + case CM_eCBUS_D_BIST: + if (BIST_TRIGGER_ECBUS_TX_RX_MASK & + dev_context->bist_trigger_info) + return 0; + return 1; + default: + return 0; + } +} + +uint16_t si_mhl_tx_drv_get_blk_rcv_buf_size(void) +{ + return LOCAL_BLK_RCV_BUFFER_SIZE; +} +/* + setup_sans_cbus1 +*/ +void setup_sans_cbus1(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)(&dev_context->drv_context); + enum hpd_control_mode mode; + uint8_t dummy_edid[256] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x4C, 0xAB, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x23, 0x17, 0x01, 0x03, 0x81, 0x56, 0x30, 0x78, + 0x8A, 0xA5, 0x8E, 0xA6, 0x54, 0x4A, 0x9C, 0x26, + 0x12, 0x45, 0x46, 0xAD, 0xCE, 0x00, 0x81, 0x40, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, + 0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58, + 0x8A, 0x00, 0x56, 0xE1, 0x31, 0x00, 0x00, 0x1E, + 0x9A, 0x29, 0xA0, 0xD0, 0x51, 0x84, 0x22, 0x30, + 0x50, 0x98, 0x36, 0x00, 0x60, 0xE1, 0x31, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x32, + 0x4B, 0x18, 0x3C, 0x0B, 0x00, 0x0A, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x53, 0x45, 0x33, 0x39, 0x55, 0x59, 0x30, + 0x34, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x01, 0x65, + 0x02, 0x03, 0x2A, 0x71, 0x4F, 0x06, 0x07, 0x02, + 0x03, 0x15, 0x96, 0x11, 0x12, 0x13, 0x04, 0x14, + 0x05, 0x1F, 0x90, 0x20, 0x23, 0x09, 0x07, 0x07, + 0x83, 0x01, 0x00, 0x00, 0x6D, 0x03, 0x0C, 0x00, + 0x30, 0x00, 0x00, 0x3C, 0x20, 0x40, 0x68, 0x01, + 0x02, 0x03, 0x8C, 0x0A, 0xD0, 0x90, 0x20, 0x40, + 0x31, 0x20, 0x0C, 0x40, 0x55, 0x00, 0x56, 0xE1, + 0x31, 0x00, 0x00, 0x18, 0x01, 0x1D, 0x80, 0x18, + 0x71, 0x1C, 0x16, 0x20, 0x58, 0x2C, 0x25, 0x00, + 0x56, 0xE1, 0x31, 0x00, 0x00, 0x9E, 0x01, 0x1D, + 0x80, 0xD0, 0x72, 0x1C, 0x16, 0x20, 0x10, 0x2C, + 0x25, 0x80, 0x56, 0xE1, 0x31, 0x00, 0x00, 0x9E, + 0x01, 0x1D, 0x00, 0xBC, 0x52, 0xD0, 0x1E, 0x20, + 0xB8, 0x28, 0x55, 0x40, 0x56, 0xE1, 0x31, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 + }; + uint16_t length = sizeof(dummy_edid); + + int cstat_p3; + + enable_intr(hw_context, INTR_EDID, 0); + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO); + mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); + mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x02); + enable_intr(hw_context, INTR_EDID, 0); + init_rx_regs(hw_context); + /* choose EDID instead of devcap to appear at the FIFO */ + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | + VAL_EDID_CTRL_DEVCAP_SELECT_EDID | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | + VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + + mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); + mhl_tx_write_reg_block(hw_context, REG_EDID_FIFO_WR_DATA, + sizeof(dummy_edid), dummy_edid); + + mhl_tx_write_reg(hw_context, REG_EDID_CTRL, + VAL_EDID_CTRL_EDID_PRIME_VALID_ENABLE | + VAL_EDID_CTRL_DEVCAP_SELECT_EDID | + VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | + VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); + enable_intr(hw_context, INTR_SCDT, BIT_INTR_SCDT_CHANGE); + /* sample REG_TMDS_CSTAT_P3 before driving upstream HDP high */ + cstat_p3 = mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); + + /* disable auto-clear */ + cstat_p3 |= BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR; +#ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH + cstat_p3 |= BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE; +#endif + mhl_tx_write_reg(hw_context, REG_TMDS_CSTAT_P3, cstat_p3); + + drive_hpd_low(hw_context); + msleep(100); + /* clear fifo over/under flow, if any */ + mhl_tx_write_reg(hw_context, REG_INTR5, 0xFF); + + mode = platform_get_hpd_control_mode(); + + if (HPD_CTRL_OPEN_DRAIN == mode) + mhl_tx_write_reg(hw_context, REG_HPD_CTRL, + BITS_HPD_CTRL_OPEN_DRAIN_HIGH | + BIT_HPD_CTRL_HPD_DS_SIGNAL); + else if (HPD_CTRL_PUSH_PULL == mode) + mhl_tx_write_reg(hw_context, REG_HPD_CTRL, + BITS_HPD_CTRL_PUSH_PULL_HIGH | + BIT_HPD_CTRL_HPD_DS_SIGNAL); + else + MHL_TX_DBG_ERR("%sUnexpected HPD mode!%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + MHL_TX_DBG_ERR("%sHPD high for BIST%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + + /* Enable SCDT interrupt to detect stable incoming clock */ + enable_intr(hw_context, INTR_SCDT, BIT_INTR_SCDT_CHANGE); + do_hpd_driven_high_callback(hw_context, dummy_edid, length); +} + +void si_mhl_tx_drv_start_avlink_bist(struct mhl_dev_context *dev_context, + struct bist_setup_info *test_info) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)(&dev_context->drv_context); + uint32_t frame_count = 0; + uint8_t start_cmd; + uint8_t bist_mode_sel; + + switch (test_info->avlink_video_mode) { + case 4: /* 1280 X 720 (720P) */ + MHL_TX_DBG_ERR("AV LINK_VIDEO_MODE 720p60\n"); + bist_mode_sel = 5; + break; + case 3: /* 720 X 480 (480P) */ + default: + MHL_TX_DBG_ERR("AV LINK_VIDEO_MODE 480p60\n"); + bist_mode_sel = 6; + break; + /* remaining cases are pre-checked in invalid_bist_parms() */ + } + mhl_tx_write_reg(hw_context, REG_BIST_VIDEO_MODE, bist_mode_sel); + + switch (test_info->avlink_data_rate) { + case 1: /* 1.5 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 1.5Gbps\n"); + bist_mode_sel = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; + mhl_tx_modify_reg(hw_context, REG_M3_POSTM, + MSK_M3_POSTM_RRP_DECODE, 0x38); + break; + case 2: /* 3.0 Gbps */ + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 3.0Gbps\n"); + bist_mode_sel = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; + mhl_tx_modify_reg(hw_context, REG_M3_POSTM, + MSK_M3_POSTM_RRP_DECODE, 0x40); + break; + case 3: /* 6.0 Gbps */ + default: + MHL_TX_DBG_ERR("AV LINK_DATA_RATE 6.0Gbps\n"); + bist_mode_sel = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; + mhl_tx_modify_reg(hw_context, REG_M3_POSTM, + MSK_M3_POSTM_RRP_DECODE, 0x40); + break; + /* remaining cases are pre-checked in invalid_bist_parms() */ + } + mhl_tx_write_reg(hw_context, REG_MHL3_TX_ZONE_CTL, + bist_mode_sel); + + switch (test_info->avlink_pattern) { + case BIST_AVLINK_PATTERN_UNSPECIFIED: + case BIST_AVLINK_PATTERN_PRBS: + MHL_TX_DBG_ERR("AV LINK_PATTERN %sPRBS%s %s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT, + si_mhl_tx_drv_get_cbus_mode_str( + hw_context->cbus_mode)) + mhl_tx_write_reg(hw_context, REG_BIST_TEST_SEL, 0x03); + break; + + case BIST_AVLINK_PATTERN_FIXED_8: + MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed8%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + mhl_tx_write_reg(hw_context, REG_BIST_8BIT_PATTERN, + (uint8_t) test_info->avlink_fixed_pat); + + mhl_tx_write_reg(hw_context, REG_BIST_TEST_SEL, 0x09); + break; + + case BIST_AVLINK_PATTERN_FIXED_10: + MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed10%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + mhl_tx_write_reg(hw_context, REG_BIST_TEST_SEL, 0x0A); + if (test_info->avlink_fixed_pat & 0x8000) { + /* alternate with bitwise inverse */ + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, + 0x8B); + } else{ + /* same 10-bit pattern every cycle */ + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, + 0x81); + } + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_HIGH, + 0x07); + + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_LOW, + (uint8_t) test_info->avlink_fixed_pat); + { + uint8_t STX_1, STX_0, STX, E98_pre, pat_lsb, pat_msb; + pat_lsb = (uint8_t) test_info->avlink_fixed_pat; + pat_msb = (uint8_t) (test_info->avlink_fixed_pat >> 8); + STX_1 = + ((pat_lsb >> 1) ^ (pat_lsb >> 3) ^ (pat_lsb >> 4) ^ + (pat_lsb >> 5) ^ (pat_lsb >> 7)) & 0x01; + STX_0 = + ((pat_lsb >> 0) ^ (pat_lsb >> 2) ^ (pat_lsb >> 4) ^ + (pat_lsb >> 6)) & 0x01; + STX = (STX_1 << 1) | STX_0; + E98_pre = ((pat_msb) ^ STX) & 0x3; + mhl_tx_write_reg(hw_context, + REG_TX_IP_BIST_PAT_HIGH, E98_pre); + } + + /* Turn on TX BIST SEL, don't enable TX BIST */ + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, 0x01); + break; + + /* remaining cases are pre-checked in invalid_bist_parms() */ + default: + MHL_TX_DBG_ERR("%sUnrecognized test pattern detected!%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + } + + if (test_info->avlink_duration) { + if (test_info->avlink_pattern == BIST_AVLINK_PATTERN_FIXED_8) + frame_count = test_info->avlink_duration * 32; + } + + mhl_tx_write_reg(hw_context, REG_BIST_DURATION_0, + (uint8_t) frame_count); + frame_count >>= 8; + mhl_tx_write_reg(hw_context, REG_BIST_DURATION_1, + (uint8_t) frame_count); + frame_count >>= 8; + mhl_tx_write_reg(hw_context, REG_BIST_DURATION_2, + (uint8_t) frame_count); + + start_cmd = BIT_BIST_EN | + BIT_BIST_ALWAYS_ON | + BIT_BIST_TRANS; + mhl_tx_write_reg(hw_context, REG_BIST_CTRL, start_cmd); + + video_sans_cbus1(dev_context); +} + +void si_mhl_tx_drv_stop_avlink_bist(struct drv_hw_context *hw_context) +{ + MHL_TX_DBG_ERR("AV_LINK BIST stop\n") + mhl_tx_write_reg(hw_context, REG_BIST_CTRL, 0x00); + /* Stop IPBIST Fix10. */ + mhl_tx_modify_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, + BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | + BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN, 0x00); + + mhl_tx_write_reg(hw_context, REG_M3_SCTRL, 0x41); +#ifdef CoC_FSM_MONITORING + #ifdef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_6, 0); + #endif +#endif +} + +void si_mhl_tx_drv_start_ecbus_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info) +{ + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); +#ifdef CoC_FSM_MONITORING + #ifdef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_7, BIT_CTRL1_GPIO_I_7); + #endif +#endif + MHL_TX_DBG_ERR("\n") + + if (dev_context->bist_trigger_info & BIST_TRIGGER_TEST_E_CBUS_D) { + hw_context->cbus_mode = CM_eCBUS_D_BIST; + si_set_cbus_mode_leds(hw_context->cbus_mode); + + } else { + MHL_TX_DBG_ERR("\n") + + hw_context->cbus_mode = CM_eCBUS_S_BIST; + si_set_cbus_mode_leds(hw_context->cbus_mode); + } +} + +int32_t si_mhl_tx_drv_get_ecbus_bist_status( + struct mhl_dev_context *dev_context, + uint8_t *rx_run_done, + uint8_t *tx_run_done) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + int err_cnt = BIST_LOCAL_COUNT_INVALID; + + if (dev_context->bist_trigger_info & BIST_TRIGGER_TEST_E_CBUS_D) { + mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x00); + err_cnt = mhl_tx_read_reg(hw_context, REG_DOC_STAT_9) & + 0x0F; + err_cnt <<= 8; + err_cnt |= mhl_tx_read_reg(hw_context, REG_DOC_STAT_8); + } else { + uint8_t coc_status[6]; + int i; + /* Trigger to update error counter */ + mhl_tx_modify_reg(hw_context, REG_COC_CTL15, + MSK_COC_CTL15_COC_CONTROL15_6_4, 0x00); + memset(coc_status, 0, ARRAY_SIZE(coc_status)); + mhl_tx_read_reg_block(hw_context, REG_COC_STAT_0, + sizeof(coc_status), coc_status); + + *tx_run_done = coc_status[1] & BIT_COC_STAT_1_TX_RUN_DONE; + *rx_run_done = coc_status[2] & BIT_COC_STAT_2_RX_RUN_DONE; + + for (err_cnt = 0, i = 3; + i < sizeof(coc_status); ++i) { + err_cnt <<= 8; + err_cnt |= coc_status[i]; + } + MHL_TX_DBG_INFO("err_cnt: 0x%x\n" + "coc_status[0]: 0x%02x\n" + "coc_status[1]: 0x%02x\n" + "coc_status[2]: 0x%02x\n", + err_cnt, + coc_status[0], + coc_status[1], + coc_status[2]) + memcpy(hw_context->prev_bist_coc_status, coc_status, + sizeof(hw_context->prev_bist_coc_status)); + + } + return err_cnt; +} + +void si_mhl_tx_drv_stop_ecbus_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info) +{ + struct mhl_dev_context *dev_context; + + dev_context = container_of((void *)hw_context, + struct mhl_dev_context, drv_context); + + + if (dev_context->bist_trigger_info & BIST_TRIGGER_TEST_E_CBUS_D) { + MHL_TX_DBG_INFO + ("eCBUS State Machine: " + "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n", + mhl_tx_read_reg(hw_context, REG_COC_STAT_0), + mhl_tx_read_reg(hw_context, REG_COC_STAT_1), + mhl_tx_read_reg(hw_context, REG_COC_STAT_2), + mhl_tx_read_reg(hw_context, REG_COC_STAT_3), + mhl_tx_read_reg(hw_context, REG_COC_STAT_4), + mhl_tx_read_reg(hw_context, REG_COC_STAT_5)); + } else { + MHL_TX_DBG_INFO + ("eCBUS State Machine: " + "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n", + mhl_tx_read_reg(hw_context, REG_COC_STAT_0), + mhl_tx_read_reg(hw_context, REG_COC_STAT_1), + mhl_tx_read_reg(hw_context, REG_COC_STAT_2), + mhl_tx_read_reg(hw_context, REG_COC_STAT_3), + mhl_tx_read_reg(hw_context, REG_COC_STAT_4), + mhl_tx_read_reg(hw_context, REG_COC_STAT_5)); + + mhl_tx_write_reg(hw_context, REG_COC_CTL11, 0x08); + mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x0C); + } + +#ifdef CoC_FSM_MONITORING + #ifdef BIST_MONITORING + mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, + BIT_CTRL1_GPIO_I_7, 0); + #endif +#endif +} + +int si_mhl_tx_drv_start_impedance_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info) +{ + int status = 0; + + switch (test_info->impedance_mode) { + case BIST_IMPEDANCE_MODE_AVLINK_TX_LOW: + case BIST_IMPEDANCE_MODE_AVLINK_TX_HIGH: + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, + 0x01); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_HIGH, + 0x07); + if (test_info->impedance_mode == + BIST_IMPEDANCE_MODE_AVLINK_TX_LOW) { + mhl_tx_write_reg(hw_context, + REG_TX_IP_BIST_PAT_LOW, 0x00); + mhl_tx_write_reg(hw_context, + REG_TX_IP_BIST_PAT_HIGH, 0x00); + } else { + mhl_tx_write_reg(hw_context, + REG_TX_IP_BIST_PAT_LOW, 0xFF); + mhl_tx_write_reg(hw_context, + REG_TX_IP_BIST_PAT_HIGH, 0x01); + + } + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, + BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | + BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN); + break; + + case BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW: + case BIST_IMPEDANCE_MODE_ECBUS_S_TX_HIGH: + mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x81); + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x00); + mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x0E); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x0B); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3D); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF0); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL0, 0xC5); + mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x18); + + if (test_info->impedance_mode == + BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW) { + mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x00); + mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x00); + } else { + mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x28); + mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x28); + } + + mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x05); + mhl_tx_write_reg(hw_context, REG_COC_CTLB, 0xFF); + mhl_tx_write_reg(hw_context, REG_COC_CTLC, 0xFF); + mhl_tx_write_reg(hw_context, REG_COC_CTLD, 0x00); + mhl_tx_write_reg(hw_context, REG_COC_CTLE, 0x18); + mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xE8); + mhl_tx_write_reg(hw_context, REG_BGR_BIAS, 0x87); + mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xD0); + mhl_tx_write_reg(hw_context, REG_COC_CTL3, 0x3F); + mhl_tx_write_reg(hw_context, REG_COC_CTL6, 0x10); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0xCD); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL4, 0x2A); + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x80); + mhl_tx_write_reg(hw_context, REG_M3_SCTRL, 0x40); + mhl_tx_write_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN); + + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, + 0x01); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_HIGH, + 0x03); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_LOW, + 0xFF); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_HIGH, + 0x03); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CONF_LOW, + 0x08); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CONF_HIGH, + 0x00); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, + BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | + BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN); + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, + BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | + BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN | + BIT_TX_IP_BIST_CNTLSTA_TXBIST_RUN | + BIT_TX_IP_BIST_CNTLSTA_TXBIST_ON); + + mhl_tx_write_reg(hw_context, REG_COC_CTL11, 0xF0); + mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x01); + mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x0C); + mhl_tx_write_reg(hw_context, REG_COC_CTL15, 0x80); + break; + + case BIST_IMPEDANCE_MODE_ECBUS_D_TX_LOW: + case BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH: + mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x01); + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, 0x02); + mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x0E); + mhl_tx_write_reg(hw_context, REG_MHL_TOP_CTL, 0x82); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF3); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0x07); + mhl_tx_write_reg(hw_context, REG_MHL_DOC_CTL0, 0x81); + mhl_tx_write_reg(hw_context, REG_MHL3_TX_ZONE_CTL, 0x00); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x0B); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3B); + mhl_tx_write_reg(hw_context, REG_DOC_CTL0, 0x02); + mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x06); + mhl_tx_write_reg(hw_context, REG_DOC_CTLE, 0x0F); + + mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x00); + mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); + mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x08); + mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); + mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x09); + mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); + mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x0A); + mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); + mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x0B); + mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); + mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x02); + mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); + if (test_info->impedance_mode == + BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH) + mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x60); + mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x1E); + mhl_tx_write_reg(hw_context, REG_DOC_CTLE, 0x0F); + mhl_tx_write_reg(hw_context, REG_DOC_CTL0, 0x60); + break; + + default: + MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " + "IMPEDANCE_MODE field\n", + test_info->impedance_mode); + status = -EINVAL; + } + return status; +} + +void si_mhl_tx_drv_stop_impedance_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info) +{ + switch (test_info->impedance_mode) { + case BIST_IMPEDANCE_MODE_AVLINK_TX_LOW: + case BIST_IMPEDANCE_MODE_AVLINK_TX_HIGH: + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, 0); + break; + + case BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW: + case BIST_IMPEDANCE_MODE_ECBUS_S_TX_HIGH: + /* Restore previous values */ + mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x80); + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x00); + mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x07); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2F); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3F); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xBC); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL0, 0xC3); + mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x5C); + mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x0A); + mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x14); + + mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x04); + mhl_tx_write_reg(hw_context, REG_COC_CTLB, 0xFF); + mhl_tx_write_reg(hw_context, REG_COC_CTLC, 0xFF); + mhl_tx_write_reg(hw_context, REG_COC_CTLD, 0x21); + mhl_tx_write_reg(hw_context, REG_COC_CTLE, 0x18); + + mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xE8); + mhl_tx_write_reg(hw_context, REG_BGR_BIAS, 0x87); + mhl_tx_write_reg(hw_context, REG_COC_CTL3, 0x40); + mhl_tx_write_reg(hw_context, REG_COC_CTL6, 0x00); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0xC7); + mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL4, 0x2A); + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x80); + mhl_tx_write_reg(hw_context, REG_M3_SCTRL, 0x41); + mhl_tx_write_reg(hw_context, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_HDCP_EN); + + mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, + 0x00); + mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x00); + mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x00); + break; + + case BIST_IMPEDANCE_MODE_ECBUS_D_TX_LOW: + case BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH: + mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x80); + mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, 0x07); + mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x07); + mhl_tx_write_reg(hw_context, REG_MHL_TOP_CTL, 0x00); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xBC); + mhl_tx_write_reg(hw_context, REG_MHL_DOC_CTL0, 0x00); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2F); + mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3F); + mhl_tx_write_reg(hw_context, REG_DOC_CTL0, 0x40); + mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x00); + mhl_tx_write_reg(hw_context, REG_DOC_CTLE, 0x00); + } +} + + +int si_mhl_tx_drv_sample_edid_buffer(struct drv_hw_context *hw_context, + uint8_t *edid_buf) +{ + mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); + mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, 256, + edid_buf); + return 0; +} + + + +int si_mhl_tx_drv_get_pp_16bpp_override(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + return (int)hw_context->pp_16bpp_override; +} +void si_mhl_tx_drv_set_pp_16bpp_override(struct mhl_dev_context *dev_context, + int override) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + hw_context->pp_16bpp_override = (enum pp_16bpp_override_t)override; +} + +int si_mhl_tx_drv_get_hpd_status(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + int ret_val; + + ret_val = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); + if (ret_val < 0) + return 0; + else if (ret_val & BIT_CBUS_STATUS_CBUS_HPD) + return 1; + else + return 0; +} + +uint32_t si_mhl_tx_drv_get_hdcp2_status(struct mhl_dev_context *dev_context) +{ + struct drv_hw_context *hw_context = + (struct drv_hw_context *)&dev_context->drv_context; + uint32_t ret_val; + + uint8_t ro_gp0; + uint8_t ro_auth[2]; + + /* Disable high-value content / enable mute */ + + ro_gp0 = mhl_tx_read_reg(hw_context, REG_HDCP2X_GP_OUT0); + mhl_tx_read_reg_block(hw_context, REG_HDCP2X_AUTH_STAT, + sizeof(ro_auth), ro_auth); + ret_val = (ro_gp0 << 16) + |(ro_auth[1] << 8) + | ro_auth[0]; + return ret_val; + +} diff --git a/drivers/video/fbdev/msm/mhl3/si_8620_drv.h b/drivers/video/fbdev/msm/mhl3/si_8620_drv.h new file mode 100644 index 000000000000..7c68ab172c3b --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_8620_drv.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#if !defined(SI_8620_DRV_H) +#define SI_8620_DRV_H + +extern uint8_t dev_cap_values[16]; + +enum pp_16bpp_override_t { + pp_16bpp_automatic = 0x00, + pp_16bpp_override_24bpp = 0x01, + pp_16bpp_override_16bpp = 0x02 +}; +struct drv_hw_context { + struct interrupt_info *intr_info; + uint8_t chip_rev_id; + uint16_t chip_device_id; + uint8_t cbus_status; + uint8_t gen2_write_burst_rcv; + uint8_t gen2_write_burst_xmit; + uint8_t hawb_write_pending; + enum { + CBUS1_IDLE_RCV_DISABLED, + CBUS1_IDLE_RCV_ENABLED, + CBUS1_IDLE_RCV_PEND, + CBUS1_MSC_PEND_DLY_RCV_EN, + CBUS1_MSC_PEND_DLY_RCV_DIS, + CBUS1_XMIT_PEND_XMIT_RCV_EN, + CBUS1_XMIT_PEND_XMIT_RCV_PEND + } cbus1_state; + uint8_t delayed_hawb_enable_reg_val; + uint8_t video_path; + uint8_t video_ready; + uint8_t mhl_peer_version_stat; + enum cbus_mode_e cbus_mode; + uint8_t current_edid_req_blk; + uint8_t edid_fifo_block_number; + uint8_t valid_vsif; +#ifdef NEVER_USED + uint8_t valid_avif; +#endif + uint8_t rx_hdmi_ctrl2_defval; + uint8_t aksv[5]; + struct avi_info_frame_t current_avi_info_frame; + union SI_PACK_THIS_STRUCT vsif_mhl3_or_hdmi_u current_vsif; + union hw_avi_payload_t outgoingAviPayLoad; + struct mhl3_vsif_t outgoing_mhl3_vsif; + uint8_t write_burst_data[MHL_SCRATCHPAD_SIZE]; + struct cbus_req current_cbus_req; + uint8_t tdm_virt_chan_slot_counts[VC_MAX]; + struct { + uint16_t received_byte_count; + unsigned long peer_blk_rx_buf_avail; + unsigned long peer_blk_rx_buf_max; + +#define NUM_BLOCK_INPUT_BUFFERS 8 + uint8_t input_buffers[NUM_BLOCK_INPUT_BUFFERS][256]; + int input_buffer_lengths[NUM_BLOCK_INPUT_BUFFERS]; + uint16_t head; + uint16_t tail; + } block_protocol; + struct si_mhl_callback_api_t callbacks; + union avif_or_cea_861_dtd_u avif_or_dtd_from_callback; + union vsif_mhl3_or_hdmi_u vsif_mhl3_or_hdmi_from_callback; + int hpd_high_callback_status; +#ifdef MANUAL_EDID_FETCH + uint8_t edid_block[EDID_BLOCK_SIZE]; +#endif + void *input_field_rate_measurement_timer; + uint8_t idx_pixel_clock_history; + uint32_t pixel_clock_history[16]; + bool hdcp2_started; + enum pp_16bpp_override_t pp_16bpp_override; + uint8_t prev_bist_coc_status[6]; +}; + +bool si_mhl_tx_set_status(struct mhl_dev_context *dev_context, + bool xstat, uint8_t reg_to_write, uint8_t value); +void *si_mhl_tx_get_sub_payload_buffer(struct mhl_dev_context *dev_context, + uint8_t size); +bool si_mhl_tx_send_write_burst(struct mhl_dev_context *dev_context, + void *buffer); +int si_mhl_tx_drv_cbus_ready_for_edid(struct mhl_dev_context *dev_context); +int si_mhl_tx_drv_set_display_mode(struct mhl_dev_context *dev_context, + enum hpd_high_callback_status status); +int si_mhl_tx_drv_sample_edid_buffer(struct drv_hw_context *hw_context, + uint8_t *edid_buffer); + +void si_set_cbus_mode_leds_impl(enum cbus_mode_e mode_sel, + const char *func_name, int line_num); +#define si_set_cbus_mode_leds(mode_sel) \ + si_set_cbus_mode_leds_impl(mode_sel, \ + __func__, __LINE__) +void si_dump_important_regs(struct drv_hw_context *hw_context); + +int si_mhl_tx_drv_get_pp_16bpp_override(struct mhl_dev_context *dev_context); +void si_mhl_tx_drv_set_pp_16bpp_override(struct mhl_dev_context *dev_context, + int override); +int si_mhl_tx_drv_get_hpd_status(struct mhl_dev_context *dev_context); +uint32_t si_mhl_tx_drv_get_hdcp2_status(struct mhl_dev_context *dev_context); +#endif /* if !defined(SI_8620_DRV_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/si_8620_internal_api.h b/drivers/video/fbdev/msm/mhl3/si_8620_internal_api.h new file mode 100644 index 000000000000..378bf7872fa4 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_8620_internal_api.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +struct drv_hw_context; + +void si_mhl_tx_drv_skip_to_next_edid_block(struct drv_hw_context *hw_context); +int si_mhl_tx_drv_get_edid_fifo_partial_block(struct drv_hw_context *hw_context, + uint8_t start, uint8_t length, uint8_t *edid_buf); +int si_mhl_tx_drv_get_edid_fifo_next_block(struct drv_hw_context *hw_context, + uint8_t *edid_buf); +int si_mhl_tx_drv_set_upstream_edid(struct drv_hw_context *hw_context, + uint8_t *edid, uint16_t length); +void setup_sans_cbus1(struct mhl_dev_context *dev_context); +uint8_t si_get_peer_mhl_version(struct mhl_dev_context *drv_context); +int si_peer_supports_packed_pixel(void *drv_context); + +uint16_t si_mhl_tx_drv_get_incoming_timing(struct drv_hw_context *hw_context, + struct si_incoming_timing_t *p_timing); diff --git a/drivers/video/fbdev/msm/mhl3/si_8620_regs.h b/drivers/video/fbdev/msm/mhl3/si_8620_regs.h new file mode 100644 index 000000000000..1913845d421f --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_8620_regs.h @@ -0,0 +1,1988 @@ +/* + * 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. + */ + +#ifndef _SI_8620_REGS_H_ +#define _SI_8620_REGS_H_ + +#ifndef ALT_I2C_ADDR + #define SA_TX_PAGE_0 (0x72) + #define SA_TX_PAGE_1 (0x7A) + #define SA_TX_PAGE_2 (0x92) + #define SA_TX_PAGE_3 (0x9A) + #define SA_TX_PAGE_4 (0xBA) + #define SA_TX_PAGE_6 (0xB2) + #define SA_TX_PAGE_7 (0xC2) + #define SA_TX_PAGE_8 (0xD2) +#else + #define SA_TX_PAGE_0 (0x76) + #define SA_TX_PAGE_1 (0x7E) + #define SA_TX_PAGE_2 (0x96) + #define SA_TX_PAGE_3 (0x9E) + #define SA_TX_PAGE_4 (0xBE) + #define SA_TX_PAGE_6 (0xB6) + #define SA_TX_PAGE_7 (0xC6) + #define SA_TX_PAGE_8 (0xD6) +#endif + +#define SA_TX_CBUS (0xC8) + +#define TX_PAGE_0 (SA_TX_PAGE_0 << 8) +#define TX_PAGE_1 (SA_TX_PAGE_1 << 8) +#define TX_PAGE_2 (SA_TX_PAGE_2 << 8) +#define TX_PAGE_3 (SA_TX_PAGE_3 << 8) +#define TX_PAGE_4 (SA_TX_PAGE_4 << 8) +#define TX_PAGE_5 (SA_TX_CBUS << 8) +#define TX_PAGE_6 (SA_TX_PAGE_6 << 8) +#define TX_PAGE_7 (SA_TX_PAGE_7 << 8) +#define TX_PAGE_8 (SA_TX_PAGE_8 << 8) + +/* Registers in TX_PAGE_0 (0x00-0xFF) */ + +/* 0x00 Vendor ID Low byte Register (Default: 0x01) */ +#define REG_VND_IDL (TX_PAGE_0 | 0x00) +#define MSK_VND_IDL_VHDL_IDL (0xFF) + +/* 0x01 Vendor ID High byte Register (Default: 0x00) */ +#define REG_VND_IDH (TX_PAGE_0 | 0x01) +#define MSK_VND_IDH_VHDL_IDH (0xFF) + +/* 0x02 Device ID Low byte Register (Default: 0x60) */ +#define REG_DEV_IDL (TX_PAGE_0 | 0x02) +#define MSK_DEV_IDL_DEV_IDL (0xFF) + +/* 0x03 Device ID High byte Register (Default: 0x86) */ +#define REG_DEV_IDH (TX_PAGE_0 | 0x03) +#define MSK_DEV_IDH_DEV_IDH (0xFF) + +/* 0x04 Device Revision Register (Default: 0x10) */ +#define REG_DEV_REV (TX_PAGE_0 | 0x04) +#define MSK_DEV_REV_DEV_REV_ID (0xFF) + +/* 0x06 OTP DBYTE510 Register (Default: 0x00) */ +#define REG_OTP_DBYTE510 (TX_PAGE_0 | 0x06) +#define MSK_OTP_DBYTE510_OTP_DBYTE510 (0xFF) + +/* 0x08 System Control #1 Register (Default: 0x00) */ +#define REG_SYS_CTRL1 (TX_PAGE_0 | 0x08) +#define BIT_SYS_CTRL1_OTPVMUTEOVR_SET (0x80) +#define BIT_SYS_CTRL1_VSYNCPIN (0x40) +#define BIT_SYS_CTRL1_OTPADROPOVR_SET (0x20) +#define BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD (0x10) +#define BIT_SYS_CTRL1_OTP2XVOVR_EN (0x08) +#define BIT_SYS_CTRL1_OTP2XAOVR_EN (0x04) +#define BIT_SYS_CTRL1_TX_CONTROL_HDMI (0x02) +#define BIT_SYS_CTRL1_OTPAMUTEOVR_SET (0x01) + +/* 0x0B System Control DPD (Default: 0x90) */ +#define REG_DPD (TX_PAGE_0 | 0x0B) +#define BIT_DPD_PWRON_PLL (0x80) +#define BIT_DPD_PDNTX12 (0x40) +#define BIT_DPD_PDNRX12 (0x20) +#define BIT_DPD_OSC_EN (0x10) +#define BIT_DPD_PWRON_HSIC (0x08) +#define BIT_DPD_PDIDCK_N (0x04) +#define BIT_DPD_PD_MHL_CLK_N (0x02) + +/* 0x0D Dual link Control Register (Default: 0x00) */ +#define REG_DCTL (TX_PAGE_0 | 0x0D) +#define BIT_DCTL_TDM_LCLK_PHASE (0x80) +#define BIT_DCTL_HSIC_CLK_PHASE (0x40) +#define BIT_DCTL_CTS_TCK_PHASE (0x20) +#define BIT_DCTL_EXT_DDC_SEL (0x10) +#define BIT_DCTL_TRANSCODE (0x08) +#define BIT_DCTL_HSIC_RX_STROBE_PHASE (0x04) +#define BIT_DCTL_HSIC_TX_BIST_START_SEL (0x02) +#define BIT_DCTL_TCLKNX_PHASE (0x01) + +/* 0x0E PWD Software Reset Register (Default: 0x20) */ +#define REG_PWD_SRST (TX_PAGE_0 | 0x0E) +#define BIT_PWD_SRST_COC_DOC_RST (0x80) +#define BIT_PWD_SRST_CBUS_RST_SW (0x40) +#define BIT_PWD_SRST_CBUS_RST_SW_EN (0x20) +#define BIT_PWD_SRST_MHLFIFO_RST (0x10) +#define BIT_PWD_SRST_CBUS_RST (0x08) +#define BIT_PWD_SRST_SW_RST_AUTO (0x04) +#define BIT_PWD_SRST_HDCP2X_SW_RST (0x02) +#define BIT_PWD_SRST_SW_RST (0x01) + +/* 0x1D AKSV_1 Register (Default: 0x00) */ +#define REG_AKSV_1 (TX_PAGE_0 | 0x1D) +#define MSK_AKSV_1_AKSV0 (0xFF) + +/* 0x3A Video H Resolution #1 Register (Default: 0x00) */ +#define REG_H_RESL (TX_PAGE_0 | 0x3A) +#define MSK_H_RESL_HRESOUT_7_0 (0xFF) + +/* 0x4A Video Mode Register (Default: 0x00) */ +#define REG_VID_MODE (TX_PAGE_0 | 0x4A) +#define BIT_VID_MODE_M1080P (0x40) +#define VAL_VID_MODE_M1080P_DISABLE (0x00) +#define VAL_VID_MODE_M1080P_ENABLE (0x40) + +/* 0x51 Video Input Mode Register (Default: 0xC0) */ +#define REG_VID_OVRRD (TX_PAGE_0 | 0x51) +#define REG_VID_OVRRD_DEFVAL 0x80 +#define BIT_VID_OVRRD_PP_AUTO_DISABLE (0x80) +#define BIT_VID_OVRRD_M1080P_OVRRD (0x40) +#define BIT_VID_OVRRD_MINIVSYNC_ON (0x20) + +#define BIT_VID_OVRRD_3DCONV_EN (0x10) +#define VAL_VID_OVRRD_3DCONV_EN_NORMAL (0x00) +#define VAL_VID_OVRRD_3DCONV_EN_FRAME_PACK (0x10) + +#define BIT_VID_OVRRD_ENABLE_AUTO_PATH_EN (0x08) +#define BIT_VID_OVRRD_ENRGB2YCBCR_OVRRD (0x04) +#define BIT_VID_OVRRD_ENDOWNSAMPLE_OVRRD (0x01) + +/* I2C Address reassignment (Default: 0x00) */ +#define REG_PAGE_MHLSPEC_ADDR (TX_PAGE_0 | 0x57) +#define REG_PAGE7_ADDR (TX_PAGE_0 | 0x58) +#define REG_PAGE8_ADDR (TX_PAGE_0 | 0x5C) + +/* 0x5F Fast Interrupt Status Register (Default: 0x00) */ +#define REG_FAST_INTR_STAT (TX_PAGE_0 | 0x5F) +#define BIT_FAST_INTR_STAT_FAST_INTR_STAT_4 (0x10) +#define BIT_FAST_INTR_STAT_FAST_INTR_STAT_3 (0x08) +#define BIT_FAST_INTR_STAT_FAST_INTR_STAT_2 (0x04) +#define BIT_FAST_INTR_STAT_FAST_INTR_STAT_1 (0x02) +#define BIT_FAST_INTR_STAT_FAST_INTR_STAT_0 (0x01) + +/* 0x6E GPIO Control Register 1 (Default: 0x15) */ +#define REG_GPIO_CTRL1 (TX_PAGE_0 | 0x6E) +#define BIT_CTRL1_GPIO_I_8 (0x20) +#define BIT_CTRL1_GPIO_OEN_8 (0x10) +#define BIT_CTRL1_GPIO_I_7 (0x08) +#define BIT_CTRL1_GPIO_OEN_7 (0x04) +#define BIT_CTRL1_GPIO_I_6 (0x02) +#define BIT_CTRL1_GPIO_OEN_6 (0x01) + +/* 0x6F Interrupt Control Register (Default: 0x06) */ +#define REG_INT_CTRL (TX_PAGE_0 | 0x6F) +#define BIT_INT_CTRL_SOFTWARE_WP (0x80) +#define BIT_INT_CTRL_INTR_OD (0x04) +#define BIT_INT_CTRL_INTR_POLARITY (0x02) + +/* 0x70 Interrupt State Register (Default: 0x00) */ +#define REG_INTR_STATE (TX_PAGE_0 | 0x70) +#define BIT_INTR_STATE_INTR_STATE (0x01) + +/* 0x71 Interrupt Source #1 Register (Default: 0x00) */ +#define REG_INTR1 (TX_PAGE_0 | 0x71) +#define BIT_INTR1_STAT7 (0x80) +#define BIT_INTR1_STAT6 (0x40) +#define BIT_INTR1_STAT5 (0x20) +#define BIT_INTR1_STAT2 (0x04) + +/* 0x72 Interrupt Source #2 Register (Default: 0x00) */ +#define REG_INTR2 (TX_PAGE_0 | 0x72) +#define BIT_INTR2_STAT7 (0x80) +#define BIT_INTR2_STAT5 (0x20) +#define BIT_INTR2_STAT2 (0x04) +#define BIT_INTR2_STAT1 (0x02) +#define BIT_INTR2_STAT0 (0x01) + +/* 0x73 Interrupt Source #3 Register (Default: 0x01) */ +#define REG_INTR3 (TX_PAGE_0 | 0x73) +#define BIT_INTR3_STAT7 (0x80) +#define BIT_INTR3_STAT6 (0x40) +#define BIT_INTR3_STAT5 (0x20) +#define BIT_INTR3_STAT4 (0x10) +#define BIT_INTR3_STAT3 (0x08) +#define BIT_INTR3_STAT2 (0x04) +#define BIT_INTR3_STAT1 (0x02) +#define BIT_INTR3_STAT0 (0x01) + +/* 0x74 Interrupt Source #5 Register (Default: 0x00) */ +#define REG_INTR5 (TX_PAGE_0 | 0x74) +#define BIT_INTR5_STAT7 (0x80) +#define BIT_INTR5_STAT6 (0x40) +#define BIT_INTR5_STAT5 (0x20) +#define BIT_INTR5_STAT4 (0x10) +#define BIT_INTR5_STAT3 (0x08) +#define BIT_INTR5_STAT2 (0x04) +#define BIT_INTR5_STAT1 (0x02) +#define BIT_INTR5_STAT0 (0x01) + +/* 0x75 Interrupt #1 Mask Register (Default: 0x00) */ +#define REG_INTR1_MASK (TX_PAGE_0 | 0x75) +#define BIT_INTR1_MASK7 (0x80) +#define BIT_INTR1_MASK6 (0x40) +#define BIT_INTR1_MASK5 (0x20) +#define BIT_INTR1_MASK2 (0x04) + +/* 0x76 Interrupt #2 Mask Register (Default: 0x00) */ +#define REG_INTR2_MASK (TX_PAGE_0 | 0x76) +#define BIT_INTR2_MASK7 (0x80) +#define BIT_INTR2_MASK5 (0x20) +#define BIT_INTR2_MASK2 (0x04) +#define BIT_INTR2_MASK1 (0x02) +#define BIT_INTR2_MASK0 (0x01) + +/* 0x77 Interrupt #3 Mask Register (Default: 0x00) */ +#define REG_INTR3_MASK (TX_PAGE_0 | 0x77) +#define BIT_INTR3_MASK7 (0x80) +#define BIT_INTR3_MASK6 (0x40) +#define BIT_INTR3_MASK5 (0x20) +#define BIT_INTR3_MASK4 (0x10) +#define BIT_INTR3_MASK3 (0x08) +#define BIT_INTR3_MASK2 (0x04) +#define BIT_INTR3_MASK1 (0x02) +#define BIT_INTR3_MASK0 (0x01) + +/* 0x78 Interrupt #5 Mask Register (Default: 0x00) */ +#define REG_INTR5_MASK (TX_PAGE_0 | 0x78) +#define BIT_INTR5_MASK7 (0x80) +#define BIT_INTR5_MASK6 (0x40) +#define BIT_INTR5_MASK5 (0x20) +#define BIT_INTR5_MASK4 (0x10) +#define BIT_INTR5_MASK3 (0x08) +#define BIT_INTR5_MASK2 (0x04) +#define BIT_INTR5_MASK1 (0x02) +#define BIT_INTR5_MASK0 (0x01) + +/* 0x79 Hot Plug Connection Control Register (Default: 0x45) */ +#define REG_HPD_CTRL (TX_PAGE_0 | 0x79) +#define BIT_HPD_CTRL_HPD_DS_SIGNAL (0x80) +#define BIT_HPD_CTRL_HPD_OUT_OD_EN (0x40) + +#define BIT_HPD_CTRL_HPD_OUT_OVR_VAL (0x20) +#define VAL_HPD_CTRL_HPD_LOW (0x00) +#define VAL_HPD_CTRL_HPD_HIGH (0x20) + +#define BIT_HPD_CTRL_HPD_OUT_OVR_EN (0x10) +#define VAL_HPD_CTRL_HPD_OUT_OVR_EN_OFF (0x00) +#define VAL_HPD_CTRL_HPD_OUT_OVR_EN_ON (0x10) + +#define BIT_HPD_CTRL_GPIO_I_1 (0x08) +#define BIT_HPD_CTRL_GPIO_OEN_1 (0x04) +#define BIT_HPD_CTRL_GPIO_I_0 (0x02) +#define BIT_HPD_CTRL_GPIO_OEN_0 (0x01) + +/* 0x7A GPIO Control Register (Default: 0x55) */ +#define REG_GPIO_CTRL (TX_PAGE_0 | 0x7A) +#define BIT_CTRL_GPIO_I_5 (0x80) +#define BIT_CTRL_GPIO_OEN_5 (0x40) +#define BIT_CTRL_GPIO_I_4 (0x20) +#define BIT_CTRL_GPIO_OEN_4 (0x10) +#define BIT_CTRL_GPIO_I_3 (0x08) +#define BIT_CTRL_GPIO_OEN_3 (0x04) +#define BIT_CTRL_GPIO_I_2 (0x02) +#define BIT_CTRL_GPIO_OEN_2 (0x01) + +/* 0x7B Interrupt Source 7 Register (Default: 0x00) */ +#define REG_INTR7 (TX_PAGE_0 | 0x7B) +#define BIT_INTR7_STAT7 (0x80) +#define BIT_INTR7_STAT6 (0x40) +#define BIT_INTR7_STAT4 (0x10) +#define BIT_INTR7_STAT3 (0x08) +#define BIT_INTR7_STAT2 (0x04) +#define BIT_INTR7_STAT1 (0x02) + +/* 0x7C Interrupt Source 8 Register (Default: 0x00) */ +#define REG_INTR8 (TX_PAGE_0 | 0x7C) +#define BIT_INTR8_STAT6 (0x40) +#define BIT_INTR8_STAT5 (0x20) +#define BIT_INTR8_STAT4 (0x10) +#define BIT_INTR8_STAT3 (0x08) +#define BIT_INTR8_STAT2 (0x04) +#define BIT_INTR8_STAT1 (0x02) +#define BIT_INTR8_STAT0 (0x01) + +/* 0x7D Interrupt #7 Mask Register (Default: 0x00) */ +#define REG_INTR7_MASK (TX_PAGE_0 | 0x7D) +#define BIT_INTR7_MASK7 (0x80) +#define BIT_INTR7_MASK6 (0x40) +#define BIT_INTR7_MASK4 (0x10) +#define BIT_INTR7_MASK3 (0x08) +#define BIT_INTR7_MASK2 (0x04) +#define BIT_INTR7_MASK1 (0x02) + +/* 0x7E Interrupt #8 Mask Register (Default: 0x00) */ +#define REG_INTR8_MASK (TX_PAGE_0 | 0x7E) +#define BIT_INTR8_MASK6 (0x40) +#define BIT_INTR8_MASK5 (0x20) +#define BIT_INTR8_MASK4 (0x10) +#define BIT_INTR8_MASK3 (0x08) +#define BIT_INTR8_MASK2 (0x04) +#define BIT_INTR8_MASK1 (0x02) +#define BIT_INTR8_MASK0 (0x01) + +/* 0x80 IEEE (Default: 0x10) */ +#define REG_TMDS_CCTRL (TX_PAGE_0 | 0x80) +#define BIT_TMDS_CCTRL_TMDS_OE (0x10) + +/* 0x85 TMDS Control #4 Register (Default: 0x02) */ +#define REG_TMDS_CTRL4 (TX_PAGE_0 | 0x85) +#define BIT_TMDS_CTRL4_SCDT_CKDT_SEL (0x02) +#define BIT_TMDS_CTRL4_TX_EN_BY_SCDT (0x01) + +/* 0xBB BIST CNTL Register (Default: 0x00) */ +#define REG_BIST_CTRL (TX_PAGE_0 | 0xBB) +#define BIT_RXBIST_VGB_EN (0x80) +#define BIT_TXBIST_VGB_EN (0x40) +#define BIT_BIST_START_SEL (0x20) +#define BIT_BIST_START_BIT (0x10) +#define BIT_BIST_ALWAYS_ON (0x08) +#define BIT_BIST_TRANS (0x04) +#define BIT_BIST_RESET (0x02) +#define BIT_BIST_EN (0x01) + +/* 0xBD BIST DURATION0 Register (Default: 0x00) */ +#define REG_BIST_TEST_SEL (TX_PAGE_0 | 0xBD) +#define MSK_BIST_TEST_SEL_BIST_PATT_SEL (0x0F) + +/* 0xBE BIST VIDEO_MODE Register (Default: 0x00) */ +#define REG_BIST_VIDEO_MODE (TX_PAGE_0 | 0xBE) +#define MSK_BIST_VIDEO_MODE_BIST_VIDEO_MODE_3_0 (0x0F) + +/* 0xBF BIST DURATION0 Register (Default: 0x00) */ +#define REG_BIST_DURATION_0 (TX_PAGE_0 | 0xBF) +#define MSK_BIST_DURATION_0_BIST_DURATION_7_0 (0xFF) + +/* 0xC0 BIST DURATION1 Register (Default: 0x00) */ +#define REG_BIST_DURATION_1 (TX_PAGE_0 | 0xC0) +#define MSK_BIST_DURATION_1_BIST_DURATION_15_8 (0xFF) + +/* 0xC1 BIST DURATION2 Register (Default: 0x00) */ +#define REG_BIST_DURATION_2 (TX_PAGE_0 | 0xC1) +#define MSK_BIST_DURATION_2_BIST_DURATION_22_16 (0x7F) + +/* 0xC2 BIST 8BIT_PATTERN Register (Default: 0x00) */ +#define REG_BIST_8BIT_PATTERN (TX_PAGE_0 | 0xC2) +#define MSK_BIST_8BIT_PATTERN_BIST_10BIT_PATT28LSB (0xFF) + +/* 0xC7 LM DDC Register (Default: 0x80) */ +#define REG_LM_DDC (TX_PAGE_0 | 0xC7) +#define BIT_LM_DDC_SW_TPI_EN (0x80) +#define VAL_LM_DDC_SW_TPI_EN_ENABLED (0x00) +#define VAL_LM_DDC_SW_TPI_EN_DISABLED (0x80) + +#define BIT_LM_DDC_VIDEO_MUTE_EN (0x20) +#define BIT_LM_DDC_DDC_TPI_SW (0x04) +#define BIT_LM_DDC_DDC_GRANT (0x02) +#define BIT_LM_DDC_DDC_GPU_REQUEST (0x01) + +/* 0xEC DDC I2C Manual Register (Default: 0x03) */ +#define REG_DDC_MANUAL (TX_PAGE_0 | 0xEC) +#define BIT_DDC_MANUAL_MAN_DDC (0x80) +#define BIT_DDC_MANUAL_VP_SEL (0x40) +#define BIT_DDC_MANUAL_DSDA (0x20) +#define BIT_DDC_MANUAL_DSCL (0x10) +#define BIT_DDC_MANUAL_GCP_HW_CTL_EN (0x08) +#define BIT_DDC_MANUAL_DDCM_ABORT_WP (0x04) +#define BIT_DDC_MANUAL_IO_DSDA (0x02) +#define BIT_DDC_MANUAL_IO_DSCL (0x01) + +/* 0xED DDC I2C Target Slave Address Register (Default: 0x00) */ +#define REG_DDC_ADDR (TX_PAGE_0 | 0xED) +#define MSK_DDC_ADDR_DDC_ADDR (0xFE) + +/* 0xEE DDC I2C Target Segment Address Register (Default: 0x00) */ +#define REG_DDC_SEGM (TX_PAGE_0 | 0xEE) +#define MSK_DDC_SEGM_DDC_SEGMENT (0xFF) + +/* 0xEF DDC I2C Target Offset Adress Register (Default: 0x00) */ +#define REG_DDC_OFFSET (TX_PAGE_0 | 0xEF) +#define MSK_DDC_OFFSET_DDC_OFFSET (0xFF) + +/* 0xF0 DDC I2C Data In count #1 Register (Default: 0x00) */ +#define REG_DDC_DIN_CNT1 (TX_PAGE_0 | 0xF0) +#define MSK_DDC_DIN_CNT1_DDC_DIN_CNT_7_0 (0xFF) + +/* 0xF1 DDC I2C Data In count #2 Register (Default: 0x00) */ +#define REG_DDC_DIN_CNT2 (TX_PAGE_0 | 0xF1) +#define MSK_DDC_DIN_CNT2_DDC_DIN_CNT_9_8 (0x03) + +/* 0xF2 DDC I2C Status Register (Default: 0x04) */ +#define REG_DDC_STATUS (TX_PAGE_0 | 0xF2) +#define BIT_DDC_STATUS_DDC_BUS_LOW (0x40) +#define BIT_DDC_STATUS_DDC_NO_ACK (0x20) +#define BIT_DDC_STATUS_DDC_I2C_IN_PROG (0x10) +#define BIT_DDC_STATUS_DDC_FIFO_FULL (0x08) +#define BIT_DDC_STATUS_DDC_FIFO_EMPTY (0x04) +#define BIT_DDC_STATUS_DDC_FIFO_READ_IN_SUE (0x02) +#define BIT_DDC_STATUS_DDC_FIFO_WRITE_IN_USE (0x01) + +/* 0xF3 DDC I2C Command Register (Default: 0x70) */ +#define REG_DDC_CMD (TX_PAGE_0 | 0xF3) +#define BIT_DDC_CMD_HDCP_DDC_EN (0x40) +#define BIT_DDC_CMD_SDA_DEL_EN (0x20) +#define BIT_DDC_CMD_DDC_FLT_EN (0x10) + +#define MSK_DDC_CMD_DDC_CMD (0x0F) +#define VAL_DDC_CMD_ENH_DDC_READ_NO_ACK (0x04) +#define VAL_DDC_CMD_DDC_CMD_CLEAR_FIFO (0x09) +#define VAL_DDC_CMD_DDC_CMD_ABORT (0x0F) + +/* 0xF4 DDC I2C FIFO Data In/Out Register (Default: 0x00) */ +#define REG_DDC_DATA (TX_PAGE_0 | 0xF4) +#define MSK_DDC_DATA_DDC_DATA_OUT (0xFF) + +/* 0xF5 DDC I2C Data Out Counter Register (Default: 0x00) */ +#define REG_DDC_DOUT_CNT (TX_PAGE_0 | 0xF5) +#define BIT_DDC_DOUT_CNT_DDC_DELAY_CNT_8 (0x80) +#define MSK_DDC_DOUT_CNT_DDC_DATA_OUT_CNT (0x1F) + +/* 0xF6 DDC I2C Delay Count Register (Default: 0x14) */ +#define REG_DDC_DELAY_CNT (TX_PAGE_0 | 0xF6) +#define MSK_DDC_DELAY_CNT_DDC_DELAY_CNT_7_0 (0xFF) + +/* 0xF7 Test Control Register (Default: 0x80) */ +#define REG_TEST_TXCTRL (TX_PAGE_0 | 0xF7) +#define BIT_TEST_TXCTRL_RCLK_REF_SEL (0x80) +#define BIT_TEST_TXCTRL_PCLK_REF_SEL (0x40) +#define MSK_TEST_TXCTRL_BYPASS_PLL_CLK (0x3C) +#define BIT_TEST_TXCTRL_HDMI_MODE (0x02) +#define BIT_TEST_TXCTRL_TST_PLLCK (0x01) + +/* 0xF8 CBUS Address Register (Default: 0x00) */ +#define REG_PAGE_CBUS_ADDR (TX_PAGE_0 | 0xF8) +#define MSK_PAGE_CBUS_ADDR_PAGE_CBUS_ADDR_7_0 (0xFF) + +/* I2C Device Address re-assignment */ +#define REG_PAGE1_ADDR (TX_PAGE_0 | 0xFC) +#define REG_PAGE2_ADDR (TX_PAGE_0 | 0xFD) +#define REG_PAGE3_ADDR (TX_PAGE_0 | 0xFE) +#define REG_HW_TPI_ADDR (TX_PAGE_0 | 0xFF) + +/* Registers in TX_PAGE_1 (0x00-0xFF) */ + +/* 0x00 USBT CTRL0 Register (Default: 0x00) */ +#define REG_UTSRST (TX_PAGE_1 | 0x00) +#define BIT_UTSRST_FC_SRST (0x20) +#define BIT_UTSRST_KEEPER_SRST (0x10) +#define BIT_UTSRST_HTX_SRST (0x08) +#define BIT_UTSRST_TRX_SRST (0x04) +#define BIT_UTSRST_TTX_SRST (0x02) +#define BIT_UTSRST_HRX_SRST (0x01) + +/* 0x04 HSIC RX Control3 Register (Default: 0x07) */ +#define REG_HRXCTRL3 (TX_PAGE_1 | 0x04) +#define MSK_HRXCTRL3_HRX_AFFCTRL (0xF0) +#define BIT_HRXCTRL3_HRX_OUT_EN (0x04) +#define BIT_HRXCTRL3_STATUS_EN (0x02) +#define BIT_HRXCTRL3_HRX_STAY_RESET (0x01) + +/* HSIC RX INT Registers */ +#define REG_HRXINTL (TX_PAGE_1 | 0x11) +#define REG_HRXINTH (TX_PAGE_1 | 0x12) + +/* 0x16 TDM TX NUMBITS Register (Default: 0x0C) */ +#define REG_TTXNUMB (TX_PAGE_1 | 0x16) +#define MSK_TTXNUMB_TTX_AFFCTRL_3_0 (0xF0) +#define BIT_TTXNUMB_TTX_COM1_AT_SYNC_WAIT (0x08) +#define MSK_TTXNUMB_TTX_NUMBPS_2_0 (0x07) + +/* 0x17 TDM TX NUMSPISYM Register (Default: 0x04) */ +#define REG_TTXSPINUMS (TX_PAGE_1 | 0x17) +#define MSK_TTXSPINUMS_TTX_NUMSPISYM (0xFF) + +/* 0x18 TDM TX NUMHSICSYM Register (Default: 0x14) */ +#define REG_TTXHSICNUMS (TX_PAGE_1 | 0x18) +#define MSK_TTXHSICNUMS_TTX_NUMHSICSYM (0xFF) + +/* 0x19 TDM TX NUMTOTSYM Register (Default: 0x18) */ +#define REG_TTXTOTNUMS (TX_PAGE_1 | 0x19) +#define MSK_TTXTOTNUMS_TTX_NUMTOTSYM (0xFF) + +/* 0x36 TDM TX INT Low Register (Default: 0x00) */ +#define REG_TTXINTL (TX_PAGE_1 | 0x36) +#define BIT_TTXINTL_TTX_INTR7 (0x80) +#define BIT_TTXINTL_TTX_INTR6 (0x40) +#define BIT_TTXINTL_TTX_INTR5 (0x20) +#define BIT_TTXINTL_TTX_INTR4 (0x10) +#define BIT_TTXINTL_TTX_INTR3 (0x08) +#define BIT_TTXINTL_TTX_INTR2 (0x04) +#define BIT_TTXINTL_TTX_INTR1 (0x02) +#define BIT_TTXINTL_TTX_INTR0 (0x01) + +/* 0x37 TDM TX INT High Register (Default: 0x00) */ +#define REG_TTXINTH (TX_PAGE_1 | 0x37) +#define BIT_TTXINTH_TTX_INTR15 (0x80) +#define BIT_TTXINTH_TTX_INTR14 (0x40) +#define BIT_TTXINTH_TTX_INTR13 (0x20) +#define BIT_TTXINTH_TTX_INTR12 (0x10) +#define BIT_TTXINTH_TTX_INTR11 (0x08) +#define BIT_TTXINTH_TTX_INTR10 (0x04) +#define BIT_TTXINTH_TTX_INTR9 (0x02) +#define BIT_TTXINTH_TTX_INTR8 (0x01) + +/* 0x3B TDM RX Control Register (Default: 0x1C) */ +#define REG_TRXCTRL (TX_PAGE_1 | 0x3B) +#define BIT_TRXCTRL_TRX_CLR_WVALLOW (0x10) +#define BIT_TRXCTRL_TRX_FROM_SE_COC (0x08) +#define MSK_TRXCTRL_TRX_NUMBPS_2_0 (0x07) + +/* 0x3C TDM RX NUMSPISYM Register (Default: 0x04) */ +#define REG_TRXSPINUMS (TX_PAGE_1 | 0x3C) + +#define MSK_TRXSPINUMS_TRX_NUMSPISYM (0xFF << 0) + +/* 0x3D TDM RX NUMHSICSYM Register (Default: 0x14) */ +#define REG_TRXHSICNUMS (TX_PAGE_1 | 0x3D) + +#define MSK_TRXHSICNUMS_TRX_NUMHSICSYM (0xFF << 0) + +/* 0x3E TDM RX NUMTOTSYM Register (Default: 0x18) */ +#define REG_TRXTOTNUMS (TX_PAGE_1 | 0x3E) + +#define MSK_TRXTOTNUMS_TRX_NUMTOTSYM (0xFF << 0) + + +/* 0x5C TDM RX Status 2nd Register (Default: 0x00) */ +#define REG_TRXSTA2 (TX_PAGE_1 | 0x5C) +#define MSK_TRXSTA2_TRX_STATUS_15_8 (0xFF) + +/* 0x63 TDM RX INT Low Register (Default: 0x00) */ +#define REG_TRXINTL (TX_PAGE_1 | 0x63) +#define BIT_TRXINTL_TRX_INTR7 (0x80) +#define BIT_TRXINTL_TRX_INTR6 (0x40) +#define BIT_TRXINTL_TRX_INTR5 (0x20) +#define BIT_TRXINTL_TRX_INTR4 (0x10) +#define BIT_TRXINTL_TRX_INTR3 (0x08) +#define BIT_TRXINTL_TRX_INTR2 (0x04) +#define BIT_TRXINTL_TRX_INTR1 (0x02) +#define BIT_TRXINTL_TRX_INTR0 (0x01) + +/* 0x64 TDM RX INT High Register (Default: 0x00) */ +#define REG_TRXINTH (TX_PAGE_1 | 0x64) +#define BIT_TRXINTH_TRX_INTR15 (0x80) +#define BIT_TRXINTH_TRX_INTR14 (0x40) +#define BIT_TRXINTH_TRX_INTR13 (0x20) +#define BIT_TRXINTH_TRX_INTR12 (0x10) +#define BIT_TRXINTH_TRX_INTR11 (0x08) +#define BIT_TRXINTH_TRX_INTR10 (0x04) +#define BIT_TRXINTH_TRX_INTR9 (0x02) +#define BIT_TRXINTH_TRX_INTR8 (0x01) + +/* 0x66 TDM RX INTMASK High Register (Default: 0x00) */ +#define REG_TRXINTMH (TX_PAGE_1 | 0x66) +#define BIT_TRXINTMH_TRX_INTRMASK15 (0x80) +#define BIT_TRXINTMH_TRX_INTRMASK14 (0x40) +#define BIT_TRXINTMH_TRX_INTRMASK13 (0x20) +#define BIT_TRXINTMH_TRX_INTRMASK12 (0x10) +#define BIT_TRXINTMH_TRX_INTRMASK11 (0x08) +#define BIT_TRXINTMH_TRX_INTRMASK10 (0x04) +#define BIT_TRXINTMH_TRX_INTRMASK9 (0x02) +#define BIT_TRXINTMH_TRX_INTRMASK8 (0x01) + +/* 0x69 HSIC TX CRTL Register (Default: 0x00) */ +#define REG_HTXCTRL (TX_PAGE_1 | 0x69) +#define BIT_HTXCTRL_HTX_ALLSBE_SOP (0x10) +#define BIT_HTXCTRL_HTX_RGDINV_USB (0x08) +#define BIT_HTXCTRL_HTX_RSPTDM_BUSY (0x04) +#define BIT_HTXCTRL_HTX_DRVCONN1 (0x02) +#define BIT_HTXCTRL_HTX_DRVRST1 (0x01) + +/* 0x7D HSIC TX INT Low Register (Default: 0x00) */ +#define REG_HTXINTL (TX_PAGE_1 | 0x7D) +#define BIT_HTXINTL_HTX_INTR7 (0x80) +#define BIT_HTXINTL_HTX_INTR6 (0x40) +#define BIT_HTXINTL_HTX_INTR5 (0x20) +#define BIT_HTXINTL_HTX_INTR4 (0x10) +#define BIT_HTXINTL_HTX_INTR3 (0x08) +#define BIT_HTXINTL_HTX_INTR2 (0x04) +#define BIT_HTXINTL_HTX_INTR1 (0x02) +#define BIT_HTXINTL_HTX_INTR0 (0x01) + +/* 0x7E HSIC TX INT High Register (Default: 0x00) */ +#define REG_HTXINTH (TX_PAGE_1 | 0x7E) +#define BIT_HTXINTH_HTX_INTR15 (0x80) +#define BIT_HTXINTH_HTX_INTR14 (0x40) +#define BIT_HTXINTH_HTX_INTR13 (0x20) +#define BIT_HTXINTH_HTX_INTR12 (0x10) +#define BIT_HTXINTH_HTX_INTR11 (0x08) +#define BIT_HTXINTH_HTX_INTR10 (0x04) +#define BIT_HTXINTH_HTX_INTR9 (0x02) +#define BIT_HTXINTH_HTX_INTR8 (0x01) + +/* 0x81 HSIC Keeper Register (Default: 0x00) */ +#define REG_KEEPER (TX_PAGE_1 | 0x81) +#define MSK_KEEPER_KEEPER_MODE_1_0 (0x03) + +/* 0x83 HSIC Flow Control General Register (Default: 0x02) */ +#define REG_FCGC (TX_PAGE_1 | 0x83) +#define BIT_FCGC_HSIC_FC_HOSTMODE (0x02) +#define BIT_FCGC_HSIC_FC_ENABLE (0x01) + +/*----------------------------------------------------------------------------*/ +/* 0x91 HSIC Flow Control CTR13 Register (Default: 0xFC) */ +#define REG_FCCTR13 (TX_PAGE_1 | 0x91) + +#define MSK_FCCTR13_HFC_CONF13 (0xFF) + +/*----------------------------------------------------------------------------*/ +/* 0x92 HSIC Flow Control CTR14 Register (Default: 0xFF) */ +#define REG_FCCTR14 (TX_PAGE_1 | 0x92) + +#define MSK_FCCTR14_HFC_CONF14 (0xFF) + +/*----------------------------------------------------------------------------*/ +/* 0x93 HSIC Flow Control CTR15 Register (Default: 0xFF) */ +#define REG_FCCTR15 (TX_PAGE_1 | 0x93) + +#define MSK_FCCTR15_HFC_CONF15 (0xFF) + +/* 0xB6 HSIC Flow Control CTR50 Register (Default: 0x03) */ +#define REG_FCCTR50 (TX_PAGE_1 | 0xB6) + +#define MSK_FCCTR50_HFC_CONF50 (0xFF) + + + + +/* 0xEC HSIC Flow Control INTR0 Register (Default: 0x00) */ +#define REG_FCINTR0 (TX_PAGE_1 | 0xEC) +#define REG_FCINTR1 (TX_PAGE_1 | 0xED) +#define REG_FCINTR2 (TX_PAGE_1 | 0xEE) +#define REG_FCINTR3 (TX_PAGE_1 | 0xEF) +#define REG_FCINTR4 (TX_PAGE_1 | 0xF0) +#define REG_FCINTR5 (TX_PAGE_1 | 0xF1) +#define REG_FCINTR6 (TX_PAGE_1 | 0xF2) +#define REG_FCINTR7 (TX_PAGE_1 | 0xF3) + +/* 0xFC TDM Low Latency Register (Default: 0x20) */ +#define REG_TDMLLCTL (TX_PAGE_1 | 0xFC) +#define MSK_TDMLLCTL_TRX_LL_SEL_MANUAL (0xC0) +#define MSK_TDMLLCTL_TRX_LL_SEL_MODE (0x30) +#define MSK_TDMLLCTL_TTX_LL_SEL_MANUAL (0x0C) +#define BIT_TDMLLCTL_TTX_LL_TIE_LOW (0x02) +#define BIT_TDMLLCTL_TTX_LL_SEL_MODE (0x01) + +/* Registers in TX_PAGE_2 (0x00-0xFF) */ + +/* 0x10 TMDS 0 Clock Control Register 1 (Default: 0x10) */ +#define REG_TMDS0_CCTRL1 (TX_PAGE_2 | 0x10) +#define MSK_TMDS0_CCTRL1_TEST_SEL (0xC0) +#define MSK_TMDS0_CCTRL1_CLK1X_CTL (0x30) + +/* 0x11 TMDS Clock Enable Register (Default: 0x00) */ +#define REG_TMDS_CLK_EN (TX_PAGE_2 | 0x11) +#define BIT_TMDS_CLK_EN_CLK_EN (0x01) + +/* 0x12 TMDS Channel Enable Register (Default: 0x00) */ +#define REG_TMDS_CH_EN (TX_PAGE_2 | 0x12) +#define BIT_TMDS_CH_EN_CH0_EN (0x10) +#define BIT_TMDS_CH_EN_CH12_EN (0x01) + +/* 0x15 BGR_BIAS Register (Default: 0x07) */ +#define REG_BGR_BIAS (TX_PAGE_2 | 0x15) +#define BIT_BGR_BIAS_BGR_EN (0x80) +#define MSK_BGR_BIAS_BIAS_BGR_D (0x0F) + +/* 0x31 TMDS 0 Digital I2C BW Register (Default: 0x0A) */ +#define REG_ALICE0_BW_I2C (TX_PAGE_2 | 0x31) +#define MSK_ALICE0_BW_I2C_TMDS0_BW_I2C_7_0 (0xFF) + +/* 0x4C TMDS 0 Digital Zone Control Register (Default: 0xE0) */ +#define REG_ALICE0_ZONE_CTRL (TX_PAGE_2 | 0x4C) +#define BIT_ALICE0_ZONE_CTRL_ICRST_N (0x80) +#define BIT_ALICE0_ZONE_CTRL_USE_INT_DIV20 (0x40) +#define MSK_ALICE0_ZONE_CTRL_SZONE_I2C (0x30) +#define MSK_ALICE0_ZONE_CTRL_ZONE_CTRL (0x0F) + +/* 0x4D TMDS 0 Digital PLL Mode Control Register (Default: 0x00) */ +#define REG_ALICE0_MODE_CTRL (TX_PAGE_2 | 0x4D) +#define MSK_ALICE0_MODE_CTRL_PLL_MODE_I2C (0x0C) +#define MSK_ALICE0_MODE_CTRL_DIV20_CTRL (0x03) + +/* 0x85 MHL Tx Control 6th Register (Default: 0xA0) */ +#define REG_MHLTX_CTL6 (TX_PAGE_2 | 0x85) +#define MSK_MHLTX_CTL6_EMI_SEL (0xE0) +#define MSK_MHLTX_CTL6_TX_CLK_SHAPE_9_8 (0x03) + +/* 0x90 Packet Filter0 Register (Default: 0x00) */ +#define REG_PKT_FILTER_0 (TX_PAGE_2 | 0x90) +#define BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT (0x80) +#define BIT_PKT_FILTER_0_DROP_CEA_CP_PKT (0x40) +#define BIT_PKT_FILTER_0_DROP_MPEG_PKT (0x20) +#define BIT_PKT_FILTER_0_DROP_SPIF_PKT (0x10) +#define BIT_PKT_FILTER_0_DROP_AIF_PKT (0x08) +#define BIT_PKT_FILTER_0_DROP_AVI_PKT (0x04) +#define BIT_PKT_FILTER_0_DROP_CTS_PKT (0x02) +#define BIT_PKT_FILTER_0_DROP_GCP_PKT (0x01) + +/* 0x91 Packet Filter1 Register (Default: 0x00) */ +#define REG_PKT_FILTER_1 (TX_PAGE_2 | 0x91) +#define BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS (0x80) +#define BIT_PKT_FILTER_1_AVI_OVERRIDE_DIS (0x40) +#define BIT_PKT_FILTER_1_DROP_AUDIO_PKT (0x08) +#define BIT_PKT_FILTER_1_DROP_GEN2_PKT (0x04) +#define BIT_PKT_FILTER_1_DROP_GEN_PKT (0x02) +#define BIT_PKT_FILTER_1_DROP_VSIF_PKT (0x01) + +/* 0xA0 TMDS Clock Status Register (Default: 0x10) */ +#define REG_TMDS_CSTAT_P3 (TX_PAGE_2 | 0xA0) +#define BIT_TMDS_CSTAT_P3_RX_HDMI_CP_CLR_MUTE (0x80) +#define BIT_TMDS_CSTAT_P3_RX_HDMI_CP_SET_MUTE (0x40) +#define BIT_TMDS_CSTAT_P3_RX_HDMI_CP_NEW_CP (0x20) +#define BIT_TMDS_CSTAT_P3_CLR_AVI (0x08) +#define BIT_TMDS_CSTAT_P3_SCDT_CLR_AVI_DIS (0x04) +#define BIT_TMDS_CSTAT_P3_SCDT (0x02) + +#define BIT_TMDS_CSTAT_P3_CKDT (0x01) +#define VAL_TMDS_CSTAT_P3_CKDT_STOPPED (0x00) +#define VAL_TMDS_CSTAT_P3_CKDT_DETECTED (0x01) + +/* 0xA1 RX_HDMI Control Register0 (Default: 0x10) */ +#define REG_RX_HDMI_CTRL0 (TX_PAGE_2 | 0xA1) +#define BIT_RX_HDMI_CTRL0_BYP_DVIFILT_SYNC (0x20) +#define BIT_RX_HDMI_CTRL0_HDMI_MODE_EN_ITSELF_CLR (0x10) +#define BIT_RX_HDMI_CTRL0_HDMI_MODE_SW_VALUE (0x08) +#define BIT_RX_HDMI_CTRL0_HDMI_MODE_OVERWRITE (0x04) +#define BIT_RX_HDMI_CTRL0_RX_HDMI_HDMI_MODE_EN (0x02) +#define BIT_RX_HDMI_CTRL0_RX_HDMI_HDMI_MODE (0x01) + +/* 0xA3 RX_HDMI Control Register2 (Default: 0x38) */ +#define REG_RX_HDMI_CTRL2 (TX_PAGE_2 | 0xA3) +#define MSK_RX_HDMI_CTRL2_IDLE_CNT (0xF0) +#define BIT_RX_HDMI_CTRL2_USE_AV_MUTE (0x08) +#define VAL_RX_HDMI_CTRL2_USE_AV_MUTE_DISABLE (0x00) +#define VAL_RX_HDMI_CTRL2_USE_AV_MUTE_ENABLE (0x08) + +#define BIT_RX_HDMI_CTRL2_VSI_MON_SEL (0x01) +#define VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI (0x00) +#define VAL_RX_HDMI_CTRL2_VSI_MON_SEL_VSI (0x01) + +/* 0xA4 RX_HDMI Control Register3 (Default: 0x0F) */ +#define REG_RX_HDMI_CTRL3 (TX_PAGE_2 | 0xA4) +#define MSK_RX_HDMI_CTRL3_PP_MODE_CLK_EN (0x0F) + +/* 0xAC rx_hdmi Clear Buffer Register (Default: 0x00) */ +#define REG_RX_HDMI_CLR_BUFFER (TX_PAGE_2 | 0xAC) +#define MSK_RX_HDMI_CLR_BUFFER_AIF4VSI_CMP (0xC0) +#define BIT_RX_HDMI_CLR_BUFFER_USE_AIF4VSI (0x20) +#define BIT_RX_HDMI_CLR_BUFFER_VSI_CLR_W_AVI (0x10) +#define BIT_RX_HDMI_CLR_BUFFER_VSI_IEEE_ID_CHK_EN (0x08) +#define BIT_RX_HDMI_CLR_BUFFER_SWAP_VSI_IEEE_ID (0x04) +#define BIT_RX_HDMI_CLR_BUFFER_AIF_CLR_EN (0x02) + +#define BIT_RX_HDMI_CLR_BUFFER_VSI_CLR_EN (0x01) +#define VAL_RX_HDMI_CLR_BUFFER_VSI_CLR_EN_STALE (0x00) +#define VAL_RX_HDMI_CLR_BUFFER_VSI_CLR_EN_CLEAR (0x01) + +/* 0xB8 RX_HDMI VSI Header1 Register (Default: 0x00) */ +#define REG_RX_HDMI_MON_PKT_HEADER1 (TX_PAGE_2 | 0xB8) +#define MSK_RX_HDMI_MON_PKT_HEADER1_RX_HDMI_MON_PKT_HEADER_7_0 (0xFF) + +/* 0xD7 RX_HDMI VSI MHL Monitor Register (Default: 0x3C) */ +#define REG_RX_HDMI_VSIF_MHL_MON (TX_PAGE_2 | 0xD7) + +#define MSK_RX_HDMI_VSIF_MHL_MON_RX_HDMI_MHL_3D_FORMAT (0x3C) +#define MSK_RX_HDMI_VSIF_MHL_MON_RX_HDMI_MHL_VID_FORMAT (0x03) + +/* 0xE0 Interrupt Source 9 Register (Default: 0x00) */ +#define REG_INTR9 (TX_PAGE_2 | 0xE0) +#define BIT_INTR9_STAT6 (0x40) +#define BIT_INTR9_STAT5 (0x20) +#define BIT_INTR9_STAT4 (0x10) +#define BIT_INTR9_STAT1 (0x02) +#define BIT_INTR9_STAT0 (0x01) + +/* 0xE1 Interrupt 9 Mask Register (Default: 0x00) */ +#define REG_INTR9_MASK (TX_PAGE_2 | 0xE1) +#define BIT_INTR9_MASK6 (0x40) +#define BIT_INTR9_MASK5 (0x20) +#define BIT_INTR9_MASK4 (0x10) +#define BIT_INTR9_MASK1 (0x02) +#define BIT_INTR9_MASK0 (0x01) + +/* 0xE2 TPI CBUS Start Register (Default: 0x00) */ +#define REG_TPI_CBUS_START (TX_PAGE_2 | 0xE2) +#define BIT_TPI_CBUS_START_RCP_REQ_START (0x80) +#define BIT_TPI_CBUS_START_RCPK_REPLY_START (0x40) +#define BIT_TPI_CBUS_START_RCPE_REPLY_START (0x20) +#define BIT_TPI_CBUS_START_PUT_LINK_MODE_START (0x10) +#define BIT_TPI_CBUS_START_PUT_DCAPCHG_START (0x08) +#define BIT_TPI_CBUS_START_PUT_DCAPRDY_START (0x04) +#define BIT_TPI_CBUS_START_GET_EDID_START_0 (0x02) +#define BIT_TPI_CBUS_START_GET_DEVCAP_START (0x01) + +/* 0xE3 EDID Control Register (Default: 0x10) */ +#define REG_EDID_CTRL (TX_PAGE_2 | 0xE3) +#define BIT_EDID_CTRL_EDID_PRIME_VALID (0x80) +#define VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE (0x00) +#define VAL_EDID_CTRL_EDID_PRIME_VALID_ENABLE (0x80) + +#define BIT_EDID_CTRL_XDEVCAP_EN (0x40) +#define BIT_EDID_CTRL_DEVCAP_SEL (0x20) +#define VAL_EDID_CTRL_DEVCAP_SELECT_EDID (0x00) +#define VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP (0x20) + +#define BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO (0x10) +#define VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_DISABLE (0x00) +#define VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE (0x10) + +#define BIT_EDID_CTRL_EDID_FIFO_ACCESS_ALWAYS_EN (0x08) +#define BIT_EDID_CTRL_EDID_FIFO_BLOCK_SEL (0x04) +#define BIT_EDID_CTRL_INVALID_BKSV (0x02) + +#define BIT_EDID_CTRL_EDID_MODE_EN (0x01) +#define VAL_EDID_CTRL_EDID_MODE_EN_DISABLE (0x00) +#define VAL_EDID_CTRL_EDID_MODE_EN_ENABLE (0x01) + +/* 0xE9 EDID FIFO Addr Register (Default: 0x00) */ +#define REG_EDID_FIFO_ADDR (TX_PAGE_2 | 0xE9) +#define MSK_EDID_FIFO_ADDR_EDID_FIFO_ADDR (0xFF) + +/* 0xEA EDID FIFO Write Data Register (Default: 0x00) */ +#define REG_EDID_FIFO_WR_DATA (TX_PAGE_2 | 0xEA) +#define MSK_EDID_FIFO_WR_DATA_EDID_FIFO_WR_DATA (0xFF) + +/* 0xEB EDID/DEVCAP FIFO Internal Addr Register (Default: 0x00) */ +#define REG_EDID_FIFO_ADDR_MON (TX_PAGE_2 | 0xEB) +#define MSK_EDID_FIFO_ADDR_MON_EDID_FIFO_ADDR_MON (0xFF) + +/* 0xEC EDID FIFO Read Data Register (Default: 0x00) */ +#define REG_EDID_FIFO_RD_DATA (TX_PAGE_2 | 0xEC) +#define MSK_EDID_FIFO_RD_DATA_EDID_FIFO_RD_DATA (0xFF) + +/* 0xED EDID DDC Segment Pointer Register (Default: 0x00) */ +#define REG_EDID_START_EXT (TX_PAGE_2 | 0xED) + +/* 0xF2 TX IP BIST CNTL and Status Register (Default: 0x00) */ +#define REG_TX_IP_BIST_CNTLSTA (TX_PAGE_2 | 0xF2) +#define BIT_TX_IP_BIST_CNTLSTA_TXBIST_QUARTER_CLK_SEL (0x40) +#define BIT_TX_IP_BIST_CNTLSTA_TXBIST_DONE (0x20) +#define BIT_TX_IP_BIST_CNTLSTA_TXBIST_ON (0x10) +#define BIT_TX_IP_BIST_CNTLSTA_TXBIST_RUN (0x08) +#define BIT_TX_IP_BIST_CNTLSTA_TXCLK_HALF_SEL (0x04) +#define BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN (0x02) +#define BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL (0x01) + +/* 0xF3 TX IP BIST INST LOW Register (Default: 0x00) */ +#define REG_TX_IP_BIST_INST_LOW (TX_PAGE_2 | 0xF3) +#define REG_TX_IP_BIST_INST_HIGH (TX_PAGE_2 | 0xF4) + +/* 0xF5 TX IP BIST PATTERN LOW Register (Default: 0x00) */ +#define REG_TX_IP_BIST_PAT_LOW (TX_PAGE_2 | 0xF5) +#define REG_TX_IP_BIST_PAT_HIGH (TX_PAGE_2 | 0xF6) + +/* 0xF7 TX IP BIST CONFIGURE LOW Register (Default: 0x00) */ +#define REG_TX_IP_BIST_CONF_LOW (TX_PAGE_2 | 0xF7) +#define REG_TX_IP_BIST_CONF_HIGH (TX_PAGE_2 | 0xF8) + +/* Registers in TX_PAGE_3 (0x00-0xFF) */ + +/* 0x00 E-MSC General Control Register (Default: 0x80) */ +#define REG_GENCTL (TX_PAGE_3 | 0x00) +#define BIT_GENCTL_SPEC_TRANS_DIS (0x80) +#define BIT_GENCTL_DIS_XMIT_ERR_STATE (0x40) +#define BIT_GENCTL_SPI_MISO_EDGE (0x20) +#define BIT_GENCTL_SPI_MOSI_EDGE (0x10) +#define BIT_GENCTL_CLR_EMSC_RFIFO (0x08) +#define BIT_GENCTL_CLR_EMSC_XFIFO (0x04) +#define BIT_GENCTL_START_TRAIN_SEQ (0x02) +#define BIT_GENCTL_EMSC_EN (0x01) + +/* 0x05 E-MSC Comma ErrorCNT Register (Default: 0x03) */ +#define REG_COMMECNT (TX_PAGE_3 | 0x05) +#define BIT_COMMECNT_I2C_TO_EMSC_EN (0x80) +#define MSK_COMMECNT_COMMA_CHAR_ERR_CNT (0x0F) + +/* 0x1A E-MSC RFIFO ByteCnt Register (Default: 0x00) */ +#define REG_EMSCRFIFOBCNTL (TX_PAGE_3 | 0x1A) +#define REG_EMSCRFIFOBCNTH (TX_PAGE_3 | 0x1B) + +/* 0x1E SPI Burst Cnt Status Register (Default: 0x00) */ +#define REG_SPIBURSTCNT (TX_PAGE_3 | 0x1E) +#define MSK_SPIBURSTCNT_SPI_BURST_CNT (0xFF) + +/* 0x22 SPI Burst Status and SWRST Register (Default: 0x00) */ +#define REG_SPIBURSTSTAT (TX_PAGE_3 | 0x22) +#define BIT_SPIBURSTSTAT_SPI_HDCPRST (0x80) +#define BIT_SPIBURSTSTAT_SPI_CBUSRST (0x40) +#define BIT_SPIBURSTSTAT_SPI_SRST (0x20) +#define BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE (0x01) + +/* 0x23 E-MSC 1st Interrupt Register (Default: 0x00) */ +#define REG_EMSCINTR (TX_PAGE_3 | 0x23) +#define BIT_EMSCINTR_EMSC_XFIFO_EMPTY (0x80) +#define BIT_EMSCINTR_EMSC_XMIT_ACK_TOUT (0x40) +#define BIT_EMSCINTR_EMSC_RFIFO_READ_ERR (0x20) +#define BIT_EMSCINTR_EMSC_XFIFO_WRITE_ERR (0x10) +#define BIT_EMSCINTR_EMSC_COMMA_CHAR_ERR (0x08) +#define BIT_EMSCINTR_EMSC_XMIT_DONE (0x04) +#define BIT_EMSCINTR_EMSC_XMIT_GNT_TOUT (0x02) +#define BIT_EMSCINTR_SPI_DVLD (0x01) + +/* 0x24 E-MSC Interrupt Mask Register (Default: 0x00) */ +#define REG_EMSCINTRMASK (TX_PAGE_3 | 0x24) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_7 (0x80) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_6 (0x40) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_5 (0x20) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_4 (0x10) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_3 (0x08) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_2 (0x04) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_1 (0x02) +#define BIT_EMSCINTRMASK_EMSC_INTRMASK0_0 (0x01) + +/* 0x2A I2C E-MSC XMIT FIFO Write Port Register (Default: 0x00) */ +#define REG_EMSC_XMIT_WRITE_PORT (TX_PAGE_3 | 0x2A) + +/* 0x2B I2C E-MSC RCV FIFO Write Port Register (Default: 0x00) */ +#define REG_EMSC_RCV_READ_PORT (TX_PAGE_3 | 0x2B) + +/* 0x2C E-MSC 2nd Interrupt Register (Default: 0x00) */ +#define REG_EMSCINTR1 (TX_PAGE_3 | 0x2C) +#define BIT_EMSCINTR1_EMSC_TRAINING_COMMA_ERR (0x01) + +/* 0x2D E-MSC Interrupt Mask Register (Default: 0x00) */ +#define REG_EMSCINTRMASK1 (TX_PAGE_3 | 0x2D) +#define BIT_EMSCINTRMASK1_EMSC_INTRMASK1_0 (0x01) + +/* 0x30 MHL Top Ctl Register (Default: 0x00) */ +#define REG_MHL_TOP_CTL (TX_PAGE_3 | 0x30) +#define BIT_MHL_TOP_CTL_MHL3_DOC_SEL (0x80) +#define BIT_MHL_TOP_CTL_MHL_PP_SEL (0x40) +#define MSK_MHL_TOP_CTL_IF_TIMING_CTL (0x03) + +/* 0x31 MHL DataPath 1st Ctl Register (Default: 0xBC) */ +#define REG_MHL_DP_CTL0 (TX_PAGE_3 | 0x31) +#define BIT_MHL_DP_CTL0_DP_OE (0x80) +#define BIT_MHL_DP_CTL0_TX_OE_OVR (0x40) +#define MSK_MHL_DP_CTL0_TX_OE (0x3F) + +/* 0x32 MHL DataPath 2nd Ctl Register (Default: 0xBB) */ +#define REG_MHL_DP_CTL1 (TX_PAGE_3 | 0x32) +#define MSK_MHL_DP_CTL1_CK_SWING_CTL (0xF0) +#define MSK_MHL_DP_CTL1_DT_SWING_CTL (0x0F) + +/* 0x33 MHL DataPath 3rd Ctl Register (Default: 0x2F) */ +#define REG_MHL_DP_CTL2 (TX_PAGE_3 | 0x33) +#define BIT_MHL_DP_CTL2_CLK_BYPASS_EN (0x80) +#define MSK_MHL_DP_CTL2_DAMP_TERM_SEL (0x30) +#define MSK_MHL_DP_CTL2_CK_TERM_SEL (0x0C) +#define MSK_MHL_DP_CTL2_DT_TERM_SEL (0x03) + +/* 0x34 MHL DataPath 4th Ctl Register (Default: 0x48) */ +#define REG_MHL_DP_CTL3 (TX_PAGE_3 | 0x34) +#define MSK_MHL_DP_CTL3_DT_DRV_VNBC_CTL (0xF0) +#define MSK_MHL_DP_CTL3_DT_DRV_VNB_CTL (0x0F) + +/* 0x35 MHL DataPath 5th Ctl Register (Default: 0x48) */ +#define REG_MHL_DP_CTL4 (TX_PAGE_3 | 0x35) +#define MSK_MHL_DP_CTL4_CK_DRV_VNBC_CTL (0xF0) +#define MSK_MHL_DP_CTL4_CK_DRV_VNB_CTL (0x0F) + +/* 0x36 MHL DataPath 6th Ctl Register (Default: 0x3F) */ +#define REG_MHL_DP_CTL5 (TX_PAGE_3 | 0x36) +#define BIT_MHL_DP_CTL5_RSEN_EN_OVR (0x80) +#define BIT_MHL_DP_CTL5_RSEN_EN (0x40) +#define MSK_MHL_DP_CTL5_DAMP_TERM_VGS_CTL (0x30) +#define MSK_MHL_DP_CTL5_CK_TERM_VGS_CTL (0x0C) +#define MSK_MHL_DP_CTL5_DT_TERM_VGS_CTL (0x03) + +/* 0x37 MHL PLL 1st Ctl Register (Default: 0x05) */ +#define REG_MHL_PLL_CTL0 (TX_PAGE_3 | 0x37) +#define BIT_MHL_PLL_CTL0_AUD_CLK_EN (0x80) + +#define MSK_MHL_PLL_CTL0_AUD_CLK_RATIO (0x70) +#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_10 (0x70) +#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_6 (0x60) +#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_4 (0x50) +/*TODO:#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_2 (0x40)*/ +#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_5 (0x30) +#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_3 (0x20) +#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_2_PRIME (0x10) +#define VAL_MHL_PLL_CTL0_AUD_CLK_RATIO_5_1 (0x00) + +#define MSK_MHL_PLL_CTL0_HDMI_CLK_RATIO (0x0C) +#define VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_4X (0x0C) +#define VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_2X (0x08) +#define VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X (0x04) +#define VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_HALF_X (0x00) + +#define BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL (0x02) +#define BIT_MHL_PLL_CTL0_ZONE_MASK_OE (0x01) + +/* 0x39 MHL PLL 3rd Ctl Register (Default: 0x80) */ +#define REG_MHL_PLL_CTL2 (TX_PAGE_3 | 0x39) +#define BIT_MHL_PLL_CTL2_CLKDETECT_EN (0x80) +#define BIT_MHL_PLL_CTL2_MEAS_FVCO (0x08) +#define BIT_MHL_PLL_CTL2_PLL_FAST_LOCK (0x04) +#define MSK_MHL_PLL_CTL2_PLL_LF_SEL (0x03) + +/* 0x40 MHL CBUS 1st Ctl Register (Default: 0x12) */ +#define REG_MHL_CBUS_CTL0 (TX_PAGE_3 | 0x40) +#define BIT_MHL_CBUS_CTL0_CBUS_RGND_TEST_MODE (0x80) + +#define MSK_MHL_CBUS_CTL0_CBUS_RGND_VTH_CTL (0x30) +#define VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_734 (0x00) +#define VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_747 (0x10) +#define VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_740 (0x20) +#define VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_754 (0x30) + +#define MSK_MHL_CBUS_CTL0_CBUS_RES_TEST_SEL (0x0C) + +#define MSK_MHL_CBUS_CTL0_CBUS_DRV_SEL (0x03) +#define VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_WEAKEST (0x00) +#define VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_WEAK (0x01) +#define VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_STRONG (0x02) +#define VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_STRONGEST (0x03) + +/* 0x41 MHL CBUS 2nd Ctl Register (Default: 0x03) */ +#define REG_MHL_CBUS_CTL1 (TX_PAGE_3 | 0x41) +#define MSK_MHL_CBUS_CTL1_CBUS_RGND_RES_CTL (0x07) +#define VAL_MHL_CBUS_CTL1_0888_OHM (0x00) +#define VAL_MHL_CBUS_CTL1_1115_OHM (0x04) +#define VAL_MHL_CBUS_CTL1_1378_OHM (0x07) + +/* 0x42 MHL CoC 1st Ctl Register (Default: 0xC3) */ +#define REG_MHL_COC_CTL0 (TX_PAGE_3 | 0x42) +#define BIT_MHL_COC_CTL0_COC_BIAS_EN (0x80) +#define MSK_MHL_COC_CTL0_COC_BIAS_CTL (0x70) +#define MSK_MHL_COC_CTL0_COC_TERM_CTL (0x07) + +/* 0x43 MHL CoC 2nd Ctl Register (Default: 0x87) */ +#define REG_MHL_COC_CTL1 (TX_PAGE_3 | 0x43) +#define BIT_MHL_COC_CTL1_COC_EN (0x80) +#define MSK_MHL_COC_CTL1_COC_DRV_CTL (0x3F) + +/* 0x45 MHL CoC 4th Ctl Register (Default: 0x00) */ +#define REG_MHL_COC_CTL3 (TX_PAGE_3 | 0x45) +#define BIT_MHL_COC_CTL3_COC_AECHO_EN (0x01) + +/* 0x46 MHL CoC 5th Ctl Register (Default: 0x28) */ +#define REG_MHL_COC_CTL4 (TX_PAGE_3 | 0x46) +#define MSK_MHL_COC_CTL4_COC_IF_CTL (0xF0) +#define MSK_MHL_COC_CTL4_COC_SLEW_CTL (0x0F) + +/* 0x47 MHL CoC 6th Ctl Register (Default: 0x0D) */ +#define REG_MHL_COC_CTL5 (TX_PAGE_3 | 0x47) +#define MSK_MHL_COC_CTL5_COC_RSV_7_0 (0xFF) + +/* 0x49 MHL DoC 1st Ctl Register (Default: 0x18) */ +#define REG_MHL_DOC_CTL0 (TX_PAGE_3 | 0x49) +#define BIT_MHL_DOC_CTL0_DOC_RXDATA_EN (0x80) +#define MSK_MHL_DOC_CTL0_DOC_DM_TERM (0x38) +#define MSK_MHL_DOC_CTL0_DOC_OPMODE (0x06) +#define BIT_MHL_DOC_CTL0_DOC_RXBIAS_EN (0x01) + +/* 0x50 MHL DataPath 7th Ctl Register (Default: 0x2A) */ +#define REG_MHL_DP_CTL6 (TX_PAGE_3 | 0x50) +#define BIT_MHL_DP_CTL6_DP_TAP2_SGN (0x20) +#define BIT_MHL_DP_CTL6_DP_TAP2_EN (0x10) +#define BIT_MHL_DP_CTL6_DP_TAP1_SGN (0x08) +#define BIT_MHL_DP_CTL6_DP_TAP1_EN (0x04) +#define BIT_MHL_DP_CTL6_DT_PREDRV_FEEDCAP_EN (0x02) +#define BIT_MHL_DP_CTL6_DP_PRE_POST_SEL (0x01) + +/* 0x51 MHL DataPath 8th Ctl Register (Default: 0x06) */ +#define REG_MHL_DP_CTL7 (TX_PAGE_3 | 0x51) +#define MSK_MHL_DP_CTL7_DT_DRV_VBIAS_CASCTL (0xF0) +#define MSK_MHL_DP_CTL7_DT_DRV_IREF_CTL (0x0F) + +/* 0x61 Tx Zone Ctl1 Register (Default: 0x00) */ +#define REG_TX_ZONE_CTL1 (TX_PAGE_3 | 0x61) +#define MSK_TX_ZONE_CTL1_TX_ZONE_CTRL (0xFF) +#define VAL_TX_ZONE_CTL1_TX_ZONE_CTRL_MODE (0x08) + +/* 0x64 MHL3 Tx Zone Ctl Register (Default: 0x00) */ +#define REG_MHL3_TX_ZONE_CTL (TX_PAGE_3 | 0x64) +#define BIT_MHL3_TX_ZONE_CTL_MHL2_INTPLT_ZONE_MANU_EN (0x80) +#define MSK_MHL3_TX_ZONE_CTL_MHL3_TX_ZONE (0x03) + +#define MSK_TX_ZONE_CTL3_TX_ZONE (0x03) +#define VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS (0x00) +#define VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS (0x01) +#define VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS (0x02) + +/* 0x91 HDCP Polling Control and Status Register (Default: 0x70) */ +#define REG_HDCP2X_POLL_CS (TX_PAGE_3 | 0x91) + +#define BIT_HDCP2X_POLL_CS_HDCP2X_MSG_SZ_CLR_OPTION (0x40) +#define BIT_HDCP2X_POLL_CS_HDCP2X_RPT_READY_CLR_OPTION (0x20) +#define BIT_HDCP2X_POLL_CS_HDCP2X_REAUTH_REQ_CLR_OPTION (0x10) +#define MSK_HDCP2X_POLL_CS_ (0x0C) +#define BIT_HDCP2X_POLL_CS_HDCP2X_DIS_POLL_GNT (0x02) +#define BIT_HDCP2X_POLL_CS_HDCP2X_DIS_POLL_EN (0x01) + +/* 0x98 HDCP Interrupt 0 Register (Default: 0x00) */ +#define REG_HDCP2X_INTR0 (TX_PAGE_3 | 0x98) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT7 (0x80) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT6 (0x40) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT5 (0x20) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT4 (0x10) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT3 (0x08) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT2 (0x04) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT1 (0x02) +#define BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT0 (0x01) + +/* 0x99 HDCP Interrupt 0 Mask Register (Default: 0x00) */ +#define REG_HDCP2X_INTR0_MASK (TX_PAGE_3 | 0x99) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK7 (0x80) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK6 (0x40) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK5 (0x20) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK4 (0x10) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK3 (0x08) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK2 (0x04) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK1 (0x02) +#define BIT_HDCP2X_INTR0_MASK_HDCP2X_INTR0_MASK0 (0x01) + +/* 0xA0 HDCP General Control 0 Register (Default: 0x02) */ +#define REG_HDCP2X_CTRL_0 (TX_PAGE_3 | 0xA0) +#define BIT_HDCP2X_CTRL_0_HDCP2X_ENCRYPT_EN (0x80) +#define BIT_HDCP2X_CTRL_0_HDCP2X_POLINT_SEL (0x40) +#define BIT_HDCP2X_CTRL_0_HDCP2X_POLINT_OVR (0x20) +#define BIT_HDCP2X_CTRL_0_HDCP2X_PRECOMPUTE (0x10) +#define BIT_HDCP2X_CTRL_0_HDCP2X_HDMIMODE (0x08) +#define BIT_HDCP2X_CTRL_0_HDCP2X_REPEATER (0x04) +#define BIT_HDCP2X_CTRL_0_HDCP2X_HDCPTX (0x02) +#define BIT_HDCP2X_CTRL_0_HDCP2X_EN (0x01) + +/* 0xA1 HDCP General Control 1 Register (Default: 0x08) */ +#define REG_HDCP2X_CTRL_1 (TX_PAGE_3 | 0xA1) +#define MSK_HDCP2X_CTRL_1_HDCP2X_REAUTH_MSK_3_0 (0xF0) +#define BIT_HDCP2X_CTRL_1_HDCP2X_HPD_SW (0x08) +#define BIT_HDCP2X_CTRL_1_HDCP2X_HPD_OVR (0x04) +#define BIT_HDCP2X_CTRL_1_HDCP2X_CTL3MSK (0x02) +#define BIT_HDCP2X_CTRL_1_HDCP2X_REAUTH_SW (0x01) + +/* 0xA5 HDCP Misc Control Register (Default: 0x00) */ +#define REG_HDCP2X_MISC_CTRL (TX_PAGE_3 | 0xA5) +#define BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_SMNG_XFER_START (0x10) +#define BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_SMNG_WR_START (0x08) +#define BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_SMNG_WR (0x04) +#define BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_RCVID_RD_START (0x02) +#define BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_RCVID_RD (0x01) + +/* 0xA6 HDCP RPT SMNG K Register (Default: 0x00) */ +#define REG_HDCP2X_RPT_SMNG_K (TX_PAGE_3 | 0xA6) +#define MSK_HDCP2X_RPT_SMNG_K_HDCP2X_RPT_SMNG_K_7_0 (0xFF) + +/* 0xA7 HDCP RPT SMNG In Register (Default: 0x00) */ +#define REG_HDCP2X_RPT_SMNG_IN (TX_PAGE_3 | 0xA7) +#define MSK_HDCP2X_RPT_SMNG_IN_HDCP2X_RPT_SMNG_IN (0xFF) + +/* 0xAA HDCP Auth Status Register (Default: 0x00) */ +#define REG_HDCP2X_AUTH_STAT (TX_PAGE_3 | 0xAA) +#define MSK_HDCP2X_AUTH_STAT_HDCP2X_AUTH_STAT_7_0 (0xFF) + +/* 0xAC HDCP RPT RCVID Out Register (Default: 0x00) */ +#define REG_HDCP2X_RPT_RCVID_OUT (TX_PAGE_3 | 0xAC) +#define MSK_HDCP2X_RPT_RCVID_OUT_HDCP2X_RPT_RCVID_OUT_7_0 (0xFF) + +/* 0xB4 HDCP TP1 Register (Default: 0x62) */ +#define REG_HDCP2X_TP1 (TX_PAGE_3 | 0xB4) +#define MSK_HDCP2X_TP1_HDCP2X_TP1_7_0 (0xFF) + +/* 0xC7 HDCP GP Out 0 Register (Default: 0x00) */ +#define REG_HDCP2X_GP_OUT0 (TX_PAGE_3 | 0xC7) +#define MSK_HDCP2X_GP_OUT0_HDCP2X_GP_OUT0_7_0 (0xFF) + +/* 0xD1 HDCP Repeater RCVR ID 0 Register (Default: 0x00) */ +#define REG_HDCP2X_RPT_RCVR_ID0 (TX_PAGE_3 | 0xD1) +#define MSK_HDCP2X_RPT_RCVR_ID0_HDCP2X_RCVR_ID_7_0 (0xFF) + +/* 0xD8 HDCP DDCM Status Register (Default: 0x00) */ +#define REG_HDCP2X_DDCM_STS (TX_PAGE_3 | 0xD8) +#define MSK_HDCP2X_DDCM_STS_HDCP2X_DDCM_ERR_STS_3_0 (0xF0) +#define MSK_HDCP2X_DDCM_STS_HDCP2X_DDCM_CTL_CS_3_0 (0x0F) + +/* 0xE0 HDMI2MHL3 Control Register (Default: 0x0A) */ +#define REG_M3_CTRL (TX_PAGE_3 | 0xE0) +#define BIT_M3_CTRL_H2M_SWRST (0x10) +#define BIT_M3_CTRL_SW_MHL3_SEL (0x08) +#define BIT_M3_CTRL_M3AV_EN (0x04) +#define BIT_M3_CTRL_ENC_TMDS (0x02) +#define BIT_M3_CTRL_MHL3_MASTER_EN (0x01) + +#define VAL_M3_CTRL_MHL3_VALUE (BIT_M3_CTRL_SW_MHL3_SEL \ + | BIT_M3_CTRL_M3AV_EN \ + | BIT_M3_CTRL_ENC_TMDS \ + | BIT_M3_CTRL_MHL3_MASTER_EN) + +#define VAL_M3_CTRL_PEER_VERSION_PENDING_VALUE \ + VAL_M3_CTRL_MHL3_VALUE + +/* 0xE1 HDMI2MHL3 Port0 Control Register (Default: 0x04) */ +#define REG_M3_P0CTRL (TX_PAGE_3 | 0xE1) +#define BIT_M3_P0CTRL_MHL3_P0_HDCP_ENC_EN (0x10) +#define BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN (0x08) +#define BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN_ON 0x08 +#define BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN_OFF 0x00 +#define BIT_M3_P0CTRL_MHL3_P0_HDCP_EN (0x04) + + +#define BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE (0x02) +#define VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_NORMAL (0x00) +#define VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED (0x02) + +#define BIT_M3_P0CTRL_MHL3_P0_PORT_EN (0x01) + +#define REG_M3_POSTM (TX_PAGE_3 | 0xE2) +#define MSK_M3_POSTM_RRP_DECODE (0xF8) +#define MSK_M3_POSTM_MHL3_P0_STM_ID (0x07) + +/* 0xE6 HDMI2MHL3 Scramble Control Register (Default: 0x41) */ +#define REG_M3_SCTRL (TX_PAGE_3 | 0xE6) +#define MSK_M3_SCTRL_MHL3_SR_LENGTH (0xF0) +#define BIT_M3_SCTRL_MHL3_SCRAMBLER_EN (0x01) + +/* 0xF2 HSIC Div Ctl Register (Default: 0x05) */ +#define REG_DIV_CTL_MAIN (TX_PAGE_3 | 0xF2) +#define MSK_DIV_CTL_MAIN_PRE_DIV_CTL_MAIN (0x1C) +#define MSK_DIV_CTL_MAIN_FB_DIV_CTL_MAIN (0x03) + +/* Registers in TX_PAGE_4 (0x00-0xFF) */ + +/* 0x00 MHL Capability 1st Byte Register (Default: 0x00) */ +#define REG_MHL_DEVCAP_0 (TX_PAGE_4 | 0x00) +#define MSK_MHL_DEVCAP_0_MHL_DEVCAP_0 (0xFF) + +/* 0x20 MHL Interrupt 1st Byte Register (Default: 0x00) */ +#define REG_MHL_INT_0 (TX_PAGE_4 | 0x20) + +/* 0x30 Device Status 1st byte Register (Default: 0x00) */ +#define REG_MHL_STAT_0 (TX_PAGE_4 | 0x30) +#define MSK_MHL_STAT_0_MHL_STAT_0 (0xFF) + +/* 0x40 CBUS Scratch Pad 1st Byte Register (Default: 0x00) */ +#define REG_MHL_SCRPAD_0 (TX_PAGE_4 | 0x40) +#define MSK_MHL_SCRPAD_0_MHL_SCRPAD_0 (0xFF) + +/* 0x80 MHL Extended Capability 1st Byte Register (Default: 0x00) */ +#define REG_MHL_EXTDEVCAP_0 (TX_PAGE_4 | 0x80) +#define MSK_MHL_EXTDEVCAP_0_MHL_EXTDEVCAP_0 (0xFF) + +/* 0x90 Device Extended Status 1st byte Register (Default: 0x00) */ +#define REG_MHL_EXTSTAT_0 (TX_PAGE_4 | 0x90) +#define MSK_MHL_EXTSTAT_0_MHL_EXTSTAT_0 (0xFF) + +/* 0x02 TPI DTD Byte2 Register (Default: 0x00) */ +#define REG_TPI_DTD_B2 (TX_PAGE_6 | 0x02) +#define MSK_TPI_DTD_B2_DTDB2 (0xFF) + +/* 0x09 Input Format Register (Default: 0x00) */ +#define REG_TPI_INPUT (TX_PAGE_6 | 0x09) +#define BIT_TPI_INPUT_EXTENDEDBITMODE (0x80) +#define BIT_TPI_INPUT_ENDITHER (0x40) +#define MSK_TPI_INPUT_INPUT_QUAN_RANGE (0x0C) +#define MSK_TPI_INPUT_INPUT_FORMAT (0x03) +#define VAL_INPUT_FORMAT_RGB (0x00) +#define VAL_INPUT_FORMAT_YCBCR444 (0x01) +#define VAL_INPUT_FORMAT_YCBCR422 (0x02) +#define VAL_INPUT_FORMAT_INTERNAL_RGB (0x03) + +/* 0x0A Output Format Register (Default: 0x00) */ +#define REG_TPI_OUTPUT (TX_PAGE_6 | 0x0A) +#define BIT_TPI_OUTPUT_CSCMODE709 (0x10) +#define MSK_TPI_OUTPUT_OUTPUT_QUAN_RANGE (0x0C) +#define MSK_TPI_OUTPUT_OUTPUT_FORMAT (0x03) + +/* 0x0C TPI AVI Check Sum Register (Default: 0x00) */ +#define REG_TPI_AVI_CHSUM (TX_PAGE_6 | 0x0C) +#define MSK_TPI_AVI_CHSUM_AVI_CHECKSUM (0xFF) + +/* 0x1A TPI System Control Register (Default: 0x00) */ +#define REG_TPI_SC (TX_PAGE_6 | 0x1A) +#define BIT_TPI_SC_TPI_UPDATE_FLG (0x80) +#define BIT_TPI_SC_TPI_REAUTH_CTL (0x40) +#define BIT_TPI_SC_TPI_OUTPUT_MODE_1 (0x20) + +#define BIT_TPI_SC_REG_TMDS_OE (0x10) +#define VAL_TPI_SC_REG_TMDS_OE_ACTIVE (0x00) +#define VAL_TPI_SC_REG_TMDS_OE_POWER_DOWN (0x10) + +#define BIT_TPI_SC_TPI_AV_MUTE (0x08) +#define VAL_TPI_SC_TPI_AV_MUTE_NORMAL (0x00) +#define VAL_TPI_SC_TPI_AV_MUTE_MUTED (0x08) + +#define BIT_TPI_SC_DDC_GPU_REQUEST (0x04) +#define BIT_TPI_SC_DDC_TPI_SW (0x02) + +#define BIT_TPI_SC_TPI_OUTPUT_MODE_0 (0x01) +#define VAL_TPI_SC_TPI_OUTPUT_MODE_0_DVI (0x00) +#define VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI (0x01) + +/* 0x29 TPI COPP Query Data Register (Default: 0x00) */ +#define REG_TPI_COPP_DATA1 (TX_PAGE_6 | 0x29) +#define BIT_TPI_COPP_DATA1_COPP_GPROT (0x80) +#define VAL_TPI_COPP_GPROT_NONE (0x00) +#define VAL_TPI_COPP_GPROT_SECURE (0x80) + +#define BIT_TPI_COPP_DATA1_COPP_LPROT (0x40) +#define VAL_TPI_COPP_LPROT_NONE (0x00) +#define VAL_TPI_COPP_LPROT_SECURE (0x40) + +#define MSK_TPI_COPP_DATA1_COPP_LINK_STATUS (0x30) +#define VAL_TPI_COPP_LINK_STATUS_NORMAL (0x00) +#define VAL_TPI_COPP_LINK_STATUS_LINK_LOST (0x10) +#define VAL_TPI_COPP_LINK_STATUS_RENEGOTIATION_REQ (0x20) +#define VAL_TPI_COPP_LINK_STATUS_LINK_SUSPENDED (0x30) + +#define BIT_TPI_COPP_DATA1_COPP_HDCP_REP (0x08) +#define BIT_TPI_COPP_DATA1_COPP_CONNTYPE_0 (0x04) +#define BIT_TPI_COPP_DATA1_COPP_PROTYPE (0x02) +#define BIT_TPI_COPP_DATA1_COPP_CONNTYPE_1 (0x01) + +/* 0x2A TPI COPP Control Data Register (Default: 0x00) */ +#define REG_TPI_COPP_DATA2 (TX_PAGE_6 | 0x2A) +#define BIT_TPI_COPP_DATA2_INTR_ENCRYPTION (0x20) +#define BIT_TPI_COPP_DATA2_KSV_FORWARD (0x10) +#define BIT_TPI_COPP_DATA2_INTERM_RI_CHECK_EN (0x08) +#define BIT_TPI_COPP_DATA2_DOUBLE_RI_CHECK (0x04) +#define BIT_TPI_COPP_DATA2_DDC_SHORT_RI_RD (0x02) + +#define BIT_TPI_COPP_DATA2_COPP_PROTLEVEL (0x01) +#define VAL_TPI_COPP_PROTLEVEL_MIN (0x00) +#define VAL_TPI_COPP_PROTLEVEL_MAX (0x01) + +/* 0x3C TPI Interrupt Enable Register (Default: 0x00) */ +#define REG_TPI_INTR_EN (TX_PAGE_6 | 0x3C) +#define BIT_TPI_INTR_EN_TPI_INTR_EN_7 (0x80) +#define BIT_TPI_INTR_EN_TPI_INTR_EN_6 (0x40) +#define BIT_TPI_INTR_EN_TPI_INTR_EN_5 (0x20) +#define BIT_TPI_INTR_EN_TPI_INTR_EN_3 (0x08) +#define BIT_TPI_INTR_EN_TPI_INTR_EN_2 (0x04) +#define BIT_TPI_INTR_EN_TPI_INTR_EN_1 (0x02) +#define BIT_TPI_INTR_EN_TPI_INTR_EN_0 (0x01) + +/* 0x3D TPI Interrupt Status Low Byte Register (Default: 0x00) */ +#define REG_TPI_INTR_ST0 (TX_PAGE_6 | 0x3D) +#define BIT_TPI_INTR_ST0_TPI_AUTH_CHNGE_STAT (0x80) +#define BIT_TPI_INTR_ST0_TPI_V_RDY_STAT (0x40) +#define BIT_TPI_INTR_ST0_TPI_COPP_CHNGE_STAT (0x20) +#define BIT_TPI_INTR_ST0_KSV_FIFO_FIRST_STAT (0x08) +#define BIT_TPI_INTR_ST0_READ_BKSV_BCAPS_DONE_STAT (0x04) +#define BIT_TPI_INTR_ST0_READ_BKSV_BCAPS_ERR_STAT (0x02) +#define BIT_TPI_INTR_ST0_READ_BKSV_ERR_STAT (0x01) + +/* 0x44 TPI DS BCAPS Status Register (Default: 0x00) */ +#define REG_TPI_DS_BCAPS (TX_PAGE_6 | 0x44) +#define MSK_TPI_DS_BCAPS_DS_BCAPS (0xFF) + +/* 0x45 TPI BStatus1 Register (Default: 0x00) */ +#define REG_TPI_BSTATUS1 (TX_PAGE_6 | 0x45) +#define BIT_TPI_BSTATUS1_DS_DEV_EXCEED (0x80) +#define MSK_TPI_BSTATUS1_DS_DEV_CNT (0x7F) + +/* 0x46 TPI BStatus2 Register (Default: 0x10) */ +#define REG_TPI_BSTATUS2 (TX_PAGE_6 | 0x46) +#define MSK_TPI_BSTATUS2_DS_BSTATUS (0xE0) +#define BIT_TPI_BSTATUS2_DS_HDMI_MODE (0x10) +#define BIT_TPI_BSTATUS2_DS_CASC_EXCEED (0x08) +#define MSK_TPI_BSTATUS2_DS_DEPTH (0x07) + +/* 0xBB TPI HW Optimization Control #3 Register (Default: 0x00) */ +#define REG_TPI_HW_OPT3 (TX_PAGE_6 | 0xBB) +#define BIT_TPI_HW_OPT3_DDC_DEBUG (0x80) +#define BIT_TPI_HW_OPT3_RI_CHECK_SKIP (0x08) +#define BIT_TPI_HW_OPT3_TPI_DDC_BURST_MODE (0x04) +#define MSK_TPI_HW_OPT3_TPI_DDC_REQ_LEVEL (0x03) + +/* 0xBF TPI Info Frame Select Register (Default: 0x00) */ +#define REG_TPI_INFO_FSEL (TX_PAGE_6 | 0xBF) +#define BIT_TPI_INFO_FSEL_TPI_INFO_EN (0x80) +#define BIT_TPI_INFO_FSEL_TPI_INFO_RPT (0x40) +#define BIT_TPI_INFO_FSEL_TPI_INFO_READ_FLAG (0x20) +#define MSK_TPI_INFO_FSEL_TPI_INFO_SEL (0x07) + +/* 0xC0 TPI Info Byte #0 Register (Default: 0x00) */ +#define REG_TPI_INFO_B0 (TX_PAGE_6 | 0xC0) +#define MSK_TPI_INFO_B0_MODE_BYTE0 (0xFF) + +/* Registers in TX_PAGE_7 (0x00-0xFF) */ + +/* 0x00 CoC 1st Status Register (Default: 0x00) */ +#define REG_COC_STAT_0 (TX_PAGE_7 | 0x00) +#define BITS_COC_STAT_0_PLL_LOCKED 0x80 +#define BIT_COC_STAT_0_COC_STATUS0_6 (0x40) +#define BIT_COC_STAT_0_COC_STATUS0_5 (0x20) +#define BIT_COC_STAT_0_COC_STATUS0_4 (0x10) +#define MSK_COC_STAT_0_COC_STATUS0_3_0 (0x0F) + +#define BITS_COC_STAT_0_CALIBRATION_MASK 0x8F +#define BITS_COC_STAT_0_CALIBRATION_STATE_2 0x02 + +/* 0x01 - 0x02 CoC Status Registers (Default: 0x00) */ +#define REG_COC_STAT_1 (TX_PAGE_7 | 0x01) +#define BIT_COC_STAT_1_TX_BIST_DONE 0x01 +#define BIT_COC_STAT_1_TX_BIST_RUN 0x02 +#define BIT_COC_STAT_1_TX_RUN_DONE 0x03 +#define REG_COC_STAT_2 (TX_PAGE_7 | 0x02) +#define BIT_COC_STAT_2_RX_BIST_DONE 0x01 +#define BIT_COC_STAT_2_RX_BIST_RUN 0x02 +#define BIT_COC_STAT_2_RX_RUN_DONE 0x03 + +#define REG_COC_STAT_3 (TX_PAGE_7 | 0x03) +#define REG_COC_STAT_4 (TX_PAGE_7 | 0x04) +#define REG_COC_STAT_5 (TX_PAGE_7 | 0x05) + +/* 0x10 CoC 1st Ctl Register (Default: 0x40) */ +#define REG_COC_CTL0 (TX_PAGE_7 | 0x10) +#define BIT_COC_CTL0_COC_CONTROL0_7 (0x80) +#define BIT_COC_CTL0_COC_CONTROL0_6 (0x40) +#define BIT_COC_CTL0_COC_CONTROL0_5 (0x20) +#define MSK_COC_CTL0_COC_CONTROL0_4_3 (0x18) +#define BIT_COC_CTL0_COC_CONTROL0_2 (0x04) +#define BIT_COC_CTL0_COC_CONTROL0_1 (0x02) +#define BIT_COC_CTL0_COC_CONTROL0_0 (0x01) + +/* 0x11 CoC 2nd Ctl Register (Default: 0x0A) */ +#define REG_COC_CTL1 (TX_PAGE_7 | 0x11) +#define MSK_COC_CTL1_COC_CONTROL1_7_6 (0xC0) +#define MSK_COC_CTL1_COC_CONTROL1_5_0 (0x3F) + +/* 0x12 CoC 3rd Ctl Register (Default: 0x14) */ +#define REG_COC_CTL2 (TX_PAGE_7 | 0x12) +#define MSK_COC_CTL2_COC_CONTROL2_7_6 (0xC0) +#define MSK_COC_CTL2_COC_CONTROL2_5_0 (0x3F) + +/* 0x13 CoC 4th Ctl Register (Default: 0x40) */ +#define REG_COC_CTL3 (TX_PAGE_7 | 0x13) +#define BIT_COC_CTL3_COC_CONTROL3_7 (0x80) +#define MSK_COC_CTL3_COC_CONTROL3_6_0 (0x7F) + +/* 0x16 CoC 7th Ctl Register (Default: 0x00) */ +#define REG_COC_CTL6 (TX_PAGE_7 | 0x16) +#define BIT_COC_CTL6_COC_CONTROL6_7 (0x80) +#define BIT_COC_CTL6_COC_CONTROL6_6 (0x40) +#define MSK_COC_CTL6_COC_CONTROL6_5_0 (0x3F) + +/* 0x17 CoC 8th Ctl Register (Default: 0x06) */ +#define REG_COC_CTL7 (TX_PAGE_7 | 0x17) +#define BIT_COC_CTL7_COC_CONTROL7_7 (0x80) +#define BIT_COC_CTL7_COC_CONTROL7_6 (0x40) +#define BIT_COC_CTL7_COC_CONTROL7_5 (0x20) +#define MSK_COC_CTL7_COC_CONTROL7_4_3 (0x18) +#define MSK_COC_CTL7_COC_CONTROL7_2_0 (0x07) + +/* 0x19 CoC 10th Ctl Register (Default: 0x00) */ +#define REG_COC_CTL9 (TX_PAGE_7 | 0x19) +#define MSK_COC_CTL9_COC_CONTROL9 (0xFF) + +/* 0x1A CoC 11th Ctl Register (Default: 0x00) */ +#define REG_COC_CTLA (TX_PAGE_7 | 0x1A) +#define MSK_COC_CTLA_COC_CONTROLA (0xFF) + +/* 0x1B CoC 12th Ctl Register (Default: 0x00) */ +#define REG_COC_CTLB (TX_PAGE_7 | 0x1B) +#define MSK_COC_CTLB_COC_CONTROLB (0xFF) + +/* 0x1C CoC 13th Ctl Register (Default: 0x0F) */ +#define REG_COC_CTLC (TX_PAGE_7 | 0x1C) +#define MSK_COC_CTLC_COC_CONTROLC (0xFF) + +/* 0x1D CoC 14th Ctl Register (Default: 0x0A) */ +#define REG_COC_CTLD (TX_PAGE_7 | 0x1D) +#define BIT_COC_CTLD_COC_CONTROLD_7 (0x80) +#define MSK_COC_CTLD_COC_CONTROLD_6_0 (0x7F) + +/* 0x1E CoC 15th Ctl Register (Default: 0x0A) */ +#define REG_COC_CTLE (TX_PAGE_7 | 0x1E) +#define BIT_COC_CTLE_COC_CONTROLE_7 (0x80) +#define MSK_COC_CTLE_COC_CONTROLE_6_0 (0x7F) + +/* 0x1F CoC 16th Ctl Register (Default: 0x00) */ +#define REG_COC_CTLF (TX_PAGE_7 | 0x1F) +#define MSK_COC_CTLF_COC_CONTROLF_7_3 (0xF8) +#define MSK_COC_CTLF_COC_CONTROLF_2_0 (0x07) + +/* 0x21 CoC 18th Ctl Register (Default: 0x32) */ +#define REG_COC_CTL11 (TX_PAGE_7 | 0x21) +#define MSK_COC_CTL11_COC_CONTROL11_7_4 (0xF0) +#define MSK_COC_CTL11_COC_CONTROL11_3_0 (0x0F) + +/* 0x24 CoC 21st Ctl Register (Default: 0x00) */ +#define REG_COC_CTL14 (TX_PAGE_7 | 0x24) +#define MSK_COC_CTL14_COC_CONTROL14_7_4 (0xF0) +#define MSK_COC_CTL14_COC_CONTROL14_3_0 (0x0F) + +/* 0x25 CoC 22nd Ctl Register (Default: 0x00) */ +#define REG_COC_CTL15 (TX_PAGE_7 | 0x25) +#define BIT_COC_CTL15_COC_CONTROL15_7 (0x80) +#define MSK_COC_CTL15_COC_CONTROL15_6_4 (0x70) +#define MSK_COC_CTL15_COC_CONTROL15_3_0 (0x0F) + +/* 0x26 CoC Interrupt Register (Default: 0x00) */ +#define REG_COC_INTR (TX_PAGE_7 | 0x26) +#define BIT_COC_INTR_COC_INTR_STAT5 (0x20) +#define BIT_COC_INTR_COC_INTR_STAT4 (0x10) +#define BIT_COC_INTR_COC_INTR_STAT3 (0x08) +#define BIT_COC_INTR_COC_INTR_STAT2 (0x04) +#define BIT_COC_INTR_COC_INTR_STAT1 (0x02) +#define BIT_COC_INTR_COC_INTR_STAT0 (0x01) + +/* 0x27 CoC Interrupt Mask Register (Default: 0x00) */ +#define REG_COC_INTR_MASK (TX_PAGE_7 | 0x27) +#define BIT_COC_INTR_MASK_COC_INTR_MASK5 (0x20) +#define BIT_COC_INTR_MASK_COC_INTR_MASK4 (0x10) +#define BIT_COC_INTR_MASK_COC_INTR_MASK3 (0x08) +#define BIT_COC_INTR_MASK_COC_INTR_MASK2 (0x04) +#define BIT_COC_INTR_MASK_COC_INTR_MASK1 (0x02) +#define BIT_COC_INTR_MASK_COC_INTR_MASK0 (0x01) + +/* 0x2A CoC 24th Ctl Register (Default: 0x00) */ +#define REG_COC_CTL17 (TX_PAGE_7 | 0x2A) +#define MSK_COC_CTL17_COC_CONTROL17_7_4 (0xF0) +#define MSK_COC_CTL17_COC_CONTROL17_3_0 (0x0F) + +/* 0x2B CoC 25th Ctl Register (Default: 0x00) */ +#define REG_COC_CTL18 (TX_PAGE_7 | 0x2B) +#define MSK_COC_CTL18_COC_CONTROL18_7_4 (0xF0) +#define MSK_COC_CTL18_COC_CONTROL18_3_0 (0x0F) + +/* 0x2C CoC 26th Ctl Register (Default: 0x00) */ +#define REG_COC_CTL19 (TX_PAGE_7 | 0x2C) +#define MSK_COC_CTL19_COC_CONTROL19_7_4 (0xF0) +#define MSK_COC_CTL19_COC_CONTROL19_3_0 (0x0F) + +/* 0x2D CoC 27th Ctl Register (Default: 0x00) */ +#define REG_COC_CTL1A (TX_PAGE_7 | 0x2D) +#define MSK_COC_CTL1A_COC_CONTROL1A_7_2 (0xFC) +#define MSK_COC_CTL1A_COC_CONTROL1A_1_0 (0x03) + +/* 0x40 DoC 9th Status Register (Default: 0x00) */ +#define REG_DOC_STAT_8 (TX_PAGE_7 | 0x40) +#define MSK_DOC_STAT_8_DOC_STATUS8_7_0 (0xFF) + +/* 0x41 DoC 10th Status Register (Default: 0x00) */ +#define REG_DOC_STAT_9 (TX_PAGE_7 | 0x41) +#define MSK_DOC_STAT_9_DOC_STATUS9_7_0 (0xFF) + +/* 0x4E DoC 5th CFG Register (Default: 0x00) */ +#define REG_DOC_CFG4 (TX_PAGE_7 | 0x4E) +#define MSK_DOC_CFG4_DBG_STATE_DOC_FSM (0x0F) + +/* 0x51 DoC 1st Ctl Register (Default: 0x40) */ +#define REG_DOC_CTL0 (TX_PAGE_7 | 0x51) +#define BIT_DOC_CTL0_DOC_CONTROL0_7 (0x80) +#define BIT_DOC_CTL0_DOC_CONTROL0_6 (0x40) +#define BIT_DOC_CTL0_DOC_CONTROL0_5 (0x20) +#define BIT_DOC_CTL0_DOC_CONTROL0_4 (0x10) +#define BIT_DOC_CTL0_DOC_CONTROL0_3 (0x08) +#define BIT_DOC_CTL0_DOC_CONTROL0_2 (0x04) +#define BIT_DOC_CTL0_DOC_CONTROL0_1 (0x02) +#define BIT_DOC_CTL0_DOC_CONTROL0_0 (0x01) + +/* 0x57 DoC 7th Ctl Register (Default: 0x00) */ +#define REG_DOC_CTL6 (TX_PAGE_7 | 0x57) +#define BIT_DOC_CTL6_DOC_CONTROL6_7 (0x80) +#define BIT_DOC_CTL6_DOC_CONTROL6_6 (0x40) +#define MSK_DOC_CTL6_DOC_CONTROL6_5_4 (0x30) +#define MSK_DOC_CTL6_DOC_CONTROL6_3_0 (0x0F) + +/* 0x58 DoC 8th Ctl Register (Default: 0x00) */ +#define REG_DOC_CTL7 (TX_PAGE_7 | 0x58) +#define BIT_DOC_CTL7_DOC_CONTROL7_7 (0x80) +#define BIT_DOC_CTL7_DOC_CONTROL7_6 (0x40) +#define BIT_DOC_CTL7_DOC_CONTROL7_5 (0x20) +#define MSK_DOC_CTL7_DOC_CONTROL7_4_3 (0x18) +#define MSK_DOC_CTL7_DOC_CONTROL7_2_0 (0x07) + +/* 0x6C DoC 9th Ctl Register (Default: 0x00) */ +#define REG_DOC_CTL8 (TX_PAGE_7 | 0x6C) +#define BIT_DOC_CTL8_DOC_CONTROL8_7 (0x80) +#define MSK_DOC_CTL8_DOC_CONTROL8_6_4 (0x70) +#define MSK_DOC_CTL8_DOC_CONTROL8_3_2 (0x0C) +#define MSK_DOC_CTL8_DOC_CONTROL8_1_0 (0x03) + +/* 0x6D DoC 10th Ctl Register (Default: 0x00) */ +#define REG_DOC_CTL9 (TX_PAGE_7 | 0x6D) +#define MSK_DOC_CTL9_DOC_CONTROL9 (0xFF) + +/* 0x6E DoC 11th Ctl Register (Default: 0x00) */ +#define REG_DOC_CTLA (TX_PAGE_7 | 0x6E) +#define MSK_DOC_CTLA_DOC_CONTROLA (0xFF) + +/* 0x72 DoC 15th Ctl Register (Default: 0x00) */ +#define REG_DOC_CTLE (TX_PAGE_7 | 0x72) +#define BIT_DOC_CTLE_DOC_CONTROLE_7 (0x80) +#define BIT_DOC_CTLE_DOC_CONTROLE_6 (0x40) +#define MSK_DOC_CTLE_DOC_CONTROLE_5_4 (0x30) +#define MSK_DOC_CTLE_DOC_CONTROLE_3_0 (0x0F) + +/* Registers in TX_PAGE_5 (0x00-0xFF) */ + +/* 0x80 Interrupt Mask 1st Register (Default: 0x00) */ +#define REG_MHL_INT_0_MASK (TX_PAGE_5 | 0x80) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK7 (0x80) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK6 (0x40) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK5 (0x20) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK4 (0x10) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK3 (0x08) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK2 (0x04) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK1 (0x02) +#define BIT_MHL_INT_0_MASK_MHL_INT_0_MASK0 (0x01) + +/* 0x81 Interrupt Mask 2nd Register (Default: 0x00) */ +#define REG_MHL_INT_1_MASK (TX_PAGE_5 | 0x81) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK7 (0x80) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK6 (0x40) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK5 (0x20) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK4 (0x10) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK3 (0x08) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK2 (0x04) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK1 (0x02) +#define BIT_MHL_INT_1_MASK_MHL_INT_1_MASK0 (0x01) + +/* 0x82 Interrupt Mask 3rd Register (Default: 0x00) */ +#define REG_MHL_INT_2_MASK (TX_PAGE_5 | 0x82) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK7 (0x80) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK6 (0x40) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK5 (0x20) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK4 (0x10) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK3 (0x08) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK2 (0x04) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK1 (0x02) +#define BIT_MHL_INT_2_MASK_MHL_INT_2_MASK0 (0x01) + +/* 0x83 Interrupt Mask 4th Register (Default: 0x00) */ +#define REG_MHL_INT_3_MASK (TX_PAGE_5 | 0x83) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK7 (0x80) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK6 (0x40) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK5 (0x20) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK4 (0x10) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK3 (0x08) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK2 (0x04) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK1 (0x02) +#define BIT_MHL_INT_3_MASK_MHL_INT_3_MASK0 (0x01) + +/* 0x84 MDT Receive Time Out Register (Default: 0x00) */ +#define REG_MDT_RCV_TIMEOUT (TX_PAGE_5 | 0x84) +#define MSK_MDT_RCV_TIMEOUT_MDT_RCV_TIMEOUT_MAX_MSB (0xFF) + +/* 0x85 MDT Transmit Time Out Register (Default: 0x00) */ +#define REG_MDT_XMIT_TIMEOUT (TX_PAGE_5 | 0x85) +#define MSK_MDT_XMIT_TIMEOUT_MDT_XMIT_TIMEOUT_MAX_MSB (0xFF) + +/* 0x86 MDT Receive Control Register (Default: 0x00) */ +#define REG_MDT_RCV_CONTROL (TX_PAGE_5 | 0x86) +#define BIT_MDT_RCV_CONTROL_MDT_RCV_EN (0x80) +#define BIT_MDT_RCV_CONTROL_MDT_DELAY_RCV_EN (0x40) +#define BIT_MDT_RCV_CONTROL_MDT_RFIFO_OVER_WR_EN (0x10) +#define BIT_MDT_RCV_CONTROL_MDT_XFIFO_OVER_WR_EN (0x08) +#define BIT_MDT_RCV_CONTROL_MDT_DISABLE (0x04) +#define BIT_MDT_RCV_CONTROL_MDT_RFIFO_CLR_ALL (0x02) +#define BIT_MDT_RCV_CONTROL_MDT_RFIFO_CLR_CUR (0x01) + +/* 0x87 MDT Receive Read Port (Default: 0x00) */ +#define REG_MDT_RCV_READ_PORT (TX_PAGE_5 | 0x87) +#define MSK_MDT_RCV_READ_PORT_MDT_RFIFO_DATA (0xFF) + +/* 0x88 MDT Transmit Control Register (Default: 0x70) */ +#define REG_MDT_XMIT_CONTROL (TX_PAGE_5 | 0x88) +#define BIT_MDT_XMIT_CONTROL_MDT_XMIT_EN (0x80) +#define BIT_MDT_XMIT_CONTROL_MDT_XMIT_CMD_MERGE_EN (0x40) +#define BIT_MDT_XMIT_CONTROL_MDT_XMIT_FIXED_BURST_LEN (0x20) +#define BIT_MDT_XMIT_CONTROL_MDT_XMIT_FIXED_AID (0x10) +#define BIT_MDT_XMIT_CONTROL_MDT_XMIT_SINGLE_RUN_EN (0x08) +#define BIT_MDT_XMIT_CONTROL_MDT_CLR_ABORT_WAIT (0x04) +#define BIT_MDT_XMIT_CONTROL_MDT_XFIFO_CLR_ALL (0x02) +#define BIT_MDT_XMIT_CONTROL_MDT_XFIFO_CLR_CUR (0x01) + +/* 0x89 MDT Receive WRITE Port (Default: 0x00) */ +#define REG_MDT_XMIT_WRITE_PORT (TX_PAGE_5 | 0x89) +#define MSK_MDT_XMIT_WRITE_PORT_MDT_XFIFO_WDATA (0xFF) + +/* 0x8A MDT RFIFO Status Register (Default: 0x00) */ +#define REG_MDT_RFIFO_STAT (TX_PAGE_5 | 0x8A) +#define MSK_MDT_RFIFO_STAT_MDT_RFIFO_CNT (0xE0) +#define MSK_MDT_RFIFO_STAT_MDT_RFIFO_CUR_BYTE_CNT (0x1F) + +/* 0x8B MDT XFIFO Status Register (Default: 0x80) */ +#define REG_MDT_XFIFO_STAT (TX_PAGE_5 | 0x8B) +#define MSK_MDT_XFIFO_STAT_MDT_XFIFO_LEVEL_AVAIL (0xE0) +#define BIT_MDT_XFIFO_STAT_MDT_XMIT_PRE_HS_EN (0x10) +#define MSK_MDT_XFIFO_STAT_MDT_WRITE_BURST_LEN (0x0F) + +/* 0x8C MDT Interrupt 0 Register (Default: 0x0C) */ +#define REG_MDT_INT_0 (TX_PAGE_5 | 0x8C) +#define BIT_MDT_INT_0_MDT_INT_0_3 (0x08) +#define BIT_MDT_INT_0_MDT_INT_0_2 (0x04) +#define BIT_MDT_INT_0_MDT_INT_0_1 (0x02) +#define BIT_MDT_INT_0_MDT_INT_0_0 (0x01) + +/* 0x8D MDT Interrupt 0 Mask Register (Default: 0x00) */ +#define REG_MDT_INT_0_MASK (TX_PAGE_5 | 0x8D) +#define BIT_MDT_INT_0_MASK_MDT_INT_0_MASK3 (0x08) +#define BIT_MDT_INT_0_MASK_MDT_INT_0_MASK2 (0x04) +#define BIT_MDT_INT_0_MASK_MDT_INT_0_MASK1 (0x02) +#define BIT_MDT_INT_0_MASK_MDT_INT_0_MASK0 (0x01) + +/* 0x8E MDT Interrupt 1 Register (Default: 0x00) */ +#define REG_MDT_INT_1 (TX_PAGE_5 | 0x8E) +#define BIT_MDT_INT_1_MDT_INT_1_7 (0x80) +#define BIT_MDT_INT_1_MDT_INT_1_6 (0x40) +#define BIT_MDT_INT_1_MDT_INT_1_5 (0x20) +#define BIT_MDT_INT_1_MDT_INT_1_2 (0x04) +#define BIT_MDT_INT_1_MDT_INT_1_1 (0x02) +#define BIT_MDT_INT_1_MDT_INT_1_0 (0x01) + +/* 0x8F MDT Interrupt 1 Mask Register (Default: 0x00) */ +#define REG_MDT_INT_1_MASK (TX_PAGE_5 | 0x8F) +#define BIT_MDT_INT_1_MASK_MDT_INT_1_MASK7 (0x80) +#define BIT_MDT_INT_1_MASK_MDT_INT_1_MASK6 (0x40) +#define BIT_MDT_INT_1_MASK_MDT_INT_1_MASK5 (0x20) +#define BIT_MDT_INT_1_MASK_MDT_INT_1_MASK2 (0x04) +#define BIT_MDT_INT_1_MASK_MDT_INT_1_MASK1 (0x02) +#define BIT_MDT_INT_1_MASK_MDT_INT_1_MASK0 (0x01) + +/* 0x90 CBUS Vendor ID Register (Default: 0x01) */ +#define REG_CBUS_VENDOR_ID (TX_PAGE_5 | 0x90) +#define MSK_CBUS_VENDOR_ID_CBUS_VENDOR_ID (0xFF) + +/* 0x91 CBUS Connection Status Register (Default: 0x00) */ +#define REG_CBUS_STATUS (TX_PAGE_5 | 0x91) +#define BIT_CBUS_STATUS_MHL_CABLE_PRESENT (0x10) +#define BIT_CBUS_STATUS_MSC_HB_SUCCESS (0x08) +#define BIT_CBUS_STATUS_CBUS_HPD (0x04) +#define BIT_CBUS_STATUS_MHL_MODE (0x02) +#define BIT_CBUS_STATUS_CBUS_CONNECTED (0x01) + +/* 0x92 CBUS Interrupt 1st Register (Default: 0x00) */ +#define REG_CBUS_INT_0 (TX_PAGE_5 | 0x92) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT7 (0x80) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT6 (0x40) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT5 (0x20) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT4 (0x10) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT3 (0x08) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT2 (0x04) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT1 (0x02) +#define BIT_CBUS_INT_0_CBUS_INT_0_STAT0 (0x01) + +/* 0x93 CBUS Interrupt Mask 1st Register (Default: 0x00) */ +#define REG_CBUS_INT_0_MASK (TX_PAGE_5 | 0x93) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK7 (0x80) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK6 (0x40) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK5 (0x20) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK4 (0x10) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK3 (0x08) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK2 (0x04) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK1 (0x02) +#define BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK0 (0x01) + +/* 0x94 CBUS Interrupt 2nd Register (Default: 0x00) */ +#define REG_CBUS_INT_1 (TX_PAGE_5 | 0x94) +#define BIT_CBUS_INT_1_CBUS_INT_1_STAT7 (0x80) +#define BIT_CBUS_INT_1_CBUS_INT_1_STAT6 (0x40) +#define BIT_CBUS_INT_1_CBUS_INT_1_STAT5 (0x20) +#define BIT_CBUS_INT_1_CBUS_INT_1_STAT3 (0x08) +#define BIT_CBUS_INT_1_CBUS_INT_1_STAT2 (0x04) +#define BIT_CBUS_INT_1_CBUS_INT_1_STAT0 (0x01) + +/* 0x95 CBUS Interrupt Mask 2nd Register (Default: 0x00) */ +#define REG_CBUS_INT_1_MASK (TX_PAGE_5 | 0x95) +#define BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK7 (0x80) +#define BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK6 (0x40) +#define BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK5 (0x20) +#define BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK3 (0x08) +#define BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK2 (0x04) +#define BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK0 (0x01) + +/* 0x98 CBUS DDC Abort Interrupt Register (Default: 0x00) */ +#define REG_DDC_ABORT_INT (TX_PAGE_5 | 0x98) +#define BIT_DDC_ABORT_INT_DDC_ABORT_INT_STAT7 (0x80) +#define BIT_DDC_ABORT_INT_DDC_ABORT_INT_STAT2 (0x04) +#define BIT_DDC_ABORT_INT_DDC_ABORT_INT_STAT1 (0x02) +#define BIT_DDC_ABORT_INT_DDC_ABORT_INT_STAT0 (0x01) + +/* 0x99 CBUS DDC Abort Interrupt Mask Register (Default: 0x00) */ +#define REG_DDC_ABORT_INT_MASK (TX_PAGE_5 | 0x99) +#define BIT_DDC_ABORT_INT_MASK_DDC_ABORT_INT_MASK7 (0x80) +#define BIT_DDC_ABORT_INT_MASK_DDC_ABORT_INT_MASK2 (0x04) +#define BIT_DDC_ABORT_INT_MASK_DDC_ABORT_INT_MASK1 (0x02) +#define BIT_DDC_ABORT_INT_MASK_DDC_ABORT_INT_MASK0 (0x01) + +/* 0x9A CBUS MSC Requester Abort Interrupt Register (Default: 0x00) */ +#define REG_MSC_MT_ABORT_INT (TX_PAGE_5 | 0x9A) +#define BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT7 (0x80) +#define BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT5 (0x20) +#define BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT3 (0x08) +#define BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT2 (0x04) +#define BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT1 (0x02) +#define BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT0 (0x01) + +/* 0x9B CBUS MSC Requester Abort Interrupt Mask Register (Default: 0x00) */ +#define REG_MSC_MT_ABORT_INT_MASK (TX_PAGE_5 | 0x9B) +#define BIT_MSC_MT_ABORT_INT_MASK_MSC_MT_ABORT_INT_MASK7 (0x80) +#define BIT_MSC_MT_ABORT_INT_MASK_MSC_MT_ABORT_INT_MASK5 (0x20) +#define BIT_MSC_MT_ABORT_INT_MASK_MSC_MT_ABORT_INT_MASK3 (0x08) +#define BIT_MSC_MT_ABORT_INT_MASK_MSC_MT_ABORT_INT_MASK2 (0x04) +#define BIT_MSC_MT_ABORT_INT_MASK_MSC_MT_ABORT_INT_MASK1 (0x02) +#define BIT_MSC_MT_ABORT_INT_MASK_MSC_MT_ABORT_INT_MASK0 (0x01) + +/* 0x9C CBUS MSC Responder Abort Interrupt Register (Default: 0x00) */ +#define REG_MSC_MR_ABORT_INT (TX_PAGE_5 | 0x9C) +#define BIT_MSC_MR_ABORT_INT_MSC_MR_ABORT_INT_STAT7 (0x80) +#define BIT_MSC_MR_ABORT_INT_MSC_MR_ABORT_INT_STAT5 (0x20) +#define BIT_MSC_MR_ABORT_INT_MSC_MR_ABORT_INT_STAT4 (0x10) +#define BIT_MSC_MR_ABORT_INT_MSC_MR_ABORT_INT_STAT3 (0x08) +#define BIT_MSC_MR_ABORT_INT_MSC_MR_ABORT_INT_STAT2 (0x04) +#define BIT_MSC_MR_ABORT_INT_MSC_MR_ABORT_INT_STAT1 (0x02) +#define BIT_MSC_MR_ABORT_INT_MSC_MR_ABORT_INT_STAT0 (0x01) + +/* 0x9D CBUS MSC Responder Abort Interrupt Mask Register (Default: 0x00) */ +#define REG_MSC_MR_ABORT_INT_MASK (TX_PAGE_5 | 0x9D) +#define BIT_MSC_MR_ABORT_INT_MASK_MSC_MR_ABORT_INT_MASK7 (0x80) +#define BIT_MSC_MR_ABORT_INT_MASK_MSC_MR_ABORT_INT_MASK5 (0x20) +#define BIT_MSC_MR_ABORT_INT_MASK_MSC_MR_ABORT_INT_MASK4 (0x10) +#define BIT_MSC_MR_ABORT_INT_MASK_MSC_MR_ABORT_INT_MASK3 (0x08) +#define BIT_MSC_MR_ABORT_INT_MASK_MSC_MR_ABORT_INT_MASK2 (0x04) +#define BIT_MSC_MR_ABORT_INT_MASK_MSC_MR_ABORT_INT_MASK1 (0x02) +#define BIT_MSC_MR_ABORT_INT_MASK_MSC_MR_ABORT_INT_MASK0 (0x01) + +/* 0x9E CBUS RX DISCOVERY interrupt Register (Default: 0x00) */ +#define REG_CBUS_RX_DISC_INT0 (TX_PAGE_5 | 0x9E) +#define BIT_CBUS_RX_DISC_INT0_CBUS_RXDISC_INTR0_STAT3 (0x08) +#define BIT_CBUS_RX_DISC_INT0_CBUS_RXDISC_INTR0_STAT2 (0x04) +#define BIT_CBUS_RX_DISC_INT0_CBUS_RXDISC_INTR0_STAT1 (0x02) +#define BIT_CBUS_RX_DISC_INT0_CBUS_RXDISC_INTR0_STAT0 (0x01) + +/* 0x9F CBUS RX DISCOVERY Interrupt Mask Register (Default: 0x00) */ +#define REG_CBUS_RX_DISC_INT0_MASK (TX_PAGE_5 | 0x9F) +#define BIT_CBUS_RX_DISC_INT0_MASK_CBUS_RXDISC_INTR0_MASK3 (0x08) +#define BIT_CBUS_RX_DISC_INT0_MASK_CBUS_RXDISC_INTR0_MASK2 (0x04) +#define BIT_CBUS_RX_DISC_INT0_MASK_CBUS_RXDISC_INTR0_MASK1 (0x02) +#define BIT_CBUS_RX_DISC_INT0_MASK_CBUS_RXDISC_INTR0_MASK0 (0x01) + +/* 0xA7 CBUS_Link_Layer Control #8 Register (Default: 0x00) */ +#define REG_CBUS_LINK_CONTROL_8 (TX_PAGE_5 | 0xA7) +#define MSK_CBUS_LINK_CONTROL_8_LNK_XMIT_BIT_TIME (0xFF) + +/* 0xB5 MDT State Machine Status Register (Default: 0x00) */ +#define REG_MDT_SM_STAT (TX_PAGE_5 | 0xB5) +#define MSK_MDT_SM_STAT_MDT_RCV_STATE (0xF0) +#define MSK_MDT_SM_STAT_MDT_XMIT_STATE (0x0F) + +/* 0xB8 CBUS MSC command trigger Register (Default: 0x00) */ +#define REG_MSC_COMMAND_START (TX_PAGE_5 | 0xB8) +#define BIT_MSC_COMMAND_START_MSC_DEBUG_CMD (0x20) +#define BIT_MSC_COMMAND_START_MSC_WRITE_BURST_CMD (0x10) +#define BIT_MSC_COMMAND_START_MSC_WRITE_STAT_CMD (0x08) +#define BIT_MSC_COMMAND_START_MSC_READ_DEVCAP_CMD (0x04) +#define BIT_MSC_COMMAND_START_MSC_MSC_MSG_CMD (0x02) +#define BIT_MSC_COMMAND_START_MSC_PEER_CMD (0x01) + +/* 0xB9 CBUS MSC Command/Offset Register (Default: 0x00) */ +#define REG_MSC_CMD_OR_OFFSET (TX_PAGE_5 | 0xB9) +#define MSK_MSC_CMD_OR_OFFSET_MSC_MT_CMD_OR_OFFSET (0xFF) + +/* CBUS MSC Transmit Data Registers (Default: 0x00) */ +#define REG_MSC_1ST_TRANSMIT_DATA (TX_PAGE_5 | 0xBA) +#define REG_MSC_2ND_TRANSMIT_DATA (TX_PAGE_5 | 0xBB) + +/* CBUS MSC Requester Received Data Registers (Default: 0x00) */ +#define REG_MSC_MT_RCVD_DATA0 (TX_PAGE_5 | 0xBC) +#define REG_MSC_MT_RCVD_DATA1 (TX_PAGE_5 | 0xBD) + +/* CBUS MSC Responder MSC_MSG Received Data Regs (Default: 0x00) */ +#define REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA (TX_PAGE_5 | 0xBF) +#define REG_MSC_MR_MSC_MSG_RCVD_2ND_DATA (TX_PAGE_5 | 0xC0) + +/* 0xC4 CBUS MSC Heartbeat Control Register (Default: 0x27) */ +#define REG_MSC_HEARTBEAT_CONTROL (TX_PAGE_5 | 0xC4) +#define BIT_MSC_HEARTBEAT_CONTROL_MSC_HB_EN (0x80) +#define MSK_MSC_HEARTBEAT_CONTROL_MSC_HB_FAIL_LIMIT (0x70) +#define MSK_MSC_HEARTBEAT_CONTROL_MSC_HB_PERIOD_MSB (0x0F) + +/* 0xC7 CBUS MSC Compatibility Control Register (Default: 0x02) */ +#define REG_CBUS_MSC_COMPATIBILITY_CONTROL (TX_PAGE_5 | 0xC7) +#define BIT_CBUS_MSC_COMPATIBILITY_CONTROL_XDEVCAP_EN (0x80) +#define BIT_CBUS_MSC_COMPATIBILITY_CONTROL_DISABLE_MSC_ON_CBUS (0x40) +#define BIT_CBUS_MSC_COMPATIBILITY_CONTROL_DISABLE_DDC_ON_CBUS (0x20) +#define BIT_CBUS_MSC_COMPATIBILITY_CONTROL_DISABLE_GET_DDC_ERRORCODE (0x08) +#define BIT_CBUS_MSC_COMPATIBILITY_CONTROL_DISABLE_GET_VS1_ERRORCODE (0x04) + +/* 0xDC CBUS3 Converter Control Register (Default: 0x24) */ +#define REG_CBUS3_CNVT (TX_PAGE_5 | 0xDC) +#define MSK_CBUS3_CNVT_CBUS3_RETRYLMT (0xF0) +#define MSK_CBUS3_CNVT_CBUS3_PEERTOUT_SEL (0x0C) +#define BIT_CBUS3_CNVT_TEARCBUS_EN (0x02) +#define BIT_CBUS3_CNVT_CBUS3CNVT_EN (0x01) + +/* 0xE0 Discovery Control1 Register (Default: 0x24) */ +#define REG_DISC_CTRL1 (TX_PAGE_5 | 0xE0) +#define BIT_DISC_CTRL1_CBUS_INTR_EN (0x80) +#define BIT_DISC_CTRL1_HB_ONLY (0x40) +#define MSK_DISC_CTRL1_DISC_ATT (0x30) +#define MSK_DISC_CTRL1_DISC_CYC (0x0C) +#define BIT_DISC_CTRL1_DISC_EN (0x01) + +/* 0xE3 Discovery Control4 Register (Default: 0x80) */ +#define REG_DISC_CTRL4 (TX_PAGE_5 | 0xE3) +#define MSK_DISC_CTRL4_CBUSDISC_PUP_SEL (0xC0) +#define MSK_DISC_CTRL4_CBUSIDLE_PUP_SEL (0x30) + +/* 0xE4 Discovery Control5 Register (Default: 0x03) */ +#define REG_DISC_CTRL5 (TX_PAGE_5 | 0xE4) +#define BIT_DISC_CTRL5_DSM_OVRIDE (0x08) +#define MSK_DISC_CTRL5_CBUSMHL_PUP_SEL (0x03) + +/* 0xE7 Discovery Control8 Register (Default: 0x81) */ +#define REG_DISC_CTRL8 (TX_PAGE_5 | 0xE7) +#define BIT_DISC_CTRL8_NOMHLINT_CLR_BYPASS (0x80) +#define BIT_DISC_CTRL8_DELAY_CBUS_INTR_EN (0x01) + +/* 0xE8 Discovery Control9 Register (Default: 0x54) */ +#define REG_DISC_CTRL9 (TX_PAGE_5 | 0xE8) +#define BIT_DISC_CTRL9_MHL3_RSEN_BYP (0x80) +#define BIT_DISC_CTRL9_MHL3DISC_EN (0x40) +#define BIT_DISC_CTRL9_WAKE_DRVFLT (0x10) +#define BIT_DISC_CTRL9_NOMHL_EST (0x08) +#define BIT_DISC_CTRL9_DISC_PULSE_PROCEED (0x04) +#define BIT_DISC_CTRL9_WAKE_PULSE_BYPASS (0x02) +#define BIT_DISC_CTRL9_VBUS_OUTPUT_CAPABILITY_SRC (0x01) + +/* 0xEB Discovery Status1 Register (Default: 0x00) */ +#define REG_DISC_STAT1 (TX_PAGE_5 | 0xEB) +#define BIT_DISC_STAT1_PSM_OVRIDE (0x20) +#define MSK_DISC_STAT1_DISC_SM (0x0F) + +/* 0xEC Discovery Status2 Register (Default: 0x00) */ +#define REG_DISC_STAT2 (TX_PAGE_5 | 0xEC) +#define BIT_DISC_STAT2_CBUS_OE_POL (0x40) +#define BIT_DISC_STAT2_CBUS_SATUS (0x20) +#define BIT_DISC_STAT2_RSEN (0x10) + +#define MSK_DISC_STAT2_MHL_VRSN (0x0C) +#define VAL_DISC_STAT2_DEFAULT (0x00) +#define VAL_DISC_STAT2_MHL1_2 (0x04) +#define VAL_DISC_STAT2_MHL3 (0x08) +#define VAL_DISC_STAT2_RESERVED (0x0C) + +#define MSK_DISC_STAT2_RGND (0x03) +#define VAL_RGND_OPEN (0x00) +#define VAL_RGND_2K (0x01) +#define VAL_RGND_1K (0x02) +#define VAL_RGND_SHORT (0x03) + +/* 0xED Interrupt CBUS_reg1 INTR0 Register (Default: 0x00) */ +#define REG_CBUS_DISC_INTR0 (TX_PAGE_5 | 0xED) +#define BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT6 (0x40) +#define BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT5 (0x20) +#define BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT4 (0x10) +#define BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT3 (0x08) +#define BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT2 (0x04) +#define BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT1 (0x02) +#define BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT0 (0x01) + +/* 0xEE Interrupt CBUS_reg1 INTR0 Mask Register (Default: 0x00) */ +#define REG_CBUS_DISC_INTR0_MASK (TX_PAGE_5 | 0xEE) +#define BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK6 (0x40) +#define BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK5 (0x20) +#define BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK4 (0x10) +#define BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK3 (0x08) +#define BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK2 (0x04) +#define BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK1 (0x02) +#define BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK0 (0x01) + +#define REG_HDCP1X_LB_BIST (TX_PAGE_8 | 0x0C) +#define BIT_HDCP1X_LB_BIST_EN (0x01) + + +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_app_devcap.h b/drivers/video/fbdev/msm/mhl3/si_app_devcap.h new file mode 100644 index 000000000000..cc36cf8b138e --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_app_devcap.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef _SI_APP_DEVCAP_H_ +#define _SI_APP_DEVCAP_H_ + +#define DEVCAP_VAL_DEV_STATE 0 +#define DEVCAP_VAL_MHL_VERSION MHL_VERSION + +#define DEVCAP_VAL_DEV_CAT (MHL_DEV_CAT_SOURCE | \ + MHL_DEV_CATEGORY_POW_BIT) + +#define DEVCAP_VAL_ADOPTER_ID_H (uint8_t)(SILICON_IMAGE_ADOPTER_ID >> 8) +#define DEVCAP_VAL_ADOPTER_ID_L (uint8_t)(SILICON_IMAGE_ADOPTER_ID & 0xFF) + +#define DEVCAP_VAL_VID_LINK_MODE (MHL_DEV_VID_LINK_SUPP_RGB444 | \ + MHL_DEV_VID_LINK_SUPP_YCBCR422 | MHL_DEV_VID_LINK_SUPP_YCBCR444 | \ + MHL_DEV_VID_LINK_SUPP_PPIXEL | MHL_DEV_VID_LINK_SUPP_ISLANDS | \ + MHL_DEV_VID_LINK_SUPP_VGA | MHL_DEV_VID_LINK_SUPP_16BPP) + +#define DEVCAP_VAL_AUD_LINK_MODE (MHL_DEV_AUD_LINK_2CH | \ + MHL_DEV_AUD_LINK_8CH) + +#define DEVCAP_VAL_VIDEO_TYPE 0 +#define DEVCAP_VAL_LOG_DEV_MAP MHL_LOGICAL_DEVICE_MAP +#define DEVCAP_VAL_BANDWIDTH 0x0F + +#define DEVCAP_VAL_FEATURE_FLAG_UCP_SEND MHL_FEATURE_UCP_SEND_SUPPORT +#define DEVCAP_VAL_FEATURE_FLAG_UCP_RECV MHL_FEATURE_UCP_RECV_SUPPORT + +#if (INCLUDE_RBP == 1) +#define DEVCAP_VAL_FEATURE_FLAG_RBP MHL_FEATURE_RBP_SUPPORT +#else +#define DEVCAP_VAL_FEATURE_FLAG_RBP 0 +#endif + +#define DEVCAP_VAL_FEATURE_FLAG (MHL_FEATURE_RCP_SUPPORT | \ + MHL_FEATURE_RAP_SUPPORT | \ + MHL_FEATURE_SP_SUPPORT | \ + DEVCAP_VAL_FEATURE_FLAG_UCP_SEND | \ + DEVCAP_VAL_FEATURE_FLAG_UCP_RECV | \ + DEVCAP_VAL_FEATURE_FLAG_RBP) \ + +#define DEVCAP_VAL_SCRATCHPAD_SIZE MHL_SCRATCHPAD_SIZE +#define DEVCAP_VAL_INT_STAT_SIZE MHL_INT_AND_STATUS_SIZE +#define DEVCAP_VAL_RESERVED 0 + +#define XDEVCAP_VAL_ECBUS_SPEEDS (MHL_XDC_ECBUS_S_075 | \ + MHL_XDC_ECBUS_S_8BIT | MHL_XDC_ECBUS_S_12BIT) + +#define XDEVCAP_VAL_TMDS_SPEEDS (MHL_XDC_TMDS_150 | \ + MHL_XDC_TMDS_300 | MHL_XDC_TMDS_600) + + +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_edid.h b/drivers/video/fbdev/msm/mhl3/si_edid.h new file mode 100644 index 000000000000..5c8ff0dd835a --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_edid.h @@ -0,0 +1,667 @@ +/* + * 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. + */ + +#if !defined(SI_EDID_H) +#define SI_EDID_H + +SI_PUSH_STRUCT_PACKING + +struct SI_PACK_THIS_STRUCT two_bytes_t { + unsigned char low; + unsigned char high; +}; + +#define EDID_EXTENSION_TAG 0x02 +#define EDID_EXTENSION_BLOCK_MAP 0xF0 +#define EDID_REV_THREE 0x03 +#define EDID_BLOCK_0 0x00 +#define EDID_BLOCK_2_3 0x01 + +enum data_block_tag_code_e { + DBTC_TERMINATOR, + DBTC_AUDIO_DATA_BLOCK, + DBTC_VIDEO_DATA_BLOCK, + DBTC_VENDOR_SPECIFIC_DATA_BLOCK, + DBTC_SPEAKER_ALLOCATION_DATA_BLOCK, + DBTC_VESA_DTC_DATA_BLOCK, + /* reserved = 6 */ + DBTC_USE_EXTENDED_TAG = 7 +}; + +struct SI_PACK_THIS_STRUCT data_block_header_fields_t { + uint8_t length_following_header:5; + enum data_block_tag_code_e tag_code:3; +}; + +union SI_PACK_THIS_STRUCT data_block_header_byte_t { + struct data_block_header_fields_t fields; + uint8_t as_byte; +}; + +enum extended_tag_code_e { + ETC_VIDEO_CAPABILITY_DATA_BLOCK, + ETC_VENDOR_SPECIFIC_VIDEO_DATA_BLOCK, + ETC_VESA_VIDEO_DISPLAY_DEVICE_INFORMATION_DATA_BLOCK, + ETC_VESA_VIDEO_DATA_BLOCK, + ETC_HDMI_VIDEO_DATA_BLOCK, + ETC_COLORIMETRY_DATA_BLOCK, + ETC_VIDEO_RELATED, + ETC_CEA_MISC_AUDIO_FIELDS = 16, + ETC_VENDOR_SPECIFIC_AUDIO_DATA_BLOCK, + ETC_HDMI_AUDIO_DATA_BLOCK, + ETC_AUDIO_RELATED, + ETC_GENERAL = 32 +}; + +struct SI_PACK_THIS_STRUCT extended_tag_code_t { + enum extended_tag_code_e etc:8; +}; + +struct SI_PACK_THIS_STRUCT cea_short_descriptor_t { + unsigned char VIC:7; + unsigned char native:1; +}; + +#if 0 +struct SI_PACK_THIS_STRUCT MHL_short_desc_t { + cea_short_descriptor_t cea_short_desc; + MHL2_video_descriptor_t mhl_vid_desc; +}; +#endif + +struct SI_PACK_THIS_STRUCT video_data_block_t { + union data_block_header_byte_t header; + struct cea_short_descriptor_t short_descriptors[1]; /*open ended */ +}; + +enum AudioFormatCodes_e { + /* reserved = 0 */ + afd_linear_PCM_IEC60958 = 1, + afd_AC3, + afd_MPEG1_layers_1_2, + afd_MPEG1_layer_3, + + afdMPEG2_MultiChannel, + afd_AAC, + afd_DTS, + afd_ATRAC, + + afd_one_bit_audio, + afd_dolby_digital, + afd_DTS_HD, + afd_MAT_MLP, + + afd_DST, + afd_WMA_Pro + /* reserved = 15 */ +}; + +struct SI_PACK_THIS_STRUCT CEA_short_audio_descriptor_t { + unsigned char max_channels_minus_one:3; + enum AudioFormatCodes_e audio_format_code:4; + unsigned char F17:1; + unsigned char freq_32_Khz:1; + unsigned char freq_44_1_KHz:1; + unsigned char freq_48_KHz:1; + unsigned char freq_88_2_KHz:1; + unsigned char freq_96_KHz:1; + unsigned char freq_176_4_KHz:1; + unsigned char freq_192_KHz:1; + unsigned char F27:1; + + union { + struct SI_PACK_THIS_STRUCT { + unsigned res_16_bit:1; + unsigned res_20_bit:1; + unsigned res_24_bit:1; + unsigned F33_37:5; + } audio_code_1_LPCM; + + struct SI_PACK_THIS_STRUCT { + uint8_t max_bit_rate_div_by_8_KHz; + } audio_codes_2_8; + + struct SI_PACK_THIS_STRUCT { + uint8_t default_zero; + } audio_codes_9_15; + } byte3; +}; + +struct SI_PACK_THIS_STRUCT audio_data_block_t { + union data_block_header_byte_t header; + /* open ended */ + struct CEA_short_audio_descriptor_t short_audio_descriptors[1]; +}; + +struct SI_PACK_THIS_STRUCT speaker_allocation_flags_t { + unsigned char spk_front_left_front_right:1; + unsigned char spk_LFE:1; + unsigned char spk_front_center:1; + unsigned char spk_rear_left_rear_right:1; + unsigned char spk_rear_center:1; + unsigned char spk_front_left_center_front_right_center:1; + unsigned char spk_rear_left_center_rear_right_center:1; + unsigned char spk_reserved:1; +}; + +struct SI_PACK_THIS_STRUCT speaker_allocation_data_block_payload_t { + struct speaker_allocation_flags_t speaker_alloc_flags; + uint8_t reserved[2]; +}; + +struct SI_PACK_THIS_STRUCT speaker_allocation_data_block_t { + union data_block_header_byte_t header; + struct speaker_allocation_data_block_payload_t payload; +}; + +struct SI_PACK_THIS_STRUCT HDMI_LLC_BA_t { + unsigned char B:4; + unsigned char A:4; +}; + +struct SI_PACK_THIS_STRUCT HDMI_LLC_DC_t { + unsigned char D:4; + unsigned char C:4; +}; + +struct SI_PACK_THIS_STRUCT HDMI_LLC_Byte6_t { + unsigned char DVI_dual:1; + unsigned char reserved:2; + unsigned char DC_Y444:1; + unsigned char DC_30bit:1; + unsigned char DC_36bit:1; + unsigned char DC_48bit:1; + unsigned char supports_AI:1; +}; + +struct SI_PACK_THIS_STRUCT HDMI_LLC_byte8_t { + unsigned char CNC0_adjacent_pixels_independent:1; + unsigned char CNC1_specific_processing_still_pictures:1; + unsigned char CNC2_specific_processing_cinema_content:1; + unsigned char CNC3_specific_processing_low_AV_latency:1; + unsigned char reserved:1; + unsigned char HDMI_video_present:1; + unsigned char I_latency_fields_present:1; + unsigned char latency_fields_present:1; +}; + +enum image_size_e { + imsz_NO_ADDITIONAL, + imsz_ASPECT_RATIO_CORRECT_BUT_NO_GUARRANTEE_OF_CORRECT_SIZE, + imsz_CORRECT_SIZES_ROUNDED_TO_NEAREST_1_CM, + imsz_CORRECT_SIZES_DIVIDED_BY_5_ROUNDED_TO_NEAREST_5_CM +}; + +struct SI_PACK_THIS_STRUCT HDMI_LLC_Byte13_t { + unsigned char reserved:3; + enum image_size_e image_size:2; + unsigned char _3D_multi_present:2; + unsigned char _3D_present:1; +}; + +struct SI_PACK_THIS_STRUCT HDMI_LLC_Byte14_t { + unsigned char HDMI_3D_len:5; + unsigned char HDMI_VIC_len:3; +}; + +struct SI_PACK_THIS_STRUCT VSDB_byte_13_through_byte_15_t { + struct HDMI_LLC_Byte13_t byte13; + struct HDMI_LLC_Byte14_t byte14; + uint8_t vicList[1]; /* variable length list base on HDMI_VIC_len */ +}; + +struct SI_PACK_THIS_STRUCT VSDB_all_fields_b9_thru_b15_t { + uint8_t video_latency; + uint8_t audio_latency; + uint8_t interlaced_video_latency; + uint8_t interlaced_audio_latency; + struct VSDB_byte_13_through_byte_15_t byte_13_through_byte_15; + /* There must be no fields after here */ +}; + +struct SI_PACK_THIS_STRUCT + VSDB_all_fields_b9_thru_b15_sans_progressive_latency_t { + uint8_t interlaced_video_latency; + uint8_t interlaced_audio_latency; + struct VSDB_byte_13_through_byte_15_t byte_13_through_byte_15; + /* There must be no fields after here */ +}; + +struct SI_PACK_THIS_STRUCT + VSDB_all_fields_b9_thru_b15_sans_interlaced_latency_t { + uint8_t video_latency; + uint8_t audio_latency; + struct VSDB_byte_13_through_byte_15_t byte_13_through_byte_15; + /* There must be no fields after here */ +}; + +struct SI_PACK_THIS_STRUCT + VSDB_all_fields_b9_thru_b15_sans_all_latency_t { + struct VSDB_byte_13_through_byte_15_t byte_13_through_byte_15; + /* There must be no fields after here */ +}; + +struct SI_PACK_THIS_STRUCT HDMI_LLC_vsdb_payload_t { + struct HDMI_LLC_BA_t B_A; + struct HDMI_LLC_DC_t D_C; + struct HDMI_LLC_Byte6_t byte6; + uint8_t maxTMDSclock; + struct HDMI_LLC_byte8_t byte8; + + union { + struct VSDB_all_fields_b9_thru_b15_sans_all_latency_t + vsdb_b9_to_b15_no_latency; + struct VSDB_all_fields_b9_thru_b15_sans_progressive_latency_t + vsdb_b9_to_b15_no_p_latency; + struct VSDB_all_fields_b9_thru_b15_sans_interlaced_latency_t + vsdb_b9_to_b15_no_i_latency; + struct VSDB_all_fields_b9_thru_b15_t vsdb_all_fields_b9_thru_b15; + } vsdb_fields_b9_thru_b15; + /* There must be no fields after here */ +}; + +struct SI_PACK_THIS_STRUCT _3D_structure_all_15_8_t { + uint8_t frame_packing:1; + uint8_t reserved1:5; + uint8_t top_bottom:1; + uint8_t reserved2:1; +}; + +struct SI_PACK_THIS_STRUCT _3D_structure_all_7_0_t { + uint8_t side_by_side:1; + uint8_t reserved:7; +}; + +struct SI_PACK_THIS_STRUCT _3D_structure_all_t { + struct _3D_structure_all_15_8_t _3D_structure_all_15_8; + struct _3D_structure_all_7_0_t _3D_structure_all_7_0; +}; + +struct SI_PACK_THIS_STRUCT _3D_mask_t { + uint8_t _3D_mask_15_8; + uint8_t _3D_mask_7_0; +}; + +struct SI_PACK_THIS_STRUCT _2D_VIC_order_3D_structure_t { + enum _3D_structure_e _3D_structure:4; /* definition from infoframe */ + unsigned _2D_VIC_order:4; +}; + +struct SI_PACK_THIS_STRUCT _3D_detail_t { + unsigned char reserved:4; + unsigned char _3D_detail:4; +}; + +struct SI_PACK_THIS_STRUCT _3D_structure_and_detail_entry_sans_byte1_t { + struct _2D_VIC_order_3D_structure_t byte0; + /*see HDMI 1.4 spec w.r.t. contents of 3D_structure_X */ +}; + +struct SI_PACK_THIS_STRUCT _3D_structure_and_detail_entry_with_byte1_t { + struct _2D_VIC_order_3D_structure_t byte0; + struct _3D_detail_t byte1; +}; + +union _3D_structure_and_detail_entry_u { + struct _3D_structure_and_detail_entry_sans_byte1_t sans_byte1; + struct _3D_structure_and_detail_entry_with_byte1_t with_byte1; +}; + +struct SI_PACK_THIS_STRUCT HDMI_3D_sub_block_sans_all_AND_mask_t { + union _3D_structure_and_detail_entry_u + _3D_structure_and_detail_list[1]; +}; + +struct SI_PACK_THIS_STRUCT HDMI_3D_sub_block_sans_mask_t { + struct _3D_structure_all_t _3D_structure_all; + union _3D_structure_and_detail_entry_u _3D_structure_and_detail_list[1]; +}; + +struct SI_PACK_THIS_STRUCT HDMI_3D_sub_block_with_all_AND_mask_t { + struct _3D_structure_all_t _3D_structure_all; + struct _3D_mask_t _3D_mask; + union _3D_structure_and_detail_entry_u + _3D_structure_and_detail_list[1]; +}; + +union HDMI_3D_sub_block_t { + struct HDMI_3D_sub_block_sans_all_AND_mask_t + HDMI_3D_sub_block_sans_all_AND_mask; + struct HDMI_3D_sub_block_sans_mask_t HDMI_3D_sub_block_sans_mask; + struct HDMI_3D_sub_block_with_all_AND_mask_t + HDMI_3D_sub_block_with_all_AND_mask; +}; + +struct SI_PACK_THIS_STRUCT vsdb_t { + union data_block_header_byte_t header; + uint8_t IEEE_OUI[3]; + union { + struct HDMI_LLC_vsdb_payload_t HDMI_LLC; + uint8_t payload[1]; /* open ended */ + } payload_u; +}; + +enum colorimetry_xvYCC_e { + xvYCC_601 = 1, + xvYCC_709 = 2 +}; + +struct SI_PACK_THIS_STRUCT colorimetry_xvYCC_t { + enum colorimetry_xvYCC_e xvYCC:2; + unsigned char reserved1:6; +}; + +struct SI_PACK_THIS_STRUCT colorimetry_meta_data_t { + unsigned char meta_data:3; + unsigned char reserved2:5; +}; + +struct SI_PACK_THIS_STRUCT colorimetry_data_payload_t { + struct colorimetry_xvYCC_t ci_data; + struct colorimetry_meta_data_t cm_meta_data; +}; + +struct SI_PACK_THIS_STRUCT colorimetry_data_block_t { + union data_block_header_byte_t header; + struct extended_tag_code_t extended_tag; + struct colorimetry_data_payload_t payload; +}; + +enum CE_overscan_underscan_behavior_e { + ceou_NEITHER, + ceou_ALWAYS_OVERSCANNED, + ceou_ALWAYS_UNDERSCANNED, + ceou_BOTH +}; + +enum IT_overscan_underscan_behavior_e { + itou_NEITHER, + itou_ALWAYS_OVERSCANNED, + itou_ALWAYS_UNDERSCANNED, + itou_BOTH +}; + +enum PT_overscan_underscan_behavior_e { + ptou_NEITHER, + ptou_ALWAYS_OVERSCANNED, + ptou_ALWAYS_UNDERSCANNED, + ptou_BOTH, +}; + +struct SI_PACK_THIS_STRUCT video_capability_data_payload_t { + enum CE_overscan_underscan_behavior_e S_CE:2; + enum IT_overscan_underscan_behavior_e S_IT:2; + enum PT_overscan_underscan_behavior_e S_PT:2; + unsigned QS:1; + unsigned quantization_range_selectable:1; +}; + +struct SI_PACK_THIS_STRUCT video_capability_data_block_t { + union data_block_header_byte_t header; + struct extended_tag_code_t extended_tag; + struct video_capability_data_payload_t payload; +}; + +struct SI_PACK_THIS_STRUCT CEA_data_block_collection_t { + union data_block_header_byte_t header; + union { + struct extended_tag_code_t extended_tag; + struct cea_short_descriptor_t short_descriptor; + } payload_u; + /* open ended array of cea_short_descriptor_t starts here */ +}; + +struct SI_PACK_THIS_STRUCT CEA_extension_version_1_t { + uint8_t reservedMustBeZero; + uint8_t reserved[123]; +}; + +struct SI_PACK_THIS_STRUCT CEA_extension_2_3_misc_support_t { + uint8_t total_number_native_dtds_in_entire_EDID:4; + uint8_t YCrCb422_support:1; + uint8_t YCrCb444_support:1; + uint8_t basic_audio_support:1; + uint8_t underscan_IT_formats_by_default:1; +}; + +struct SI_PACK_THIS_STRUCT CEA_extension_version_2_t { + struct CEA_extension_2_3_misc_support_t misc_support; + uint8_t reserved[123]; +}; + +struct SI_PACK_THIS_STRUCT CEA_extension_version_3_t { + struct CEA_extension_2_3_misc_support_t misc_support; + union { + uint8_t data_block_collection[123]; + uint8_t reserved[123]; + } Offset4_u; +}; + +struct SI_PACK_THIS_STRUCT block_map_t { + uint8_t tag; + uint8_t block_tags[126]; + uint8_t checksum; +}; + +struct SI_PACK_THIS_STRUCT CEA_extension_t { + uint8_t tag; + uint8_t revision; + uint8_t byte_offset_to_18_byte_descriptors; + union { + struct CEA_extension_version_1_t version1; + struct CEA_extension_version_2_t version2; + struct CEA_extension_version_3_t version3; + } version_u; + uint8_t checksum; +}; + +struct SI_PACK_THIS_STRUCT detailed_timing_descriptor_t { + uint8_t pixel_clock_low; + uint8_t pixel_clock_high; + uint8_t horz_active_7_0; + uint8_t horz_blanking_7_0; + struct SI_PACK_THIS_STRUCT { + unsigned char horz_blanking_11_8:4; + unsigned char horz_active_11_8:4; + } horz_active_blanking_high; + uint8_t vert_active_7_0; + uint8_t vert_blanking_7_0; + struct SI_PACK_THIS_STRUCT { + unsigned char vert_blanking_11_8:4; + unsigned char vert_active_11_8:4; + } vert_active_blanking_high; + uint8_t horz_sync_offset_7_0; + uint8_t horz_sync_pulse_width7_0; + struct SI_PACK_THIS_STRUCT { + unsigned char vert_sync_pulse_width_3_0:4; + unsigned char vert_sync_offset_3_0:4; + } vert_sync_offset_width; + struct SI_PACK_THIS_STRUCT { + unsigned char vert_sync_pulse_width_5_4:2; + unsigned char vert_sync_offset_5_4:2; + unsigned char horz_sync_pulse_width_9_8:2; + unsigned char horz_sync_offset_9_8:2; + } hs_vs_offset_pulse_width; + uint8_t horz_image_size_in_mm_7_0; + uint8_t vert_image_size_in_mm_7_0; + struct SI_PACK_THIS_STRUCT { + unsigned char vert_image_size_in_mm_11_8:4; + unsigned char horz_image_size_in_mm_11_8:4; + } image_size_high; + uint8_t horz_border_in_lines; + uint8_t vert_border_in_pixels; + struct SI_PACK_THIS_STRUCT { + unsigned char stereo_bit_0:1; + unsigned char sync_signal_options:2; + unsigned char sync_signal_type:2; + unsigned char stereo_bits_2_1:2; + unsigned char interlaced:1; + } flags; +}; + +struct SI_PACK_THIS_STRUCT red_green_bits_1_0_t { + unsigned char green_y:2; + unsigned char green_x:2; + unsigned char red_y:2; + unsigned char red_x:2; +}; + +struct SI_PACK_THIS_STRUCT blue_white_bits_1_0_t { + unsigned char white_y:2; + unsigned char white_x:2; + unsigned char blue_y:2; + unsigned char blue_x:2; +}; + +struct SI_PACK_THIS_STRUCT established_timings_I_t { + unsigned char et800x600_60p:1; + unsigned char et800x600_56p:1; + unsigned char et640x480_75p:1; + unsigned char et640x480_72p:1; + unsigned char et640x480_67p:1; + unsigned char et640x480_60p:1; + unsigned char et720x400_88p:1; + unsigned char et720x400_70p:1; +}; + +struct SI_PACK_THIS_STRUCT established_timings_II_t { + unsigned char et1280x1024_75p:1; + unsigned char et1024x768_75p:1; + unsigned char et1024x768_70p:1; + unsigned char et1024x768_60p:1; + unsigned char et1024x768_87i:1; + unsigned char et832x624_75p:1; + unsigned char et800x600_75p:1; + unsigned char et800x600_72p:1; +}; + +struct SI_PACK_THIS_STRUCT manufacturers_timings_t { + unsigned char reserved:7; + unsigned char et1152x870_75p:1; +}; + +enum image_aspect_ratio_e { + iar_16_to_10, + iar_4_to_3, + iar_5_to_4, + iar_16_to_9 +}; + +struct SI_PACK_THIS_STRUCT standard_timing_t { + unsigned char horz_pix_div_8_minus_31; + unsigned char field_refresh_rate_minus_60:6; + enum image_aspect_ratio_e image_aspect_ratio:2; +}; + +struct SI_PACK_THIS_STRUCT EDID_block0_t { + unsigned char header_data[8]; + struct two_bytes_t id_manufacturer_name; + struct two_bytes_t id_product_code; + unsigned char serial_number[4]; + unsigned char week_of_manufacture; + unsigned char year_of_manufacture; + unsigned char EDID_version; + unsigned char EDID_revision; + unsigned char video_input_definition; + unsigned char horz_screen_size_or_aspect_ratio; + unsigned char vert_screen_size_or_aspect_ratio; + unsigned char display_transfer_characteristic; + unsigned char feature_support; + struct red_green_bits_1_0_t red_green_bits_1_0; + struct blue_white_bits_1_0_t blue_white_bits_1_0; + unsigned char red_x; + unsigned char red_y; + unsigned char green_x; + unsigned char green_y; + unsigned char blue_x; + unsigned char blue_y; + unsigned char white_x; + unsigned char white_y; + struct established_timings_I_t established_timings_I; + struct established_timings_II_t established_timings_II; + struct manufacturers_timings_t manufacturers_timings; + struct standard_timing_t standard_timings[8]; + struct detailed_timing_descriptor_t detailed_timing_descriptors[4]; + unsigned char extension_flag; + unsigned char checksum; +}; + +struct SI_PACK_THIS_STRUCT monitor_name_t { + uint8_t flag_required[2]; + uint8_t flag_reserved; + uint8_t data_type_tag; + uint8_t flag; + uint8_t ascii_name[13]; +}; + +struct SI_PACK_THIS_STRUCT monitor_range_limits_t { + uint8_t flag_required[2]; + uint8_t flag_reserved; + uint8_t data_type_tag; + uint8_t flag; + uint8_t min_vertical_rate_in_Hz; + uint8_t max_vertical_rate_in_Hz; + uint8_t min_horizontal_rate_in_KHz; + uint8_t max_horizontal_rate_in_KHz; + uint8_t max_pixel_clock_in_MHz_div_10; + uint8_t tag_secondary_formula; + uint8_t filler[7]; +}; + +union _18_byte_descriptor_u { + struct detailed_timing_descriptor_t dtd; + struct monitor_name_t name; + struct monitor_range_limits_t range_limits; +}; + +struct SI_PACK_THIS_STRUCT display_mode_3D_info_t { + unsigned char dmi_3D_supported:1; + unsigned char dmi_sufficient_bandwidth:1; +}; + +#ifdef ENABLE_EDID_DEBUG_PRINT +void dump_EDID_block_impl(const char *pszFunction, int iLineNum, + uint8_t override, uint8_t *pData, uint16_t length); + +void clear_EDID_block_impl(uint8_t *pData); + +#define DUMP_EDID_BLOCK(override, pData, length) \ + dump_EDID_block_impl(__func__, __LINE__, override, (uint8_t *)pData, \ + length) +#define CLEAR_EDID_BLOCK(pData) clear_EDID_block_impl(pData) +#else +#define DUMP_EDID_BLOCK(override, pData, length) /* nothing to do */ +#define CLEAR_EDID_BLOCK(pData) /* nothing to do */ +#endif + +enum EDID_error_codes { + EDID_OK, + EDID_INCORRECT_HEADER, + EDID_CHECKSUM_ERROR, + EDID_NO_861_EXTENSIONS, + EDID_SHORT_DESCRIPTORS_OK, + EDID_LONG_DESCRIPTORS_OK, + EDID_EXT_TAG_ERROR, + EDID_REV_ADDR_ERROR, + EDID_V_DESCR_OVERFLOW, + EDID_UNKNOWN_TAG_CODE, + EDID_NO_DETAILED_DESCRIPTORS, + EDID_DDC_BUS_REQ_FAILURE, + EDID_DDC_BUS_RELEASE_FAILURE, + EDID_READ_TIMEOUT +}; + +SI_POP_STRUCT_PACKING +#endif /* #if !defined(SI_EDID_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/si_emsc_hid-mt.c b/drivers/video/fbdev/msm/mhl3/si_emsc_hid-mt.c new file mode 100644 index 000000000000..757fb677b920 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_emsc_hid-mt.c @@ -0,0 +1,1919 @@ +/* + * MHL3 HID driver for multitouch panels + * + * Copyright (c) 2013-2014 Lee Mulcahy + * Copyright (c) 2013-2014 Silicon Image, Inc + * + * This code is based on hid-multitouch.c: + * + * Copyright (c) 2010-2011 Stephane Chatty + * Copyright (c) 2010-2011 Benjamin Tissoires + * Copyright (c) 2010-2011 Ecole Nationale de l'Aviation Civile, France + * + */ + +/* + * 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; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "hid-ids.h" + +#include "si_emsc_hid.h" + +#ifndef PCI_VENDOR_ID_SILICONIMAGE +#define PCI_VENDOR_ID_SILICONIMAGE 0x1095 +#define USB_VENDOR_ID_SILICONIMAGE 0x1A4A +#define MHL_PRODUCT_ID_SILICONIMAGE_9394 0x9394 +#define MHL_PRODUCT_ID_SILICONIMAGE_9679 0x9679 +#define MHL_PRODUCT_ID_SILICONIMAGE_TEST 0x0000 +#endif + + + +#ifndef USB_VENDOR_ID_ATMEL +#define USB_VENDOR_ID_ATMEL 0x03EB +#endif +#ifndef USB_DEVICE_ID_ATMEL_MXT_384E +#define USB_DEVICE_ID_ATMEL_MXT_384E 0x2128 +#endif +#ifndef USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 1 +#endif + +#ifndef USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 1 +#endif +#ifndef USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2 2 +#endif +#ifndef USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3 3 +#endif +#ifndef USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4 4 +#endif + +#ifndef USB_VENDOR_ID_ATMEL +#define USB_VENDOR_ID_ATMEL 0x03EB +#endif +#ifndef USB_DEVICE_ID_ATMEL_MXT_384E +#define USB_DEVICE_ID_ATMEL_MXT_384E 0x2128 +#endif + +/* quirks to control the device */ +#define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0) +#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1) +#define MT_QUIRK_CYPRESS (1 << 2) +#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3) +#define MT_QUIRK_ALWAYS_VALID (1 << 4) +#define MT_QUIRK_VALID_IS_INRANGE (1 << 5) +#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6) +#define MT_QUIRK_EGALAX_XYZ_FIXUP (1 << 7) +#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8) +#if (LINUX_KERNEL_VER >= 311) +#define MT_QUIRK_NO_AREA (1 << 9) +#define MT_QUIRK_IGNORE_DUPLICATES (1 << 10) +#define MT_QUIRK_HOVERING (1 << 11) +#define MT_QUIRK_CONTACT_CNT_ACCURATE (1 << 12) +#endif +/* SIMG GCS - 130603 - bz33633 */ +#define MT_QUIRK_SIMG_FIRST 13 +#define MT_QUIRK_SWAP_LEFTRIGHT (1 << (MT_QUIRK_SIMG_FIRST+0)) +#define MT_QUIRK_SWAP_UPDOWN (1 << (MT_QUIRK_SIMG_FIRST+1)) +#define MT_QUIRK_SWAP_XY (1 << (MT_QUIRK_SIMG_FIRST+2)) +#define MT_QUIRK_SWAP_WH (1 << (MT_QUIRK_SIMG_FIRST+3)) +struct mt_slot { +#if (LINUX_KERNEL_VER >= 311) + __s32 x, y, cx, cy, p, w, h; +#else + __s32 x, y, p, w, h; +#endif + + __s32 contactid; /* the device ContactID assigned to this slot */ + bool touch_state; /* is the touch valid? */ +#if (LINUX_KERNEL_VER >= 311) + bool inrange_state; /* is the finger in proximity of the sensor? */ +#else + bool seen_in_this_frame;/* has this slot been updated */ +#endif +}; + +struct mt_class { + __s32 name; /* MT_CLS */ + __s32 quirks; + __s32 sn_move; /* Signal/noise ratio for move events */ + __s32 sn_width; /* Signal/noise ratio for width events */ + __s32 sn_height; /* Signal/noise ratio for height events */ + __s32 sn_pressure; /* Signal/noise ratio for pressure events */ + __u8 maxcontacts; +#if (LINUX_KERNEL_VER >= 311) + bool is_indirect; /* true for touchpads */ +#endif +}; + +#if (LINUX_KERNEL_VER >= 311) +struct mt_device { + struct mt_slot curdata; /* placeholder of incoming data */ + struct mt_class mtclass; /* our mt device class */ + struct mt_fields *fields; /* temporary placeholder for storing the + multitouch fields */ + int cc_index; /* contact count field index in the report */ + int cc_value_index; /* contact count value index in the field */ + unsigned last_slot_field; /* the last field of a slot */ + unsigned mt_report_id; /* the report ID of the multitouch device */ + unsigned pen_report_id; /* the report ID of the pen device */ + __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ + __s16 inputmode_index; /* InputMode HID feature index in the report */ + __s16 maxcontact_report_id; /* Maximum Contact Number HID feature, + -1 if non-existent */ + __u8 num_received; /* how many contacts we received */ + __u8 num_expected; /* expected last contact index */ + __u8 maxcontacts; + __u8 touches_by_report; /* how many touches are present in one report: + * 1 means we should use a serial protocol + * > 1 means hybrid (multitouch) protocol */ + bool serial_maybe; /* need to check for serial protocol */ + bool curvalid; /* is the current contact valid? */ + unsigned mt_flags; /* flags to pass to input-mt */ +}; + +struct mt_fields { + unsigned usages[HID_MAX_FIELDS]; + unsigned int length; +}; + +static void mt_post_parse_default_settings(struct mt_device *td); +static void mt_post_parse(struct mt_device *td); + + +#else +struct mt_device { + struct mt_slot curdata; /* placeholder of incoming data */ + struct mt_class *mtclass; /* our mt device class */ + unsigned last_field_index; /* last field index of the report */ + unsigned last_slot_field; /* the last field of a slot */ + int last_mt_collection; /* last known mt-related collection */ + __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ + __u8 num_received; /* how many contacts we received */ + __u8 num_expected; /* expected last contact index */ + __u8 maxcontacts; + bool curvalid; /* is the current contact valid? */ + struct mt_slot *slots; +}; +#endif + +/* classes of device behavior */ +#define MT_CLS_DEFAULT 0x0001 + +#define MT_CLS_SERIAL 0x0002 +#define MT_CLS_CONFIDENCE 0x0003 + +#if (LINUX_KERNEL_VER >= 311) +#define MT_CLS_CONFIDENCE_CONTACT_ID 0x0004 +#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0005 +#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0006 +#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0007 + +#define MT_CLS_DUAL_NSMU_CONTACTID 0x0008 +#define MT_CLS_INRANGE_CONTACTNUMBER 0x0009 +#define MT_CLS_NSMU 0x000a +#define MT_CLS_DUAL_CONTACT_NUMBER 0x0010 +#define MT_CLS_DUAL_CONTACT_ID 0x0011 + +#else +#define MT_CLS_CONFIDENCE_MINUS_ONE 0x0004 +#define MT_CLS_DUAL_INRANGE_CONTACTID 0x0005 +#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER 0x0006 +#define MT_CLS_DUAL_NSMU_CONTACTID 0x0007 +#endif + +/* vendor specific classes */ +#define MT_CLS_3M 0x0101 +#define MT_CLS_CYPRESS 0x0102 +#define MT_CLS_EGALAX 0x0103 +#if (LINUX_KERNEL_VER >= 311) +#define MT_CLS_EGALAX_SERIAL 0x0104 +#define MT_CLS_TOPSEED 0x0105 +#define MT_CLS_PANASONIC 0x0106 +#define MT_CLS_FLATFROG 0x0107 +#define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108 +#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109 +#endif +/* SIMG 140714 - BZ33633 */ +#define MT_CLS_OCULAR 0x010A + +#define MT_DEFAULT_MAXCONTACT 10 + +#define MT_DEFAULT_MAXCONTACT 10 +#define MT_MAX_MAXCONTACT 250 +#if (LINUX_KERNEL_VER >= 311) +#define MT_USB_DEVICE(v, p) HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH, v, p) +#define MT_BT_DEVICE(v, p) HID_DEVICE(BUS_BLUETOOTH, \ + HID_GROUP_MULTITOUCH, v, p) +#else +#define MT_USB_DEVICE(v, p) HID_USB_DEVICE(v, p) +#endif + +/* + * These device-dependent functions determine what slot corresponds + * to a valid contact that was just read. + */ + +static int cypress_compute_slot(struct mt_device *td) +{ + if (td->curdata.contactid != 0 || td->num_received == 0) + return td->curdata.contactid; + else + return -1; +} + +#if (LINUX_KERNEL_VER < 309) +static int find_slot_from_contactid(struct mt_device *td) +{ + int i; + for (i = 0; i < td->maxcontacts; ++i) { + if (td->slots[i].contactid == td->curdata.contactid && + td->slots[i].touch_state) + return i; + } + for (i = 0; i < td->maxcontacts; ++i) { + if (!td->slots[i].seen_in_this_frame && + !td->slots[i].touch_state) + return i; + } + /* should not occurs. If this happens that means + * that the device sent more touches that it says + * in the report descriptor. It is ignored then. */ + return -1; +} +#endif + + +#if (LINUX_KERNEL_VER >= 311) +struct mt_class mhl3_mt_classes[] = { + { .name = MT_CLS_DEFAULT, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE }, + { .name = MT_CLS_NSMU, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, + { .name = MT_CLS_SERIAL, + .quirks = MT_QUIRK_ALWAYS_VALID}, + { .name = MT_CLS_CONFIDENCE, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE }, + { .name = MT_CLS_CONFIDENCE_CONTACT_ID, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | + MT_QUIRK_SLOT_IS_CONTACTID }, + { .name = MT_CLS_CONFIDENCE_MINUS_ONE, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | + MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE }, + { .name = MT_CLS_DUAL_INRANGE_CONTACTID, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTID, + .maxcontacts = 2 }, + { .name = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER, + .maxcontacts = 2 }, + { .name = MT_CLS_DUAL_NSMU_CONTACTID, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_SLOT_IS_CONTACTID, + .maxcontacts = 2 }, + { .name = MT_CLS_INRANGE_CONTACTNUMBER, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER }, + { .name = MT_CLS_DUAL_CONTACT_NUMBER, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER, + .maxcontacts = 2 }, + { .name = MT_CLS_DUAL_CONTACT_ID, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_SLOT_IS_CONTACTID, + .maxcontacts = 2 }, + + /* + * vendor specific classes + */ + { .name = MT_CLS_3M, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | + MT_QUIRK_SLOT_IS_CONTACTID, + .sn_move = 2048, + .sn_width = 128, + .sn_height = 128, + .maxcontacts = 60, + }, + { .name = MT_CLS_CYPRESS, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_CYPRESS, + .maxcontacts = 10 }, + { .name = MT_CLS_EGALAX, + .quirks = MT_QUIRK_SLOT_IS_CONTACTID | + MT_QUIRK_VALID_IS_INRANGE, + .sn_move = 4096, + .sn_pressure = 32, + }, + { .name = MT_CLS_OCULAR, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_SWAP_UPDOWN | + MT_QUIRK_SWAP_LEFTRIGHT, + .maxcontacts = 8, + }, + { .name = MT_CLS_EGALAX_SERIAL, + .quirks = MT_QUIRK_SLOT_IS_CONTACTID | + MT_QUIRK_ALWAYS_VALID, + .sn_move = 4096, + .sn_pressure = 32, + }, + { .name = MT_CLS_TOPSEED, + .quirks = MT_QUIRK_ALWAYS_VALID, + .is_indirect = true, + .maxcontacts = 2, + }, + { .name = MT_CLS_PANASONIC, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP, + .maxcontacts = 4 }, + { .name = MT_CLS_GENERALTOUCH_TWOFINGERS, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER, + .maxcontacts = 2 + }, + { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_SLOT_IS_CONTACTNUMBER + }, + + { .name = MT_CLS_FLATFROG, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_NO_AREA, + .sn_move = 2048, + .maxcontacts = 40, + }, + { } +}; +#else +struct mt_class mhl3_mt_classes[] = { + { .name = MT_CLS_DEFAULT, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP }, + { .name = MT_CLS_SERIAL, + .quirks = MT_QUIRK_ALWAYS_VALID}, + { .name = MT_CLS_CONFIDENCE, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE }, + { .name = MT_CLS_CONFIDENCE_MINUS_ONE, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | + MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE }, + { .name = MT_CLS_DUAL_INRANGE_CONTACTID, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTID, + .maxcontacts = 2 }, + { .name = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER, + .maxcontacts = 2 }, + { .name = MT_CLS_DUAL_NSMU_CONTACTID, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_SLOT_IS_CONTACTID, + .maxcontacts = 2 }, + + /* + * vendor specific classes + */ + { .name = MT_CLS_3M, + .quirks = MT_QUIRK_VALID_IS_CONFIDENCE | + MT_QUIRK_SLOT_IS_CONTACTID, + .sn_move = 2048, + .sn_width = 128, + .sn_height = 128 }, + { .name = MT_CLS_CYPRESS, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_CYPRESS, + .maxcontacts = 10 }, + { .name = MT_CLS_EGALAX, + .quirks = MT_QUIRK_SLOT_IS_CONTACTID | + MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_EGALAX_XYZ_FIXUP, + .maxcontacts = 2, + .sn_move = 4096, + .sn_pressure = 32, + }, + { .name = MT_CLS_OCULAR, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_SWAP_UPDOWN | + MT_QUIRK_SWAP_LEFTRIGHT, + .maxcontacts = 8, + }, + { } +}; +#endif + +#if (LINUX_KERNEL_VER >= 311) +static void mt_free_input_name(struct hid_input *hi) +{ + struct hid_device *hdev = hi->report->device; + const char *name = hi->input->name; + + if (name != hdev->name) { + hi->input->name = hdev->name; + kfree(name); + } +} +#endif + +void mt_feature_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage) +{ + struct mt_device *td = hid_get_drvdata(hdev); + + switch (usage->hid) { + case HID_DG_INPUTMODE: +#if (LINUX_KERNEL_VER >= 311) + /* Ignore if value index is out of bounds. */ + if (usage->usage_index >= field->report_count) { + dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n"); + break; + } + + td->inputmode = field->report->id; + td->inputmode_index = usage->usage_index; +#else + td->inputmode = field->report->id; +#endif + break; + case HID_DG_CONTACTMAX: +#if (LINUX_KERNEL_VER >= 311) + td->maxcontact_report_id = field->report->id; + td->maxcontacts = field->value[0]; + if (!td->maxcontacts && + field->logical_maximum <= MT_MAX_MAXCONTACT) + td->maxcontacts = field->logical_maximum; + /* check if the maxcontacts is given by the class */ + if (td->mtclass.maxcontacts) + td->maxcontacts = td->mtclass.maxcontacts; +#else + td->maxcontacts = field->value[0]; + /* check if the maxcontacts is given by the class */ + if (td->mtclass->maxcontacts) + td->maxcontacts = td->mtclass->maxcontacts; +#endif + + break; +#if (LINUX_KERNEL_VER >= 311) + case 0xff0000c5: + if (field->report_count == 256 && field->report_size == 8) { + /* Win 8 devices need special quirks */ + __s32 *quirks = &td->mtclass.quirks; + *quirks |= MT_QUIRK_ALWAYS_VALID; + *quirks |= MT_QUIRK_IGNORE_DUPLICATES; + *quirks |= MT_QUIRK_HOVERING; + *quirks |= MT_QUIRK_CONTACT_CNT_ACCURATE; + *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; + *quirks &= ~MT_QUIRK_VALID_IS_INRANGE; + *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; + } +#endif + } +} + +static void set_abs(struct input_dev *input, unsigned int code, + struct hid_field *field, int snratio) +{ + int fmin = field->logical_minimum; + int fmax = field->logical_maximum; + int fuzz = snratio ? (fmax - fmin) / snratio : 0; + input_set_abs_params(input, code, fmin, fmax, fuzz, 0); +#if (LINUX_KERNEL_VER >= 311) + input_abs_set_res(input, code, hidinput_calc_abs_res(field, code)); +#endif +} + +#if (LINUX_KERNEL_VER >= 311) +static void mt_store_field(struct hid_usage *usage, struct mt_device *td, + struct hid_input *hi) +{ + struct mt_fields *f = td->fields; + + if (f->length >= HID_MAX_FIELDS) + return; + + f->usages[f->length++] = usage->hid; +} +#endif + +/* + * In 3.11, this function is called mt_touch_input_mapping + */ +int mhl3_mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +#if (LINUX_KERNEL_VER >= 311) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_class *cls = &td->mtclass; + int code; + struct hid_usage *prev_usage = NULL; + + if (field->application == HID_DG_TOUCHSCREEN) + td->mt_flags |= INPUT_MT_DIRECT; + + /* + * Model touchscreens providing buttons as touchpads. + */ + if (field->application == HID_DG_TOUCHPAD || + (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) + td->mt_flags |= INPUT_MT_POINTER; + + if (usage->usage_index) + prev_usage = &field->usage[usage->usage_index - 1]; + + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + if (prev_usage && (prev_usage->hid == usage->hid)) { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOOL_X); + set_abs(hi->input, ABS_MT_TOOL_X, field, + cls->sn_move); + } else { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_X); + set_abs(hi->input, ABS_MT_POSITION_X, field, + cls->sn_move); + } + + mt_store_field(usage, td, hi); + return 1; + case HID_GD_Y: + if (prev_usage && (prev_usage->hid == usage->hid)) { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOOL_Y); + set_abs(hi->input, ABS_MT_TOOL_Y, field, + cls->sn_move); + } else { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + set_abs(hi->input, ABS_MT_POSITION_Y, field, + cls->sn_move); + } + + mt_store_field(usage, td, hi); + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + case HID_DG_INRANGE: + if (cls->quirks & MT_QUIRK_HOVERING) { + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_DISTANCE); + input_set_abs_params(hi->input, + ABS_MT_DISTANCE, 0, 1, 0, 0); + } + mt_store_field(usage, td, hi); + return 1; + case HID_DG_CONFIDENCE: + mt_store_field(usage, td, hi); + return 1; + case HID_DG_TIPSWITCH: + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + input_set_capability(hi->input, EV_KEY, BTN_TOUCH); + mt_store_field(usage, td, hi); + return 1; + case HID_DG_CONTACTID: + mt_store_field(usage, td, hi); + td->touches_by_report++; + td->mt_report_id = field->report->id; + return 1; + case HID_DG_WIDTH: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MAJOR); + if (!(cls->quirks & MT_QUIRK_NO_AREA)) + set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, + cls->sn_width); + mt_store_field(usage, td, hi); + return 1; + case HID_DG_HEIGHT: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MINOR); + if (!(cls->quirks & MT_QUIRK_NO_AREA)) { + set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, + cls->sn_height); + input_set_abs_params(hi->input, + ABS_MT_ORIENTATION, 0, 1, 0, 0); + } + mt_store_field(usage, td, hi); + return 1; + case HID_DG_TIPPRESSURE: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_PRESSURE); + set_abs(hi->input, ABS_MT_PRESSURE, field, + cls->sn_pressure); + mt_store_field(usage, td, hi); + return 1; + case HID_DG_CONTACTCOUNT: + /* Ignore if indexes are out of bounds. */ + if (field->index >= field->report->maxfield || + usage->usage_index >= field->report_count) + return 1; + td->cc_index = field->index; + td->cc_value_index = usage->usage_index; + return 1; + case HID_DG_CONTACTMAX: + /* we don't set td->last_slot_field as contactcount and + * contact max are global to the report */ + return -1; + case HID_DG_TOUCH: + /* Legacy devices use TIPSWITCH and not TOUCH. + * Let's just ignore this field. */ + return -1; + } + /* let hid-input decide for the others */ + return 0; + + case HID_UP_BUTTON: + code = BTN_MOUSE + ((usage->hid - 1) & HID_USAGE); + hid_map_usage(hi, usage, bit, max, EV_KEY, code); + input_set_capability(hi->input, EV_KEY, code); + return 1; + + case 0xff000000: + /* we do not want to map these: no input-oriented meaning */ + return -1; + } + + return 0; +} +#else +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_class *cls = td->mtclass; + +#ifdef GCS_QUIRKS_FOR_SIMG + __s32 quirks = cls->quirks; +#endif + +#if (LINUX_KERNEL_VER >= 311) + /* Only map fields from TouchScreen or TouchPad collections. + * We need to ignore fields that belong to other collections + * such as Mouse that might have the same GenericDesktop usages. */ + if (field->application != HID_DG_TOUCHSCREEN && + field->application != HID_DG_TOUCHPAD) + return -1; +#else + /* Only map fields from TouchScreen or TouchPad collections. + * We need to ignore fields that belong to other collections + * such as Mouse that might have the same GenericDesktop usages. */ + if (field->application == HID_DG_TOUCHSCREEN) + set_bit(INPUT_PROP_DIRECT, hi->input->propbit); + else if (field->application == HID_DG_TOUCHPAD) + set_bit(INPUT_PROP_POINTER, hi->input->propbit); + else + return 0; +#endif + + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: +#ifdef GCS_QUIRKS_FOR_SIMG + if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) + field->logical_maximum = 32760; +#endif + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_X); + set_abs(hi->input, ABS_MT_POSITION_X, field, + cls->sn_move); + /* touchscreen emulation */ + set_abs(hi->input, ABS_X, field, cls->sn_move); + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + case HID_GD_Y: +#ifdef GCS_QUIRKS_FOR_SIMG + if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) + field->logical_maximum = 32760; +#endif + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + set_abs(hi->input, ABS_MT_POSITION_Y, field, + cls->sn_move); + /* touchscreen emulation */ + set_abs(hi->input, ABS_Y, field, cls->sn_move); + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + case HID_DG_INRANGE: + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + case HID_DG_CONFIDENCE: + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + case HID_DG_TIPSWITCH: + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + input_set_capability(hi->input, EV_KEY, BTN_TOUCH); + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + case HID_DG_CONTACTID: + if (!td->maxcontacts) + td->maxcontacts = MT_DEFAULT_MAXCONTACT; +#if (LINUX_KERNEL_VER >= 311) + input_mt_init_slots(hi->input, td->maxcontacts, 0); +#else + input_mt_init_slots(hi->input, td->maxcontacts); +#endif + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + td->last_mt_collection = usage->collection_index; + return 1; + case HID_DG_WIDTH: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MAJOR); + set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, + cls->sn_width); + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + case HID_DG_HEIGHT: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MINOR); + set_abs(hi->input, ABS_MT_TOUCH_MINOR, field, + cls->sn_height); + input_set_abs_params(hi->input, + ABS_MT_ORIENTATION, 0, 1, 0, 0); + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + case HID_DG_TIPPRESSURE: + if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP) + field->logical_minimum = 0; + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_PRESSURE); + set_abs(hi->input, ABS_MT_PRESSURE, field, + cls->sn_pressure); + /* touchscreen emulation */ + set_abs(hi->input, ABS_PRESSURE, field, + cls->sn_pressure); + if (td->last_mt_collection == usage->collection_index) { + td->last_slot_field = usage->hid; + td->last_field_index = field->index; + } + return 1; + case HID_DG_CONTACTCOUNT: + if (td->last_mt_collection == usage->collection_index) + td->last_field_index = field->index; + return 1; + case HID_DG_CONTACTMAX: + /* we don't set td->last_slot_field as contactcount and + * contact max are global to the report */ + if (td->last_mt_collection == usage->collection_index) + td->last_field_index = field->index; + return -1; + } + /* let hid-input decide for the others */ + return 0; + + case 0xff000000: + /* we do not want to map these: no input-oriented meaning */ + return -1; + } + + return 0; +} +#endif + +int mhl3_mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ +#if (LINUX_KERNEL_VER >= 311) + if (field->physical == HID_DG_STYLUS) + return 0; +#endif + + if (usage->type == EV_KEY || usage->type == EV_ABS) + set_bit(usage->type, hi->input->evbit); + + return -1; +} + +#if (LINUX_KERNEL_VER >= 311) +static int mt_compute_slot(struct mt_device *td, struct input_dev *input) +#else +static int mt_compute_slot(struct mt_device *td) +#endif +{ +#if (LINUX_KERNEL_VER >= 311) + __s32 quirks = td->mtclass.quirks; +#else + __s32 quirks = td->mtclass->quirks; +#endif + + if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) + return td->curdata.contactid; + + if (quirks & MT_QUIRK_CYPRESS) + return cypress_compute_slot(td); + + if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER) + return td->num_received; + + if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE) + return td->curdata.contactid - 1; + +#if (LINUX_KERNEL_VER >= 307) + return input_mt_get_slot_by_key(input, td->curdata.contactid); +#else + return find_slot_from_contactid(td); +#endif +} + +/* + * this function is called when a whole contact has been processed, + * so that it can assign it to a slot and store the data there + */ +#if (LINUX_KERNEL_VER >= 311) +static void mt_complete_slot(struct mt_device *td, struct input_dev *input) +{ + if ((td->mtclass.quirks & MT_QUIRK_CONTACT_CNT_ACCURATE) && + td->num_received >= td->num_expected) + return; + + if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) { + int slotnum = mt_compute_slot(td, input); + struct mt_slot *s = &td->curdata; + struct input_mt *mt = input->mt; + + if (slotnum < 0 || slotnum >= td->maxcontacts) + return; + + if ((td->mtclass.quirks & MT_QUIRK_IGNORE_DUPLICATES) && mt) { + struct input_mt_slot *slot = &mt->slots[slotnum]; + if (input_mt_is_active(slot) && + input_mt_is_used(mt, slot)) + return; + } + + input_mt_slot(input, slotnum); + input_mt_report_slot_state(input, MT_TOOL_FINGER, + s->touch_state || s->inrange_state); + if (s->touch_state || s->inrange_state) { + + /* SIMG GCS - 130603 - bz33633 */ + /* this finger is on the screen */ + int wide, major, minor; + if (td->mtclass.quirks & MT_QUIRK_SWAP_WH) { + wide = (s->h > s->w); + /* + * Divided by two to match visual scale + * of touch + */ + major = min(s->w, s->h) >> 1; + minor = max(s->w, s->h) >> 1; + } else { + wide = (s->w > s->h); + /* + * Divided by two to match visual scale + * of touch + */ + major = max(s->w, s->h) >> 1; + minor = min(s->w, s->h) >> 1; + } + + if (td->mtclass.quirks & MT_QUIRK_SWAP_XY) { + input_event(input, EV_ABS, ABS_MT_POSITION_X, + s->y); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, + s->x); + input_event(input, EV_ABS, ABS_MT_TOOL_X, + s->cy); + input_event(input, EV_ABS, ABS_MT_TOOL_Y, + s->cx); + } else { + input_event(input, EV_ABS, ABS_MT_POSITION_X, + s->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, + s->y); + input_event(input, EV_ABS, ABS_MT_TOOL_X, + s->cx); + input_event(input, EV_ABS, ABS_MT_TOOL_Y, + s->cy); + + } + input_event(input, EV_ABS, ABS_MT_DISTANCE, + !s->touch_state); + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + } + } + + td->num_received++; +} +/* + * this function is called when a whole packet has been received and processed, + * so that it can decide what to send to the input layer. + */ +static void mt_sync_frame(struct mt_device *td, struct input_dev *input) +{ + input_mt_sync_frame(input); + input_sync(input); + td->num_received = 0; +} +#else + +static void mt_complete_slot(struct mt_device *td) +{ + td->curdata.seen_in_this_frame = true; + if (td->curvalid) { + int slotnum = mt_compute_slot(td); + + if (slotnum >= 0 && slotnum < td->maxcontacts) + td->slots[slotnum] = td->curdata; + } + td->num_received++; +} + +/* + * this function is called when a whole packet has been received and processed, + * so that it can decide what to send to the input layer. + */ +static void mt_emit_event(struct mt_device *td, struct input_dev *input) +{ + int i; + + for (i = 0; i < td->maxcontacts; ++i) { + struct mt_slot *s = &(td->slots[i]); + if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && + !s->seen_in_this_frame) { + s->touch_state = false; + } + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, + s->touch_state); + if (s->touch_state) { + /* SIMG GCS - 130603 - bz33633 */ + /* this finger is on the screen */ + int wide, major, minor; + if (td->mtclass->quirks & MT_QUIRK_SWAP_WH) { + wide = (s->h > s->w); + /* + * Divided by two to match visual scale of + * touch + */ + major = min(s->w, s->h) >> 1; + minor = max(s->w, s->h) >> 1; + } else { + wide = (s->w > s->h); + /* + * Divided by two to match visual scale of + * touch + */ + major = max(s->w, s->h) >> 1; + minor = min(s->w, s->h) >> 1; + } + + if (td->mtclass->quirks & MT_QUIRK_SWAP_XY) { + input_event(input, EV_ABS, ABS_MT_POSITION_X, + s->y); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, + s->x); + } else { + input_event(input, EV_ABS, ABS_MT_POSITION_X, + s->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, + s->y); + } + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + } + s->seen_in_this_frame = false; + + } + + input_mt_report_pointer_emulation(input, true); + input_sync(input); + td->num_received = 0; +} +#endif + +int mhl3_mt_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct mt_device *td = hid_get_drvdata(hid); +#if (LINUX_KERNEL_VER >= 311) + __s32 quirks = td->mtclass.quirks; +#else + __s32 quirks = td->mtclass->quirks; +#endif + +#if (LINUX_KERNEL_VER >= 311) + struct input_dev *input = field->hidinput->input; + + if (field->report->id != td->mt_report_id) + return 1; + + if (hid->claimed & HID_CLAIMED_INPUT) { +#else + if (hid->claimed & HID_CLAIMED_INPUT && td->slots) { +#endif + switch (usage->hid) { + case HID_DG_INRANGE: + if (quirks & MT_QUIRK_ALWAYS_VALID) + td->curvalid = true; + else if (quirks & MT_QUIRK_VALID_IS_INRANGE) +#if (LINUX_KERNEL_VER >= 311) + td->curdata.inrange_state = value; +#else + td->curvalid = value; +#endif + break; + case HID_DG_TIPSWITCH: + if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + td->curvalid = value; + td->curdata.touch_state = value; + break; + case HID_DG_CONFIDENCE: + if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE) + td->curvalid = value; + break; + case HID_DG_CONTACTID: + td->curdata.contactid = value; + break; + case HID_DG_TIPPRESSURE: + td->curdata.p = value; + break; + case HID_GD_X: + /* SIMG 140714 - BZ33633 */ + if (quirks & MT_QUIRK_SWAP_LEFTRIGHT) + value = field->logical_maximum - value; +#if (LINUX_KERNEL_VER >= 308) + if (usage->code == ABS_MT_TOOL_X) + td->curdata.cx = value; + else +#endif + td->curdata.x = value; + break; + case HID_GD_Y: + /* SIMG 140714 - BZ33633 */ + if (quirks & MT_QUIRK_SWAP_UPDOWN) + value = field->logical_maximum - value; +#if (LINUX_KERNEL_VER >= 308) + if (usage->code == ABS_MT_TOOL_Y) + td->curdata.cy = value; + else +#endif + td->curdata.y = value; + break; + case HID_DG_WIDTH: + td->curdata.w = value; + break; + case HID_DG_HEIGHT: + td->curdata.h = value; + break; + case HID_DG_CONTACTCOUNT: +#if (LINUX_KERNEL_VER < 311) + /* + * Includes multi-packet support where subsequent + * packets are sent with zero contactcount. + */ + if (value) + td->num_expected = value; +#endif + break; + +#if (LINUX_KERNEL_VER >= 311) + case HID_DG_TOUCH: + /* do nothing */ + break; + default: + if (usage->type) + input_event(input, usage->type, usage->code, + value); + return 1; + } /* end of switch */ + + if (usage->usage_index + 1 == field->report_count) { + /* we only take into account the last report. */ + if (usage->hid == td->last_slot_field) + mt_complete_slot(td, field->hidinput->input); + } + } + +#else + default: + /* fallback to the generic hidinput handling */ + return 0; + } + + if (usage->hid == td->last_slot_field) + mt_complete_slot(td); + + if (field->index == td->last_field_index + && td->num_received >= td->num_expected) + mt_emit_event(td, field->hidinput->input); + } + + /* we have handled the hidinput part, now remains hiddev */ + if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); +#endif + return 1; +} + +#if (LINUX_KERNEL_VER >= 311) +static void mt_touch_report(struct hid_device *hid, struct hid_report *report) +{ + struct mt_device *td = hid_get_drvdata(hid); + struct hid_field *field; + unsigned count; + int r, n; + + /* + * Includes multi-packet support where subsequent + * packets are sent with zero contactcount. + */ + if (td->cc_index >= 0) { + struct hid_field *field = report->field[td->cc_index]; + int value = field->value[td->cc_value_index]; + if (value) + td->num_expected = value; + } + + for (r = 0; r < report->maxfield; r++) { + field = report->field[r]; + count = field->report_count; + + if (!(HID_MAIN_ITEM_VARIABLE & field->flags)) + continue; + + for (n = 0; n < count; n++) + mhl3_mt_event(hid, field, &field->usage[n], + field->value[n]); + } + + if (td->num_received >= td->num_expected) + mt_sync_frame(td, report->field[0]->hidinput->input); +} + +static void mt_touch_input_configured(struct hid_device *hdev, + struct hid_input *hi) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_class *cls = &td->mtclass; + struct input_dev *input = hi->input; + + if (!td->maxcontacts) + td->maxcontacts = MT_DEFAULT_MAXCONTACT; + + mt_post_parse(td); + if (td->serial_maybe) + mt_post_parse_default_settings(td); + + if (cls->is_indirect) + td->mt_flags |= INPUT_MT_POINTER; + + if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + td->mt_flags |= INPUT_MT_DROP_UNUSED; + + input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + + td->mt_flags = 0; +} + +void mt_report(struct hid_device *hid, struct hid_report *report) +{ + struct mt_device *td = hid_get_drvdata(hid); + + MHL3_HID_DBG_INFO( + "%s mt_report welcome %x %x\n", __func__, + (int)(hid->claimed & HID_CLAIMED_INPUT), + (int)(report->id == td->mt_report_id)); + + if (!(hid->claimed & HID_CLAIMED_INPUT)) + return; + + if (report->id == td->mt_report_id) + mt_touch_report(hid, report); +} +#endif + +static void mt_set_input_mode(struct hid_device *hdev) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct hid_report *r; + struct hid_report_enum *re; + + if (td->inputmode < 0) + return; + + re = &(hdev->report_enum[HID_FEATURE_REPORT]); + r = re->report_id_hash[td->inputmode]; +#if (LINUX_KERNEL_VER >= 311) + if (r) { + r->field[0]->value[td->inputmode_index] = 0x02; +/* hid_hw_request(hdev, r, HID_REQ_SET_REPORT); */ + } +#else + if (r) { + r->field[0]->value[0] = 0x02; +/* TODO: mhl3_hid_submit_report(hdev, r, USB_DIR_OUT); */ + } +#endif +} + +#if (LINUX_KERNEL_VER >= 311) +static void mt_set_maxcontacts(struct hid_device *hdev) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct hid_report *r; + struct hid_report_enum *re; + int fieldmax, max; + + if (td->maxcontact_report_id < 0) + return; + + if (!td->mtclass.maxcontacts) + return; + + re = &hdev->report_enum[HID_FEATURE_REPORT]; + r = re->report_id_hash[td->maxcontact_report_id]; + if (r) { + max = td->mtclass.maxcontacts; + fieldmax = r->field[0]->logical_maximum; + max = min(fieldmax, max); + if (r->field[0]->value[0] != max) { + r->field[0]->value[0] = max; + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + } + } +} +static void mt_post_parse_default_settings(struct mt_device *td) +{ + __s32 quirks = td->mtclass.quirks; + + /* unknown serial device needs special quirks */ + if (td->touches_by_report == 1) { + quirks |= MT_QUIRK_ALWAYS_VALID; + quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; + quirks &= ~MT_QUIRK_VALID_IS_INRANGE; + quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; + quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; + } + + td->mtclass.quirks = quirks; +} + +static void mt_post_parse(struct mt_device *td) +{ + struct mt_fields *f = td->fields; + struct mt_class *cls = &td->mtclass; + + if (td->touches_by_report > 0) { + int field_count_per_touch = f->length / td->touches_by_report; + td->last_slot_field = f->usages[field_count_per_touch - 1]; + } + + if (td->cc_index < 0) + cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; +} + +void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) +{ + struct mt_device *td = hid_get_drvdata(hdev); + char *name = kstrdup(hdev->name, GFP_KERNEL); + + MHL3_HID_DBG_INFO( + "%s mt_input_configured welcome\n", __func__); + + if (name) + hi->input->name = name; + + if (hi->report->id == td->mt_report_id) + mt_touch_input_configured(hdev, hi); +} +#endif + +static const struct hid_device_id mhl3_mt_devices[] = { + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_SILICONIMAGE, + MHL_PRODUCT_ID_SILICONIMAGE_9394) }, + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_SILICONIMAGE, + MHL_PRODUCT_ID_SILICONIMAGE_9679) }, + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_SILICONIMAGE, + MHL_PRODUCT_ID_SILICONIMAGE_TEST) }, + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, + MHL_PRODUCT_ID_SILICONIMAGE_9394) }, + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, + MHL_PRODUCT_ID_SILICONIMAGE_9679) }, + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(PCI_VENDOR_ID_SILICONIMAGE, + MHL_PRODUCT_ID_SILICONIMAGE_TEST) }, + { .driver_data = MT_CLS_DEFAULT, MT_USB_DEVICE(0x0457, 0x10A8) }, + { .driver_data = MT_CLS_DEFAULT, MT_USB_DEVICE(0x2383, 0x0065) }, + { .driver_data = MT_CLS_DEFAULT, MT_USB_DEVICE(0x1013, 0x1030) }, + { .driver_data = MT_CLS_DEFAULT, MT_USB_DEVICE(0x04d8, 0xf723) }, + { .driver_data = MT_CLS_DEFAULT, MT_USB_DEVICE(0x1b96, 0x0007) }, + + /* 3M panels */ + { .driver_data = MT_CLS_3M, + HID_USB_DEVICE(USB_VENDOR_ID_3M, + USB_DEVICE_ID_3M1968) }, + { .driver_data = MT_CLS_3M, + HID_USB_DEVICE(USB_VENDOR_ID_3M, + USB_DEVICE_ID_3M2256) }, + + /* ActionStar panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, + USB_DEVICE_ID_ACTIONSTAR_1011) }, + + /* Added - Atmel panels */ + { .driver_data = MT_CLS_OCULAR, + HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, + USB_DEVICE_ID_ATMEL_MXT_384E) }, + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, 0x214E) }, + + /* Cando panels */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) }, + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) }, + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, + + /* Chunghwa Telecom touch panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, + USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) }, + + /* CVTouch panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_CVTOUCH, + USB_DEVICE_ID_CVTOUCH_SCREEN) }, + + /* Cypress panel */ + { .driver_data = MT_CLS_CYPRESS, + HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, + USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, + + /* Elo TouchSystems IntelliTouch Plus panel */ + { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, + HID_USB_DEVICE(USB_VENDOR_ID_ELO, + USB_DEVICE_ID_ELO_TS2515) }, + + /* GeneralTouch panel */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, + HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, + + /* GoodTouch panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, + USB_DEVICE_ID_GOODTOUCH_000f) }, + + /* Ideacom panel */ + { .driver_data = MT_CLS_SERIAL, + HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, + USB_DEVICE_ID_IDEACOM_IDC6650) }, + + /* Ilitek dual touch panel */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH) }, + + /* IRTOUCH panels */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, + HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, + USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, + + /* LG Display panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_LG, + USB_DEVICE_ID_LG_MULTITOUCH) }, + + /* Lumio panels */ + { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, + HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, + USB_DEVICE_ID_CRYSTALTOUCH) }, + { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, + HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, + USB_DEVICE_ID_CRYSTALTOUCH_DUAL) }, + + /* MosArt panels */ + { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, + HID_USB_DEVICE(USB_VENDOR_ID_ASUS, + USB_DEVICE_ID_ASUS_T91MT)}, + { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, + HID_USB_DEVICE(USB_VENDOR_ID_ASUS, + USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) }, + { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, + HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, + USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) }, + + /* PenMount panels */ + { .driver_data = MT_CLS_CONFIDENCE, + HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, + USB_DEVICE_ID_PENMOUNT_PCI) }, + + /* PixCir-based panels */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, + HID_USB_DEVICE(USB_VENDOR_ID_HANVON, + USB_DEVICE_ID_HANVON_MULTITOUCH) }, + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, + HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, + + /* Stantum panels */ + { .driver_data = MT_CLS_CONFIDENCE, + HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, + USB_DEVICE_ID_MTP)}, + { .driver_data = MT_CLS_CONFIDENCE, + HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, + USB_DEVICE_ID_MTP_STM)}, + { .driver_data = MT_CLS_CONFIDENCE, + HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, + USB_DEVICE_ID_MTP_SITRONIX)}, + + /* Touch International panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_TOUCH_INTL, + USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH) }, + + /* Unitec panels */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, + USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) }, + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, + USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) }, + /* XAT */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_XAT, + USB_DEVICE_ID_XAT_CSR) }, + +#if (LINUX_KERNEL_VER >= 311) + { .driver_data = MT_CLS_3M, + MT_USB_DEVICE(USB_VENDOR_ID_3M, + USB_DEVICE_ID_3M3266) }, + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_ATMEL, + USB_DEVICE_ID_ATMEL_MULTITOUCH) }, + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_ATMEL, + USB_DEVICE_ID_ATMEL_MXT_DIGITIZER) }, + + /* Baanto multitouch devices */ + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_BAANTO, + USB_DEVICE_ID_BAANTO_MT_190W2) }, + + /* Data Modul easyMaxTouch */ + { .driver_data = MT_CLS_DEFAULT, + MT_USB_DEVICE(USB_VENDOR_ID_DATA_MODUL, + USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH) }, + + /* eGalax devices (resistive) */ + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, + + /* eGalax devices (capacitive) */ + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7) }, + { .driver_data = MT_CLS_EGALAX_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, + + /* Flatfrog Panels */ + { .driver_data = MT_CLS_FLATFROG, + MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG, + USB_DEVICE_ID_MULTITOUCH_3200) }, + + { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, + MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) }, + + /* Gametel game controller */ + { .driver_data = MT_CLS_NSMU, + MT_BT_DEVICE(USB_VENDOR_ID_FRUCTEL, + USB_DEVICE_ID_GAMETEL_MT_MODE) }, + + /* Hanvon panels */ + { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, + MT_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, + USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, + + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM, + USB_DEVICE_ID_IDEACOM_IDC6651) }, + + /* Nexio panels */ + { .driver_data = MT_CLS_DEFAULT, + MT_USB_DEVICE(USB_VENDOR_ID_NEXIO, + USB_DEVICE_ID_NEXIO_MULTITOUCH_420)}, + + /* Panasonic panels */ + { .driver_data = MT_CLS_PANASONIC, + MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, + USB_DEVICE_ID_PANABOARD_UBT780) }, + { .driver_data = MT_CLS_PANASONIC, + MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, + USB_DEVICE_ID_PANABOARD_UBT880) }, + + /* Novatek Panel */ + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_NOVATEK, + USB_DEVICE_ID_NOVATEK_PCT) }, + + /* PixArt optical touch screen */ + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + MT_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) }, + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + MT_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) }, + { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, + MT_USB_DEVICE(USB_VENDOR_ID_PIXART, + USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) }, + + /* Quanta-based panels */ + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) }, + { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, + MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) }, + + /* TopSeed panels */ + { .driver_data = MT_CLS_TOPSEED, + MT_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, + USB_DEVICE_ID_TOPSEED2_PERIPAD_701) }, + + /* Xiroku */ + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX1) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX1) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR1) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_SPX2) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_MPX2) }, + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, + USB_DEVICE_ID_XIROKU_CSR2) }, + + /* Zytronic panels */ + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_ZYTRONIC, + USB_DEVICE_ID_ZYTRONIC_ZXY100) }, + + /* Generic MT device */ + { HID_DEVICE(HID_BUS_ANY, + HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, + + { .driver_data = MT_CLS_DEFAULT, + MT_USB_DEVICE(USB_VENDOR_ID_DATA_MODUL, + USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH) }, + + { HID_DEVICE(BUS_VIRTUAL, + HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, +#endif + + { } +}; +MODULE_DEVICE_TABLE(hid, mhl3_mt_devices); + +static const struct hid_usage_id mt_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} +}; + +/* If incoming HID device is a multitouch device, this function + * will process it instead of the normal HID connect. + * + * Do not call in an interrupt context. + */ +int mhl3_mt_add(struct mhl3_hid_data *mhid, struct hid_device_id *id) +{ + int ret, i; + struct hid_device *hdev = mhid->hid; + struct mt_device *td; + struct mt_class *mtclass = mhl3_mt_classes; /* MT_CLS_DEFAULT */ + const struct hid_device_id *this_id = NULL; + +#if (LINUX_KERNEL_VER >= 311) + struct hid_input *hi; +#endif + + for (i = 0; mhl3_mt_devices[i].driver_data; i++) { + if ((id->vendor == mhl3_mt_devices[i].vendor) && + (id->product == mhl3_mt_devices[i].product)) { + this_id = &mhl3_mt_devices[i]; + break; + } + } + if (this_id == NULL) { + MHL3_HID_DBG_INFO( + "VID/PID %04X/%04X not found\n", (int)id->vendor, + (int)id->product); + return -ENODEV; + } + + for (i = 0; mhl3_mt_classes[i].name; i++) { + if (this_id->driver_data == mhl3_mt_classes[i].name) { + mtclass = &(mhl3_mt_classes[i]); + break; + } + } + + /* This allows the driver to correctly support devices + * that emit events over several HID messages. + */ + hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; + +#if (LINUX_KERNEL_VER >= 311) + /* + * This allows the driver to handle different input sensors + * that emits events through different reports on the same HID + * device. + */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; +#endif + + td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); + if (!td) { + dev_err(&hdev->dev, "cannot allocate multitouch data\n"); + return -ENOMEM; + } +#if (LINUX_KERNEL_VER >= 311) + td->mtclass = *mtclass; + td->maxcontact_report_id = -1; + td->cc_index = -1; + td->mt_report_id = -1; + td->pen_report_id = -1; +#else + td->mtclass = mtclass; + td->last_mt_collection = -1; +#endif + td->inputmode = -1; + + hid_set_drvdata(hdev, td); + +#if (LINUX_KERNEL_VER >= 311) + td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL); + if (!td->fields) { + dev_err(&hdev->dev, "cannot allocate multitouch fields data\n"); + ret = -ENOMEM; + goto fail; + } + + if ((id->vendor == HID_ANY_ID) && (id->product == HID_ANY_ID)) + td->serial_maybe = true; +#endif + + ret = mhl3_hid_report_desc_parse(mhid); + if (ret != 0) + goto fail; + +/* ret = hid_connect(hdev, HID_CONNECT_DEFAULT); */ + ret = hidinput_connect(hdev, 0); + if (!ret) { + MHL3_HID_DBG_ERR( + "%s hidinput_connect succeeded\n", __func__); + hdev->claimed |= HID_CLAIMED_INPUT; + mhid->flags |= MHL3_HID_CONNECTED; + } else { + MHL3_HID_DBG_ERR( + "%s hidinput_connect FAILED with value %x\n", __func__, + (int)ret); + +#if (LINUX_KERNEL_VER >= 311) + goto hid_fail; +#endif + } + +/* TODO: HIDRAW not supported + if (!hidraw_connect(hdev)) { + MHL3_HID_DBG_ERR( + "%s hidraw_connect succeeded\n", __func__); + hdev->claimed |= HID_CLAIMED_HIDRAW; + mhid->flags |= MHL3_HID_CONNECTED; + } else { + MHL3_HID_DBG_ERR( + "%s hidraw_connect FAILED\n", __func__); + } +*/ + +/* if (ret) + goto fail; +*/ +#if (LINUX_KERNEL_VER >= 311) + mt_set_maxcontacts(hdev); + mt_set_input_mode(hdev); + + kfree(td->fields); + td->fields = NULL; + return 0; + +hid_fail: + list_for_each_entry(hi, &hdev->inputs, list) + mt_free_input_name(hi); +fail: + kfree(td->fields); +#else + td->slots = kzalloc( + td->maxcontacts * sizeof(struct mt_slot), GFP_KERNEL); + if (!td->slots) { + dev_err(&hdev->dev, "cannot allocate multitouch slots\n"); + hid_hw_stop(hdev); + ret = -ENOMEM; + goto fail; + } + mt_set_input_mode(hdev); + return 0; +fail: +#endif + kfree(td); + return ret; +} + +#if 0 +#ifdef CONFIG_PM +static int mt_reset_resume(struct hid_device *hdev) +{ + mt_set_input_mode(hdev); + return 0; +} +#endif + +static void mt_remove(struct hid_device *hdev) +{ + struct mt_device *td = hid_get_drvdata(hdev); + +#if (LINUX_KERNEL_VER >= 311) + struct hid_input *hi; + + list_for_each_entry(hi, &hdev->inputs, list) + mt_free_input_name(hi); + hid_hw_stop(hdev); +#else + hid_hw_stop(hdev); + kfree(td->slots); +#endif + kfree(td); + hid_set_drvdata(hdev, NULL); +} + + +static struct hid_driver mt_driver = { + .name = "hid-multitouch", + .id_table = mhl3_mt_devices, +/* .probe = mt_probe, */ + .remove = mt_remove, + .input_mapping = mhl3_mt_input_mapping, + .input_mapped = mhl3_mt_input_mapped, +#if (LINUX_KERNEL_VER >= 311) + .input_configured = mt_input_configured, +#endif + .feature_mapping = mt_feature_mapping, + .usage_table = mt_grabbed_usages, +/* .event = mt_event, */ +#if (LINUX_KERNEL_VER >= 311) + .report = mt_report, +#endif +#ifdef CONFIG_PM + .reset_resume = mt_reset_resume, +#if (LINUX_KERNEL_VER >= 311) + .resume = mt_resume, +#endif +#endif +}; + +static int __init mt_init(void) +{ + return hid_register_driver(&mt_driver); +} + +static void __exit mt_exit(void) +{ + hid_unregister_driver(&mt_driver); +} + +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_emsc_hid.c b/drivers/video/fbdev/msm/mhl3/si_emsc_hid.c new file mode 100644 index 000000000000..17d33c99ef54 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_emsc_hid.c @@ -0,0 +1,1531 @@ +/* + * MHL3 HID Tunneling implementation + * + * Copyright (c) 2013-2014 Lee Mulcahy + * 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. + * + * This code is inspired by the "HID over I2C protocol implementation" + * + * Copyright (c) 2012 Benjamin Tissoires + * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France + * Copyright (c) 2012 Red Hat, Inc + * + * and the "USB HID support for Linux" + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2010 Jiri Kosina + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "si_fw_macros.h" +#include "si_app_devcap.h" +#include "si_infoframe.h" +#include "si_edid.h" +#include "si_mhl_defs.h" +#include "si_mhl2_edid_3d_api.h" +#include "si_8620_internal_api.h" +#include "si_mhl_tx_hw_drv_api.h" +#ifdef MEDIA_DATA_TUNNEL_SUPPORT +#include "si_mdt_inputdev.h" +#endif +#include "si_mhl_callback_api.h" +#include "si_8620_drv.h" +#include "mhl_linux_tx.h" +#include "si_8620_regs.h" +#include "mhl_supp.h" +#include "platform.h" +#include "si_emsc_hid.h" + +#define EMSC_RCV_MSG_START 0 /* First fragment of message. */ +#define EMSC_RCV_MSG_NEXT 1 /* Subsequent fragment of message. */ + +void dump_array(int level, char *ptitle, uint8_t *pdata, int count) +{ + int i, buf_offset; + int bufsize = 128; + char *buf; + + if (level > debug_level) + return; + + if (count > 1) { + bufsize += count * 3; + bufsize += ((count / 16) + 1) * 8; + } + + buf = kmalloc(bufsize, GFP_KERNEL); + if (!buf) + return; + + buf_offset = 0; + if (ptitle) + buf_offset = scnprintf(&buf[0], bufsize, + "%s (%d bytes):", ptitle, count); + for (i = 0; i < count; i++) { + if ((i & 0x0F) == 0) + buf_offset += scnprintf(&buf[buf_offset], + bufsize - buf_offset, "\n%04X: ", i); + buf_offset += scnprintf(&buf[buf_offset], + bufsize - buf_offset, "%02X ", pdata[i]); + } + buf_offset += scnprintf(&buf[buf_offset], bufsize - buf_offset, "\n"); + print_formatted_debug_msg(NULL, NULL, -1, buf); + kfree(buf); +} + +struct cbus_req *hid_host_role_request_done(struct mhl_dev_context *mdev, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} + +/* + * Send Host role request or relinquish it. This should be called with + * MHL_RHID_REQUEST_HOST for a source during MHL connection and when the + * want_host flag is set (after the sink has relinquished the Host role that + * we let them have by sending MHL_RHID_RELIQUISH_HOST). + */ +void mhl_tx_hid_host_role_request(struct mhl_dev_context *context, int request) +{ + MHL3_HID_DBG_INFO("RHID: Sending HID Host role %s message\n", + (request == MHL_RHID_REQUEST_HOST) ? + "REQUEST" : "RELINQUISH"); + /* During a request, we are in limbo */ + context->mhl_ghid.is_host = false; + context->mhl_ghid.is_device = false; + context->mhl_ghid.want_host = false; + + si_mhl_tx_send_msc_msg(context, MHL_MSC_MSG_RHID, request, + hid_host_role_request_done); +} + +struct cbus_req *rhidk_done(struct mhl_dev_context *mdev, + struct cbus_req *req, uint8_t data1) +{ + MHL_TX_DBG_ERR("\n") + return req; +} + +/* + * Handle host-device negotiation request messages received from peer. + * + * A sink requesting Host role is typically just trying to determine + * if the source has already requested the Host role, which is required + * of the sink before starting the Device role (14.3.1.1). + * As a source, we should have sent a host role request to the sink + * before this (dev_context->mhl_ghid.is_host == true), so host + * requests should be refused. + * + * When the sink relinquishes the Host role (assuming we let the sink have + * it for some reason), we immediately want it back. + */ +void mhl_tx_hid_host_negotiation(struct mhl_dev_context *mdev) +{ + uint8_t rhidk_status = MHL_RHID_NO_ERR; + + if (mdev->msc_msg_sub_command == MHL_MSC_MSG_RHID) { + if (mdev->msc_msg_data == MHL_RHID_REQUEST_HOST) { + if (mdev->mhl_ghid.is_host) + rhidk_status = MHL_RHID_DENY; + } else if (mdev->msc_msg_data == MHL_RHID_RELINQUISH_HOST) { + mdev->mhl_ghid.want_host = true; + } else { + rhidk_status = MHL_RHID_INVALID; + } + + MHL3_HID_DBG_INFO( + "RHID: Received HID Host role %s result: %d\n", + (mdev->msc_msg_data == MHL_RHID_REQUEST_HOST) ? + "REQUEST" : "RELINQUISH", rhidk_status); + + /* Always RHIDK to the peer */ + si_mhl_tx_send_msc_msg(mdev, MHL_MSC_MSG_RHIDK, rhidk_status, + rhidk_done); + + } else if (mdev->msc_msg_sub_command == MHL_MSC_MSG_RHIDK) { + if (mdev->msc_msg_data == MHL_RHID_NO_ERR) { + if (mdev->msc_msg_last_data == MHL_RHID_REQUEST_HOST) { + mdev->mhl_ghid.is_host = true; + mdev->mhl_ghid.is_device = false; + MHL3_HID_DBG_INFO("HID HOST role granted\n"); + } else { + mdev->mhl_ghid.is_host = false; + mdev->mhl_ghid.is_device = true; + MHL3_HID_DBG_INFO("HID DEVICE role granted\n"); + } + } else if (mdev->msc_msg_last_data == MHL_RHID_REQUEST_HOST) { + mdev->mhl_ghid.is_host = false; + mdev->mhl_ghid.is_device = true; + MHL3_HID_DBG_INFO("HID HOST role DENIED\n"); + } + } +} + +/* + * Add a HID message to the eMSC output queue using one or more + * eMSC BLOCK commands. + */ +static int si_mhl_tx_emsc_add_hid_message(struct mhl_dev_context *mdev, + uint8_t hb0, uint8_t hb1, uint8_t *msg, int msg_len) +{ + int i, cmd_size, fragment_count, msg_index, index; + uint8_t *payload; + uint8_t payload_size; + uint16_t accum; + uint16_t *pchksum; + bool first_fragment; + + /* If message exceeds one (empty) BLOCK command buffer, we must + * break it into fragments. Since the first fragment will be + * the full command buffer size, the current request (if any) will be + * sent before starting to add this one. + */ + i = msg_len + HID_MSG_HEADER_LEN + HID_MSG_CHKSUM_LEN; + fragment_count = i / HID_FRAG_LEN_MAX; + if ((fragment_count * HID_FRAG_LEN_MAX) != i) + fragment_count++; + + /* This time don't include the standard header in the message length. */ + cmd_size = + HID_BURST_ID_LEN + + HID_FRAG_HEADER_LEN + + HID_MSG_HEADER_LEN + + HID_MSG_CHKSUM_LEN + + msg_len; + + first_fragment = true; + msg_index = 0; + index = 0; + accum = 0; + + /* + * TODO: Need to make sure there will be enough buffers + * for the fragments. + */ + index = 0; + while (fragment_count > 0) { + payload_size = (cmd_size > EMSC_BLK_CMD_MAX_LEN) ? + EMSC_BLK_CMD_MAX_LEN : cmd_size; + + /* TODO: Need up to 17 buffers (do we have them?) */ + payload = (uint8_t *)si_mhl_tx_get_sub_payload_buffer( + mdev, payload_size); + if (payload == NULL) { + MHL3_HID_DBG_ERR( + "%ssi_mhl_tx_get_sub_payload_buffer failed%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + /* + * TODO: Should be handled with an error code and + * exit, but need to clean up any buffers that may + * have been successfully allocated. + */ + } else { + payload[index++] = (uint8_t)(burst_id_HID_PAYLOAD >> 8); + payload[index++] = (uint8_t)(burst_id_HID_PAYLOAD); + payload[index++] = payload_size - HID_BURST_ID_LEN; + payload[index++] = fragment_count - 1; + payload_size -= + (HID_BURST_ID_LEN + HID_FRAG_HEADER_LEN); + if (fragment_count == 1) + payload_size -= HID_MSG_CHKSUM_LEN; + + if (first_fragment) { + first_fragment = false; + payload[index++] = hb0; + payload[index++] = hb1; + payload_size -= HID_MSG_HEADER_LEN; + + accum = ((((uint16_t)hb1) << 8) | hb0); + pchksum = (uint16_t *)&msg[0]; + for (i = 0; i < (msg_len / 2); i++) + accum += pchksum[i]; + if (msg_len & 0x01) + accum += ((uint16_t)msg[msg_len - 1]); + accum += (msg_len + HID_MSG_HEADER_LEN); + } + memcpy(&payload[index], &msg[msg_index], payload_size); + msg_index += payload_size; + index += payload_size; + + if (fragment_count == 1) { + payload[index++] = (uint8_t)accum; + payload[index++] = (uint8_t)(accum >> 8); + } + } + fragment_count--; + } + return 0; +} + +/* + * Build a HID tunneling message and send it. + * Returns non-zero if an error occurred, such as the device was + * disconnected. + * Always use the CTRL channel to send messages from the HOST. + * + * This function should be called wrapped in an isr_lock semaphore pair UNLESS + * it is being called from the Titan interrupt handler. + */ +static int send_hid_msg(struct mhl3_hid_data *mhid, int outlen, bool want_ack) +{ + struct mhl_dev_context *mdev = mhid->mdev; + uint8_t hb0, hb1; + int ret; + + hb0 = mhid->id << 4; + + if (want_ack) { + hb1 = mhid->msg_count[0] | EMSC_HID_HB1_ACK; + mhid->msg_count[0] = + (mhid->msg_count[0] + 1) & EMSC_HID_HB1_MSG_CNT_FLD; + } + hb1 = mhid->msg_count[0] | (want_ack ? EMSC_HID_HB1_ACK : 0x00); + mhid->msg_count[0] = + (mhid->msg_count[0] + 1) & EMSC_HID_HB1_MSG_CNT_FLD; + ret = si_mhl_tx_emsc_add_hid_message(mdev, hb0, hb1, + mhid->out_data, outlen); + if (ret == 0) + si_mhl_tx_push_block_transactions(mdev); + + return ret; +} + +/* + * Send an MHL3 HID Command and wait for a response. + * called only from a WORK QUEUE function; Do NOT call from + * the Titan interrupt context. + * Returns negative if an error occurred, such as the device was + * disconnected, otherwise returns the number of bytes + * received. + */ +static int send_hid_wait(struct mhl3_hid_data *mhid, + int outlen, uint8_t *pin, int inlen, bool want_ack) +{ + struct mhl_dev_context *mdev = mhid->mdev; + int count, ret; + + /* + * Take a hold of the wait lock. It will be released + * when the response is received. This down call will be + * released by an up call in the hid_message_processor() + * function when the response message has been received. Not + * conventional, but until I think of a better way, + * this is it. + */ + if (down_timeout(&mhid->data_wait_lock, 1*HZ)) { + MHL3_HID_DBG_ERR("Could not acquire data_wait lock !!!\n"); + return -ENODEV; + } + MHL3_HID_DBG_INFO("Acquired data_wait_lock\n"); + + /* + * Send the message when the Titan ISR is not active so + * that we don't deadlock with eMsc block buffer allocation. + */ + if (down_interruptible(&mdev->isr_lock)) { + MHL3_HID_DBG_ERR( + "Could not acquire isr_lock for HID work queue\n"); + ret = -ERESTARTSYS; + goto done_release; + } + + /* If canceling, get out. */ + if (mhid->flags & HID_FLAGS_WQ_CANCEL) { + ret = -ENODEV; + up(&mdev->isr_lock); + goto done_release; + } + + ret = send_hid_msg(mhid, outlen, want_ack); + up(&mdev->isr_lock); + if (ret == 0) { + /* Wait until a message is ready (when the driver unblocks). */ + if (down_timeout(&mhid->data_wait_lock, 15*HZ)) { + MHL3_HID_DBG_WARN("Timed out waiting for HID msg!\n"); + ret = -EBUSY; + + /* + * As odd as it may seem, we must release the semaphore + * that we were waiting for because something + * apparently has gone wrong with the communications + * protocol and we need to start over. + */ + goto done_release; + } + + /* Intercept HID_ACK{NO_DEV} messages. */ + if ((mhid->in_data[0] == MHL3_HID_ACK) && + (mhid->in_data[1] == HID_ACK_NODEV)) { + ret = -ENODEV; + goto done_release; + } + + /* Message has been placed in our input buffer. */ + count = (mhid->in_data_length > inlen) ? + inlen : mhid->in_data_length; + memcpy(pin, mhid->in_data, count); + ret = count; + } + +done_release: + up(&mhid->data_wait_lock); + return ret; +} + +/* + * Message protocol level ACK packet, as opposed to the HID_ACK message. + */ +static int send_ack_packet(struct mhl_dev_context *mdev, + uint8_t hb0, uint8_t hb1) +{ + uint8_t *payload; + + payload = (uint8_t *)si_mhl_tx_get_sub_payload_buffer( + mdev, HID_BURST_ID_LEN + HID_ACK_PACKET_LEN); + if (payload == NULL) { + MHL3_HID_DBG_ERR( + "%ssi_mhl_tx_get_sub_payload_buffer failed%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + /* + * TODO: Should be handled with an error code and + * exit, but need to clean up any buffers that may + * have been successfully allocated. + */ + } else { + payload[0] = (uint8_t)(burst_id_HID_PAYLOAD >> 8); + payload[1] = (uint8_t)(burst_id_HID_PAYLOAD); + payload[2] = HID_ACK_PACKET_LEN; + payload[3] = hb1 | 0x80; + payload[4] = hb0; + } + return 0; +} + +/* + * Send a HID_ACK message with the passed error code from the interrupt + * context without using the mhid device structure (in case it's not there). + */ +static int mhl3_int_send_ack(struct mhl_dev_context *mdev, + int reason, uint8_t hb0) +{ + int status; + uint8_t out_data[2]; + + MHL3_HID_DBG_WARN("HID_ACK reason code: %02X\n", reason); + out_data[0] = MHL3_HID_ACK; + out_data[1] = (reason < 0) ? HID_ACK_NODEV : reason; + + status = si_mhl_tx_emsc_add_hid_message(mdev, hb0, 0, out_data, 2); + if (status < 0) + MHL3_HID_DBG_ERR("MHID: Failed to send HID_ACK to device.\n"); + return status; +} + +/* + * Send a HID_ACK message with the passed error code. + */ +static int mhl3_send_ack(struct mhl3_hid_data *mhid, uint8_t reason) +{ + int status; + struct mhl_dev_context *mdev = mhid->mdev; + + if (mhid == 0) + return -ENODEV; + + MHL3_HID_DBG_WARN("%s - HID_ACK reason code: %02X\n", __func__, reason); + MHL3_HID_DBG_ERR("mhid->mdev: %p\n", mhid->mdev); + mhid->out_data[0] = MHL3_HID_ACK; + mhid->out_data[1] = reason; + + /* + * Send the message when the Titan ISR is not active so + * that we don't deadlock with eMsc block buffer allocation. + */ + if (down_interruptible(&mdev->isr_lock)) { + MHL3_HID_DBG_ERR("Could not acquire isr_lock for HID work\n"); + return -ERESTARTSYS; + } + status = send_hid_msg(mhid, 2, false); + up(&mdev->isr_lock); + if (status < 0) + MHL3_HID_DBG_ERR("Failed to send a HID_ACK to device.\n"); + return status; +} + +/* + * Called from mhl3_hid_get_raw_report() and mhl3_hid_init_report() + * to get report data from the device. + * + * It CANNOT be called from the Titan driver interrupt context, because + * it blocks until it gets a response FROM the Titan interrupt. + */ +static int mhl3_hid_get_report(struct mhl3_hid_data *mhid, u8 report_type, + u8 report_id, unsigned char *pin, int inlen) +{ + int ret = 0; + + /* Build MHL3_GET_REPORT message and add it to the out queue. */ + mhid->out_data[0] = MHL3_GET_REPORT; + mhid->out_data[1] = report_type; + mhid->out_data[2] = report_id; + ret = send_hid_wait(mhid, 3, pin, inlen, false); + if (ret < 0) + MHL3_HID_DBG_ERR("Failed to retrieve report from device.\n"); + + return ret; +} + +/* + * Called from mhl3_hid_output_raw_report() to send report data to + * the device. + */ +static int mhl3_hid_set_report(struct mhl3_hid_data *mhid, u8 report_type, + u8 report_id, unsigned char *pout, size_t outlen) +{ + struct mhl_dev_context *mdev = mhid->mdev; + int ret = 0; + + mhid->out_data[0] = MHL3_SET_REPORT; + mhid->out_data[1] = report_type; + mhid->out_data[2] = report_id; + memcpy(&mhid->out_data[3], pout, outlen); + + /* TODO: Need to make sure that this function is never called from + * the Titan ISR (through linkage from a HID function called + * from a HID report response or some such). Just needs some + * research.... + */ + + /* + * Send the message when the Titan ISR is not active so + * that we don't deadlock with eMsc block buffer allocation. + */ + if (down_interruptible(&mdev->isr_lock)) { + MHL3_HID_DBG_ERR("Could not acquire isr_lock for HID work\n"); + return -ERESTARTSYS; + } + ret = send_hid_msg(mhid, outlen + 3, false); + up(&mdev->isr_lock); + if (ret) { + MHL3_HID_DBG_ERR("Failed to set a report to device.\n"); + return ret; + } + + return outlen + 3; +} + +/* + * TODO: Doesn't do anything yet + */ +static int mhl3_hid_set_power(struct mhl_dev_context *context, int power_state) +{ + int ret = 0; + + return ret; +} + +static int mhl3_hid_alloc_buffers(struct mhl3_hid_data *mhid, + size_t report_size) +{ + mhid->in_report_buf = kzalloc(report_size, GFP_KERNEL); + if (mhid->in_report_buf == NULL) { + kfree(mhid->hdesc); + mhid->hdesc = NULL; + kfree(mhid->in_report_buf); + mhid->in_report_buf = NULL; + return -ENOMEM; + } + mhid->bufsize = report_size; + + return 0; +} + +/* + * Called from the HID driver to obtain raw report data from the + * device. + * + * It is not called from an interrupt context, so it is OK to block + * until a reply is given. + */ +#if (LINUX_KERNEL_VER < 315) +static int mhl3_hid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, + size_t count, unsigned char report_type) +{ + struct mhl3_hid_data *mhid = hid->driver_data; + int ret; + + if (report_type == HID_OUTPUT_REPORT) + return -EINVAL; + + ret = mhl3_hid_get_report(mhid, + report_type == HID_FEATURE_REPORT ? 0x03 : 0x01, + report_number, buf, count); + return ret; +} + +/* + * Called from the HID driver to send report data to the device. + */ +static int mhl3_hid_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t count, unsigned char report_type) +{ + struct mhl3_hid_data *mhid = hid->driver_data; + int report_id = buf[0]; + int ret; + + if (report_type == HID_INPUT_REPORT) + return -EINVAL; + + if (report_id) { + buf++; + count--; + } + + ret = mhl3_hid_set_report(mhid, + report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, + report_id, buf, count); + + if (report_id && ret >= 0) + ret++; /* add report_id to the number of transfered bytes */ + + return ret; +} +#endif + +static int mhl3_hid_get_report_length(struct hid_report *report) +{ + return ((report->size - 1) >> 3) + 1 + + report->device->report_enum[report->type].numbered + 2; +} + +/* + * Traverse the supplied list of reports and find the longest + */ +static void mhl3_hid_find_max_report(struct hid_device *hid, unsigned int type, + unsigned int *max) +{ + struct hid_report *report; + unsigned int size; + + /* + * We should not rely on wMaxInputLength, as some devices may set + * it to a wrong length. + */ + list_for_each_entry(report, &hid->report_enum[type].report_list, list) { + size = mhl3_hid_get_report_length(report); + if (*max < size) + *max = size; + } +} + +/* + * + */ +#if 0 +static void mhl3_hid_init_report(struct hid_report *report, u8 *buffer, + size_t bufsize) +{ + struct hid_device *hid = report->device; + struct mhl3_hid_data *mhid = hid->driver_data; + unsigned int size, ret_size; + + size = mhl3_hid_get_report_length(report); + if (mhl3_hid_get_report(mhid, + report->type == HID_FEATURE_REPORT ? 0x03 : 0x01, + report->id, buffer, size)) + return; + + ret_size = buffer[0] | (buffer[1] << 8); + + if (ret_size != size) { + MHL3_HID_DBG_ERR("Error in %s size:%d / ret_size:%d\n", + __func__, size, ret_size); + return; + } + + /* + * hid->driver_lock is held as we are in probe function, + * we just need to setup the input fields, so using + * hid_report_raw_event is safe. + */ + hid_report_raw_event(hid, report->type, buffer + 2, size - 2, 1); +} +#endif + +#if 0 +/* + * Initialize all reports. This gets the current value of all + * input/feature reports for the device so that the HID-core can keep + * them in internal structures. The structure is updated as further + * device reports occur. + */ +static void mhl3_hid_init_reports(struct hid_device *hid) +{ + struct mhl3_hid_data *mhid = hid->driver_data; + struct hid_report *report; + + MHL3_HID_DBG_INFO("%s\n", __func__); + list_for_each_entry(report, + &hid->report_enum[HID_INPUT_REPORT].report_list, list) + mhl3_hid_init_report( + report, mhid->in_report_buf, mhid->bufsize); + + list_for_each_entry(report, + &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + mhl3_hid_init_report( + report, mhid->in_report_buf, mhid->bufsize); +} +#endif + +/* + * TODO: Doesn't do anything yet except manage the open count + */ +static int mhl3_hid_open(struct hid_device *hid) +{ + struct mhl_dev_context *mdev = 0; + struct mhl3_hid_data *mhid = 0; + int ret = 0; + + if (hid) + mhid = hid->driver_data; + if (mhid == 0) + return 0; + mdev = mhid->mdev; + + mutex_lock(&mhl3_hid_open_mutex); + if (!hid->open++) { + ret = mhl3_hid_set_power(mdev, MHL3_HID_PWR_ON); + if (ret) { + hid->open--; + goto done; + } + mhid->flags |= MHL3_HID_STARTED; + } +done: + mutex_unlock(&mhl3_hid_open_mutex); + return ret; +} + +/* + * TODO: Doesn't do anything yet except manage the open count + */ +static void mhl3_hid_close(struct hid_device *hid) +{ + struct mhl_dev_context *mdev = 0; + struct mhl3_hid_data *mhid = 0; + + if (hid) + mhid = hid->driver_data; + if (mhid == 0) + return; + mdev = mhid->mdev; + + /* + * Protecting hid->open to make sure we don't restart + * data acquisition due to a resumption we no longer + * care about + */ + mutex_lock(&mhl3_hid_open_mutex); + if (!--hid->open) { + mhid->flags &= ~MHL3_HID_STARTED; + mhl3_hid_set_power(mdev, MHL3_HID_PWR_SLEEP); + } + mutex_unlock(&mhl3_hid_open_mutex); +} + +/* + * TODO: Doesn't do anything yet + */ +static int mhl3_hid_power(struct hid_device *hid, int lvl) +{ + int ret = 0; + + MHL3_HID_DBG_ERR("level: %d\n", lvl); + + switch (lvl) { + case PM_HINT_FULLON: + break; + case PM_HINT_NORMAL: + break; + } + return ret; +} + +static struct hid_ll_driver mhl3_hid_ll_driver = { + .open = mhl3_hid_open, + .close = mhl3_hid_close, + .power = mhl3_hid_power, +}; + +/* + * Use the MHL3 GET_MHID_DSCRPT message to request the HID Device + * Descriptor from the device on the control channel. The device + * should reply with an MHL3_MHID_DSCRPT message + * + * This function MUST be called from a work queue function. + * + */ +static int mhid_fetch_hid_descriptor(struct mhl3_hid_data *mhid) +{ + struct mhl3_hid_desc *hdesc; + uint8_t *pdesc_raw; + int ret = -ENODEV; + int desc_len, raw_offset; + + /* The actual length of the data will likely not be this much. */ + desc_len = sizeof(struct mhl3_hid_desc) + + sizeof(mhid->desc_product_name) + + sizeof(mhid->desc_mfg_name) + + sizeof(mhid->desc_serial_number); + pdesc_raw = kmalloc(desc_len, GFP_KERNEL); + if (!pdesc_raw) { + MHL3_HID_DBG_ERR("Couldn't allocate raw descriptor memory\n"); + return -ENOMEM; + } + MHL3_HID_DBG_INFO("Fetching the HID descriptor\n"); + + /* + * Build GET_MHID_DSCRPT message and add it to the out queue. + * By setting the LANG_ID field bytes to 0, we allow the + * device to send any language it chooses. + */ + mhid->out_data[0] = MHL3_GET_MHID_DSCRPT; + mhid->out_data[1] = 0; + mhid->out_data[2] = 0; + mhid->opState = OP_STATE_WAIT_MHID_DSCRPT; + ret = send_hid_wait(mhid, 3, pdesc_raw, desc_len, false); + if ((ret < 0) || (mhid->opState == OP_STATE_IDLE)) { + MHL3_HID_DBG_ERR( + "Failed to get MHID descriptor: %d.\n", ret); + goto raw_cleanup; + } + + hdesc = kmalloc(desc_len, GFP_KERNEL); + if (!hdesc) { + MHL3_HID_DBG_ERR("Couldn't allocate hdesc descriptor memory\n"); + ret = -ENOMEM; + goto raw_cleanup; + } + + /* dump_array(DBG_MSG_LEVEL_INFO, "HID descriptor", pdesc_raw, ret); */ + + /* Get the fixed length part and verify. */ + memcpy(hdesc, pdesc_raw, sizeof(struct mhl3_hid_desc)); + raw_offset = sizeof(struct mhl3_hid_desc); + + /* Do some simple checks. */ + if (hdesc->bMHL3HIDmessageID != 0x05) { + MHL3_HID_DBG_ERR("Invalid MHID_DSCRPT data\n"); + ret = -EINVAL; + goto hdesc_cleanup; + } + + if (ret < (sizeof(struct mhl3_hid_desc) + + hdesc->bProductNameSize + + hdesc->bManufacturerNameSize + + hdesc->bSerialNumberSize)) { + + memcpy(mhid->desc_product_name, + &pdesc_raw[raw_offset], hdesc->bProductNameSize); + raw_offset += hdesc->bProductNameSize; + memcpy(mhid->desc_mfg_name, + &pdesc_raw[raw_offset], hdesc->bManufacturerNameSize); + raw_offset += hdesc->bManufacturerNameSize; + memcpy(mhid->desc_serial_number, + &pdesc_raw[raw_offset], hdesc->bSerialNumberSize); + } + + /* + * If this was an existing mhid being updated, free the previous + * descriptor and reassign with the new one. + */ + kfree(mhid->hdesc); + mhid->hdesc = hdesc; + ret = 0; + goto raw_cleanup; + +hdesc_cleanup: + kfree(hdesc); +raw_cleanup: + kfree(pdesc_raw); + return ret; +} + +/* TODO: Eliminated ACPI stuff; i2c_hid_acpi_pdata. etc. */ + +/* + * The users of the hid_device structure don't always check that + * the hid_device structure has a valid hid_driver before trying to access + * its members. Creating an empty structure at least avoids a + * kernel panic. + */ +static struct hid_driver mhl3_hid_driver = { + + .name = "mhl3_hid", +}; + +static struct hid_driver mhl3_mt3_hid_driver = { + + .name = "mhl3_hid-mt", + .event = mhl3_mt_event, + .input_mapping = mhl3_mt_input_mapping, + .input_mapped = mhl3_mt_input_mapped, + .feature_mapping = mt_feature_mapping, +#if (LINUX_KERNEL_VER >= 311) + .input_configured = mt_input_configured, + .report = mt_report, +#endif +}; + + +/* + * Get report descriptors from the device and parse them. + * The Report Descriptor describes the report ID and the type(s) + * of data it contains. During operation, when the device returns + * a report with a specific ReportID, the HID core will know how + * to parse it. + */ +int mhl3_hid_report_desc_parse(struct mhl3_hid_data *mhid) +{ + unsigned int report_len; + int ret; + uint8_t *report_desc_raw = NULL; + struct hid_device *hdev; + + hdev = mhid->hid; + report_desc_raw = kmalloc(HID_MAX_DESCRIPTOR_SIZE, GFP_KERNEL); + if (!report_desc_raw) { + MHL3_HID_DBG_ERR("Allocate raw report descriptor mem failed\n"); + ret = -ENOMEM; + goto err; + } + mhid->out_data[0] = MHL3_GET_REPORT_DSCRPT; + mhid->opState = OP_STATE_WAIT_REPORT_DSCRPT; + ret = send_hid_wait(mhid, 1, report_desc_raw, + HID_MAX_DESCRIPTOR_SIZE, false); + if (ret < 0) { + MHL3_HID_DBG_ERR( + "Failed to get descriptor from device: %d.\n", ret); + goto err; + } + if (mhid->opState == OP_STATE_IDLE) + goto done; + if (report_desc_raw[0] != MHL3_REPORT_DSCRPT) { + MHL3_HID_DBG_ERR( + "Invalid response to MHL3_GET_REPORT_DSCRPT\n"); + ret = -EINVAL; + goto err; + } + report_len = report_desc_raw[2]; + report_len <<= 8; + report_len |= report_desc_raw[1]; + report_len = (report_len < (ret-3)) ? report_len : (ret-3); + ret = hid_parse_report(hdev, &report_desc_raw[3], report_len); + if (ret) + goto err; + +#if (LINUX_KERNEL_VER >= 305) + ret = hid_open_report(hdev); + if (ret) { + MHL3_HID_DBG_ERR("hid_open_report failed\n"); + goto err; + } +#endif + goto done; +err: + MHL3_HID_DBG_ERR("WORK QUEUE mhl3_hid_report_desc_parse() FAIL\n"); +done: + kfree(report_desc_raw); + return ret; +} + +static void mhl3_disconnect_and_destroy_hid_device(struct mhl3_hid_data *mhid) +{ + if (mhid->hid) { + if (mhid->hid->claimed & HID_CLAIMED_INPUT) { + hidinput_disconnect(mhid->hid); + mhid->hid->claimed &= ~HID_CLAIMED_INPUT; + } + hid_destroy_device(mhid->hid); + mhid->hid = 0; + } +} + +/* + * The second part of the mhl3_hid_add() function, implemented as + * a work queue. It is entered at opState == OP_STATE_IDLE + * A failure here deletes the mhid device. + */ +static void mhid_add_work(struct work_struct *workdata) +{ + struct mhl3_hid_data *mhid = + ((struct hid_add_work_struct *)workdata)->mhid; + struct mhl_dev_context *mdev = mhid->mdev; + struct hid_device *hdev; + struct hid_device_id id = {0}; + unsigned int bufsize = HID_MIN_BUFFER_SIZE; + int ret; + + mhid->flags |= HID_FLAGS_WQ_ACTIVE; + MHL3_HID_DBG_ERR("WORK QUEUE function executing\n"); + + mdev->mhl_hid[mhid->id] = mhid; + ret = mhid_fetch_hid_descriptor(mhid); /* Allocates hdesc */ + if ((ret < 0) || (mhid->opState == OP_STATE_IDLE)) + goto mhid_cleanup; + + /* Get a HID device if this is not an update of the existing HID. */ + if (mhid->hid == NULL) { + hdev = hid_allocate_device(); + if (IS_ERR(hdev)) { + ret = PTR_ERR(hdev); + goto mhid_cleanup; + } + + mhid->hid = hdev; + hdev->driver = &mhl3_mt3_hid_driver; + hdev->driver_data = mhid; + id.vendor = mhid->hdesc->wHIDVendorID; + id.product = mhid->hdesc->wHIDProductID; + MHL3_HID_DBG_ERR("Allocated HID\n"); + } + hdev = mhid->hid; + + hdev->ll_driver = &mhl3_hid_ll_driver; +#if (LINUX_KERNEL_VER < 315) + hdev->hid_get_raw_report = mhl3_hid_get_raw_report; + hdev->hid_output_raw_report = mhl3_hid_output_raw_report; +#endif + + hdev->dev.parent = NULL; + hdev->bus = BUS_VIRTUAL; + + hdev->version = mhid->hdesc->wBcdHID; + hdev->vendor = mhid->hdesc->wHIDVendorID; + hdev->product = mhid->hdesc->wHIDProductID; + + snprintf(hdev->name, sizeof(hdev->name), "MHL3 HID %04hX:%04hX", + hdev->vendor, hdev->product); + + /* Check for multitouch device first. */ + ret = mhl3_mt_add(mhid, &id); + if (ret) { + MHL3_HID_DBG_INFO("NOT Multitouch, trying generic HID\n"); + hdev->driver = &mhl3_hid_driver; + ret = mhl3_hid_report_desc_parse(mhid); + } + if ((ret < 0) || (mhid->opState == OP_STATE_IDLE)) + goto mhid_cleanup; + + if ((mhid->flags & MHL3_HID_CONNECTED) == 0) { + if (!hidinput_connect(hdev, 0)) { + MHL3_HID_DBG_INFO( + "%s hidinput_connect succeeded\n", __func__); + hdev->claimed |= HID_CLAIMED_INPUT; + mhid->flags |= MHL3_HID_CONNECTED; + + } else { + MHL3_HID_DBG_ERR( + "%s hidinput_connect FAILED\n", __func__); + goto mhid_cleanup; + } + } + + /* + * Allocate some report buffers and read the initial state of + * the INPUT and FEATURE reports. + */ + mhl3_hid_find_max_report(hdev, HID_INPUT_REPORT, &bufsize); + mhl3_hid_find_max_report(hdev, HID_OUTPUT_REPORT, &bufsize); + mhl3_hid_find_max_report(hdev, HID_FEATURE_REPORT, &bufsize); + if (bufsize > mhid->bufsize) { + kfree(mhid->in_report_buf); + mhid->in_report_buf = NULL; + ret = mhl3_hid_alloc_buffers(mhid, bufsize); + if (ret) + goto mhid_cleanup; + } + +/* if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS)) + mhl3_hid_init_reports(hdev); +*/ + mhid->opState = OP_STATE_CONNECTED; + + MHL3_HID_DBG_ERR("WORK QUEUE function SUCCESS\n"); + mhid->flags &= ~HID_FLAGS_WQ_ACTIVE; + return; + +mhid_cleanup: + + mhl3_send_ack(mhid, HID_ACK_NODEV); + + mhid->flags |= HID_FLAGS_WQ_CANCEL; + MHL3_HID_DBG_ERR("WORK QUEUE function FAIL - mhid: %p\n", mhid); + mhl3_disconnect_and_destroy_hid_device(mhid); + + /* + * Don't destroy the mhid while an interrupt is in progress on the + * off chance that the message we were waiting for came in after the + * timeout. + */ + if (down_interruptible(&mdev->isr_lock)) { + MHL3_HID_DBG_ERR("Could not acquire isr_lock\n"); + return; + } + + kfree(mhid->hdesc); + kfree(mhid->in_report_buf); + kfree(mhid); + mdev->mhl_hid[mhid->id] = 0; + + up(&mdev->isr_lock); + MHL3_HID_DBG_ERR("WORK QUEUE function exit\n"); +} + +/* + * Create and initialize the device control structures and get added to + * the system HID device list. This is the equivalent to the probe + * function of normal HID-type drivers, but it is called when the + * MHL transport receives the eMSC Block transfer HID Tunneling request + * message MHL3_DSCRPT_UPDATE + * + * Called from a Titan interrupt handler. All events in the MHL driver + * are handled in interrupt handlers, so the real work here is performed on + * a work queue so the function can wait for responses from the driver. + */ +static int mhid_add(struct mhl_dev_context *mdev, int dev_id) +{ + struct mhl3_hid_data *mhid; + int status; + + MHL3_HID_DBG_ERR("Adding device %d\n", dev_id); + + if (dev_id >= MAX_HID_MESSAGE_CHANNELS) + return -EINVAL; + + mhid = kzalloc(sizeof(struct mhl3_hid_data), GFP_KERNEL); + if (!mhid) + return -ENOMEM; + + sema_init(&mhid->data_wait_lock, 1); + + mhid->mhl3_work.mhid = mhid; + mhid->id = dev_id; + mhid->mdev = mdev; + + INIT_WORK((struct work_struct *)&mhid->mhl3_work, mhid_add_work); + status = queue_work( + mdev->hid_work_queue, + (struct work_struct *)&mhid->mhl3_work); + return 0; +} + +/* + * This is the reverse of the mhl3_hid_add() function. + * The device_id parameter is for when we support multiple HID devices. + */ +static int mhid_remove(struct mhl_dev_context *mdev, int dev_id) +{ + struct mhl3_hid_data *mhid; + + if (dev_id >= MAX_HID_MESSAGE_CHANNELS) + return -EINVAL; + + mhid = mdev->mhl_hid[dev_id]; + if (mhid == NULL) + return 0; + /* + * If work queue is not active, we need to free the mhid + * here, otherwise let the WQ do it. + */ + if ((mhid->flags & HID_FLAGS_WQ_ACTIVE) == 0) { + mhl3_disconnect_and_destroy_hid_device(mhid); + kfree(mhid->hdesc); + kfree(mhid->in_report_buf); + kfree(mhid); + mdev->mhl_hid[dev_id] = 0; + } else { + mhid->flags |= HID_FLAGS_WQ_CANCEL; + + /* Release the data wait to let the work queue finish. */ + if (down_trylock(&mhid->data_wait_lock)) + MHL3_HID_DBG_ERR("Waiting for data when HID removed\n"); + up(&mhid->data_wait_lock); + } + return 0; +} + +/* + * Remove all MHID devices. + */ +void mhl3_hid_remove_all(struct mhl_dev_context *mdev) +{ + int dev_id; + + for (dev_id = 0; dev_id < MAX_HID_MESSAGE_CHANNELS; dev_id++) { + if (mdev->mhl_hid[dev_id]) + mhid_remove(mdev, dev_id); + } +} + +/* + * We have received a completed MHL3 HID Tunneling message. Parse the + * header to decide what to do with it. + * Called indirectly from the Titan interrupt handler. + */ +static void hid_message_processor(struct mhl_dev_context *mdev, + uint8_t hb0, uint8_t hb1, uint8_t *pmsg, int length) +{ + struct mhl3_hid_data *mhid; + uint8_t msg_id; + bool want_ack; + bool matched_expected_message; + int opState, dev_id, hid_msg_count, header_len, ret; + int msg_channel; + + dev_id = (hb0 >> 4) & 0x0F; + msg_channel = (hb0 & 0x01); + want_ack = ((hb1 & 0x80) != 0); + hid_msg_count = hb1 & 0x7F; + msg_id = pmsg[0]; + matched_expected_message = false; + + if (msg_id != 1) { + MHL3_HID_DBG_WARN( + "Received message: msg_id: %02X, deviceID: %02X\n", + msg_id, dev_id); + } + + mhid = mdev->mhl_hid[dev_id]; + opState = OP_STATE_IDLE; + if (mhid == 0) { + MHL3_HID_DBG_WARN("Message received with mhid == 0\n"); + if (msg_id != MHL3_DSCRPT_UPDATE) { + MHL3_HID_DBG_ERR( + "Message NOT MHL3_DSCRPT_UPDATE: %02X\n", + msg_id); + mhl3_int_send_ack(mdev, HID_ACK_TIMEOUT, hb0); + return; + } + } else { + if (want_ack) { + if (hid_msg_count != + (mhid->msg_count[msg_channel] + 1)) { + send_ack_packet( + mdev, hb0, + mhid->msg_count[msg_channel]); + return; + } else { + mhid->msg_count[msg_channel] += 1; + } + } + opState = mhid->opState; + } + +/* if (want_ack) + send_ack_packet(mdev, hb0, hb1); */ + + /* + * If a DSCRPT_UPDATE we need to queue a new device add, no matter + * if the device already exists or not. If it already exists, + * destroy it first. + */ + if ((msg_id == MHL3_DSCRPT_UPDATE) && (mhid != 0)) { + opState = OP_STATE_IDLE; + mhid_remove(mdev, dev_id); + } + + switch (opState) { + case OP_STATE_IDLE: + if (msg_id == MHL3_DSCRPT_UPDATE) { + MHL3_HID_DBG_ERR("call mhid_add from OP_STATE_IDLE\n"); + ret = mhid_add(mdev, dev_id); + if (ret < 0) + mhl3_int_send_ack(mdev, ret, hb0); + } else { + mhl3_int_send_ack(mdev, HID_ACK_PROTV, hb0); + } + return; + + case OP_STATE_WAIT_MHID_DSCRPT: + MHL3_HID_DBG_INFO("OP_STATE_WAIT_MHID_DSCRPT\n"); + if ((msg_id == MHL3_MHID_DSCRPT) || + (msg_id == MHL3_DSCRPT_UPDATE)) { + matched_expected_message = true; + } + break; + + case OP_STATE_WAIT_REPORT_DSCRPT: + MHL3_HID_DBG_INFO("OP_STATE_WAIT_REPORT_DSCRPT\n"); + if ((msg_id == MHL3_REPORT_DSCRPT) || + (msg_id == MHL3_DSCRPT_UPDATE)) + matched_expected_message = true; + break; + case OP_STATE_WAIT_REPORT: + MHL3_HID_DBG_INFO("OP_STATE_WAIT_REPORT\n"); + if ((msg_id == MHL3_REPORT) || (msg_id == MHL3_DSCRPT_UPDATE)) + matched_expected_message = true; + break; + + case OP_STATE_CONNECTED: + /* + * If sent by the interrupt channel, the message is NOT from a + * MHL3_GET_REPORT request by the host, and we send it + * directly to the HID core. Otherwise, we return it via the + * WORK QUEUE. + */ + switch (msg_id) { + case MHL3_REPORT: + if (msg_channel == HID_ACHID_INT) { + /* Send the report directly to the HID core, + * minus the MSG_ID, REPORT_TYPE, REPORT_ID + * (if present), and LENGTH (2 bytes). */ + /* + * According to MHL spec 3.2, the + * mhl_hid_report_msg.id member should reflect + * the report ID for numbered reports, and not + * be present for non-numbered reports. + * However, for numbered reports, the report + * number is already in the data being passed, + * and we don't need it for anything. Since + * the HID device side driver does not + * parse the HID data, it has no way to tell + * if the report is numbered. Therefore, it + * ALWAYS includes this byte and sets it to 0. + */ + header_len = + sizeof(struct mhl_hid_report_msg) - 1; + ret = hid_input_report( + mhid->hid, HID_INPUT_REPORT, + pmsg + header_len, + length - header_len, 1); + return; + } + matched_expected_message = true; + break; + case MHL3_DSCRPT_UPDATE: + MHL3_HID_DBG_ERR( + "call mhid_add from OP_STATE_CONNECTED\n"); + ret = mhid_add(mdev, dev_id); + if (ret < 0) + mhl3_int_send_ack(mdev, ret, hb0); + return; + break; + default: + if (msg_id != MHL3_HID_ACK) + matched_expected_message = true; + break; + } + break; + } + + if (!matched_expected_message && (msg_id != MHL3_HID_ACK)) { + mhl3_int_send_ack(mdev, HID_ACK_PROTV, hb0); + return; + } else if (matched_expected_message && (msg_id == MHL3_DSCRPT_UPDATE)) { + + /* + * opstate is OP_STATE_WAIT_MHID_DSCRPT, + * OP_STATE_WAIT_REPORT_DSCRPT, or OP_STATE_WAIT_REPORT, and + * we're still running the work queue function, so we have to + * restart it. + */ + mhid->opState = OP_STATE_IDLE; + } + + /* If someone was waiting for this data, let them know it's here. */ + if (down_trylock(&mhid->data_wait_lock)) { + memcpy(mhid->in_data, pmsg, length); + mhid->in_data_length = length; + } else { + /* TODO: If not on a WORK QUEUE, where does it go? */ + MHL3_HID_DBG_ERR("Sending received msg into the ether!!!\n"); + } + /* + * Either the try was successful, in which case no one was waiting + * for the message, or it wasn't, meaning someone WAS waiting. In + * either case we need to release the semaphore. + */ + up(&mhid->data_wait_lock); +} + +static void validate_ack(struct mhl_dev_context *mdev, + uint8_t *pmsg, int length) +{ +/* uint8_t ack_msg, channel; */ + int dev_id; + struct mhl3_hid_data *mhid; + + if (length != HID_ACK_PACKET_LEN) + return; + + dev_id = (pmsg[1] & HID_HB0_DEV_ID_MSK) >> 4; + mhid = mdev->mhl_hid[dev_id]; + if (mhid == 0) + return; + +/* + ack_msg = pmsg[0] & HID_FRAG_HB0_CNT_MSK; + channel = pmsg[1] & HID_HB0_ACHID_MSK; + if (ack_msg == mhid->msg_count[channel]) + resend_last_message(); +*/ +} +/* + * Accumulate fragments of a HID message until the entire message has + * been received, then dispatch it properly. + * Called from the Titan interrupt + */ +void build_received_hid_message(struct mhl_dev_context *mdev, + uint8_t *pmsg, int length) +{ + int i, fragment_count = 0; + uint16_t *pchksum; + uint16_t accum, msg_chksum; + struct mhl3_hid_global_data *emsc = &mdev->mhl_ghid; + + if (length == 0) + return; + switch (emsc->hid_receive_state) { + + case EMSC_RCV_MSG_START: + + if ((pmsg[0] & HID_FRAG_HB0_TYPE) == HID_FRAG_HB0_TYPE_ACK) { + validate_ack(mdev, pmsg, length); + return; + } + + fragment_count = pmsg[0]; + if (length >= (HID_FRAG_HEADER_LEN + + HID_MSG_HEADER_LEN + + HID_MSG_CHKSUM_LEN + 1)) { + emsc->hb0 = pmsg[1]; + emsc->hb1 = pmsg[2]; + length -= 3; + memcpy(&emsc->in_buf[0], &pmsg[3], length); + } + emsc->msg_length = length; + emsc->hid_receive_state = EMSC_RCV_MSG_NEXT; + break; + + case EMSC_RCV_MSG_NEXT: + fragment_count = pmsg[0]; + if (length >= (HID_FRAG_HEADER_LEN + 1)) { + length--; + memcpy(&emsc->in_buf[emsc->msg_length], + &pmsg[1], length); + emsc->msg_length += length; + } + break; + default: + /* This is an error, so don't dispatch anything. */ + fragment_count = 1; + break; + } + + /* If the end of the message, dispatch it. */ + if (fragment_count == 0) { + emsc->hid_receive_state = EMSC_RCV_MSG_START; + accum = 0; + pchksum = (uint16_t *)&emsc->in_buf[0]; + emsc->msg_length -= HID_MSG_CHKSUM_LEN; + for (i = 0; i < (emsc->msg_length / 2); i++) + accum += pchksum[i]; + accum += ((uint16_t)emsc->hb0 | (((uint16_t)emsc->hb1) << 8)); + if (emsc->msg_length & 0x01) + accum += ((uint16_t)emsc->in_buf[emsc->msg_length - 1]); + + /* Add in length of message including HB0/HB1. */ + accum += emsc->msg_length + HID_MSG_HEADER_LEN; + + msg_chksum = *((uint16_t *)&emsc->in_buf[emsc->msg_length]); + if (accum != msg_chksum) { + MHL3_HID_DBG_ERR( + "HID MSG CKSM fail, ignoring message: " + "Act: %04X Exp: %04X\n", + accum, msg_chksum); + + /* Request a re-try. */ + if (emsc->hb1 & EMSC_HID_HB1_ACK) { + send_ack_packet( + mdev, emsc->hb0, + (emsc->hb1 & EMSC_HID_HB1_MSG_CNT_FLD) + - 1); + } + return; + } + + hid_message_processor(mdev, + emsc->hb0, emsc->hb1, emsc->in_buf, + emsc->msg_length); + } +} + +#ifdef SI_CONFIG_PM_SLEEP /* this was originally CONFIG_PM_SLEEP, + * but we don't want the compiler warnings + */ +/* + * TODO: This is used during system suspend and hibernation as well + * as normal runtime PM. Needs work. + */ +static int mhl3_hid_suspend(struct device *dev) +{ +/* struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + mhl3_hid_set_power(client, I2C_HID_PWR_SLEEP); +*/ + return 0; +} + +/* + * TODO: This is used during system suspend and hibernation as well + * as normal runtime PM. Needs work. + */ +static int mhl3_hid_resume(struct device *dev) +{ +/* int ret; + struct i2c_client *client = to_i2c_client(dev); + + ret = i2c_hid_hwreset(client); + if (ret) + return ret; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); +*/ + return 0; +} +#endif + diff --git a/drivers/video/fbdev/msm/mhl3/si_emsc_hid.h b/drivers/video/fbdev/msm/mhl3/si_emsc_hid.h new file mode 100644 index 000000000000..a034afa427d2 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_emsc_hid.h @@ -0,0 +1,278 @@ +/* + * SiI8620 Linux Driver eMSC HID stuff + +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. +*/ + +/* + @file si_emsc_hid.h +*/ + +#ifndef _SI_EMSC_HID_H_ +#define _SI_EMSC_HID_H_ + +#include +#include + +#include "si_fw_macros.h" +#include "si_app_devcap.h" +#include "si_infoframe.h" +#include "si_edid.h" +#include "si_mhl_defs.h" +#include "si_mhl2_edid_3d_api.h" +#include "si_8620_internal_api.h" +#include "si_mhl_tx_hw_drv_api.h" +#include "platform.h" + +extern int debug_level; + +#define MHL3_HID_DBG_ERR(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_ERR, __VA_ARGS__) +#define MHL3_HID_DBG_WARN(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_WARN, __VA_ARGS__) +#define MHL3_HID_DBG_INFO(...) \ + MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_INFO, __VA_ARGS__) + +/* mhl3_hid_data.flags */ +#define MHL3_HID_STARTED (1 << 0) +#define MHL3_HID_CONNECTED (1 << 1) +#define HID_FLAGS_WQ_ACTIVE (1 << 2) +#define HID_FLAGS_WQ_CANCEL (1 << 3) + +#define MHL3_HID_PWR_ON 0x00 +#define MHL3_HID_PWR_SLEEP 0x01 + +#define MHL3_HID_MAX_DESC_STR_LEN 256 + +#define MAX_HID_MESSAGE_CHANNELS 16 + +/* TODO: Start + * The following belongs in the kernel mod_devicetable.h file, along + * with updating the scripts/mod/file2alias.c file to match + */ + +#define MHL3_NAME_SIZE 20 +#define MHL3_MODULE_PREFIX "mhl3:" + +struct mhl3_device_id { + char name[MHL3_NAME_SIZE]; + kernel_ulong_t driver_data; /* Data private to the driver */ +}; +/* TODO: End */ + + +#define HID_BURST_ID_LEN (2 + 1) +#define HID_MSG_HEADER_LEN 2 +#define HID_MSG_CHKSUM_LEN 2 +#define HID_FRAG_HEADER_LEN 1 +#define HID_ACK_PACKET_LEN 2 + +#define HID_FRAG_LEN_MAX (EMSC_BLK_MAX_LENGTH - \ + (EMSC_BLK_STD_HDR_LEN + \ + HID_BURST_ID_LEN + \ + HID_FRAG_HEADER_LEN)) + +#define HID_FRAG_HB0_TYPE 0x80 +#define HID_FRAG_HB0_TYPE_ACK 0x80 +#define HID_FRAG_HB0_CNT_MSK 0x7F + +#define HID_HB0_DEV_ID_MSK 0xF0 +#define HID_HB0_ACHID_MSK 0x01 +#define HID_ACHID_INT 0x01 + +#define EMSC_HID_HB1_ACK 0x80 +#define EMSC_HID_HB1_MSG_CNT_FLD 0x7F + +/* HID tunneling message IDs */ +#define MHL3_HID_ACK 0x00 +#define MHL3_REPORT 0x01 +#define MHL3_GET_REPORT_DSCRPT 0x02 +#define MHL3_REPORT_DSCRPT 0x03 +#define MHL3_GET_MHID_DSCRPT 0x04 +#define MHL3_MHID_DSCRPT 0x05 +#define MHL3_GET_REPORT 0x06 +#define MHL3_SET_REPORT 0x07 +#define MHL3_DSCRPT_UPDATE 0x08 + +/* HID_ACK values */ +#define HID_ACK_SUCCESS 0x00 +#define HID_ACK_NODEV 0x01 +#define HID_ACK_NODATA 0x02 +#define HID_ACK_WAIT 0x03 +#define HID_ACK_TIMEOUT 0x04 +#define HID_ACK_PROTV 0x05 +#define HID_ACK_WRTYPE 0x06 +#define HID_ACK_WRID 0x07 +#define HID_ACK_WRFMT 0x08 +#define HID_ACK_WRMFMT 0x09 + + +/* RHID Operand Codes */ +#define MHL_RHID_REQUEST_HOST 0x00 +#define MHL_RHID_RELINQUISH_HOST 0x01 + +/* RHIDK status codes */ +#define MHL_RHID_NO_ERR 0x00 +#define MHL_RHID_INVALID 0x01 +#define MHL_RHID_DENY 0x02 + +#define OP_STATE_IDLE 0x00 +#define OP_STATE_WAIT_MHID_DSCRPT 0x01 +#define OP_STATE_WAIT_REPORT_DSCRPT 0x02 +#define OP_STATE_WAIT_REPORT 0x03 +#define OP_STATE_CONNECTED 0x04 + +/* + * This structure cannot be directly loaded from the MHL3 HID + * MHID_DSCRPT message data because the strings are variable length + * up to 255 characters each, not the full 255 UNICODE character buffer + * defined here. Note that the structure I derived this from in the + * USB driver used __le16 in place of the __u16 used below. + */ +struct mhl3_hid_desc { + __u8 bMHL3HIDmessageID; + __u16 wHIDVendorID; + __u16 wHIDProductID; + __u8 bCountryCode; + __u16 wBcdHID; + __u16 bBcdDevice; + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u16 wLanguageID; + __u8 bProductNameSize; + __u8 bManufacturerNameSize; + __u8 bSerialNumberSize; +} __packed; + +static DEFINE_MUTEX(mhl3_hid_open_mutex); + +struct mhl3_hid_global_data { + /* RHID/RHIDK host-device negotiation */ + uint8_t is_host; /* 1- Successfully negotiated as host */ + uint8_t is_device; /* 1- Relinquished host role */ + uint8_t want_host; /* 1- Want the host role */ + + int hid_receive_state; + uint8_t hb0; + uint8_t hb1; + uint8_t in_buf[4096]; /* This buffer is shared by all + * MHL3 HID devices. This is + * OK because device messages + * must be sent sequentially + * if it is a multi-fragment + * message so that they will + * not get mixed up. */ + int msg_length; +}; + +struct hid_add_work_struct { + struct work_struct work; + struct mhl3_hid_data *mhid; +}; + +struct wq_indata_t { + uint8_t *ptr; + int buffer_size; +}; + +#define MAX_HID_INQUEUE 4 + +/* The main HID Tunneling device structure */ +struct mhl3_hid_data { + struct mhl_dev_context *mdev; /* MHL driver */ + struct hid_device *hid; /* pointer to HID dev */ + + uint8_t *in_report_buf; /* Input report buffer. */ + int bufsize; /* Size of report buffer */ + + /* MHL3 MHID_DSCRPT message in multiple parts */ + struct mhl3_hid_desc *hdesc; /* The fixed length part */ + __u8 desc_product_name[MHL3_HID_MAX_DESC_STR_LEN]; + __u8 desc_mfg_name[MHL3_HID_MAX_DESC_STR_LEN]; + __u8 desc_serial_number[MHL3_HID_MAX_DESC_STR_LEN]; + + uint8_t id; /* MHL HID device ID (0-15) */ + uint8_t msg_count[2]; /* MSG_CNT for each channel */ + unsigned long flags; /* device flags */ + + /* TODO: Lee - make this a constant of the correct size or + * make this an allocated buffer. + */ + uint8_t report_desc[1024];/* Device report descriptor */ + uint8_t in_data[4096]; /* Contains the last HID message + * received if it was not + * consumed directly. */ + int in_data_length; + uint8_t out_data[4096]; /* Holds the MHL3 HID wrapped + * version of the HID message + * to be sent. */ + + int opState; /* Determines the type of HID + * messages that will be + * accepted */ + + /* For deferred processing */ + struct hid_add_work_struct mhl3_work; + struct semaphore data_wait_lock; /* Semaphore to wait for data + * requested from the remote + * device */ + +}; + +struct SI_PACK_THIS_STRUCT mhl_hid_report_msg { + uint8_t msg_id; + uint8_t type; + uint8_t id; /* According to MHL spec 3.2, this + * byte is ONLY present for numbered + * reports, but our driver has no way + * of determining if the reports are + * numbered or not, so we ALWAYS + * use it. + */ + uint8_t len_lo; + uint8_t len_hi; + uint8_t data; /* Actually the start of variable length + * report data of length specified in + * len_hi / len_lo + */ + }; + + +void mhl_tx_hid_host_role_request(struct mhl_dev_context *context, int request); +void mhl_tx_hid_host_negotiation(struct mhl_dev_context *context); + +int mhl3_hid_report_desc_parse(struct mhl3_hid_data *mhid); +void build_received_hid_message(struct mhl_dev_context *context, + uint8_t *pmsg, int length); + +void mhl3_hid_remove_all(struct mhl_dev_context *context); + +int mhl3_mt_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value); +int mhl3_mt_add(struct mhl3_hid_data *mhid, struct hid_device_id *id); +int mhl3_mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max); +int mhl3_mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max); +void mt_feature_mapping(struct hid_device *hdev, + struct hid_field *field, struct hid_usage *usage); + +void dump_array(int level, char *ptitle, uint8_t *pdata, int count); + +#if (LINUX_KERNEL_VER >= 311) +void mt_input_configured(struct hid_device *hdev, struct hid_input *hi); +void mt_report(struct hid_device *hid, struct hid_report *report); +#endif + +#endif /* #ifndef _SI_EMSC_HID_H_ */ diff --git a/drivers/video/fbdev/msm/mhl3/si_fw_macros.h b/drivers/video/fbdev/msm/mhl3/si_fw_macros.h new file mode 100644 index 000000000000..d656743a8889 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_fw_macros.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef _SI_FW_MACROS_H_ +#define _SI_FW_MACROS_H_ + +/* + * Define Linux versions of macros used to cross compile driver code for other + * platforms. + */ + +#define PLACE_IN_CODE_SEG + +#define SI_PUSH_STRUCT_PACKING +#define SI_POP_STRUCT_PACKING +#define SI_PACK_THIS_STRUCT __attribute__((__packed__)) + +#define SII_OFFSETOF offsetof + +#define SII_ASSERT(cond, ...) \ +do { \ + if (!(cond)) { \ + printk(__VA_ARGS__); \ + BUG(); \ + } \ +} while (0) + +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_infoframe.h b/drivers/video/fbdev/msm/mhl3/si_infoframe.h new file mode 100644 index 000000000000..355c5e9bcf6b --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_infoframe.h @@ -0,0 +1,219 @@ +/* + * 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. + */ + +#if !defined(SI_INFOFRAME_H) +#define SI_INFOFRAME_H + +struct __attribute__ ((__packed__)) info_frame_header_t { + uint8_t type_code; + uint8_t version_number; + uint8_t length; +}; + +enum AviColorSpace_e { + acsRGB, + acsYCbCr422, + acsYCbCr444, + acsYCbCr420 +}; + +enum avi_quant_range_e { + aqr_default = 0, + aqr_limited_range, + aqr_full_range, + aqr_reserved +}; + +/* + * AVI Info Frame Structure + */ +struct __attribute__ ((__packed__)) avi_info_frame_data_byte_1_t { + uint8_t ScanInfo:2; + uint8_t BarInfo:2; + uint8_t ActiveFormatInfoPresent:1; + enum AviColorSpace_e colorSpace:2; + uint8_t futureMustBeZero:1; +}; + +struct __attribute__ ((__packed__)) avi_info_frame_data_byte_2_t { + uint8_t ActiveFormatAspectRatio:4; + uint8_t PictureAspectRatio:2; + uint8_t Colorimetry:2; +}; + +struct __attribute__ ((__packed__)) avi_info_frame_data_byte_3_t { + uint8_t NonUniformPictureScaling:2; + uint8_t RGBQuantizationRange:2; + uint8_t ExtendedColorimetry:3; + uint8_t ITContent:1; +}; + +struct __attribute__ ((__packed__)) avi_info_frame_data_byte_4_t { + uint8_t VIC:7; + uint8_t futureMustBeZero:1; +}; + +enum BitsContent_e { + cnGraphics, + cnPhoto, + cnCinema, + cnGame +}; + +enum AviQuantization_e { + aqLimitedRange, + aqFullRange, + aqReserved0, + aqReserved1 +}; + +struct __attribute__ ((__packed__)) avi_info_frame_data_byte_5_t { + uint8_t pixelRepetionFactor:4; + enum BitsContent_e content:2; + enum AviQuantization_e quantization:2; +}; + +struct __attribute__ ((__packed__)) hw_avi_named_payload_t { + uint8_t checksum; + union { + struct __attribute__ ((__packed__)) { + struct avi_info_frame_data_byte_1_t pb1; + struct avi_info_frame_data_byte_2_t + colorimetryAspectRatio; + struct avi_info_frame_data_byte_3_t pb3; + struct avi_info_frame_data_byte_4_t VIC; + struct avi_info_frame_data_byte_5_t pb5; + uint8_t LineNumEndTopBarLow; + uint8_t LineNumEndTopBarHigh; + uint8_t LineNumStartBottomBarLow; + uint8_t LineNumStartBottomBarHigh; + uint8_t LineNumEndLeftBarLow; + uint8_t LineNumEndLeftBarHigh; + uint8_t LineNumStartRightBarLow; + uint8_t LineNumStartRightBarHigh; + } bitFields; + uint8_t infoFrameData[13]; + } ifData_u; +}; + +/* this union overlays the TPI HW for AVI InfoFrames, + * starting at REG_TPI_AVI_CHSUM. + */ +union hw_avi_payload_t { + struct hw_avi_named_payload_t namedIfData; + uint8_t ifData[14]; +}; + +struct __attribute__ ((__packed__)) avi_payload_t { + union hw_avi_payload_t hwPayLoad; + uint8_t byte_14; + uint8_t byte_15; +}; + +struct __attribute__ ((__packed__)) avi_info_frame_t { + struct info_frame_header_t header; + struct avi_payload_t payLoad; +}; + +/* these values determine the interpretation of PB5 */ +enum HDMI_Video_Format_e { + hvfNoAdditionalHDMIVideoFormatPresent, + hvfExtendedResolutionFormatPresent, + hvf3DFormatIndicationPresent +}; + +enum _3D_structure_e { + tdsFramePacking, + tdsTopAndBottom = 0x06, + tdsSideBySide = 0x08 +}; + +enum ThreeDExtData_e { + tdedHorizontalSubSampling, + tdedQuincunxOddLeftOddRight = 0x04, + tdedQuincunxOddLeftEvenRight, + tdedQuincunxEvenLeftOddRight, + tdedQuincunxEvenLeftEvenRight +}; + +enum ThreeDMetaDataType_e { + tdmdParallaxIso23022_3Section6_x_2_2 +}; + +struct __attribute__ ((__packed__)) hdmi_vendor_specific_payload_t { + struct __attribute__ ((__packed__)) { + unsigned reserved:5; + enum HDMI_Video_Format_e HDMI_Video_Format:3; + } pb4; + union { + uint8_t HDMI_VIC; + struct __attribute__ ((__packed__)) _ThreeDStructure { + unsigned reserved:3; + unsigned ThreeDMetaPresent:1; + enum _3D_structure_e threeDStructure:4; + } ThreeDStructure; + } pb5; + struct __attribute__ ((__packed__)) { + uint8_t reserved:4; + uint8_t threeDExtData:4; /* ThreeDExtData_e */ + } pb6; + struct __attribute__ ((__packed__)) _PB7 { + uint8_t threeDMetaDataLength:5; + uint8_t threeDMetaDataType:3; /* ThreeDMetaDataType_e */ + } pb7; +}; + +#define IEEE_OUI_HDMI 0x000C03 +#define VSIF_COMMON_FIELDS \ + struct info_frame_header_t header; \ + uint8_t checksum; \ + uint8_t ieee_oui[3]; + +struct __attribute__ ((__packed__)) hdmi_vsif_t{ + VSIF_COMMON_FIELDS + struct hdmi_vendor_specific_payload_t payLoad; +}; + +struct __attribute__ ((__packed__)) vsif_common_header_t { + VSIF_COMMON_FIELDS +}; +/* + * MPEG Info Frame Structure + * Table 8-11 on page 141 of HDMI Spec v1.4 + */ +struct __attribute__ ((__packed__)) unr_info_frame_t { + struct info_frame_header_t header; + uint8_t checksum; + uint8_t byte_1; + uint8_t byte_2; + uint8_t byte_3; + uint8_t byte_4; + uint8_t byte_5; + uint8_t byte_6; +}; + +#ifdef ENABLE_DUMP_INFOFRAME + +void DumpIncomingInfoFrameImpl(char *pszId, char *pszFile, int iLine, + info_frame_t *pInfoFrame, uint8_t length); + +#define DumpIncomingInfoFrame(pData, length) \ + DumpIncomingInfoFrameImpl(#pData, __FILE__, __LINE__, \ + (info_frame_t *)pData, length) +#else +#define DumpIncomingInfoFrame(pData, length) /* do nothing */ +#endif + +#endif /* if !defined(SI_INFOFRAME_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.c b/drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.c new file mode 100644 index 000000000000..13d2a08831af --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.c @@ -0,0 +1,851 @@ +/* + * 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. + */ + +#ifdef MEDIA_DATA_TUNNEL_SUPPORT + +#include +#include +#include +#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" +#include "si_mdt_inputdev.h" +#include "mhl_linux_tx.h" +#include "platform.h" +#ifdef KERNEL_2_6_38_AND_LATER +#include +#endif +#ifdef DEBUG +#include +#endif +/* keycode map from usbkbd.c */ +uint8_t usb_kbd_keycode[256] = { + 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, + 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, + 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 87, 88, 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, + 105, 108, 103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, + 72, 73, 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, + 190, + 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135, + 136, 113, + 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94, 95, 0, 0, 0, + 122, 123, 90, 91, 85, 0, 0, 0, 0, 0, 150, 155, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 200, 201, 207, 208, 213, 215, 216, 217, 226, 139, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115, 114, + 113, + 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140 +}; + +static bool is_mdt_dev_active(struct mhl_dev_context *dev_context, + enum mdt_dev_types_e dev_type) +{ + if (dev_context->mdt_devs.is_dev_registered[dev_type] == INPUT_ACTIVE) + return true; + else + return false; +} + +static bool is_mdt_dev_waiting(struct mhl_dev_context *dev_context, + enum mdt_dev_types_e dev_type) +{ + if (dev_context->mdt_devs.is_dev_registered[dev_type] == + INPUT_WAITING_FOR_REGISTRATION) + return true; + else + return false; +} + +static void destroy_mouse(struct mhl_dev_context *dev_context) +{ + if (dev_context->mdt_devs.dev_mouse == NULL) + return; + + MHL_TX_DBG_INFO("Unregistering mouse: %p\n", + dev_context->mdt_devs.dev_mouse); + input_unregister_device(dev_context->mdt_devs.dev_mouse); + MHL_TX_DBG_INFO("Freeing mouse: %p\n", dev_context->mdt_devs.dev_mouse); + input_free_device(dev_context->mdt_devs.dev_mouse); + dev_context->mdt_devs.dev_mouse = NULL; +} + +static void destroy_keyboard(struct mhl_dev_context *dev_context) +{ + if (dev_context->mdt_devs.dev_keyboard == NULL) + return; + + MHL_TX_DBG_INFO("Unregistering keyboard: %p\n", + dev_context->mdt_devs.dev_keyboard); + input_unregister_device(dev_context->mdt_devs.dev_keyboard); + MHL_TX_DBG_INFO("Freeing keyboard: %p\n", + dev_context->mdt_devs.dev_keyboard); + input_free_device(dev_context->mdt_devs.dev_keyboard); + dev_context->mdt_devs.dev_keyboard = NULL; +} + +static void destroy_touchscreen(struct mhl_dev_context *dev_context) +{ + if (dev_context->mdt_devs.dev_touchscreen == NULL) + return; + + MHL_TX_DBG_INFO("Unregistering mouse: %p\n", + dev_context->mdt_devs.dev_touchscreen); + input_unregister_device(dev_context->mdt_devs.dev_touchscreen); + MHL_TX_DBG_INFO("Freeing mouse: %p\n", + dev_context->mdt_devs.dev_touchscreen); + input_free_device(dev_context->mdt_devs.dev_touchscreen); + dev_context->mdt_devs.dev_touchscreen = NULL; + memset(dev_context->mdt_devs.prior_touch_events, 0, + MAX_TOUCH_CONTACTS * + sizeof(dev_context->mdt_devs.prior_touch_events[0])); +} + +int init_mdt_keyboard(struct mhl_dev_context *dev_context) +{ + int i; + uint8_t error; + struct input_dev *dev_keyboard; + + dev_keyboard = input_allocate_device(); + if (!dev_keyboard) { + MHL_TX_DBG_ERR("Not enough memory\n"); + return -ENOMEM; + } + MHL_TX_DBG_INFO("Allocated keyboard: %p\n", dev_keyboard); + + set_bit(EV_KEY, dev_keyboard->evbit); + set_bit(EV_REP, dev_keyboard->evbit); + + dev_keyboard->phys = "mdt_kbd/input0"; + dev_keyboard->name = "MDTkeyboard"; + dev_keyboard->keycode = usb_kbd_keycode; + dev_keyboard->keycodesize = sizeof(unsigned char); + dev_keyboard->keycodemax = ARRAY_SIZE(usb_kbd_keycode); + + for (i = 1; i < 256; i++) + set_bit(usb_kbd_keycode[i], dev_keyboard->keybit); + + dev_keyboard->id.bustype = BUS_VIRTUAL; + dev_keyboard->id.vendor = 0x1095; + dev_keyboard->id.product = MHL_PRODUCT_NUM; + + /* Use version to distinguish between devices */ + dev_keyboard->id.version = 0xA; + + error = input_register_device(dev_keyboard); + if (error) { + MHL_TX_DBG_ERR("Failed to register device\n"); + input_free_device(dev_keyboard); + return error; + } + + MHL_TX_DBG_INFO("Registered keyboard: %p\n", dev_keyboard); + + dev_context->mdt_devs.dev_keyboard = dev_keyboard; + + return 0; +} + +int init_mdt_mouse(struct mhl_dev_context *dev_context) +{ + uint8_t error; + struct input_dev *dev_mouse; + + dev_mouse = input_allocate_device(); + if (!dev_mouse) { + MHL_TX_DBG_ERR("Not enough memory\n"); + return -ENOMEM; + } + MHL_TX_DBG_INFO("Allocated mouse: %p\n", dev_mouse); + + set_bit(EV_REL, dev_mouse->evbit); + set_bit(EV_KEY, dev_mouse->evbit); + set_bit(BTN_LEFT, dev_mouse->keybit); + set_bit(BTN_RIGHT, dev_mouse->keybit); + set_bit(BTN_MIDDLE, dev_mouse->keybit); + set_bit(BTN_SIDE, dev_mouse->keybit); + set_bit(BTN_EXTRA, dev_mouse->keybit); + set_bit(REL_X, dev_mouse->relbit); + set_bit(REL_Y, dev_mouse->relbit); + set_bit(REL_WHEEL, dev_mouse->relbit); +#if (RIGHT_MOUSE_BUTTON_IS_ESC == 1) + set_bit(KEY_ESC, dev_mouse->keybit); + dev_context->mdt_devs.prior_right_button = 0; +#endif + + dev_mouse->phys = "mdt_mouse/input0"; + dev_mouse->name = "MDTmouse"; + dev_mouse->id.bustype = BUS_VIRTUAL; + dev_mouse->id.vendor = 0x1095; + dev_mouse->id.product = MHL_PRODUCT_NUM; + + /* Use version to distinguish between devices */ + dev_mouse->id.version = 0xB; + + error = input_register_device(dev_mouse); + if (error) { + MHL_TX_DBG_ERR("Failed to register device\n"); + input_free_device(dev_mouse); + return error; + } + + MHL_TX_DBG_INFO("Registered mouse: %p\n", dev_mouse); + + dev_context->mdt_devs.dev_mouse = dev_mouse; + + return 0; +} + +int init_mdt_touchscreen(struct mhl_dev_context *dev_context) +{ + uint8_t error; + struct input_dev *dev_touchscreen; + + dev_touchscreen = input_allocate_device(); + if (!dev_touchscreen) { + MHL_TX_DBG_ERR("Not enough memory\n"); + return -ENOMEM; + } + + MHL_TX_DBG_INFO("Allocated touch screen: %p\n", dev_touchscreen); + +#if !defined(SINGLE_TOUCH) && defined(KERNEL_2_6_38_AND_LATER) + input_mt_init_slots(dev_touchscreen, MAX_TOUCH_CONTACTS); +#endif + + dev_touchscreen->phys = "mdt_touch/input0"; + dev_touchscreen->name = "MDTtouchscreen"; + dev_touchscreen->id.bustype = BUS_VIRTUAL; + dev_touchscreen->id.vendor = 0x1095; + dev_touchscreen->id.product = MHL_PRODUCT_NUM; + + /* use version to distinguish between devices */ + dev_touchscreen->id.version = 0xC; + +#if defined(SINGLE_TOUCH) + __set_bit(EV_ABS, dev_touchscreen->evbit); + __set_bit(ABS_X, dev_touchscreen->absbit); + __set_bit(ABS_Y, dev_touchscreen->absbit); + __set_bit(EV_KEY, dev_touchscreen->evbit); +#if (CORNER_BUTTON == 1) + __set_bit(KEY_ESC, dev_touchscreen->keybit); +#endif + __set_bit(BTN_TOUCH, dev_touchscreen->keybit); +#ifdef KERNEL_2_6_38_AND_LATER + __set_bit(INPUT_PROP_DIRECT, dev_touchscreen->propbit); +#endif + input_set_abs_params(dev_touchscreen, ABS_X, 0, + dev_context->mdt_devs.x_max, 0, 0); + input_set_abs_params(dev_touchscreen, ABS_Y, 0, + dev_context->mdt_devs.y_max, 0, 0); +#else + + __set_bit(EV_ABS, dev_touchscreen->evbit); + __set_bit(EV_KEY, dev_touchscreen->evbit); +#ifdef KERNEL_2_6_38_AND_LATER + __set_bit(EV_SYN, dev_touchscreen->evbit); + __set_bit(MT_TOOL_FINGER, dev_touchscreen->keybit); + __set_bit(INPUT_PROP_DIRECT, dev_touchscreen->propbit); + input_mt_init_slots(dev_touchscreen, MAX_TOUCH_CONTACTS); +#else + __set_bit(BTN_TOUCH, dev_touchscreen->keybit); + input_set_abs_params(dev_touchscreen, ABS_MT_WIDTH_MAJOR, 0, 3, 0, 0); + input_set_abs_params(dev_touchscreen, ABS_MT_TRACKING_ID, 0, 3, 0, 0); +#endif +#if (CORNER_BUTTON == 1) + __set_bit(KEY_ESC, dev_touchscreen->keybit); +#endif + input_set_abs_params(dev_touchscreen, ABS_MT_TOUCH_MAJOR, 0, 30, 0, 0); + input_set_abs_params(dev_touchscreen, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(dev_touchscreen, ABS_MT_POSITION_X, 0, + dev_context->mdt_devs.x_max, 0, 0); + input_set_abs_params(dev_touchscreen, ABS_MT_POSITION_Y, 0, + dev_context->mdt_devs.y_max, 0, 0); + +#endif + +#if (JB_421 == 1) && (ICS_BAR == 1) +#if (Y_BUTTON_RECENTAPPS_TOP != 0) + __set_bit(KEY_MENU, dev_touchscreen->keybit); +#endif +#if (Y_BUTTON_HOME_TOP != 0) + __set_bit(KEY_HOMEPAGE, dev_touchscreen->keybit); +#endif +#if (Y_BUTTON_BACK_TOP != 0) + __set_bit(KEY_BACK, dev_touchscreen->keybit); +#endif +#endif + + error = input_register_device(dev_touchscreen); + if (error) { + MHL_TX_DBG_ERR("Failed to register device\n"); + input_free_device(dev_touchscreen); + return error; + } + MHL_TX_DBG_INFO("Registered touchscreen: %p\n", dev_touchscreen); + + dev_context->mdt_devs.dev_touchscreen = dev_touchscreen; + + /* initialize history; in parcitular initialize state elements with + * MDT_TOUCH_INACTIVE + */ + memset(dev_context->mdt_devs.prior_touch_events, 0, + MAX_TOUCH_CONTACTS * + sizeof(dev_context->mdt_devs.prior_touch_events[0])); + + return 0; +} + +static int destroy_device(struct mhl_dev_context *dev_context, + enum mdt_dev_types_e mdt_device_type) +{ + if ((mdt_device_type >= MDT_TYPE_COUNT) || + (is_mdt_dev_active(dev_context, mdt_device_type) == 0)) { + MHL_TX_DBG_INFO("FAILURE. Invalid disconnect request.\n", + mdt_device_type); + return REGISTRATION_ERROR; + } + + switch (mdt_device_type) { + case MDT_TYPE_MOUSE: + destroy_mouse(dev_context); + break; + case MDT_TYPE_KEYBOARD: + destroy_keyboard(dev_context); + break; + case MDT_TYPE_TOUCHSCREEN: + destroy_touchscreen(dev_context); + break; + default: + /* redundant check to pacify the compiler */ + break; + } + + dev_context->mdt_devs.is_dev_registered[mdt_device_type] = + INPUT_WAITING_FOR_REGISTRATION; + + MHL_TX_DBG_INFO + ("SUCCESS. Disconnect event handled for %d device type.\n", + mdt_device_type); + + return REGISTRATION_SUCCESS; +} + +/* The recursive piece of the registration function. */ +static int registration_helper(struct mhl_dev_context *dev_context, + enum mdt_dev_types_e mdt_device_type) +{ + switch (mdt_device_type) { + case MDT_TYPE_KEYBOARD: + if (dev_context->mdt_devs.dev_keyboard != 0) + return REGISTRATION_SUCCESS; + return init_mdt_keyboard(dev_context); + break; + case MDT_TYPE_MOUSE: + if (dev_context->mdt_devs.dev_mouse != 0) + return REGISTRATION_SUCCESS; + if (init_mdt_mouse(dev_context) != REGISTRATION_SUCCESS) + return REGISTRATION_ERROR; + + /* Do not support both a pointer and touch. */ + destroy_device(dev_context, MDT_TYPE_TOUCHSCREEN); + break; + case MDT_TYPE_TOUCHSCREEN: + if (dev_context->mdt_devs.dev_touchscreen != 0) + return REGISTRATION_SUCCESS; + if (init_mdt_touchscreen(dev_context) != REGISTRATION_SUCCESS) + return REGISTRATION_ERROR; + + /* Do not support both a pointer and touch. */ + destroy_device(dev_context, MDT_TYPE_MOUSE); + break; + case MDT_TYPE_COUNT: + /* + * This case is out of range. + * Code is included to avoid compiler warning. + */ + break; + } + + return REGISTRATION_SUCCESS; +} + +static int register_device(struct mhl_dev_context *dev_context, + enum mdt_dev_types_e mdt_device_type) +{ + uint8_t error = 0; + + if ((mdt_device_type >= MDT_TYPE_COUNT) || + (is_mdt_dev_waiting(dev_context, mdt_device_type) == false)) + return REGISTRATION_ERROR; + + /* Call recursive part of the function. + Don't update is_dev_registered there. */ + error = registration_helper(dev_context, mdt_device_type); + + if (error != REGISTRATION_SUCCESS) { + dev_context->mdt_devs.is_dev_registered[mdt_device_type] = + INPUT_DISABLED; + MHL_TX_DBG_INFO("SUCCESS. Device type %d registered.\n", + mdt_device_type); + } else { + dev_context->mdt_devs.is_dev_registered[mdt_device_type] = + INPUT_ACTIVE; + MHL_TX_DBG_INFO + ("FAILURE. Device type %d registration failed.\n", + mdt_device_type); + } + + return error; +} + +void generate_event_keyboard(struct mhl_dev_context *dev_context, + struct mdt_packet *keyboard_packet) +{ + struct input_dev *dev_keyboard = dev_context->mdt_devs.dev_keyboard; + uint8_t *keycodes_new = dev_context->mdt_devs.keycodes_new; + uint8_t *keycodes_old = dev_context->mdt_devs.keycodes_old; + int i; + + register_device(dev_context, MDT_TYPE_KEYBOARD); + + memcpy(keycodes_new, &keyboard_packet->header, HID_INPUT_REPORT_CNT); + MHL_TX_DBG_INFO("Key (scancode %02X) asserted.\n", keycodes_new[1]); + + if (dev_keyboard == 0) { + MHL_TX_DBG_INFO("MDT_ERR_NOKEY\n"); + return; + } + + /* following code was copied from usbkbd.c */ + /* generate events for CRL, SHIFT, and ALT keys */ + for (i = 0; i < 3; i++) + input_report_key(dev_keyboard, + usb_kbd_keycode[i + 224], + (keycodes_new[0] >> i) & 1); + + /* + * Generate key press/release events for the + * remaining bytes in the input packet + */ + for (i = 1; i < 7; i++) { + /* If keycode in pervious HID payload doesn't appear + * in NEW HID payload, generate de-assertion event + */ + if ((keycodes_old[i] > 3) && + ((uint8_t *) memscan(keycodes_new + 1, keycodes_old[i], 6) + == ((uint8_t *) (keycodes_new) + 7))) { + if (usb_kbd_keycode[keycodes_old[i]]) { + input_report_key(dev_keyboard, + usb_kbd_keycode[keycodes_old + [i]], 0); + } else { + MHL_TX_DBG_INFO("Unknown key (scancode %#x) " + "released.\n", keycodes_old[i]); + } + } + + /* If keycode in NEW HID paylaod doesn't appear in previous + * HID payload, generate assertion event + */ + if (keycodes_new[i] > 3 && + memscan(keycodes_old + 1, keycodes_new[i], + 6) == keycodes_old + 7) { + if (usb_kbd_keycode[keycodes_new[i]]) { + input_report_key(dev_keyboard, + usb_kbd_keycode[keycodes_new + [i]], 1); + } else { + MHL_TX_DBG_INFO("Unknown key (scancode %#x) " + "pressed.\n", keycodes_new[i]); + } + } + } + + input_sync(dev_keyboard); + + /* NEW HID payload is now OLD */ + memcpy(keycodes_old, keycodes_new, HID_INPUT_REPORT_CNT); +} + +static void mdt_toggle_keycode(struct input_dev *hid_device, + unsigned char keycode) +{ + if (NULL == hid_device) + return; + + input_report_key(hid_device, keycode, KEY_PRESSED); + input_sync(hid_device); + + input_report_key(hid_device, keycode, KEY_RELEASED); + input_sync(hid_device); +} + +void mdt_toggle_keyboard_keycode(struct mhl_dev_context *dev_context, + unsigned char keycode) +{ + mdt_toggle_keycode(dev_context->mdt_devs.dev_keyboard, keycode); +} + +void generate_event_mouse(struct mhl_dev_context *dev_context, + struct mdt_packet *mousePacket) +{ + struct input_dev *dev_mouse = dev_context->mdt_devs.dev_mouse; + + register_device(dev_context, MDT_TYPE_MOUSE); + + MHL_TX_DBG_INFO("mouse buttons (0x%02x)\n", + mousePacket->header & MDT_HDR_MOUSE_BUTTON_MASK); + + if (dev_mouse == 0) { + MHL_TX_DBG_ERR("MDT_ERR_NOMOUSE\n"); + return; + } + + /* Translate and report mouse button changes */ + input_report_key(dev_mouse, BTN_LEFT, + mousePacket->header & MDT_HDR_MOUSE_BUTTON_1); + +#if (RIGHT_MOUSE_BUTTON_IS_ESC == 1) + if (mousePacket->header & MDT_HDR_MOUSE_BUTTON_2) { + if (!dev_context->mdt_devs.prior_right_button) { + dev_context->mdt_devs.prior_right_button = 1; + mdt_toggle_keycode(dev_mouse, KEY_ESC); + } + } else + dev_context->mdt_devs.prior_right_button = 0; +#else + input_report_key(dev_mouse, BTN_RIGHT, + mousePacket->header & MDT_HDR_MOUSE_BUTTON_2); +#endif + input_report_key(dev_mouse, BTN_MIDDLE, + mousePacket->header & MDT_HDR_MOUSE_BUTTON_3); + + input_report_rel(dev_mouse, REL_X, + mousePacket->event.mouse.x_displacement); + + input_report_rel(dev_mouse, REL_Y, + mousePacket->event.mouse.y_displacement); + + input_report_rel(dev_mouse, REL_WHEEL, + mousePacket->event.mouse.z_displacement); + + input_sync(dev_mouse); +} + +static uint8_t process_touch_packet(struct mhl_dev_context *dev_context, + struct mdt_packet *touchPacket) +{ + struct mdt_touch_history_t *prior_event; + int abs_x, abs_y; + uint8_t isTouched = (touchPacket->header & 0x01); + uint8_t contactID = ((touchPacket->header & 0x06) >> 1); + + prior_event = + (struct mdt_touch_history_t *)&(dev_context->mdt_devs. + prior_touch_events[contactID]); + + abs_x = touchPacket->event.touch_pad.x_abs_coordinate[MDT_TOUCH_X_LOW] | + (touchPacket->event.touch_pad. + x_abs_coordinate[MDT_TOUCH_X_HIGH] << 8); + abs_y = + touchPacket->event.touch_pad. + y_abs_coordinate[MDT_TOUCH_Y_LOW] | (touchPacket->event.touch_pad. + y_abs_coordinate + [MDT_TOUCH_Y_HIGH] << 8); + +#if (CORNER_BUTTON == 1) + /* Handle LOWER RIGHT corner like a EXIT button (ESC key) */ + if ((abs_x > X_CORNER_RIGHT_LOWER) && (abs_y > Y_CORNER_RIGHT_LOWER)) { + if (isTouched != dev_context->mdt_devs.prior_touch_button) { + dev_context->mdt_devs.prior_touch_button = isTouched; + if (isTouched) + mdt_toggle_keycode(dev_context->mdt_devs. + dev_touchscreen, KEY_ESC); + } + return 0xFF; + } +#elif (ICS_BAR == 1) + /* JB421 doesn't allow this driver to trigger buttons on the bar. + implement custom buttons to workaround the problem. */ + if ((isTouched != dev_context->mdt_devs.prior_touch_button) + && (abs_x >= X_BUTTON_BAR_START)) { + if ((abs_y > Y_BUTTON_RECENTAPPS_TOP) + && (abs_y < Y_BUTTON_RECENTAPPS_BOTTOM)) + mdt_toggle_keycode(dev_context->mdt_devs. + dev_touchscreen, KEY_MENU); + else if ((abs_y > Y_BUTTON_HOME_TOP) + && (abs_y < Y_BUTTON_HOME_BOTTOM)) + mdt_toggle_keycode(dev_context->mdt_devs. + dev_touchscreen, KEY_HOMEPAGE); + else if ((abs_y > Y_BUTTON_BACK_TOP) + && (abs_y < Y_BUTTON_BACK_BOTTOM)) + mdt_toggle_keycode(dev_context->mdt_devs. + dev_touchscreen, KEY_BACK); + return 0xFF; + } +#endif + + /* support dynamic configuration through ATTRIBUTES */ + if (dev_context->mdt_devs.swap_xy != 0) { + prior_event->abs_x = abs_y; + prior_event->abs_y = abs_x; + } else { + prior_event->abs_x = abs_x; + prior_event->abs_y = abs_y; + } + + if ((dev_context->mdt_devs.x_raw != 0) && + (dev_context->mdt_devs.x_screen != 0) && + (prior_event->abs_x != 0)) { + prior_event->abs_x *= dev_context->mdt_devs.x_screen; + prior_event->abs_x /= dev_context->mdt_devs.x_raw; + } + + if ((dev_context->mdt_devs.y_raw != 0) && + (dev_context->mdt_devs.y_screen != 0) && + (prior_event->abs_y != 0)) { + prior_event->abs_y *= dev_context->mdt_devs.y_screen; + prior_event->abs_y /= dev_context->mdt_devs.y_raw; + } + + if ((dev_context->mdt_devs.swap_leftright) && + (dev_context->mdt_devs.x_max >= prior_event->abs_x)) + prior_event->abs_x = + (dev_context->mdt_devs.x_max - prior_event->abs_x); + + if ((dev_context->mdt_devs.swap_updown) && + (dev_context->mdt_devs.y_max >= prior_event->abs_y)) + prior_event->abs_y = + (dev_context->mdt_devs.y_max - prior_event->abs_y); + + prior_event->abs_x += dev_context->mdt_devs.x_shift; + prior_event->abs_y += dev_context->mdt_devs.y_shift; + + if (isTouched == 0) { + if (prior_event->isTouched == 0) + /* Multiple release events; + * declare contact inactive & ignore + */ + prior_event->state = MDT_TOUCH_INACTIVE; + } else { + prior_event->state = MDT_TOUCH_ACTIVE; + } + prior_event->isTouched = isTouched; + + return contactID; +} + +#if defined(SINGLE_TOUCH) +static void submit_touchscreen_events_as_single_touch( + struct mhl_dev_context *dev_context) +{ + struct input_dev *dev_ts = dev_context->mdt_devs.dev_touchscreen; + struct mdt_touch_history_t *prior_event; + + prior_event = &(dev_context->mdt_devs.prior_touch_events[0]); + input_report_key(dev_ts, BTN_TOUCH, prior_event->isTouched); + input_report_abs(dev_ts, ABS_X, prior_event->abs_x); + input_report_abs(dev_ts, ABS_Y, prior_event->abs_y); +} +#elif defined(KERNEL_2_6_38_AND_LATER) +static void submit_touchscreen_events_with_proto_B( + struct mhl_dev_context *dev_context, uint8_t contactID) +{ + struct input_dev *dev_ts = dev_context->mdt_devs.dev_touchscreen; + struct mdt_touch_history_t *prior_event; + uint8_t i; + uint8_t counter = 0; + + for (i = 0; i < MAX_TOUCH_CONTACTS; i++) { + + prior_event = &(dev_context->mdt_devs.prior_touch_events[i]); + + if (prior_event->state == MDT_TOUCH_INACTIVE) + continue; + + input_mt_slot(dev_ts, i); + input_mt_report_slot_state(dev_ts, MT_TOOL_FINGER, + prior_event->isTouched); + + /* Event already handled; don't handle it again. */ + if (prior_event->isTouched == 0) { + prior_event->state = MDT_TOUCH_INACTIVE; + } else { + counter++; + input_report_abs(dev_ts, ABS_MT_TOUCH_MAJOR, 15); + input_report_abs(dev_ts, ABS_MT_PRESSURE, 50); + input_report_abs(dev_ts, ABS_MT_POSITION_X, + prior_event->abs_x); + input_report_abs(dev_ts, ABS_MT_POSITION_Y, + prior_event->abs_y); + } + + /* BTN_TOUCH breaks support brokend as of JB42 */ +#if !defined(JB_421) + if (counter == 1) + input_report_key(dev_ts, BTN_TOUCH, 1); + else + input_report_key(dev_ts, BTN_TOUCH, 0); +#endif + } +#else +static void submit_touchscreen_events_with_proto_A( + struct mhl_dev_context *dev_context, uint8_t contactID) +{ + struct input_dev *dev_ts = dev_context->mdt_devs.dev_touchscreen; + struct mdt_touch_history_t *prior_event; + uint8_t i; + uint8_t count = 0; + + for (i = 0; i < MAX_TOUCH_CONTACTS; i++) { + prior_event = &(dev_context->mdt_devs.prior_touch_events[i]); + + if (prior_event->state == MDT_TOUCH_INACTIVE) + continue; + + count++; + + if (prior_event->isTouched == 0) + /* Event handled; don't handle it again. */ + prior_event->state = MDT_TOUCH_INACTIVE; + + input_report_key(dev_ts, BTN_TOUCH, prior_event->isTouched); + input_report_abs(dev_ts, ABS_MT_TOUCH_MAJOR, + prior_event->isTouched); + input_report_abs(dev_ts, ABS_MT_TRACKING_ID, i); + input_report_abs(dev_ts, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(dev_ts, ABS_MT_POSITION_X, prior_event->abs_x); + input_report_abs(dev_ts, ABS_MT_POSITION_Y, prior_event->abs_y); + input_mt_sync(dev_ts); + } + + if (count == 0) + input_mt_sync(dev_ts); +} +#endif + +static void generate_event_touchscreen(struct mhl_dev_context *dev_context, + struct mdt_packet *touchPacket) +{ + struct input_dev *dev_ts = dev_context->mdt_devs.dev_touchscreen; + uint8_t contactID; + + register_device(dev_context, MDT_TYPE_TOUCHSCREEN); + + if (dev_ts == 0) { + MHL_TX_DBG_ERR("MDT_ERR_NOTOUCHSCREEN\n"); + return; + } + + /* process touch packet into prior_touch_events */ + contactID = process_touch_packet(dev_context, touchPacket); + if (contactID == 0xFF) + return; + +#if defined(SINGLE_TOUCH) + submit_touchscreen_events_as_single_touch(dev_context); +#elif defined(KERNEL_2_6_38_AND_LATER) + submit_touchscreen_events_with_proto_B(dev_context, contactID); +#else + submit_touchscreen_events_with_proto_A(dev_context, contactID); +#endif + /* generate touchscreen assertion */ + input_sync(dev_ts); +} + +static bool process_hotplug_packet(struct mhl_dev_context *dev_context, + struct mdt_packet *hotplug_packet) +{ + /* 'M' previoulsy found to be in the header byte */ + if ((hotplug_packet->event.hotplug.sub_header_d != D_CHAR) && + (hotplug_packet->event.hotplug.sub_header_t != T_CHAR) && + (hotplug_packet->event.hotplug.mdt_version != MDT_VERSION)) + return false; + + /* in the future, support response with ACK or NACK */ + MHL_TX_DBG_INFO("HP packet. Device type: %02x. Event: %02x.\n", + hotplug_packet->event.hotplug.device_type, + hotplug_packet->event.hotplug.event_code); + switch (hotplug_packet->event.hotplug.event_code) { + case NOTICE_DEV_PLUG: + register_device(dev_context, + hotplug_packet->event.hotplug.device_type); + break; + case NOTICE_DEV_UNPLUG: + destroy_device(dev_context, + hotplug_packet->event.hotplug.device_type); + break; + default: + return false; + } + return true; +} + +bool si_mhl_tx_mdt_process_packet(struct mhl_dev_context *dev_context, + void *packet) +{ + struct mdt_packet *mdt_event_packet = (struct mdt_packet *)packet; + + if (!(MDT_HDR_IS_HID & mdt_event_packet->header)) { + if (M_CHAR == mdt_event_packet->header) + return process_hotplug_packet(dev_context, + mdt_event_packet); + + MHL_TX_DBG_INFO("Ignoring non-HID packet\n"); + return false; + } + + if (MDT_HDR_IS_KEYBOARD & mdt_event_packet->header) { + generate_event_keyboard(dev_context, mdt_event_packet); + + } else if (!(MDT_HDR_IS_KEYBOARD & mdt_event_packet->header) && + (!(MDT_HDR_IS_NOT_MOUSE & mdt_event_packet->header))) { + generate_event_mouse(dev_context, mdt_event_packet); + + } else if (!(MDT_HDR_IS_KEYBOARD & mdt_event_packet->header) && + (MDT_HDR_IS_NOT_MOUSE & mdt_event_packet->header)) { + generate_event_touchscreen(dev_context, mdt_event_packet); + + } else if (!(MDT_HDR_IS_KEYBOARD & mdt_event_packet->header) && + (MDT_HDR_IS_NOT_MOUSE & mdt_event_packet->header) && + (MDT_HDR_IS_NOT_LAST & mdt_event_packet->header)) { + + MHL_TX_DBG_INFO("Unsupported gaming controller " + "event received\n"); + } else { + MHL_TX_DBG_INFO("Event is either not an HID event or " + "is an an unknown HID event type\n"); + return false; + } + + /* Consume the write burst event as an MDT event */ + return true; +} + +void mdt_destroy(struct mhl_dev_context *dev_context) +{ + destroy_device(dev_context, MDT_TYPE_KEYBOARD); + destroy_device(dev_context, MDT_TYPE_MOUSE); + destroy_device(dev_context, MDT_TYPE_TOUCHSCREEN); +} +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.h b/drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.h new file mode 100644 index 000000000000..9a108a0724bd --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.h @@ -0,0 +1,216 @@ +/* + * 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. + */ + +#ifndef _SI_MDT_INPUTDEV_H_ +#define _SI_MDT_INPUTDEV_H_ + +#define HID_INPUT_REPORT_CNT 7 +#define MAX_TOUCH_CONTACTS 4 + +/* MDT header byte bit definitions */ +#define REGISTRATION_SUCCESS 0 +#define REGISTRATION_ERROR 1 +#define KEY_PRESSED 1 +#define KEY_RELEASED 0 +#define MDT_TOUCH_INACTIVE 1 +#define MDT_TOUCH_ACTIVE 2 + +/* Common header bit definitions */ +#define MDT_HDR_IS_HID 0x80 +#define MDT_HDR_IS_PORT_B 0x40 +#define MDT_HDR_IS_KEYBOARD 0x20 +#define MDT_HDR_IS_NOT_LAST 0x10 +#define MDT_HDR_IS_NOT_MOUSE 0x08 + +/* Keyboard event specific header bit definitions */ +#define MDT_HDR_KBD_LEFT_ALT 0x04 +#define MDT_HDR_KBD_LEFT_SHIFT 0x02 +#define MDT_HDR_KBD_LEFT_CTRL 0x01 + +/* Mouse event specific header bit definitions */ +#define MDT_HDR_MOUSE_BUTTON_3 0x04 +#define MDT_HDR_MOUSE_BUTTON_2 0x02 +#define MDT_HDR_MOUSE_BUTTON_1 0x01 +#define MDT_HDR_MOUSE_BUTTON_MASK 0x07 + +/* Touch pad event specific header bit definitions */ +#define MDT_HDR_TOUCH_IS_TOUCHED 0x01 +#define MDT_HDR_TOUCH_CONTACT_ID_MASK 0x06 + +/* Game controller event specific header bit definitions */ +#define MDT_HDR_GAME_BUTTON_3 0x04 +#define MDT_HDR_GAME_BUTTON_2 0x02 +#define MDT_HDR_GAME_BUTTON_1 0x01 + +/* MDT hot-plug prefix and event information */ +#define MDT_VERSION 1 +#define M_CHAR 'M' +#define D_CHAR 'D' +#define T_CHAR 'T' +#define NOTICE_DEV_PLUG 'R' +#define NOTICE_DEV_UNPLUG 'U' +#define RESPONSE_ACK 'A' +#define RESPONSE_NACK 'N' + +/* MDT Touch screen resources and parameters */ + +#define MDT_TOUCH_X 0 +#define MDT_TOUCH_Y 1 +#define BYTE_LOW 0 +#define BYTE_HIGH 1 +#define MDT_TOUCH_X_LOW BYTE_LOW +#define MDT_TOUCH_X_HIGH BYTE_HIGH +#define MDT_TOUCH_Y_LOW BYTE_LOW +#define MDT_TOUCH_Y_HIGH BYTE_HIGH + +/* support 11 bit absolute addressing */ +#define X_CORNER_RIGHT_LOWER 1870 +#define Y_CORNER_RIGHT_LOWER 1870 +#define ICS_BeagleboardxM 1 +#define X_MAX 1920 +#define Y_MAX 1920 +#define SCALE_X_RAW 0 +#define SCALE_X_SCREEN 0 +#define SCALE_Y_RAW 0 +#define SCALE_Y_SCREEN 0 +#define X_SHIFT 0 +#define Y_SHIFT 0 +#define SWAP_LEFTRIGHT 0 +#define SWAP_UPDOWN 0 +#define SWAP_XY 0 +#define SINGLE_TOUCH 1 +#define CORNER_BUTTON 1 +#define ICS_BAR 0 +#define RIGHT_MOUSE_BUTTON_IS_ESC 1 +/* requires installation of IDC file */ +/* #define KERNEL_2_6_38_AND_LATER */ +/* as of JB the IDC file is needed but, doesn't + guarantee acess to virtual buttons. */ +#define JB_421 0 +#if (JB_421 == 1) +#define X_BUTTON_BAR_START 0x4F0 +#define Y_BUTTON_RECENTAPPS_TOP 0x050 +#define Y_BUTTON_RECENTAPPS_BOTTOM 0x165 +#define Y_BUTTON_HOME_TOP 0x185 +#define Y_BUTTON_HOME_BOTTOM 0x2C0 +#define Y_BUTTON_BACK_TOP 0x2E0 +#define Y_BUTTON_BACK_BOTTOM 0x3E0 +#endif + +enum mdt_dev_state_e { + INPUT_DISABLED, + INPUT_WAITING_FOR_REGISTRATION, + INPUT_ACTIVE +}; + +enum mdt_dev_types_e { + MDT_TYPE_MOUSE, + MDT_TYPE_KEYBOARD, + MDT_TYPE_TOUCHSCREEN, + MDT_TYPE_COUNT +}; + +struct mdt_touch_history_t { + uint32_t abs_x; + uint32_t abs_y; + uint8_t isTouched; + uint8_t state; +}; + +struct mdt_inputdevs { + /* Prior HID input report */ + uint8_t keycodes_old[HID_INPUT_REPORT_CNT]; + /* Current HID input report */ + uint8_t keycodes_new[HID_INPUT_REPORT_CNT]; + struct input_dev *dev_keyboard; + struct input_dev *dev_mouse; + struct input_dev *dev_touchscreen; + /* Instance tracking variable */ + uint8_t is_dev_registered[MDT_TYPE_COUNT]; + struct mdt_touch_history_t prior_touch_events[MAX_TOUCH_CONTACTS]; + unsigned char prior_touch_button; + +#if (RIGHT_MOUSE_BUTTON_IS_ESC == 1) + unsigned char prior_right_button; +#endif + /* ser overrides to allow runtime calibration */ + uint32_t x_max, y_max; + uint32_t x_screen, x_raw, x_shift; + uint32_t y_screen, y_raw, y_shift; + uint32_t swap_xy, swap_updown, swap_leftright; +}; + +struct keyboard_event_data { + uint8_t first_key[3]; + uint8_t second_key[3]; +}; + +struct mouse_event_data { + int8_t x_displacement; + int8_t y_displacement; + int8_t z_displacement; + uint8_t vendor_specific[2]; + uint8_t vendor_specific_game_flag; +}; + +struct touch_pad_event_data { + uint8_t x_abs_coordinate[2]; + uint8_t y_abs_coordinate[2]; + uint8_t vendor_specific; + uint8_t vendor_specific_game_flag; +}; + +struct gaming_controller { + int8_t x_rel_displacement; + int8_t y_rel_displacement; + int8_t z_rel_displacement; + int8_t y2_rel_displacement; + uint8_t buttons_ext; + uint8_t id_dpad; +}; + +struct mdt_hotplug_data { + uint8_t sub_header_d; + uint8_t sub_header_t; + uint8_t event_code; + uint8_t device_type; + uint8_t mdt_version; + uint8_t reserved; +}; + +struct mdt_packet { + uint8_t adopter_id_h; + uint8_t adopter_id_l; + uint8_t header; + union { + struct keyboard_event_data keyboard; + struct mouse_event_data mouse; + struct touch_pad_event_data touch_pad; + struct gaming_controller game_controller; + struct mdt_hotplug_data hotplug; + uint8_t bytes[6]; + } event; +}; + +struct mhl_dev_context; +extern struct attribute_group mdt_attr_group; +void mdt_toggle_keyboard_keycode(struct mhl_dev_context *dev_context, + unsigned char keycode); +bool si_mhl_tx_mdt_process_packet(struct mhl_dev_context *dev_context, + void *packet); + +void mdt_destroy(struct mhl_dev_context *dev_context); + +#endif /* #ifndef _SI_MDT_INPUTDEV_H_ */ diff --git a/drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d.c b/drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d.c new file mode 100644 index 000000000000..7a3856b39915 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d.c @@ -0,0 +1,4562 @@ +/* + * 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 +#include +#include +#include +#include + +#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_8620_internal_api.h" +#include "si_mhl_tx_hw_drv_api.h" +#ifdef MEDIA_DATA_TUNNEL_SUPPORT +#include "si_mdt_inputdev.h" +#endif +#include "mhl_linux_tx.h" + +#include "platform.h" +#include "si_mhl_callback_api.h" +#include "si_8620_drv.h" +#include "mhl_supp.h" + +#define SET_3D_FLAG(context, x) \ + do { \ + context->parse_data.flags.x = 1; \ + MHL_TX_EDID_INFO("set %s\n", #x); \ + } while (0) + +#define CLR_3D_FLAG(context, x) \ + do { \ + context->parse_data.flags.x = 0; \ + MHL_TX_EDID_INFO("clr %s\n", #x); \ + } while (0) + +#define TEST_3D_FLAG(context, x) (context->parse_data.flags.x) + +struct timing_mode_from_data_sheet_t { + uint16_t h_total; + uint16_t v_total; + uint16_t columns; + uint16_t rows; + uint16_t field_rate; + uint32_t pixel_clock; + uint8_t mhl3_vic; + struct { + uint8_t interlaced:1; + uint8_t reserved:7; + } flags; + char *description; +}; + +/* note that this table is sorted by: + columns, + then by rows, + then by field_rate, + then by h_total, + then by v_total, + then by pixel_clock. +*/ + +struct timing_mode_from_data_sheet_t timing_modes[] = { + { 832, 445, 640, 350, 85, 31500000, 0, {0, 0}, "640x350-85"}, + { 832, 445, 640, 400, 85, 31500000, 0, {0, 0}, "640x400-85"}, + { 800, 525, 640, 480, 60, 25175000, 0, {0, 0}, "VGA60"}, + { 864, 525, 640, 480, 66, 29937600, 0, {0, 0}, "640x480-66"}, + { 832, 520, 640, 480, 72, 31500000, 0, {0, 0}, "VGA72"}, + { 840, 500, 640, 480, 75, 31500000, 0, {0, 0}, "VGA75"}, + { 832, 509, 640, 480, 85, 36000000, 0, {0, 0}, "VGA85"}, + {1716, 262, 720, 240, 60, 27000000, 6, {1, 0}, "480i"}, + {1728, 312, 720, 288, 60, 27000000, 21, {1, 0}, "576i"}, + { 936, 446, 720, 400, 85, 35500000, 0, {0, 0}, "720x400"}, + { 858, 525, 720, 480, 60, 27000000, 2, {0, 0}, "480p"}, + { 864, 625, 720, 576, 50, 27000000, 17, {0, 0}, "576p"}, + {1024, 625, 800, 600, 56, 36000000, 0, {0, 0}, "SVGA56"}, + {1056, 628, 800, 600, 60, 40000000, 0, {0, 0}, "SVGA60"}, + {1040, 666, 800, 600, 72, 50000000, 0, {0, 0}, "SVGA72"}, + {1056, 625, 800, 600, 75, 49500000, 0, {0, 0}, "SVGA75"}, + {1048, 631, 800, 600, 85, 56250000, 0, {0, 0}, "SVGA85"}, + { 960, 636, 800, 600, 120, 73250000, 0, {0, 0}, "SVGA120RB"}, + {1120, 654, 832, 624, 75, 54936000, 0, {0, 0}, "832x624-75"}, + {1088, 517, 848, 480, 60, 33750000, 0, {0, 0}, "WVGA"}, + {1072, 529, 852, 480, 60, 34025280, 0, {0, 0}, "852x480-60"}, + {1264, 408, 1024, 384, 87, 44900000, 0, {1, 0}, "XGA87i"}, + {1320, 596, 1024, 576, 60, 47203200, 0, {0, 0}, "1024x576-60"}, + {1344, 806, 1024, 768, 60, 65000000, 0, {0, 0}, "XGA"}, + {1328, 806, 1024, 768, 70, 75000000, 0, {0, 0}, "XGA70"}, + {1328, 804, 1024, 768, 74, 79010688, 0, {0, 0}, "1024x768-74"}, + {1312, 800, 1024, 768, 75, 78750000, 0, {0, 0}, "XGA75"}, + {1376, 808, 1024, 768, 85, 94500000, 0, {0, 0}, "XGA85"}, + {1184, 813, 1024, 768, 120, 115500000, 0, {0, 0}, "XGA120RB"}, + {1600, 900, 1152, 864, 75, 108000000, 0, {0, 0}, "1152x864-75"}, + {1456, 915, 1152, 870, 75, 99918000, 0, {0, 0}, "1152x870-75"}, + {1696, 750, 1280, 720, 59, 75048000, 0, {0, 0}, "1280x720-59"}, + {1650, 750, 1280, 720, 60, 74250000, 4, {0, 0}, "720p"}, + {3300, 750, 1280, 720, 24, 59400000, 60, {0, 0}, "720p-24"}, + {3960, 750, 1280, 720, 25, 74250000, 61, {0, 0}, "720p-25"}, + {3300, 750, 1280, 720, 30, 74250000, 62, {0, 0}, "720p-30"}, + {1440, 790, 1280, 768, 60, 68250000, 0, {0, 0}, "1280x768-60RB"}, + {1664, 798, 1280, 768, 60, 79500000, 0, {0, 0}, "1280x768-60"}, + {1696, 805, 1280, 768, 75, 102250000, 0, {0, 0}, "1280x768-75"}, + {1712, 809, 1280, 768, 85, 117500000, 0, {0, 0}, "1280x768-85"}, + {1440, 813, 1280, 768, 120, 140250000, 0, {0, 0}, "1280x768-120RB"}, + {1440, 823, 1280, 800, 60, 71000000, 0, {0, 0}, "1280x8000RB"}, + {1680, 831, 1280, 800, 60, 83500000, 0, {0, 0}, "1280x800-60"}, + {1696, 838, 1280, 800, 75, 106550000, 0, {0, 0}, "1280x800-75"}, + {1712, 843, 1280, 800, 85, 122500000, 0, {0, 0}, "1280x800-85"}, + {1440, 847, 1280, 800, 120, 146250000, 0, {0, 0}, "1280x800-120RB"}, + {1800, 1000, 1280, 960, 60, 108000000, 0, {0, 0}, "1280x960-60"}, + {1728, 1011, 1280, 960, 85, 148500000, 0, {0, 0}, "1280x960-85"}, + {1440, 1017, 1280, 960, 120, 175500000, 0, {0, 0}, "1280x960-120RB"}, + {1688, 1066, 1280, 1024, 60, 108000000, 0, {0, 0}, "1280x1024-60"}, + {1688, 1066, 1280, 1024, 75, 135000000, 0, {0, 0}, "1280x1024-75"}, + {1728, 1072, 1280, 1024, 85, 157500000, 0, {0, 0}, "1280x1024-85"}, + {1760, 1082, 1280, 1024, 95, 180910400, 0, {0, 0}, "1280x1024-95"}, + {1440, 1084, 1280, 1024, 120, 187250000, 0, {0, 0}, "1280x1024-120RB"}, + {1792, 795, 1360, 768, 60, 85500000, 0, {0, 0}, "1360x768-60"}, + {1520, 813, 1360, 768, 120, 148250000, 0, {0, 0}, "1360x768-120RB"}, + {1840, 1087, 1365, 1024, 59, 118004720, 0, {0, 0}, "1365x1024-59"}, + {1800, 1065, 1365, 1024, 75, 143775000, 0, {0, 0}, "1365x1024-75"}, + {1500, 800, 1366, 768, 60, 72000000, 0, {0, 0}, "1366x768-60RB"}, + {1792, 798, 1366, 768, 60, 85500000, 0, {0, 0}, "1366x768-60"}, + {1800, 850, 1400, 788, 59, 90270000, 0, {0, 0}, "1400x788-59"}, + {1864, 1089, 1400, 1050, 59, 119763864, 0, {0, 0}, "1400x1050-59"}, + {1600, 926, 1440, 900, 60, 88750000, 0, {0, 0}, "1440x900-60RB"}, + {1904, 934, 1440, 900, 60, 106500000, 0, {0, 0}, "1440x900-60"}, + {1936, 942, 1440, 900, 75, 136750000, 0, {0, 0}, "1440x900-75"}, + {1952, 948, 1440, 900, 85, 157000000, 0, {0, 0}, "1440x900-85"}, + {1600, 953, 1440, 900, 120, 182750000, 0, {0, 0}, "1440x900-120RB"}, + {1792, 1000, 1440, 960, 71, 127232000, 0, {0, 0}, "1440x960-71"}, + {1560, 1080, 1440, 1050, 60, 101000000, 0, {0, 0}, "1440x1050-60RB"}, + {1864, 1089, 1440, 1050, 60, 121750000, 0, {0, 0}, "1440x1050-60"}, + {1896, 1099, 1440, 1050, 75, 156000000, 0, {0, 0}, "1440x1050-75"}, + {1912, 1105, 1440, 1050, 85, 179500000, 0, {0, 0}, "1440x1050-85"}, + {1560, 1112, 1440, 1050, 120, 208000000, 0, {0, 0}, "1440x1050-120RB"}, + {1800, 1000, 1600, 900, 60, 108000000, 0, {0, 0}, "1600x900-60RB"}, + {2144, 1060, 1600, 1024, 59, 134085760, 0, {0, 0}, "1600x1024-59"}, + {1840, 1080, 1600, 1050, 60, 119000000, 0, {0, 0}, "1600x1050-60RB"}, + {2240, 1089, 1600, 1050, 60, 146250000, 0, {0, 0}, "1600x1050-60"}, + {2272, 1099, 1600, 1050, 75, 187000000, 0, {0, 0}, "1600x1050-75"}, + {2288, 1105, 1600, 1050, 85, 214750000, 0, {0, 0}, "1600x1050-85"}, + {1840, 1112, 1600, 1050, 120, 245500000, 0, {0, 0}, "1600x1050-120RB"}, + {2160, 1250, 1600, 1200, 60, 162000000, 0, {0, 0}, "1600x1200"}, + {2160, 1250, 1600, 1200, 65, 175500000, 0, {0, 0}, "1600x1200-65"}, + {2160, 1250, 1600, 1200, 70, 189000000, 0, {0, 0}, "1600x1200-70"}, + {2160, 1250, 1600, 1200, 75, 202500000, 0, {0, 0}, "1600x1200-75"}, + {2160, 1250, 1600, 1200, 85, 229500000, 0, {0, 0}, "1600x1200-85"}, + {1760, 1271, 1600, 1200, 120, 245500000, 0, {0, 0}, "1600x1200-120RB"}, + {2240, 1089, 1680, 1050, 59, 143922240, 0, {0, 0}, "1680x1050-59"}, + {2448, 1394, 1792, 1344, 60, 204750000, 0, {0, 0}, "1792x1344-60"}, + {2456, 1417, 1792, 1344, 75, 261000000, 0, {0, 0}, "1792x1344-75"}, + {1952, 1423, 1792, 1344, 120, 333250000, 0, {0, 0}, "1792x1344-120RB"}, + {2528, 1439, 1856, 1392, 60, 218250000, 0, {0, 0}, "1856x1392-60"}, + {2560, 1500, 1856, 1392, 75, 288000000, 0, {0, 0}, "1856x1392-75"}, + {2016, 1474, 1856, 1392, 120, 356500000, 0, {0, 0}, "1856x1392-120RB"}, + {2200, 562, 1920, 540, 60, 74250000, 5, {1, 0}, "1080i"}, + {2750, 1125, 1920, 1080, 24, 74250000, 32, {0, 0}, "1080p24"}, + {2750, 1125, 1920, 1080, 30, 74250000, 34, {0, 0}, "1080p30"}, + {2640, 1125, 1920, 1080, 50, 148500000, 20, {0, 0}, "1080p50"}, + {2080, 1111, 1920, 1080, 59, 136341920, 0, {0, 0}, "1920x1080-59"}, + {2200, 1125, 1920, 1080, 60, 148500000, 16, {0, 0}, "1080p60"}, + {2080, 1235, 1920, 1200, 60, 154000000, 0, {0, 0}, "1920x1200-60RB"}, + {2592, 1245, 1920, 1200, 60, 193250000, 0, {0, 0}, "1920x1200-60"}, + {2608, 1255, 1920, 1200, 75, 245250000, 0, {0, 0}, "1920x1200-75"}, + {2624, 1262, 1920, 1200, 85, 281250000, 0, {0, 0}, "1920x1200-85"}, + {2080, 1271, 1920, 1200, 120, 317000000, 0, {0, 0}, "1920x1200-120RB"}, + {2600, 1500, 1920, 1440, 60, 234000000, 0, {0, 0}, "1920x1440-60"}, + {2640, 1500, 1920, 1440, 75, 297000000, 0, {0, 0}, "1920x1440-75"}, + {2080, 1525, 1920, 1440, 120, 380500000, 0, {0, 0}, "1920x1440-120RB"}, + {2250, 1200, 2048, 1152, 60, 162000000, 0, {0, 0}, "2048x1152-60RB"}, + {2784, 1325, 2048, 1280, 60, 221328000, 0, {0, 0}, "2048x1280-60"}, + {2720, 1646, 2560, 1600, 60, 268500000, 0, {0, 0}, "2560x1600-60RB"}, + {3504, 1658, 2560, 1600, 60, 348500000, 0, {0, 0}, "2560x1600-60"}, + {3536, 1672, 2560, 1600, 75, 443250000, 0, {0, 0}, "2560x1600-75"}, + {3536, 1682, 2560, 1600, 85, 505250000, 0, {0, 0}, "2560x1600-85"}, + {2720, 1694, 2560, 1600, 120, 552750000, 0, {0, 0}, "2560x1600-120RB"}, + {4400, 2250, 3840, 2160, 30, 297000000, 95, {0, 0}, "3840x2160-30"}, + {5280, 2250, 3840, 2160, 25, 297000000, 94, {0, 0}, "3840x2160-25"}, + {5500, 2250, 3840, 2160, 24, 296703000, 93, {0, 0}, "3840x2160-24"}, + {5500, 2250, 4096, 2160, 24, 297000000, 98, {0, 0}, + "4096x2160-24 SMPTE"} +}; + + +void display_timing_enumeration_callback( + struct edid_3d_data_t *mhl_edid_3d_data, + uint16_t columns, uint16_t rows, uint8_t bits_per_pixel, + uint32_t vertical_refresh_rate_in_milliHz, uint16_t burst_id, + union video_burst_descriptor_u *p_descriptor) +{ + struct MHL2_video_descriptor_t *pMHL2_video_descriptor; + struct MHL3_hev_vic_descriptor_t *hev_vic_descriptor; + struct MHL3_hev_dtd_item_t *dtd_payload; + + if (p_descriptor) { + struct drv_hw_context *hw_context = + mhl_edid_3d_data->drv_context; + switch (burst_id) { + case burst_id_3D_VIC: + case burst_id_3D_DTD: + { + pMHL2_video_descriptor = + &p_descriptor->mhl2_3d_descriptor; + MHL_TX_EDID_INFO("%4d x %4d, %2d bpp at %u Hz " + "3D - %16s %16s %16s\n\n", + columns, rows, (uint16_t) bits_per_pixel, + vertical_refresh_rate_in_milliHz, + pMHL2_video_descriptor->left_right ? + "Left/Right" : "", + pMHL2_video_descriptor->top_bottom ? + "Top/Bottom" : "", + pMHL2_video_descriptor->frame_sequential ? + "Frame Sequential" : ""); + } + break; + case burst_id_HEV_VIC: + { + hev_vic_descriptor = + &p_descriptor->mhl3_hev_vic_descriptor; + MHL_TX_EDID_INFO + ("%4d x %4d, %2d bpp at %u Hz VIC: %d\n\n", + columns, rows, (uint16_t) bits_per_pixel, + vertical_refresh_rate_in_milliHz, + hev_vic_descriptor->vic_cea861f); + } + break; + case burst_id_HEV_DTDB: + { + dtd_payload = &p_descriptor->mhl3_hev_dtd; + MHL_TX_EDID_INFO("seq: %d pclk: %9d h_active: " + "%5d h_blank: %5d h_fp: %5d h_sw: %5d " + "h_flags: 0x%02x\nv_tot: %5d v_blnk: %3d " + "v_fp: %3d v_sw: %3d v_fields: %3d\n", + p_descriptor->mhl3_hev_dtd.sequence_index, + ENDIAN_CONVERT_16(dtd_payload->a. + pixel_clock_in_MHz), + ENDIAN_CONVERT_16(dtd_payload->a. + h_active_in_pixels), + ENDIAN_CONVERT_16(dtd_payload->a. + h_blank_in_pixels), + ENDIAN_CONVERT_16(dtd_payload->a. + h_front_porch_in_pixels), + ENDIAN_CONVERT_16(dtd_payload->a. + h_sync_width_in_pixels), + dtd_payload->a.h_flags, + ENDIAN_CONVERT_16(dtd_payload->b. + v_total_in_lines), + dtd_payload->b.v_blank_in_lines, + dtd_payload->b.v_front_porch_in_lines, + dtd_payload->b.v_sync_width_in_lines, + dtd_payload->b. + v_refresh_rate_in_fields_per_second, + dtd_payload->b.v_flags); + } + break; + } + hw_context->callbacks.display_timing_enum_item( + hw_context->callbacks.context, + columns, rows, bits_per_pixel, + vertical_refresh_rate_in_milliHz, + burst_id, p_descriptor); + } else { + MHL_TX_EDID_INFO("%4d x %4d, %2d bpp at %u Hz 3D\n\n", columns, + rows, (uint16_t) bits_per_pixel, + vertical_refresh_rate_in_milliHz); + } +} + +void display_timing_enumeration_end(struct edid_3d_data_t *mhl_edid_3d_data) +{ + struct drv_hw_context *hw_context = mhl_edid_3d_data->drv_context; + int vic_3d_idx, dtd_3d_idx, vic_hev_idx, dtd_hev_idx; + int count_hev, count_3d; + + MHL_TX_DBG_WARN("\n"); + + hw_context->callbacks.display_timing_enum_end( + hw_context->callbacks.context); + /* MHL3.0 spec requires that,in response to FEAT_REQ, all 3D_VIC and + * 3D_DTD bursts shall be sent before all HEV_VIC and HEV_DTD bursts. + * Now that we're done with all of the above, pair up the 3D_VIC and + * 3D_DTD data with the HEV_VIC and HEV_DTD data. + */ + if (NULL == mhl_edid_3d_data->hev_vic_list) { + MHL_TX_DBG_WARN("no HEV_VICs\n"); + } else if (NULL == mhl_edid_3d_data->_3d_vic_list) { + MHL_TX_DBG_WARN("no 3D_VICs\n"); + /* do nothing */ + } else if (mhl_edid_3d_data->hev_vic_info.num_items) { + MHL_TX_DBG_WARN("\n"); + count_hev = mhl_edid_3d_data->hev_vic_info.num_items; + count_3d = mhl_edid_3d_data->_3d_vic_info.num_items; + vic_3d_idx = mhl_edid_3d_data->parse_data.vic_2d_index; + for (vic_hev_idx = 0; + (vic_hev_idx < count_hev) && (vic_3d_idx < count_3d); + ++vic_hev_idx, ++vic_3d_idx) { + MHL_TX_DBG_WARN("\n"); + mhl_edid_3d_data->hev_vic_list[vic_hev_idx]._3d_info = + mhl_edid_3d_data->_3d_vic_list[vic_3d_idx]._3d_info; + } + } + if (NULL == mhl_edid_3d_data->hev_dtd_list) { + /* do nothing */ + MHL_TX_DBG_WARN("no HEV_DTDs\n"); + } else if (NULL == mhl_edid_3d_data->_3d_dtd_list) { + MHL_TX_DBG_WARN("no 3D_DTDs\n"); + /* do nothing */ + } else if (mhl_edid_3d_data->hev_dtd_info.num_items) { + MHL_TX_DBG_WARN( + "num_vesa_timing_dtds:0x%x num_cea_861_timing_dtds:0x%x\n", + mhl_edid_3d_data->parse_data.num_vesa_timing_dtds, + mhl_edid_3d_data->parse_data.num_cea_861_timing_dtds); + + count_hev = mhl_edid_3d_data->hev_dtd_info.num_items; + count_3d = mhl_edid_3d_data->_3d_dtd_info.num_items; + dtd_3d_idx = + mhl_edid_3d_data->parse_data.num_vesa_timing_dtds + + mhl_edid_3d_data->parse_data.num_cea_861_timing_dtds; + for (dtd_hev_idx = 0; + (dtd_hev_idx < count_hev) && (dtd_3d_idx < count_3d); + ++dtd_hev_idx, ++dtd_3d_idx) { + MHL_TX_DBG_WARN( + "hev_dtd_index:0x%x _3d_dtd_index:0x%x\n", + dtd_hev_idx, dtd_3d_idx); + mhl_edid_3d_data->hev_dtd_list[dtd_hev_idx]._3d_info = + mhl_edid_3d_data-> + _3d_dtd_list[dtd_3d_idx++]._3d_info; + } + } +} + +static bool field_rate_within_measurement_precision(int table, int measured) +{ +int difference; + /* reference clock varies +/- 5% */ + difference = table - measured; + difference *= 100; + difference /= table; + if ((difference < -5) || (difference > 5)) + return false; + MHL_TX_DBG_ERR("field rate delta: %d\%\n", difference) + return true; +} + +/* + * si_mhl_tx_validate_timings_table + * + * Returns the number of indistinct timing modes in + * timing_modes_from_data_sheet. + * + * This is included to sound the alarm if the table contains entries that + * cannot be reliably distinguished from each other. + */ +static int validate_timings_table(void) +{ + int match_count = 0; + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(timing_modes); ++i) { + for (j = i + 1; j < ARRAY_SIZE(timing_modes); ++j) { + if (timing_modes[i].h_total != + timing_modes[j].h_total) { + continue; + } + if (timing_modes[i].v_total != + timing_modes[j].v_total) { + continue; + } + if (timing_modes[i].columns != + timing_modes[j].columns) { + continue; + } + if (timing_modes[i].rows != + timing_modes[j].rows) { + continue; + } + if ( !field_rate_within_measurement_precision( + timing_modes[i].field_rate, + timing_modes[j].field_rate)) + continue; + + match_count++; + MHL_TX_DBG_ERR( + "%sentry %d:\n\t{%d,%d,%d,%d,%d}\n" + "\tmatches entry %d:\n\t{%d,%d,%d,%d,%d}%s\n", + ANSI_ESC_RED_TEXT, i, + timing_modes[i].h_total, + timing_modes[i].v_total, + timing_modes[i].columns, + timing_modes[i].rows, + timing_modes[i].field_rate, + j, + timing_modes[j].h_total, + timing_modes[j].v_total, + timing_modes[j].columns, + timing_modes[j].rows, + timing_modes[j].field_rate, + ANSI_ESC_RESET_TEXT); + } + } + if (match_count) { + MHL_TX_DBG_ERR( + "%s%d matching table entries! We can't have this!%s\n", + ANSI_ESC_RED_TEXT, match_count, ANSI_ESC_RESET_TEXT); + } else { + MHL_TX_DBG_WARN("%sAll timing entries distinguishable%s\n", + ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); + } + return match_count; +} + +uint32_t si_mhl_tx_find_timings_from_totals( + struct edid_3d_data_t *mhl_edid_3d_data, + struct si_incoming_timing_t *p_timing) +{ + uint32_t ret_val = 0; + uint8_t i; + for (i = 0; i < ARRAY_SIZE(timing_modes); ++i) { + + if (timing_modes[i].h_total != p_timing->h_total) + continue; + if (timing_modes[i].v_total != p_timing->v_total) + continue; + if (timing_modes[i].columns != p_timing->columns) + continue; + if (timing_modes[i].rows != p_timing->rows) + continue; + + if ( !field_rate_within_measurement_precision( + timing_modes[i].field_rate, + p_timing->field_rate)) + continue; + + p_timing->calculated_pixel_clock = + timing_modes[i].h_total * + timing_modes[i].v_total * + timing_modes[i].field_rate; + + ret_val = timing_modes[i].pixel_clock; + p_timing->mhl3_vic = timing_modes[i].mhl3_vic; + MHL_TX_DBG_ERR("%svic: %d %s%s\n", ANSI_ESC_GREEN_TEXT, + timing_modes[i].mhl3_vic, + timing_modes[i].description, + ANSI_ESC_RESET_TEXT) + return ret_val; + } + MHL_TX_DBG_WARN("VIC was zero!!!\n" + "\t\th_total:\t%d\n" + "\t\tv_total:\t%d\n" + "\t\tcolumns:\t%d\n" + "\t\t rows:\t%d\n" + "\t\tfield_rate:\t%d\n", + p_timing->h_total, p_timing->v_total, + p_timing->columns, p_timing->rows, + p_timing->field_rate); + return 0; +} + +PLACE_IN_CODE_SEG char *psz_space = "n/a"; +PLACE_IN_CODE_SEG char *psz_frame_sequential = "FS "; +PLACE_IN_CODE_SEG char *psz_top_bottom = "TB "; +PLACE_IN_CODE_SEG char *psz_left_right = "LR "; + +enum cea_image_aspect_ratio_e { + cea_iar_4_to_3, + cea_iar_16_to_9, + cea_iar_64_to_27, + cea_iar_256_to_135 +}; + +enum VIC_info_flags_e { + vif_single_frame_rate, + vif_dual_frame_rate +}; + +enum VIC_scan_mode_e { + vsm_progressive, + vsm_interlaced +}; + +enum pixel_aspect_ratio_e { + par_1_to_1, + par_16_to_15, + par_16_to_27, + par_16_to_45, + par_16_to_45_160_to_45, + par_1_to_15_10_to_15, + par_1_to_9_10_to_9, + par_2_to_15_20_to_15, + par_2_to_9, + par_2_to_9_20_to_9, + par_32_to_27, + par_32_to_45, + par_4_to_27_40_to_27, + par_4_to_9, + par_4_to_15, + par_64_to_45, + par_8_to_15, + par_8_to_27, + par_8_to_27_80_to_27, + par_8_to_45_80_to_45, + par_8_to_9, + par_4_to_3, + par_64_to_63 +}; + +struct SI_PACK_THIS_STRUCT VIC_info_fields_t { + enum cea_image_aspect_ratio_e image_aspect_ratio:2; + enum VIC_scan_mode_e interlaced:1; + enum pixel_aspect_ratio_e pixel_aspect_ratio:5; + + enum VIC_info_flags_e frame_rate_info:1; + uint8_t clocks_per_pixel_shift_count:2; + uint8_t field2_v_blank:2; + uint8_t reserved:3; +}; + +struct SI_PACK_THIS_STRUCT VIC_info_t { + uint16_t columns; + uint16_t rows; + uint16_t h_blank_in_pixels; + uint16_t v_blank_in_pixels; + uint32_t field_rate_in_milliHz; + struct VIC_info_fields_t fields; +}; + +struct SI_PACK_THIS_STRUCT HDMI_VIC_info_t { + uint16_t columns; + uint16_t rows; + uint32_t field_rate_0_in_milliHz; + uint32_t field_rate_1_in_milliHz; + uint32_t pixel_clock_0; + uint32_t pixel_clock_1; + uint8_t corresponding_MHL3_VIC; +}; + +/* VIC is a place holder, and not actually stored */ +#define CEA_861_F_VIC_info_entry(VIC, columns, rows, HBlank, VBLank, \ + FieldRate, image_aspect_ratio, scanmode, PixelAspectRatio, flags, \ + clocksPerPelShift, AdditionalVBlank) \ + {columns, rows, HBlank, VBLank, FieldRate, {image_aspect_ratio, \ + scanmode, PixelAspectRatio, flags, clocksPerPelShift, \ + AdditionalVBlank} } + +static struct VIC_info_t VIC_info[] = { + CEA_861_F_VIC_info_entry(0, 0, 0, 0, 0, 0000, cea_iar_4_to_3, + vsm_progressive, par_1_to_1, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(1, 640, 480, 160, 45, 60000, cea_iar_4_to_3, + vsm_progressive, par_1_to_1, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(2, 720, 480, 138, 45, 60000, cea_iar_4_to_3, + vsm_progressive, par_8_to_9, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(3, 720, 480, 138, 45, 60000, cea_iar_16_to_9, + vsm_progressive, par_32_to_27, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(4, 1280, 720, 370, 30, 60000, cea_iar_16_to_9, + vsm_progressive, par_1_to_1, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(5, 1920, 1080, 280, 22, 60000, cea_iar_16_to_9, + vsm_interlaced, par_1_to_1, vif_dual_frame_rate, 0, 1), + CEA_861_F_VIC_info_entry(6, 720, 480, 276, 22, 60000, cea_iar_4_to_3, + vsm_interlaced, par_8_to_9, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(7, 720, 480, 276, 22, 60000, cea_iar_16_to_9, + vsm_interlaced, par_32_to_27, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(8, 720, 240, 276, 22, 60000, cea_iar_4_to_3, + vsm_progressive, par_4_to_9, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(9, 720, 428, 276, 22, 60000, cea_iar_16_to_9, + vsm_progressive, par_16_to_27, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(10, 2880, 480, 552, 22, 60000, cea_iar_4_to_3, + vsm_interlaced, par_2_to_9_20_to_9, vif_dual_frame_rate, 0, 1), + CEA_861_F_VIC_info_entry(11, 2880, 480, 552, 22, 60000, cea_iar_16_to_9, + vsm_interlaced, par_8_to_27_80_to_27, vif_dual_frame_rate, 0, + 1), + CEA_861_F_VIC_info_entry(12, 2880, 240, 552, 22, 60000, cea_iar_4_to_3, + vsm_progressive, par_1_to_9_10_to_9, vif_dual_frame_rate, 0, 1), + CEA_861_F_VIC_info_entry(13, 2880, 240, 552, 22, 60000, cea_iar_16_to_9, + vsm_progressive, par_4_to_27_40_to_27, vif_dual_frame_rate, 0, + 1), + CEA_861_F_VIC_info_entry(14, 1440, 480, 276, 45, 60000, cea_iar_4_to_3, + vsm_progressive, par_4_to_9, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(15, 1440, 480, 276, 45, 60000, cea_iar_16_to_9, + vsm_progressive, par_16_to_27, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(16, 1920, 1080, 280, 45, 60000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(17, 720, 576, 144, 49, 50000, cea_iar_4_to_3, + vsm_progressive, par_16_to_15, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(18, 720, 576, 144, 49, 50000, cea_iar_16_to_9, + vsm_progressive, par_64_to_45, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(19, 1280, 720, 700, 30, 50000, cea_iar_16_to_9, + vsm_progressive, par_1_to_1, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(20, 1920, 1080, 720, 22, 50000, + cea_iar_16_to_9, vsm_interlaced, par_1_to_1, + vif_single_frame_rate, 0, 1), + CEA_861_F_VIC_info_entry(21, 720, 576, 288, 24, 50000, cea_iar_4_to_3, + vsm_interlaced, par_16_to_15, vif_single_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(22, 720, 576, 288, 24, 50000, cea_iar_16_to_9, + vsm_interlaced, par_64_to_45, vif_single_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(23, 720, 288, 288, 24, 50000, cea_iar_4_to_3, + vsm_progressive, par_8_to_15, vif_single_frame_rate, 1, 2), + CEA_861_F_VIC_info_entry(24, 720, 288, 288, 24, 50000, cea_iar_16_to_9, + vsm_progressive, par_32_to_45, vif_single_frame_rate, 1, 2), + CEA_861_F_VIC_info_entry(25, 2880, 576, 576, 24, 50000, cea_iar_4_to_3, + vsm_interlaced, par_2_to_15_20_to_15, vif_single_frame_rate, 0, + 1), + CEA_861_F_VIC_info_entry(26, 2880, 576, 576, 24, 50000, cea_iar_16_to_9, + vsm_interlaced, par_16_to_45_160_to_45, vif_single_frame_rate, + 0, 1), + CEA_861_F_VIC_info_entry(27, 2880, 288, 576, 24, 50000, cea_iar_4_to_3, + vsm_progressive, par_1_to_15_10_to_15, vif_single_frame_rate, 0, + 2), + CEA_861_F_VIC_info_entry(28, 2880, 288, 576, 24, 50000, cea_iar_16_to_9, + vsm_progressive, par_8_to_45_80_to_45, vif_single_frame_rate, + 0, 2), + CEA_861_F_VIC_info_entry(29, 1440, 576, 288, 49, 50000, cea_iar_4_to_3, + vsm_progressive, par_8_to_15, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(30, 1440, 576, 288, 49, 50000, cea_iar_16_to_9, + vsm_progressive, par_32_to_45, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(31, 1920, 1080, 720, 45, 50000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(32, 1920, 1080, 830, 45, 24000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(33, 1920, 1080, 720, 45, 25000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(34, 1920, 1080, 280, 45, 30000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(35, 2880, 480, 552, 45, 60000, cea_iar_4_to_3, + vsm_progressive, par_2_to_9, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(36, 2880, 480, 552, 45, 60000, cea_iar_16_to_9, + vsm_progressive, par_8_to_27, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(37, 2880, 576, 576, 49, 50000, cea_iar_4_to_3, + vsm_progressive, par_4_to_15, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(38, 2880, 576, 576, 49, 50000, cea_iar_16_to_9, + vsm_progressive, par_16_to_45, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(39, 1920, 1080, 384, 85, 50000, + cea_iar_16_to_9, vsm_interlaced, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(40, 1920, 1080, 720, 22, 100000, + cea_iar_16_to_9, vsm_interlaced, par_1_to_1, + vif_single_frame_rate, 0, 1), + CEA_861_F_VIC_info_entry(41, 1280, 720, 700, 30, 100000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(42, 720, 576, 144, 49, 100000, cea_iar_4_to_3, + vsm_progressive, par_16_to_15, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(43, 720, 576, 144, 49, 100000, cea_iar_16_to_9, + vsm_progressive, par_64_to_45, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(44, 720, 576, 288, 24, 100000, cea_iar_4_to_3, + vsm_interlaced, par_16_to_15, vif_single_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(45, 720, 576, 288, 24, 100000, cea_iar_16_to_9, + vsm_interlaced, par_64_to_45, vif_single_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(46, 1920, 1080, 280, 22, 120000, + cea_iar_16_to_9, vsm_interlaced, par_1_to_1, + vif_dual_frame_rate, 0, 1), + CEA_861_F_VIC_info_entry(47, 1280, 720, 370, 30, 120000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(48, 720, 480, 138, 45, 120000, cea_iar_4_to_3, + vsm_progressive, par_8_to_9, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(49, 720, 480, 138, 45, 120000, cea_iar_16_to_9, + vsm_progressive, par_32_to_27, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(50, 720, 480, 276, 22, 120000, cea_iar_4_to_3, + vsm_interlaced, par_8_to_9, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(51, 720, 480, 276, 22, 120000, cea_iar_16_to_9, + vsm_interlaced, par_32_to_27, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(52, 720, 576, 144, 49, 200000, cea_iar_4_to_3, + vsm_progressive, par_16_to_15, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(53, 720, 576, 144, 49, 200000, cea_iar_16_to_9, + vsm_progressive, par_64_to_45, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(54, 720, 576, 288, 24, 200000, cea_iar_4_to_3, + vsm_interlaced, par_16_to_15, vif_single_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(55, 720, 576, 288, 24, 200000, cea_iar_16_to_9, + vsm_interlaced, par_64_to_45, vif_single_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(56, 720, 480, 138, 45, 240000, cea_iar_4_to_3, + vsm_progressive, par_8_to_9, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(57, 720, 480, 138, 45, 240000, cea_iar_16_to_9, + vsm_progressive, par_32_to_27, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(58, 720, 480, 276, 22, 240000, cea_iar_4_to_3, + vsm_interlaced, par_8_to_9, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(59, 720, 480, 276, 22, 240000, cea_iar_16_to_9, + vsm_interlaced, par_32_to_27, vif_dual_frame_rate, 1, 1), + CEA_861_F_VIC_info_entry(60, 1280, 720, 2020, 30, 24000, + cea_iar_16_to_9, + vsm_progressive, par_1_to_1, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(61, 1280, 720, 2680, 30, 25000, + cea_iar_16_to_9, + vsm_progressive, par_1_to_1, vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(62, 1280, 720, 2020, 30, 30000, + cea_iar_16_to_9, + vsm_progressive, par_1_to_1, vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(63, 1920, 1080, 280, 45, 120000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(64, 1920, 1080, 720, 45, 100000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(65, 1280, 720, 2020, 30, 24000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(66, 1280, 720, 2680, 30, 25000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(67, 1280, 720, 2020, 30, 30000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(68, 1280, 720, 700, 30, 50000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(69, 1280, 720, 370, 30, 60000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(70, 1280, 720, 700, 30, 100000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(71, 1280, 720, 370, 30, 120000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(72, 1920, 1080, 830, 45, 24000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(73, 1920, 1080, 720, 45, 25000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(74, 1920, 1080, 280, 45, 30000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(75, 1920, 1080, 720, 45, 50000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(76, 1920, 1080, 280, 45, 60000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(77, 1920, 1080, 720, 45, 100000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(78, 1920, 1080, 280, 45, 120000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(79, 1680, 720, 1620, 30, 24000, + cea_iar_64_to_27, vsm_progressive, par_64_to_63, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(80, 1680, 720, 1488, 30, 25000, + cea_iar_64_to_27, vsm_progressive, par_64_to_63, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(81, 1680, 720, 960, 30, 30000, + cea_iar_64_to_27, vsm_progressive, par_64_to_63, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(82, 1680, 720, 520, 30, 50000, + cea_iar_64_to_27, vsm_progressive, par_64_to_63, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(83, 1680, 720, 520, 30, 60000, + cea_iar_64_to_27, vsm_progressive, par_64_to_63, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(84, 1680, 720, 320, 105, 100000, + cea_iar_64_to_27, vsm_progressive, par_64_to_63, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(85, 1680, 720, 320, 105, 120000, + cea_iar_64_to_27, vsm_progressive, par_64_to_63, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(86, 2560, 1080, 1190, 20, 24000, + cea_iar_64_to_27, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(87, 2560, 1080, 640, 45, 25000, + cea_iar_64_to_27, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(88, 2560, 1080, 960, 45, 30000, + cea_iar_64_to_27, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(89, 2560, 1080, 740, 45, 50000, + cea_iar_64_to_27, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(90, 2560, 1080, 440, 20, 60000, + cea_iar_64_to_27, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(91, 2560, 1080, 410, 170, 100000, + cea_iar_64_to_27, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(92, 2560, 1080, 740, 170, 120000, + cea_iar_64_to_27, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(93, 3840, 2160, 1660, 90, 24000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(94, 3840, 2160, 1440, 90, 25000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(95, 3840, 2160, 560, 90, 30000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(96, 3840, 2160, 1440, 90, 50000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(97, 3840, 2160, 560, 90, 60000, + cea_iar_16_to_9, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(98, 4096, 2160, 1404, 90, 24000, + cea_iar_256_to_135, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(99, 4096, 2160, 1184, 90, 25000, + cea_iar_256_to_135, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(100, 4096, 2160, 304, 90, 30000, + cea_iar_256_to_135, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(101, 4096, 2160, 1184, 90, 50000, + cea_iar_256_to_135, vsm_progressive, par_1_to_1, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(102, 4096, 2160, 304, 90, 60000, + cea_iar_256_to_135, vsm_progressive, par_1_to_1, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(103, 3840, 2160, 1660, 90, 24000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(104, 3840, 2160, 1440, 90, 25000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(105, 3840, 2160, 560, 90, 30000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(106, 3840, 2160, 1440, 90, 50000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_single_frame_rate, 0, 0), + CEA_861_F_VIC_info_entry(107, 3840, 2160, 560, 90, 60000, + cea_iar_64_to_27, vsm_progressive, par_4_to_3, + vif_dual_frame_rate, 0, 0) +}; + +uint32_t calculate_pixel_clock(struct edid_3d_data_t *mhl_edid_3d_data, + uint16_t columns, uint16_t rows, + uint32_t vertical_sync_frequency_in_milliHz, uint8_t VIC) +{ + uint32_t pixel_clock_frequency; + uint32_t vertical_sync_period_in_microseconds; + uint32_t vertical_active_period_in_microseconds; + uint32_t vertical_blank_period_in_microseconds; + uint32_t horizontal_sync_frequency_in_hundredths_of_KHz; + uint32_t horizontal_sync_period_in_nanoseconds; + uint32_t horizontal_active_period_in_nanoseconds; + uint32_t horizontal_blank_period_in_nanoseconds; + + MHL_TX_EDID_INFO("vertical_sync_frequency_in_milliHz: %u\n", + vertical_sync_frequency_in_milliHz); + + if (0 == vertical_sync_frequency_in_milliHz) + return 0; + + vertical_sync_period_in_microseconds = + 1000000000 / vertical_sync_frequency_in_milliHz; + MHL_TX_EDID_INFO + ("vertical_sync_frequency_in_milliHz:%u " + "vertical_sync_period_in_microseconds: %u\n", + vertical_sync_frequency_in_milliHz, + vertical_sync_period_in_microseconds); + + VIC &= 0x7F; + if (VIC >= sizeof(VIC_info) / sizeof(VIC_info[0])) { + MHL_TX_DBG_ERR("%sVIC out of range%s\n", ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + return 0; + } + + if (0 == VIC) { + /* rule of thumb: */ + vertical_active_period_in_microseconds = + (vertical_sync_period_in_microseconds * 8) / 10; + + } else { + uint16_t v_total_in_lines; + uint16_t v_blank_in_lines; + + if (vsm_interlaced == VIC_info[VIC].fields.interlaced) { + /* fix up these two values */ + vertical_sync_frequency_in_milliHz /= 2; + vertical_sync_period_in_microseconds *= 2; + MHL_TX_EDID_INFO + ("interlaced vertical_sync_frequency_in_milliHz:%u" + " vertical_sync_period_in_microseconds: %u\n", + vertical_sync_frequency_in_milliHz, + vertical_sync_period_in_microseconds); + + /* proceed with calculations */ + v_blank_in_lines = + 2 * VIC_info[VIC].v_blank_in_pixels + + VIC_info[VIC].fields.field2_v_blank; + + } else { + /* when multiple vertical blanking values present, + * allow for higher clocks by calculating maximum + * possible + */ + v_blank_in_lines = + VIC_info[VIC].v_blank_in_pixels + + VIC_info[VIC].fields.field2_v_blank; + } + v_total_in_lines = VIC_info[VIC].rows + v_blank_in_lines; + vertical_active_period_in_microseconds = + (vertical_sync_period_in_microseconds * + VIC_info[VIC].rows) / v_total_in_lines; + + } + MHL_TX_EDID_INFO("vertical_active_period_in_microseconds: %u\n", + vertical_active_period_in_microseconds); + + /* rigorous calculation: */ + vertical_blank_period_in_microseconds = + vertical_sync_period_in_microseconds - + vertical_active_period_in_microseconds; + MHL_TX_EDID_INFO("vertical_blank_period_in_microseconds: %u\n", + vertical_blank_period_in_microseconds); + + horizontal_sync_frequency_in_hundredths_of_KHz = rows * 100000; + horizontal_sync_frequency_in_hundredths_of_KHz /= + vertical_active_period_in_microseconds; + + MHL_TX_EDID_INFO("horizontal_sync_frequency_in_hundredths_of_KHz: %u\n", + horizontal_sync_frequency_in_hundredths_of_KHz); + + horizontal_sync_period_in_nanoseconds = + 100000000 / horizontal_sync_frequency_in_hundredths_of_KHz; + + MHL_TX_EDID_INFO("horizontal_sync_period_in_nanoseconds: %u\n", + horizontal_sync_period_in_nanoseconds); + + if (0 == VIC) { + /* rule of thumb: */ + horizontal_active_period_in_nanoseconds = + (horizontal_sync_period_in_nanoseconds * 8) / 10; + MHL_TX_EDID_INFO + ("horizontal_active_period_in_nanoseconds: %u\n", + horizontal_active_period_in_nanoseconds); + } else { + uint16_t h_total_in_pixels; + uint16_t h_clocks; + h_clocks = + VIC_info[VIC].columns << VIC_info[VIC].fields. + clocks_per_pixel_shift_count; + h_total_in_pixels = h_clocks + VIC_info[VIC].h_blank_in_pixels; + horizontal_active_period_in_nanoseconds = + (horizontal_sync_period_in_nanoseconds * h_clocks) / + h_total_in_pixels; + } + /* rigorous calculation: */ + horizontal_blank_period_in_nanoseconds = + horizontal_sync_period_in_nanoseconds - + horizontal_active_period_in_nanoseconds; + MHL_TX_EDID_INFO("horizontal_blank_period_in_nanoseconds: %u\n", + horizontal_blank_period_in_nanoseconds); + + pixel_clock_frequency = + columns * (1000000000 / horizontal_active_period_in_nanoseconds); + + MHL_TX_EDID_INFO("pixel_clock_frequency: %u\n", pixel_clock_frequency); + + return pixel_clock_frequency; +} + +uint8_t qualify_pixel_clock_for_mhl(struct edid_3d_data_t *mhl_edid_3d_data, + uint32_t pixel_clock_frequency, uint8_t bits_per_pixel) +{ + uint32_t link_clock_frequency; + uint32_t max_link_clock_frequency = 0; + uint8_t ret_val; + int peer_is_mhl3; + + peer_is_mhl3 = + si_mhl_tx_drv_connection_is_mhl3(mhl_edid_3d_data->dev_context); + if (16 != bits_per_pixel) { + MHL_TX_EDID_INFO("not 16 bpp\n") + ; + } else if (!si_edid_sink_supports_YCbCr422(mhl_edid_3d_data)) { + MHL_TX_EDID_INFO("no EDID 4:2:2\n") + return 0; + } else if (peer_is_mhl3) { + MHL_TX_EDID_INFO("peer is MHL3\n") + if (!_16_BPP_AVAILABLE(mhl_edid_3d_data->dev_context)) { + MHL_TX_EDID_INFO("no DEVCAP 16bpp\n") + return 0; + } + } else { + MHL_TX_EDID_INFO("peer is not MHL3\n") + if (!PACKED_PIXEL_AVAILABLE(mhl_edid_3d_data->dev_context)) { + MHL_TX_EDID_INFO("no DEVCAP packed pixel\n") + return 0; + } + } + + link_clock_frequency = + pixel_clock_frequency * ((uint32_t) (bits_per_pixel >> 3)); + + if (peer_is_mhl3) { + switch (si_mhl_tx_drv_get_highest_tmds_link_speed + (mhl_edid_3d_data->dev_context)) { + case MHL_XDC_TMDS_600: + MHL_TX_EDID_INFO("MHL_XDC_TMDS_600\n"); + max_link_clock_frequency = 600000000; + break; + case MHL_XDC_TMDS_300: + MHL_TX_EDID_INFO("MHL_XDC_TMDS_300\n"); + max_link_clock_frequency = 300000000; + break; + case MHL_XDC_TMDS_150: + MHL_TX_EDID_INFO("MHL_XDC_TMDS_150\n"); + max_link_clock_frequency = 150000000; + break; + case MHL_XDC_TMDS_000: + max_link_clock_frequency = 000000000; + break; + } + } else { + if (si_peer_supports_packed_pixel + (mhl_edid_3d_data->dev_context)) { + max_link_clock_frequency = 300000000; + } else { + max_link_clock_frequency = 225000000; + } + } + + if (link_clock_frequency < max_link_clock_frequency) + ret_val = 1; + else + ret_val = 0; + + MHL_TX_EDID_INFO + ("Link clock:%u Hz %12s for MHL at %d bpp (max: %u Hz)\n", + link_clock_frequency, ret_val ? "valid" : "unattainable", + (uint16_t) bits_per_pixel, max_link_clock_frequency); + return ret_val; +} + +/* + is_MHL_timing_mode + + MHL has a maximum link clock of 75Mhz. + For now, we use a rule of thumb regarding + blanking intervals to calculate a pixel clock, + then we convert it to a link clock and compare to 75MHz + +*/ + +static uint8_t is_MHL_timing_mode(struct edid_3d_data_t *mhl_edid_3d_data, + uint16_t columns, uint16_t rows, + uint32_t vertical_sync_frequency_in_milliHz, uint16_t burst_id, + union video_burst_descriptor_u *p_descriptor, uint8_t VIC) +{ + uint32_t pixel_clock_frequency; + uint8_t ret_val = 0; + + if (p_descriptor) { + if (burst_id_HEV_DTDB == burst_id) { + struct MHL3_hev_dtd_item_t *descriptor = + &p_descriptor->mhl3_hev_dtd; + pixel_clock_frequency = + 1000000 * + (uint32_t) ENDIAN_CONVERT_16(descriptor->a. + pixel_clock_in_MHz); + columns = + ENDIAN_CONVERT_16(descriptor->a.h_active_in_pixels); + rows = ENDIAN_CONVERT_16(descriptor->b.v_total_in_lines) + - descriptor->b.v_blank_in_lines; + vertical_sync_frequency_in_milliHz = + 1000 * + descriptor->b.v_refresh_rate_in_fields_per_second; + } else { + pixel_clock_frequency = calculate_pixel_clock( + mhl_edid_3d_data, columns, rows, + vertical_sync_frequency_in_milliHz, VIC); + } + } else { + pixel_clock_frequency = calculate_pixel_clock(mhl_edid_3d_data, + columns, rows, vertical_sync_frequency_in_milliHz, VIC); + } + if (qualify_pixel_clock_for_mhl(mhl_edid_3d_data, pixel_clock_frequency, + 24)) { + + display_timing_enumeration_callback(mhl_edid_3d_data, columns, + rows, 24, vertical_sync_frequency_in_milliHz, burst_id, + p_descriptor); + ret_val = 1; + } + if (qualify_pixel_clock_for_mhl(mhl_edid_3d_data, pixel_clock_frequency, + 16)) { + + display_timing_enumeration_callback(mhl_edid_3d_data, columns, + rows, 16, vertical_sync_frequency_in_milliHz, burst_id, + p_descriptor); + ret_val = 1; + } + + return ret_val; +} + +static void tx_prune_dtd_list(struct edid_3d_data_t *mhl_edid_3d_data, + union _18_byte_descriptor_u *p_desc, uint8_t limit) +{ + uint8_t i; + uint8_t number_that_we_pruned = 0; + union _18_byte_descriptor_u *p_start = p_desc; + MHL_TX_EDID_INFO("limit: %d\n", (uint16_t) limit); + if (limit) { + for (i = 0; i < limit - 1; ++i) { + MHL_TX_EDID_INFO("i: %d\n", (uint16_t) i); + if ((0 != p_desc->dtd.pixel_clock_low) || + (0 != p_desc->dtd.pixel_clock_high)) { + MHL_TX_EDID_INFO( + "pix clock non-zero p_desc:%p", p_desc) + if ((0 == p_desc->dtd.horz_active_7_0) && + (0 == p_desc->dtd.horz_active_blanking_high. + horz_active_11_8)) { + union _18_byte_descriptor_u + *p_holder = p_desc, + *p_next_desc = p_desc + 1; + uint8_t j; + MHL_TX_DBG_INFO("pruning\n"); + number_that_we_pruned++; + for (j = i + 1; j < limit; ++j) { + /* move the rest of the entries + * one by one + */ + MHL_TX_EDID_INFO( + "p_desc:%p p_next_desc:%p\n", + p_desc, p_next_desc) + *p_desc++ = *p_next_desc++; + } + /* re-consider the new occupant of the + * i'th entry on the next iteration + */ + i--; + p_desc = p_holder; + } else { + p_desc++; + MHL_TX_EDID_INFO("p_desc:%p\n", p_desc) + } + } + } + + p_desc = p_start + (limit - number_that_we_pruned); + for (; number_that_we_pruned > 0; + --number_that_we_pruned, --p_desc) { + uint8_t *pu8_temp = (uint8_t *) p_desc; + uint8_t size; + + for (size = sizeof(*p_desc); size > 0; --size) + *pu8_temp++ = 0; + } + } +} + +/* + FUNCTION : si_mhl_tx_parse_detailed_timing_descriptor() + PURPOSE : Parse the detailed timing section of EDID Block 0 and + print their decoded meaning to the screen. + INPUT PARAMS : Pointer to the array where the data read from EDID + Block0 is stored. + Offset to the beginning of the Detailed Timing Descriptor + data. + Block indicator to distinguish between block #0 and blocks + #2, #3 + + OUTPUT PARAMS: None + GLOBALS USED : None + RETURNS : true if valid timing, false if not +*/ + +static bool si_mhl_tx_parse_detailed_timing_descriptor( + struct edid_3d_data_t *mhl_edid_3d_data, + union _18_byte_descriptor_u *p_desc, uint8_t Block, + uint8_t *p_is_timing, uint16_t burst_id, + union video_burst_descriptor_u *p_descriptor) +{ + uint8_t tmp_byte; + uint8_t i; + uint16_t tmp_word; + + *p_is_timing = 0; + tmp_word = + p_desc->dtd.pixel_clock_low + 256 * p_desc->dtd.pixel_clock_high; + /* 18 byte partition is used as either for Monitor Name or for Monitor + * Range Limits or it is unused + */ + if (tmp_word == 0x00) { + /* if called from Block #0 and first 2 bytes are 0 => either + * Monitor Name or for Monitor Range Limits + */ + if (Block == EDID_BLOCK_0) { + if (0xFC == p_desc->name.data_type_tag) { + char monitor_name[14]; + + for (i = 0; i < 13; i++) { + monitor_name[i] = + p_desc->name.ascii_name[i]; + } + monitor_name[13] = '\0'; + MHL_TX_EDID_INFO("Monitor Name:\"%s\"\n", + monitor_name); + } else if (0xFD == p_desc->name.data_type_tag) { + MHL_TX_EDID_INFO("Monitor Range Limits:\n\n"); + + /*i = 0;*/ + MHL_TX_EDID_INFO( + "Min Vertical Rate in Hz: %d\n", + (int)p_desc->range_limits. + min_vertical_rate_in_Hz); + MHL_TX_EDID_INFO( + "Max Vertical Rate in Hz: %d\n", + (int)p_desc->range_limits. + max_vertical_rate_in_Hz); + MHL_TX_EDID_INFO( + "Min Horizontal Rate in KHz: %d\n", + (int)p_desc->range_limits. + min_horizontal_rate_in_KHz); + MHL_TX_EDID_INFO( + "Max Horizontal Rate in KHz: %d\n", + (int)p_desc->range_limits. + max_horizontal_rate_in_KHz); + MHL_TX_EDID_INFO( + "Max Supported pixel clock rate in " + "MHz/10: %d\n", + (int)p_desc->range_limits. + max_pixel_clock_in_MHz_div_10); + MHL_TX_EDID_INFO( + "Tag for secondary timing formula " + "(00h=not used): %d\n", + (int)p_desc->range_limits. + tag_secondary_formula); + MHL_TX_EDID_INFO("\n"); + } + } else if (Block == EDID_BLOCK_2_3) { + /* if called from block #2 or #3 and first 2 bytes are + * 0x00 (padding) then this descriptor partition is not + * used and parsing should be stopped + */ + MHL_TX_EDID_INFO + ("No More Detailed descriptors in this block\n"); + MHL_TX_EDID_INFO("\n"); + return false; + } + } else { + /* first 2 bytes are not 0 => this is a detailed timing + * descriptor from either block + */ + uint32_t pixel_clock_frequency; + uint16_t columns, rows, vertical_sync_period_in_lines; + uint32_t vertical_refresh_rate_in_milliHz, + horizontal_sync_frequency_in_Hz, + horizontal_sync_period_in_pixels; + uint8_t this_mode_doable = 0; + + *p_is_timing = 1; + + pixel_clock_frequency = (uint32_t) tmp_word * 10000; + + MHL_TX_EDID_INFO + ("Pixel Clock: %d.%02d MHz or %d Hz (0x%x Hz)\n", + tmp_word / 100, tmp_word % 100, pixel_clock_frequency, + pixel_clock_frequency); + + columns = + p_desc->dtd.horz_active_7_0 + + 256 * + p_desc->dtd.horz_active_blanking_high.horz_active_11_8; + MHL_TX_EDID_INFO("Horizontal Active Pixels: %d\n", columns); + + tmp_word = + p_desc->dtd.horz_blanking_7_0 + + 256 * + p_desc->dtd.horz_active_blanking_high.horz_blanking_11_8; + MHL_TX_EDID_INFO("Horizontal Blanking (Pixels): %d\n", + tmp_word); + + horizontal_sync_period_in_pixels = + (uint32_t) columns + (uint32_t) tmp_word; + horizontal_sync_frequency_in_Hz = + pixel_clock_frequency / horizontal_sync_period_in_pixels; + + MHL_TX_EDID_INFO("horizontal period %u pixels, " + "horizontal_sync_frequency_in_Hz: %u Hz\n", + horizontal_sync_period_in_pixels, + horizontal_sync_frequency_in_Hz); + + rows = + p_desc->dtd.vert_active_7_0 + + 256 * + p_desc->dtd.vert_active_blanking_high.vert_active_11_8; + MHL_TX_EDID_INFO("Vertical Active (Lines): %u\n", rows); + + tmp_word = + p_desc->dtd.vert_blanking_7_0 + + 256 * + p_desc->dtd.vert_active_blanking_high.vert_blanking_11_8; + MHL_TX_EDID_INFO("Vertical Blanking (Lines): %u\n", tmp_word); + + vertical_sync_period_in_lines = rows + tmp_word; + vertical_refresh_rate_in_milliHz = + horizontal_sync_frequency_in_Hz * 1000 / + (vertical_sync_period_in_lines); + MHL_TX_EDID_INFO + ("vertical period %u lines, frequency %u MilliHz\n", + vertical_sync_period_in_lines, + vertical_refresh_rate_in_milliHz); + + tmp_word = + p_desc->dtd.horz_sync_offset_7_0 + + 256 * p_desc->dtd.hs_vs_offset_pulse_width. + horz_sync_offset_9_8; + MHL_TX_EDID_INFO("Horizontal Sync Offset (Pixels): %d\n", + tmp_word); + + tmp_word = + p_desc->dtd.horz_sync_pulse_width7_0 + + 256 * p_desc->dtd.hs_vs_offset_pulse_width. + horz_sync_pulse_width_9_8; + MHL_TX_EDID_INFO("Horizontal Sync Pulse Width (Pixels): %d\n", + tmp_word); + + tmp_word = + p_desc->dtd.vert_sync_offset_width.vert_sync_offset_3_0 + + 16 * p_desc->dtd.hs_vs_offset_pulse_width. + vert_sync_offset_5_4; + MHL_TX_EDID_INFO("Vertical Sync Offset (Lines): %d\n", + tmp_word); + + tmp_word = + p_desc->dtd.vert_sync_offset_width. + vert_sync_pulse_width_3_0 + + 16 * p_desc->dtd.hs_vs_offset_pulse_width. + vert_sync_pulse_width_5_4; + MHL_TX_EDID_INFO("Vertical Sync Pulse Width (Lines): %d\n", + tmp_word); + + tmp_word = p_desc->dtd.horz_image_size_in_mm_7_0 + + + 256 * + p_desc->dtd.image_size_high.horz_image_size_in_mm_11_8; + MHL_TX_EDID_INFO("Horizontal Image Size (mm): %d\n", tmp_word); + + tmp_word = p_desc->dtd.vert_image_size_in_mm_7_0 + + + 256 * + p_desc->dtd.image_size_high.vert_image_size_in_mm_11_8; + MHL_TX_EDID_INFO("Vertical Image Size (mm): %d\n", tmp_word); + + tmp_byte = p_desc->dtd.horz_border_in_lines; + MHL_TX_EDID_INFO("Horizontal Border (Pixels): %d\n", + (int)tmp_byte); + + tmp_byte = p_desc->dtd.vert_border_in_pixels; + MHL_TX_EDID_INFO("Vertical Border (Lines): %d\n", + (int)tmp_byte); + + MHL_TX_EDID_INFO("%snterlaced\n", p_desc->dtd.flags.interlaced + ? "I" : "Non-i"); + + switch (p_desc->dtd.flags.stereo_bits_2_1) { + case 0: + MHL_TX_EDID_INFO("Normal Display, No Stereo\n"); + break; + case 1: + if (0 == p_desc->dtd.flags.stereo_bit_0) { + MHL_TX_EDID_INFO + ("Field sequential stereo, right image " + "when stereo sync signal == 1\n"); + } else { + MHL_TX_EDID_INFO + ("2-way interleaved stereo, right image " + "on even lines\n"); + } + break; + case 2: + if (0 == p_desc->dtd.flags.stereo_bit_0) { + MHL_TX_EDID_INFO + ("field-sequential stereo, left image " + "when stereo sync signal == 1\n"); + } else { + MHL_TX_EDID_INFO + ("2-way interleaved stereo, left image on " + "even lines.\n"); + } + break; + case 3: + if (0 == p_desc->dtd.flags.stereo_bit_0) { + MHL_TX_EDID_INFO("4-way interleaved stereo\n"); + } else { + MHL_TX_EDID_INFO + ("side-by-side interleaved stereo.\n"); + } + break; + } + + switch (p_desc->dtd.flags.sync_signal_type) { + case 0x0: + MHL_TX_EDID_INFO("Analog Composite\n"); + break; + case 0x1: + MHL_TX_EDID_INFO("Bipolar Analog Composite\n"); + break; + case 0x2: + MHL_TX_EDID_INFO("Digital Composite\n"); + break; + case 0x3: + MHL_TX_EDID_INFO("Digital Separate\n"); + break; + } + + this_mode_doable = qualify_pixel_clock_for_mhl( + (void *)mhl_edid_3d_data, + pixel_clock_frequency, 24); + if (this_mode_doable) { + if (p_descriptor) { + display_timing_enumeration_callback( + mhl_edid_3d_data, columns, rows, 24, + vertical_refresh_rate_in_milliHz, + burst_id, p_descriptor); + } + } + + this_mode_doable |= qualify_pixel_clock_for_mhl( + (void *)mhl_edid_3d_data, + pixel_clock_frequency, 16); + if (this_mode_doable) { + if (p_descriptor) { + display_timing_enumeration_callback( + mhl_edid_3d_data, columns, rows, 16, + vertical_refresh_rate_in_milliHz, + burst_id, p_descriptor); + } + } + + if (!this_mode_doable) { + /* + * Mark this mode for pruning by setting + * horizontal active to zero + */ + MHL_TX_DBG_ERR("%smark for pruning%s %p\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT, + p_desc); + p_desc->dtd.horz_active_7_0 = 0; + p_desc->dtd.horz_active_blanking_high. + horz_active_11_8 = 0; + return false; + } + } + return true; +} + +static uint8_t si_mhl_tx_parse_861_long_descriptors( + struct edid_3d_data_t *mhl_edid_3d_data, uint8_t *p_EDID_block_data) +{ + struct CEA_extension_t *p_CEA_ext = + (struct CEA_extension_t *) p_EDID_block_data; + + /* per CEA-861-D, table 27 */ + if (!p_CEA_ext->byte_offset_to_18_byte_descriptors) { + MHL_TX_DBG_ERR("EDID -> No Detailed Descriptors\n"); + return EDID_NO_DETAILED_DESCRIPTORS; + } else { + uint8_t *puc_next_block; + uint8_t descriptor_num = 1; + union { + uint8_t *puc_data_block; + union _18_byte_descriptor_u *p_long_descriptors; + } p_data_u; + + p_data_u.p_long_descriptors = + (union _18_byte_descriptor_u *)(((uint8_t *)p_CEA_ext) + + p_CEA_ext->byte_offset_to_18_byte_descriptors); + puc_next_block = + ((uint8_t *)p_CEA_ext) + EDID_BLOCK_SIZE; + + while ((uint8_t *) (p_data_u.p_long_descriptors + 1) < + puc_next_block) { + uint8_t is_timing = 0; + bool valid; + MHL_TX_EDID_INFO( + "Parse Results - CEA-861 Long Descriptor #%d:\n", + (int)descriptor_num); + MHL_TX_EDID_INFO( + "============================================\n"); + + valid = si_mhl_tx_parse_detailed_timing_descriptor + (mhl_edid_3d_data, p_data_u.p_long_descriptors, + EDID_BLOCK_2_3, &is_timing, 0, NULL); + if (is_timing) { + ++mhl_edid_3d_data->parse_data. + num_cea_861_timing_dtds; + } else if (valid) { + MHL_TX_EDID_INFO("stopping at %p\n", + p_data_u.p_long_descriptors) + break; + } + p_data_u.p_long_descriptors++; + descriptor_num++; + } + + return EDID_LONG_DESCRIPTORS_OK; + } +} + +static void prune_hdmi_vsdb_vic_list( + struct edid_3d_data_t *mhl_edid_3d_data, + struct CEA_extension_t *p_CEA_extension, + uint8_t length_VIC) +{ + uint8_t i, num_HDMI_VICs_pruned = 0; + uint8_t inner_loop_limit = length_VIC; + uint8_t outer_loop_limit = length_VIC - 1; + uint8_t *pb_limit = (uint8_t *)(p_CEA_extension + 1); + + MHL_TX_EDID_INFO( + "len_VIC:%d inner_loop_limit: %d outer_loop_limit: %d\n", + (uint16_t) length_VIC, (uint16_t) inner_loop_limit, + (uint16_t) outer_loop_limit); + for (i = 0; i < outer_loop_limit;) { + if (0 == mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->vicList[i]) { + uint8_t j, prev; + + prev = i; + for (j = i + 1; j < inner_loop_limit; ++j, ++prev) { + uint16_t VIC0, VIC1; + + VIC0 = mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->vicList[prev]; + VIC1 = + mhl_edid_3d_data-> + parse_data. + p_byte_13_through_byte_15-> + vicList[j]; + MHL_TX_EDID_INFO( + "replacing VIC: %3d at index: %3d" + " with VIC:%3d from index: %3d\n", + VIC0, (uint16_t) prev, + VIC1, (uint16_t) j); + mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15-> + vicList[prev] + = mhl_edid_3d_data-> + parse_data. + p_byte_13_through_byte_15-> + vicList[j]; + } + num_HDMI_VICs_pruned++; + inner_loop_limit--; + outer_loop_limit--; + } else { + /* This mode is doable on MHL, + * so move on to the next index + */ + ++i; + } + } + /* check the last one */ + if (0 == mhl_edid_3d_data->parse_data.p_byte_13_through_byte_15-> + vicList[outer_loop_limit]) { + num_HDMI_VICs_pruned++; + inner_loop_limit--; + } + + DUMP_EDID_BLOCK(0, p_CEA_extension, sizeof(*p_CEA_extension)); + /* now move all other data up */ + if (num_HDMI_VICs_pruned) { + uint8_t *pb_dest = (uint8_t *) &mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->vicList[inner_loop_limit]; + uint8_t *pb_src = (uint8_t *) &mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->vicList[length_VIC]; + + SII_ASSERT(EDID_BLOCK_SIZE == sizeof(*p_CEA_extension), + ("\n\n unexpected extension size\n\n")); + while (pb_src < pb_limit) { + MHL_TX_EDID_INFO( + "moving data up %02x(0x%02x) <- %02x(0x%02x)\n", + pb_dest, (uint16_t) *pb_dest, + pb_src, (uint16_t) *pb_src); + *pb_dest++ = *pb_src++; + } + + while (pb_dest < pb_limit) { + MHL_TX_EDID_INFO("clearing data %02x <- 0\n", pb_dest); + *pb_dest++ = 0; + } + } + + mhl_edid_3d_data->parse_data.p_byte_13_through_byte_15->byte14. + HDMI_VIC_len = inner_loop_limit; + p_CEA_extension->byte_offset_to_18_byte_descriptors -= + num_HDMI_VICs_pruned; + MHL_TX_EDID_INFO("%p\n", mhl_edid_3d_data->parse_data.p_HDMI_vsdb); + if (mhl_edid_3d_data->parse_data.p_HDMI_vsdb) { + mhl_edid_3d_data->parse_data.p_HDMI_vsdb-> + header.fields.length_following_header -= + num_HDMI_VICs_pruned; + } else if (num_HDMI_VICs_pruned) { + MHL_TX_DBG_ERR("HDMI VICs to prune but no HDMI VSDB!\n"); + } +} + + +static void inner_loop( + struct edid_3d_data_t *mhl_edid_3d_data, + int outer_index, int vdb_index, int limit) +{ + uint8_t j, prev; + + prev = outer_index; + for (j = outer_index + 1; j < limit; ++j, ++prev) { + uint16_t VIC0, VIC1; + + VIC0 = mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d[vdb_index]-> + short_descriptors[prev].VIC; + VIC1 = mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d[vdb_index]-> + short_descriptors[j].VIC; + MHL_TX_EDID_INFO( + "Replacing SVD:%6s 0x%02x at index: 0x%02x with " + "SVD:%6s 0x%02x from index: 0x%02x\n", + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[prev].native ? "Native" : "", + VIC0, (uint16_t)prev, + mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d[vdb_index]-> + short_descriptors[j].native ? "Native" : "", + VIC1, (uint16_t) j); + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[prev] = mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]->short_descriptors[j]; + } +} + +static void prune_svd_list( + struct edid_3d_data_t *mhl_edid_3d_data, + struct CEA_extension_t *p_CEA_extension) +{ + uint8_t i, num_CEA_VICs_pruned = 0; + uint8_t *pb_limit = (uint8_t *)(p_CEA_extension + 1); + + /* + pack each vdb to eliminate the bytes that have been zeroed. + */ + int8_t vdb_index; + for (vdb_index = mhl_edid_3d_data->parse_data.num_video_data_blocks - 1; + vdb_index >= 0; --vdb_index) { + uint8_t inner_loop_limit = mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]->header.fields. + length_following_header; + if (inner_loop_limit) { + uint8_t outer_loop_limit = (inner_loop_limit - 1); + + for (i = 0; i < outer_loop_limit;) { + if (0 == mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[i].VIC) { + num_CEA_VICs_pruned++; + inner_loop(mhl_edid_3d_data, + i, vdb_index, + inner_loop_limit); + inner_loop_limit--; + outer_loop_limit--; + MHL_TX_EDID_INFO( + "outer_limit:%d inner_limit:%d\n", + (uint16_t)outer_loop_limit, + (uint16_t)inner_loop_limit); + } else { + /* This mode is doable on MHL, + * so move on to the next index. + */ + ++i; + } + } + /* check the last one */ + if (0 == mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[outer_loop_limit].VIC) { + num_CEA_VICs_pruned++; + inner_loop_limit--; + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[outer_loop_limit]. + native = 0; + } + + DUMP_EDID_BLOCK(0, p_CEA_extension, + sizeof(*p_CEA_extension)); + { + /* now move all other data up */ + uint8_t *pb_dest = + (uint8_t *) &mhl_edid_3d_data-> + parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[inner_loop_limit]; + uint8_t *pb_src = + (uint8_t *) &mhl_edid_3d_data-> + parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[mhl_edid_3d_data-> + parse_data. + p_video_data_blocks_2d + [vdb_index]-> + header.fields. + length_following_header]; + + SII_ASSERT(EDID_BLOCK_SIZE == + sizeof(*p_CEA_extension), + ("\n\nInvalid extension size\n\n")); + while (pb_src < pb_limit) { + MHL_TX_EDID_INFO( + "moving data up %p(0x%02X) " + "<- %p(0x%02X)\n", + pb_dest, (uint16_t)*pb_dest, + pb_src, (uint16_t)*pb_src); + *pb_dest++ = *pb_src++; + } + + while (pb_dest < pb_limit) { + MHL_TX_EDID_INFO( + "clearing data %02X <- 0\n", + *pb_dest); + *pb_dest++ = 0; + } + + } + MHL_TX_EDID_INFO + ("CEA-861-D DTDs began at 0x%02x" + "CEA-861-D SVD count: 0x%x\n", + (uint16_t) p_CEA_extension-> + byte_offset_to_18_byte_descriptors, + (uint16_t) mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]->header. + fields.length_following_header); + + p_CEA_extension->byte_offset_to_18_byte_descriptors -= + num_CEA_VICs_pruned; + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]->header. + fields.length_following_header = + inner_loop_limit; + + MHL_TX_EDID_INFO + ("CEA-861-D DTDs now begin at 0x%02x" + "CEA-861-D SVD count: 0x%x\n", + (uint16_t) p_CEA_extension-> + byte_offset_to_18_byte_descriptors, + (uint16_t) mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]->header. + fields.length_following_header); + + DUMP_EDID_BLOCK(0, p_CEA_extension, + sizeof(*p_CEA_extension)); + } + } +} + +static void si_mhl_tx_prune_edid(struct edid_3d_data_t *mhl_edid_3d_data) +{ + struct EDID_block0_t *p_EDID_block_0 = + (struct EDID_block0_t *) &mhl_edid_3d_data->EDID_block_data[0]; + uint8_t dtd_limit; + struct CEA_extension_t *p_CEA_extension = + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE]; + struct block_map_t *p_block_map = NULL; + uint8_t *pb_limit; + union { + uint8_t *puc_data_block; + union _18_byte_descriptor_u *p_long_descriptors; + } p_data_u; + + if (EDID_EXTENSION_BLOCK_MAP == p_CEA_extension->tag) { + /* save to overwrite later */ + p_block_map = (struct block_map_t *) p_CEA_extension; + + /* advance to next block */ + p_CEA_extension++; + } + pb_limit = (uint8_t *) (p_CEA_extension + 1); + + MHL_TX_EDID_INFO("mhl_tx: %s\n", __func__); + p_data_u.puc_data_block = (uint8_t *) p_CEA_extension + + p_CEA_extension->byte_offset_to_18_byte_descriptors; + + DUMP_EDID_BLOCK(0, p_EDID_block_0, sizeof(*p_EDID_block_0)); + + /* zero out checksums before modifying data */ + p_CEA_extension->checksum = 0; + p_EDID_block_0->checksum = 0; + + /* Is there an HDMI VSDB? */ + if (mhl_edid_3d_data->parse_data.p_HDMI_vsdb) { + struct HDMI_LLC_vsdb_payload_t *p_HDMI_vendor_specific_payload = + &mhl_edid_3d_data->parse_data.p_HDMI_vsdb->payload_u. + HDMI_LLC; + uint8_t *p_next_db = + (uint8_t *) p_HDMI_vendor_specific_payload + + mhl_edid_3d_data->parse_data.p_HDMI_vsdb->header.fields. + length_following_header; + + /* if deep color information is provided... */ + if (((uint8_t *) &p_HDMI_vendor_specific_payload->byte6) < + p_next_db) { + p_HDMI_vendor_specific_payload->byte6.DC_Y444 = 0; + p_HDMI_vendor_specific_payload->byte6.DC_30bit = 0; + p_HDMI_vendor_specific_payload->byte6.DC_36bit = 0; + p_HDMI_vendor_specific_payload->byte6.DC_48bit = 0; + } + } + /* prune the DTDs in block 0 */ + dtd_limit = + sizeof(p_EDID_block_0->detailed_timing_descriptors) / + sizeof(p_EDID_block_0->detailed_timing_descriptors[0]); + tx_prune_dtd_list(mhl_edid_3d_data, + (union _18_byte_descriptor_u *) &p_EDID_block_0-> + detailed_timing_descriptors[0], dtd_limit); + DUMP_EDID_BLOCK(0, p_EDID_block_0, sizeof(*p_EDID_block_0)); + DUMP_EDID_BLOCK(0, p_CEA_extension, sizeof(*p_CEA_extension)); + + /* prune the DTDs in the CEA-861D extension */ +#if 0 + dtd_limit = (uint8_t) p_CEA_extension->version_u.version3.misc_support. + total_number_native_dtds_in_entire_EDID; +#else + + dtd_limit = (EDID_BLOCK_SIZE + - p_CEA_extension->byte_offset_to_18_byte_descriptors) + / sizeof(p_data_u.p_long_descriptors[0]) ; +#endif + tx_prune_dtd_list(mhl_edid_3d_data, + &p_data_u.p_long_descriptors[0], dtd_limit); + DUMP_EDID_BLOCK(0, p_CEA_extension, sizeof(*p_CEA_extension)); + /* adjust the mask according to which 2D VICs were set to zero */ + if (mhl_edid_3d_data->parse_data.p_3d_mask) { + uint8_t lower_mask; + uint32_t mask32; + int8_t index = + mhl_edid_3d_data->parse_data.p_video_data_blocks_2d[0]-> + header.fields.length_following_header - 1; + index = (index > 15) ? 15 : index; + + mask32 = 0xFFFF00 >> (15 - index); + lower_mask = (index > 7) ? 0x7F : (0x7F >> (7 - index)); + + MHL_TX_EDID_INFO("3d mask 15..8: 0x%02x", + (uint16_t) mhl_edid_3d_data->parse_data. + p_3d_mask->_3D_mask_15_8); + for (; index >= 8; mask32 >>= 1, lower_mask >>= 1, --index) { + if (0 == mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[0]->short_descriptors[index]. + VIC) { + uint8_t lower_bits, upper_bits; + uint8_t upper_mask; + upper_mask = (uint8_t) mask32; + + /* preserve the lower bits */ + lower_bits = + lower_mask & mhl_edid_3d_data->parse_data. + p_3d_mask->_3D_mask_15_8; + + /* and out the bit in question */ + upper_bits = + upper_mask & mhl_edid_3d_data->parse_data. + p_3d_mask->_3D_mask_15_8; + + /* adjust the positions of the upper bits */ + upper_bits >>= 1; + + mhl_edid_3d_data->parse_data.p_3d_mask-> + _3D_mask_15_8 = lower_bits | upper_bits; + MHL_TX_EDID_INFO("3d mask 15..8: 0x%02x", + (uint16_t) mhl_edid_3d_data-> + parse_data.p_3d_mask-> + _3D_mask_15_8); + } + } + MHL_TX_EDID_INFO("3d mask 7..0: 0x%02x", + (uint16_t) mhl_edid_3d_data->parse_data. + p_3d_mask->_3D_mask_7_0); + lower_mask = 0x7F >> (7 - index); + for (; index >= 0; mask32 >>= 1, lower_mask >>= 1, --index) { + if (0 == mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[0]-> + short_descriptors[index].VIC) { + uint8_t lower_bits, upper_bits; + uint8_t upper_mask; + upper_mask = (uint8_t) mask32; + + /* preserve the lower bits */ + lower_bits = + lower_mask & mhl_edid_3d_data->parse_data. + p_3d_mask->_3D_mask_7_0; + + /* AND out the bit in question */ + upper_bits = + upper_mask & mhl_edid_3d_data->parse_data. + p_3d_mask->_3D_mask_7_0; + + /* adjust the positions of the upper bits */ + upper_bits >>= 1; + + mhl_edid_3d_data->parse_data.p_3d_mask-> + _3D_mask_7_0 = lower_bits | upper_bits; + MHL_TX_EDID_INFO("3d mask 7..0: 0x%02x\n", + (uint16_t) mhl_edid_3d_data-> + parse_data.p_3d_mask-> + _3D_mask_7_0); + } + } + } + + if (mhl_edid_3d_data->parse_data.p_three_d) { + uint8_t num_3D_structure_bytes_pruned = 0; + union { + union _3D_structure_and_detail_entry_u *p_3D; + struct _3D_structure_and_detail_entry_sans_byte1_t + *p_sans_byte_1; + struct _3D_structure_and_detail_entry_with_byte1_t + *p_with_byte_1; + uint8_t *p_as_bytes; + } p_3D_u; + uint32_t deletion_mask = 0; + uint8_t limit_2D_VIC = + mhl_edid_3d_data->parse_data.p_video_data_blocks_2d[0]-> + header.fields.length_following_header; + /* + * Prior to moving things around, make a bitmap of + * the positions of the VICs that are zero + */ + { + uint8_t i; + uint32_t this_bit; + for (i = 0, this_bit = 1; i < limit_2D_VIC; + ++i, this_bit <<= 1) { + uint8_t VIC; + VIC = + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[0]-> + short_descriptors[i].VIC; + if (0 == VIC) { + /* set the bit that corresponds to the + * VIC that was set to zero + */ + deletion_mask |= this_bit; + MHL_TX_EDID_INFO + ("vic: 0x%02x deletion_mask:0x%08x" + " this_bit:0x%08x\n", + VIC, deletion_mask, this_bit); + } + } + } + + p_3D_u.p_3D = mhl_edid_3d_data->parse_data.p_three_d; + while (p_3D_u.p_as_bytes < + mhl_edid_3d_data->parse_data.p_3d_limit) { + uint8_t _2D_VIC_order = + p_3D_u.p_sans_byte_1->byte0._2D_VIC_order; + enum _3D_structure_e _3D_structure = + p_3D_u.p_sans_byte_1->byte0._3D_structure; + uint8_t VIC; + VIC = + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[0]-> + short_descriptors[_2D_VIC_order].VIC; + if (0 == VIC) { + /* delete this 3D_Structure/3D_detail info */ + uint8_t *pSrc, *pDest = p_3D_u.p_as_bytes; + + if (_3D_structure < tdsSideBySide) { + pSrc = + (uint8_t *) (p_3D_u.p_sans_byte_1 + + 1); + num_3D_structure_bytes_pruned += + sizeof(*p_3D_u.p_sans_byte_1); + } else { + pSrc = + (uint8_t *) (p_3D_u.p_with_byte_1 + + 1); + num_3D_structure_bytes_pruned += + sizeof(*p_3D_u.p_with_byte_1); + } + while (pSrc < pb_limit) + *pDest++ = *pSrc++; + + while (pDest < pb_limit) + *pDest++ = 0; + } else { + uint8_t i; + uint8_t limit = _2D_VIC_order; + uint32_t this_bit; + MHL_TX_EDID_INFO + ("2D vic order: 0x%02x " + "deletion_mask:0x%08x\n", + _2D_VIC_order, deletion_mask); + + for (i = 0, this_bit = 1; i < limit; + ++i, this_bit <<= 1) { + if (this_bit & deletion_mask) + _2D_VIC_order--; + } + p_3D_u.p_sans_byte_1->byte0._2D_VIC_order = + _2D_VIC_order; + MHL_TX_EDID_INFO + ("2D vic order: 0x%02x this_bit:0x%08x\n", + _2D_VIC_order, this_bit); + + if (_3D_structure < tdsSideBySide) + p_3D_u.p_sans_byte_1++; + else + p_3D_u.p_with_byte_1++; + } + } + MHL_TX_EDID_INFO("num_3D_structure_bytes_pruned:0x%x " + "byte14: 0x%02x " + "offset to DTDs: 0x%x " + "vsdb header: 0x%x\n", + num_3D_structure_bytes_pruned, + *((uint8_t *) &mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->byte14), + p_CEA_extension-> + byte_offset_to_18_byte_descriptors, + *((uint8_t *) &mhl_edid_3d_data->parse_data. + p_HDMI_vsdb->header) + ); + + mhl_edid_3d_data->parse_data.p_byte_13_through_byte_15->byte14. + HDMI_3D_len -= num_3D_structure_bytes_pruned; + p_CEA_extension->byte_offset_to_18_byte_descriptors -= + num_3D_structure_bytes_pruned; + mhl_edid_3d_data->parse_data.p_HDMI_vsdb->header.fields. + length_following_header -= num_3D_structure_bytes_pruned; + + MHL_TX_EDID_INFO("num_3D_structure_bytes_pruned:0x%x " + "byte14: 0x%02x " + "offset to DTDs: 0x%x " + "vsdb header: 0x%x\n", + num_3D_structure_bytes_pruned, + *((uint8_t *) &mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->byte14), + p_CEA_extension-> + byte_offset_to_18_byte_descriptors, + *((uint8_t *) &mhl_edid_3d_data->parse_data. + p_HDMI_vsdb->header) + ); + } + /* Prune the HDMI VSDB VIC list */ + if (mhl_edid_3d_data->parse_data.p_byte_13_through_byte_15) { + uint8_t length_VIC = + mhl_edid_3d_data->parse_data.p_byte_13_through_byte_15-> + byte14.HDMI_VIC_len; + + if (0 == length_VIC) { + MHL_TX_EDID_INFO("length_VIC:%d\n", + (uint16_t) length_VIC); + } else { + prune_hdmi_vsdb_vic_list(mhl_edid_3d_data, + p_CEA_extension, length_VIC); + } + } + + /* Prune the SVD list and move the CEA 861-D data blocks and DTDs up */ + prune_svd_list(mhl_edid_3d_data, p_CEA_extension); + + /* re-compute the checksum(s) */ + SII_ASSERT(EDID_BLOCK_SIZE == sizeof(*p_EDID_block_0), + ("\n\n unexpected size for block 0\n\n")); + SII_ASSERT(EDID_BLOCK_SIZE == sizeof(*p_CEA_extension), + ("\n\n unexpected size for CEA extension\n\n")); + + if (p_block_map) { + struct CEA_extension_t *p_CEA_extensionDest = + (struct CEA_extension_t *) p_block_map; + *p_CEA_extensionDest = *p_CEA_extension; + p_CEA_extension = p_CEA_extensionDest; + } + p_EDID_block_0->extension_flag = p_EDID_block_0->extension_flag ? 1 : 0; + + p_EDID_block_0->checksum = + calculate_generic_checksum((uint8_t *)p_EDID_block_0, 0, + sizeof(*p_EDID_block_0)); + + p_CEA_extension->checksum = + calculate_generic_checksum((uint8_t *)p_CEA_extension, 0, + sizeof(*p_CEA_extension)); + + DUMP_EDID_BLOCK(0, p_EDID_block_0, sizeof(*p_EDID_block_0)); + DUMP_EDID_BLOCK(0, p_CEA_extension, sizeof(*p_CEA_extension)); + + /* + * TODO: adjust all pointers into the EDID along the way of pruning the + * contents, instead of re-parsing here + */ +#ifndef EDID_PASSTHROUGH + if (0 == si_mhl_tx_drv_set_upstream_edid(mhl_edid_3d_data->drv_context, + mhl_edid_3d_data->EDID_block_data, 2 * EDID_BLOCK_SIZE)) +#endif + { + SET_3D_FLAG(mhl_edid_3d_data, FLAGS_EDID_READ_DONE); + } +} + +/* +*/ +static uint8_t IsQualifiedMhlVIC(struct edid_3d_data_t *mhl_edid_3d_data, + uint8_t VIC, uint16_t burst_id, + union video_burst_descriptor_u *p_descriptor) +{ + uint8_t ret_val = 0; + if (VIC > 0) { + ret_val = is_MHL_timing_mode(mhl_edid_3d_data, + VIC_info[VIC].columns, + VIC_info[VIC].rows, + VIC_info[VIC].field_rate_in_milliHz, + burst_id, p_descriptor, VIC); + if (vif_dual_frame_rate == + VIC_info[VIC].fields.frame_rate_info) { + uint32_t field_rate_in_milliHz; + switch (VIC_info[VIC].field_rate_in_milliHz) { + case 24000: /* 23.97 */ + field_rate_in_milliHz = 23970; + break; + + case 30000: /* 29.97 */ + field_rate_in_milliHz = 29970; + break; + + case 60000: /* 59.94 */ + field_rate_in_milliHz = 59940; + break; + + case 120000: /* 119.88 */ + field_rate_in_milliHz = 119880; + break; + + case 240000: /* 239.76 */ + field_rate_in_milliHz = 239760; + break; + + default: /* error or unknown case */ + field_rate_in_milliHz = 0; + break; + } + ret_val |= + is_MHL_timing_mode(mhl_edid_3d_data, + VIC_info[VIC].columns, + VIC_info[VIC].rows, + field_rate_in_milliHz, burst_id, + p_descriptor, VIC); + } + } + return ret_val; +} + +/* HDMI_VIC is a place holder, and not actually stored */ +#define HDMI_VIC(HDMI_VIC, cols, rows, field0, field1, pclk0, pclk1, mhl_vic) \ + {cols, rows, field0, field1, pclk0, pclk1, mhl_vic} + +PLACE_IN_CODE_SEG struct HDMI_VIC_info_t hdmi_vic_info[] = { + HDMI_VIC(0, 0, 0, 0, 0, 0, 0, 0), + HDMI_VIC(1, 3840, 2160, 30000, 29970, 297000000, 296703000, 95), + HDMI_VIC(2, 3840, 2160, 25000, 25000, 297000000, 297000000, 94), + HDMI_VIC(3, 3840, 2160, 24000, 23976, 297000000, 296703000, 93), + HDMI_VIC(4, 4096, 2160, 24000, 24000, 297000000, 297000000, 98) +}; + +/* +*/ +static uint8_t is_qualified_mhl_hdmi_vic( + struct edid_3d_data_t *mhl_edid_3d_data, uint8_t VIC) +{ + uint8_t ret_val = 0; + + if (qualify_pixel_clock_for_mhl + (mhl_edid_3d_data, hdmi_vic_info[VIC].pixel_clock_0, 24)) { + display_timing_enumeration_callback(mhl_edid_3d_data, + hdmi_vic_info[VIC].columns, + hdmi_vic_info[VIC].rows, 24, + hdmi_vic_info[VIC]. + field_rate_0_in_milliHz, 0, + NULL); + ret_val = 1; + } + if (qualify_pixel_clock_for_mhl + (mhl_edid_3d_data, hdmi_vic_info[VIC].pixel_clock_0, 16)) { + display_timing_enumeration_callback(mhl_edid_3d_data, + hdmi_vic_info[VIC].columns, + hdmi_vic_info[VIC].rows, 16, + hdmi_vic_info[VIC]. + field_rate_0_in_milliHz, 0, + NULL); + ret_val = 1; + } + if (hdmi_vic_info[VIC].pixel_clock_0 != + hdmi_vic_info[VIC].pixel_clock_1) { + if (qualify_pixel_clock_for_mhl + (mhl_edid_3d_data, hdmi_vic_info[VIC].pixel_clock_1, 24)) { + display_timing_enumeration_callback( + mhl_edid_3d_data, + hdmi_vic_info[VIC].columns, + hdmi_vic_info[VIC].rows, 24, + hdmi_vic_info[VIC].field_rate_1_in_milliHz, + 0, NULL); + ret_val = 1; + } + if (qualify_pixel_clock_for_mhl + (mhl_edid_3d_data, hdmi_vic_info[VIC].pixel_clock_1, 16)) { + display_timing_enumeration_callback( + mhl_edid_3d_data, + hdmi_vic_info[VIC].columns, + hdmi_vic_info[VIC].rows, 16, + hdmi_vic_info[VIC].field_rate_1_in_milliHz, + 0, NULL); + ret_val = 1; + } + } + return ret_val; +} + +void si_mhl_tx_enumerate_hdmi_vsdb(struct edid_3d_data_t *mhl_edid_3d_data) +{ + int8_t vdb_index = 0; + uint8_t hdmi3D_present, hdmi_3D_multi_present; + struct HDMI_LLC_vsdb_payload_t *p_HDMI_vendor_specific_payload; + uint8_t *p_next_db; + uint8_t length_VIC; + uint8_t index; + uint8_t length_3D; + union HDMI_3D_sub_block_t *pThree3DSubBlock; + union { + union _3D_structure_and_detail_entry_u *p_3D; + struct _3D_structure_and_detail_entry_sans_byte1_t *psans_byte1; + struct _3D_structure_and_detail_entry_with_byte1_t *pwith_byte1; + uint8_t *p_bytes; + } p_3D_u; + uint8_t limit; + + if (mhl_edid_3d_data->parse_data.p_HDMI_vsdb == 0) + return; + if (mhl_edid_3d_data->parse_data.p_byte_13_through_byte_15 == 0) + return; + + p_HDMI_vendor_specific_payload = &mhl_edid_3d_data-> + parse_data.p_HDMI_vsdb->payload_u.HDMI_LLC; + p_next_db = (uint8_t *)p_HDMI_vendor_specific_payload + + mhl_edid_3d_data->parse_data. + p_HDMI_vsdb->header.fields.length_following_header; + + if (((uint8_t *)&mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15->byte13) >= + p_next_db) + return; + + hdmi3D_present = mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15-> + byte13._3D_present; + hdmi_3D_multi_present = mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15-> + byte13._3D_multi_present; + + if (((uint8_t *)&mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15->byte14) >= + p_next_db) + return; + + /* HDMI_VIC_len is present... */ + length_VIC = mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15->byte14.HDMI_VIC_len; + for (index = 0; index < length_VIC; ++index) { + uint8_t VIC; + + VIC = mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15->vicList[index]; + if (!is_qualified_mhl_hdmi_vic(mhl_edid_3d_data, VIC)) { + MHL_TX_EDID_INFO( + "'can't do HDMI VIC:%d\n", (uint16_t) VIC); + mhl_edid_3d_data-> + parse_data. + p_byte_13_through_byte_15->vicList[index] = 0; + } + } + if (hdmi3D_present == 0) + return; + + pThree3DSubBlock = (union HDMI_3D_sub_block_t *)&mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15->vicList[length_VIC]; + + p_3D_u.p_3D = NULL; + length_3D = mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15->byte14.HDMI_3D_len; + limit = mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d[vdb_index]-> + header.fields.length_following_header; + + limit = (limit > 16) ? 16 : limit; + switch (hdmi_3D_multi_present) { + case 0x00: + /* 3D_Structure_ALL_15..0 and 3D_MASK_15..0 + * fields are not present + * */ + p_3D_u.p_3D = &pThree3DSubBlock-> + HDMI_3D_sub_block_sans_all_AND_mask. + _3D_structure_and_detail_list[0]; + break; + case 0x01: + /* + * 3D_Structure_ALL_15..0 is present and assigns 3D formats + * to all of the VICs listed in the first 16 entries in the + * EDID 3D_mask_15..0 is not present + */ + { + union video_burst_descriptor_u descriptor; + struct _3D_structure_all_t *p_3D_structure_all = + (struct _3D_structure_all_t *)&mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15-> + vicList[length_VIC]; + descriptor.mhl2_3d_descriptor.left_right = + p_3D_structure_all->_3D_structure_all_7_0. + side_by_side; + descriptor.mhl2_3d_descriptor.top_bottom = + p_3D_structure_all->_3D_structure_all_15_8. + top_bottom; + descriptor.mhl2_3d_descriptor.frame_sequential = + p_3D_structure_all->_3D_structure_all_15_8. + frame_packing; + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], EDID_BLOCK_SIZE); + for (index = 0; index < limit; ++index) { + uint8_t VIC; + + VIC = mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[index].VIC; + if (VIC) { + if (!IsQualifiedMhlVIC( + mhl_edid_3d_data, VIC, + burst_id_3D_DTD, &descriptor)) { + mhl_edid_3d_data-> + parse_data. + p_video_data_blocks_2d[ + vdb_index]-> + short_descriptors[index].VIC = + 0; + } + } + } + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], + EDID_BLOCK_SIZE); + length_3D -= sizeof(*p_3D_structure_all); + } + p_3D_u.p_3D = &pThree3DSubBlock-> + HDMI_3D_sub_block_sans_mask. + _3D_structure_and_detail_list[0]; + break; + case 0x02: + /* + 3D_Structure_ALL_15..0 and 3D_mask_15..0 are present and + assign 3D formats to some of the VICS listed in the first + 16 entries in the EDID + + */ + { + struct _3D_structure_all_t *p_3D_structure_all = + (struct _3D_structure_all_t *)&mhl_edid_3d_data-> + parse_data.p_byte_13_through_byte_15-> + vicList[length_VIC]; + struct _3D_mask_t *p3DMask = + (struct _3D_mask_t *)(p_3D_structure_all + 1); + uint8_t mask; + union video_burst_descriptor_u descriptor; + + descriptor.mhl2_3d_descriptor.left_right = p_3D_structure_all-> + _3D_structure_all_7_0.side_by_side; + descriptor.mhl2_3d_descriptor.top_bottom = p_3D_structure_all-> + _3D_structure_all_15_8.top_bottom; + descriptor.mhl2_3d_descriptor.frame_sequential = + p_3D_structure_all-> + _3D_structure_all_15_8.frame_packing; + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], EDID_BLOCK_SIZE); + for (mask = 1, index = 0; (mask > 0) && (index < limit); + ++index, mask <<= 1) { + uint8_t VIC; + union video_burst_descriptor_u this_desc; + + if (mask & p3DMask->_3D_mask_7_0) { + this_desc = descriptor; + } else { + this_desc.mhl2_3d_descriptor.left_right = 0; + this_desc.mhl2_3d_descriptor.top_bottom = 0; + this_desc. + mhl2_3d_descriptor.frame_sequential = 0; + } + + VIC = mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d[vdb_index]-> + short_descriptors[index].VIC; + if (VIC) { + if (!IsQualifiedMhlVIC( + mhl_edid_3d_data, VIC, burst_id_3D_VIC, + &this_desc)) { + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[index].VIC = 0; + } + } + } + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], EDID_BLOCK_SIZE); + for (mask = 1; (mask > 0) && (index < limit); + ++index, mask <<= 1) { + uint8_t VIC; + union video_burst_descriptor_u this_desc; + + if (mask & p3DMask->_3D_mask_15_8) { + this_desc = descriptor; + } else { + this_desc.mhl2_3d_descriptor.left_right = 0; + this_desc.mhl2_3d_descriptor.top_bottom = 0; + this_desc. + mhl2_3d_descriptor.frame_sequential = 0; + } + + VIC = mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d[vdb_index]-> + short_descriptors[index].VIC; + if (VIC) { + if (!IsQualifiedMhlVIC( + mhl_edid_3d_data, VIC, burst_id_3D_VIC, + &this_desc)) { + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[index].VIC = 0; + } + } + } + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], EDID_BLOCK_SIZE); + length_3D -= sizeof(*p_3D_structure_all); + length_3D -= sizeof(*p3DMask); + } + p_3D_u.p_3D = &pThree3DSubBlock-> + HDMI_3D_sub_block_with_all_AND_mask. + _3D_structure_and_detail_list[0]; + mhl_edid_3d_data->parse_data.p_3d_mask = &pThree3DSubBlock-> + HDMI_3D_sub_block_with_all_AND_mask._3D_mask; + break; + case 0x03: + /* + * Reserved for future use. + * 3D_Structure_ALL_15..0 and 3D_mask_15..0 are NOT present + */ + p_3D_u.p_3D = &pThree3DSubBlock-> + HDMI_3D_sub_block_sans_all_AND_mask. + _3D_structure_and_detail_list[0]; + break; + } + + mhl_edid_3d_data->parse_data.p_three_d = p_3D_u.p_3D; + mhl_edid_3d_data->parse_data.p_3d_limit = &p_3D_u.p_bytes[length_3D]; + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], EDID_BLOCK_SIZE); + while (p_3D_u.p_bytes < mhl_edid_3d_data->parse_data.p_3d_limit) { + uint8_t _2D_VIC_order; + enum _3D_structure_e _3D_structure; + uint8_t VIC; + + _2D_VIC_order = p_3D_u.psans_byte1->byte0._2D_VIC_order; + _3D_structure = p_3D_u.psans_byte1->byte0._3D_structure; + VIC = mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d[vdb_index]-> + short_descriptors[_2D_VIC_order].VIC; + + /* this VIC position might have gotten disqualified already */ + if (VIC) { + union video_burst_descriptor_u descriptor; + descriptor.mhl2_3d_descriptor.left_right = 0; + descriptor.mhl2_3d_descriptor.top_bottom = 0; + descriptor.mhl2_3d_descriptor.frame_sequential = 0; + switch (_3D_structure) { + case tdsSideBySide: + { + /*TODO: re-visit uint8_t _3D_detail = + * p_3D_u.pwith_byte1->byte1._3D_detail; + */ + descriptor.mhl2_3d_descriptor. + left_right = 1; + } + break; + case tdsTopAndBottom: + descriptor.mhl2_3d_descriptor.top_bottom = 1; + break; + case tdsFramePacking: + descriptor.mhl2_3d_descriptor. + frame_sequential = 1; + break; + } + if (!IsQualifiedMhlVIC( + mhl_edid_3d_data, + VIC, burst_id_3D_VIC, + &descriptor)) { + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[vdb_index]-> + short_descriptors[_2D_VIC_order].VIC = 0; + } + } + if (_3D_structure < tdsSideBySide) + p_3D_u.psans_byte1++; + else + p_3D_u.pwith_byte1++; + } + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], EDID_BLOCK_SIZE); +} + +void si_mhl_tx_display_timing_enumeration_end( + struct edid_3d_data_t *mhl_edid_3d_data) +{ + mhl_edid_3d_data->parse_data.flags.parse_3d_in_progress = 0; + /* finish off with any 3D modes reported via the HDMI VSDB */ + si_mhl_tx_enumerate_hdmi_vsdb(mhl_edid_3d_data); + /* notify the app (board specific) layer */ + display_timing_enumeration_end(mhl_edid_3d_data); + SET_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_DONE); + si_mhl_tx_prune_edid(mhl_edid_3d_data); +} + +static void CheckForAll3DBurstDone(struct edid_3d_data_t *mhl_edid_3d_data) +{ + if (TEST_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_VIC_DONE)) { + if (TEST_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_DTD_DONE)) { + if (!TEST_3D_FLAG + (mhl_edid_3d_data, FLAGS_BURST_3D_DONE)) { + /* For MHL 3.0 and newer peers, + * we wait for MHL_INT_FEAT_COMPLETE + */ + if (si_get_peer_mhl_version + (mhl_edid_3d_data->dev_context) < 0x30) { + si_mhl_tx_display_timing_enumeration_end + (mhl_edid_3d_data); + } + } + } + } +} + +/* +*/ +void si_mhl_tx_process_3d_vic_burst(void *context, + struct MHL2_video_format_data_t *p_write_burst_data) +{ + struct edid_3d_data_t *mhl_edid_3d_data; + uint8_t block_index = 0; + /* + * TODO: re-visit uint8_t edidLimit = + * mhl_edid_3d_data->parse_data.p_byte_13_through_byte_15-> + * byte14.HDMI_3D_len; + */ + mhl_edid_3d_data = (struct edid_3d_data_t *)context; + + MHL_TX_EDID_INFO("burstEntryCount3D_VIC: %d\n", + mhl_edid_3d_data->parse_data.burst_entry_count_3d_vic); + if (1 == p_write_burst_data->header.sequence_index) { + size_t _3d_vic_size; + _3d_vic_size = + p_write_burst_data->header.total_entries * + sizeof(*mhl_edid_3d_data->_3d_vic_list); + if (p_write_burst_data->header.total_entries >= + mhl_edid_3d_data->_3d_vic_info.num_items_allocated) { + if (NULL != mhl_edid_3d_data->_3d_vic_list) + kfree(mhl_edid_3d_data->_3d_vic_list); + + mhl_edid_3d_data->_3d_vic_list = + kmalloc(_3d_vic_size, GFP_KERNEL); + + if (NULL == mhl_edid_3d_data->_3d_vic_list) { + mhl_edid_3d_data->_3d_vic_info. + num_items_allocated = 0; + mhl_edid_3d_data->_3d_vic_info.num_items = 0; + MHL_TX_DBG_ERR + ("%sunable to allocate memory%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return; + } else { + mhl_edid_3d_data->_3d_vic_info. + num_items_allocated = + p_write_burst_data->header.total_entries; + } + } + mhl_edid_3d_data->_3d_vic_info.num_items = + p_write_burst_data->header.total_entries; + } + + if (NULL == mhl_edid_3d_data->_3d_vic_list) { + MHL_TX_DBG_ERR("%s no place to put 3D_VIC burst%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return; + } + if (mhl_edid_3d_data->parse_data.flags.parse_3d_in_progress) { + /* check to see if it's time to move on to the next block */ + if (mhl_edid_3d_data->parse_data.vic_2d_index >= + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[mhl_edid_3d_data->parse_data. + video_data_block_index]->header. + fields.length_following_header) { + mhl_edid_3d_data->parse_data.video_data_block_index++; + if (mhl_edid_3d_data->parse_data. + video_data_block_index >= + mhl_edid_3d_data->parse_data. + num_video_data_blocks) { + SET_3D_FLAG(mhl_edid_3d_data, + FLAGS_BURST_3D_VIC_DONE); + CheckForAll3DBurstDone(mhl_edid_3d_data); + return; + } + } + + if (mhl_edid_3d_data->parse_data.burst_entry_count_3d_vic < + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[mhl_edid_3d_data->parse_data. + video_data_block_index]->header. + fields.length_following_header) { + /* each SVD is 1 byte long */ + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], + EDID_BLOCK_SIZE); + /* block_index is set to zero above */ + for (; (block_index < + p_write_burst_data-> + num_entries_this_burst) + && (mhl_edid_3d_data->parse_data. + burst_entry_count_3d_vic < + p_write_burst_data->header. + total_entries); + ++block_index, + ++mhl_edid_3d_data->parse_data. + burst_entry_count_3d_vic, + ++mhl_edid_3d_data->parse_data. + vic_2d_index) { + uint8_t VIC; + struct cea_short_descriptor_t svd; + uint8_t this_mode_doable = 0; + union video_burst_descriptor_u *p_descriptor; + /* + * Check to see if it's time to move on + * to the next block + */ + if (mhl_edid_3d_data->parse_data.vic_2d_index >= + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[mhl_edid_3d_data-> + parse_data. + video_data_block_index]-> + header.fields.length_following_header) { + mhl_edid_3d_data->parse_data. + video_data_block_index++; + if (mhl_edid_3d_data->parse_data. + video_data_block_index >= + mhl_edid_3d_data->parse_data. + num_video_data_blocks) { + SET_3D_FLAG( + mhl_edid_3d_data, + FLAGS_BURST_3D_VIC_DONE); + break; + } + } + + p_descriptor = + (union video_burst_descriptor_u *) & + p_write_burst_data-> + video_descriptors[block_index]; + svd = + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[mhl_edid_3d_data-> + parse_data. + video_data_block_index]-> + short_descriptors[mhl_edid_3d_data-> + parse_data.vic_2d_index]; + VIC = svd.VIC; + mhl_edid_3d_data-> + _3d_vic_list[mhl_edid_3d_data->parse_data. + vic_2d_index].svd = svd; + mhl_edid_3d_data-> + _3d_vic_list[mhl_edid_3d_data->parse_data. + vic_2d_index]._3d_info = + p_descriptor->mhl3_3d_descriptor; + + if (VIC) { + MHL_TX_EDID_INFO( + "short Descriptor[%d] 3D VIC: " + "%d %s %s %s\n", + mhl_edid_3d_data->parse_data. + burst_entry_count_3d_vic, VIC, + p_descriptor->mhl2_3d_descriptor. + left_right ? psz_left_right : + psz_space, + p_descriptor->mhl2_3d_descriptor. + top_bottom ? psz_top_bottom : + psz_space, + p_descriptor->mhl2_3d_descriptor. + frame_sequential ? + psz_frame_sequential : psz_space); + this_mode_doable = + IsQualifiedMhlVIC(mhl_edid_3d_data, + VIC, + burst_id_3D_VIC, + p_descriptor); + if (!this_mode_doable) { + MHL_TX_EDID_INFO + ("'can't do CEA VIC:%d\n", + (uint16_t) VIC); + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d + [mhl_edid_3d_data-> + parse_data. + video_data_block_index]-> + short_descriptors + [mhl_edid_3d_data-> + parse_data.vic_2d_index]. + VIC = 0; + } + } + } + DUMP_EDID_BLOCK(0, + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], + EDID_BLOCK_SIZE); + } + + if (mhl_edid_3d_data->parse_data.burst_entry_count_3d_vic >= + p_write_burst_data->header.total_entries) { + SET_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_VIC_DONE); + } + CheckForAll3DBurstDone(mhl_edid_3d_data); + } +} + +void check_3d_dtd_sequence_done(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL2_video_format_data_t *p_write_burst_data, uint8_t dtd_limit) +{ + int flag = 0; + if (mhl_edid_3d_data->parse_data.cea_861_dtd_index >= dtd_limit) + flag = 1; + + if (mhl_edid_3d_data->parse_data.burst_entry_count_3d_dtd >= + p_write_burst_data->header.total_entries) { + flag = 1; + } + if (flag) { + SET_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_DTD_DONE); + if (TEST_3D_FLAG(mhl_edid_3d_data, + FLAGS_BURST_3D_VIC_DONE)) { + if (!TEST_3D_FLAG(mhl_edid_3d_data, + FLAGS_BURST_3D_DONE)) { + /* + * For MHL 3.0 and newer peers, wait for + * MHL_INT_FEAT_COMPLETE + */ + if (si_get_peer_mhl_version( + mhl_edid_3d_data->dev_context) < 0x30) { + si_mhl_tx_display_timing_enumeration_end + (mhl_edid_3d_data); + } + } + } + } +} + +static void process_cea_dtds(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL2_video_format_data_t *p_write_burst_data, + int burst_index) +{ + uint8_t is_timing = 0; + union video_burst_descriptor_u *p_burst_desc; + struct CEA_extension_t *p_CEA_extension = + (struct CEA_extension_t *)&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE]; +#if 0 + uint8_t dtd_limit = + (uint8_t) p_CEA_extension->version_u.version3.misc_support. + total_number_native_dtds_in_entire_EDID; +#else + uint8_t dtd_limit = + (EDID_BLOCK_SIZE + - p_CEA_extension->byte_offset_to_18_byte_descriptors) + / sizeof(struct detailed_timing_descriptor_t); +#endif + union { + uint8_t *puc_data_block; + union _18_byte_descriptor_u *p_long_descriptors; + } p_data_u; + p_data_u.p_long_descriptors = + (union _18_byte_descriptor_u *)(((uint8_t *)p_CEA_extension) + + p_CEA_extension->byte_offset_to_18_byte_descriptors); + MHL_TX_EDID_INFO("continuing with CEA-861-D/E DTDs" + "\n\tburst_index: %d" + "\n\tburst_entry_count_3d_dtd: %d" + "\n\tnum_entries_this_burst: %d" + "\n\ttotal_entries:%d" + "\n\tdtd_limit:%d" + "\n\toffsetTo18_byte_descriptors:0x%x\n", + burst_index, + mhl_edid_3d_data->parse_data. + burst_entry_count_3d_dtd, + p_write_burst_data-> + num_entries_this_burst, + p_write_burst_data->header. + total_entries, dtd_limit, + p_CEA_extension-> + byte_offset_to_18_byte_descriptors); + /* continue with CEA-861-D/E DTDs when done with VESA DTDs */ + /* burst_index is set to zero above */ + for (; (burst_index < + p_write_burst_data->num_entries_this_burst) + && (mhl_edid_3d_data->parse_data. + burst_entry_count_3d_dtd < + p_write_burst_data->header. + total_entries) + && (mhl_edid_3d_data->parse_data. + cea_861_dtd_index < dtd_limit); + ++mhl_edid_3d_data->parse_data. + cea_861_dtd_index) { + union _18_byte_descriptor_u *p_desc = + &p_data_u. + p_long_descriptors[mhl_edid_3d_data-> + parse_data. + cea_861_dtd_index]; + bool is_valid = 0; + p_burst_desc = + (union video_burst_descriptor_u *) + &p_write_burst_data-> + video_descriptors[burst_index]; + is_valid = + si_mhl_tx_parse_detailed_timing_descriptor + (mhl_edid_3d_data, p_desc, EDID_BLOCK_2_3, + &is_timing, burst_id_3D_DTD, p_burst_desc); + /* only count it if it's a valid timing */ + if (is_timing) { + + if (is_valid) { + MHL_TX_EDID_INFO + ("CEA-861 DTD index: %d burst index:%d DTD " + "SP index:%d %s %s %s\n\n", + (uint16_t)mhl_edid_3d_data-> + parse_data.cea_861_dtd_index, + (uint16_t)burst_index, + (uint16_t)mhl_edid_3d_data-> + parse_data.burst_entry_count_3d_dtd, + p_burst_desc->mhl2_3d_descriptor. + left_right ? psz_left_right : psz_space, + p_burst_desc->mhl2_3d_descriptor. + top_bottom ? psz_top_bottom : psz_space, + p_burst_desc->mhl2_3d_descriptor. + frame_sequential ? + psz_frame_sequential : psz_space); + mhl_edid_3d_data-> + _3d_dtd_list[mhl_edid_3d_data-> + parse_data.num_vesa_timing_dtds + + mhl_edid_3d_data->parse_data. + cea_861_dtd_index].dtd_cea_861 = + p_desc->dtd; + mhl_edid_3d_data->_3d_dtd_list[ + mhl_edid_3d_data->parse_data. + num_vesa_timing_dtds + + mhl_edid_3d_data->parse_data. + cea_861_dtd_index]._3d_info = + p_burst_desc->mhl3_3d_descriptor; + } else { + /* + * Mark this mode for pruning by setting + * horizontal active to zero + */ + MHL_TX_DBG_INFO("%smark for pruning%s\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT); + p_desc->dtd.horz_active_7_0 = 0; + p_desc->dtd.horz_active_blanking_high. + horz_active_11_8 = 0; + } + + ++burst_index; + ++mhl_edid_3d_data->parse_data. + burst_entry_count_3d_dtd; + } else { + MHL_TX_EDID_INFO + ("CEA-861 DTD index: %d\n", + (uint16_t) mhl_edid_3d_data-> + parse_data.vesa_dtd_index); + } + } + + MHL_TX_EDID_INFO("DTD burst complete\n"); + check_3d_dtd_sequence_done(mhl_edid_3d_data, + p_write_burst_data, dtd_limit); +} + +static void print_vesa_dtd_index(int burst_index, + struct edid_3d_data_t *mhl_edid_3d_data, + union video_burst_descriptor_u *p_burst_desc) +{ + MHL_TX_EDID_INFO( + "VESA DTD index: %d burst index:%d DTD SP index:%d %s %s %s\n", + (uint16_t)mhl_edid_3d_data->parse_data.vesa_dtd_index, + (uint16_t) burst_index, + (uint16_t)mhl_edid_3d_data->parse_data.burst_entry_count_3d_dtd, + p_burst_desc->mhl2_3d_descriptor.left_right ? + psz_left_right : psz_space, + p_burst_desc->mhl2_3d_descriptor.top_bottom ? + psz_top_bottom : psz_space, + p_burst_desc->mhl2_3d_descriptor.frame_sequential ? + psz_frame_sequential : psz_space); +} + + +void si_mhl_tx_process_3d_dtd_burst(void *context, + struct MHL2_video_format_data_t *p_write_burst_data) +{ + struct edid_3d_data_t *mhl_edid_3d_data; + struct EDID_block0_t *p_EDID_block_0; + union video_burst_descriptor_u *p_burst_desc; + int burst_index = 0; + uint8_t is_timing = 0; + struct CEA_extension_t *p_CEA_extension; + uint8_t dtd_limit; + + mhl_edid_3d_data = (struct edid_3d_data_t *)context; + p_CEA_extension = (struct CEA_extension_t *) &mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE]; +#if 0 + dtd_limit = (uint8_t) p_CEA_extension->version_u.version3.misc_support. + total_number_native_dtds_in_entire_EDID; +#else + + dtd_limit = (EDID_BLOCK_SIZE + - p_CEA_extension->byte_offset_to_18_byte_descriptors) + / sizeof(struct detailed_timing_descriptor_t) ; +#endif + + if (1 == p_write_burst_data->header.sequence_index) { + size_t _3d_dtd_size; + _3d_dtd_size = + p_write_burst_data->header.total_entries * + sizeof(*mhl_edid_3d_data->_3d_dtd_list); + if (p_write_burst_data->header.total_entries >= + mhl_edid_3d_data->_3d_dtd_info.num_items_allocated) { + if (NULL != mhl_edid_3d_data->_3d_dtd_list) + kfree(mhl_edid_3d_data->_3d_dtd_list); + + mhl_edid_3d_data->_3d_dtd_list = + kmalloc(_3d_dtd_size, GFP_KERNEL); + if (NULL == mhl_edid_3d_data->_3d_dtd_list) { + mhl_edid_3d_data->_3d_dtd_info. + num_items_allocated = 0; + mhl_edid_3d_data->_3d_dtd_info.num_items = 0; + MHL_TX_DBG_ERR + ("%sunable to allocate memory%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return; + } else { + mhl_edid_3d_data->_3d_dtd_info. + num_items_allocated = + p_write_burst_data->header.total_entries; + } + } + mhl_edid_3d_data->_3d_dtd_info.num_items = + p_write_burst_data->header.total_entries; + } + if (NULL == mhl_edid_3d_data->_3d_dtd_list) { + MHL_TX_DBG_ERR("%s no place to put 3D_DTD burst%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return; + } + if (!mhl_edid_3d_data->parse_data.flags.parse_3d_in_progress) + return; + + if (TEST_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_DTD_VESA_DONE)) { + process_cea_dtds(mhl_edid_3d_data, + p_write_burst_data, + burst_index); + return; + } + + + p_EDID_block_0 = (struct EDID_block0_t *)&mhl_edid_3d_data-> + EDID_block_data[0]; + /* + * Up to four DTDs are possible in the base VESA EDID + * this will be covered by a single burst. + */ +#define DESC_COUNT (sizeof(p_EDID_block_0->detailed_timing_descriptors) / \ + sizeof(p_EDID_block_0->detailed_timing_descriptors[0])) + + for (; (burst_index < p_write_burst_data->num_entries_this_burst) && + (mhl_edid_3d_data->parse_data.burst_entry_count_3d_dtd < + p_write_burst_data->header.total_entries) && + (mhl_edid_3d_data->parse_data.vesa_dtd_index < DESC_COUNT); + ++mhl_edid_3d_data->parse_data.vesa_dtd_index) { + union _18_byte_descriptor_u *p_desc = + (union _18_byte_descriptor_u *) + &p_EDID_block_0->detailed_timing_descriptors[ + mhl_edid_3d_data->parse_data.vesa_dtd_index]; + bool is_valid = 0; + + p_burst_desc = + (union video_burst_descriptor_u *) & + p_write_burst_data-> + video_descriptors[burst_index]; + is_valid = + si_mhl_tx_parse_detailed_timing_descriptor + (mhl_edid_3d_data, p_desc, EDID_BLOCK_0, + &is_timing, burst_id_3D_DTD, p_burst_desc); + + if (is_timing) { + + if (is_valid) { + print_vesa_dtd_index(burst_index, + mhl_edid_3d_data, + p_burst_desc); + mhl_edid_3d_data->_3d_dtd_list[ + mhl_edid_3d_data->parse_data. + vesa_dtd_index].dtd_cea_861 = + p_desc->dtd; + mhl_edid_3d_data->_3d_dtd_list[ + mhl_edid_3d_data->parse_data. + vesa_dtd_index]._3d_info = + p_burst_desc->mhl3_3d_descriptor; + } else { + MHL_TX_DBG_INFO("%smark for pruning%s\n", + ANSI_ESC_YELLOW_TEXT, + ANSI_ESC_RESET_TEXT); + /* + * Mark this mode for pruning by setting + * horizontal active to zero + */ + p_desc->dtd.horz_active_7_0 = 0; + p_desc->dtd.horz_active_blanking_high. + horz_active_11_8 = 0; + } + + burst_index++; + mhl_edid_3d_data->parse_data.burst_entry_count_3d_dtd++; + } else { + MHL_TX_EDID_INFO("VESA DTD index: %d\n", + (uint16_t)mhl_edid_3d_data-> + parse_data.vesa_dtd_index); + } + } + + if (mhl_edid_3d_data->parse_data.vesa_dtd_index >= + sizeof(p_EDID_block_0-> + detailed_timing_descriptors) / + sizeof(p_EDID_block_0-> + detailed_timing_descriptors[0])) { + /* we got past the VESA DTDs in this burst */ + SET_3D_FLAG(mhl_edid_3d_data, + FLAGS_BURST_3D_DTD_VESA_DONE); + } else { + check_3d_dtd_sequence_done(mhl_edid_3d_data, + p_write_burst_data, + dtd_limit); + /* more VESA DTDs to process in next burst */ + MHL_TX_EDID_INFO( + "%s\n", + TEST_3D_FLAG(mhl_edid_3d_data, + FLAGS_BURST_3D_DTD_DONE) ? + "3D DTD descriptors exhausted" + : "more VESA DTDs to process"); + return; + } + + process_cea_dtds(mhl_edid_3d_data, p_write_burst_data, burst_index); +} + +void si_mhl_tx_process_hev_vic_burst(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL3_hev_vic_data_t *p_burst) +{ + int i, hev_index = mhl_edid_3d_data->hev_vic_info.index; + + if (0 == p_burst->header.total_entries) { + MHL_TX_DBG_ERR("invalid zero value for total entries\n") + return; + } + if (1 == p_burst->header.sequence_index) { + size_t hev_vic_size; + hev_vic_size = + p_burst->header.total_entries * + sizeof(*mhl_edid_3d_data->hev_vic_list); + if (p_burst->header.total_entries > + mhl_edid_3d_data->hev_vic_info.num_items_allocated) { + if (NULL != mhl_edid_3d_data->hev_vic_list) + kfree(mhl_edid_3d_data->hev_vic_list); + + mhl_edid_3d_data->hev_vic_list = + kmalloc(hev_vic_size, GFP_KERNEL); + if (NULL == mhl_edid_3d_data->hev_vic_list) { + mhl_edid_3d_data->hev_vic_info. + num_items_allocated = 0; + mhl_edid_3d_data->hev_vic_info.num_items = 0; + MHL_TX_DBG_ERR + ("%sunable to allocate memory%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return; + } else { + MHL_TX_DBG_WARN(" %d %p\n", hev_index, + mhl_edid_3d_data->hev_vic_list) + mhl_edid_3d_data->hev_vic_info. + num_items_allocated = + p_burst->header.total_entries; + } + } + mhl_edid_3d_data->hev_vic_info.num_items = + p_burst->header.total_entries; + } else if (NULL == mhl_edid_3d_data->hev_vic_list) { + MHL_TX_DBG_ERR("bogus write burst, no hev_vic_list\n") + return; + } + MHL_TX_DBG_WARN(" %d %p\n", hev_index, mhl_edid_3d_data->hev_vic_list) + if (NULL == mhl_edid_3d_data->hev_vic_list) { + MHL_TX_DBG_ERR("%s no place to put HEV_VIC burst%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return; + } + for (i = 0; (i < p_burst->num_entries_this_burst) && + (hev_index < p_burst->header.total_entries); + ++i, ++hev_index) { + /* We don't have an EDID entry to prune here, so just + throw away the return value + */ + + if (IsQualifiedMhlVIC + (mhl_edid_3d_data, + p_burst->video_descriptors[i].vic_cea861f, + burst_id_HEV_VIC, + (union video_burst_descriptor_u *) &p_burst-> + video_descriptors[i])) { + MHL_TX_DBG_INFO(" %d %p\n", + hev_index, mhl_edid_3d_data->hev_vic_list) + mhl_edid_3d_data->hev_vic_list[hev_index]. + mhl3_hev_vic_descriptor = + p_burst->video_descriptors[i]; + } + } + mhl_edid_3d_data->hev_vic_info.index = hev_index; +} + +void si_mhl_tx_process_hev_dtd_a_burst(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL3_hev_dtd_a_data_t *p_burst) +{ + mhl_edid_3d_data->hev_dtd_payload.sequence_index = + p_burst->header.sequence_index; + mhl_edid_3d_data->hev_dtd_payload.a = p_burst->payload; + if (0 == p_burst->header.total_entries) { + MHL_TX_DBG_ERR("invalid zero value for total entries\n") + return; + } + if (1 == p_burst->header.sequence_index) { + size_t hev_dtd_size; + hev_dtd_size = + p_burst->header.total_entries * + sizeof(*mhl_edid_3d_data->hev_dtd_list); + if (p_burst->header.total_entries >= + mhl_edid_3d_data->hev_dtd_info.num_items_allocated) { + if (NULL != mhl_edid_3d_data->hev_dtd_list) + kfree(mhl_edid_3d_data->hev_dtd_list); + + mhl_edid_3d_data->hev_dtd_list = + kmalloc(hev_dtd_size, GFP_KERNEL); + if (NULL == mhl_edid_3d_data->hev_dtd_list) { + mhl_edid_3d_data->hev_dtd_info. + num_items_allocated = 0; + mhl_edid_3d_data->hev_dtd_info.num_items = 0; + MHL_TX_DBG_ERR + ("%sunable to allocate memory%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return; + } else { + mhl_edid_3d_data->hev_dtd_info. + num_items_allocated = + p_burst->header.total_entries; + } + } + mhl_edid_3d_data->hev_dtd_info.num_items = + p_burst->header.total_entries; + } + if (mhl_edid_3d_data->hev_dtd_list) { + if (p_burst->header.total_entries < + mhl_edid_3d_data->hev_dtd_info.num_items) { + mhl_edid_3d_data->hev_dtd_list[p_burst->header. + sequence_index - 1].a = + p_burst->payload; + } + } +} + +void si_mhl_tx_process_hev_dtd_b_burst(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL3_hev_dtd_b_data_t *p_burst) +{ + mhl_edid_3d_data->hev_dtd_payload.b = p_burst->payload; + + if (0 == p_burst->header.total_entries) { + MHL_TX_DBG_ERR("invalid zero value for total entries\n") + return; + } + if (mhl_edid_3d_data->hev_dtd_list) { + if (p_burst->header.total_entries < + mhl_edid_3d_data->hev_dtd_info.num_items) { + mhl_edid_3d_data->hev_dtd_list[p_burst->header. + sequence_index - 1].b = + p_burst->payload; + } + } + /* no EDID to prune here, so throw away the result */ + is_MHL_timing_mode(mhl_edid_3d_data, 0, 0, 0, burst_id_HEV_DTDB, + (union video_burst_descriptor_u *) p_burst, 0); +} + +/* + FUNCTION : si_mhl_tx_parse_established_timing() + PURPOSE : Parse the established timing section of EDID Block 0 and + print their decoded meaning to the screen. + INPUT PARAMS : Pointer to the array where the data read from EDID + Block0 is stored. + OUTPUT PARAMS: None + GLOBALS USED : None + RETURNS : Void +*/ + +#define STRINGIZE(x) #x + +#define DUMP_OFFSET(s, m) \ + MHL_TX_EDID_INFO(STRINGIZE(m)" offset:%x\n", SII_OFFSETOF(s, m)) + +#define DUMP_ESTABLISHED_TIMING(group, width, height, refresh, progressive) \ +if (p_EDID_block_0->group.et##width##x##height##_##refresh##progressive) { \ + MHL_TX_EDID_INFO(STRINGIZE(group)"."STRINGIZE(width)"x" \ + STRINGIZE(height)"@"STRINGIZE(refresh)STRINGIZE(progressive) \ + "\n"); \ + if (!is_MHL_timing_mode(mhl_edid_3d_data, width, height, refresh*1000, \ + 0, NULL, 0)) { \ + p_EDID_block_0-> \ + group.et##width##x##height##_##refresh##progressive = 0; \ + } \ +} + +static void si_mhl_tx_parse_established_timing( + struct edid_3d_data_t *mhl_edid_3d_data, + struct EDID_block0_t *p_EDID_block_0) +{ + DUMP_OFFSET(struct EDID_block0_t, header_data[0]); + DUMP_OFFSET(struct EDID_block0_t, id_manufacturer_name); + DUMP_OFFSET(struct EDID_block0_t, id_product_code); + DUMP_OFFSET(struct EDID_block0_t, serial_number[0]); + DUMP_OFFSET(struct EDID_block0_t, week_of_manufacture); + DUMP_OFFSET(struct EDID_block0_t, year_of_manufacture); + DUMP_OFFSET(struct EDID_block0_t, EDID_version); + DUMP_OFFSET(struct EDID_block0_t, EDID_revision); + DUMP_OFFSET(struct EDID_block0_t, video_input_definition); + DUMP_OFFSET(struct EDID_block0_t, horz_screen_size_or_aspect_ratio); + DUMP_OFFSET(struct EDID_block0_t, vert_screen_size_or_aspect_ratio); + DUMP_OFFSET(struct EDID_block0_t, display_transfer_characteristic); + DUMP_OFFSET(struct EDID_block0_t, feature_support); + DUMP_OFFSET(struct EDID_block0_t, red_green_bits_1_0); + DUMP_OFFSET(struct EDID_block0_t, blue_white_bits_1_0); + DUMP_OFFSET(struct EDID_block0_t, red_x); + DUMP_OFFSET(struct EDID_block0_t, red_y); + DUMP_OFFSET(struct EDID_block0_t, green_x); + DUMP_OFFSET(struct EDID_block0_t, green_y); + DUMP_OFFSET(struct EDID_block0_t, blue_x); + DUMP_OFFSET(struct EDID_block0_t, blue_y); + DUMP_OFFSET(struct EDID_block0_t, white_x); + DUMP_OFFSET(struct EDID_block0_t, white_y); + + /* MHL cannot support these modes, so prune them */ + p_EDID_block_0->established_timings_II.et1280x1024_75p = 0; + p_EDID_block_0->manufacturers_timings.et1152x870_75p = 0; + + MHL_TX_EDID_INFO("Parsing Established Timing:\n"); + MHL_TX_EDID_INFO("===========================\n"); + + /* Parse Established Timing Byte #0 */ + DUMP_OFFSET(struct EDID_block0_t, established_timings_I); + DUMP_ESTABLISHED_TIMING(established_timings_I, 720, 400, 70, p) + DUMP_ESTABLISHED_TIMING(established_timings_I, 720, 400, 88, p) + DUMP_ESTABLISHED_TIMING(established_timings_I, 640, 480, 60, p) + DUMP_ESTABLISHED_TIMING(established_timings_I, 640, 480, 67, p) + DUMP_ESTABLISHED_TIMING(established_timings_I, 640, 480, 72, p) + DUMP_ESTABLISHED_TIMING(established_timings_I, 640, 480, 75, p) + DUMP_ESTABLISHED_TIMING(established_timings_I, 800, 600, 56, p) + DUMP_ESTABLISHED_TIMING(established_timings_I, 800, 600, 60, p) + + /* Parse Established Timing Byte #1: */ + DUMP_OFFSET(struct EDID_block0_t, established_timings_II); + DUMP_ESTABLISHED_TIMING(established_timings_II, 800, 600, 72, p) + DUMP_ESTABLISHED_TIMING(established_timings_II, 800, 600, 75, p) + DUMP_ESTABLISHED_TIMING(established_timings_II, 832, 624, 75, p) + DUMP_ESTABLISHED_TIMING(established_timings_II, 1024, 768, 87, i) + DUMP_ESTABLISHED_TIMING(established_timings_II, 1024, 768, 60, p) + DUMP_ESTABLISHED_TIMING(established_timings_II, 1024, 768, 70, p) + DUMP_ESTABLISHED_TIMING(established_timings_II, 1024, 768, 75, p) + DUMP_ESTABLISHED_TIMING(established_timings_II, 1280, 1024, 75, p) + + /* Parse Established Timing Byte #2: */ + DUMP_OFFSET(struct EDID_block0_t, manufacturers_timings); + DUMP_ESTABLISHED_TIMING(manufacturers_timings, 1152, 870, 75, p) + + if ((!p_EDID_block_0->header_data[0]) && + (0 == *((uint8_t *) &p_EDID_block_0->established_timings_II)) && + (!p_EDID_block_0->header_data[2])) { + MHL_TX_EDID_INFO("No established video modes\n"); + } +} + +/* + FUNCTION : si_mhl_tx_parse_standard_timing() + PURPOSE : Parse the standard timing section of EDID Block 0 and + print their decoded meaning to the screen. + INPUT PARAMS : Pointer to the array where the data read from EDID + Block0 is stored. + OUTPUT PARAMS: None + GLOBALS USED : None + RETURNS : Void +*/ + +static void si_mhl_tx_parse_standard_timing( + struct edid_3d_data_t *mhl_edid_3d_data, + struct EDID_block0_t *p_EDID_block_0) +{ + uint8_t i; + uint8_t AR_code; + + MHL_TX_EDID_INFO("Parsing Standard Timing:\n"); + MHL_TX_EDID_INFO("========================\n"); + + for (i = 0; + i < + sizeof(p_EDID_block_0->standard_timings) / + sizeof(p_EDID_block_0->standard_timings[0]); i += 2) { + if ((1 == + p_EDID_block_0->standard_timings[i]. + horz_pix_div_8_minus_31) + && (1 == + p_EDID_block_0->standard_timings[i]. + field_refresh_rate_minus_60) + && (0 == + p_EDID_block_0->standard_timings[i].image_aspect_ratio) + ) { + /* per VESA EDID standard, Release A, Revision 1, + * February 9, 2000, Sec. 3.9 + */ + MHL_TX_EDID_INFO("Standard Timing Undefined\n"); + } else { + uint16_t horz_active = + (uint16_t) ((p_EDID_block_0->standard_timings[i]. + horz_pix_div_8_minus_31 + 31) * 8); + uint16_t vert_active = 0; + uint16_t refresh_rate_in_milliHz = + (uint16_t) (p_EDID_block_0->standard_timings[i]. + field_refresh_rate_minus_60 + + 60) * 1000; + char *psz_ratio_string = ""; + + /* per VESA EDID standard, Release A, Revision 1, + * February 9, 2000, Table 3.15 + */ + AR_code = + p_EDID_block_0->standard_timings[i]. + image_aspect_ratio; + + switch (AR_code) { + case iar_16_to_10: + psz_ratio_string = "16:10"; + vert_active = horz_active * 10 / 16; + break; + + case iar_4_to_3: + psz_ratio_string = "4:3"; + vert_active = horz_active * 3 / 4; + break; + + case iar_5_to_4: + psz_ratio_string = "5:4"; + vert_active = horz_active * 4 / 5; + break; + + case iar_16_to_9: + psz_ratio_string = "16:9"; + vert_active = horz_active * 9 / 16; + break; + } + MHL_TX_EDID_INFO + ("Aspect Ratio: %5s %4d x %4d at %3d Hz.\n", + psz_ratio_string, horz_active, vert_active, + refresh_rate_in_milliHz); + + if (!is_MHL_timing_mode + (mhl_edid_3d_data, horz_active, vert_active, + refresh_rate_in_milliHz, 0, NULL, 0)) { + /* disable this mode */ + p_EDID_block_0->standard_timings[i]. + horz_pix_div_8_minus_31 = 1; + p_EDID_block_0->standard_timings[i]. + field_refresh_rate_minus_60 = 1; + p_EDID_block_0->standard_timings[i]. + image_aspect_ratio = 0; + MHL_TX_EDID_INFO("Disabled\n\n"); + } + } + } +} + +/* + FUNCTION : si_mhl_tx_parse_block_zero_timing_descriptors() + PURPOSE : Parse EDID Block 0 timing descriptors per EEDID 1.3 + standard. printf() values to screen. + INPUT PARAMS : Pointer to the 128 byte array where the data read from EDID + Block0 is stored. + OUTPUT PARAMS: None + GLOBALS USED : None + RETURNS : Void +*/ + +static void si_mhl_tx_parse_block_zero_timing_descriptors( + struct edid_3d_data_t *mhl_edid_3d_data, + struct EDID_block0_t *p_EDID_block_0) +{ + uint8_t i; + uint8_t is_timing = 0; + si_mhl_tx_parse_established_timing(mhl_edid_3d_data, p_EDID_block_0); + si_mhl_tx_parse_standard_timing(mhl_edid_3d_data, p_EDID_block_0); + + for (i = 0; i < + sizeof(p_EDID_block_0->detailed_timing_descriptors) / + sizeof(p_EDID_block_0->detailed_timing_descriptors[0]); i++) { + MHL_TX_EDID_INFO + ("EDID Block #0, Detailed Descriptor Number %d:\n", (int)i); + MHL_TX_EDID_INFO + ("============================================\n\n"); + si_mhl_tx_parse_detailed_timing_descriptor(mhl_edid_3d_data, + (union _18_byte_descriptor_u *) &p_EDID_block_0-> + detailed_timing_descriptors[i], EDID_BLOCK_0, + &is_timing, 0, NULL); + if (is_timing) + ++mhl_edid_3d_data->parse_data.num_vesa_timing_dtds; + } +} + +/* + FUNCTION : bool si_mhl_tx_do_edid_checksum() + PURPOSE : Calculte checksum of the 128 byte block pointed to by the + pointer passed as parameter + INPUT PARAMS : Pointer to a 128 byte block whose checksum needs to be + calculated + OUTPUT PARAMS: None + GLOBALS USED : None + RETURNS : true if chcksum is 0. false if not. +*/ + +static bool si_mhl_tx_do_edid_checksum(uint8_t *p_EDID_block_data) +{ + uint8_t i; + uint8_t checksum = 0; + + for (i = 0; i < EDID_BLOCK_SIZE; i++) + checksum += p_EDID_block_data[i]; + + if (checksum) + return false; + + return true; +} + +/* + * Checks if EDID header is correct per VESA E-EDID standard + * Must be 00 FF FF FF FF FF FF 00 + */ + +#define EDID_OFFSET_HEADER_FIRST_00 0x00 +#define EDID_OFFSET_HEADER_FIRST_FF 0x01 +#define EDID_OFFSET_HEADER_LAST_FF 0x06 +#define EDID_OFFSET_HEADER_LAST_00 0x07 + +#ifndef MANUAL_EDID_FETCH +static +#endif +bool si_mhl_tx_check_edid_header(struct edid_3d_data_t *mhl_edid_3d_data, + void *pdata) +{ + struct EDID_block0_t *p_EDID = (struct EDID_block0_t *)pdata; + uint8_t i = 0; + + if (0 != p_EDID->header_data[EDID_OFFSET_HEADER_FIRST_00]) { + DUMP_EDID_BLOCK(1, p_EDID, sizeof(*p_EDID)); + MHL_TX_DBG_ERR("%sEDID 0 first check failed%s\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); + return false; + } + + for (i = EDID_OFFSET_HEADER_FIRST_FF; i <= EDID_OFFSET_HEADER_LAST_FF; + i++) { + if (0xFF != p_EDID->header_data[i]) { + DUMP_EDID_BLOCK(1, p_EDID, sizeof(*p_EDID)); + MHL_TX_DBG_ERR("%sEDID -1 check failed%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT); + return false; + } + } + + if (0x00 != p_EDID->header_data[EDID_OFFSET_HEADER_LAST_00]) { + DUMP_EDID_BLOCK(1, p_EDID, sizeof(*p_EDID)); + MHL_TX_DBG_ERR("EDID 0 last check failed\n"); + return false; + } + + return true; +} + +void SiiMhlTxMakeItDVI(struct edid_3d_data_t *mhl_edid_3d_data, + struct EDID_block0_t *p_EDID_block_0) +{ + /* Make it DVI */ + mhl_edid_3d_data->parse_data.HDMI_sink = false; +#ifdef MHL3_DVI_SUPPORT_FORCE_HDMI + if (si_mhl_tx_drv_connection_is_mhl3(mhl_edid_3d_data->dev_context)) { + /* + MHL3 connections always require infoframes + So present an EDID that indicates HDMI + so that our upstream sink will send infoframes + */ + + uint8_t hdmi_proxy_edid_block1[EDID_BLOCK_SIZE] = { + 0x02, 0x03, 0x19, 0x70, + (DBTC_VIDEO_DATA_BLOCK << 5) | 0x0B, + 0x01, /* */ + 0x03, /* 1280x720p60 */ + 0x04, + 0x05, /* 1920x1080i60 */ + 0x90, + 0x20, + 0x11, + 0x12, + 0x13, + 0x14, + 0x1f, + (DBTC_VENDOR_SPECIFIC_DATA_BLOCK << 5) | 0x08, + 0x03, 0x0C, 0x00, /* HDMI LLC */ + 0x10, 0x00, 0x00, 0x2D, 0x00, + + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + struct EDID_block0_t *p_EDID_block_0 = + (struct EDID_block0_t *) + &mhl_edid_3d_data->EDID_block_data[0]; + MHL_TX_DBG_ERR("HDMI EDID for MHL3 dongle\n") + p_EDID_block_0->extension_flag = 1; + memcpy(&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], + hdmi_proxy_edid_block1, EDID_BLOCK_SIZE); + mhl_edid_3d_data->EDID_block_data[2*EDID_BLOCK_SIZE-1] = + calculate_generic_checksum(&mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE], + 0, EDID_BLOCK_SIZE); + } else +#endif + { + uint8_t *p_EDID_block_data = (uint8_t *) p_EDID_block_0; + uint8_t counter; + + p_EDID_block_0->extension_flag = 0; + + /* blank out the second block of the upstream EDID */ + MHL_TX_DBG_INFO("DVI EDID ...Setting second block to 0xFF %d\n", + (uint16_t) EDID_REV_ADDR_ERROR); + p_EDID_block_data += EDID_BLOCK_SIZE; + for (counter = 0; counter < EDID_BLOCK_SIZE; counter++) + p_EDID_block_data[counter] = 0xFF; + MHL_TX_DBG_ERR("EDID: second block now all 0xFF\n"); + } + +} + +static void SiiMhlTx3dReq(struct edid_3d_data_t *mhl_edid_3d_data) +{ + MHL_TX_EDID_INFO("Mhl2Tx: outputMode: %s\n", + mhl_edid_3d_data->parse_data. + HDMI_sink ? "HDMI" : "DVI"); + if (mhl_edid_3d_data->parse_data.HDMI_sink) { + uint8_t mhl_peer_version; + mhl_peer_version = + si_get_peer_mhl_version(mhl_edid_3d_data->dev_context); + if (mhl_peer_version >= 0x20) { + struct drv_hw_context *hw_context = + mhl_edid_3d_data->drv_context; + MHL_TX_EDID_INFO("MHL 2.x sink detected\n"); + + mhl_edid_3d_data->parse_data.flags. + parse_3d_in_progress = 1; + hw_context->callbacks.display_timing_enum_begin( + hw_context->callbacks.context); + /* tell sink to begin sending 3D, etc. write bursts */ + si_mhl_tx_send_3d_req_or_feat_req(mhl_edid_3d_data-> + dev_context); + + mhl_edid_3d_data->parse_data.video_data_block_index = 0; + mhl_edid_3d_data->parse_data.burst_entry_count_3d_dtd = + 0; + mhl_edid_3d_data->parse_data.vesa_dtd_index = 0; + mhl_edid_3d_data->parse_data.burst_entry_count_3d_vic = + 0; + mhl_edid_3d_data->parse_data.vic_2d_index = 0; + mhl_edid_3d_data->parse_data.vic_3d_index = 0; + mhl_edid_3d_data->parse_data.cea_861_dtd_index = 0; + mhl_edid_3d_data->parse_data.num_vesa_timing_dtds = 0; + mhl_edid_3d_data->parse_data.num_cea_861_timing_dtds = + 0; + + mhl_edid_3d_data->hev_dtd_info.num_items = 0; + mhl_edid_3d_data->hev_vic_info.num_items = 0; + mhl_edid_3d_data->_3d_dtd_info.num_items = 0; + mhl_edid_3d_data->_3d_vic_info.num_items = 0; + + mhl_edid_3d_data->hev_dtd_info.index = 0; + mhl_edid_3d_data->hev_vic_info.index = 0; + mhl_edid_3d_data->_3d_dtd_info.index = 0; + mhl_edid_3d_data->_3d_vic_info.index = 0; + + SET_3D_FLAG(mhl_edid_3d_data, FLAGS_SENT_3D_REQ); + CLR_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_DONE); + CLR_3D_FLAG(mhl_edid_3d_data, FLAGS_BURST_3D_VIC_DONE); + CLR_3D_FLAG(mhl_edid_3d_data, + FLAGS_BURST_3D_DTD_VESA_DONE); + } else { + MHL_TX_EDID_INFO("MHL 1.x sink detected\n"); + si_mhl_tx_prune_edid(mhl_edid_3d_data); + } + } else { + struct EDID_block0_t *p_EDID_block_0 = (struct EDID_block0_t *) + &mhl_edid_3d_data->EDID_block_data[0]; + uint8_t dtd_limit; + /* prune the DTDs in block 0 for DVI sinks */ + dtd_limit = + sizeof(p_EDID_block_0->detailed_timing_descriptors) / + sizeof(p_EDID_block_0->detailed_timing_descriptors[0]); + tx_prune_dtd_list(mhl_edid_3d_data, + (union _18_byte_descriptor_u *) &p_EDID_block_0-> + detailed_timing_descriptors[0], dtd_limit); + p_EDID_block_0->checksum = 0; + p_EDID_block_0->checksum = + calculate_generic_checksum((uint8_t *)p_EDID_block_0, 0, + sizeof(*p_EDID_block_0)); +#ifndef EDID_PASSTHROUGH + if (0 == si_mhl_tx_drv_set_upstream_edid( + mhl_edid_3d_data->drv_context, + mhl_edid_3d_data->EDID_block_data, + 2 * EDID_BLOCK_SIZE)) +#endif + { + SET_3D_FLAG(mhl_edid_3d_data, FLAGS_EDID_READ_DONE); + } + } +} + +static uint8_t parse_861_short_descriptors( + struct edid_3d_data_t *mhl_edid_3d_data, uint8_t *p_EDID_block_data) +{ + uint8_t i; + struct CEA_extension_t *p_CEA_extension; + struct CEA_extension_version_3_t *p_CEA_extension_version_3; + union { + uint8_t *puc_data_block; + struct CEA_data_block_collection_t *p_CEA_data_block; + } p_data_u; + uint8_t *puc_long_descriptors; + struct extended_tag_code_t ext_tag_code; + struct video_capability_data_block_t *p_video_capability; + struct video_capability_data_payload_t *p_payload; + struct colorimetry_data_block_t *p_colorimetry; + struct colorimetry_data_payload_t *p_cpayload; + struct vsdb_t *p_vsdb; + uint8_t *puc_next_db; + + p_CEA_extension = (struct CEA_extension_t *) p_EDID_block_data; + p_CEA_extension_version_3 = &p_CEA_extension->version_u.version3; + + if (EDID_EXTENSION_TAG != p_CEA_extension->tag) { + enum NumExtensions_e ret_val; + MHL_TX_DBG_ERR("%sEDID -> Non-CEA Extension read fifo again\n\t" + "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x%s\n", ANSI_ESC_RED_TEXT, p_EDID_block_data[0], + p_EDID_block_data[1], p_EDID_block_data[2], + p_EDID_block_data[3], p_EDID_block_data[4], + p_EDID_block_data[5], p_EDID_block_data[6], + p_EDID_block_data[7], ANSI_ESC_RESET_TEXT); + ret_val = si_mhl_tx_drv_get_edid_fifo_next_block( + mhl_edid_3d_data->drv_context, p_EDID_block_data); + if (ne_SUCCESS != ret_val) + return ret_val; + else if (EDID_EXTENSION_TAG != p_CEA_extension->tag) + return EDID_EXT_TAG_ERROR; + + } + if (EDID_REV_THREE != p_CEA_extension->revision) { + MHL_TX_DBG_ERR("EDID -> Non-HDMI EIA-861 Revision ID. Expected " + "%02X. Got %02X\n", (int)EDID_REV_THREE, + (int)p_CEA_extension->revision); + return EDID_REV_ADDR_ERROR; + } + + /* block offset where long descriptors start */ + puc_long_descriptors = ((uint8_t *) p_CEA_extension) + + p_CEA_extension->byte_offset_to_18_byte_descriptors; + + /* byte #3 of CEA extension version 3 */ + mhl_edid_3d_data->parse_data.underscan = + p_CEA_extension_version_3->misc_support. + underscan_IT_formats_by_default ? 1 : 0; + mhl_edid_3d_data->parse_data.basic_audio = + p_CEA_extension_version_3->misc_support.basic_audio_support ? 1 : 0; + mhl_edid_3d_data->parse_data.YCbCr_4_4_4 = + p_CEA_extension_version_3->misc_support.YCrCb444_support; + mhl_edid_3d_data->parse_data.YCbCr_4_2_2 = + p_CEA_extension_version_3->misc_support.YCrCb422_support; + MHL_TX_EDID_INFO("misc support index-> %02x\n", + *((uint8_t *) &p_CEA_extension_version_3->misc_support)); + + p_data_u.puc_data_block = + &p_CEA_extension->version_u.version3.Offset4_u. + data_block_collection[0]; + + while (p_data_u.puc_data_block < puc_long_descriptors) { + enum data_block_tag_code_e tag_code; + uint8_t data_block_length; + + tag_code = p_data_u.p_CEA_data_block->header.fields.tag_code; + data_block_length = p_data_u.p_CEA_data_block->header.fields. + length_following_header; + MHL_TX_EDID_INFO("tag_code:%d data_block_length:%d\n", + tag_code, data_block_length); + if ((p_data_u.puc_data_block + data_block_length) > + puc_long_descriptors) { + MHL_TX_DBG_ERR( + "EDID -> V Descriptor Overflow\n"); + return EDID_V_DESCR_OVERFLOW; + } + + /* num of short video descriptors in this data block */ + i = 0; + switch (tag_code) { + case DBTC_VIDEO_DATA_BLOCK: + MHL_TX_EDID_INFO("DBTC_VIDEO_DATA_BLOCK:\n"); + if (mhl_edid_3d_data->parse_data. + num_video_data_blocks < + NUM_VIDEO_DATA_BLOCKS_LIMIT) { + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d[ + mhl_edid_3d_data->parse_data. + num_video_data_blocks] = + (struct video_data_block_t *) + p_data_u.puc_data_block; + + /* each SVD is 1 byte long */ + while (i < data_block_length) { + uint8_t VIC; + VIC = + mhl_edid_3d_data-> + parse_data.p_video_data_blocks_2d + [mhl_edid_3d_data->parse_data. + num_video_data_blocks]-> + short_descriptors[i].VIC; + MHL_TX_EDID_INFO( + "short desc[%d]: VIC: %d\n", + i, VIC); + if (!IsQualifiedMhlVIC + (mhl_edid_3d_data, VIC, 0, + NULL)) { + mhl_edid_3d_data->parse_data. + p_video_data_blocks_2d + [mhl_edid_3d_data-> + parse_data. + num_video_data_blocks]-> + short_descriptors + [i].VIC = 0; + } + i++; + } + MHL_TX_EDID_INFO( + "EDID -> %d descriptors in " + "Short Descriptor Video Block\n", + i); + mhl_edid_3d_data->parse_data. + num_video_data_blocks++; + } + break; + + case DBTC_AUDIO_DATA_BLOCK: + { + struct audio_data_block_t *p_audio_data_block = + (struct audio_data_block_t *) p_data_u. + puc_data_block; + uint8_t a_desc_index = 0; + + while (i < + data_block_length / + sizeof(p_audio_data_block-> + short_audio_descriptors[0])) { + mhl_edid_3d_data->parse_data. + audio_descriptors[a_desc_index] + = p_audio_data_block-> + short_audio_descriptors[i]; + a_desc_index++; + i++; + } + MHL_TX_EDID_INFO( + "EDID -> Short Descriptor Audio Block\n"); + } + break; + + case DBTC_SPEAKER_ALLOCATION_DATA_BLOCK: + { + struct speaker_allocation_data_block_t + *p_speaker = + (struct speaker_allocation_data_block_t *) + p_data_u.puc_data_block; + + *((struct speaker_allocation_flags_t *) + &mhl_edid_3d_data->parse_data. + speaker_alloc[i++]) = + p_speaker->payload.speaker_alloc_flags; + MHL_TX_EDID_INFO( + "EDID -> Short Desc Speaker Alloc Block\n"); + } + break; + + case DBTC_USE_EXTENDED_TAG: + ext_tag_code = p_data_u.p_CEA_data_block-> + payload_u.extended_tag; + switch (ext_tag_code.etc) { + case ETC_VIDEO_CAPABILITY_DATA_BLOCK: + p_video_capability = + (struct video_capability_data_block_t *) + p_data_u.puc_data_block; + p_payload = &p_video_capability->payload; + mhl_edid_3d_data->parse_data. + video_capability_flags = + *((uint8_t *)p_payload); + mhl_edid_3d_data->parse_data. + p_video_capability_data_block = + p_video_capability; + MHL_TX_EDID_INFO( + "EDID -> Short Desc Video Cap Block\n"); + break; + + case ETC_COLORIMETRY_DATA_BLOCK: + p_colorimetry = + (struct colorimetry_data_block_t *)p_data_u. + puc_data_block; + p_cpayload = &p_colorimetry->payload; + mhl_edid_3d_data->parse_data. + colorimetry_support_flags = + p_cpayload->ci_data.xvYCC; + mhl_edid_3d_data->parse_data.meta_data_profile = + p_cpayload->cm_meta_data.meta_data; + + MHL_TX_EDID_INFO( + "EDID -> Short Desc Colorimetry Block\n"); + break; + } + + break; + + case DBTC_VENDOR_SPECIFIC_DATA_BLOCK: + p_vsdb = (struct vsdb_t *) p_data_u.puc_data_block; + puc_next_db = ((uint8_t *) &p_vsdb->header) + + sizeof(p_vsdb->header) + + data_block_length; + + if ((p_vsdb->IEEE_OUI[0] == 0x03) + && (p_vsdb->IEEE_OUI[1] == 0x0C) + && (p_vsdb->IEEE_OUI[2] == 0x00) + ) { + struct HDMI_LLC_vsdb_payload_t + *p_HDMI_vs_payload = + &p_vsdb->payload_u.HDMI_LLC; + + mhl_edid_3d_data->parse_data.p_HDMI_vsdb = + p_vsdb; + SII_ASSERT(5 <= data_block_length, + ("unexpected data_block_length\n")); + mhl_edid_3d_data->parse_data.HDMI_sink = true; + + /* CEC Physical address */ + *((struct HDMI_LLC_BA_t *) + &mhl_edid_3d_data->parse_data.CEC_A_B) = + p_HDMI_vs_payload->B_A; + *((struct HDMI_LLC_DC_t *) + &mhl_edid_3d_data->parse_data.CEC_C_D) = + p_HDMI_vs_payload->D_C; + /* Offset of 3D_Present bit in VSDB */ + if (p_HDMI_vs_payload->byte8. + latency_fields_present) { + if (p_HDMI_vs_payload->byte8. + I_latency_fields_present) { + mhl_edid_3d_data-> + parse_data. + p_byte_13_through_byte_15 + = + &p_HDMI_vs_payload-> + vsdb_fields_b9_thru_b15. + vsdb_all_fields_b9_thru_b15. + byte_13_through_byte_15; + } else { + mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15 = + &p_HDMI_vs_payload-> + vsdb_fields_b9_thru_b15. + vsdb_b9_to_b15_no_i_latency. + byte_13_through_byte_15; + } + } else { + if (p_HDMI_vs_payload->byte8. + I_latency_fields_present) { + mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15 = + &p_HDMI_vs_payload-> + vsdb_fields_b9_thru_b15. + vsdb_b9_to_b15_no_p_latency. + byte_13_through_byte_15; + } else { + mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15 = + &p_HDMI_vs_payload-> + vsdb_fields_b9_thru_b15. + vsdb_b9_to_b15_no_latency. + byte_13_through_byte_15; + } + } + if (((u8 *)&mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->byte13) >= + puc_next_db) { + mhl_edid_3d_data->parse_data. + _3D_supported = false; + } else if (mhl_edid_3d_data->parse_data. + p_byte_13_through_byte_15->byte13. + _3D_present) { + mhl_edid_3d_data->parse_data. + _3D_supported = true; + } else { + mhl_edid_3d_data->parse_data. + _3D_supported = false; + } + + MHL_TX_EDID_INFO( + "EDID indicates %s3D support\n", + mhl_edid_3d_data->parse_data. + _3D_supported ? "" : "NO "); + } + + MHL_TX_EDID_INFO( + "EDID -> Short Descriptor Vendor Block\n\n"); + break; + case DBTC_TERMINATOR: + MHL_TX_EDID_INFO("found terminator tag code\n"); + return EDID_SHORT_DESCRIPTORS_OK; + break; + + default: + MHL_TX_DBG_ERR("EDID -> Unknown Tag Code:0x%02x\n", + (uint16_t) tag_code); + return EDID_UNKNOWN_TAG_CODE; + + } + p_data_u.puc_data_block += + sizeof(p_data_u.p_CEA_data_block->header) + + data_block_length; + } + + return EDID_SHORT_DESCRIPTORS_OK; +} + +static uint8_t parse_861_block(struct edid_3d_data_t *mhl_edid_3d_data, + uint8_t *p_EDID_block_data) +{ + uint8_t err_code; + struct CEA_extension_t *p_CEA_extension = + (struct CEA_extension_t *) p_EDID_block_data; + + mhl_edid_3d_data->parse_data.p_HDMI_vsdb = NULL; + + MHL_TX_EDID_INFO("tag:place holder EDID block:%p\n", p_EDID_block_data); + if (EDID_EXTENSION_BLOCK_MAP == p_CEA_extension->tag) { + struct block_map_t *p_block_map; + int i; + + p_block_map = (struct block_map_t *) p_EDID_block_data; + + MHL_TX_EDID_INFO("Edid: Block Map\n"); + /* loop limit is adjusted by one to account for block map */ + for (i = 0; + i < mhl_edid_3d_data->parse_data.num_EDID_extensions - 1; + ++i) { + if (EDID_EXTENSION_TAG != p_block_map->block_tags[i]) { + MHL_TX_EDID_INFO( + "Edid: Adjusting number of extensions " + "according to Block Map\n"); + mhl_edid_3d_data->parse_data. + num_EDID_extensions = i; + break; + } + } + + return EDID_OK; + + } else { + err_code = + parse_861_short_descriptors(mhl_edid_3d_data, + p_EDID_block_data); + if (err_code != EDID_SHORT_DESCRIPTORS_OK) { + MHL_TX_DBG_ERR("EDID: Non-HDMI extension Errcode:%d\n", + (uint16_t) err_code); + return err_code; + } + + /* adjust */ + err_code = + si_mhl_tx_parse_861_long_descriptors(mhl_edid_3d_data, + p_EDID_block_data); + if (err_code != EDID_LONG_DESCRIPTORS_OK) { + MHL_TX_DBG_ERR("EDID: Errcode:%d\n", + (uint16_t) err_code); + return err_code; + } + } + return EDID_OK; +} + +void si_mhl_tx_handle_atomic_hw_edid_read_complete( + struct edid_3d_data_t *mhl_edid_3d_data) +{ + struct EDID_block0_t *p_EDID_block_0 = + (struct EDID_block0_t *)&mhl_edid_3d_data->EDID_block_data[0]; + uint8_t counter; + MHL_TX_EDID_INFO("tag: Entire EDID Read complete\n"); +#ifdef EDID_PASSTHROUGH + si_mhl_tx_drv_set_upstream_edid(mhl_edid_3d_data->drv_context, + mhl_edid_3d_data->EDID_block_data, + 2 * EDID_BLOCK_SIZE); +#endif + /* assume DVI until we encounter an HDMI VSDB */ + mhl_edid_3d_data->parse_data.HDMI_sink = false; + + /* Parse EDID Block #0 Desctiptors */ + si_mhl_tx_parse_block_zero_timing_descriptors(mhl_edid_3d_data, + p_EDID_block_0); + + MHL_TX_EDID_INFO("EDID -> Number of 861 Extensions = %d\n", + (uint16_t) p_EDID_block_0->extension_flag); + + mhl_edid_3d_data->parse_data.num_EDID_extensions = + p_EDID_block_0->extension_flag; + if (0 == p_EDID_block_0->extension_flag) { + /* No extensions to worry about */ + DUMP_EDID_BLOCK(0, (uint8_t *) p_EDID_block_0, EDID_BLOCK_SIZE); + MHL_TX_DBG_ERR + ("EDID -> no extensions, assuming DVI. tag offset:0x%x\n", + SII_OFFSETOF(struct EDID_block0_t, extension_flag)); + SiiMhlTxMakeItDVI(mhl_edid_3d_data, p_EDID_block_0); + } else { + uint8_t Result = EDID_OK; + MHL_TX_EDID_INFO(" tag:place holder\n"); + /* number of extensions is one less than number of blocks */ + for (counter = 1; + counter <= + mhl_edid_3d_data->parse_data.num_EDID_extensions; + ++counter) { + MHL_TX_EDID_INFO + (" counter:%d tag:place holder EDID block:%p\n", + counter, + &mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE * counter]); + Result = + parse_861_block(mhl_edid_3d_data, + &mhl_edid_3d_data-> + EDID_block_data[EDID_BLOCK_SIZE * + counter]); + if (EDID_OK != Result) { + MHL_TX_DBG_ERR + ("EDID -> Ext[%d] is not HDMI: Result:%d\n", + counter, (uint16_t) Result); + SiiMhlTxMakeItDVI(mhl_edid_3d_data, + p_EDID_block_0); + Result = EDID_OK; + } + } + } + /* + * Since our working copy of the block zero EDID gets modified, + * we must re-compute its checksum + */ + p_EDID_block_0->checksum = 0; + p_EDID_block_0->checksum = + calculate_generic_checksum((uint8_t *) p_EDID_block_0, 0, + sizeof(*p_EDID_block_0)); + + SiiMhlTx3dReq(mhl_edid_3d_data); +} + +static void handle_emsc_edid_complete( + struct edid_3d_data_t *mhl_edid_3d_data) +{ + uint8_t *normal_edid = &mhl_edid_3d_data->EDID_block_data[0]; + if (NULL == mhl_edid_3d_data->p_edid_emsc) { + MHL_TX_DBG_ERR("%soops!%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT) + MHL_TX_DBG_ERR("normative EDID\n") + si_edid_reset(mhl_edid_3d_data); + si_mhl_tx_request_first_edid_block(mhl_edid_3d_data-> + dev_context->edid_parser_context); + } else { + size_t edid_size = EDID_BLOCK_SIZE * + mhl_edid_3d_data->num_edid_emsc_blocks; + memcpy(normal_edid, + mhl_edid_3d_data->p_edid_emsc, edid_size); + + DUMP_EDID_BLOCK(0, normal_edid, edid_size); + si_mhl_tx_handle_atomic_hw_edid_read_complete(mhl_edid_3d_data); + } +} +/* + EXPORTED FUNCTIONS +*/ + +void si_mhl_tx_initiate_edid_sequence(struct edid_3d_data_t *mhl_edid_3d_data) +{ + MHL_TX_EDID_INFO("tag:\n"); + if (si_mhl_tx_drv_cbus_ready_for_edid(mhl_edid_3d_data->dev_context)) { + mhl_edid_3d_data->parse_data.num_video_data_blocks = 0; + + if (mhl_edid_3d_data->dev_context->sii_adopter_id) { + MHL_TX_DBG_ERR("vendor specific EDID\n") + handle_emsc_edid_complete(mhl_edid_3d_data); + } else { + MHL_TX_DBG_ERR("normative EDID\n") + /* + Initiate the EDID reading sequence see + SiiMhlTxMscCommandDone for additional processing. + */ + + si_edid_reset(mhl_edid_3d_data); + si_mhl_tx_request_first_edid_block(mhl_edid_3d_data-> + dev_context); + } + } +} + +#define EDID_BLOCK_0_HEADER_SIZE 8 + +static int do_block_0(struct edid_3d_data_t *mhl_edid_3d_data) +{ + uint8_t *pb_data = &mhl_edid_3d_data->EDID_block_data[0]; + struct EDID_block0_t *p_EDID = + (struct EDID_block0_t *)&mhl_edid_3d_data->EDID_block_data; + + if (ne_SUCCESS == si_mhl_tx_drv_get_edid_fifo_partial_block( + mhl_edid_3d_data->drv_context, 0, + EDID_BLOCK_0_HEADER_SIZE, pb_data)) { + if (!si_mhl_tx_check_edid_header(mhl_edid_3d_data, p_EDID)) { + MHL_TX_DBG_ERR + ("%sEDID -> Incorrect Header pb_data:%s" + "\t0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x 0x%02x\n", + ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT, + pb_data[0], pb_data[1], pb_data[2], + pb_data[3], pb_data[4], pb_data[5], + pb_data[6], pb_data[7] + ); + DUMP_EDID_BLOCK(1, pb_data, + EDID_BLOCK_0_HEADER_SIZE); + if (si_mhl_tx_check_edid_header( + mhl_edid_3d_data, &mhl_edid_3d_data-> + EDID_block_data[1])) { + MHL_TX_DBG_ERR( + "%sEDID header misaligned:%x%s\n", + ANSI_ESC_RED_TEXT, pb_data, + ANSI_ESC_RESET_TEXT); + return ne_BAD_HEADER_OFFSET_BY_1; + } else { + int ret_val; + MHL_TX_DBG_ERR( + "%sre-reading buffer:%x%s\n", + ANSI_ESC_RED_TEXT, pb_data, + ANSI_ESC_RESET_TEXT); + si_mhl_tx_drv_skip_to_next_edid_block( + mhl_edid_3d_data->drv_context); + ret_val = + si_mhl_tx_drv_get_edid_fifo_next_block( + mhl_edid_3d_data->drv_context, + pb_data); + if (ne_SUCCESS != ret_val) { + MHL_TX_DBG_ERR("loss of HPD\n", + pb_data); + return ret_val; + } else + if (!si_mhl_tx_check_edid_header( + mhl_edid_3d_data, &mhl_edid_3d_data-> + EDID_block_data[0])) { + MHL_TX_DBG_ERR( + "%sBAD Header\n\t%02x %02x %02x " + "%02x %02x %02x %02x %02x%s\n", + ANSI_ESC_RED_TEXT, + pb_data[0], pb_data[1], + pb_data[2], pb_data[3], + pb_data[4], pb_data[5], + pb_data[6], pb_data[7], + ANSI_ESC_RESET_TEXT); + return ne_BAD_HEADER; + } + } + } else if (ne_SUCCESS != + si_mhl_tx_drv_get_edid_fifo_partial_block + (mhl_edid_3d_data->drv_context, + EDID_BLOCK_0_HEADER_SIZE, + EDID_BLOCK_SIZE - EDID_BLOCK_0_HEADER_SIZE, + &pb_data[EDID_BLOCK_0_HEADER_SIZE])) { + return ne_NO_HPD; + } + } + + return ne_SUCCESS; +} + +int si_mhl_tx_get_num_cea_861_extensions(void *context, uint8_t block_number) +{ + struct edid_3d_data_t *mhl_edid_3d_data = + (struct edid_3d_data_t *)context; + + struct EDID_block0_t *p_EDID_block_0 = (struct EDID_block0_t *) + &mhl_edid_3d_data->EDID_block_data; + uint8_t limit_blocks = sizeof(mhl_edid_3d_data->EDID_block_data) / + EDID_BLOCK_SIZE; + + uint8_t *pb_data = + &mhl_edid_3d_data->EDID_block_data[EDID_BLOCK_SIZE * block_number]; + + MHL_TX_EDID_INFO("block number:%d pb_data:%x\n", block_number, pb_data); + + if (0 == block_number) { + int ret_val = do_block_0(mhl_edid_3d_data); + if (ret_val != ne_SUCCESS) + return ret_val; + } else if (ne_SUCCESS != si_mhl_tx_drv_get_edid_fifo_next_block( + mhl_edid_3d_data->drv_context, pb_data)) { + return ne_NO_HPD; + } + if (!si_mhl_tx_do_edid_checksum(pb_data)) { + MHL_TX_DBG_ERR("%sEDID -> Checksum Error pb_data:%x%s\n", + ANSI_ESC_RED_TEXT, pb_data, ANSI_ESC_RESET_TEXT); + DUMP_EDID_BLOCK(1, pb_data, EDID_BLOCK_SIZE); + return ne_BAD_CHECKSUM; + } + + if (p_EDID_block_0->extension_flag < limit_blocks) { + return p_EDID_block_0->extension_flag; + } else { + MHL_TX_DBG_ERR("%snot enough room for %d extensions%s\n", + ANSI_ESC_RED_TEXT, p_EDID_block_0->extension_flag, + ANSI_ESC_RESET_TEXT); + return (int)limit_blocks - 1; + } +} + +int si_edid_sink_is_hdmi(void *context) +{ + struct edid_3d_data_t *mhl_edid_3d_data = + (struct edid_3d_data_t *) context; + return mhl_edid_3d_data->parse_data.HDMI_sink; +} + +int si_edid_quantization_range_selectable(void *context) +{ + struct edid_3d_data_t *mhl_edid_3d_data = + (struct edid_3d_data_t *) context; + return mhl_edid_3d_data->parse_data.video_capability_flags & 0x80; +} + +int si_edid_sink_supports_YCbCr422(void *context) +{ + struct edid_3d_data_t *mhl_edid_3d_data = + (struct edid_3d_data_t *) context; + MHL_TX_EDID_INFO("Mhl2Tx: YCbCr422 support:%s\n", + mhl_edid_3d_data->parse_data. + YCbCr_4_2_2 ? "Yup" : "Nope"); + return mhl_edid_3d_data->parse_data.YCbCr_4_2_2; +} + +int si_edid_sink_supports_YCbCr444(void *context) +{ + struct edid_3d_data_t *mhl_edid_3d_data = + (struct edid_3d_data_t *) context; + MHL_TX_EDID_INFO("Mhl2Tx: YCbCr444 support:%s\n", + mhl_edid_3d_data->parse_data. + YCbCr_4_4_4 ? "Yup" : "Nope"); + return mhl_edid_3d_data->parse_data.YCbCr_4_4_4; +} + +int si_edid_find_pixel_clock_from_HDMI_VIC(void *context, uint8_t vic) +{ + if (vic < ARRAY_SIZE(hdmi_vic_info)) { + return hdmi_vic_info[vic].pixel_clock_0; + } else { + MHL_TX_DBG_ERR("vic:%d is out of range\n", vic); + return 0; + } +} + +uint8_t si_edid_map_hdmi_vic_to_mhl3_vic(void *context, uint8_t vic) +{ + if (vic < ARRAY_SIZE(hdmi_vic_info)) { + return hdmi_vic_info[vic].corresponding_MHL3_VIC; + } else { + MHL_TX_DBG_ERR("vic:%d is out of range\n", vic); + return 0; + } +} + +int si_edid_find_pixel_clock_from_AVI_VIC(void *context, uint8_t vic) +{ + if (vic < ARRAY_SIZE(VIC_info)) { + return calculate_pixel_clock(context, + (uint16_t) VIC_info[vic].columns, + (uint16_t) VIC_info[vic].rows, + (uint32_t) VIC_info[vic].field_rate_in_milliHz, vic); + } else { + MHL_TX_DBG_ERR("vic:%d is out of range\n", vic); + return 0; + } +} + +uint32_t si_edid_find_pixel_clock_from_HEV_DTD( + struct edid_3d_data_t *mhl_edid_3d_data, struct MHL_high_low_t hev_fmt) +{ + /* SEQ numbers start at one */ + uint16_t index = ENDIAN_CONVERT_16(hev_fmt) - 16384 - 1; + if (NULL == mhl_edid_3d_data->hev_dtd_list) { + MHL_TX_DBG_ERR("No HEV DTDs available\n", index) + } else if (index >= mhl_edid_3d_data->hev_dtd_info.num_items) { + MHL_TX_DBG_ERR("hev_fmt out of range: %x\n", index) + } else { + return (uint32_t) ENDIAN_CONVERT_16( + mhl_edid_3d_data->hev_dtd_list[index].a. + pixel_clock_in_MHz) * 1000000; + } + return 0; +} + +int si_edid_read_done(void *context) +{ + struct edid_3d_data_t *mhl_edid_3d_data = + (struct edid_3d_data_t *) context; + return TEST_3D_FLAG(mhl_edid_3d_data, FLAGS_EDID_READ_DONE); +} + +void si_edid_reset(struct edid_3d_data_t *mhl_edid_3d_data) +{ + int i; + uint8_t *pData = (uint8_t *)&mhl_edid_3d_data->parse_data; + + MHL_TX_DBG_INFO("\n") + /* clear out EDID parse results */ + for (i = 0; i < sizeof(mhl_edid_3d_data->parse_data); ++i) + pData[i] = 0; + + CLR_3D_FLAG(mhl_edid_3d_data, FLAGS_EDID_READ_DONE); + mhl_edid_3d_data->num_emsc_edid_extensions = 0; + mhl_edid_3d_data->num_edid_emsc_blocks = 0; + mhl_edid_3d_data->cur_edid_emsc_block = 0; +} + +struct mhl_dev_context *si_edid_create_context( + struct mhl_dev_context *dev_context, + struct drv_hw_context *drv_context) +{ + struct edid_3d_data_t *temp; + temp = kmalloc(sizeof(struct edid_3d_data_t), GFP_KERNEL); + if (temp) { + memset((void *)temp, 0, sizeof(*temp)); + temp->dev_context = dev_context; + temp->drv_context = drv_context; + validate_timings_table(); + } + return (void *)temp; +} + +void *si_edid_get_processed_edid(struct edid_3d_data_t *mhl_edid_3d_data) +{ + return mhl_edid_3d_data->EDID_block_data; +} + +void si_edid_destroy_context(struct edid_3d_data_t *mhl_edid_3d_data) +{ + if (mhl_edid_3d_data) { + kfree(mhl_edid_3d_data->hev_dtd_list); + kfree(mhl_edid_3d_data->hev_vic_list); + kfree(mhl_edid_3d_data->_3d_dtd_list); + kfree(mhl_edid_3d_data->_3d_vic_list); + kfree(mhl_edid_3d_data->p_edid_emsc); + + memset(mhl_edid_3d_data, 0, sizeof(*mhl_edid_3d_data)); + kfree(mhl_edid_3d_data); + } +} + +#ifdef ENABLE_EDID_DEBUG_PRINT +void dump_EDID_block_impl(const char *pszFunction, int iLineNum, + uint8_t override, uint8_t *pData, uint16_t length) +{ + uint16_t i; + char hex_digits[] = "0123456789ABCDEF"; + printk(KERN_DEFAULT "%s:%d EDID DATA:\n", pszFunction, iLineNum); + for (i = 0; i < length;) { + uint16_t j, k; + uint16_t temp = i; + char buffer[16*4+3]; + for (j = 0, k = 0; (j < 16) && (i < length); ++j, ++i) { + buffer[k++] = ' '; + buffer[k++] = hex_digits[(pData[i]>>4) & 0x0F]; + buffer[k++] = hex_digits[pData[i] & 0x0F]; + } + + buffer[k++] = ' '; + buffer[k++] = '|'; + buffer[k++] = ' '; + for (j = 0; (j < 16) && (temp < length); ++j, ++temp) + buffer[k++] = ((pData[temp] >= ' ') + && (pData[temp] <= 'z')) ? pData[temp] : '.'; + + printk(KERN_DEFAULT "%s\n", buffer); + } +} +#endif + +int process_emsc_edid_sub_payload(struct edid_3d_data_t *edid_context, + struct si_adopter_id_data *p_burst) +{ + uint8_t *p_block; + p_block = &p_burst->opcode_data.edid_blk.data[0]; + if (0 == p_burst->opcode_data.edid_blk.block_num) { + size_t alloc_size; + struct EDID_block0_t *edid_block0; + edid_block0 = (struct EDID_block0_t *)p_block; + si_edid_reset(edid_context); + if (NULL != edid_context->p_edid_emsc) { + MHL_TX_DBG_INFO("freeing edid bypass\n") + kfree(edid_context->p_edid_emsc); + edid_context->p_edid_emsc = NULL; + } + edid_context->num_emsc_edid_extensions = + edid_block0->extension_flag; + edid_context->num_edid_emsc_blocks = 1 + + edid_block0->extension_flag; + edid_context->cur_edid_emsc_block = 0; + alloc_size = EDID_BLOCK_SIZE * + edid_context->num_edid_emsc_blocks; + edid_context->p_edid_emsc = kmalloc(alloc_size, GFP_KERNEL); + if (NULL == edid_context->p_edid_emsc) { + MHL_TX_DBG_ERR("%skmalloc failed%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT) + return -1; + } else { + MHL_TX_DBG_ERR("block 0\n") + memcpy(edid_context->p_edid_emsc, + p_block, + EDID_BLOCK_SIZE); + if (0 == edid_block0->extension_flag) { + MHL_TX_DBG_WARN("eMSC EDID done\n") + MHL_TX_DBG_WARN("waiting for SET_HPD\n") + } + } + + } else if (edid_context->p_edid_emsc) { + size_t edid_offset; + uint8_t block_num; + edid_offset = EDID_BLOCK_SIZE * + p_burst->opcode_data.edid_blk.block_num; + block_num = p_burst->opcode_data.edid_blk.block_num; + if (edid_offset >= EDID_BLOCK_SIZE * + edid_context->num_edid_emsc_blocks) { + MHL_TX_DBG_ERR("EDID overflow %d offset: 0x%X\n", + block_num, edid_offset) + return -1; + } else if (edid_context->cur_edid_emsc_block < + edid_context->num_edid_emsc_blocks) { + MHL_TX_DBG_ERR("block %d offset 0x%X\n", + block_num, edid_offset) + DUMP_EDID_BLOCK(0, p_block, EDID_BLOCK_SIZE); + memcpy(&edid_context->p_edid_emsc[edid_offset], + p_block, EDID_BLOCK_SIZE); + edid_context->cur_edid_emsc_block++; + if (edid_context->cur_edid_emsc_block >= + edid_context->num_emsc_edid_extensions) { + MHL_TX_DBG_WARN("eMSC EDID done\n") + MHL_TX_DBG_WARN("waiting for SET_HPD\n") + } + } + } else { + MHL_TX_DBG_ERR("%s unexpected NULL pointer%s\n", + ANSI_ESC_RED_TEXT, + ANSI_ESC_RESET_TEXT) + } + + return 0; +} diff --git a/drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d_api.h b/drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d_api.h new file mode 100644 index 000000000000..02e08607db16 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d_api.h @@ -0,0 +1,183 @@ +/* + * 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. + */ + +#ifndef _SI_MHL2_EDID_3D_API_H_ +#define _SI_MHL2_EDID_3D_API_H_ + +#define EDID_BLOCK_SIZE 128 +#define BIT_EDID_FIELD_FORMAT_HDMI_TO_RGB 0x00 +#define BIT_EDID_FIELD_FORMAT_YCbCr422 0x01 +#define BIT_EDID_FIELD_FORMAT_YCbCr444 0x02 +#define BIT_EDID_FIELD_FORMAT_DVI_TO_RGB 0x03 + +struct edid_3d_flags_t { + unsigned parse_3d_in_progress:1; + unsigned FLAGS_SENT_3D_REQ:1; + unsigned FLAGS_BURST_3D_VIC_DONE:1; + unsigned FLAGS_BURST_3D_DTD_DONE:1; + + unsigned FLAGS_BURST_3D_DTD_VESA_DONE:1; + unsigned FLAGS_BURST_3D_DONE:1; + unsigned FLAGS_EDID_READ_DONE:1; + unsigned reserved:1; +}; + +#define MAX_V_DESCRIPTORS 21 +#define MAX_A_DESCRIPTORS 10 +#define MAX_SPEAKER_CONFIGURATIONS 4 +#define AUDIO_DESCR_SIZE 3 + +#define NUM_VIDEO_DATA_BLOCKS_LIMIT 3 + +struct edid_parse_data_t { + struct edid_3d_flags_t flags; + struct vsdb_t *p_HDMI_vsdb; + struct video_data_block_t * + p_video_data_blocks_2d[NUM_VIDEO_DATA_BLOCKS_LIMIT]; + struct video_capability_data_block_t *p_video_capability_data_block; + struct VSDB_byte_13_through_byte_15_t *p_byte_13_through_byte_15; + struct _3D_mask_t *p_3d_mask; + union _3D_structure_and_detail_entry_u *p_three_d; + uint8_t *p_3d_limit; + /* counter for initial EDID parsing, persists afterwards */ + uint8_t num_video_data_blocks; + /* counter for 3D write burst parsing. */ + uint8_t video_data_block_index; + uint8_t burst_entry_count_3d_vic; + uint8_t vic_2d_index; + uint8_t vic_3d_index; + uint8_t burst_entry_count_3d_dtd; + uint8_t vesa_dtd_index; + uint8_t cea_861_dtd_index; + uint8_t num_vesa_timing_dtds; + uint8_t num_cea_861_timing_dtds; + /* maximum number of audio descriptors */ + struct CEA_short_audio_descriptor_t + audio_descriptors[MAX_A_DESCRIPTORS]; + /* maximum number of speaker configurations */ + uint8_t speaker_alloc[MAX_SPEAKER_CONFIGURATIONS]; + /* "1" if DTV monitor underscans IT video formats by default */ + bool underscan; + bool basic_audio; /* Sink supports Basic Audio */ + bool YCbCr_4_4_4; /* Sink supports YCbCr 4:4:4 */ + bool YCbCr_4_2_2; /* Sink supports YCbCr 4:2:2 */ + bool HDMI_sink; /* "1" if HDMI signature found */ + /* CEC Physical address. See HDMI 1.3 Table 8-6 */ + uint8_t CEC_A_B; + uint8_t CEC_C_D; + uint8_t video_capability_flags; + /* IEC 61966-2-4 colorimetry support: 1 - xvYCC601; 2 - xvYCC709 */ + uint8_t colorimetry_support_flags; + uint8_t meta_data_profile; + bool _3D_supported; + uint8_t num_EDID_extensions; +}; + +struct mhl_dev_context; + +struct item_alloc_info_t { + size_t num_items; + size_t num_items_allocated; + size_t index; +}; + +struct edid_3d_data_t { + struct mhl_dev_context *dev_context; + struct drv_hw_context *drv_context; + struct MHL3_hev_dtd_item_t hev_dtd_payload; + struct MHL3_hev_dtd_item_t *hev_dtd_list; + struct item_alloc_info_t hev_dtd_info; + struct MHL3_hev_vic_item_t *hev_vic_list; + struct item_alloc_info_t hev_vic_info; + struct MHL3_3d_dtd_item_t *_3d_dtd_list; + struct item_alloc_info_t _3d_dtd_info; + struct MHL3_3d_vic_item_t *_3d_vic_list; + struct item_alloc_info_t _3d_vic_info; + struct edid_parse_data_t parse_data; + uint8_t num_emsc_edid_extensions; + uint8_t num_edid_emsc_blocks; + uint8_t cur_edid_emsc_block; + uint8_t *p_edid_emsc; + uint8_t EDID_block_data[4 * EDID_BLOCK_SIZE]; + +}; + +struct SI_PACK_THIS_STRUCT si_incoming_timing_t { + uint32_t calculated_pixel_clock; + uint16_t h_total; + uint16_t v_total; + uint16_t columns; + uint16_t rows; + uint16_t field_rate; + uint8_t mhl3_vic; +}; + +struct mhl_dev_context *si_edid_create_context( + struct mhl_dev_context *dev_context, + struct drv_hw_context *drv_context); + +void *si_edid_get_processed_edid(struct edid_3d_data_t *mhl_edid_3d_data); +void si_edid_destroy_context(struct edid_3d_data_t *mhl_edid_3d_data); +void si_mhl_tx_initiate_edid_sequence(struct edid_3d_data_t *mhl_edid_3d_data); +void si_mhl_tx_process_3d_vic_burst(void *context, + struct MHL2_video_format_data_t *pWriteBurstData); +void si_mhl_tx_process_3d_dtd_burst(void *context, + struct MHL2_video_format_data_t *pWriteBurstData); +void si_mhl_tx_process_hev_vic_burst(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL3_hev_vic_data_t *p_write_burst_data); +void si_mhl_tx_process_hev_dtd_a_burst(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL3_hev_dtd_a_data_t *p_burst); +void si_mhl_tx_process_hev_dtd_b_burst(struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL3_hev_dtd_b_data_t *p_burst); +uint32_t si_mhl_tx_find_timings_from_totals( + struct edid_3d_data_t *mhl_edid_3d_data, + struct si_incoming_timing_t *p_timing); +int si_edid_sink_is_hdmi(void *context); +int si_edid_quantization_range_selectable(void *context); +int si_edid_sink_supports_YCbCr422(void *context); +int si_edid_sink_supports_YCbCr444(void *context); +int si_edid_find_pixel_clock_from_HDMI_VIC(void *context, uint8_t vic); +int si_edid_find_pixel_clock_from_AVI_VIC(void *context, uint8_t vic); +uint8_t si_edid_map_hdmi_vic_to_mhl3_vic(void *context, uint8_t vic); + +enum NumExtensions_e { + ne_NO_HPD = -4, + ne_BAD_DATA = -3, + ne_BAD_CHECKSUM = ne_BAD_DATA, + ne_BAD_HEADER = -2, + ne_BAD_HEADER_OFFSET_BY_1 = -1, + ne_SUCCESS = 0 +}; + +#ifdef MANUAL_EDID_FETCH +bool si_mhl_tx_check_edid_header(struct edid_3d_data_t *mhl_edid_3d_data, + void *pdata); +#endif +int si_mhl_tx_get_num_cea_861_extensions(void *context, uint8_t block_number); +int si_edid_read_done(void *context); +void si_edid_reset(struct edid_3d_data_t *mhl_edid_3d_data); +uint8_t qualify_pixel_clock_for_mhl(struct edid_3d_data_t *mhl_edid_3d_data, + uint32_t pixel_clock_frequency, uint8_t bits_per_pixel); +uint8_t calculate_generic_checksum(void *infoFrameData, uint8_t checkSum, + uint8_t length); +uint32_t si_edid_find_pixel_clock_from_HEV_DTD( + struct edid_3d_data_t *mhl_edid_3d_data, + struct MHL_high_low_t hev_fmt); +void si_mhl_tx_display_timing_enumeration_end( + struct edid_3d_data_t *mhl_edid_3d_data); + +int process_emsc_edid_sub_payload(struct edid_3d_data_t *edid_context, + struct si_adopter_id_data *p_burst); +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_mhl_callback_api.h b/drivers/video/fbdev/msm/mhl3/si_mhl_callback_api.h new file mode 100644 index 000000000000..f9f48ecd00a6 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_mhl_callback_api.h @@ -0,0 +1,243 @@ +#ifndef _SI_MHL_CALLBACK_API_H_ +#define _SI_MHL_CALLBACK_API_H_ + +union __attribute__((__packed__)) avif_or_cea_861_dtd_u +{ + struct detailed_timing_descriptor_t cea_861_dtd; + struct avi_info_frame_t avif; +}; + +enum hpd_high_callback_status { + /* successful return values for hpd_driven_high(): */ + + /* a DTD has been written to the p_avif_or_dtd buffer instead of an AVI + * infoframe + */ + HH_FMT_DVI = 0x00000000, + /* No Vendor Specific InfoFrame provided */ + HH_FMT_HDMI_VSIF_NONE = 0x00000001, + /* HDMI vsif has been filled into p_vsif */ + HH_FMT_HDMI_VSIF_HDMI = 0x00000002, + /* MHL3 vsif has been filled into p_vsif */ + HH_FMT_HDMI_VSIF_MHL3 = 0x00000003, + /* a DTD has been written to the DTD buffer instead of an AVI infoframe + * and 8620 shall expect HDCP enabled on its HDMI input + */ + HH_FMT_DVI_HDCP_ON = 0x00000004, + /* No Vendor Specific InfoFrame provided and 8620 shall expect HDCP + * enabled on its HDMI input + */ + HH_FMT_HDMI_VSIF_NONE_HDCP_ON = 0x00000005, + /* HDMI vsif has been filled into p_vsif and 8620 shall expect HDCP + * enabled on its HDMI input + */ + HH_FMT_HDMI_VSIF_HDMI_HDCP_ON = 0x00000006, + /* MHL3 vsif has been filled into p_vsif and 8620 shall expect HDCP + * enabled on its HDMI input + */ + HH_FMT_HDMI_VSIF_MHL3_HDCP_ON = 0x00000007, + /* a DTD has been written to the DTD buffer instead of an AVI + * infoframe + */ + HH_FMT_DVI_NOT_RPT = 0x00000008, + /* No Vendor Specific InfoFrame provided */ + HH_FMT_HDMI_VSIF_NONE_NOT_RPT = 0x00000009, + /* HDMI vsif has been filled into p_vsif */ + HH_FMT_HDMI_VSIF_HDMI_NOT_RPT = 0x0000000A, + /* MHL3 vsif has been filled into p_vsif */ + HH_FMT_HDMI_VSIF_MHL3_NOT_RPT = 0x0000000B, + /* a DTD has been written to the DTD buffer instead of an AVI infoframe + * and 8620 shall expect HDCP enabled on its HDMI input + */ + HH_FMT_DVI_HDCP_ON_NOT_RPT = 0x0000000C, + /* No Vendor Specific InfoFrame provided and 8620 shall expect HDCP + * enabled on its HDMI input + */ + HH_FMT_HDMI_VSIF_NONE_HDCP_ON_NOT_RPT = 0x0000000D, + /* HDMI vsif has been filled into p_vsif and 8620 shall expect HDCP + * enabled on its HDMI input + */ + HH_FMT_HDMI_VSIF_HDMI_HDCP_ON_NOT_RPT = 0x0000000E, + /* MHL3 vsif has been filled into p_vsif and 8620 shall expect HDCP + * enabled on its HDMI input + */ + HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_RPT = 0x0000000F, + + /* failure return values for hpd_driven_high(): */ + + /* avi_max_length not large enough for AVI info frame data. */ + HH_AVI_BUFFER_TOO_SMALL = 0x80000001, + /* vsif_max_length not large enough for info frame data. */ + HH_VSIF_BUFFER_TOO_SMALL = 0x80000002, + /* The callee is not ready to start video */ + HH_VIDEO_NOT_RDY = 0x80000004 +}; + +struct __attribute__((__packed__)) si_mhl_callback_api_t { + void *context; +#if 1 + int (*display_timing_enum_begin) (void *context); + int (*display_timing_enum_item) (void *context, uint16_t columns, + uint16_t rows, uint8_t bits_per_pixel, + uint32_t vertical_refresh_rate_in_milliHz, uint16_t burst_id, + union video_burst_descriptor_u *p_descriptor); + int (*display_timing_enum_end) (void *context); +#endif + /* + hpd_driven_low: + This gets called in response to CLR_HPD messages from the MHL sink. + The upstream client that registers this callback should disable + video and all DDC access before returning. + */ + void (*hpd_driven_low) (void *context); + + /* + hpd_driven_high: + This gets called when the driver is ready for upstream video + activity. The upstream client that registers this callback should + respond in the same way in which it would respond to a rising HPD + signal (which happens prior to the call). + Parameters: + *p_edid: + + The processed EDID block(s) that the MHL driver has derived + from the downstream EDID and WRITE_BURST info associated with the + sink's responses to 3D_REQ (MHL 2.x) or FEAT_REQ (MHL 3.x and newer). + + edid_length: + + *p_emsc_edid: + The unprocessed EDID transferred via eMSC BLOCK + using SILICON_IMAGE_ADOPTER_ID + + emsc_edid_length: + The length, in bytes of the data in *p_emsc_edid + + The length, in bytes, of the data in *p_edid + + *p_hev_dtd: + The processed result of all the HEV_DTDA/HEV_DTDB WRITE_BURSTs + including the associated 3D_DTD VDI for each HEV_DTD pair. + + num_hev_dtds: + The number of MHL3_hev_dtd_t elements in *p_hev_dtd. + + p_hev_vic: + The processed result of all the HEV_VIC WRITE_BURSTs including + the associated 3D_VIC VDI for each HEV_DTD pair. + + num_hev_vic_items: + The number of MHL3_hev_vic_item_t elements in p_hev_vic. + + *p_3d_dtd_items: + The processed result of all the 3D_DTD WRITE_BURSTs including + the associated DTD from the EDID when VDI_H.HEV_FMT is zero. + + num_3d_dtd_items: + The number of MHL3_3d_dtd_item_t elements in p_3d_dtd_items; + + *p_3d_vic: + The processed result of all the 3D_VIC WRITE_BURSTs including + the associated VIC code from the EDID. + + num_3d_vic_items: + The number of MHL3_3d_vic_item_t elements in p_3d_vic. + + p_avif_or_dtd: + + If the callee sends HDMI content, it shall fill in *p_avif_or_dtd + with the contents of its outgoing AVI info frame, including the + checksum byte, and return one of the values described under the + parameter p_vsif. + + If the callee, sends DVI content, is shall fill in *p_avif_or_dtd + with a Detailed Timing Descriptor (defined in CEA-861D) that + accurately describes the timing parameters of the video which is + presented at the 8620's HDMI input and return one of: + HH_FMT_DVI + HH_FMT_DVI_HDCP_ON + HH_FMT_DVI_NOT_REPEATABLE + HH_FMT_DVI_HDCP_ON_NOT_REPEATABLE. + + This buffer will be pre-initialized to zeroes prior to the call. + + avi_max_length: + + The length of the buffer pointed to by p_avif_or_dtd. + + p_vsif: + + A buffer into which the upstream driver should + write the contents of its outgoing vendor specific + info frame, if any, including the checksum byte. This + buffer will be pre-initialized to zeroes prior to the call. + + If the callee chooses to write an HDMI vendor specific info frame + into p_vsif, it shall return one of: + HH_FMT_HDMI_VSIF_HDMI + HH_FMT_HDMI_VSIF_HDMI_HDCP_ON + HH_FMT_HDMI_VSIF_HDMI_NOT_REPEATABLE + HH_FMT_HDMI_VSIF_HDMI_HDCP_ON_NOT_REPEATABLE. + + If the callee chooses to write an MHL3 vendor specific info frame + into p_vsif, it shall return one of: + HH_FMT_HDMI_VSIF_MHL3 + HH_FMT_HDMI_VSIF_MHL3_HDCP_ON + HH_FMT_HDMI_VSIF_MHL3_NOT_REPEATABLE + HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_REPEATABLE. + + If the callee does not write a vendor specific + info frame into this buffer, the callee shall return one of: + HH_FMT_HDMI_VSIF_NONE + HH_FMT_HDMI_VSIF_NONE_HDCP_ON + HH_FMT_HDMI_VSIF_NONE_NOT_RPT + HH_FMT_HDMI_VSIF_NONE_HDCP_ON_NOT_RPT + and the 8620 will infer the contents of the outgoing MHL3 VSIF from + the contents of the HDMI VSIF (if any) presented at the 8620's HDMI + input. + + vsif_max_length: + + The length of the buffer pointed to by p_vsif. + + Return values for hpd_driven_high(): + + If the callee enabled video during the duration of this call, then + the callee shall return one of the values in + hpd_high_callback_status that do not have the sign bit set, + indicating the usage of parameters. + + If the callee did not enable video during the duration of this call, + then the callee shall indicate specific reasons for not starting + video by returning the bitwise OR of the values in + hpd_high_callback_status that do have the sign bit set. + + */ + enum hpd_high_callback_status(*hpd_driven_high) (void *context, + uint8_t *p_edid, size_t edid_length, + uint8_t *p_emsc_edid, size_t emsc_edid_length, + struct MHL3_hev_dtd_item_t *p_hev_dtd, size_t num_hev_dtds, + struct MHL3_hev_vic_item_t *p_hev_vic, size_t num_hev_vic_items, + struct MHL3_3d_dtd_item_t *p_3d_dtd_items, + size_t num_3d_dtd_items, + struct MHL3_3d_vic_item_t *p_3d_vic, size_t num_3d_vic_items, + union avif_or_cea_861_dtd_u *p_avif_or_dtd, + size_t avif_or_dtd_max_length, + union vsif_mhl3_or_hdmi_u *p_vsif, + size_t vsif_max_length); +}; + +/* call this function to register the callback structure */ +int si_8620_register_callbacks(struct si_mhl_callback_api_t *p_callbacks); + +/* call this function to change video modes */ +int si_8620_info_frame_change(enum hpd_high_callback_status status, + union avif_or_cea_861_dtd_u *p_avif_or_dtd, + size_t avif_or_dtd_max_length, + union vsif_mhl3_or_hdmi_u *p_vsif, + size_t vsif_max_length); + +/* call this function to query downstream HPD status */ +int si_8620_get_hpd_status(int *hpd_status); +int si_8620_get_hdcp2_status(uint32_t *hdcp2_status); +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_mhl_defs.h b/drivers/video/fbdev/msm/mhl3/si_mhl_defs.h new file mode 100644 index 000000000000..03fde261145f --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_mhl_defs.h @@ -0,0 +1,961 @@ +/* + * 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. + */ + +#ifndef _SI_MHL_DEFS_H_ +#define _SI_MHL_DEFS_H_ + +/* + * This file contains MHL Specs related definitions. + */ + +/* + * DEVCAP offsets + */ + +enum { + DEVCAP_OFFSET_DEV_STATE, + DEVCAP_OFFSET_MHL_VERSION, + DEVCAP_OFFSET_DEV_CAT, + DEVCAP_OFFSET_ADOPTER_ID_H, + DEVCAP_OFFSET_ADOPTER_ID_L, + DEVCAP_OFFSET_VID_LINK_MODE, + DEVCAP_OFFSET_AUD_LINK_MODE, + DEVCAP_OFFSET_VIDEO_TYPE, + DEVCAP_OFFSET_LOG_DEV_MAP, + DEVCAP_OFFSET_BANDWIDTH, + DEVCAP_OFFSET_FEATURE_FLAG, + DEVCAP_OFFSET_DEVICE_ID_H, + DEVCAP_OFFSET_DEVICE_ID_L, + DEVCAP_OFFSET_SCRATCHPAD_SIZE, + DEVCAP_OFFSET_INT_STAT_SIZE, + DEVCAP_OFFSET_RESERVED, + /* this one must be last */ + DEVCAP_SIZE +}; + +SI_PUSH_STRUCT_PACKING +struct SI_PACK_THIS_STRUCT MHLDevCap_t { + uint8_t state; + uint8_t mhl_version; + uint8_t deviceCategory; + uint8_t adopterIdHigh; + uint8_t adopterIdLow; + uint8_t vid_link_mode; + uint8_t audLinkMode; + uint8_t videoType; + uint8_t logicalDeviceMap; + uint8_t bandWidth; + uint8_t featureFlag; + uint8_t deviceIdHigh; + uint8_t deviceIdLow; + uint8_t scratchPadSize; + uint8_t int_state_size; + uint8_t reserved; +}; + +union MHLDevCap_u { + struct MHLDevCap_t mdc; + uint8_t devcap_cache[DEVCAP_SIZE]; +}; + +/* Version that this chip supports */ +#define MHL_VER_MAJOR 0x30 +#define MHL_VER_MINOR 0x02 +#define MHL_VERSION (MHL_VER_MAJOR | MHL_VER_MINOR) + +/* Device Category */ +#define MHL_DEV_CATEGORY_OFFSET DEVCAP_OFFSET_DEV_CAT +#define MHL_DEV_CATEGORY_POW_BIT 0x10 +#define MHL_DEV_CATEGORY_PLIM2_0 0xE0 + +#define I_VBUS_PRE_DISCOVERY 100 +#define I_VBUS_SOURCE_TO_DONGLE 200 +#define I_VBUS_POST_DISCOVERY 500 + +#define MHL_DEV_CAT_SINK 0x01 +#define MHL_DEV_CAT_SOURCE 0x02 +#define MHL_DEV_CAT_DONGLE 0x03 +#define MHL_DEV_CAT_SELF_POWERED_DONGLE 0x13 + +/* Video Link Mode */ +#define MHL_DEV_VID_LINK_SUPP_RGB444 0x01 +#define MHL_DEV_VID_LINK_SUPP_YCBCR444 0x02 +#define MHL_DEV_VID_LINK_SUPP_YCBCR422 0x04 +#define MHL_DEV_VID_LINK_SUPP_PPIXEL 0x08 +#define MHL_DEV_VID_LINK_SUPP_ISLANDS 0x10 +#define MHL_DEV_VID_LINK_SUPP_VGA 0x20 +#define MHL_DEV_VID_LINK_SUPP_16BPP 0x40 + +/* Audio Link Mode Support */ +#define MHL_DEV_AUD_LINK_2CH 0x01 +#define MHL_DEV_AUD_LINK_8CH 0x02 + +/* Feature Flag in the devcap */ +#define MHL_DEV_FEATURE_FLAG_OFFSET DEVCAP_OFFSET_FEATURE_FLAG +#define MHL_FEATURE_RCP_SUPPORT 0x01 +#define MHL_FEATURE_RAP_SUPPORT 0x02 +#define MHL_FEATURE_SP_SUPPORT 0x04 +#define MHL_FEATURE_UCP_SEND_SUPPORT 0x08 +#define MHL_FEATURE_UCP_RECV_SUPPORT 0x10 +#define MHL_FEATURE_RBP_SUPPORT 0x40 + +/* VIDEO TYPES */ +#define MHL_VT_GRAPHICS 0x00 +#define MHL_VT_PHOTO 0x02 +#define MHL_VT_CINEMA 0x04 +#define MHL_VT_GAMES 0x08 +#define MHL_SUPP_VT 0x80 + +/* Logical Dev Map */ +#define MHL_DEV_LD_DISPLAY 0x01 +#define MHL_DEV_LD_VIDEO 0x02 +#define MHL_DEV_LD_AUDIO 0x04 +#define MHL_DEV_LD_MEDIA 0x08 +#define MHL_DEV_LD_TUNER 0x10 +#define MHL_DEV_LD_RECORD 0x20 +#define MHL_DEV_LD_SPEAKER 0x40 +#define MHL_DEV_LD_GUI 0x80 + +/* Bandwidth */ +#define MHL_BANDWIDTH_LIMIT 22 /* 225 MHz */ + +#define MHL_STATUS_REG_CONNECTED_RDY 0x30 +#define MHL_STATUS_REG_LINK_MODE 0x31 +#define MHL_STATUS_REG_VERSION_STAT 0x32 + +#define MHL_STATUS_DCAP_RDY 0x01 +#define MHL_STATUS_XDEVCAPP_SUPP 0x02 +#define MHL_STATUS_POW_STAT 0x04 +#define MHL_STATUS_PLIM_STAT_MASK 0x38 + +#define MHL_STATUS_CLK_MODE_MASK 0x07 +#define MHL_STATUS_CLK_MODE_PACKED_PIXEL 0x02 +#define MHL_STATUS_CLK_MODE_NORMAL 0x03 +#define MHL_STATUS_PATH_EN_MASK 0x08 +#define MHL_STATUS_PATH_ENABLED 0x08 +#define MHL_STATUS_PATH_DISABLED 0x00 +#define MHL_STATUS_MUTED_MASK 0x10 + +#define MHL_RCHANGE_INT 0x20 +#define MHL_DCHANGE_INT 0x21 + +#define MHL_INT_DCAP_CHG 0x01 +#define MHL_INT_DSCR_CHG 0x02 +#define MHL_INT_REQ_WRT 0x04 +#define MHL_INT_GRT_WRT 0x08 +#define MHL2_INT_3D_REQ 0x10 +#define MHL3_INT_FEAT_REQ 0x20 +#define MHL3_INT_FEAT_COMPLETE 0x40 + +/* On INTR_1 the EDID_CHG is located at BIT 0 */ +#define MHL_INT_EDID_CHG 0x02 + +/* This contains one nibble each - max offset */ +#define MHL_INT_AND_STATUS_SIZE 0x33 +#define MHL_SCRATCHPAD_SIZE 16 +/* manually define highest number */ +#define MHL_MAX_BUFFER_SIZE MHL_SCRATCHPAD_SIZE + +#define SILICON_IMAGE_ADOPTER_ID 322 + +enum BurstId_e { + MHL_TEST_ADOPTER_ID = 0x0000, + burst_id_3D_VIC = 0x0010, + burst_id_3D_DTD = 0x0011, + burst_id_HEV_VIC = 0x0020, + burst_id_HEV_DTDA = 0x0021, + burst_id_HEV_DTDB = 0x0022, + burst_id_VC_ASSIGN = 0x0038, + burst_id_VC_CONFIRM = 0x0039, + burst_id_AUD_DELAY = 0x0040, + burst_id_ADT_BURSTID = 0x0041, + burst_id_BIST_SETUP = 0x0051, + burst_id_BIST_RETURN_STAT = 0x0052, + burst_id_BIST_DISCARD = 0x0053, + burst_id_BIST_ECHO_REQUEST = 0x0054, + burst_id_BIST_ECHO_RESPONSE = 0x0055, + burst_id_EMSC_SUPPORT = 0x0061, + burst_id_HID_PAYLOAD = 0x0062, + burst_id_BLK_RCV_BUFFER_INFO = 0x0063, + burst_id_BITS_PER_PIXEL_FMT = 0x0064, + adopter_id_RANGE_START = 0x0080, + LOCAL_ADOPTER_ID = SILICON_IMAGE_ADOPTER_ID, + /* add new burst ID's above here */ + + /* Burst ID's are a 16-bit big-endian quantity. */ + burst_id_16_BITS_REQUIRED = 0x8000 +}; + +struct SI_PACK_THIS_STRUCT MHL_high_low_t { + uint8_t high; + uint8_t low; +}; + +struct SI_PACK_THIS_STRUCT MHL_burst_id_t { + uint8_t high; + uint8_t low; +}; + +struct SI_PACK_THIS_STRUCT EMSC_BLK_ADOPT_ID_PAYLD_HDR { + struct MHL_burst_id_t burst_id; + uint8_t remaining_length; +}; + +#define ENDIAN_CONVERT_16(a) \ + ((((uint16_t)((a).high))<<8)|((uint16_t)((a).low))) +#define BURST_ID(bid) (enum BurstId_e)ENDIAN_CONVERT_16(bid) + +#define HIGH_BYTE_16(x) (uint8_t)((x >> 8) & 0xFF) +#define LOW_BYTE_16(x) (uint8_t)(x & 0xFF) +#define ENCODE_BURST_ID(id) {HIGH_BYTE_16(id), LOW_BYTE_16(id)} + +struct SI_PACK_THIS_STRUCT standard_transport_header_t { + uint8_t rx_unload_ack; + uint8_t length_remaining; +}; + +#define STD_TRANSPORT_HDR_SIZE \ + sizeof(struct SI_PACK_THIS_STRUCT standard_transport_header_t) + +struct SI_PACK_THIS_STRUCT block_rcv_buffer_info_t { + /* use the BURST_ID macro to access this */ + struct MHL_burst_id_t burst_id; + uint8_t blk_rcv_buffer_size_low; + uint8_t blk_rcv_buffer_size_high; +}; + +/* see MHL2.0 spec section 5.9.1.2 */ +struct SI_PACK_THIS_STRUCT MHL2_video_descriptor_t { + uint8_t reserved_high; + unsigned char frame_sequential:1; /*FB_SUPP*/ + unsigned char top_bottom:1; /*TB_SUPP*/ + unsigned char left_right:1; /*LR_SUPP*/ + unsigned char reserved_low:5; +}; + +struct MHL3_vdi_l_t { + unsigned char frame_sequential:1; /*FB_SUPP*/ + unsigned char top_bottom:1; /*TB_SUPP*/ + unsigned char left_right:1; /*LR_SUPP*/ + unsigned char reserved_low:5; +}; + +struct MHL3_vdi_h_t { + unsigned char reserved; +}; + +/* see MHL3.0 spec section 5.11 */ +struct SI_PACK_THIS_STRUCT MHL3_video_descriptor_t { + /* VDI_H comes before VDI_L. See Table 5-5 */ + struct MHL3_vdi_h_t vdi_h; + struct MHL3_vdi_l_t vdi_l; +}; + +struct SI_PACK_THIS_STRUCT MHL3_burst_header_t { + struct MHL_burst_id_t burst_id; + uint8_t checksum; + uint8_t total_entries; + uint8_t sequence_index; +}; + +struct SI_PACK_THIS_STRUCT MHL2_video_format_data_t { + struct MHL3_burst_header_t header; + uint8_t num_entries_this_burst; + struct MHL2_video_descriptor_t video_descriptors[5]; +}; + +struct SI_PACK_THIS_STRUCT MHL3_hev_vic_descriptor_t { + uint8_t reserved; + uint8_t vic_cea861f; +}; + +struct SI_PACK_THIS_STRUCT MHL3_hev_vic_data_t { + struct MHL3_burst_header_t header; + uint8_t num_entries_this_burst; + struct MHL3_hev_vic_descriptor_t video_descriptors[5]; +}; + +struct SI_PACK_THIS_STRUCT MHL3_hev_dtd_a_payload_t { + struct MHL_high_low_t pixel_clock_in_MHz; + struct MHL_high_low_t h_active_in_pixels; + struct MHL_high_low_t h_blank_in_pixels; + struct MHL_high_low_t h_front_porch_in_pixels; + struct MHL_high_low_t h_sync_width_in_pixels; + uint8_t h_flags; +}; + +struct SI_PACK_THIS_STRUCT MHL3_hev_dtd_b_payload_t { + struct MHL_high_low_t v_total_in_lines; + uint8_t v_blank_in_lines; /* note 7 for table 5-16 is wrong */ + uint8_t v_front_porch_in_lines; /* note 7 for table 5-16 is wrong */ + uint8_t v_sync_width_in_lines; /* note 7 for table 5-16 is wrong */ + uint8_t v_refresh_rate_in_fields_per_second; + uint8_t v_flags; + uint8_t reserved[4]; +}; + +struct SI_PACK_THIS_STRUCT MHL3_hev_dtd_a_data_t { + struct MHL3_burst_header_t header; + struct MHL3_hev_dtd_a_payload_t payload; +}; + +struct SI_PACK_THIS_STRUCT MHL3_hev_dtd_b_data_t { + struct MHL3_burst_header_t header; + struct MHL3_hev_dtd_b_payload_t payload; +}; + +struct SI_PACK_THIS_STRUCT MHL3_hev_dtd_item_t { + uint8_t sequence_index; + struct MHL3_hev_dtd_a_payload_t a; + struct MHL3_hev_dtd_b_payload_t b; + struct MHL3_video_descriptor_t _3d_info; +}; + +struct MHL3_hev_vic_item_t { + struct MHL3_hev_vic_descriptor_t mhl3_hev_vic_descriptor; + struct MHL3_video_descriptor_t _3d_info; +}; + +struct MHL3_3d_vic_item_t { + struct cea_short_descriptor_t svd; + struct MHL3_video_descriptor_t _3d_info; +}; + +struct MHL3_3d_dtd_item_t { + uint8_t index; + struct detailed_timing_descriptor_t dtd_cea_861; + struct MHL3_video_descriptor_t _3d_info; +}; + +struct SI_PACK_THIS_STRUCT MHL3_speaker_allocation_data_block_t { + uint8_t cea861f_spkr_alloc[3]; +}; + +struct SI_PACK_THIS_STRUCT MHL3_adt_payload_t { + uint8_t format_flags; + union { + uint8_t short_descs[9]; + struct MHL3_speaker_allocation_data_block_t spkr_alloc_db[3]; + } descriptors; + uint8_t reserved; +}; + +struct SI_PACK_THIS_STRUCT MHL3_audio_delay_burst_t { + struct MHL_burst_id_t burst_id; + uint8_t checksum; + uint8_t delay_h; + uint8_t delay_m; + uint8_t delay_l; + uint8_t reserved[10]; +}; + +struct SI_PACK_THIS_STRUCT MHL3_adt_data_t { + struct MHL3_burst_header_t header; + struct MHL3_adt_payload_t payload; +}; + +struct SI_PACK_THIS_STRUCT MHL3_emsc_support_payload_t { + struct MHL_burst_id_t burst_ids[5]; +}; + +struct SI_PACK_THIS_STRUCT MHL3_emsc_support_data_t { + struct MHL3_burst_header_t header; + uint8_t num_entries_this_burst; + struct MHL3_emsc_support_payload_t payload; +}; + +enum view_pixel_fmt_e { + VIEW_PIX_FMT_24BPP, + VIEW_PIX_FMT_16BPP +}; + +struct SI_PACK_THIS_STRUCT MHL3_bits_per_pixel_fmt_descriptor_t { + uint8_t stream_id; + uint8_t stream_pixel_format; +}; + +struct SI_PACK_THIS_STRUCT MHL_bits_per_pixel_fmt_data_t { + struct MHL3_burst_header_t header; + uint8_t num_entries_this_burst; + + /* reserve 5 for use with WRITE_BURST + actual length is variable, indicated by + num_entries_this_burst + */ + /* todo change this to 1 when WRITE_BURST OPTION is removed */ + struct MHL3_bits_per_pixel_fmt_descriptor_t descriptors[5]; +}; + +union SI_PACK_THIS_STRUCT video_burst_descriptor_u { + struct MHL2_video_descriptor_t mhl2_3d_descriptor; + struct MHL3_video_descriptor_t mhl3_3d_descriptor; + struct MHL3_hev_vic_descriptor_t mhl3_hev_vic_descriptor; + struct MHL3_hev_dtd_item_t mhl3_hev_dtd; +}; + +struct SI_PACK_THIS_STRUCT mhl3_vsif_t { + VSIF_COMMON_FIELDS + uint8_t pb4; + uint8_t pb5_reserved; + uint8_t pb6; + struct MHL_high_low_t mhl_hev_fmt_type; + uint8_t pb9; + + struct MHL_high_low_t av_delay_sync; +}; + +/* the enum's in the following section are + defined "in position" to avoid + shifting on the fly +*/ +#define MHL3_VSIF_TYPE 0x81 +#define MHL3_VSIF_VERSION 0x03 +#define IEEE_OUI_MHL 0x7CA61D + +#define PB4_MASK_MHL_VID_FMT 0x03 +enum mhl_vid_fmt_e { + mhl_vid_fmt_no_additional, + mhl_vid_fmt_3d_fmt_present, + mhl_vid_fmt_multi_view, + mhl_vid_fmt_dual_3d +}; + +#define PB4_MASK_MHL_3D_FMT_TYPE 0x1C +enum mhl_3d_fmt_type_e { + MHL_3D_FMT_TYPE_FS, /* Frame Sequential */ + MHL_3D_FMT_TYPE_TB = 0x04, /* Top-Bottom */ + MHL_3D_FMT_TYPE_LR = 0x08, /* Left-Right */ + MHL_3D_FMT_TYPE_FS_TB = 0x0C, /* Frame Sequential Top-Bottom */ + MHL_3D_FMT_TYPE_FS_LR = 0x10, /* Frame Sequential Left-Right */ + MHL_3D_FMT_TYPE_TBLR = 0x14 /* Top-Bottom-Left-Right */ +}; + +#define PB4_MASK_SEP_AUD 0x20 +enum mhl_sep_audio_e { + mhl_sep_audio_not_available, + mhl_sep_audio_available = 0x20 +}; + +#define PB4_MASK_RESERVED 0xC0 + +#define MHL3_VSIF_PB4(vid_fmt, _3d_fmt_type, sep_aud) \ + (uint8_t)(((vid_fmt)&PB4_MASK_MHL_VID_FMT) | \ + ((_3d_fmt_type)&PB4_MASK_MHL_3D_FMT_TYPE) | \ + ((sep_aud)&PB4_MASK_SEP_AUD)) + +#define PB6_MASK_MHL_HEV_FMT 0x03 +enum mhl_hev_fmt_e { + mhl_hev_fmt_no_additional, + mhl_hev_fmt_hev_present, + mhl_hev_fmt_reserved_2, + mhl_hev_fmt_reserved_3 +}; + +#define PB6_MASK_RESERVED 0xFC +#define MHL3_VSIF_PB6(hev_fmt) (uint8_t)((hev_fmt) & PB6_MASK_MHL_HEV_FMT) + +#define PB9_MASK_AV_DELAY_SYNC_19_16 0x0F + +#define PB9_MASK_AV_DELAY_DIR 0x10 +enum mhl_av_delay_dir_e { + mhl_av_delay_dir_audio_earlier, + mhl_av_delay_dir_video_earlier = 0x10 +}; + +#define PB9_MASK_RESERVED 0xE0 + +#define MHL3_VSIF_PB9(delay_sync, delay_dir) \ + (uint8_t)((((delay_sync) >> 16) & PB9_MASK_AV_DELAY_SYNC_19_16) | \ + ((delay_dir) & PB9_MASK_AV_DELAY_DIR)) + +struct SI_PACK_THIS_STRUCT info_frame_t { + union { + struct info_frame_header_t header; + struct avi_info_frame_t avi; + struct hdmi_vsif_t vendorSpecific; + struct mhl3_vsif_t mhl3_vsif; + struct unr_info_frame_t unr; + } body; +}; + +union SI_PACK_THIS_STRUCT vsif_mhl3_or_hdmi_u { + struct SI_PACK_THIS_STRUCT vsif_common_header_t common; + struct SI_PACK_THIS_STRUCT hdmi_vsif_t hdmi; + struct SI_PACK_THIS_STRUCT mhl3_vsif_t mhl3; +}; +enum InfoFrameType_e { + InfoFrameType_AVI, + InfoFrameType_VendorSpecific, + InfoFrameType_VendorSpecific_MHL3, + InfoFrameType_Audio +}; + +SI_POP_STRUCT_PACKING +enum { + MHL_MSC_MSG_RCP = 0x10, /* RCP sub-command */ + MHL_MSC_MSG_RCPK = 0x11, /* RCP Acknowledge sub-command */ + MHL_MSC_MSG_RCPE = 0x12, /* RCP Error sub-command */ + MHL_MSC_MSG_RAP = 0x20, /* Mode Change Warning sub-command */ + MHL_MSC_MSG_RAPK = 0x21, /* MCW Acknowledge sub-command */ + MHL_MSC_MSG_RBP = 0x22, /* Remote Button Protocol sub-command */ + MHL_MSC_MSG_RBPK = 0x23, /* RBP Acknowledge sub-command */ + MHL_MSC_MSG_RBPE = 0x24, /* RBP Error sub-command */ + MHL_MSC_MSG_UCP = 0x30, /* UCP sub-command */ + MHL_MSC_MSG_UCPK = 0x31, /* UCP Acknowledge sub-command */ + MHL_MSC_MSG_UCPE = 0x32, /* UCP Error sub-command */ + MHL_MSC_MSG_RUSB = 0x40, /* Request USB host role */ + MHL_MSC_MSG_RUSBK = 0x41, /* Acknowledge request for USB host role */ + MHL_MSC_MSG_RHID = 0x42, /* Request HID host role */ + MHL_MSC_MSG_RHIDK = 0x43, /* Acknowledge request for HID host role */ + MHL_MSC_MSG_ATT = 0x50, /* Request attention sub-command */ + MHL_MSC_MSG_ATTK = 0x51, /* ATT Acknowledge sub-command */ + MHL_MSC_MSG_BIST_TRIGGER = 0x60, + MHL_MSC_MSG_BIST_REQUEST_STAT = 0x61, + MHL_MSC_MSG_BIST_READY = 0x62, + MHL_MSC_MSG_BIST_STOP = 0x63, +}; + +#define BIST_TRIGGER_E_CBUS_TX 0x01 +#define BIST_TRIGGER_E_CBUS_RX 0x02 +#define BIST_TRIGGER_E_CBUS_TYPE_MASK 0x08 +#define BIST_TRIGGER_TEST_E_CBUS_S 0x00 +#define BIST_TRIGGER_TEST_E_CBUS_D 0x08 +#define BIST_TRIGGER_AVLINK_TX 0x10 +#define BIST_TRIGGER_AVLINK_RX 0x20 +#define BIST_TRIGGER_IMPEDANCE_TEST 0x40 + +#define BIST_TRIGGER_ECBUS_AV_LINK_MASK (BIST_TRIGGER_AVLINK_TX | \ + BIST_TRIGGER_AVLINK_RX) +#define BIST_TRIGGER_ECBUS_TX_RX_MASK (BIST_TRIGGER_E_CBUS_TX | \ + BIST_TRIGGER_E_CBUS_RX) + +#define BIST_TRIGGER_OPERAND_VALID_MASK 0x7B + +#define BIST_READY_E_CBUS_READY 0x01 +#define BIST_READY_AVLINK_READY 0x02 +#define BIST_READY_TERM_READY 0x04 +#define BIST_READY_E_CBUS_ERROR 0x10 +#define BIST_READY_AVLINK_ERROR 0x20 +#define BIST_READY_TERM_ERROR 0x40 + +#define RCPE_NO_ERROR 0x00 +#define RCPE_INEFFECTIVE_KEY_CODE 0x01 +#define RCPE_BUSY 0x02 + +#define MHL_RCP_KEY_RELEASED_MASK 0x80 +#define MHL_RCP_KEY_ID_MASK 0x7F + +#define RBPE_NO_ERROR 0x00 +#define RBPE_INEFFECTIVE_BUTTON_CODE 0x01 +#define RBPE_BUSY 0x02 + +#define MHL_RBP_BUTTON_RELEASED_MASK 0x80 +#define MHL_RBP_BUTTON_ID_MASK 0x7F + +#define T_PRESS_MODE 300 + +#define T_HOLD_MAINTAIN 2000 +#define T_RAP_WAIT_MIN 100 +#define T_RAP_WAIT_MAX 1000 + +enum { + /* Command or Data byte acknowledge */ + MHL_ACK = 0x33, + /* Command or Data byte not acknowledge */ + MHL_NACK = 0x34, + /* Transaction abort */ + MHL_ABORT = 0x35, + /* Write one status register strip top bit */ + MHL_WRITE_STAT = 0x60 | 0x80, + /* Write one interrupt register */ + MHL_SET_INT = 0x60, + /* Read one register */ + MHL_READ_DEVCAP_REG = 0x61, + /* Read CBUS revision level from follower */ + MHL_GET_STATE = 0x62, + /* Read vendor ID value from follower */ + MHL_GET_VENDOR_ID = 0x63, + /* Set Hot Plug Detect in follower */ + MHL_SET_HPD = 0x64, + /* Clear Hot Plug Detect in follower */ + MHL_CLR_HPD = 0x65, + /* Set Capture ID for downstream device */ + MHL_SET_CAP_ID = 0x66, + /* Get Capture ID from downstream device */ + MHL_GET_CAP_ID = 0x67, + /* VS command to send RCP sub-commands */ + MHL_MSC_MSG = 0x68, + /* Get Vendor-Specific command error code */ + MHL_GET_SC1_ERRORCODE = 0x69, + /* Get DDC channel command error code */ + MHL_GET_DDC_ERRORCODE = 0x6A, + /* Get MSC command error code */ + MHL_GET_MSC_ERRORCODE = 0x6B, + /* Write 1-16 bytes to responder's scratchpad */ + MHL_WRITE_BURST = 0x6C, + /* Get channel 3 command error code */ + MHL_GET_SC3_ERRORCODE = 0x6D, + /* Write one extended status register */ + MHL_WRITE_XSTAT = 0x70, + /* Read one extended devcap register */ + MHL_READ_XDEVCAP_REG = 0x71, + /* let the rest of these float, they are software specific */ + MHL_READ_EDID_BLOCK, + MHL_SEND_3D_REQ_OR_FEAT_REQ, + MHL_READ_DEVCAP, + MHL_READ_XDEVCAP +}; + +/* RAP action codes */ +#define MHL_RAP_POLL 0x00 /* Just do an ack */ +#define MHL_RAP_CONTENT_ON 0x10 /* Turn content stream ON */ +#define MHL_RAP_CONTENT_OFF 0x11 /* Turn content stream OFF */ +#define MHL_RAP_CBUS_MODE_DOWN 0x20 +#define MHL_RAP_CBUS_MODE_UP 0x21 + +/* RAPK status codes */ +#define MHL_RAPK_NO_ERR 0x00 /* RAP action recognized & supported */ +#define MHL_RAPK_UNRECOGNIZED 0x01 /* Unknown RAP action code received */ +#define MHL_RAPK_UNSUPPORTED 0x02 /* Rcvd RAP action code not supported */ +#define MHL_RAPK_BUSY 0x03 /* Responder too busy to respond */ + +/* + * Error status codes for RCPE messages + */ +/* No error. (Not allowed in RCPE messages) */ +#define MHL_RCPE_STATUS_NO_ERROR 0x00 +/* Unsupported/unrecognized key code */ +#define MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE 0x01 +/* Responder busy. Initiator may retry message */ +#define MHL_RCPE_STATUS_BUSY 0x02 + +/* + * Error status codes for RBPE messages + */ +/* No error. (Not allowed in RBPE messages) */ +#define MHL_RBPE_STATUS_NO_ERROR 0x00 +/* Unsupported/unrecognized button code */ +#define MHL_RBPE_STATUS_INEFFECTIVE_BUTTON_CODE 0x01 +/* Responder busy. Initiator may retry message */ +#define MHL_RBPE_STATUS_BUSY 0x02 + +/* + * Error status codes for UCPE messages + */ +/* No error. (Not allowed in UCPE messages) */ +#define MHL_UCPE_STATUS_NO_ERROR 0x00 +/* Unsupported/unrecognized key code */ +#define MHL_UCPE_STATUS_INEFFECTIVE_KEY_CODE 0x01 + +/* Extended Device Capability Registers 7.12.1 */ +enum { + XDEVCAP_START = 0x80, + XDEVCAP_ADDR_ECBUS_SPEEDS = XDEVCAP_START, + XDEVCAP_ADDR_TMDS_SPEEDS = 0x81, + XDEVCAP_ADDR_ECBUS_DEV_ROLES = 0x82, + XDEVCAP_ADDR_LOG_DEV_MAPX = 0x83, + XDEVCAP_LIMIT, /* don't hard-code this one */ + XDEVCAP_ADDR_RESERVED_4 = 0x84, + XDEVCAP_ADDR_RESERVED_5 = 0x85, + XDEVCAP_ADDR_RESERVED_6 = 0x86, + XDEVCAP_ADDR_RESERVED_7 = 0x87, + XDEVCAP_ADDR_RESERVED_8 = 0x88, + XDEVCAP_ADDR_RESERVED_9 = 0x89, + XDEVCAP_ADDR_RESERVED_A = 0x8A, + XDEVCAP_ADDR_RESERVED_B = 0x8B, + XDEVCAP_ADDR_RESERVED_C = 0x8C, + XDEVCAP_ADDR_RESERVED_D = 0x8D, + XDEVCAP_ADDR_RESERVED_E = 0x8E, + XDEVCAP_ADDR_RESERVED_F = 0x8F, + XDEVCAP_ADDR_LAST, /* this one must be last */ + XDEVCAP_SIZE = XDEVCAP_ADDR_LAST - XDEVCAP_START +}; + +#define XDEVCAP_OFFSET(reg) (reg - XDEVCAP_ADDR_ECBUS_SPEEDS) +SI_PUSH_STRUCT_PACKING +struct SI_PACK_THIS_STRUCT MHLXDevCap_t { + uint8_t ecbus_speeds; + uint8_t tmds_speeds; + uint8_t ecbus_dev_roles; + uint8_t log_dev_mapx; + uint8_t reserved_4; + uint8_t reserved_5; + uint8_t reserved_6; + uint8_t reserved_7; + uint8_t reserved_8; + uint8_t reserved_9; + uint8_t reserved_a; + uint8_t reserved_b; + uint8_t reserved_c; + uint8_t reserved_d; + uint8_t reserved_e; + uint8_t reserved_f; +}; + +union MHLXDevCap_u { + struct MHLXDevCap_t mxdc; + uint8_t xdevcap_cache[XDEVCAP_SIZE]; +}; +SI_POP_STRUCT_PACKING + +/* XDEVCAP - eCBUS Speeds 7.12.1.1 */ +#define MHL_XDC_ECBUS_S_075 0x01 +#define MHL_XDC_ECBUS_S_8BIT 0x02 +#define MHL_XDC_ECBUS_S_12BIT 0x04 +#define MHL_XDC_ECBUS_D_150 0x10 +#define MHL_XDC_ECBUS_D_8BIT 0x20 + +/* XDEVCAP - TMDS Speeds 7.12.1.2 */ +#define MHL_XDC_TMDS_000 0x00 +#define MHL_XDC_TMDS_150 0x01 +#define MHL_XDC_TMDS_300 0x02 +#define MHL_XDC_TMDS_600 0x04 + +/* XDEVCAP - Device Roles 7.12.1.3 */ +#define MHL_XDC_DEV_HOST 0x01 +#define MHL_XDC_DEV_DEVICE 0x02 +#define MHL_XDC_DEV_CHARGER 0x04 +#define MHL_XDC_HID_HOST 0x08 +#define MHL_XDC_HID_DEVICE 0x10 + +/* XDEVCAP - Extended Logical Device Map 7.12.1.4 */ +#define MHL_XDC_LD_PHONE 0x01 + +/* Extended Device Status Registers 7.12.2 */ +enum { + XDEVSTAT_OFFSET_CURR_ECBUS_MODE, + XDEVSTAT_OFFSET_AVLINK_MODE_STATUS, + XDEVSTAT_OFFSET_AVLINK_MODE_CONTROL, + XDEVSTAT_OFFSET_MULTI_SINK_STATUS, + XDEVSTAT_OFFSET_RESERVED_04, + XDEVSTAT_OFFSET_RESERVED_05, + XDEVSTAT_OFFSET_RESERVED_06, + XDEVSTAT_OFFSET_RESERVED_07, + XDEVSTAT_OFFSET_RESERVED_08, + XDEVSTAT_OFFSET_RESERVED_09, + XDEVSTAT_OFFSET_RESERVED_0A, + XDEVSTAT_OFFSET_RESERVED_0B, + XDEVSTAT_OFFSET_RESERVED_0C, + XDEVSTAT_OFFSET_RESERVED_0D, + XDEVSTAT_OFFSET_RESERVED_0E, + XDEVSTAT_OFFSET_RESERVED_0F, + XDEVSTAT_OFFSET_RESERVED_10, + XDEVSTAT_OFFSET_RESERVED_11, + XDEVSTAT_OFFSET_RESERVED_12, + XDEVSTAT_OFFSET_RESERVED_13, + XDEVSTAT_OFFSET_RESERVED_14, + XDEVSTAT_OFFSET_RESERVED_15, + XDEVSTAT_OFFSET_RESERVED_16, + XDEVSTAT_OFFSET_RESERVED_17, + XDEVSTAT_OFFSET_RESERVED_18, + XDEVSTAT_OFFSET_RESERVED_19, + XDEVSTAT_OFFSET_RESERVED_1A, + XDEVSTAT_OFFSET_RESERVED_1B, + XDEVSTAT_OFFSET_RESERVED_1C, + XDEVSTAT_OFFSET_RESERVED_1D, + XDEVSTAT_OFFSET_RESERVED_1E, + XDEVSTAT_OFFSET_RESERVED_1F, + /* this one must be last */ + XDEVSTAT_SIZE +}; + +/* XDEVSTAT - Current eCBUS Mode 7.12.2.1 */ +#define MHL_XSTATUS_REG_CBUS_MODE 0x90 +#define MHL_XDS_SLOT_MODE_8BIT 0x00 +#define MHL_XDS_SLOT_MODE_6BIT 0x01 +#define MHL_XDS_ECBUS_S 0x04 +#define MHL_XDS_ECBUS_D 0x08 + +#define MHL_XDS_LINK_CLOCK_75MHZ 0x00 +#define MHL_XDS_LINK_CLOCK_150MHZ 0x10 +#define MHL_XDS_LINK_CLOCK_300MHZ 0x20 +#define MHL_XDS_LINK_CLOCK_600MHZ 0x30 + +/* XDEVSTAT - AV Link Mode Status 7.12.2.2 */ +#define MHL_XDS_LINK_STATUS_NO_SIGNAL 0x00 +#define MHL_XDS_LINK_STATUS_CRU_LOCKED 0x01 +#define MHL_XDS_LINK_STATUS_TMDS_NORMAL 0x02 +#define MHL_XDS_LINK_STATUS_TMDS_RESERVED 0x03 + +/* XDEVSTAT - AV Link Mode Control 7.12.2.3 */ +#define MHL_STATUS_REG_AV_LINK_MODE_CONTROL 0x92 +#define MHL_XDS_LINK_RATE_1_5_GBPS 0x00 +#define MHL_XDS_LINK_RATE_3_0_GBPS 0x01 +#define MHL_XDS_LINK_RATE_6_0_GBPS 0x02 +#define MHL_XDS_ATT_CAPABLE 0x08 + +/* XDEVSTAT - Multi-Sink Status 7.12.2.4 */ +#define MHL_XDS_SINK_STATUS_1_HPD_LOW 0x00 +#define MHL_XDS_SINK_STATUS_1_HPD_HIGH 0x01 +#define MHL_XDS_SINK_STATUS_2_HPD_LOW 0x00 +#define MHL_XDS_SINK_STATUS_2_HPD_HIGH 0x04 +#define MHL_XDS_SINK_STATUS_3_HPD_LOW 0x00 +#define MHL_XDS_SINK_STATUS_3_HPD_HIGH 0x10 +#define MHL_XDS_SINK_STATUS_4_HPD_LOW 0x00 +#define MHL_XDS_SINK_STATUS_4_HPD_HIGH 0x40 + +/* + * Define format of Write Burst used in MHL 3 + * to assign TDM slots to virtual channels. + */ +struct SI_PACK_THIS_STRUCT virt_chan_info { + uint8_t vc_num; + uint8_t feature_id; +#define FEATURE_ID_E_MSC 0x00 +#define FEATURE_ID_USB 0x01 +#define FEATURE_ID_AUDIO 0x02 +#define FEATURE_ID_IP 0x03 +#define FEATURE_ID_COMP_VIDEO 0x04 +#define FEATURE_ID_HID 0x05 +#define FEATURE_ID_LAST FEATURE_ID_HID + union { + uint8_t channel_size; + uint8_t response; +#define VC_RESPONSE_ACCEPT 0x00 +#define VC_RESPONSE_BAD_VC_NUM 0x01 +#define VC_RESPONSE_BAD_FEATURE_ID 0x02 +#define VC_RESPONSE_BAD_CHANNEL_SIZE 0x03 + } req_resp; +}; + +#define MAX_VC_ENTRIES 3 +struct SI_PACK_THIS_STRUCT tdm_alloc_burst { + struct MHL3_burst_header_t header; + uint8_t num_entries_this_burst; + struct virt_chan_info vc_info[MAX_VC_ENTRIES]; + uint8_t reserved; +}; + +/* BIST_SETUP WRITE_BURST 15.1.1 */ +#define BIST_ECBUS_PATTERN_UNSPECIFIED 0x00 +#define BIST_ECBUS_PATTERN_PRBS 0x01 +#define BIST_ECBUS_PATTERN_FIXED_8 0x02 +#define BIST_ECBUS_PATTERN_FIXED_10 0x03 +#define BIST_ECBUS_PATTERN_MAX BIST_ECBUS_PATTERN_FIXED_10 + +#define BIST_AVLINK_DATA_RATE_UNSPECIFIED 0x00 +#define BIST_AVLINK_DATA_RATE_1500MBPS 0x01 +#define BIST_AVLINK_DATA_RATE_3000MBPS 0x02 +#define BIST_AVLINK_DATA_RATE_6000MBPS 0x03 +#define BIST_AVLINK_DATA_RATE_MAX BIST_AVLINK_DATA_RATE_6000MBPS + +#define BIST_AVLINK_PATTERN_UNSPECIFIED 0x00 +#define BIST_AVLINK_PATTERN_PRBS 0x01 +#define BIST_AVLINK_PATTERN_FIXED_8 0x02 +#define BIST_AVLINK_PATTERN_FIXED_10 0x03 +#define BIST_AVLINK_PATTERN_MAX BIST_AVLINK_PATTERN_FIXED_10 + +#define BIST_IMPEDANCE_MODE_AVLINK_TX_LOW 0x00 +#define BIST_IMPEDANCE_MODE_AVLINK_TX_HIGH 0x01 +#define BIST_IMPEDANCE_MODE_AVLINK_RX 0x02 +#define BIST_IMPEDANCE_MODE_RESERVED_1 0x03 +#define BIST_IMPEDANCE_MODE_ECBUS_D_TX_LOW 0x04 +#define BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH 0x05 +#define BIST_IMPEDANCE_MODE_ECBUS_D_RX 0x06 +#define BIST_IMPEDANCE_MODE_RESERVED_2 0x07 +#define BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW 0x08 +#define BIST_IMPEDANCE_MODE_ECBUS_S_TX_HIGH 0x09 +#define BIST_IMPEDANCE_MODE_ECBUS_S_RX 0x0A +#define BIST_IMPEDANCE_MODE_MAX BIST_IMPEDANCE_MODE_ECBUS_S_RX + +struct SI_PACK_THIS_STRUCT bist_setup_burst { + uint8_t burst_id_h; + uint8_t burst_id_l; + uint8_t checksum; + uint8_t e_cbus_duration; + uint8_t e_cbus_pattern; + uint8_t e_cbus_fixed_h; + uint8_t e_cbus_fixed_l; + uint8_t reserved; + uint8_t avlink_data_rate; + uint8_t avlink_pattern; + uint8_t avlink_video_mode; + uint8_t avlink_duration; + uint8_t avlink_fixed_h; + uint8_t avlink_fixed_l; + uint8_t avlink_randomizer; + uint8_t impedance_mode; +}; + +/* BIST_RETURN_STAT WRITE_BURST 15.1.2 */ +struct SI_PACK_THIS_STRUCT bist_return_stat_burst { + uint8_t burst_id_h; + uint8_t burst_id_l; + uint8_t checksum; + uint8_t reserved[9]; + uint8_t e_cbus_stat_h; + uint8_t e_cbus_stat_l; + uint8_t avlink_stat_h; + uint8_t avlink_stat_l; +}; + +struct SI_PACK_THIS_STRUCT bist_discard_burst_hdr { + struct MHL_burst_id_t burst_id; + uint8_t remaining_length; +}; + +struct SI_PACK_THIS_STRUCT bist_discard_burst { + struct bist_discard_burst_hdr hdr; + uint8_t payload[13]; +}; + +struct SI_PACK_THIS_STRUCT bist_echo_request_burst_hdr { + struct MHL_burst_id_t burst_id; + uint8_t remaining_length; +}; + +struct SI_PACK_THIS_STRUCT bist_echo_request_burst { + struct bist_echo_request_burst_hdr hdr; + uint8_t payload[13]; +}; + +struct SI_PACK_THIS_STRUCT bist_echo_response_burst_hdr { + struct MHL_burst_id_t burst_id; + uint8_t remaining_length; +}; + +struct SI_PACK_THIS_STRUCT bist_echo_response_burst { + struct bist_echo_response_burst_hdr hdr; + uint8_t payload[13]; +}; + +struct SI_PACK_THIS_STRUCT si_adopter_id_sub_payload_hdr { + struct MHL_burst_id_t burst_id; + uint8_t remaining_length; + uint8_t checksum; + uint8_t op_code; +}; +enum si_adopter_id_opcode { + EDID_BLOCK = 0, + EDID_STOP = 1, +}; +struct SI_PACK_THIS_STRUCT si_opcode_data_edid_block { + uint8_t block_num; + uint8_t data[128]; +}; + +struct SI_PACK_THIS_STRUCT si_adopter_id_data { + struct SI_PACK_THIS_STRUCT si_adopter_id_sub_payload_hdr hdr; + union { + struct SI_PACK_THIS_STRUCT si_opcode_data_edid_block edid_blk; + /* more members to come later */ + } opcode_data; +}; + +#define MHL_T_src_vbus_cbus_stable_min 100 +#define T_BIST_MODE_DOWN_MAX 5000 +#define T_BIST_MODE_DOWN_MIN 2000 + +#endif diff --git a/drivers/video/fbdev/msm/mhl3/si_mhl_tx_hw_drv_api.h b/drivers/video/fbdev/msm/mhl3/si_mhl_tx_hw_drv_api.h new file mode 100644 index 000000000000..9bd5327f7143 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/si_mhl_tx_hw_drv_api.h @@ -0,0 +1,222 @@ +/* + * 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. + */ + +#if !defined(SI_MHL_TX_DRV_API_H) +#define SI_MHL_TX_DRV_API_H + +/* + * Structure to hold command details from upper layer to CBUS module + */ +struct cbus_req { + struct list_head link; + union { + struct { + uint8_t cancel:1; /* this command has been canceled */ + uint8_t resvd:7; + } flags; + uint8_t as_uint8; + } status; + uint8_t retry_count; + uint8_t command; /* VS_CMD or RCP opcode */ + uint8_t reg; + uint8_t reg_data; + uint8_t burst_offset; /* register offset */ + uint8_t length; /* Only applicable to write burst */ + uint8_t msg_data[16]; /* scratch pad data area. */ + const char *function; + int line; + int sequence; + struct cbus_req *(*completion)(struct mhl_dev_context *dev_context, + struct cbus_req *req, + uint8_t data1); +}; +struct SI_PACK_THIS_STRUCT tport_hdr_and_burst_id_t { + /* waste two bytes to save on memory copying when submitting BLOCK + * transactions + */ + struct SI_PACK_THIS_STRUCT standard_transport_header_t tport_hdr; + + /* sub-payloads start here */ + struct MHL_burst_id_t burst_id; +}; +union SI_PACK_THIS_STRUCT emsc_payload_t { + struct SI_PACK_THIS_STRUCT tport_hdr_and_burst_id_t hdr_and_burst_id; + uint8_t as_bytes[256]; +}; + +struct SI_PACK_THIS_STRUCT block_req { + struct list_head link; + const char *function; + int line; + int sequence; + uint16_t count; /* (size - 1) see MHL spec section 13.5.7.2 */ + uint8_t space_remaining; + uint8_t sub_payload_size; + uint8_t *platform_header; + union SI_PACK_THIS_STRUCT emsc_payload_t *payload; +}; + +enum quantization_settings_e { + qs_auto_select_by_color_space, + qs_full_range, + qs_limited_range, + qs_reserved +}; + +struct bist_setup_info { + uint8_t e_cbus_duration; + uint8_t e_cbus_pattern; + uint16_t e_cbus_fixed_pat; + uint8_t avlink_data_rate; + uint8_t avlink_pattern; + uint8_t avlink_video_mode; + uint8_t avlink_duration; + uint16_t avlink_fixed_pat; + uint8_t avlink_randomizer; + uint8_t impedance_mode; + uint8_t bist_trigger_parm; + uint8_t bist_stat_parm; + uint32_t t_bist_mode_down; +}; + +struct bist_stat_info { + uint16_t e_cbus_remote_stat; + int32_t e_cbus_next_local_stat; + int32_t e_cbus_local_stat; + int32_t e_cbus_prev_local_stat; + uint16_t avlink_stat; +}; + +/* + * The APIs listed below must be implemented by the MHL transmitter + * hardware support module. + */ + +struct drv_hw_context; +struct interrupt_info; + +int si_mhl_tx_chip_initialize(struct drv_hw_context *hw_context); +void si_mhl_tx_drv_device_isr(struct drv_hw_context *hw_context, + struct interrupt_info *intr_info); +void si_mhl_tx_drv_disable_video_path(struct drv_hw_context *hw_context); +void si_mhl_tx_drv_enable_video_path(struct drv_hw_context *hw_context); + +void si_mhl_tx_drv_content_on(struct drv_hw_context *hw_context); +void si_mhl_tx_drv_content_off(struct drv_hw_context *hw_context); +uint8_t si_mhl_tx_drv_send_cbus_command(struct drv_hw_context *hw_context, + struct cbus_req *req); + +void mhl_tx_drv_send_block(struct drv_hw_context *hw_context, + struct block_req *req); +int si_mhl_tx_drv_get_scratch_pad(struct drv_hw_context *hw_context, + uint8_t start_reg, uint8_t *data, uint8_t length); +void si_mhl_tx_read_devcap_fifo(struct drv_hw_context *hw_context, + union MHLDevCap_u *dev_cap_buf); +void si_mhl_tx_read_xdevcap_fifo(struct drv_hw_context *hw_context, + union MHLXDevCap_u *xdev_cap_buf); +int si_mhl_tx_drv_get_aksv(struct drv_hw_context *hw_context, uint8_t *buffer); + +void si_mhl_tx_drv_start_avlink_bist(struct mhl_dev_context *dev_context, + struct bist_setup_info *test_info); +void si_mhl_tx_drv_stop_avlink_bist(struct drv_hw_context *hw_context); +void si_mhl_tx_drv_start_ecbus_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info); + +uint8_t si_mhl_tx_drv_ecbus_connected(struct mhl_dev_context *dev_context); +void si_mhl_tx_drv_continue_ecbus_bist( + struct mhl_dev_context *dev_context); +int32_t si_mhl_tx_drv_get_ecbus_bist_status( + struct mhl_dev_context *dev_context, + uint8_t *rx_run_done, + uint8_t *tx_run_done); +#define BIST_LOCAL_COUNT_INVALID -2 +void si_mhl_tx_drv_stop_ecbus_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info); +int si_mhl_tx_drv_start_impedance_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info); +void si_mhl_tx_drv_stop_impedance_bist(struct drv_hw_context *hw_context, + struct bist_setup_info *test_info); + +void si_mhl_tx_drv_shutdown(struct drv_hw_context *hw_context); + +int si_mhl_tx_drv_connection_is_mhl3(struct mhl_dev_context *dev_context); + +int si_mhl_tx_drv_get_highest_tmds_link_speed( + struct mhl_dev_context *dev_context); + +uint8_t si_mhl_tx_drv_hawb_xfifo_avail(struct mhl_dev_context *dev_context); + +uint8_t si_mhl_tx_drv_get_pending_hawb_write_status( + struct mhl_dev_context *dev_context); + +enum cbus_mode_e { + CM_NO_CONNECTION, + CM_NO_CONNECTION_BIST_SETUP, + CM_NO_CONNECTION_BIST_STAT, + CM_oCBUS_PEER_VERSION_PENDING, + CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP, + CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT, + CM_oCBUS_PEER_IS_MHL1_2, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT, + CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY, + CM_oCBUS_PEER_IS_MHL3_BIST_STAT, + CM_oCBUS_PEER_IS_MHL3, + CM_TRANSITIONAL_TO_eCBUS_S_BIST, + CM_TRANSITIONAL_TO_eCBUS_D_BIST, + CM_TRANSITIONAL_TO_eCBUS_S, + CM_TRANSITIONAL_TO_eCBUS_D, + CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST, + CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST, + CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED, + CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED, + CM_eCBUS_S_BIST, + CM_eCBUS_D_BIST, + CM_BIST_DONE_PENDING_DISCONNECT, + CM_eCBUS_S, + CM_eCBUS_D, + CM_eCBUS_S_AV_BIST, + CM_eCBUS_D_AV_BIST, + + NUM_CM_MODES +}; + +enum cbus_mode_e si_mhl_tx_drv_get_cbus_mode( + struct mhl_dev_context *dev_context); +char *si_mhl_tx_drv_get_cbus_mode_str(enum cbus_mode_e cbus_mode); + +uint16_t si_mhl_tx_drv_get_blk_rcv_buf_size(void); + +void si_mhl_tx_drv_start_cp(struct mhl_dev_context *dev_context); +void si_mhl_tx_drv_shut_down_HDCP2(struct drv_hw_context *hw_context); + +bool si_mhl_tx_drv_support_e_cbus_d(struct drv_hw_context *hw_context); + +int si_mhl_tx_drv_switch_cbus_mode(struct drv_hw_context *hw_context, + enum cbus_mode_e mode_sel); +void si_mhl_tx_drv_free_block_input_buffer(struct mhl_dev_context *dev_context); +int si_mhl_tx_drv_peek_block_input_buffer(struct mhl_dev_context *dev_context, + uint8_t **buffer, int *length); + +enum tdm_vc_num { + VC_CBUS1, + VC_E_MSC, + VC_T_CBUS, + VC_MAX +}; +int si_mhl_tx_drv_set_tdm_slot_allocation(struct drv_hw_context *hw_context, + uint8_t *vc_slot_counts, bool program); + +#endif /* if !defined(SI_MHL_TX_DRV_API_H) */ diff --git a/drivers/video/fbdev/msm/mhl3/sii6031/Kconfig b/drivers/video/fbdev/msm/mhl3/sii6031/Kconfig new file mode 100644 index 000000000000..36e166dc3c47 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/sii6031/Kconfig @@ -0,0 +1,13 @@ +comment "Driver for Silicon Image SiI6031 MHL/USB switch (Stark)" + +config SII6031_MHL_SWITCH + bool "SIMG GPIO support" + depends on GPIOLIB + default y + help + yes links this driver. + This driver is required by Qualcomm APQ8074 platform + with modified usb otg driver (msm_otg.c) for SiI6031 switch. + + If unsure, say N. + diff --git a/drivers/video/fbdev/msm/mhl3/sii6031/Makefile b/drivers/video/fbdev/msm/mhl3/sii6031/Makefile new file mode 100644 index 000000000000..471ae5c840a4 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/sii6031/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SII6031_MHL_SWITCH) +=si_6031_switch.o +#si_6031_switch-objs := si_6031_switch.o diff --git a/drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.c b/drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.c new file mode 100644 index 000000000000..d84cc69786ef --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.c @@ -0,0 +1,151 @@ +/* + * SIMG SiI6031 MHL-USB Switch driver + * + * Copyright 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 version 2 + * as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio stark_gpio_ctrl[3] = { + {0, GPIOF_OUT_INIT_HIGH, "MHL_USB_0"}, + {0, GPIOF_OUT_INIT_HIGH, "MHL_USB_1"}, + {0, GPIOF_OUT_INIT_HIGH, "MHL_VBUS"} +}; + +void sii_switch_to_mhl(bool switch_to_mhl) +{ + if(switch_to_mhl) + { + printk("%s(): SIMG: switch to MHL gpio [%d, %d] \n", __func__, + stark_gpio_ctrl[MHL_USB_0].gpio, + stark_gpio_ctrl[MHL_USB_1].gpio); + gpio_set_value(stark_gpio_ctrl[MHL_USB_0].gpio, 1); + gpio_set_value(stark_gpio_ctrl[MHL_USB_1].gpio, 1); + } + else { + printk("%s(): SIMG: switch to USB gpio [%d, %d] \n", __func__, + stark_gpio_ctrl[MHL_USB_0].gpio, + stark_gpio_ctrl[MHL_USB_1].gpio); + gpio_set_value(stark_gpio_ctrl[MHL_USB_0].gpio, 0); + gpio_set_value(stark_gpio_ctrl[MHL_USB_1].gpio, 0); + } + /*gpio_set_value(stark_gpio_ctrl[MHL_USB_VBUS].gpio, 1);*/ + printk("%s(): exit\n", __func__); +} +EXPORT_SYMBOL(sii_switch_to_mhl); + +int sii6031_gpio_init(void) +{ + int ret; + + printk("%s(): called\n", __func__); + ret = gpio_request_array(stark_gpio_ctrl, ARRAY_SIZE(stark_gpio_ctrl)); + if (ret < 0) + printk("%s(): gpio_request_array failed, error code %d\n",__func__, ret); + + return ret; +} + +static int sii6031_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int value; + + value = of_get_named_gpio_flags(np, "simg,gpio_sel0", 0, NULL); + if (value >= 0) + stark_gpio_ctrl[MHL_USB_0].gpio = value; + + value = of_get_named_gpio_flags(np, "simg,gpio_sel1", 0, NULL); + if (value >= 0) + stark_gpio_ctrl[MHL_USB_1].gpio = value; + + value = of_get_named_gpio_flags(np, "simg,gpio_vbus", 0, NULL); + if (value >= 0) + stark_gpio_ctrl[MHL_USB_VBUS].gpio = value; + + + return 0; + +} + +static int __devinit sii6031_probe(struct platform_device *pdev) +{ + int ret =0; + + + if(pdev->dev.of_node) + ret = sii6031_parse_dt(&pdev->dev); + + + if(ret) + return -1; + + + ret = sii6031_gpio_init(); + + if(ret) + return -1; + + + return 0; + +} +static int __devexit sii6031_remove(struct platform_device *pdev) +{ + gpio_free_array(stark_gpio_ctrl,ARRAY_SIZE(stark_gpio_ctrl)); + + return 0; +} + +static const struct of_device_id sii6031_gpio_match[] = { + { .compatible = "simg,sii-6031", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sii6031_gpio_match); + +static struct platform_driver sii6031_driver = { + .probe = sii6031_probe, + .remove = __devexit_p(sii6031_remove), + .driver = { + .name = "simg,sii-6031", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sii6031_gpio_match), + }, +}; + +static int __init sii6031_init(void) +{ + return platform_driver_register(&sii6031_driver); +} + +static void __exit sii6031_exit(void) +{ + platform_driver_unregister(&sii6031_driver); +} +module_init(sii6031_init); +module_exit(sii6031_exit); + +MODULE_AUTHOR("Praveen Kumar Vuppala"); +MODULE_DESCRIPTION("SiI6031 Switch driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.h b/drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.h new file mode 100644 index 000000000000..13e3c6f57c77 --- /dev/null +++ b/drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.h @@ -0,0 +1,20 @@ +#ifndef __MHL_8620_H +#define __MHL_8620_H + + +#define MHL_USB_0 0 +#define MHL_USB_1 1 +#define MHL_USB_VBUS 2 + +void sii_switch_to_mhl(bool switch_to_mhl); +/*#ifdef CONFIG_SII8061_MHL_SWITCH +void sii_switch_to_mhl(bool switch_to_mhl); +#else +void inline sii_switch_to_mhl(bool switch_to_mhl) +{ + return; + +} +#endif +*/ +#endif