summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorMikhail Amchislavsky <Mikhail.Amchislavsky@siliconimage.com>2014-05-22 13:34:38 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:35:54 -0700
commit3b9fec79f59623447f31cbc3422dd30fe2fafe9c (patch)
treec444bfbcb80ccb6a0445aea6a17e065460e65618 /drivers/video/fbdev
parent9d1c95d590ee445fc14cbeda181487d48af1e4a2 (diff)
mdss: mhl3: Add driver for MHL Transmitter
Code drop from https://github.com/siliconimageinc/sii8620 taken from commit 20b4c581d705cffce422bc79c2bacf7ed5363beb "Posting driver 1.03 release candidate. Version 1.03.19 Signed-off-by: Mikhail Amchislavsky <Mikhail.Amchislavsky@siliconimage.com>" Adding driver for SiI 8620 MHL transmitter. Integrated the files into drivers/video/msm/mdss/mhl3/. Removed unnecessary files: apq8074_kernel_update/apq8074-dragonboard.dtsi apq8074_kernel_update/board-8974-gpiomux.c apq8074_kernel_update/msm8974.dtsi apq8074_kernel_update/sii6031/msm_otg.c apq_build.sh build build_num.txt clean Moved files relating to SiI 6031: apq8074_kernel_update/sii6031/msm_otg.c apq8074_kernel_update/sii6031/si_6031_switch.h apq8074_kernel_update/sii6031/sii_6031/Kconfig apq8074_kernel_update/sii6031/sii_6031/Makefile apq8074_kernel_update/sii6031/sii_6031/si_6031_switch.c into drivers/video/fbdev/msm/mhl3/sii6031/. Change-Id: I29adf3bd4a02406bd9b47c0727d4093cdea94496 Signed-off-by: Casey Piper <cpiper@codeaurora.org> [cip@codeaurora.org: Moved new file locations] Signed-off-by: Clarence Ip <cip@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/mhl3/Kconfig17
-rw-r--r--drivers/video/fbdev/msm/mhl3/Makefile165
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_device_cfg.h39
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_linux_tx.c5717
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_linux_tx.h455
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.c350
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_rbp_inputdev.h40
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.c845
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_rcp_inputdev.h42
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_supp.c4700
-rw-r--r--drivers/video/fbdev/msm/mhl3/mhl_supp.h108
-rw-r--r--drivers/video/fbdev/msm/mhl3/platform.c2181
-rw-r--r--drivers/video/fbdev/msm/mhl3/platform.h236
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_8620.lnt1
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_8620_drv.c7638
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_8620_drv.h113
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_8620_internal_api.h30
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_8620_regs.h1988
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_app_devcap.h67
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_edid.h667
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_emsc_hid-mt.c1919
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_emsc_hid.c1531
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_emsc_hid.h278
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_fw_macros.h40
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_infoframe.h219
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.c851
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_mdt_inputdev.h216
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d.c4562
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_mhl2_edid_3d_api.h183
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_mhl_callback_api.h243
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_mhl_defs.h961
-rw-r--r--drivers/video/fbdev/msm/mhl3/si_mhl_tx_hw_drv_api.h222
-rw-r--r--drivers/video/fbdev/msm/mhl3/sii6031/Kconfig13
-rw-r--r--drivers/video/fbdev/msm/mhl3/sii6031/Makefile2
-rw-r--r--drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.c151
-rw-r--r--drivers/video/fbdev/msm/mhl3/sii6031/si_6031_switch.h20
36 files changed, 36810 insertions, 0 deletions
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 <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/fs.h>
+#include <linux/semaphore.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/cdev.h>
+#include <linux/stringify.h>
+#include <linux/uaccess.h>
+
+#include "si_fw_macros.h"
+#include "si_infoframe.h"
+#include "si_edid.h"
+#include "si_mhl_defs.h"
+#include "si_mhl2_edid_3d_api.h"
+#include "si_mhl_tx_hw_drv_api.h"
+#ifdef MEDIA_DATA_TUNNEL_SUPPORT
+#include "si_mdt_inputdev.h"
+#endif
+#include "mhl_rcp_inputdev.h"
+#if (INCLUDE_RBP == 1)
+#include "mhl_rbp_inputdev.h"
+#endif
+#include "mhl_linux_tx.h"
+#include "mhl_supp.h"
+#include "platform.h"
+#include "si_mhl_callback_api.h"
+#include "si_8620_drv.h"
+
+#define MHL_DRIVER_MINOR_MAX 1
+
+/* Convert a value specified in milliseconds to nanoseconds */
+#define MSEC_TO_NSEC(x) (x * 1000000UL)
+
+static char *white_space = "' ', '\t'";
+static dev_t dev_num;
+
+static struct class *mhl_class;
+
+static void mhl_tx_destroy_timer_support(struct mhl_dev_context *dev_context);
+
+/* Define SysFs attribute names */
+#define SYS_ATTR_NAME_CONN connection_state
+#define SYS_ATTR_NAME_DSHPD ds_hpd
+#define SYS_ATTR_NAME_HDCP2 hdcp2_status
+
+#define SYS_ATTR_NAME_SPAD spad
+#define SYS_ATTR_NAME_I2C_REGISTERS i2c_registers
+#define SYS_ATTR_NAME_DEBUG_LEVEL debug_level
+#define SYS_ATTR_NAME_REG_DEBUG_LEVEL debug_reg_dump
+#define SYS_ATTR_NAME_AKSV aksv
+#define SYS_ATTR_NAME_EDID edid
+#define SYS_ATTR_NAME_HEV_3D_DATA hev_3d_data
+#define SYS_ATTR_NAME_GPIO_INDEX gpio_index
+#define SYS_ATTR_NAME_GPIO_VALUE gpio_value
+#define SYS_ATTR_NAME_PP_16BPP pp_16bpp
+
+#ifdef DEBUG
+#define SYS_ATTR_NAME_TX_POWER tx_power
+#define SYS_ATTR_NAME_STARK_CTL set_stark_ctl
+#endif
+
+/* define SysFs object names */
+#define SYS_ATTR_NAME_IN in
+#define SYS_ATTR_NAME_IN_STATUS in_status
+#define SYS_ATTR_NAME_OUT out
+#define SYS_ATTR_NAME_OUT_STATUS out_status
+#define SYS_ATTR_NAME_INPUT_DEV input_dev
+
+
+#define SYS_OBJECT_NAME_BIST bist
+#define SYS_ATTR_NAME_BIST_ECBUS_DUR ecbus_duration
+#define SYS_ATTR_NAME_BIST_ECBUS_PAT ecbus_pattern
+#define SYS_ATTR_NAME_BIST_ECBUS_FPAT ecbus_fixed_pattern
+#define SYS_ATTR_NAME_BIST_AV_LINK_DR av_link_data_rate
+#define SYS_ATTR_NAME_BIST_AV_LINK_PAT av_link_pattern
+#define SYS_ATTR_NAME_BIST_AV_LINK_VM av_link_video_mode
+#define SYS_ATTR_NAME_BIST_AV_LINK_DUR av_link_duration
+#define SYS_ATTR_NAME_BIST_AV_LINK_FPAT av_link_fixed_pattern
+#define SYS_ATTR_NAME_BIST_AV_LINK_RDZR av_link_randomizer
+#define SYS_ATTR_NAME_BIST_AV_LINK_IMPM impedance_mode
+#define SYS_ATTR_NAME_BIST_SETUP setup
+#define SYS_ATTR_NAME_BIST_TRIGGER trigger
+#define SYS_ATTR_NAME_BIST_STOP stop
+#define SYS_ATTR_NAME_BIST_STATUS_REQ status_req
+#define SYS_ATTR_NAME_T_BIST_MODE_DOWN t_bist_mode_down
+
+
+#define SYS_OBJECT_NAME_REG_ACCESS reg_access
+#define SYS_ATTR_NAME_REG_ACCESS_PAGE page
+#define SYS_ATTR_NAME_REG_ACCESS_OFFSET offset
+#define SYS_ATTR_NAME_REG_ACCESS_LENGTH length
+#define SYS_ATTR_NAME_REG_ACCESS_DATA data
+
+#define SYS_OBJECT_NAME_RAP rap
+#define SYS_ATTR_NAME_RAP_IN SYS_ATTR_NAME_IN
+#define SYS_ATTR_NAME_RAP_IN_STATUS SYS_ATTR_NAME_IN_STATUS
+#define SYS_ATTR_NAME_RAP_OUT SYS_ATTR_NAME_OUT
+#define SYS_ATTR_NAME_RAP_OUT_STATUS SYS_ATTR_NAME_OUT_STATUS
+#define SYS_ATTR_NAME_RAP_INPUT_DEV SYS_ATTR_NAME_INPUT_DEV
+
+#define SYS_OBJECT_NAME_RCP rcp
+#define SYS_ATTR_NAME_RCP_IN SYS_ATTR_NAME_IN
+#define SYS_ATTR_NAME_RCP_IN_STATUS SYS_ATTR_NAME_IN_STATUS
+#define SYS_ATTR_NAME_RCP_OUT SYS_ATTR_NAME_OUT
+#define SYS_ATTR_NAME_RCP_OUT_STATUS SYS_ATTR_NAME_OUT_STATUS
+#define SYS_ATTR_NAME_RCP_INPUT_DEV SYS_ATTR_NAME_INPUT_DEV
+
+#if (INCLUDE_RBP == 1)
+#define SYS_OBJECT_NAME_RBP rbp
+#define SYS_ATTR_NAME_RBP_IN SYS_ATTR_NAME_IN
+#define SYS_ATTR_NAME_RBP_IN_STATUS SYS_ATTR_NAME_IN_STATUS
+#define SYS_ATTR_NAME_RBP_OUT SYS_ATTR_NAME_OUT
+#define SYS_ATTR_NAME_RBP_OUT_STATUS SYS_ATTR_NAME_OUT_STATUS
+#define SYS_ATTR_NAME_RBP_INPUT_DEV SYS_ATTR_NAME_INPUT_DEV
+#endif
+
+#define SYS_OBJECT_NAME_UCP ucp
+#define SYS_ATTR_NAME_UCP_IN SYS_ATTR_NAME_IN
+#define SYS_ATTR_NAME_UCP_IN_STATUS SYS_ATTR_NAME_IN_STATUS
+#define SYS_ATTR_NAME_UCP_OUT SYS_ATTR_NAME_OUT
+#define SYS_ATTR_NAME_UCP_OUT_STATUS SYS_ATTR_NAME_OUT_STATUS
+#define SYS_ATTR_NAME_UCP_INPUT_DEV SYS_ATTR_NAME_INPUT_DEV
+
+#define SYS_OBJECT_NAME_DEVCAP devcap
+#define SYS_ATTR_NAME_DEVCAP_LOCAL_OFFSET local_offset
+#define SYS_ATTR_NAME_DEVCAP_LOCAL local
+#define SYS_ATTR_NAME_DEVCAP_REMOTE_OFFSET remote_offset
+#define SYS_ATTR_NAME_DEVCAP_REMOTE remote
+
+#define SYS_OBJECT_NAME_HDCP hdcp
+#define SYS_ATTR_NAME_HDCP_CONTENT_TYPE hdcp_content_type
+
+#define SYS_OBJECT_NAME_VC vc
+#define SYS_ATTR_NAME_VC_ASSIGN vc_assign
+
+/*
+ * show_connection_state() - Handle read request to the connection_state
+ * attribute file.
+ */
+ssize_t show_connection_state(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+
+ if (dev_context->mhl_flags & MHL_STATE_FLAG_CONNECTED)
+ return scnprintf(buf, PAGE_SIZE, "connected");
+ else
+ return scnprintf(buf, PAGE_SIZE, "not connected");
+}
+
+/*
+ * set_connection_state() - Handle write request to the connection_state
+ * attribute file.
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t set_connection_state(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long new_connection_state = 0x100;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (buf != NULL) {
+ /* BUGZILLA 27012 no prefix here, only on module parameter. */
+ status = kstrtoul(buf, 0, &new_connection_state);
+ if ((status != 0) || (new_connection_state > 0xFF)) {
+ MHL_TX_DBG_ERR("Invalid connection_state: 0x%02lX\n",
+ new_connection_state);
+ goto err_exit;
+ }
+ } else {
+ MHL_TX_DBG_ERR("Missing connection_state parameter\n");
+ status = -EINVAL;
+ goto err_exit;
+ }
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto err_exit;
+ }
+ si_mhl_tx_drv_switch_cbus_mode((struct drv_hw_context *)
+ &dev_context->drv_context, CM_NO_CONNECTION);
+ status = count;
+
+ MHL_TX_DBG_ERR("%sdisconnecting%s\n",
+ ANSI_ESC_YELLOW_TEXT,
+ ANSI_ESC_RESET_TEXT);
+
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * show_ds_hpd_state() - Handle read request to the ds_hpd attribute file.
+ */
+ssize_t show_ds_hpd_state(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int hpd_status = 0;
+ int status;
+
+ status = si_8620_get_hpd_status(&hpd_status);
+ if (status)
+ return status;
+ else
+ return scnprintf(buf, PAGE_SIZE, "%d", hpd_status);
+}
+
+/*
+ * show_ds_hdcp2_status() - Handle read request to the ds_hpd attribute file.
+ */
+ssize_t show_hdcp2_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ uint32_t hdcp2_status = 0;
+ int status;
+
+ status = si_8620_get_hdcp2_status(&hdcp2_status);
+ if (status)
+ return status;
+ else
+ return scnprintf(buf, PAGE_SIZE, "%x", hdcp2_status);
+}
+
+/*
+ * Wrapper for kstrtoul() that nul-terminates the input string at
+ * the first non-digit character instead of returning an error.
+ *
+ * This function is destructive to the input string.
+ *
+ */
+static int si_strtoul(char **str, int base, unsigned long *val)
+{
+ int tok_length, status, nul_offset;
+ char *tstr = *str;
+
+ nul_offset = 1;
+ status = -EINVAL;
+ if ((base == 0) && (tstr[0] == '0') && (tolower(tstr[1]) == 'x')) {
+ tstr += 2;
+ base = 16;
+ }
+
+ tok_length = strspn(tstr, "0123456789ABCDEFabcdef");
+ if (tok_length) {
+ if ((tstr[tok_length] == '\n') || (tstr[tok_length] == 0))
+ nul_offset = 0;
+
+ tstr[tok_length] = 0;
+ status = kstrtoul(tstr, base, val);
+ if (status == 0) {
+ tstr = (tstr + tok_length) + nul_offset;
+ *str = tstr;
+ }
+ }
+ return status;
+}
+
+/*
+ * send_scratch_pad() - Handle write request to the spad attribute file.
+ *
+ * This file is used to either initiate a write to the scratch pad registers
+ * of an attached device, or to set the offset and byte count for a subsequent
+ * read from the local scratch pad registers.
+ *
+ * The format of the string in buf must be:
+ * offset=<offset_value> length=<Length_value> \
+ * data=data_byte_0 ... data_byte_length-1
+ * where: <offset_value> specifies the starting register offset to begin
+ * read/writing within the scratch pad register space
+ * <length_value> number of scratch pad registers to be written/read
+ * data_byte space separated list of <length_value> data bytes to be
+ * written. If no data bytes are present then the write
+ * to this file will only be used to set the offset and
+ * length for a subsequent read from this file.
+ */
+ssize_t send_scratch_pad(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ unsigned long offset = 0x100; /* initialize with invalid values */
+ unsigned long length = 0x100;
+ unsigned long value;
+ u8 data[MAX_SCRATCH_PAD_TRANSFER_SIZE];
+ unsigned int idx;
+ char *str;
+ int status = -EINVAL;
+ struct bist_setup_burst *setup_burst;
+ uint16_t burst_id;
+ char *pinput = kmalloc(count, GFP_KERNEL);
+
+ MHL_TX_DBG_INFO("received string(%d):%s\n", count, buf)
+
+ if (pinput == 0) {
+
+ MHL_TX_DBG_ERR("error exit\n");
+ return -EINVAL;
+ }
+ memcpy(pinput, buf, count);
+
+ /*
+ * Parse the input string and extract the scratch pad register selection
+ * parameters
+ */
+ str = 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:<offset> length:<lenvalue> data:<datavalues>
+ * where: <offset> is the last scratch pad register offset
+ * written to this file
+ * <lenvalue> is the last scratch pad register transfer length
+ * written to this file
+ * <datavalue> space separated list of <lenvalue> scratch pad
+ * register values in OxXX format
+ */
+ssize_t show_scratch_pad(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ u8 data[MAX_SCRATCH_PAD_TRANSFER_SIZE];
+ u8 idx;
+ enum scratch_pad_status scratch_pad_status;
+ int status = -EINVAL;
+
+ MHL_TX_DBG_INFO("called\n");
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto err_exit;
+ }
+
+ if (dev_context->mhl_flags & MHL_STATE_FLAG_CONNECTED) {
+
+ scratch_pad_status = si_get_scratch_pad_vector(dev_context,
+ dev_context->spad_offset,
+ dev_context->spad_xfer_length, data);
+
+ switch (scratch_pad_status) {
+ case SCRATCHPAD_SUCCESS:
+ status = scnprintf(buf, PAGE_SIZE, "offset:0x%02x "
+ "length:0x%02x data:",
+ dev_context->spad_offset,
+ dev_context->spad_xfer_length);
+
+ for (idx = 0; idx < dev_context->spad_xfer_length;
+ idx++) {
+ status +=
+ scnprintf(&buf[status], PAGE_SIZE,
+ "0x%02x ", data[idx]);
+ }
+ break;
+
+ case SCRATCHPAD_BUSY:
+ status = -EAGAIN;
+ break;
+
+ default:
+ status = -EFAULT;
+ break;
+ }
+ }
+
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * set_reg_access_page() - Handle write request to set the
+ * reg access page value.
+ *
+ * The format of the string in buf must be:
+ * <pageaddr>
+ * Where: <pageaddr> specifies the reg page of the register(s)
+ * to be written/read
+ */
+ssize_t set_reg_access_page(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ unsigned long address = 0x100;
+ int status = -EINVAL;
+ char my_buf[20];
+ unsigned int i;
+
+ MHL_TX_COMM_INFO("received string: %c%s%c\n", '"', buf, '"');
+ if (count >= sizeof(my_buf)) {
+ MHL_TX_DBG_ERR("string too long %c%s%c\n", '"', buf, '"');
+ return status;
+ }
+ for (i = 0; i < count; ++i) {
+ if ('\n' == buf[i]) {
+ my_buf[i] = '\0';
+ break;
+ }
+ if ('\t' == buf[i]) {
+ my_buf[i] = '\0';
+ break;
+ }
+ if (' ' == buf[i]) {
+ my_buf[i] = '\0';
+ break;
+ }
+ my_buf[i] = buf[i];
+ }
+
+ status = kstrtoul(my_buf, 0, &address);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
+ } else if (address > 0xFF) {
+ MHL_TX_DBG_ERR("address:0x%x buf:%s%s%s\n", address,
+ ANSI_ESC_RED_TEXT, my_buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->debug_i2c_address = address;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+/*
+ * show_reg_access_page() - Show the current page number to be used when
+ * reg_access/data is accessed.
+ *
+ */
+ssize_t show_reg_access_page(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ MHL_TX_COMM_INFO("called\n");
+
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x",
+ dev_context->debug_i2c_address);
+
+ return status;
+}
+
+/*
+ * set_reg_access_offset() - Handle write request to set the
+ * reg access page value.
+ *
+ * The format of the string in buf must be:
+ * <pageaddr>
+ * Where: <pageaddr> specifies the reg page of the register(s)
+ * to be written/read
+ */
+ssize_t set_reg_access_offset(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ unsigned long offset = 0x100;
+ int status = -EINVAL;
+
+ MHL_TX_COMM_INFO("received string: " "%s" "\n", buf);
+
+ status = kstrtoul(buf, 0, &offset);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%c%s%c\n",
+ status, '"', buf, '"');
+ } else if (offset > 0xFF) {
+ MHL_TX_DBG_ERR("offset:0x%x buf:%c%s%c\n",
+ offset, '"', buf, '"');
+ } else {
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ dev_context->debug_i2c_offset = offset;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+/*
+ * show_reg_access_offset() - Show the current page number to be used when
+ * reg_access/data is accessed.
+ *
+ */
+ssize_t show_reg_access_offset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ MHL_TX_COMM_INFO("called\n");
+
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x",
+ dev_context->debug_i2c_offset);
+
+ return status;
+}
+
+/*
+ * set_reg_access_length() - Handle write request to set the
+ * reg access page value.
+ *
+ * The format of the string in buf must be:
+ * <pageaddr>
+ * Where: <pageaddr> specifies the reg page of the register(s)
+ * to be written/read
+ */
+ssize_t set_reg_access_length(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ unsigned long length = 0x100;
+ int status = -EINVAL;
+
+ MHL_TX_COMM_INFO("received string: " "%s" "\n", buf);
+
+ status = kstrtoul(buf, 0, &length);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s'%s'%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%c%s%c\n",
+ status, '"', buf, '"');
+ } else if (length > 0xFF) {
+ MHL_TX_DBG_ERR("length:0x%x buf:%c%s%c\n",
+ length, '"', buf, '"');
+ } else {
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ dev_context->debug_i2c_xfer_length = length;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+
+ return status;
+}
+
+/*
+ * show_reg_access_length() - Show the current page number to be used when
+ * reg_access/data is accessed.
+ *
+ */
+ssize_t show_reg_access_length(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ MHL_TX_COMM_INFO("called\n");
+
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x",
+ dev_context->debug_i2c_xfer_length);
+
+ return status;
+}
+
+/*
+ * set_reg_access_data() - Handle write request to the
+ * reg_access_data attribute file.
+ *
+ * This file is used to either perform a write to registers of the transmitter
+ * or to set the address, offset and byte count for a subsequent from the
+ * register(s) of the transmitter.
+ *
+ * The format of the string in buf must be:
+ * data_byte_0 ... data_byte_length-1
+ * Where: data_byte is a space separated list of <length_value> data bytes
+ * to be written. If no data bytes are present then
+ * the write to this file will only be used to set
+ * the page address, offset and length for a
+ * subsequent read from this file.
+ */
+ssize_t set_reg_access_data(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ unsigned long value;
+ u8 data[MAX_DEBUG_TRANSFER_SIZE];
+ int i;
+ char *str;
+ int status = -EINVAL;
+ char *pinput = kmalloc(count, GFP_KERNEL);
+
+ MHL_TX_COMM_INFO("received string: %c%s%c\n", '"', buf, '"');
+
+ if (pinput == 0)
+ return -EINVAL;
+ memcpy(pinput, buf, count);
+
+ /*
+ * Parse the input string and extract the scratch pad register
+ * selection parameters
+ */
+ str = pinput;
+ for (i = 0; (i < MAX_DEBUG_TRANSFER_SIZE) && ('\0' != *str); i++) {
+
+ status = si_strtoul(&str, 0, &value);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
+ goto exit_reg_access_data;
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
+ goto exit_reg_access_data;
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d %s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
+ goto exit_reg_access_data;
+ } else if (value > 0xFF) {
+ MHL_TX_DBG_ERR("value:0x%x str:%s%s%s\n", value,
+ ANSI_ESC_RED_TEXT, str, ANSI_ESC_RESET_TEXT);
+ goto exit_reg_access_data;
+ } else {
+ data[i] = value;
+ }
+ }
+
+ if (i == 0) {
+ MHL_TX_COMM_INFO("No data specified\n");
+ goto exit_reg_access_data;
+ }
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ status = -ERESTARTSYS;
+ goto exit_reg_access_data;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+
+ status = dev_context->drv_info->mhl_device_dbg_i2c_reg_xfer(
+ &dev_context->drv_context,
+ dev_context->debug_i2c_address,
+ dev_context->debug_i2c_offset,
+ i, DEBUG_I2C_WRITE, data);
+ if (status == 0)
+ status = count;
+ }
+
+ up(&dev_context->isr_lock);
+
+exit_reg_access_data:
+ kfree(pinput);
+ return status;
+}
+
+/*
+ * show_reg_access_data() - Handle read request to the
+ reg_access_data attribute file.
+ *
+ * Reads from this file return one or more transmitter register values in
+ * hexadecimal string format. The registers returned are specified by the
+ * address, offset and length values previously written to this file.
+ *
+ * The return value is the number characters written to buf, or an error
+ * code if the I2C read fails.
+ *
+ * The format of the string returned in buf is:
+ * "address:<pageaddr> offset:<offset> length:<lenvalue> data:<datavalues>
+ * where: <pageaddr> is the last I2C register page address written
+ * to this file
+ * <offset> is the last register offset written to this file
+ * <lenvalue> is the last register transfer length written
+ * to this file
+ * <datavalue> space separated list of <lenvalue> register
+ * values in OxXX format
+ */
+ssize_t show_reg_access_data(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ u8 data[MAX_DEBUG_TRANSFER_SIZE] = {0};
+ u8 idx;
+ int status = -EINVAL;
+
+ MHL_TX_COMM_INFO("called\n");
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto no_dev;
+ }
+
+ status = dev_context->drv_info->mhl_device_dbg_i2c_reg_xfer(
+ &dev_context->drv_context,
+ dev_context->debug_i2c_address,
+ dev_context->debug_i2c_offset,
+ dev_context->debug_i2c_xfer_length,
+ DEBUG_I2C_READ,
+ data);
+no_dev:
+ up(&dev_context->isr_lock);
+
+ if (status == 0) {
+ status = scnprintf(buf, PAGE_SIZE,
+ "0x%02x'0x%02x:",
+ dev_context->debug_i2c_address,
+ dev_context->debug_i2c_offset
+ );
+
+ for (idx = 0; idx < dev_context->debug_i2c_xfer_length;
+ idx++) {
+ status += scnprintf(&buf[status], PAGE_SIZE, " 0x%02x",
+ data[idx]);
+ }
+ }
+
+ return status;
+}
+
+static struct device_attribute reg_access_page_attr =
+ __ATTR(SYS_ATTR_NAME_REG_ACCESS_PAGE, 0666,
+ show_reg_access_page, set_reg_access_page);
+
+static struct device_attribute reg_access_offset_attr =
+ __ATTR(SYS_ATTR_NAME_REG_ACCESS_OFFSET, 0666,
+ show_reg_access_offset, set_reg_access_offset);
+
+static struct device_attribute reg_access_length_attr =
+ __ATTR(SYS_ATTR_NAME_REG_ACCESS_LENGTH, 0666,
+ show_reg_access_length, set_reg_access_length);
+
+static struct device_attribute reg_access_data_attr =
+ __ATTR(SYS_ATTR_NAME_REG_ACCESS_DATA, 0666,
+ show_reg_access_data, set_reg_access_data);
+
+static struct attribute *reg_access_attrs[] = {
+ &reg_access_page_attr.attr,
+ &reg_access_offset_attr.attr,
+ &reg_access_length_attr.attr,
+ &reg_access_data_attr.attr,
+ NULL
+};
+
+static struct attribute_group reg_access_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_REG_ACCESS),
+ .attrs = reg_access_attrs
+};
+/*
+ * show_debug_level() - Handle read request to the debug_level attribute file.
+ *
+ * The return value is the number characters written to buf, or EAGAIN
+ * if the driver is busy and cannot service the read request immediately.
+ * If EAGAIN is returned the caller should wait a little and retry the
+ * read.
+ */
+ssize_t get_debug_level(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ goto err_exit;
+ }
+
+ status = scnprintf(buf, PAGE_SIZE, "level=%d %s",
+ debug_level,
+ debug_reg_dump ? "(includes reg dump)" : "");
+ MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');
+
+err_exit:
+ up(&dev_context->isr_lock);
+/* this should be a separate sysfs!!!
+ si_dump_important_regs((struct drv_hw_context *)&dev_context->
+ drv_context);
+*/
+
+ return status;
+}
+
+/*
+ * set_debug_level() - Handle write request to the debug_level attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t set_debug_level(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ long new_debug_level = 0x100;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (buf != NULL) {
+ /* BUGZILLA 27012 no prefix here, only on module parameter. */
+ status = kstrtol(buf, 0, &new_debug_level);
+ if (status != 0) {
+ MHL_TX_DBG_ERR("Invalid debug_level: 0x%02lX\n",
+ new_debug_level);
+ goto err_exit;
+ }
+ } else {
+ MHL_TX_DBG_ERR("Missing debug_level parameter\n");
+ status = -EINVAL;
+ goto err_exit;
+ }
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto err_exit;
+ }
+ debug_level = new_debug_level;
+ status = count;
+
+ MHL_TX_GENERIC_DBG_PRINT(debug_level,
+ "new debug_level=%d\n", debug_level);
+
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * show_debug_reg_dump()
+ *
+ * Handle read request to the debug_reg_dump attribute file.
+ *
+ * The return value is the number characters written to buf, or EAGAIN
+ * if the driver is busy and cannot service the read request immediately.
+ * If EAGAIN is returned the caller should wait a little and retry the
+ * read.
+ */
+ssize_t get_debug_reg_dump(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ goto err_exit;
+ }
+
+ status = scnprintf(buf, PAGE_SIZE, "%s",
+ debug_reg_dump ? "(includes reg dump)" : "");
+ MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');
+
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * set_debug_reg_dump()
+ *
+ * Handle write request to the debug_reg_dump attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t set_debug_reg_dump(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long new_debug_reg_dump = 0x100;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &new_debug_reg_dump);
+ if (status != 0)
+ goto err_exit2;
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto err_exit;
+ }
+ debug_reg_dump = (new_debug_reg_dump > 0) ? true : false;
+ MHL_TX_DBG_ERR("debug dump %s\n", debug_reg_dump ? "ON" : "OFF");
+
+ status = count;
+err_exit:
+ up(&dev_context->isr_lock);
+err_exit2:
+ return status;
+}
+
+/*
+ * show_gpio_index() - Handle read request to the gpio_index attribute file.
+ *
+ * The return value is the number characters written to buf, or EAGAIN
+ * if the driver is busy and cannot service the read request immediately.
+ * If EAGAIN is returned the caller should wait a little and retry the
+ * read.
+ */
+ssize_t get_gpio_index(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ goto err_exit;
+ }
+
+ status = scnprintf(buf, PAGE_SIZE, "%d", gpio_index);
+ MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');
+
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * set_gpio_index() - Handle write request to the gpio_index attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t set_gpio_index(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long new_gpio_index;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto err_exit;
+ }
+ if (kstrtoul(buf, 0, &new_gpio_index) != 0)
+ goto err_exit;
+
+ gpio_index = new_gpio_index;
+ MHL_TX_DBG_INFO("gpio: %d\n", gpio_index);
+
+ status = count;
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * show_gpio_level() - Handle read request to the gpio_level attribute file.
+ *
+ * The return value is the number characters written to buf, or EAGAIN
+ * if the driver is busy and cannot service the read request immediately.
+ * If EAGAIN is returned the caller should wait a little and retry the
+ * read.
+ */
+ssize_t get_gpio_level(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+ int gpio_level;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ goto err_exit;
+ }
+ gpio_level = gpio_get_value(gpio_index);
+ status = scnprintf(buf, PAGE_SIZE, "%d", gpio_level);
+ MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');
+
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * set_gpio_level() - Handle write request to the gpio_level attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t set_gpio_level(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long gpio_level;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto err_exit;
+ }
+ if (kstrtoul(buf, 0, &gpio_level) != 0)
+ goto err_exit;
+
+ MHL_TX_DBG_INFO("gpio: %d<-%d\n", gpio_index, gpio_level);
+ gpio_set_value(gpio_index, gpio_level);
+ status = count;
+
+err_exit:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * show_pp_16bpp() - Handle read request to the gpio_level attribute file.
+ *
+ * The return value is the number characters written to buf, or EAGAIN
+ * if the driver is busy and cannot service the read request immediately.
+ * If EAGAIN is returned the caller should wait a little and retry the
+ * read.
+ */
+ssize_t show_pp_16bpp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+ int pp_16bpp;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ pp_16bpp = si_mhl_tx_drv_get_pp_16bpp_override(dev_context);
+ status = scnprintf(buf, PAGE_SIZE, "%d", pp_16bpp);
+ MHL_TX_DBG_INFO("buf:%c%s%c\n", '"', buf, '"');
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * set_pp_16bpp() - Handle write request to the gpio_level attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t set_pp_16bpp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+ unsigned long pp_16bpp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &pp_16bpp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n"
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ si_mhl_tx_drv_set_pp_16bpp_override(dev_context,
+ pp_16bpp);
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+/*
+ * show_aksv() - Handle read request to the aksv attribute file.
+ *
+ * Reads from this file return the 5 bytes of the transmitter's
+ * Key Selection Vector (KSV). The registers are returned as a string
+ * of space separated list of hexadecimal values formated as 0xXX.
+ *
+ * The return value is the number characters written to buf.
+ */
+ssize_t show_aksv(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ u8 data[5] = {0};
+ u8 idx;
+ int status = -EINVAL;
+
+ MHL_TX_DBG_INFO("called\n");
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ goto no_dev;
+ }
+
+ status = dev_context->drv_info->mhl_device_get_aksv(
+ (struct drv_hw_context *)&dev_context->drv_context, data);
+no_dev:
+ up(&dev_context->isr_lock);
+
+ if (status == 0) {
+
+ for (idx = 0; idx < 5; idx++) {
+ status += scnprintf(&buf[status], PAGE_SIZE, "0x%02x ",
+ data[idx]);
+ }
+ }
+
+ return status;
+}
+
+/*
+ * show_edid() - Handle read request to the aksv attribute file.
+ *
+ * Reads from this file return the edid, as processed by this driver and
+ * presented upon the upstream DDC interface.
+ *
+ * The return value is the number characters written to buf.
+ */
+ssize_t show_edid(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ u8 edid_buffer[256] = {0};
+ int status = -EINVAL;
+
+ MHL_TX_DBG_INFO("called\n");
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ status = si_mhl_tx_drv_sample_edid_buffer(
+ (struct drv_hw_context *)&dev_context->drv_context,
+ edid_buffer);
+ }
+
+ up(&dev_context->isr_lock);
+
+ if (status == 0) {
+ int idx, i;
+ for (idx = 0, i = 0; i < 16; i++) {
+ u8 j;
+ for (j = 0; j < 16; ++j, ++idx) {
+ status += scnprintf(&buf[status],
+ PAGE_SIZE, "0x%02x ",
+ edid_buffer[idx]);
+ }
+ status += scnprintf(&buf[status], PAGE_SIZE, "\n");
+ }
+ }
+
+ return status;
+}
+
+/*
+ * show_hev_3d()
+ *
+ * Reads from this file return the HEV_VIC, HEV_DTD,3D_VIC, and
+ * 3D_DTD WRITE_BURST information presented in a format showing the
+ * respective associations.
+ *
+ * The return value is the number characters written to buf.
+ */
+ssize_t show_hev_3d(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ MHL_TX_DBG_INFO("called\n");
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ struct edid_3d_data_t *p_edid_data =
+ dev_context->edid_parser_context;
+ int i;
+ status = 0;
+ status += scnprintf(&buf[status], PAGE_SIZE, "HEV_DTD list:\n");
+ for (i = 0; i < (int)p_edid_data->hev_dtd_info.num_items; ++i) {
+
+ status += scnprintf(&buf[status], PAGE_SIZE,
+ "%s %s %s 0x%02x 0x%04x 0x%04x 0x%04x 0x%04x "
+ "0x%04x 0x%02x -- 0x%04x 0x%02x 0x%02x 0x%02x "
+ "0x%02x 0x%02x\n",
+ p_edid_data->hev_dtd_list[i]._3d_info.vdi_l.
+ top_bottom ? "TB" : "--",
+ p_edid_data->hev_dtd_list[i]._3d_info.vdi_l.
+ left_right ? "LR" : "--",
+ p_edid_data->hev_dtd_list[i]._3d_info.vdi_l.
+ frame_sequential ? "FS" : "--",
+ p_edid_data->hev_dtd_list[i].sequence_index,
+ ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
+ a.pixel_clock_in_MHz),
+ ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
+ a.h_active_in_pixels),
+ ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
+ a.h_blank_in_pixels),
+ ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
+ a.h_front_porch_in_pixels),
+ ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
+ a.h_sync_width_in_pixels),
+ p_edid_data->hev_dtd_list[i].a.h_flags,
+ ENDIAN_CONVERT_16(p_edid_data->hev_dtd_list[i].
+ b.v_total_in_lines),
+ p_edid_data->hev_dtd_list[i].b.
+ v_blank_in_lines,
+ p_edid_data->hev_dtd_list[i].b.
+ v_front_porch_in_lines,
+ p_edid_data->hev_dtd_list[i].b.
+ v_sync_width_in_lines,
+ p_edid_data->hev_dtd_list[i].b.
+ v_refresh_rate_in_fields_per_second,
+ p_edid_data->hev_dtd_list[i].b.v_flags);
+ }
+
+ status += scnprintf(&buf[status], PAGE_SIZE, "HEV_VIC list:\n");
+ for (i = 0; i < (int)p_edid_data->hev_vic_info.num_items; ++i) {
+ status += scnprintf(&buf[status], PAGE_SIZE,
+ "%s %s %s 0x%02x 0x%02x\n",
+ p_edid_data->hev_vic_list[i]._3d_info.vdi_l.
+ top_bottom ? "TB" : "--",
+ p_edid_data->hev_vic_list[i]._3d_info.vdi_l.
+ left_right ? "LR" : "--",
+ p_edid_data->hev_vic_list[i]._3d_info.vdi_l.
+ frame_sequential ? "FS" : "--",
+ p_edid_data->hev_vic_list[i].mhl3_hev_vic_descriptor.
+ vic_cea861f,
+ p_edid_data->hev_vic_list[i].mhl3_hev_vic_descriptor.
+ reserved);
+ }
+
+ status += scnprintf(&buf[status], PAGE_SIZE, "3D_DTD list:\n");
+ for (i = 0; i < (int)p_edid_data->_3d_dtd_info.num_items; ++i) {
+ status += scnprintf(&buf[status], PAGE_SIZE,
+ /* pixel clk */
+ "%s %s %s " "0x%02x 0x%02x "
+ /* horizontal active and blanking */
+ "0x%02x 0x%02x {0x%1x 0x%1x} "
+ /* vertical active and blanking */
+ "0x%02x 0x%02x {0x%1x 0x%1x} "
+ /* sync pulse width and offset */
+ "0x%02x 0x%02x {0x%1x 0x%1x} "
+ "{0x%1x 0x%1x 0x%1x 0x%1x} "
+ /* Image sizes */
+ "0x%02x 0x%02x {0x%1x 0x%1x} "
+ /* borders and flags */
+ "0x%1x 0x%1x {0x%1x 0x%1x 0x%1x 0x%1x %s}\n",
+ p_edid_data->_3d_dtd_list[i]._3d_info.vdi_l.
+ top_bottom ? "TB" : "--",
+ p_edid_data->_3d_dtd_list[i]._3d_info.vdi_l.
+ left_right ? "LR" : "--",
+ p_edid_data->_3d_dtd_list[i]._3d_info.vdi_l.
+ frame_sequential ? "FS" : "--",
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ pixel_clock_low,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ pixel_clock_high,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_active_7_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_blanking_7_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_active_blanking_high.
+ horz_blanking_11_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_active_blanking_high.
+ horz_active_11_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ vert_active_7_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ vert_blanking_7_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ vert_active_blanking_high.
+ vert_blanking_11_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ vert_active_blanking_high.
+ vert_active_11_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_sync_offset_7_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_sync_pulse_width7_0,
+ p_edid_data->_3d_dtd_list[i].
+ dtd_cea_861.vert_sync_offset_width.
+ vert_sync_pulse_width_3_0,
+ p_edid_data->_3d_dtd_list[i].
+ dtd_cea_861.vert_sync_offset_width.
+ vert_sync_offset_3_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ hs_vs_offset_pulse_width.
+ vert_sync_pulse_width_5_4,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ hs_vs_offset_pulse_width.
+ vert_sync_offset_5_4,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ hs_vs_offset_pulse_width.
+ horz_sync_pulse_width_9_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ hs_vs_offset_pulse_width.
+ horz_sync_offset_9_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_image_size_in_mm_7_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ vert_image_size_in_mm_7_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ image_size_high.
+ vert_image_size_in_mm_11_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ image_size_high.
+ horz_image_size_in_mm_11_8,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ horz_border_in_lines,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.
+ vert_border_in_pixels,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
+ stereo_bit_0,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
+ sync_signal_options,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
+ sync_signal_type,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
+ stereo_bits_2_1,
+ p_edid_data->_3d_dtd_list[i].dtd_cea_861.flags.
+ interlaced ? "interlaced" :
+ "progressive");
+ }
+
+ status += scnprintf(&buf[status], PAGE_SIZE, "3d_VIC list:\n");
+ for (i = 0; i < (int)p_edid_data->_3d_vic_info.num_items; ++i) {
+ status +=
+ scnprintf(&buf[status], PAGE_SIZE,
+ "%s %s %s %s 0x%02x\n",
+ p_edid_data->_3d_vic_list[i]._3d_info.
+ vdi_l.top_bottom ? "TB" : "--",
+ p_edid_data->_3d_vic_list[i]._3d_info.
+ vdi_l.left_right ? "LR" : "--",
+ p_edid_data->_3d_vic_list[i]._3d_info.
+ vdi_l.frame_sequential ? "FS" : "--",
+ p_edid_data->_3d_vic_list[i].svd.
+ native ? "N" : " ",
+ p_edid_data->_3d_vic_list[i].svd.VIC);
+ }
+ }
+
+ up(&dev_context->isr_lock);
+ return status;
+}
+
+#ifdef DEBUG
+/*
+ * set_tx_power() - Handle write request to the tx_power attribute file.
+ *
+ * Write the string "on" or "off" to this file to power on or off the
+ * MHL transmitter.
+ *
+ * The return value is the number of characters in buf if successful or an
+ * error code if not successful.
+ */
+ssize_t set_tx_power(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = 0;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else if (strnicmp("on", buf, count - 1) == 0) {
+ status = si_8620_power_control(true);
+ } else if (strnicmp("off", buf, count - 1) == 0) {
+ status = si_8620_power_control(false);
+ } else {
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ }
+
+ if (status != 0)
+ return status;
+ else
+ return count;
+}
+
+/*
+ * set_stark_ctl() - Handle write request to the tx_power attribute file.
+ *
+ * Write the string "on" or "off" to this file to power on or off the
+ * MHL transmitter.
+ *
+ * The return value is the number of characters in buf if successful or an
+ * error code if not successful.
+ */
+ssize_t set_stark_ctl(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = 0;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ if (strnicmp("on", buf, count - 1) == 0) {
+ /*
+ * Set MHL/USB switch to USB
+ * NOTE: Switch control is implemented differently on
+ * each version of the starter kit.
+ */
+ set_pin(X02_USB_SW_CTRL, 1);
+ } else if (strnicmp("off", buf, count - 1) == 0) {
+ /*
+ * Set MHL/USB switch to USB
+ * NOTE: Switch control is implemented differently on
+ * each version of the starter kit.
+ */
+ set_pin(X02_USB_SW_CTRL, 0);
+ } else {
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ if (status != 0)
+ return status;
+ else
+ return count;
+}
+#endif
+
+ssize_t show_bist_ecbus_duration(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->sysfs_bist_setup.e_cbus_duration);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_ecbus_duration(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.e_cbus_duration = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_ecbus_pattern(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%04x\n",
+ dev_context->sysfs_bist_setup.e_cbus_pattern);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_ecbus_pattern(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.e_cbus_pattern = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+
+}
+
+ssize_t show_bist_ecbus_fixed_pattern(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%04x\n",
+ dev_context->sysfs_bist_setup.e_cbus_fixed_pat);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_ecbus_fixed_pattern(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFFFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.e_cbus_fixed_pat = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_av_link_data_rate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE,
+ "avlink_data_rate: 0x%02x\n",
+ dev_context->sysfs_bist_setup.avlink_data_rate);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_av_link_data_rate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.avlink_data_rate = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_av_link_pattern(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->sysfs_bist_setup.avlink_pattern);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_av_link_pattern(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.avlink_pattern = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_av_link_video_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->sysfs_bist_setup.avlink_video_mode);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_av_link_video_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.avlink_video_mode = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_av_link_duration(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->sysfs_bist_setup.avlink_duration);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_av_link_duration(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.avlink_duration = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_av_link_fixed_pattern(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%04x\n",
+ dev_context->sysfs_bist_setup.avlink_fixed_pat);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_av_link_fixed_pattern(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFFFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.avlink_fixed_pat = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_av_link_randomizer(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->sysfs_bist_setup.avlink_randomizer);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_av_link_randomizer(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.avlink_randomizer = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_impedance_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->sysfs_bist_setup.impedance_mode);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_impedance_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.impedance_mode = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t set_bist_setup(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ MHL_TX_DBG_ERR("\n");
+ si_mhl_tx_execute_bist(dev_context,
+ &dev_context->sysfs_bist_setup);
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t set_bist_trigger(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.bist_trigger_parm =
+ (u8)temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t set_bist_stop(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ status = si_mhl_tx_bist_stop(dev_context);
+ MHL_TX_DBG_ERR("stop status: %d\n", status);
+ if (status == BIST_STATUS_NO_ERROR)
+ status = count;
+ else
+ status = -EINVAL;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_bist_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "unimplemented");
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_bist_status_req(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.bist_stat_parm =
+ (u8) temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+ssize_t show_t_bist_mode_down(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "%d",
+ dev_context->sysfs_bist_setup.t_bist_mode_down);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_t_bist_mode_down(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long temp;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ status = kstrtoul(buf, 0, &temp);
+ if (-ERANGE == status) {
+ MHL_TX_DBG_ERR("ERANGE %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (-EINVAL == status) {
+ MHL_TX_DBG_ERR("EINVAL %s%s%s\n",
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (status != 0) {
+ MHL_TX_DBG_ERR("status:%d buf:%s%s%s\n", status,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else if (temp > 0xFF) {
+ MHL_TX_DBG_ERR("temp:0x%x buf:%s%s%s\n", temp,
+ ANSI_ESC_RED_TEXT, buf, ANSI_ESC_RESET_TEXT);
+ } else {
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("%scould not get mutex%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("%sDEV_FLAG_SHUTDOWN%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = -ENODEV;
+ } else {
+ dev_context->sysfs_bist_setup.t_bist_mode_down = temp;
+ status = count;
+ }
+ up(&dev_context->isr_lock);
+ }
+
+ return status;
+}
+
+static struct device_attribute bist_ecbus_duration_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_ECBUS_DUR,
+ 0666, show_bist_ecbus_duration, set_bist_ecbus_duration);
+static struct device_attribute bist_ecbus_pattern_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_ECBUS_PAT,
+ 0666, show_bist_ecbus_pattern, set_bist_ecbus_pattern);
+static struct device_attribute bist_ecbus_fixed_pattern_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_ECBUS_FPAT, 0666,
+ show_bist_ecbus_fixed_pattern, set_bist_ecbus_fixed_pattern);
+static struct device_attribute bist_av_link_data_rate_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_AV_LINK_DR, 0666,
+ show_bist_av_link_data_rate, set_bist_av_link_data_rate);
+static struct device_attribute bist_av_link_pattern_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_AV_LINK_PAT, 0666,
+ show_bist_av_link_pattern, set_bist_av_link_pattern);
+static struct device_attribute bist_av_link_video_mode_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_AV_LINK_VM, 0666,
+ show_bist_av_link_video_mode, set_bist_av_link_video_mode);
+static struct device_attribute bist_av_link_duration_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_AV_LINK_DUR, 0666,
+ show_bist_av_link_duration, set_bist_av_link_duration);
+static struct device_attribute bist_av_link_fixed_pattern_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_AV_LINK_FPAT, 0666,
+ show_bist_av_link_fixed_pattern,
+ set_bist_av_link_fixed_pattern);
+static struct device_attribute bist_av_link_randomizer_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_AV_LINK_RDZR, 0666,
+ show_bist_av_link_randomizer, set_bist_av_link_randomizer);
+static struct device_attribute bist_impedance_mode_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_AV_LINK_IMPM, 0666,
+ show_bist_impedance_mode, set_bist_impedance_mode);
+static struct device_attribute bist_setup_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_SETUP, 0222,
+ NULL, set_bist_setup);
+static struct device_attribute bist_trigger_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_TRIGGER, 0222,
+ NULL, set_bist_trigger);
+static struct device_attribute bist_stop_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_STOP, 0222,
+ NULL, set_bist_stop);
+static struct device_attribute bist_status_attr =
+ __ATTR(SYS_ATTR_NAME_BIST_STATUS_REQ, 0666,
+ show_bist_status, set_bist_status_req);
+
+static struct device_attribute t_bist_mode_down_attr =
+ __ATTR(SYS_ATTR_NAME_T_BIST_MODE_DOWN, 0666,
+ show_t_bist_mode_down, set_t_bist_mode_down);
+
+static struct attribute *bist_attrs[] = {
+ &bist_ecbus_duration_attr.attr,
+ &bist_ecbus_pattern_attr.attr,
+ &bist_ecbus_fixed_pattern_attr.attr,
+ &bist_av_link_data_rate_attr.attr,
+ &bist_av_link_pattern_attr.attr,
+ &bist_av_link_video_mode_attr.attr,
+ &bist_av_link_duration_attr.attr,
+ &bist_av_link_fixed_pattern_attr.attr,
+ &bist_av_link_randomizer_attr.attr,
+ &bist_impedance_mode_attr.attr,
+ &bist_setup_attr.attr,
+ &bist_trigger_attr.attr,
+ &bist_stop_attr.attr,
+ &bist_status_attr.attr,
+ &t_bist_mode_down_attr.attr,
+ NULL
+};
+
+static struct attribute_group bist_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_BIST),
+ .attrs = bist_attrs
+};
+
+
+#if (INCLUDE_SII6031 == 1)
+static int mhl_tx_discover_mhl_device(void *mhl_ctx, int id,
+ void (*mhl_notify_cb)(void *, int), void *motg)
+{
+ struct mhl_dev_context *dev_context;
+ int remaining_time;
+
+ dev_context = (struct mhl_dev_context *)mhl_ctx;
+ dev_context->mhl_discovery_in_progress = true;
+ init_completion(&dev_context->sem_mhl_discovery_complete);
+
+ remaining_time = wait_for_completion_interruptible_timeout(
+ &dev_context->sem_mhl_discovery_complete, 2 * HZ);
+ dev_context->mhl_discovery_in_progress = false;
+ if (dev_context->mhl_detected) {
+ pr_debug("mhl driver detected mhl connection\n");
+ return 1;
+ } else
+ return 0;
+}
+
+void mhl_tx_notify_otg(struct mhl_dev_context *dev_context, bool mhl_detected)
+{
+ dev_context->mhl_detected = mhl_detected;
+ if (dev_context->mhl_discovery_in_progress)
+ complete(&dev_context->sem_mhl_discovery_complete);
+ otg_mhl_notify(dev_context, mhl_detected);
+}
+
+static void mhl_tx_register_mhl_discovery(struct mhl_dev_context *dev_context)
+{
+ if (otg_register_mhl_discovery((void *)dev_context,
+ mhl_tx_discover_mhl_device))
+ pr_debug("%s: USB callback registration failed\n", __func__);
+}
+#endif
+#define MAX_EVENT_STRING_LEN 128
+/*
+ * Handler for event notifications from the MhlTx layer.
+ *
+ */
+void mhl_event_notify(struct mhl_dev_context *dev_context, u32 event,
+ u32 event_param, void *data)
+{
+ char event_string[MAX_EVENT_STRING_LEN];
+ char *envp[] = { event_string, NULL };
+ char *buf;
+ u32 length;
+ u32 count;
+ int idx;
+
+ MHL_TX_DBG_INFO("called, event: 0x%08x event_param: 0x%08x\n",
+ event, event_param);
+
+ switch (event) {
+
+ case MHL_TX_EVENT_CONNECTION:
+ dev_context->mhl_flags |= MHL_STATE_FLAG_CONNECTED;
+ init_rcp_input_dev(dev_context);
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_CONN));
+
+ strncpy(event_string, "MHLEVENT=connected",
+ MAX_EVENT_STRING_LEN);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+#if (INCLUDE_SII6031 == 1)
+ mhl_tx_notify_otg(dev_context, true);
+#endif
+ break;
+
+ case MHL_TX_EVENT_DISCONNECTION:
+ dev_context->mhl_flags = 0;
+#if (INCLUDE_RBP == 1)
+ dev_context->rbp_in_button_code = 0;
+ dev_context->rbp_out_button_code = 0;
+ dev_context->rbp_err_code = 0;
+ dev_context->rbp_send_status = 0;
+#endif
+ dev_context->rcp_in_key_code = 0;
+ dev_context->rcp_out_key_code = 0;
+ dev_context->rcp_err_code = 0;
+ dev_context->rcp_send_status = 0;
+ dev_context->ucp_in_key_code = 0;
+ dev_context->ucp_out_key_code = 0;
+ dev_context->ucp_err_code = 0;
+ dev_context->spad_send_status = 0;
+
+#if (INCLUDE_HID == 1)
+ mhl3_hid_remove_all(dev_context);
+#endif
+#ifdef MEDIA_DATA_TUNNEL_SUPPORT
+ mdt_destroy(dev_context);
+#endif
+#if (INCLUDE_SII6031 == 1)
+ mhl_tx_notify_otg(dev_context, false);
+#endif
+ destroy_rcp_input_dev(dev_context);
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_CONN));
+
+ strncpy(event_string, "MHLEVENT=disconnected",
+ MAX_EVENT_STRING_LEN);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+ break;
+
+ case MHL_TX_EVENT_RCP_RECEIVED:
+ if (input_dev_rcp) {
+ int result;
+
+ result = generate_rcp_input_event(dev_context,
+ (uint8_t) event_param);
+ if (0 == result)
+ si_mhl_tx_rcpk_send(dev_context,
+ (uint8_t) event_param);
+ else
+ si_mhl_tx_rcpe_send(dev_context,
+ MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE);
+ } else {
+ dev_context->mhl_flags &= ~MHL_STATE_FLAG_RCP_SENT;
+ dev_context->mhl_flags |= MHL_STATE_FLAG_RCP_RECEIVED;
+ dev_context->rcp_in_key_code = event_param;
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_RCP));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_RCP key code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ }
+ break;
+
+ case MHL_TX_EVENT_RCPK_RECEIVED:
+ if ((dev_context->mhl_flags & MHL_STATE_FLAG_RCP_SENT)
+ && (dev_context->rcp_out_key_code == event_param)) {
+
+ if (!(dev_context->mhl_flags & MHL_STATE_FLAG_RCP_NAK))
+ dev_context->rcp_err_code = 0;
+ dev_context->mhl_flags |= MHL_STATE_FLAG_RCP_ACK;
+ dev_context->mhl_flags &= ~MHL_STATE_FLAG_RCP_SENT;
+ MHL_TX_DBG_INFO(
+ "Generating RCPK rcvd event, keycode: 0x%02x\n",
+ event_param);
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_RCPK));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_RCPK key code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_ERR(
+ "Unexpected RCPK rcvd event, keycode: 0x%02x\n",
+ event_param);
+ }
+ break;
+
+ case MHL_TX_EVENT_RCPE_RECEIVED:
+ if (input_dev_rcp) {
+ /* do nothing */
+ } else {
+ if (dev_context->mhl_flags & MHL_STATE_FLAG_RCP_SENT) {
+
+ dev_context->rcp_err_code = event_param;
+ dev_context->mhl_flags |=
+ MHL_STATE_FLAG_RCP_NAK;
+
+ MHL_TX_DBG_INFO(
+ "Generating RCPE received event, "
+ "error code: 0x%02x\n", event_param);
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_RCPK));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=rcvd_RCPE error code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_ERR(
+ "Ignoring unexpected RCPE received "
+ "event, error code: 0x%02x\n",
+ event_param);
+ }
+ }
+ break;
+
+ case MHL_TX_EVENT_UCP_RECEIVED:
+ if (input_dev_ucp) {
+ if (event_param <= 0xFF)
+ si_mhl_tx_ucpk_send(dev_context,
+ (uint8_t) event_param);
+ else
+ si_mhl_tx_ucpe_send(dev_context,
+ MHL_UCPE_STATUS_INEFFECTIVE_KEY_CODE);
+ } else {
+ dev_context->mhl_flags &= ~MHL_STATE_FLAG_UCP_SENT;
+ dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_RECEIVED;
+ dev_context->ucp_in_key_code = event_param;
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_UCP));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_UCP key code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ }
+ break;
+
+ case MHL_TX_EVENT_UCPK_RECEIVED:
+ if ((dev_context->mhl_flags & MHL_STATE_FLAG_UCP_SENT) &&
+ (dev_context->ucp_out_key_code == event_param)) {
+
+ dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_ACK;
+
+ MHL_TX_DBG_INFO("Generating UCPK received event, "
+ "keycode: 0x%02x\n", event_param);
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_UCPK));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_UCPK key code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_ERR("Ignoring unexpected UCPK received "
+ "event, keycode: 0x%02x\n", event_param);
+ }
+ break;
+
+ case MHL_TX_EVENT_UCPE_RECEIVED:
+ if (dev_context->mhl_flags & MHL_STATE_FLAG_UCP_SENT) {
+
+ dev_context->ucp_err_code = event_param;
+ dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_NAK;
+
+ MHL_TX_DBG_INFO("Generating UCPE received event, "
+ "error code: 0x%02x\n", event_param);
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_UCPK));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_UCPE error code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_ERR("Ignoring unexpected UCPE received "
+ "event, error code: 0x%02x\n",
+ event_param);
+ }
+ break;
+#if (INCLUDE_RBP == 1)
+ case MHL_TX_EVENT_RBP_RECEIVED:
+ if (input_dev_rbp) {
+ if (((event_param >= 0x01) && (event_param <= 0x07)) ||
+ ((event_param >= 0x20) && (event_param <= 0x21)) ||
+ ((event_param >= 0x30) && (event_param <= 0x35)))
+ si_mhl_tx_rbpk_send(dev_context,
+ (uint8_t) event_param);
+ else
+ si_mhl_tx_rbpe_send(dev_context,
+ MHL_RBPE_STATUS_INEFFECTIVE_BUTTON_CODE
+ );
+ } else {
+ dev_context->mhl_flags |= MHL_STATE_FLAG_RBP_RECEIVED;
+ dev_context->rbp_in_button_code = event_param;
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_RBP));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_RBP button code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ }
+ break;
+
+ case MHL_TX_EVENT_RBPK_RECEIVED:
+ MHL_TX_DBG_INFO("RBPK received\n");
+ if ((dev_context->mhl_flags & MHL_STATE_FLAG_RBP_SENT)
+ && (dev_context->rbp_out_button_code == event_param)) {
+
+ dev_context->mhl_flags |= MHL_STATE_FLAG_RBP_ACK;
+
+ MHL_TX_DBG_INFO(
+ "Generating RBPK rcvd event, button code: 0x%02x\n",
+ event_param);
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_RBPK));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_RBPK button code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_ERR(
+ "Unexpected RBPK rcvd event, button code: 0x%02x\n",
+ event_param);
+ }
+ break;
+
+ case MHL_TX_EVENT_RBPE_RECEIVED:
+ MHL_TX_DBG_INFO("RBPE received\n");
+ if (input_dev_rbp) {
+ /* do nothing */
+ } else {
+ if (dev_context->mhl_flags & MHL_STATE_FLAG_RBP_SENT) {
+
+ dev_context->rbp_err_code = event_param;
+ dev_context->mhl_flags |=
+ MHL_STATE_FLAG_RBP_NAK;
+
+ MHL_TX_DBG_INFO(
+ "Generating RBPE received event, "
+ "error code: 0x%02x\n", event_param);
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_RBPK));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=rcvd_RBPE error code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_ERR(
+ "Ignoring unexpected RBPE received "
+ "event, error code: 0x%02x\n",
+ event_param);
+ }
+ }
+ break;
+#endif
+ case MHL_TX_EVENT_SPAD_RECEIVED:
+ length = event_param;
+ buf = data;
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_SPAD));
+
+ idx = snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=SPAD_CHG length=0x%02x data=", length);
+
+ count = 0;
+ while (idx < MAX_EVENT_STRING_LEN) {
+ if (count >= length)
+ break;
+
+ idx += snprintf(&event_string[idx],
+ MAX_EVENT_STRING_LEN - idx, "0x%02x ",
+ buf[count]);
+ count++;
+ }
+
+ if (idx < MAX_EVENT_STRING_LEN) {
+ kobject_uevent_env(&dev_context->mhl_dev->kobj,
+ KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_ERR(
+ "Buffer too small for scratch pad data!\n");
+ }
+ break;
+
+ case MHL_TX_EVENT_POW_BIT_CHG:
+ MHL_TX_DBG_INFO("Generating VBUS power bit change "
+ "event, POW bit is %s\n",
+ event_param ? "ON" : "OFF");
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=MHL VBUS power %s",
+ event_param ? "ON" : "OFF");
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+ break;
+
+ case MHL_TX_EVENT_RAP_RECEIVED:
+ MHL_TX_DBG_INFO("Generating RAP received event, "
+ "action code: 0x%02x\n", event_param);
+
+ sysfs_notify(&dev_context->mhl_dev->kobj, NULL,
+ __stringify(SYS_ATTR_NAME_RAP_IN));
+
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_RAP action code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+ break;
+
+ case MHL_TX_EVENT_BIST_READY_RECEIVED:
+ MHL_TX_DBG_INFO("Received BIST_READY 0x%02X\n", event_param);
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_BIST_READY code=0x%02x",
+ event_param);
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+ break;
+
+ case MHL_TX_EVENT_BIST_TEST_DONE:
+ MHL_TX_DBG_INFO("Triggered BIST test completed.\n");
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=BIST_complete");
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+ break;
+
+ case MHL_TX_EVENT_BIST_STATUS_RECEIVED:
+ {
+ struct bist_stat_info *stat =
+ (struct bist_stat_info *)data;
+ MHL_TX_DBG_INFO("Received BIST_RETURN_STAT\n",
+ event_param);
+ if (stat != NULL) {
+ MHL_TX_DBG_INFO
+ ("eCBUS Rx: 0x%04X,"
+ " eCBUS Tx: 0x04X,"
+ " AV_LINK: 0x%04X\n",
+ stat->e_cbus_local_stat,
+ stat->e_cbus_remote_stat,
+ stat->avlink_stat);
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=received_BIST_STATUS "\
+ "e_cbus_rx=0x%04x"
+ "e_cbus_tx=0x%04x"
+ " av_link=0x%04x",
+ stat->e_cbus_local_stat,
+ stat->e_cbus_remote_stat,
+ stat->avlink_stat);
+ kobject_uevent_env(&dev_context->
+ mhl_dev->kobj, KOBJ_CHANGE, envp);
+ } else {
+ MHL_TX_DBG_INFO("\n");
+ }
+ }
+ break;
+ case MHL_TX_EVENT_T_RAP_MAX_EXPIRED:
+ MHL_TX_DBG_INFO("T_RAP_MAX expired\n");
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=T_RAP_MAX_EXPIRED");
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+
+ break;
+ case MHL_EVENT_AUD_DELAY_RCVD:
+ MHL_TX_DBG_INFO("Audio Delay burst received\n");
+ snprintf(event_string, MAX_EVENT_STRING_LEN,
+ "MHLEVENT=AUD_DELAY_RCVD");
+ kobject_uevent_env(&dev_context->mhl_dev->kobj, KOBJ_CHANGE,
+ envp);
+
+ break;
+ default:
+ MHL_TX_DBG_ERR("called with unrecognized event code!\n");
+ break;
+ }
+}
+
+/*
+ * File operations supported by the MHL driver
+ */
+static const struct file_operations mhl_fops = {
+ .owner = THIS_MODULE
+};
+
+/*
+ * Sysfs attribute files supported by this driver.
+ */
+struct device_attribute driver_attribs[] = {
+ __ATTR(SYS_ATTR_NAME_CONN, 0666, show_connection_state,
+ set_connection_state),
+ __ATTR(SYS_ATTR_NAME_DSHPD, 0444, show_ds_hpd_state, NULL),
+ __ATTR(SYS_ATTR_NAME_HDCP2, 0444, show_hdcp2_status, NULL),
+ __ATTR(SYS_ATTR_NAME_SPAD, 0666, show_scratch_pad, send_scratch_pad),
+ __ATTR(SYS_ATTR_NAME_DEBUG_LEVEL, 0666, get_debug_level,
+ set_debug_level),
+ __ATTR(SYS_ATTR_NAME_REG_DEBUG_LEVEL, 0666, get_debug_reg_dump,
+ set_debug_reg_dump),
+ __ATTR(SYS_ATTR_NAME_GPIO_INDEX, 0666, get_gpio_index, set_gpio_index),
+ __ATTR(SYS_ATTR_NAME_GPIO_VALUE, 0666, get_gpio_level, set_gpio_level),
+ __ATTR(SYS_ATTR_NAME_PP_16BPP , 0666, show_pp_16bpp, set_pp_16bpp),
+ __ATTR(SYS_ATTR_NAME_AKSV, 0444, show_aksv, NULL),
+ __ATTR(SYS_ATTR_NAME_EDID, 0444, show_edid, NULL),
+ __ATTR(SYS_ATTR_NAME_HEV_3D_DATA, 0444, show_hev_3d, NULL),
+#ifdef DEBUG
+ __ATTR(SYS_ATTR_NAME_TX_POWER, 0222, NULL, set_tx_power),
+ __ATTR(SYS_ATTR_NAME_STARK_CTL, 0222, NULL, set_stark_ctl),
+#endif
+
+ __ATTR_NULL
+};
+
+ssize_t show_rap_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->rap_in_sub_command);
+ }
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * set_rap_in_status() - Handle write request to the rap attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t set_rap_in_status(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ param = INT_MAX;
+ switch (param) {
+ case 0x00:
+ dev_context->mhl_flags &=
+ ~MHL_STATE_APPLICATION_RAP_BUSY;
+ break;
+ case 0x03:
+ dev_context->mhl_flags |=
+ MHL_STATE_APPLICATION_RAP_BUSY;
+ break;
+ default:
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ break;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rap_out(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->rap_out_sub_command);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * send_rap_out() - Handle write request to the rap attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t send_rap_out(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ param = INT_MAX;
+ switch (param) {
+ case MHL_RAP_POLL:
+ case MHL_RAP_CONTENT_ON:
+ case MHL_RAP_CONTENT_OFF:
+ case MHL_RAP_CBUS_MODE_DOWN:
+ case MHL_RAP_CBUS_MODE_UP:
+ if (!si_mhl_tx_rap_send(dev_context, param)) {
+ MHL_TX_DBG_ERR("-EPERM\n");
+ status = -EPERM;
+ } else {
+ dev_context->rap_out_sub_command = (u8) param;
+ }
+ break;
+ default:
+ dev_context->rap_out_status = MHL_RAPK_UNRECOGNIZED;
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ break;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rap_out_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ /* todo: check for un-ack'ed RAP sub command
+ * and block until ack received.
+ */
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->rap_out_status);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rap_input_dev(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_rap);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_rap_input_dev(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ param = INT_MAX;
+ switch (param) {
+ case 0:
+ case 1:
+ input_dev_rap = (bool)param;
+ break;
+ default:
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ break;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+static struct device_attribute rap_in_attr =
+ __ATTR(SYS_ATTR_NAME_RAP_IN, 0444, show_rap_in, NULL);
+static struct device_attribute rap_in_status_attr =
+ __ATTR(SYS_ATTR_NAME_RAP_IN_STATUS, 0222, NULL, set_rap_in_status);
+static struct device_attribute rap_out_attr =
+ __ATTR(SYS_ATTR_NAME_RAP_OUT, 0666, show_rap_out, send_rap_out);
+static struct device_attribute rap_out_status_attr =
+ __ATTR(SYS_ATTR_NAME_RAP_OUT_STATUS, 0444, show_rap_out_status, NULL);
+static struct device_attribute rap_input_dev =
+ __ATTR(SYS_ATTR_NAME_RAP_INPUT_DEV, 0666, show_rap_input_dev,
+ set_rap_input_dev);
+
+static struct attribute *rap_attrs[] = {
+ &rap_in_attr.attr,
+ &rap_in_status_attr.attr,
+ &rap_out_attr.attr,
+ &rap_out_status_attr.attr,
+ &rap_input_dev.attr,
+ NULL
+};
+
+static struct attribute_group rap_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_RAP),
+ .attrs = rap_attrs
+};
+
+ssize_t show_rcp_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else if (input_dev_rcp) {
+ MHL_TX_DBG_INFO("\n");
+ status = scnprintf(buf, PAGE_SIZE, "rcp_input_dev\n");
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->rcp_in_key_code);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * send_rcp_in_status() - Handle write request to the rcp attribute file.
+ *
+ * Writes to this file cause a RCP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t send_rcp_in_status(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ unsigned long err_code;
+ if (kstrtoul(buf, 0, &err_code) != 0)
+ status = -EINVAL;
+ else {
+ status = count;
+ if (err_code == 0) {
+ if (!si_mhl_tx_rcpk_send(
+ dev_context,
+ dev_context->rcp_in_key_code)) {
+ status = -ENOMEM;
+ }
+ } else if (!si_mhl_tx_rcpe_send(dev_context,
+ (u8)err_code)) {
+ status = -EINVAL;
+ }
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rcp_out(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02X\n",
+ dev_context->rcp_out_key_code);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * send_rcp_out() - Handle write request to the rcp attribute file.
+ *
+ * Writes to this file cause a RCP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t send_rcp_out(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ status = -EINVAL;
+ else {
+ dev_context->mhl_flags &=
+ ~(MHL_STATE_FLAG_RCP_RECEIVED |
+ MHL_STATE_FLAG_RCP_ACK |
+ MHL_STATE_FLAG_RCP_NAK);
+ dev_context->mhl_flags |= MHL_STATE_FLAG_RCP_SENT;
+ dev_context->rcp_send_status = 0;
+ dev_context->rcp_err_code = 0; /*reset error code*/
+ if (!si_mhl_tx_rcp_send(dev_context, (u8) param)) {
+ MHL_TX_DBG_ERR("-EPERM\n");
+ status = -EPERM;
+ } else {
+ dev_context->rcp_out_key_code = param;
+ }
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rcp_out_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ /* todo: check for un-ack'ed RCP sub command
+ * and block until ack received.
+ */
+ if (dev_context->
+ mhl_flags & (MHL_STATE_FLAG_RCP_ACK |
+ MHL_STATE_FLAG_RCP_NAK)) {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02X\n",
+ dev_context->rcp_err_code);
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rcp_input_dev(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_rcp);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_rcp_input_dev(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ param = INT_MAX;
+ switch (param) {
+ case 0:
+ case 1:
+ input_dev_rcp = (bool)param;
+ break;
+ default:
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ break;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+static struct device_attribute rcp_in_attr =
+ __ATTR(SYS_ATTR_NAME_RCP_IN, 0444, show_rcp_in, NULL);
+static struct device_attribute rcp_in_status_attr =
+ __ATTR(SYS_ATTR_NAME_RCP_IN_STATUS, 0222, NULL, send_rcp_in_status);
+static struct device_attribute rcp_out_attr =
+ __ATTR(SYS_ATTR_NAME_RCP_OUT, 0666, show_rcp_out, send_rcp_out);
+static struct device_attribute rcp_out_status_attr =
+ __ATTR(SYS_ATTR_NAME_RCP_OUT_STATUS, 0444, show_rcp_out_status, NULL);
+static struct device_attribute rcp_input_dev =
+ __ATTR(SYS_ATTR_NAME_RCP_INPUT_DEV, 0666, show_rcp_input_dev,
+ set_rcp_input_dev);
+
+static struct attribute *rcp_attrs[] = {
+ &rcp_in_attr.attr,
+ &rcp_in_status_attr.attr,
+ &rcp_out_attr.attr,
+ &rcp_out_status_attr.attr,
+ &rcp_input_dev.attr,
+ NULL
+};
+
+static struct attribute_group rcp_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_RCP),
+ .attrs = rcp_attrs
+};
+#if (INCLUDE_RBP == 1)
+ssize_t show_rbp_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->rbp_in_button_code);
+ }
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * send_rbp_in_status() - Handle write request to the rbp attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t send_rbp_in_status(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else {
+ unsigned long err_code;
+ status = kstrtoul(buf, 0, &err_code);
+ if (status == 0) {
+ status = count;
+ if (err_code == 0) {
+ if (!si_mhl_tx_rbpk_send(
+ dev_context,
+ dev_context->rbp_in_button_code)) {
+ status = -ENOMEM;
+ }
+ } else if (!si_mhl_tx_rbpe_send(
+ dev_context, (u8)err_code)) {
+ status = -EINVAL;
+ }
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * send_rbp_out() - Handle write request to the rbp attribute file.
+ *
+ * Writes to this file cause a RBP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t send_rbp_out(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ status = -EINVAL;
+ else {
+ dev_context->mhl_flags &=
+ ~(MHL_STATE_FLAG_RBP_RECEIVED |
+ MHL_STATE_FLAG_RBP_ACK |
+ MHL_STATE_FLAG_RBP_NAK);
+ dev_context->mhl_flags |= MHL_STATE_FLAG_RBP_SENT;
+ dev_context->rbp_send_status = 0;
+ if (!si_mhl_tx_rbp_send(dev_context, (u8) param)) {
+ MHL_TX_DBG_ERR("-EPERM\n");
+ status = -EPERM;
+ } else {
+ dev_context->rbp_out_button_code = param;
+ }
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rbp_out(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02X\n",
+ dev_context->rbp_out_button_code);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_rbp_out_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ /* todo: check for un-ack'ed RBP sub command
+ * and block until ack received.
+ */
+ if (dev_context->
+ mhl_flags & (MHL_STATE_FLAG_RBP_ACK |
+ MHL_STATE_FLAG_RBP_NAK)) {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02X\n",
+ dev_context->rbp_err_code);
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+
+ssize_t show_rbp_input_dev(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_rbp);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_rbp_input_dev(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ param = INT_MAX;
+ switch (param) {
+ case 0:
+ case 1:
+ input_dev_rbp = (bool)param;
+ break;
+ default:
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ break;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+static struct device_attribute rbp_in_attr =
+ __ATTR(SYS_ATTR_NAME_RBP_IN, 0444, show_rbp_in, NULL);
+static struct device_attribute rbp_in_status_attr =
+ __ATTR(SYS_ATTR_NAME_RBP_IN_STATUS, 0222, NULL, send_rbp_in_status);
+static struct device_attribute rbp_out_attr =
+ __ATTR(SYS_ATTR_NAME_RBP_OUT, 0666, show_rbp_out, send_rbp_out);
+static struct device_attribute rbp_out_status_attr =
+ __ATTR(SYS_ATTR_NAME_RBP_OUT_STATUS, 0444, show_rbp_out_status, NULL);
+static struct device_attribute rbp_input_dev =
+ __ATTR(SYS_ATTR_NAME_RBP_INPUT_DEV, 0666, show_rbp_input_dev,
+ set_rbp_input_dev);
+
+static struct attribute *rbp_attrs[] = {
+ &rbp_in_attr.attr,
+ &rbp_in_status_attr.attr,
+ &rbp_out_attr.attr,
+ &rbp_out_status_attr.attr,
+ &rbp_input_dev.attr,
+ NULL
+};
+
+static struct attribute_group rbp_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_RBP),
+ .attrs = rbp_attrs
+};
+#endif
+ssize_t show_ucp_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("could not acquire mutex!!!\n");
+ return -ERESTARTSYS;
+ }
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->ucp_in_key_code);
+ }
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * send_ucp_in_status() - Handle write request to the ucp attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t send_ucp_in_status(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock))
+ return -ERESTARTSYS;
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ status = -ENODEV;
+ } else if (input_dev_ucp) {
+ MHL_TX_DBG_ERR("channel busy\n");
+ } else {
+ unsigned long err_code;
+ status = kstrtoul(buf, 0, &err_code);
+ if (status == 0) {
+ status = count;
+ if (err_code == 0) {
+ if (!si_mhl_tx_ucpk_send(
+ dev_context,
+ dev_context->ucp_in_key_code)) {
+ status = -ENOMEM;
+ }
+ } else if (!si_mhl_tx_ucpe_send(
+ dev_context, (u8) err_code)) {
+ status = -EINVAL;
+ }
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_ucp_out(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->ucp_out_key_code);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+/*
+ * send_ucp_out() - Handle write request to the ucp attribute file.
+ *
+ * Writes to this file cause a RAP message with the specified action code
+ * to be sent to the downstream device.
+ */
+ssize_t send_ucp_out(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ status = kstrtoul(buf, 0, &param);
+ if (status == 0) {
+ dev_context->mhl_flags &=
+ ~(MHL_STATE_FLAG_UCP_RECEIVED |
+ MHL_STATE_FLAG_UCP_ACK |
+ MHL_STATE_FLAG_UCP_NAK);
+ dev_context->mhl_flags |= MHL_STATE_FLAG_UCP_SENT;
+ if (!si_mhl_tx_ucp_send(dev_context, (u8)param)) {
+ MHL_TX_DBG_ERR("-EPERM\n");
+ status = -EPERM;
+ } else {
+ dev_context->ucp_out_key_code = (u8)param;
+ status = count;
+ }
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_ucp_out_status(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ /* todo: check for un-ack'ed RAP sub command
+ * and block until ack received.
+ */
+ if (dev_context->
+ mhl_flags & (MHL_STATE_FLAG_UCP_ACK |
+ MHL_STATE_FLAG_UCP_NAK)) {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->ucp_err_code);
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_ucp_input_dev(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n", input_dev_ucp);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_ucp_input_dev(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ /* Assume success */
+ status = count;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ if (kstrtoul(buf, 0, &param) != 0)
+ param = INT_MAX;
+ switch (param) {
+ case 0:
+ case 1:
+ input_dev_ucp = (bool)param;
+ break;
+ default:
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ break;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+static struct device_attribute ucp_in_attr =
+ __ATTR(SYS_ATTR_NAME_UCP_IN, 0444, show_ucp_in, NULL);
+static struct device_attribute ucp_in_status_attr =
+ __ATTR(SYS_ATTR_NAME_UCP_IN_STATUS, 0222, NULL, send_ucp_in_status);
+static struct device_attribute ucp_out_attr =
+ __ATTR(SYS_ATTR_NAME_UCP_OUT, 0666, show_ucp_out, send_ucp_out);
+static struct device_attribute ucp_out_status_attr =
+ __ATTR(SYS_ATTR_NAME_UCP_OUT_STATUS, 0444, show_ucp_out_status, NULL);
+
+static struct device_attribute ucp_input_dev =
+ __ATTR(SYS_ATTR_NAME_UCP_INPUT_DEV, 0666, show_ucp_input_dev,
+ set_ucp_input_dev);
+
+static struct attribute *ucp_attrs[] = {
+ &ucp_in_attr.attr,
+ &ucp_in_status_attr.attr,
+ &ucp_out_attr.attr,
+ &ucp_out_status_attr.attr,
+ &ucp_input_dev.attr,
+ NULL
+};
+
+static struct attribute_group ucp_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_UCP),
+ .attrs = ucp_attrs
+};
+
+ssize_t show_devcap_local_offset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->dev_cap_local_offset);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_devcap_local_offset(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ status = kstrtoul(buf, 0, &param);
+ if ((status != 0) || (param >= 16)) {
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ } else {
+ dev_context->dev_cap_local_offset = param;
+ status = count;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+ return status;
+}
+
+ssize_t show_devcap_local(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_cap_values[dev_context->dev_cap_local_offset]);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_devcap_local(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ status = kstrtoul(buf, 0, &param);
+ if ((status != 0) || (param > 0xFF)) {
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ } else {
+ dev_cap_values[dev_context->dev_cap_local_offset] =
+ param;
+ status = count;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_devcap_remote_offset(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status =
+ scnprintf(buf, PAGE_SIZE, "0x%02x\n",
+ dev_context->dev_cap_remote_offset);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t set_devcap_remote_offset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ status = kstrtoul(buf, 0, &param);
+ if ((status == 0) && (param < 16)) {
+ dev_context->dev_cap_remote_offset = (u8)param;
+ status = count;
+ } else {
+ MHL_TX_DBG_ERR("Invalid parameter %s received\n", buf);
+ status = -EINVAL;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_devcap_remote(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ uint8_t regValue;
+ status = si_mhl_tx_get_peer_dev_cap_entry(dev_context,
+ dev_context->
+ dev_cap_remote_offset,
+ &regValue);
+ if (status != 0) {
+ /*
+ * Driver is busy and cannot provide the requested
+ * DEVCAP register value right now so inform caller
+ * they need to try again later.
+ */
+ status = -EAGAIN;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "0x%02x", regValue);
+ }
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+static struct device_attribute attr_devcap_local_offset =
+ __ATTR(SYS_ATTR_NAME_DEVCAP_LOCAL_OFFSET, 0666,
+ show_devcap_local_offset, set_devcap_local_offset);
+static struct device_attribute attr_devcap_local =
+ __ATTR(SYS_ATTR_NAME_DEVCAP_LOCAL, 0666, show_devcap_local,
+ set_devcap_local);
+static struct device_attribute attr_devcap_remote_offset =
+ __ATTR(SYS_ATTR_NAME_DEVCAP_REMOTE_OFFSET, 0666,
+ show_devcap_remote_offset, set_devcap_remote_offset);
+static struct device_attribute attr_devcap_remote =
+ __ATTR(SYS_ATTR_NAME_DEVCAP_REMOTE, 0444, show_devcap_remote, NULL);
+
+static struct attribute *devcap_attrs[] = {
+ &attr_devcap_local_offset.attr,
+ &attr_devcap_local.attr,
+ &attr_devcap_remote_offset.attr,
+ &attr_devcap_remote.attr,
+ NULL
+};
+
+static struct attribute_group devcap_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_DEVCAP),
+ .attrs = devcap_attrs
+};
+
+ssize_t set_hdcp_force_content_type(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ unsigned long new_hdcp_content_type;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = kstrtoul(buf, 0, &new_hdcp_content_type);
+ if (status == 0) {
+ status = count;
+ hdcp_content_type = new_hdcp_content_type;
+ }
+ }
+
+ up(&dev_context->isr_lock);
+ return status;
+}
+
+ssize_t show_hdcp_force_content_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "%d", hdcp_content_type);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+static struct device_attribute attr_hdcp_force_content_type =
+ __ATTR(SYS_ATTR_NAME_HDCP_CONTENT_TYPE, 0666,
+ show_hdcp_force_content_type, set_hdcp_force_content_type);
+
+static struct attribute *hdcp_attrs[] = {
+ &attr_hdcp_force_content_type.attr,
+ NULL
+};
+
+static struct attribute_group hdcp_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_HDCP),
+ .attrs = hdcp_attrs
+};
+
+ssize_t set_vc_assign(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status;
+ struct tdm_alloc_burst tdm_burst;
+ uint8_t slots;
+
+ MHL_TX_DBG_INFO("received string: %s\n", buf);
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ unsigned long param;
+ enum cbus_mode_e cbus_mode;
+ status = kstrtoul(buf, 0, &param);
+ if (status != 0)
+ goto input_err;
+ status = count;
+
+ cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context);
+ switch (cbus_mode) {
+ case CM_eCBUS_S:
+ if (param > 24)
+ goto input_err;
+ break;
+ case CM_eCBUS_D:
+ if (param > 200)
+ goto input_err;
+ break;
+ default:
+input_err:
+ MHL_TX_DBG_ERR(
+ "Invalid number of eMSC slots specified\n");
+ status = -EINVAL;
+ goto done;
+ }
+
+ slots = (u8)param;
+
+ memset(&tdm_burst, 0, sizeof(tdm_burst));
+ tdm_burst.header.burst_id.high = burst_id_VC_ASSIGN >> 8;
+ tdm_burst.header.burst_id.low = (uint8_t) burst_id_VC_ASSIGN;
+ tdm_burst.header.sequence_index = 1;
+ tdm_burst.header.total_entries = 2;
+ tdm_burst.num_entries_this_burst = 2;
+ tdm_burst.vc_info[0].vc_num = TDM_VC_E_MSC;
+ tdm_burst.vc_info[0].feature_id = FEATURE_ID_E_MSC;
+ tdm_burst.vc_info[0].req_resp.channel_size = slots;
+ tdm_burst.vc_info[1].vc_num = TDM_VC_T_CBUS;
+ tdm_burst.vc_info[1].feature_id = FEATURE_ID_USB;
+ tdm_burst.vc_info[1].req_resp.channel_size = (u8) (24 - slots);
+ tdm_burst.vc_info[2].vc_num = 0;
+ tdm_burst.vc_info[2].feature_id = 0;
+ tdm_burst.vc_info[2].req_resp.channel_size = 0;
+ tdm_burst.header.checksum = 0;
+ tdm_burst.header.checksum =
+ calculate_generic_checksum((uint8_t *) (&tdm_burst), 0,
+ sizeof(tdm_burst));
+
+ dev_context->prev_virt_chan_slot_counts[TDM_VC_E_MSC] =
+ dev_context->virt_chan_slot_counts[TDM_VC_E_MSC];
+ dev_context->prev_virt_chan_slot_counts[TDM_VC_T_CBUS] =
+ dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS];
+
+ dev_context->virt_chan_slot_counts[TDM_VC_E_MSC] = slots;
+ dev_context->virt_chan_slot_counts[TDM_VC_T_CBUS] =
+ (u8) (24 - slots);
+
+ si_mhl_tx_request_write_burst(dev_context, 0, sizeof(tdm_burst),
+ (uint8_t *) &tdm_burst);
+ status = count;
+ }
+done:
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+ssize_t show_vc_assign(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mhl_dev_context *dev_context = dev_get_drvdata(dev);
+ int status = -EINVAL;
+
+ if (down_interruptible(&dev_context->isr_lock)) {
+ MHL_TX_DBG_ERR("-ERESTARTSYS\n");
+ return -ERESTARTSYS;
+ }
+
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN) {
+ MHL_TX_DBG_ERR("-ENODEV\n");
+ status = -ENODEV;
+ } else {
+ status = scnprintf(buf, PAGE_SIZE, "%d", 0);
+ }
+
+ up(&dev_context->isr_lock);
+
+ return status;
+}
+
+static struct device_attribute attr_vc_assign =
+ __ATTR(SYS_ATTR_NAME_VC_ASSIGN, 0666, show_vc_assign, set_vc_assign);
+
+static struct attribute *vc_attrs[] = {
+ &attr_vc_assign.attr,
+ NULL
+};
+
+static struct attribute_group vc_attribute_group = {
+ .name = __stringify(SYS_OBJECT_NAME_VC),
+ .attrs = vc_attrs
+};
+
+/*
+process_si_adopter_id
+*/
+
+void process_si_adopter_id(struct mhl_dev_context *dev_context,
+ struct si_adopter_id_data *p_burst)
+{
+ uint8_t opcode = p_burst->hdr.op_code;
+ uint8_t checksum;
+ size_t total_size;
+ total_size = SII_OFFSETOF(struct si_adopter_id_data, hdr.checksum)
+ + p_burst->hdr.remaining_length;
+ checksum = calculate_generic_checksum(p_burst, 0, total_size);
+ MHL_TX_DBG_INFO("total_size: 0x%02X"
+ " id: %02X%02X"
+ " rem_len: 0x%02X"
+ " cksum: 0x%02X"
+ " opcode: 0x%02X\n",
+ total_size,
+ p_burst->hdr.burst_id.high,
+ p_burst->hdr.burst_id.low,
+ p_burst->hdr.remaining_length,
+ p_burst->hdr.checksum,
+ p_burst->hdr.op_code)
+ if (checksum) {
+ MHL_TX_DBG_ERR("%schecksum error 0x%02X %s\n",
+ ANSI_ESC_RED_TEXT,
+ checksum,
+ ANSI_ESC_RESET_TEXT)
+ } else
+ switch (opcode) {
+ case EDID_BLOCK: {
+ struct edid_3d_data_t *edid_context;
+ edid_context = dev_context->edid_parser_context;
+ MHL_TX_DBG_INFO("EDID_BLOCK "
+ "blk_num: %02X\n",
+ p_burst->opcode_data.edid_blk.block_num)
+ process_emsc_edid_sub_payload(edid_context, p_burst);
+ }
+ break;
+ case EDID_STOP:
+ MHL_TX_DBG_ERR("EDID_STOP: %d\n", total_size)
+ break;
+ }
+
+}
+
+/*
+ * Called from the Titan interrupt handler to parse data
+ * received from the eSMC BLOCK hardware. Process all waiting input
+ * buffers. If multiple messages are sent in the same BLOCK Command buffer,
+ * they are sorted out here also.
+ */
+static void si_mhl_tx_emsc_received(struct mhl_dev_context *context)
+{
+ struct drv_hw_context *hw_context =
+ (struct drv_hw_context *)(&context->drv_context);
+ int length, index, pass;
+ uint16_t burst_id;
+ uint8_t *prbuf, *pmsg;
+
+ while (si_mhl_tx_drv_peek_block_input_buffer(context,
+ &prbuf, &length) == 0) {
+ index = 0;
+ pass = 0;
+/* dump_array(DBG_MSG_LEVEL_INFO, "si_mhl_tx_emsc_received",
+ prbuf, length); */
+ do {
+ struct MHL_burst_id_t *p_burst_id;
+ pmsg = &prbuf[index];
+ p_burst_id = (struct MHL_burst_id_t *)pmsg;
+ burst_id = (((uint16_t)pmsg[0]) << 8) | pmsg[1];
+ /* Move past the BURST ID */
+ index += sizeof(*p_burst_id);
+
+ switch (burst_id) {
+ case burst_id_HID_PAYLOAD:
+#if (INCLUDE_HID == 1)
+ build_received_hid_message(context,
+ &pmsg[3], pmsg[2]);
+#endif
+ index += (1 + pmsg[2]);
+ break;
+
+ case burst_id_BLK_RCV_BUFFER_INFO:
+ hw_context->block_protocol.peer_blk_rx_buf_max
+ = pmsg[3];
+ hw_context->block_protocol.peer_blk_rx_buf_max
+ <<= 8;
+ hw_context->block_protocol.peer_blk_rx_buf_max
+ |= pmsg[2];
+
+ hw_context->block_protocol.
+ peer_blk_rx_buf_avail =
+ hw_context->block_protocol.
+ peer_blk_rx_buf_max -
+ (EMSC_RCV_BUFFER_DEFAULT -
+ hw_context->block_protocol.
+ peer_blk_rx_buf_avail);
+ MHL_TX_DBG_INFO(
+ "Total PEER Buffer Size: %d\n",
+ hw_context->block_protocol.
+ peer_blk_rx_buf_max);
+
+ index += 2;
+ break;
+
+ case burst_id_BITS_PER_PIXEL_FMT:
+ index += (4 + (2 * pmsg[5]));
+ /*
+ * Inform the rest of the code of the
+ * acknowledgment
+ */
+ break;
+ /*
+ * Any BURST_ID reported by EMSC_SUPPORT can be
+ * sent using eMSC BLOCK
+ */
+ case burst_id_HEV_DTDA:{
+ struct MHL3_hev_dtd_a_data_t *p_dtda =
+ (struct MHL3_hev_dtd_a_data_t *)
+ p_burst_id;
+ index += sizeof(*p_dtda) - sizeof(*p_burst_id);
+ }
+ break;
+ case burst_id_HEV_DTDB:{
+ struct MHL3_hev_dtd_b_data_t *p_dtdb =
+ (struct MHL3_hev_dtd_b_data_t *) p_burst_id;
+ index +=
+ sizeof(*p_dtdb) -
+ sizeof(*p_burst_id);
+ }
+ /* for now, as a robustness excercise, adjust
+ * index according to the this burst field
+ */
+ break;
+ case burst_id_HEV_VIC:{
+ struct MHL3_hev_vic_data_t *p_burst;
+ p_burst = (struct MHL3_hev_vic_data_t *)
+ p_burst_id;
+ index += sizeof(*p_burst) -
+ sizeof(*p_burst_id) +
+ sizeof(p_burst->video_descriptors[0]) *
+ p_burst->num_entries_this_burst;
+ }
+ break;
+ case burst_id_3D_VIC:{
+ struct MHL3_hev_vic_data_t *p_burst =
+ (struct MHL3_hev_vic_data_t *)
+ p_burst_id;
+ index += sizeof(*p_burst) -
+ sizeof(*p_burst_id) -
+ sizeof(p_burst->video_descriptors) +
+ sizeof(p_burst->video_descriptors[0]) *
+ p_burst->num_entries_this_burst;
+ }
+ break;
+ case burst_id_3D_DTD:{
+ struct MHL2_video_format_data_t *p_burst =
+ (struct MHL2_video_format_data_t *)
+ p_burst_id;
+ index += sizeof(*p_burst) -
+ sizeof(*p_burst_id) -
+ sizeof(p_burst->video_descriptors) +
+ sizeof(p_burst->video_descriptors[0]) *
+ p_burst->num_entries_this_burst;
+ }
+ break;
+ case burst_id_VC_ASSIGN:{
+ struct SI_PACK_THIS_STRUCT tdm_alloc_burst
+ *p_burst;
+ p_burst = (struct SI_PACK_THIS_STRUCT
+ tdm_alloc_burst*)p_burst_id;
+ index += sizeof(*p_burst) -
+ sizeof(*p_burst_id) -
+ sizeof(p_burst->reserved) -
+ sizeof(p_burst->vc_info) +
+ sizeof(p_burst->vc_info[0]) *
+ p_burst->num_entries_this_burst;
+ }
+ break;
+ case burst_id_VC_CONFIRM:{
+ struct SI_PACK_THIS_STRUCT tdm_alloc_burst
+ *p_burst;
+ p_burst = (struct SI_PACK_THIS_STRUCT
+ tdm_alloc_burst*)p_burst_id;
+ index += sizeof(*p_burst) -
+ sizeof(*p_burst_id) -
+ sizeof(p_burst->reserved) -
+ sizeof(p_burst->vc_info) +
+ sizeof(p_burst->vc_info[0]) *
+ p_burst->num_entries_this_burst;
+ }
+ break;
+ case burst_id_AUD_DELAY:{
+ struct MHL3_audio_delay_burst_t *p_burst =
+ (struct MHL3_audio_delay_burst_t *)
+ p_burst_id;
+ index += sizeof(*p_burst) -
+ sizeof(*p_burst_id) -
+ sizeof(p_burst->reserved);
+ }
+ break;
+ case burst_id_ADT_BURSTID:{
+ struct MHL3_adt_data_t *p_burst =
+ (struct MHL3_adt_data_t *)p_burst_id;
+ index += sizeof(*p_burst) - sizeof(*p_burst_id);
+ }
+ break;
+ case burst_id_BIST_SETUP:{
+ struct SI_PACK_THIS_STRUCT
+ bist_setup_burst * p_bist_setup;
+ p_bist_setup = (struct SI_PACK_THIS_STRUCT
+ bist_setup_burst*)p_burst_id;
+ index += sizeof(*p_bist_setup) -
+ sizeof(*p_burst_id);
+ }
+ break;
+ case burst_id_BIST_RETURN_STAT:{
+ struct SI_PACK_THIS_STRUCT
+ bist_return_stat_burst
+ *p_bist_return;
+ p_bist_return = (struct SI_PACK_THIS_STRUCT
+ bist_return_stat_burst*)p_burst_id;
+ index += sizeof(*p_bist_return) -
+ sizeof(*p_burst_id);
+ }
+ break;
+ case burst_id_EMSC_SUPPORT:{
+ /*
+ * For robustness, adjust index
+ * according to the num-entries this
+ * burst field
+ */
+ struct MHL3_emsc_support_data_t *p_burst =
+ (struct MHL3_emsc_support_data_t *)
+ p_burst_id;
+ int i;
+ index += sizeof(*p_burst) -
+ sizeof(*p_burst_id) -
+ sizeof(p_burst->payload) +
+ sizeof(p_burst->payload.burst_ids[0]) *
+ p_burst->num_entries_this_burst;
+ for (i = 0;
+ i < p_burst->num_entries_this_burst;
+ ++i) {
+ enum BurstId_e burst_id =
+ BURST_ID(p_burst->payload.burst_ids[i]);
+ MHL_TX_DBG_INFO("BURST_ID: %04X\n",
+ burst_id)
+ switch (burst_id) {
+ case SILICON_IMAGE_ADOPTER_ID:
+ context->sii_adopter_id = true;
+ break;
+ case burst_id_HID_PAYLOAD:
+ break;
+ default:
+ MHL_TX_DBG_ERR("%sunexpected"
+ " burst/adopter id:%04X%s\n",
+ ANSI_ESC_RED_TEXT,
+ burst_id,
+ ANSI_ESC_RESET_TEXT)
+ }
+ }
+ }
+ break;
+ case SILICON_IMAGE_ADOPTER_ID:
+ {
+ struct si_adopter_id_data *p_burst =
+ (struct si_adopter_id_data *)
+ p_burst_id;
+
+ index +=
+ sizeof(p_burst->hdr.remaining_length)
+ + p_burst->hdr.remaining_length;
+ process_si_adopter_id(context, p_burst);
+
+ }
+ break;
+ default:
+ if ((MHL_TEST_ADOPTER_ID == burst_id) ||
+ (burst_id >= adopter_id_RANGE_START)) {
+ struct EMSC_BLK_ADOPT_ID_PAYLD_HDR*
+ p_adopter_id = (struct
+ EMSC_BLK_ADOPT_ID_PAYLD_HDR*)
+ pmsg;
+ MHL_TX_DBG_ERR(
+ "Bad ADOPTER_ID: %04X\n",
+ burst_id);
+ index += p_adopter_id->remaining_length;
+ } else {
+ MHL_TX_DBG_ERR(
+ "Bad BURST ID: %04X\n",
+ burst_id);
+ index = length;
+ }
+ break;
+ }
+
+ length -= index;
+ } while (length > 0);
+ si_mhl_tx_drv_free_block_input_buffer(context);
+ }
+}
+
+static void check_drv_intr_flags(struct mhl_dev_context *dev_context)
+{
+ enum cbus_mode_e cbus_mode;
+ cbus_mode = si_mhl_tx_drv_get_cbus_mode(dev_context);
+ if (dev_context->intr_info.flags & DRV_INTR_CBUS_ABORT)
+ process_cbus_abort(dev_context);
+
+ if (dev_context->intr_info.flags & DRV_INTR_WRITE_BURST)
+ si_mhl_tx_process_write_burst_data(dev_context);
+
+ /* sometimes SET_INT and WRITE_STAT come at the
+ * same time. Always process WRITE_STAT first.
+ */
+ if (dev_context->intr_info.flags & DRV_INTR_WRITE_STAT)
+ si_mhl_tx_got_mhl_status(dev_context,
+ &dev_context->intr_info.dev_status);
+
+ if (dev_context->intr_info.flags & DRV_INTR_SET_INT)
+ si_mhl_tx_got_mhl_intr(dev_context,
+ dev_context->intr_info.int_msg[0],
+ dev_context->intr_info.int_msg[1]);
+
+ if (dev_context->intr_info.flags & DRV_INTR_MSC_DONE)
+ si_mhl_tx_msc_command_done(dev_context,
+ dev_context->intr_info.msc_done_data);
+
+ if (dev_context->intr_info.flags & DRV_INTR_EMSC_INCOMING)
+ si_mhl_tx_emsc_received(dev_context);
+
+ if (dev_context->intr_info.flags & DRV_INTR_HPD_CHANGE)
+ si_mhl_tx_notify_downstream_hpd_change
+ (dev_context, dev_context->intr_info.hpd_status);
+
+ if (dev_context->intr_info.flags & DRV_INTR_MSC_RECVD) {
+ dev_context->msc_msg_arrived = true;
+ dev_context->msc_msg_sub_command =
+ dev_context->intr_info.msc_msg[0];
+ dev_context->msc_msg_data =
+ dev_context->intr_info.msc_msg[1];
+ si_mhl_tx_process_events(dev_context);
+ }
+
+ if (DRV_INTR_COC_CAL & dev_context->intr_info.flags) {
+ bool do_test_now = false;
+ switch (cbus_mode) {
+ case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST:
+ if (0 == dev_context->bist_setup.avlink_duration) {
+ MHL_TX_DBG_ERR("infinite av_link duration\n")
+ do_test_now = false;
+ } else if (BIST_TRIGGER_ECBUS_AV_LINK_MASK &
+ dev_context->bist_trigger_info) {
+ MHL_TX_DBG_ERR("%sAV_LINK BIST start%s\n",
+ ANSI_ESC_GREEN_TEXT,
+ ANSI_ESC_RESET_TEXT)
+ do_test_now = true;
+ }
+
+ if (BIST_TRIGGER_ECBUS_TX_RX_MASK &
+ dev_context->bist_trigger_info) {
+ MHL_TX_DBG_ERR("%seCBUS BIST start%s\n",
+ ANSI_ESC_GREEN_TEXT,
+ ANSI_ESC_RESET_TEXT)
+ do_test_now = true;
+ }
+ if (!do_test_now)
+ ;
+ else if (dev_context->misc_flags.flags.bist_role_TE)
+ start_bist_initiator_test(dev_context);
+ else
+ initiate_bist_test(dev_context);
+ break;
+ default:
+ ;
+ }
+ }
+ if (DRV_INTR_TDM_SYNC & dev_context->intr_info.flags) {
+ switch (cbus_mode) {
+ case CM_eCBUS_S_BIST:
+ case CM_eCBUS_D_BIST:
+ break;
+ case CM_eCBUS_S_AV_BIST:
+ case CM_eCBUS_D_AV_BIST:
+ if (dev_context->bist_setup.avlink_duration) {
+ MHL_TX_DBG_ERR("finite av_link duration\n")
+ } else if (BIST_TRIGGER_ECBUS_AV_LINK_MASK &
+ dev_context->bist_trigger_info) {
+ if (dev_context->
+ misc_flags.flags.bist_role_TE) {
+ MHL_TX_DBG_ERR("\n")
+ start_bist_initiator_test(dev_context);
+ } else {
+ MHL_TX_DBG_ERR("\n")
+ initiate_bist_test(dev_context);
+ }
+ } else {
+ MHL_TX_DBG_ERR("infinite av_link duration\n")
+ }
+ break;
+ case CM_eCBUS_S:
+ case CM_eCBUS_D:
+ /*
+ MHL3 spec requires the process of reading
+ the remainder of XDEVCAP and all of DEVCAP
+ to be deferred until after the first
+ transition to eCBUS mode.
+ Check DCAP_RDY, initiate XDEVCAP read here.
+ */
+ if (MHL_STATUS_DCAP_RDY & dev_context->status_0)
+ si_mhl_tx_ecbus_started(dev_context);
+ if (dev_context->
+ misc_flags.flags.have_complete_devcap) {
+ if (dev_context->misc_flags.flags.mhl_hpd &&
+ !dev_context->edid_valid) {
+ si_mhl_tx_initiate_edid_sequence(
+ dev_context->edid_parser_context);
+ }
+ }
+ break;
+ default:
+ MHL_TX_DBG_ERR("%sunexpected CBUS mode%s:%s\n",
+ ANSI_ESC_RED_TEXT,
+ si_mhl_tx_drv_get_cbus_mode_str(cbus_mode),
+ ANSI_ESC_RESET_TEXT)
+
+ }
+ }
+}
+/*
+ * Interrupt handler for MHL transmitter interrupts.
+ *
+ * @irq: The number of the asserted IRQ line that caused
+ * this handler to be called.
+ * @data: Data pointer passed when the interrupt was enabled,
+ * which in this case is a pointer to an mhl_dev_context struct.
+ *
+ * Always returns IRQ_HANDLED.
+ */
+static irqreturn_t mhl_irq_handler(int irq, void *data)
+{
+ struct mhl_dev_context *dev_context = (struct mhl_dev_context *)data;
+
+ MHL_TX_DBG_INFO("called\n");
+
+ if (!down_interruptible(&dev_context->isr_lock)) {
+ int loop_limit = 5;
+ if (dev_context->dev_flags & DEV_FLAG_SHUTDOWN)
+ goto irq_done;
+ if (dev_context->dev_flags & DEV_FLAG_COMM_MODE)
+ goto irq_done;
+
+ do {
+ memset(&dev_context->intr_info, 0,
+ sizeof(*(&dev_context->intr_info)));
+
+ dev_context->intr_info.edid_parser_context =
+ dev_context->edid_parser_context;
+
+ dev_context->drv_info->
+ mhl_device_isr((struct drv_hw_context *)
+ (&dev_context->drv_context),
+ &dev_context->intr_info);
+
+ /* post process events raised by interrupt handler */
+ if (dev_context->intr_info.flags &
+ DRV_INTR_DISCONNECT) {
+ dev_context->misc_flags.flags.rap_content_on =
+ false;
+ dev_context->misc_flags.flags.mhl_rsen = false;
+ dev_context->mhl_connection_event = true;
+ dev_context->mhl_connected =
+ MHL_TX_EVENT_DISCONNECTION;
+ si_mhl_tx_process_events(dev_context);
+ } else {
+ if (dev_context->intr_info.flags &
+ DRV_INTR_CONNECT) {
+ dev_context->misc_flags.flags.
+ rap_content_on = true;
+ dev_context->rap_in_sub_command =
+ MHL_RAP_CONTENT_ON;
+ dev_context->misc_flags.flags.mhl_rsen =
+ true;
+ dev_context->mhl_connection_event =
+ true;
+ dev_context->mhl_connected =
+ MHL_TX_EVENT_CONNECTION;
+ si_mhl_tx_process_events(dev_context);
+ }
+
+
+ check_drv_intr_flags(dev_context);
+ }
+ /*
+ * Send any messages that may have been queued up
+ * as the result of interrupt processing.
+ */
+ si_mhl_tx_drive_states(dev_context);
+ if (si_mhl_tx_drv_get_cbus_mode(dev_context) >=
+ CM_eCBUS_S) {
+ si_mhl_tx_push_block_transactions(dev_context);
+ }
+ } while ((--loop_limit > 0) && is_interrupt_asserted());
+irq_done:
+ up(&dev_context->isr_lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* APIs provided by the MHL layer to the lower level driver */
+
+int mhl_handle_power_change_request(struct device *parent_dev, bool power_up)
+{
+ struct mhl_dev_context *dev_context;
+ int status;
+
+ dev_context = dev_get_drvdata(parent_dev);
+
+ MHL_TX_DBG_INFO("\n");
+
+ /* Power down the MHL transmitter hardware. */
+ status = down_interruptible(&dev_context->isr_lock);
+ if (status) {
+ MHL_TX_DBG_ERR("failed to acquire ISR semaphore,"
+ "status: %d\n", status);
+ goto done;
+ }
+
+ if (power_up)
+ status = si_mhl_tx_initialize(dev_context);
+ else
+ status = si_mhl_tx_shutdown(dev_context);
+
+ up(&dev_context->isr_lock);
+done:
+ return status;
+
+}
+
+int mhl_tx_init(struct mhl_drv_info const *drv_info, struct device *parent_dev)
+{
+ struct mhl_dev_context *dev_context;
+ int status = 0;
+ int ret;
+
+ if (drv_info == NULL || parent_dev == NULL) {
+ pr_err("Null parameter passed to %s\n", __func__);
+ return -EINVAL;
+ }
+
+ if (drv_info->mhl_device_isr == NULL || drv_info->irq == 0) {
+ dev_err(parent_dev, "No IRQ specified!\n");
+ return -EINVAL;
+ }
+
+ dev_context = kzalloc(sizeof(*dev_context) + drv_info->drv_context_size,
+ GFP_KERNEL);
+ if (!dev_context) {
+ dev_err(parent_dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ dev_context->signature = MHL_DEV_CONTEXT_SIGNATURE;
+ dev_context->drv_info = drv_info;
+
+ sema_init(&dev_context->isr_lock, 1);
+ INIT_LIST_HEAD(&dev_context->timer_list);
+ dev_context->timer_work_queue = create_workqueue(MHL_DRIVER_NAME);
+ if (dev_context->timer_work_queue == NULL) {
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+
+#if (INCLUDE_HID == 1)
+ /*
+ * Create a single-threaded work queue so that no two queued
+ * items can run at the same time.
+ */
+ dev_context->hid_work_queue = create_singlethread_workqueue(
+ "MHL3_HID_WORK");
+ if (dev_context->hid_work_queue == NULL) {
+ ret = -ENOMEM;
+ goto timer_cleanup;
+ }
+#endif
+
+ if (mhl_class == NULL) {
+ mhl_class = class_create(THIS_MODULE, "mhl");
+ if (IS_ERR(mhl_class)) {
+ ret = PTR_ERR(mhl_class);
+ pr_info("class_create failed %d\n", ret);
+ goto hid_cleanup;
+ }
+
+#if (LINUX_KERNEL_VER < 315)
+ mhl_class->dev_attrs = driver_attribs;
+#endif
+
+ ret = alloc_chrdev_region(&dev_num,
+ 0, MHL_DRIVER_MINOR_MAX, MHL_DRIVER_NAME);
+
+ if (ret) {
+ pr_info("register_chrdev %s failed, error code: %d\n",
+ MHL_DRIVER_NAME, ret);
+ goto free_class;
+ }
+
+ cdev_init(&dev_context->mhl_cdev, &mhl_fops);
+ dev_context->mhl_cdev.owner = THIS_MODULE;
+ ret =
+ cdev_add(&dev_context->mhl_cdev, MINOR(dev_num),
+ MHL_DRIVER_MINOR_MAX);
+ if (ret) {
+ pr_info("cdev_add %s failed %d\n", MHL_DRIVER_NAME,
+ ret);
+ goto free_chrdev;
+ }
+ }
+
+ dev_context->mhl_dev = device_create(mhl_class, parent_dev,
+ dev_num, dev_context, "%s", MHL_DEVICE_NAME);
+ if (IS_ERR(dev_context->mhl_dev)) {
+ ret = PTR_ERR(dev_context->mhl_dev);
+ pr_info("device_create failed %s %d\n", MHL_DEVICE_NAME, ret);
+ goto free_cdev;
+ }
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &reg_access_attribute_group);
+
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &bist_attribute_group);
+ if (status) {
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n",
+ status);
+ }
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &rap_attribute_group);
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+#if (INCLUDE_RBP == 1)
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &rbp_attribute_group);
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+#endif
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &rcp_attribute_group);
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &ucp_attribute_group);
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &devcap_attribute_group);
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &hdcp_attribute_group);
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+ status = sysfs_create_group(&dev_context->mhl_dev->kobj,
+ &vc_attribute_group);
+ if (status)
+ MHL_TX_DBG_ERR("sysfs_create_group failed:%d\n", status);
+
+ ret = request_threaded_irq(drv_info->irq, NULL,
+ mhl_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ MHL_DEVICE_NAME, dev_context);
+ if (ret < 0) {
+ dev_err(parent_dev,
+ "request_threaded_irq failed, status: %d\n", ret);
+ goto free_device;
+ }
+
+ /* Initialize the MHL transmitter hardware. */
+ ret = down_interruptible(&dev_context->isr_lock);
+ if (ret) {
+ dev_err(parent_dev,
+ "Failed to acquire ISR semaphore, status: %d\n", ret);
+ goto free_irq_handler;
+ }
+
+ /* Initialize EDID parser module */
+ dev_context->edid_parser_context = si_edid_create_context(dev_context,
+ (struct drv_hw_context *)&dev_context->drv_context);
+ rcp_input_dev_one_time_init(dev_context);
+
+ ret = si_mhl_tx_reserve_resources(dev_context);
+ if (ret)
+ dev_err(parent_dev,
+ "failed to reserve resources\n");
+ else
+ ret = si_mhl_tx_initialize(dev_context);
+
+ up(&dev_context->isr_lock);
+
+ if (ret)
+ goto free_edid_context;
+
+#if (INCLUDE_SII6031 == 1)
+ mhl_tx_register_mhl_discovery(dev_context);
+#endif
+ MHL_TX_DBG_INFO("MHL transmitter successfully initialized\n");
+ dev_set_drvdata(parent_dev, dev_context);
+
+ return ret;
+
+free_edid_context:
+ MHL_TX_DBG_INFO("%sMHL transmitter initialization failed%s\n",
+ ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT);
+ status = down_interruptible(&dev_context->isr_lock);
+ if (status) {
+ dev_err(parent_dev,
+ "Failed to acquire ISR semaphore, "
+ "could not destroy EDID context. Status: %d\n",
+ status);
+ goto free_irq_handler;
+ }
+ if (dev_context->edid_parser_context)
+ si_edid_destroy_context(dev_context->edid_parser_context);
+ up(&dev_context->isr_lock);
+
+free_irq_handler:
+ if (dev_context->client)
+ free_irq(dev_context->client->irq, dev_context);
+
+free_device:
+ device_destroy(mhl_class, dev_num);
+
+free_cdev:
+ cdev_del(&dev_context->mhl_cdev);
+
+free_chrdev:
+ unregister_chrdev_region(dev_num, MHL_DRIVER_MINOR_MAX);
+ dev_num = 0;
+
+free_class:
+ class_destroy(mhl_class);
+
+hid_cleanup:
+#if (INCLUDE_HID == 1)
+ destroy_workqueue(dev_context->hid_work_queue);
+timer_cleanup:
+#endif
+ destroy_workqueue(dev_context->timer_work_queue);
+
+free_mem:
+ kfree(dev_context);
+
+ return ret;
+}
+
+int mhl_tx_remove(struct device *parent_dev)
+{
+ struct mhl_dev_context *dev_context;
+ int ret = 0;
+
+ dev_context = dev_get_drvdata(parent_dev);
+
+ if (dev_context != NULL) {
+ MHL_TX_DBG_INFO("%x\n", dev_context);
+
+ dev_context->dev_flags |= DEV_FLAG_SHUTDOWN;
+ ret = down_interruptible(&dev_context->isr_lock);
+ free_irq(dev_context->drv_info->irq, dev_context);
+ up(&dev_context->isr_lock);
+
+#if (INCLUDE_HID == 1)
+ mhl3_hid_remove_all(dev_context);
+ destroy_workqueue(dev_context->hid_work_queue);
+ dev_context->hid_work_queue = NULL;
+#endif
+ ret = si_mhl_tx_shutdown(dev_context);
+
+ mhl_tx_destroy_timer_support(dev_context);
+
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &reg_access_attribute_group);
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &rap_attribute_group);
+#if (INCLUDE_RBP == 1)
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &rbp_attribute_group);
+#endif
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &rcp_attribute_group);
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &ucp_attribute_group);
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &devcap_attribute_group);
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &hdcp_attribute_group);
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &vc_attribute_group);
+ sysfs_remove_group(&dev_context->mhl_dev->kobj,
+ &bist_attribute_group);
+
+ device_destroy(mhl_class, dev_num);
+ cdev_del(&dev_context->mhl_cdev);
+ unregister_chrdev_region(dev_num, MHL_DRIVER_MINOR_MAX);
+ dev_num = 0;
+ class_destroy(mhl_class);
+ mhl_class = NULL;
+
+#ifdef MEDIA_DATA_TUNNEL_SUPPORT
+ mdt_destroy(dev_context);
+#endif
+ destroy_rcp_input_dev(dev_context);
+ si_edid_destroy_context(dev_context->edid_parser_context);
+#if (INCLUDE_SII6031 == 1)
+ otg_unregister_mhl_discovery();
+#endif
+ kfree(dev_context);
+ }
+ return ret;
+}
+void mhl_tx_stop_all_timers(struct mhl_dev_context *dev_context)
+{
+ struct timer_obj *timer = NULL;
+
+ list_for_each_entry(timer, &dev_context->timer_list, list_link) {
+ mhl_tx_stop_timer(dev_context, timer);
+ }
+}
+static void mhl_tx_destroy_timer_support(struct mhl_dev_context *dev_context)
+{
+ struct timer_obj *mhl_timer;
+
+ /*
+ * Make sure all outstanding timer objects are canceled and the
+ * memory allocated for them is freed.
+ */
+ while (!list_empty(&dev_context->timer_list)) {
+ mhl_timer = list_first_entry(&dev_context->timer_list,
+ struct timer_obj, list_link);
+ hrtimer_cancel(&mhl_timer->hr_timer);
+ list_del(&mhl_timer->list_link);
+ kfree(mhl_timer);
+ }
+
+ destroy_workqueue(dev_context->timer_work_queue);
+ dev_context->timer_work_queue = NULL;
+}
+
+static void mhl_tx_timer_work_handler(struct work_struct *work)
+{
+ struct timer_obj *mhl_timer;
+
+ mhl_timer = container_of(work, struct timer_obj, work_item);
+
+ mhl_timer->flags |= TIMER_OBJ_FLAG_WORK_IP;
+ if (!down_interruptible(&mhl_timer->dev_context->isr_lock)) {
+
+ mhl_timer->timer_callback_handler(mhl_timer->callback_param);
+
+ up(&mhl_timer->dev_context->isr_lock);
+ }
+ mhl_timer->flags &= ~TIMER_OBJ_FLAG_WORK_IP;
+
+ if (mhl_timer->flags & TIMER_OBJ_FLAG_DEL_REQ) {
+ /*
+ * Deletion of this timer was requested during the execution of
+ * the callback handler so go ahead and delete it now.
+ */
+ kfree(mhl_timer);
+ }
+}
+
+static enum hrtimer_restart mhl_tx_timer_handler(struct hrtimer *timer)
+{
+ struct timer_obj *mhl_timer;
+
+ mhl_timer = container_of(timer, struct timer_obj, hr_timer);
+
+ queue_work(mhl_timer->dev_context->timer_work_queue,
+ &mhl_timer->work_item);
+
+ return HRTIMER_NORESTART;
+}
+
+static int is_timer_handle_valid(struct mhl_dev_context *dev_context,
+ void *timer_handle)
+{
+ struct timer_obj *timer = timer_handle; /* Set to avoid lint warning. */
+
+ list_for_each_entry(timer, &dev_context->timer_list, list_link) {
+ if (timer == timer_handle)
+ break;
+ }
+
+ if (timer != timer_handle) {
+ MHL_TX_DBG_WARN("Invalid timer handle %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 <linux/input.h>
+#include <linux/cdev.h>
+#include <linux/hrtimer.h>
+#include "si_fw_macros.h"
+#include "si_infoframe.h"
+#include "si_edid.h"
+#include "si_mhl_defs.h"
+#include "si_mhl2_edid_3d_api.h"
+#include "si_mhl_tx_hw_drv_api.h"
+#ifdef MEDIA_DATA_TUNNEL_SUPPORT
+#include "si_mdt_inputdev.h"
+#endif
+#include "mhl_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 <linux/input.h>
+#include <linux/cdev.h>
+#include <linux/hrtimer.h>
+#include "si_fw_macros.h"
+#include "si_infoframe.h"
+#include "si_edid.h"
+#include "si_mhl_defs.h"
+#include "si_mhl2_edid_3d_api.h"
+#include "si_mhl_tx_hw_drv_api.h"
+#ifdef MEDIA_DATA_TUNNEL_SUPPORT
+#include "si_mdt_inputdev.h"
+#endif
+#include "mhl_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 <linux/kernel.h>
+#include <linux/semaphore.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+
+#include "si_fw_macros.h"
+#include "si_infoframe.h"
+#include "si_edid.h"
+#include "si_mhl_defs.h"
+#include "si_mhl2_edid_3d_api.h"
+#include "si_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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/semaphore.h>
+#include <linux/cdev.h>
+#include <linux/spi/spi.h>
+
+#include "si_fw_macros.h"
+#include "si_infoframe.h"
+#include "si_edid.h"
+#include "si_mhl_defs.h"
+#include "si_mhl2_edid_3d_api.h"
+#include "si_mhl_tx_hw_drv_api.h"
+#ifdef MEDIA_DATA_TUNNEL_SUPPORT
+#include <linux/input.h>
+#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 <linux/of_gpio.h>
+#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 <http://www.siliconimage.com>");
+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 <linux/kernel.h>
+#include <linux/semaphore.h>
+#include <linux/cdev.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/slab.h>
+
+#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 <william.mulcahy@siliconimage.com>
+ * Copyright (c) 2013-2014 Silicon Image, Inc
+ *
+ * This code is based on hid-multitouch.c:
+ *
+ * Copyright (c) 2010-2011 Stephane Chatty <chatty@enac.fr>
+ * Copyright (c) 2010-2011 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * 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 <linux/device.h>
+#include <linux/hid.h>
+#include <linux/hidraw.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input/mt.h>
+
+#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 <william.mulcahy@siliconimage.com>
+ * 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 <benjamin.tissoires@gmail.com>
+ * 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 <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> 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 <linux/kernel.h>
+#include <linux/semaphore.h>
+#include <linux/cdev.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/hid.h>
+#include <linux/workqueue.h>
+
+#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 <linux/mod_devicetable.h>
+#include <linux/hid.h>
+
+#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 <linux/input.h>
+#include <linux/cdev.h>
+#include <linux/hrtimer.h>
+#include "si_fw_macros.h"
+#include "si_infoframe.h"
+#include "si_edid.h"
+#include "si_mhl_defs.h"
+#include "si_mhl2_edid_3d_api.h"
+#include "si_mhl_tx_hw_drv_api.h"
+#include "si_mdt_inputdev.h"
+#include "mhl_linux_tx.h"
+#include "platform.h"
+#ifdef KERNEL_2_6_38_AND_LATER
+#include <linux/input/mt.h>
+#endif
+#ifdef DEBUG
+#include <linux/kernel.h>
+#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 <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <linux/semaphore.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+#include "si_fw_macros.h"
+#include "si_infoframe.h"
+#include "si_edid.h"
+#include "si_mhl_defs.h"
+#include "si_mhl2_edid_3d_api.h"
+#include "si_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 <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/mod_devicetable.h>
+#include <linux/si_6031_switch.h>
+
+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<praveen.vuppala@siliconimage.com>");
+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