devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCHv5 0/3] Add initial support for slimport anx78xx
@ 2015-11-13 12:01 Enric Balletbo i Serra
  2015-11-13 12:01 ` [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc Enric Balletbo i Serra
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Enric Balletbo i Serra @ 2015-11-13 12:01 UTC (permalink / raw)
  To: devicetree, linux-kernel, dri-devel, devel
  Cc: mark.rutland, drinkcat, laurent.pinchart, pawel.moll,
	ijc+devicetree, gregkh, cawa.cheng, jb.tsai, sjoerd.simons,
	robh+dt, span, galak, javier, eddie.huang, cjiao, dan.carpenter,
	nathan.chung

Hi all,

This is the fifth version of patch set. Any comments are welcome.

See the changelog below for details.

The following series add initial support for the Slimport ANX7814 transmitter, a
ultra-low power Full-HD (1080p60) transmitter designed for portable device.

The driver was originally created and based from the work of Junhua Xia from
Analogix. This driver is a refactor of the original driver and fixes different
coding style lines, and different errors/warnings reported by checkpatch. Also
there were things that I noticed that we need to change like:

 - Convert the numbered GPIO API to the new descriptor based GPIO API.
 - Review the DT binding
 - Add missing MODULE_DEVICE_TABLE(of, ...);
 - Fix Makefiles and Kconfig to build conditionally.
 - Use SIMPLE_DEV_PM_OPS() instead of the deprecated i2c .suspend and
  .resume callbacks.
 - Move to use managed device resources.
 - Remove dead/unused code.
 - And others ...

Changes since v4:
Rob Herring and Laurent Pinchart:
 - Add ports binding for describing data connections.
Nicolas Boichat:
 - He did a big effort refactoring the driver and in order to simplify the different
   state machines involved and the code in general.
Enric Balletbo:
 - Remove some magic numbers.
 - Use some DRM helpers.
 - Document better the driver in general

Changes since v3:

Nicolas Boichat: 
 - Integrate sp_edid_header_result with sp_check_edid_data
 - Fix loop forever.
 - Use meaningful messages and variable names
 - Replace some 'while' loops and use a for loop.
 - Might be clearer to say >= LINK_6P75G
 - Convert a function to void function because always return 0
 - Remove some magic numbers and refactor sp_tx_pclk_calc
 - Replace sp_read_reg(SP_TX_LINK_BW_SET_REG) for sp_tx_get_link_bw.
 - Mask bits 4:0. Bit 5 has another purpose, and 7:6 are reserved.
 - Use ARRAY_SIZE.
 - Use memset for initialization.
 - Simply condition if (!(c1 & POLLING_EN) || (c & POLLING_ERR))
 - Don not use a temporal variable write the value directly.
 - Fix various typos
 - Return directly PTR_ERR.

Dan Carpenter: 
 - Refactor while loop removing the earlier condition and do while (--c) instead
 - Simplify double negative and fix alignment
 - Remove the superflous casts to u16 and parens
 - Remove debug printks and use ftrace instead.
 - Flip this condition around and pull the code in one indent level.
 - Fix return value 'ret' that should be an int. It causes a signedness bug later.
 - Use better style for devm_kzalloc
 - Get rid of AUX_*.  They aren't used much and we could easily use normal error codes instead.

Enric Balletbo
 - Fix errors reported by scripts/checkpatch.pl --strict --subjective
 - Remove XTAL_CLK_M10 XTAL_CLK definitions
 - replace ulong for unsigned long
 - remove some magic numbers and refactor sp_tx_enable_audio_output
 - remove some magic numbers and refactor sp_tx_phy_auto_test

Changes since v2 (requested by Daniel Kurtz):
 - clean up the typos, and little nits requested by Dan.
 - move to the drm/bridge directory
 - rename the files, variables, types, etc. to anx78xx
 - plumb through the context struct to all functions that act on the device
 - use proper messaging (dev_ rather than pr_, _dbg/_err rather than _info)

Changes since v1:
 - As requested by Greg, move from staging to a subsystem.

Best regards,

Enric Balletbo i Serra (3):
  of: Add vendor prefix for Analogix Semiconductor, Inc.
  devicetree: Add new ANX7814 SlimPort transmitter binding.
  drm: bridge: anx78xx: Add anx78xx driver support by analogix.

 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 .../devicetree/bindings/video/bridge/anx7814.txt   |   36 +
 drivers/gpu/drm/bridge/Kconfig                     |    2 +
 drivers/gpu/drm/bridge/Makefile                    |    1 +
 drivers/gpu/drm/bridge/anx78xx/Kconfig             |    5 +
 drivers/gpu/drm/bridge/anx78xx/Makefile            |    4 +
 drivers/gpu/drm/bridge/anx78xx/anx78xx.h           |   45 +
 drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c      |  347 +++
 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c   | 3226 ++++++++++++++++++++
 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h   |  149 +
 drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h   |  754 +++++
 11 files changed, 4570 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/bridge/anx7814.txt
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/Kconfig
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/Makefile
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx.h
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h

-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc.
  2015-11-13 12:01 [PATCHv5 0/3] Add initial support for slimport anx78xx Enric Balletbo i Serra
@ 2015-11-13 12:01 ` Enric Balletbo i Serra
  2015-11-13 14:38   ` Rob Herring
  2015-11-13 12:01 ` [PATCHv5 2/3] devicetree: Add new ANX7814 SlimPort transmitter binding Enric Balletbo i Serra
  2015-11-13 12:01 ` [PATCHv5 3/3] drm: bridge: anx78xx: Add anx78xx driver support by analogix Enric Balletbo i Serra
  2 siblings, 1 reply; 8+ messages in thread
From: Enric Balletbo i Serra @ 2015-11-13 12:01 UTC (permalink / raw)
  To: devicetree, linux-kernel, dri-devel, devel
  Cc: mark.rutland, drinkcat, laurent.pinchart, pawel.moll,
	ijc+devicetree, gregkh, cawa.cheng, jb.tsai, sjoerd.simons,
	robh+dt, span, galak, javier, eddie.huang, cjiao, dan.carpenter,
	nathan.chung

Analogix Semiconductor develops analog and mixed-signal devices for digital
media and communications interconnect applications.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 82d2ac9..8987ee8 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -22,6 +22,7 @@ ampire	Ampire Co., Ltd.
 ams	AMS AG
 amstaos	AMS-Taos Inc.
 apm	Applied Micro Circuits Corporation (APM)
+analogix	Analogix Semiconductor, Inc.
 aptina	Aptina Imaging
 arasan	Arasan Chip Systems
 arm	ARM Ltd.
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCHv5 2/3] devicetree: Add new ANX7814 SlimPort transmitter binding.
  2015-11-13 12:01 [PATCHv5 0/3] Add initial support for slimport anx78xx Enric Balletbo i Serra
  2015-11-13 12:01 ` [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc Enric Balletbo i Serra
@ 2015-11-13 12:01 ` Enric Balletbo i Serra
  2015-11-13 14:37   ` Rob Herring
  2015-11-13 12:01 ` [PATCHv5 3/3] drm: bridge: anx78xx: Add anx78xx driver support by analogix Enric Balletbo i Serra
  2 siblings, 1 reply; 8+ messages in thread
From: Enric Balletbo i Serra @ 2015-11-13 12:01 UTC (permalink / raw)
  To: devicetree, linux-kernel, dri-devel, devel
  Cc: mark.rutland, drinkcat, laurent.pinchart, pawel.moll,
	ijc+devicetree, gregkh, cawa.cheng, jb.tsai, sjoerd.simons,
	robh+dt, span, galak, javier, eddie.huang, cjiao, dan.carpenter,
	nathan.chung

The ANX7814 is an ultra-low power Full-HD (1080p60) SlimPort transmitter
designed for portable devices.

You can add support to your board with current binding.

Example:

	anx7814: anx7814@38 {
		compatible = "analogix,anx7814";
		reg = <0x38>;
		pd-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
		reset-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
	};

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 .../devicetree/bindings/video/bridge/anx7814.txt   | 36 ++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/bridge/anx7814.txt

diff --git a/Documentation/devicetree/bindings/video/bridge/anx7814.txt b/Documentation/devicetree/bindings/video/bridge/anx7814.txt
new file mode 100644
index 0000000..f7bdca9
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/bridge/anx7814.txt
@@ -0,0 +1,36 @@
+Analogix ANX7814 SlimPort (Full-HD Transmitter)
+-----------------------------------------------
+
+The ANX7814 is an ultra-low power Full-HD (1080p60) SlimPort transmitter
+designed for portable devices.
+
+Required properties:
+
+ - compatible		: "analogix,anx7814"
+ - reg			: I2C address of the device
+ - cable-det-gpios	: Which GPIO to use for cable detection
+ - pd-gpios		: Which GPIO to use for power down
+ - reset-gpios		: Which GPIO to use for reset
+
+Optional properties:
+
+ - v10-gpios	: Which GPIO to use for V10 control.
+ - video interfaces: Device node can contain video interface port nodes.
+
+Example:
+
+	anx7814: anx7814@38 {
+		compatible = "analogix,anx7814";
+		reg = <0x38>;
+		cable-det-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
+		pd-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
+		v10-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
+		ports {
+			port@0 {
+				anx7814_in: endpoint {
+					remote-endpoint = <&hdmi0_out>;
+				};
+			};
+		};
+	};
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCHv5 3/3] drm: bridge: anx78xx: Add anx78xx driver support by analogix.
  2015-11-13 12:01 [PATCHv5 0/3] Add initial support for slimport anx78xx Enric Balletbo i Serra
  2015-11-13 12:01 ` [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc Enric Balletbo i Serra
  2015-11-13 12:01 ` [PATCHv5 2/3] devicetree: Add new ANX7814 SlimPort transmitter binding Enric Balletbo i Serra
@ 2015-11-13 12:01 ` Enric Balletbo i Serra
  2015-11-16  1:03   ` kbuild test robot
  2 siblings, 1 reply; 8+ messages in thread
From: Enric Balletbo i Serra @ 2015-11-13 12:01 UTC (permalink / raw)
  To: devicetree, linux-kernel, dri-devel, devel
  Cc: mark.rutland, drinkcat, laurent.pinchart, pawel.moll,
	ijc+devicetree, gregkh, cawa.cheng, jb.tsai, sjoerd.simons,
	robh+dt, span, galak, javier, eddie.huang, cjiao, dan.carpenter,
	nathan.chung

At the moment it only supports ANX7814.

The ANX7814 is an ultra-low power Full-HD (1080p60) SlimPort transmitter
designed for portable devices.

This driver adds initial support and supports HDMI to DP pass-through mode.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---
 drivers/gpu/drm/bridge/Kconfig                   |    2 +
 drivers/gpu/drm/bridge/Makefile                  |    1 +
 drivers/gpu/drm/bridge/anx78xx/Kconfig           |    5 +
 drivers/gpu/drm/bridge/anx78xx/Makefile          |    4 +
 drivers/gpu/drm/bridge/anx78xx/anx78xx.h         |   45 +
 drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c    |  347 +++
 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c | 3226 ++++++++++++++++++++++
 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h |  149 +
 drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h |  754 +++++
 9 files changed, 4533 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/Kconfig
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/Makefile
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx.h
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h
 create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 2de52a5..aa6fe12 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -29,4 +29,6 @@ config DRM_PARADE_PS8622
 	---help---
 	  Parade eDP-LVDS bridge chip driver.
 
+source "drivers/gpu/drm/bridge/anx78xx/Kconfig"
+
 endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index e2eef1c..e5bd38b 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
+obj-$(CONFIG_DRM_ANX78XX) += anx78xx/
diff --git a/drivers/gpu/drm/bridge/anx78xx/Kconfig b/drivers/gpu/drm/bridge/anx78xx/Kconfig
new file mode 100644
index 0000000..7537673
--- /dev/null
+++ b/drivers/gpu/drm/bridge/anx78xx/Kconfig
@@ -0,0 +1,5 @@
+config DRM_ANX78XX
+	tristate "Analogix ANX78XX bridge"
+	help
+        	ANX78XX is a HD video transmitter chip over micro-USB
+		connector for smartphone device.
diff --git a/drivers/gpu/drm/bridge/anx78xx/Makefile b/drivers/gpu/drm/bridge/anx78xx/Makefile
new file mode 100644
index 0000000..a843733
--- /dev/null
+++ b/drivers/gpu/drm/bridge/anx78xx/Makefile
@@ -0,0 +1,4 @@
+obj-${CONFIG_DRM_ANX78XX} :=  anx78xx.o
+
+anx78xx-y += anx78xx_main.o
+anx78xx-y += slimport_tx_drv.o
diff --git a/drivers/gpu/drm/bridge/anx78xx/anx78xx.h b/drivers/gpu/drm/bridge/anx78xx/anx78xx.h
new file mode 100644
index 0000000..ff8bbe8
--- /dev/null
+++ b/drivers/gpu/drm/bridge/anx78xx/anx78xx.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright(c) 2015, Analogix Semiconductor. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ANX78xx_H
+#define __ANX78xx_H
+
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/gpio/consumer.h>
+
+#include <drm/drm_crtc.h>
+
+struct anx78xx_platform_data {
+	struct gpio_desc *gpiod_cable_det;
+	struct gpio_desc *gpiod_pd;
+	struct gpio_desc *gpiod_reset;
+	struct gpio_desc *gpiod_v10;
+};
+
+struct anx78xx {
+	struct drm_bridge bridge;
+	struct i2c_client *client;
+	struct anx78xx_platform_data *pdata;
+	struct delayed_work work;
+	struct workqueue_struct *workqueue;
+};
+
+void anx78xx_poweron(struct anx78xx *data);
+void anx78xx_poweroff(struct anx78xx *data);
+bool anx78xx_cable_is_detected(struct anx78xx *anx78xx);
+
+#endif
diff --git a/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c b/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c
new file mode 100644
index 0000000..4ea1274
--- /dev/null
+++ b/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright(c) 2015, Analogix Semiconductor. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/async.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/types.h>
+
+#include "anx78xx.h"
+#include "slimport_tx_drv.h"
+
+void anx78xx_poweron(struct anx78xx *anx78xx)
+{
+	struct anx78xx_platform_data *pdata = anx78xx->pdata;
+
+	if (pdata->gpiod_v10) {
+		gpiod_set_value_cansleep(pdata->gpiod_v10, 1);
+		usleep_range(1000, 2000);
+	}
+
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+	usleep_range(1000, 2000);
+
+	gpiod_set_value_cansleep(pdata->gpiod_pd, 0);
+	usleep_range(1000, 2000);
+
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
+}
+
+void anx78xx_poweroff(struct anx78xx *anx78xx)
+{
+	struct anx78xx_platform_data *pdata = anx78xx->pdata;
+
+	if (pdata->gpiod_v10) {
+		gpiod_set_value_cansleep(pdata->gpiod_v10, 0);
+		usleep_range(1000, 2000);
+	}
+
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+	usleep_range(1000, 2000);
+
+	gpiod_set_value_cansleep(pdata->gpiod_pd, 1);
+	usleep_range(1000, 2000);
+}
+
+static int anx78xx_init_gpio(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	struct anx78xx_platform_data *pdata = anx78xx->pdata;
+
+	/* gpio for cable detection */
+	pdata->gpiod_cable_det = devm_gpiod_get(dev, "cable-det", GPIOD_IN);
+	if (IS_ERR(pdata->gpiod_cable_det)) {
+		dev_err(dev, "unable to claim cable-det gpio\n");
+		return PTR_ERR(pdata->gpiod_cable_det);
+	}
+
+	/* gpio for chip power down */
+	pdata->gpiod_pd = devm_gpiod_get(dev, "pd", GPIOD_OUT_HIGH);
+	if (IS_ERR(pdata->gpiod_pd)) {
+		dev_err(dev, "unable to claim pd gpio\n");
+		return PTR_ERR(pdata->gpiod_pd);
+	}
+
+	/* gpio for chip reset */
+	pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(pdata->gpiod_reset)) {
+		dev_err(dev, "unable to claim reset gpio\n");
+		return PTR_ERR(pdata->gpiod_reset);
+	}
+
+	/* gpio for V10 power control */
+	pdata->gpiod_v10 = devm_gpiod_get_optional(dev, "v10", GPIOD_OUT_LOW);
+	if (IS_ERR(pdata->gpiod_v10)) {
+		dev_err(dev, "unable to claim v10 gpio\n");
+		return PTR_ERR(pdata->gpiod_v10);
+	}
+
+	return 0;
+}
+
+bool anx78xx_cable_is_detected(struct anx78xx *anx78xx)
+{
+	int i, count = 0;
+
+	for (i = 0; i < 10; i++) {
+		if (gpiod_get_value(anx78xx->pdata->gpiod_cable_det))
+			count++;
+		mdelay(5);
+	}
+
+	return (count > 5);
+}
+
+/*
+ * HPD IRQ Event: HPD pulse width greater than 0.25ms but narrower than 2ms
+ * Hot Unplug Event: HPD pulse stays low longer than 2ms.
+ *
+ * AP just monitor HPD pulse high in this irq. If HPD is high, the driver
+ * will power on the chip, and then the driver controls when to power down
+ * the chip, if HPD event is HPD IRQ, the driver deals with IRQ event from
+ * downstream, finally, if HPD event is Hot Plug, the driver power down the
+ * chip.
+ */
+static irqreturn_t anx78xx_cable_isr(int irq, void *data)
+{
+	struct anx78xx *anx78xx = data;
+
+	queue_delayed_work(anx78xx->workqueue, &anx78xx->work, 0);
+
+	return IRQ_HANDLED;
+}
+
+static void anx78xx_work_func(struct work_struct *work)
+{
+	struct anx78xx *anx78xx = container_of(work, struct anx78xx,
+					       work.work);
+
+	if (sp_main_process(anx78xx))
+		queue_delayed_work(anx78xx->workqueue, &anx78xx->work,
+				   msecs_to_jiffies(500));
+	else
+		cancel_delayed_work(&anx78xx->work);
+}
+
+static inline struct anx78xx *bridge_to_anx78xx(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct anx78xx, bridge);
+}
+
+static int anx78xx_bridge_attach(struct drm_bridge *bridge)
+{
+	return 0;
+}
+
+static bool anx78xx_bridge_mode_fixup(struct drm_bridge *bridge,
+				      const struct drm_display_mode *mode,
+				      struct drm_display_mode *adjusted_mode)
+{
+	struct anx78xx *anx78xx = bridge_to_anx78xx(bridge);
+
+	dev_dbg(&anx78xx->client->dev, "mode_fixup %d<%d; %d; %d\n",
+		sp_get_link_bandwidth(anx78xx), SP_LINK_5P4G,
+		mode->clock, mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		return false;
+
+	/* Max 720p at 2.7 Ghz, one lane */
+	if (sp_get_link_bandwidth(anx78xx) < SP_LINK_5P4G &&
+	    mode->clock > 74250)
+		return false;
+
+	/* Max 1200p at 5.4 Ghz, one lane */
+	if (mode->clock > 154000)
+		return false;
+
+	return true;
+}
+
+static const struct drm_bridge_funcs anx78xx_bridge_funcs = {
+	.attach		= anx78xx_bridge_attach,
+	.mode_fixup	= anx78xx_bridge_mode_fixup,
+};
+
+static int anx78xx_i2c_probe(struct i2c_client *client,
+			     const struct i2c_device_id *id)
+{
+	struct anx78xx *anx78xx;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_I2C_BLOCK)) {
+		dev_err(&client->dev, "i2c bus does not support the device\n");
+		return -ENODEV;
+	}
+
+	anx78xx = devm_kzalloc(&client->dev, sizeof(*anx78xx), GFP_KERNEL);
+	if (!anx78xx)
+		return -ENOMEM;
+
+	anx78xx->pdata = devm_kzalloc(&client->dev,
+				      sizeof(struct anx78xx_platform_data),
+				      GFP_KERNEL);
+	if (!anx78xx->pdata)
+		return -ENOMEM;
+
+	anx78xx->bridge.of_node = client->dev.of_node;
+	anx78xx->bridge.funcs = &anx78xx_bridge_funcs;
+	ret = drm_bridge_add(&anx78xx->bridge);
+	if (ret < 0) {
+		dev_err(&client->dev, "add drm bridge failed\n");
+		return ret;
+	}
+
+	anx78xx->client = client;
+
+	i2c_set_clientdata(client, anx78xx);
+
+	ret = anx78xx_init_gpio(anx78xx);
+	if (ret) {
+		dev_err(&client->dev, "failed to initialize gpios\n");
+		goto fail_remove_bridge;
+	}
+
+	INIT_DELAYED_WORK(&anx78xx->work, anx78xx_work_func);
+
+	anx78xx->workqueue = create_singlethread_workqueue("anx78xx_work");
+	if (!anx78xx->workqueue) {
+		dev_err(&client->dev, "failed to create work queue\n");
+		ret = -ENOMEM;
+		goto fail_remove_bridge;
+	}
+
+	ret = sp_system_init(anx78xx);
+	if (ret) {
+		dev_err(&client->dev, "failed to initialize anx78xx\n");
+		goto fail_free_wq;
+	}
+
+	client->irq = gpiod_to_irq(anx78xx->pdata->gpiod_cable_det);
+	if (client->irq < 0) {
+		dev_err(&client->dev, "failed to get irq: %d\n", client->irq);
+		ret = client->irq;
+		goto fail_free_wq;
+	}
+
+	ret = request_threaded_irq(client->irq, NULL, anx78xx_cable_isr,
+				   IRQF_TRIGGER_RISING |
+				   IRQF_TRIGGER_FALLING |
+				   IRQF_ONESHOT, "anx78xx", anx78xx);
+	if (ret) {
+		dev_err(&client->dev, "failed to request threaded irq\n");
+		goto fail_free_wq;
+	}
+
+	ret = irq_set_irq_wake(client->irq, 1);
+	if (ret) {
+		dev_err(&client->dev, "failed to set irq wake\n");
+		goto fail_free_wq;
+	}
+
+	ret = enable_irq_wake(client->irq);
+	if (ret) {
+		dev_err(&client->dev, "failed to enable irq wake\n");
+		goto fail_free_wq;
+	}
+
+	/* enable driver */
+	queue_delayed_work(anx78xx->workqueue, &anx78xx->work, 0);
+
+	return 0;
+
+fail_free_wq:
+	destroy_workqueue(anx78xx->workqueue);
+fail_remove_bridge:
+	drm_bridge_remove(&anx78xx->bridge);
+	return ret;
+}
+
+static int anx78xx_i2c_remove(struct i2c_client *client)
+{
+	struct anx78xx *anx78xx = i2c_get_clientdata(client);
+
+	destroy_workqueue(anx78xx->workqueue);
+	drm_bridge_remove(&anx78xx->bridge);
+
+	return 0;
+}
+
+static int anx78xx_i2c_suspend(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct anx78xx *anx78xx = i2c_get_clientdata(client);
+
+	if (anx78xx_cable_is_detected(anx78xx)) {
+		cancel_delayed_work_sync(&anx78xx->work);
+		flush_workqueue(anx78xx->workqueue);
+		anx78xx_poweroff(anx78xx);
+	}
+
+	return 0;
+}
+
+static int anx78xx_i2c_resume(struct device *dev)
+{
+	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+	struct anx78xx *anx78xx = i2c_get_clientdata(client);
+
+	if (anx78xx_cable_is_detected(anx78xx))
+		queue_delayed_work(anx78xx->workqueue, &anx78xx->work, 0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(anx78xx_i2c_pm_ops,
+			anx78xx_i2c_suspend, anx78xx_i2c_resume);
+
+static const struct i2c_device_id anx78xx_id[] = {
+	{"anx7814", 0},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(i2c, anx78xx_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id anx78xx_match_table[] = {
+	{.compatible = "analogix,anx7814",},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, anx78xx_match_table);
+#endif
+
+static struct i2c_driver anx78xx_driver = {
+	.driver = {
+		   .name = "anx7814",
+		   .pm = &anx78xx_i2c_pm_ops,
+#ifdef CONFIG_OF
+		   .of_match_table = of_match_ptr(anx78xx_match_table),
+#endif
+		   },
+	.probe = anx78xx_i2c_probe,
+	.remove = anx78xx_i2c_remove,
+	.id_table = anx78xx_id,
+};
+
+module_i2c_driver(anx78xx_driver);
+
+MODULE_DESCRIPTION("Slimport transmitter ANX78XX driver");
+MODULE_AUTHOR("Junhua Xia <jxia@analogixsemi.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.1");
diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c
new file mode 100644
index 0000000..2d9ec0f
--- /dev/null
+++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c
@@ -0,0 +1,3226 @@
+/*
+ * Copyright(c) 2015, Analogix Semiconductor. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+
+#include <linux/delay.h>
+#include <linux/types.h>
+
+#include "anx78xx.h"
+#include "slimport_tx_drv.h"
+
+#define XTAL_27M	270
+#define XTAL_CLK	XTAL_27M
+
+struct slimport {
+	bool	hdcp_enabled;	/* HDCP control enable/ disable from AP */
+
+	u8	tx_test_bw;
+	bool	tx_test_lt;
+	bool	tx_test_edid;
+
+	u8	changed_bandwidth;
+
+	bool	need_clean_status;
+
+	u8	hdcp_error_count;
+	u8	hdcp_fail_count;
+	u8	audio_stable_count;	/* Audio stable counter */
+
+	u8	edid_blocks[EDID_LENGTH];
+
+	bool	read_edid_flag;
+
+	bool	down_sample_en;
+
+	struct packet_audio	tx_packet_audio;
+	struct packet_avi	tx_packet_avi;
+	struct packet_mpeg	tx_packet_mpeg;
+	struct packet_vsi	tx_packet_vsi;
+
+	/* Interrupt status registers */
+	u8	common_int[4];
+	u8	dp_int;
+	u8	sp_hdmi_int[7];
+
+	enum sp_tx_state		tx_system_state;
+	enum audio_output_status	tx_ao_state;
+	enum video_output_status	tx_vo_state;
+	enum sp_tx_lt_status		tx_lt_state;
+	enum hdcp_status		hdcp_state;
+	enum repeater_status		repeater_state;
+};
+
+static struct slimport sp;
+
+static const u16 chipid_list[] = {
+	0x7802,
+	0x7806,
+	0x7810,
+	0x7812,
+	0x7814,
+	0x7816,
+	0x7818,
+};
+
+static void sp_hdmi_new_vsi_int(struct anx78xx *anx78xx);
+static void sp_print_system_state(struct anx78xx *anx78xx,
+				  enum sp_tx_state state);
+static void sp_show_information(struct anx78xx *anx78xx);
+static void sp_variable_init(void);
+
+/**
+ * sp_reg_read: Read ai value from a single register.
+ *
+ * @anx78xx: Device to read from.
+ * @addr: Address to read from.
+ * @reg: Register to be read from.
+ * @val: Pointer to store read value.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+static int sp_reg_read(struct anx78xx *anx78xx, u8 addr, u8 offset, u8 *val)
+{
+	int ret;
+	struct i2c_client *client = anx78xx->client;
+
+	client->addr = addr >> 1;
+
+	ret = i2c_smbus_read_byte_data(client, offset);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to read i2c addr=%x\n", addr);
+		return ret;
+	}
+
+	*val = ret;
+
+	return 0;
+}
+
+/**
+ * sp_reg_write(): Write a value to a single register
+ *
+ * @anx78xx: Device to read from.
+ * @addr: Address to write to.
+ * @offset: Byte interpreted by slave.
+ * @val: Value to be written.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+static int sp_reg_write(struct anx78xx *anx78xx, u8 addr, u8 offset, u8 val)
+{
+	int ret;
+	struct i2c_client *client = anx78xx->client;
+
+	client->addr = addr >> 1;
+
+	ret = i2c_smbus_write_byte_data(client, offset, val);
+	if (ret < 0)
+		dev_err(&client->dev, "failed to write i2c addr=%x\n", addr);
+
+	return ret;
+}
+
+/**
+ * sp_reg_update_bits: Perform a read/modify/write cycle on the register.
+ *
+ * @anx78xx: Device to read from.
+ * @addr: Address to write to.
+ * @offset: Byte interpreted by slave.
+ * @mask: Bitmask to change.
+ * @val: New value for bitmask.
+ *
+ * Returns zero for success, a negative number on error.
+ */
+static int sp_reg_update_bits(struct anx78xx *anx78xx, u8 addr, u8 offset,
+			      u8 mask, u8 val)
+{
+	int ret;
+	u8 orig, tmp;
+
+	ret = sp_reg_read(anx78xx, addr, offset, &orig);
+	if (ret < 0)
+		return ret;
+
+	tmp = orig & ~mask;
+	tmp |= val & mask;
+
+	return sp_reg_write(anx78xx, addr, offset, tmp);
+}
+
+/**
+ * sp_reg_set_bits: Perform a read/write cycle to set bits in register.
+ *
+ * @anx78xx: Device to read from.
+ * @addr: Address to write to.
+ * @offset: Byte interpreted by slave.
+ * @mask: Bitmask to change.
+ *
+ * Returns zero for success, a negative number on error.
+ */
+static inline int sp_reg_set_bits(struct anx78xx *anx78xx, u8 addr,
+				  u8 offset, u8 mask)
+{
+	return sp_reg_update_bits(anx78xx, addr, offset, mask, mask);
+}
+
+/**
+ * sp_reg_clear_bits: Perform a read/write cycle to clear bits in register.
+ *
+ * @anx78xx: Device to read from.
+ * @addr: Address to write to.
+ * @offset: Byte interpreted by slave.
+ * @mask: Bitmask to change.
+ *
+ * Returns zero for success, a negative number on error.
+ */
+static inline int sp_reg_clear_bits(struct anx78xx *anx78xx, u8 addr,
+				    u8 offset, u8 mask)
+{
+	return sp_reg_update_bits(anx78xx, addr, offset, mask, 0);
+}
+
+static inline void sp_video_mute(struct anx78xx *anx78xx, bool enable)
+{
+	if (enable)
+		sp_reg_set_bits(anx78xx, TX_P2, SP_VID_CTRL1_REG,
+				SP_VIDEO_MUTE);
+	else
+		sp_reg_clear_bits(anx78xx, TX_P2, SP_VID_CTRL1_REG,
+				  SP_VIDEO_MUTE);
+}
+
+static inline void sp_hdmi_mute_audio(struct anx78xx *anx78xx, bool enable)
+{
+	if (enable)
+		sp_reg_set_bits(anx78xx, RX_P0, SP_HDMI_MUTE_CTRL_REG,
+				SP_AUD_MUTE);
+	else
+		sp_reg_clear_bits(anx78xx, RX_P0, SP_HDMI_MUTE_CTRL_REG,
+				  SP_AUD_MUTE);
+}
+
+static inline void sp_hdmi_mute_video(struct anx78xx *anx78xx, bool enable)
+{
+	if (enable)
+		sp_reg_set_bits(anx78xx, RX_P0, SP_HDMI_MUTE_CTRL_REG,
+				SP_VID_MUTE);
+	else
+		sp_reg_clear_bits(anx78xx, RX_P0, SP_HDMI_MUTE_CTRL_REG,
+				  SP_VID_MUTE);
+}
+
+static inline void sp_addronly_set(struct anx78xx *anx78xx, bool enable)
+{
+	if (enable)
+		sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG,
+				SP_ADDR_ONLY);
+	else
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG,
+				  SP_ADDR_ONLY);
+}
+
+static inline void sp_set_link_bw(struct anx78xx *anx78xx, u8 bw)
+{
+	sp_reg_write(anx78xx, TX_P0, SP_DP_MAIN_LINK_BW_SET_REG, bw);
+}
+
+static inline u8 sp_get_link_bw(struct anx78xx *anx78xx)
+{
+	u8 val;
+
+	sp_reg_read(anx78xx, TX_P0, SP_DP_MAIN_LINK_BW_SET_REG, &val);
+
+	return val & SP_LINK_BW_SET_MASK;
+}
+
+static inline bool sp_get_pll_lock_status(struct anx78xx *anx78xx)
+{
+	u8 val;
+
+	sp_reg_read(anx78xx, TX_P0, SP_DP_DEBUG1_REG, &val);
+
+	return (val & SP_DEBUG_PLL_LOCK) != 0;
+}
+
+static inline void sp_gen_m_clk_with_downspreading(struct anx78xx *anx78xx)
+{
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_M_CALCULATION_CTRL_REG,
+			SP_M_GEN_CLK_SEL);
+}
+
+static inline void sp_gen_m_clk_without_downspreading(struct anx78xx *anx78xx)
+{
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_M_CALCULATION_CTRL_REG,
+			  SP_M_GEN_CLK_SEL);
+}
+
+static inline void sp_hdmi_set_hpd(struct anx78xx *anx78xx, bool enable)
+{
+	if (enable)
+		sp_reg_set_bits(anx78xx, TX_P2, SP_VID_CTRL3_REG, SP_HPD_OUT);
+	else
+		sp_reg_clear_bits(anx78xx, TX_P2, SP_VID_CTRL3_REG,
+				  SP_HPD_OUT);
+}
+
+static inline void sp_hdmi_set_termination(struct anx78xx *anx78xx,
+					   bool enable)
+{
+	if (enable)
+		sp_reg_clear_bits(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 7,
+				  SP_PD_RT);
+	else
+		sp_reg_set_bits(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 7,
+				SP_PD_RT);
+}
+
+static inline bool sp_hdcp_repeater_mode(struct anx78xx *anx78xx)
+{
+	u8 val;
+
+	sp_reg_read(anx78xx, RX_P1, SP_HDCP_BCAPS_SHADOW_REG, &val);
+
+	return (val & SP_BCAPS_REPEATER);
+}
+
+static inline void sp_clean_hdcp_status(struct anx78xx *anx78xx)
+{
+	sp_reg_write(anx78xx, TX_P0, SP_HDCP_CTRL0_REG,
+		     SP_BKSV_SRM_PASS | SP_KSVLIST_VLD);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_HDCP_CTRL0_REG, SP_RE_AUTH);
+	usleep_range(2000, 4000);
+}
+
+static const u8 dp_tx_output_precise_tune_bits[20] = {
+	0x01, 0x03, 0x07, 0x7f, 0x71, 0x6b, 0x7f,
+	0x73, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00,
+	0x0c, 0x42, 0x1e, 0x3e, 0x72, 0x7e,
+};
+
+static void sp_link_phy_initialization(struct anx78xx *anx78xx)
+{
+	int i;
+
+	/*
+	 * REVISIT : It is writing to a RESERVED bits in Analog Control 0
+	 * register.
+	 */
+	sp_reg_write(anx78xx, TX_P2, SP_ANALOG_CTRL0_REG, 0x02);
+
+	/*
+	 * Write DP TX output emphasis precise tune bits.
+	 */
+	for (i = 0; i < ARRAY_SIZE(dp_tx_output_precise_tune_bits); i++)
+		sp_reg_write(anx78xx, TX_P1, SP_DP_TX_LT_CTRL0_REG + i,
+			     dp_tx_output_precise_tune_bits[i]);
+}
+
+static void sp_set_system_state(struct anx78xx *anx78xx,
+				enum sp_tx_state new_state)
+{
+	u8 val;
+	struct device *dev = &anx78xx->client->dev;
+
+	if ((sp.tx_system_state >= STATE_LINK_TRAINING) &&
+	    (new_state < STATE_LINK_TRAINING))
+		sp_reg_set_bits(anx78xx, TX_P0, SP_DP_ANALOG_POWER_DOWN_REG,
+				SP_CH0_PD);
+
+	dev_dbg(dev, "<< System State Transiton A -> B:\n");
+	dev_dbg(dev, "<< A:\n");
+	sp_print_system_state(anx78xx, sp.tx_system_state);
+	dev_dbg(dev, "<< B:\n");
+	sp_print_system_state(anx78xx, new_state);
+
+	if (sp.tx_system_state >= STATE_LINK_TRAINING) {
+		if (new_state >= STATE_AUDIO_OUTPUT) {
+			sp_hdmi_mute_audio(anx78xx, true);
+		} else {
+			sp_hdmi_mute_video(anx78xx, true);
+			sp_video_mute(anx78xx, true);
+		}
+	}
+
+	if (!sp_hdcp_repeater_mode(anx78xx)) {
+		if (sp.tx_system_state >= STATE_HDCP_AUTH &&
+		    new_state <= STATE_HDCP_AUTH) {
+			sp_reg_read(anx78xx, TX_P0, SP_HDCP_CTRL0_REG, &val);
+			if (val & ~(SP_KSVLIST_VLD | SP_BKSV_SRM_PASS))
+				sp_clean_hdcp_status(anx78xx);
+		}
+	} else {
+		if (sp.tx_system_state > STATE_LINK_TRAINING &&
+		    new_state <= STATE_LINK_TRAINING) {
+			/* Inform AP to re-auth */
+			sp_hdmi_set_hpd(anx78xx, false);
+			sp_hdmi_set_termination(anx78xx, false);
+			msleep(50);
+		}
+	}
+
+	sp.tx_system_state = new_state;
+	sp.hdcp_state = HDCP_CAPABLE_CHECK;
+	sp.tx_lt_state = LT_INIT;
+	sp.tx_vo_state = VO_WAIT_VIDEO_STABLE;
+	/* Reset audio stable counter */
+	sp.audio_stable_count = 0;
+}
+
+static inline void sp_reg_hardware_reset(struct anx78xx *anx78xx)
+{
+	sp_reg_set_bits(anx78xx, TX_P2, SP_RESET_CTRL1_REG, SP_HW_RST);
+	sp_variable_init();
+	sp_set_system_state(anx78xx, STATE_SP_INITIALIZED);
+	msleep(500);
+}
+
+static inline void sp_write_dpcd_addr(struct anx78xx *anx78xx, u8 addrh,
+				      u8 addrm, u8 addrl)
+{
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_ADDR_7_0_REG, addrl);
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_ADDR_15_8_REG, addrm);
+
+	/*
+	 * DP AUX CH Address Register #2, only update bits[3:0]
+	 * [7:4] RESERVED
+	 * [3:0] AUX_ADDR[19:16], Register control AUX CH address.
+	 */
+	sp_reg_update_bits(anx78xx, TX_P0, SP_AUX_ADDR_19_16_REG,
+			   SP_AUX_ADDR_19_16_MASK, addrh);
+}
+
+static int sp_wait_aux_op_finish(struct anx78xx *anx78xx)
+{
+	u8 errcnt;
+	u8 val;
+	struct device *dev = &anx78xx->client->dev;
+
+	errcnt = 150;
+	while (errcnt--) {
+		sp_reg_read(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, &val);
+		if (!(val & SP_AUX_EN))
+			break;
+		usleep_range(2000, 4000);
+	}
+
+	if (!errcnt) {
+		dev_err(dev, "aux operate failed!\n");
+		return -1;
+	}
+
+	sp_reg_read(anx78xx, TX_P0, SP_AUX_CH_STATUS_REG, &val);
+	if (val & SP_AUX_STATUS) {
+		dev_err(dev, "wait aux operation status %.2x\n", val);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void sp_print_system_state(struct anx78xx *anx78xx,
+				  enum sp_tx_state state)
+{
+	struct device *dev = &anx78xx->client->dev;
+
+	switch (state) {
+	case STATE_WAITING_CABLE_PLUG:
+		dev_dbg(dev, "-- WAITING CABLE PLUG --\n");
+		break;
+	case STATE_SP_INITIALIZED:
+		dev_dbg(dev, "-- SP INITIALIZED --\n");
+		break;
+	case STATE_SINK_CONNECTION:
+		dev_dbg(dev, "-- SINK CONNECTION --\n");
+		break;
+	case STATE_PARSE_EDID:
+		dev_dbg(dev, "-- PARSE EDID --\n");
+		break;
+	case STATE_LINK_TRAINING:
+		dev_dbg(dev, "-- LINK TRAINING --\n");
+		break;
+	case STATE_VIDEO_OUTPUT:
+		dev_dbg(dev, "-- VIDEO OUTPUT --\n");
+		break;
+	case STATE_HDCP_AUTH:
+		dev_dbg(dev, "-- HDCP AUTH --\n");
+		break;
+	case STATE_AUDIO_OUTPUT:
+		dev_dbg(dev, "-- AUDIO OUTPUT --\n");
+		break;
+	case STATE_PLAY_BACK:
+		dev_dbg(dev, "-- PLAY BACK --\n");
+		break;
+	default:
+		dev_err(dev, "-- UNKNOWN! --\n");
+		break;
+	}
+}
+
+static void sp_reset_aux(struct anx78xx *anx78xx)
+{
+	sp_reg_set_bits(anx78xx, TX_P2, SP_RESET_CTRL2_REG, SP_AUX_RST);
+	sp_reg_clear_bits(anx78xx, TX_P2, SP_RESET_CTRL2_REG, SP_AUX_RST);
+}
+
+static u8 sp_aux_dpcdread_bytes(struct anx78xx *anx78xx, u8 addrh,
+				u8 addrm, u8 addrl, u8 count, u8 *buf)
+{
+	u8 val, val1, i;
+	struct device *dev = &anx78xx->client->dev;
+
+	sp_reg_write(anx78xx, TX_P0, SP_BUF_DATA_COUNT_REG, SP_BUF_CLR);
+
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG,
+		     ((count - 1) << SP_AUX_LENGTH_SHIFT) | 0x09);
+	sp_write_dpcd_addr(anx78xx, addrh, addrm, addrl);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, SP_AUX_EN);
+	usleep_range(2000, 4000);
+
+	if (sp_wait_aux_op_finish(anx78xx)) {
+		dev_err(dev, "aux read failed\n");
+		sp_reg_read(anx78xx, TX_P2, SP_DP_INT_STATUS_REG, &val);
+		sp_reg_read(anx78xx, TX_P0, SP_DP_DEBUG1_REG, &val1);
+		if (!(val1 & SP_POLLING_EN) || (val & SP_POLLING_ERR))
+			sp_reset_aux(anx78xx);
+		return -1;
+	}
+
+	for (i = 0; i < count; i++) {
+		sp_reg_read(anx78xx, TX_P0, SP_DP_BUF_DATA0_REG + i, &val);
+		buf[i] = val;
+	}
+
+	return 0;
+}
+
+static int sp_aux_dpcdwrite_bytes(struct anx78xx *anx78xx, u8 addrh,
+				  u8 addrm, u8 addrl, u8 count, u8 *buf)
+{
+	int i;
+
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG,
+		     ((count - 1) << SP_AUX_LENGTH_SHIFT) | 0x08);
+	sp_write_dpcd_addr(anx78xx, addrh, addrm, addrl);
+	for (i = 0; i < count && i < 16; i++)
+		sp_reg_write(anx78xx, TX_P0, SP_DP_BUF_DATA0_REG + i, buf[i]);
+
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, SP_AUX_EN);
+	return sp_wait_aux_op_finish(anx78xx);
+}
+
+static void sp_block_power_ctrl(struct anx78xx *anx78xx,
+				enum sp_tx_power_block sp_tx_pd_block,
+				bool power)
+{
+	if (power)
+		sp_reg_clear_bits(anx78xx, TX_P2, SP_POWERDOWN_CTRL_REG,
+				  sp_tx_pd_block);
+	else
+		sp_reg_set_bits(anx78xx, TX_P2, SP_POWERDOWN_CTRL_REG,
+				sp_tx_pd_block);
+
+	dev_dbg(&anx78xx->client->dev,
+		"sp_tx_power_on: %.2x\n", sp_tx_pd_block);
+}
+
+static void sp_variable_init(void)
+{
+	sp.hdcp_enabled = false;
+
+	sp.tx_system_state = STATE_WAITING_CABLE_PLUG;
+
+	sp.read_edid_flag = false;
+
+	memset(sp.edid_blocks, 0, sizeof(*sp.edid_blocks));
+
+	sp.tx_lt_state = LT_INIT;
+	sp.hdcp_state = HDCP_CAPABLE_CHECK;
+	sp.repeater_state = HDCP_DONE;
+	sp.tx_vo_state = VO_WAIT_VIDEO_STABLE;
+	sp.tx_ao_state = AO_INIT;
+	sp.changed_bandwidth = SP_LINK_5P4G;
+
+	sp.hdcp_error_count = 0;
+	sp.hdcp_fail_count = 0;
+	sp.audio_stable_count = 0;
+
+	sp.tx_test_lt = false;
+	sp.tx_test_bw = 0;
+	sp.tx_test_edid = false;
+}
+
+static void sp_hdmi_tmds_phy_initialization(struct anx78xx *anx78xx)
+{
+	sp_reg_write(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 1, 0x90);
+	sp_reg_write(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 2, 0xa9);
+	sp_reg_write(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 6, 0x92);
+	sp_reg_write(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 7, 0x80);
+	sp_reg_write(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 20, 0xf2);
+}
+
+static void sp_hdmi_initialization(struct anx78xx *anx78xx)
+{
+	sp_reg_write(anx78xx, RX_P0, SP_HDMI_MUTE_CTRL_REG, SP_AUD_MUTE |
+		     SP_VID_MUTE);
+	sp_reg_set_bits(anx78xx, RX_P0, SP_CHIP_CTRL_REG, SP_MAN_HDMI5V_DET |
+			SP_PLLLOCK_CKDT_EN | SP_DIGITAL_CKDT_EN);
+
+	sp_reg_set_bits(anx78xx, RX_P0, SP_SOFTWARE_RESET1_REG,
+			SP_HDCP_MAN_RST | SP_SW_MAN_RST | SP_TMDS_RST |
+			SP_VIDEO_RST);
+	sp_reg_clear_bits(anx78xx, RX_P0, SP_SOFTWARE_RESET1_REG,
+			  SP_HDCP_MAN_RST | SP_SW_MAN_RST | SP_TMDS_RST |
+			  SP_VIDEO_RST);
+
+	/* Sync detect change, GP set mute */
+	sp_reg_set_bits(anx78xx, RX_P0, SP_AUD_EXCEPTION_ENABLE_BASE + 1,
+			BIT(5) | BIT(6));
+	sp_reg_set_bits(anx78xx, RX_P0, SP_AUD_EXCEPTION_ENABLE_BASE + 3,
+			SP_AEC_EN21);
+	sp_reg_set_bits(anx78xx, RX_P0, SP_AUDVID_CTRL_REG, SP_AVC_EN |
+			SP_AAC_OE | SP_AAC_EN);
+
+	sp_reg_clear_bits(anx78xx, RX_P0, SP_SYSTEM_POWER_DOWN1_REG,
+			  SP_PWDN_CTRL);
+
+	sp_reg_set_bits(anx78xx, RX_P0, SP_VID_DATA_RANGE_CTRL_REG,
+			SP_R2Y_INPUT_LIMIT);
+	sp_reg_write(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 22, 0xc4);
+	sp_reg_write(anx78xx, RX_P0, SP_TMDS_CTRL_BASE + 23, 0x18);
+
+	/* Enable DDC stretch */
+	sp_reg_write(anx78xx, TX_P0, SP_DP_EXTRA_I2C_DEV_ADDR_REG,
+		     SP_I2C_EXTRA_ADDR);
+
+	sp_hdmi_tmds_phy_initialization(anx78xx);
+	sp_hdmi_set_hpd(anx78xx, false);
+	sp_hdmi_set_termination(anx78xx, false);
+}
+
+static void sp_xtal_clk_sel(struct anx78xx *anx78xx)
+{
+	u8 val;
+
+	sp_reg_update_bits(anx78xx, TX_P2, SP_ANALOG_DEBUG2_REG,
+			   SP_XTAL_FRQ | SP_FORCE_SW_OFF_BYPASS,
+			   SP_XTAL_FRQ_27M);
+
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL3_REG,
+		     XTAL_CLK & SP_WAIT_COUNTER_7_0_MASK);
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL4_REG,
+		     ((XTAL_CLK & 0xff00) >> 2) | (XTAL_CLK / 10));
+
+	sp_reg_write(anx78xx, TX_P0, SP_I2C_GEN_10US_TIMER0_REG,
+		     XTAL_CLK & 0xff);
+	sp_reg_write(anx78xx, TX_P0, SP_I2C_GEN_10US_TIMER1_REG,
+		     (XTAL_CLK & 0xff00) >> 8);
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_MISC_CTRL_REG,
+		     XTAL_CLK / 10 - 1);
+
+	sp_reg_read(anx78xx, RX_P0, SP_HDMI_US_TIMER_CTRL_REG, &val);
+	sp_reg_write(anx78xx, RX_P0, SP_HDMI_US_TIMER_CTRL_REG,
+		     (val & SP_MS_TIMER_MARGIN_10_8_MASK) |
+		     ((((XTAL_CLK / 10) >> 1) - 2) << 3));
+}
+
+void sp_tx_initialization(struct anx78xx *anx78xx)
+{
+	/* set terminal resistor to 50 ohm */
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, 0x30);
+	/* enable aux double diff output */
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, 0x08);
+
+	if (!sp_hdcp_repeater_mode(anx78xx)) {
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_HDCP_CTRL_REG,
+				  SP_AUTO_EN | SP_AUTO_START);
+		sp_reg_write(anx78xx, TX_P0, SP_OTP_KEY_PROTECT1_REG,
+			     SP_OTP_PSW1);
+		sp_reg_write(anx78xx, TX_P0, SP_OTP_KEY_PROTECT2_REG,
+			     SP_OTP_PSW2);
+		sp_reg_write(anx78xx, TX_P0, SP_OTP_KEY_PROTECT3_REG,
+			     SP_OTP_PSW3);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_HDCP_KEY_COMMAND_REG,
+				SP_DISABLE_SYNC_HDCP);
+	}
+
+	sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL8_REG, SP_VID_VRES_TH);
+
+	/*
+	 * DP HDCP auto authentication wai timer (when downstream starts to
+	 * auth, DP side will wait for this period then do auth automatically)
+	 */
+	sp_reg_write(anx78xx, TX_P0, SP_HDCP_AUTO_TIMER_REG, 0x00);
+
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_HDCP_CTRL_REG, SP_LINK_POLLING);
+
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_LINK_DEBUG_CTRL_REG,
+			SP_M_VID_DEBUG);
+	sp_reg_set_bits(anx78xx, TX_P2, SP_ANALOG_DEBUG2_REG,
+			SP_POWERON_TIME_1P5MS);
+
+	sp_xtal_clk_sel(anx78xx);
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_DEFER_CTRL_REG,
+		     SP_DEFER_CTRL_EN | 0x0c);
+
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_POLLING_CTRL_REG,
+			SP_AUTO_POLLING_DISABLE);
+	/*
+	 * Short the link integrity check timer to speed up bstatus
+	 * polling for HDCP CTS item 1A-07
+	 */
+	sp_reg_write(anx78xx, TX_P0, SP_HDCP_LINK_CHECK_TIMER_REG, 0x1d);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_MISC_CTRL_REG,
+			SP_EQ_TRAINING_LOOP);
+
+	/* power down the main link by default */
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_ANALOG_POWER_DOWN_REG,
+			SP_CH0_PD);
+
+	sp_reg_write(anx78xx, TX_P2, SP_INT_CTRL_REG, 0x01);
+
+	sp_link_phy_initialization(anx78xx);
+	sp_gen_m_clk_with_downspreading(anx78xx);
+
+	sp.down_sample_en = false;
+}
+
+/*
+ * Check if it is ANALOGIX dongle.
+ */
+static const u8 anx_oui[3] = {0x00, 0x22, 0xb9};
+
+static bool is_anx_dongle(struct anx78xx *anx78xx)
+{
+	u8 buf[3];
+
+	/* DPCD 400 show ANX-dongle */
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x04, 0x00, 3, buf);
+	if (!memcmp(buf, anx_oui, 3))
+		return true;
+
+	/* 0x0500~0x0502: BRANCH_IEEE_OUI */
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x00, 3, buf);
+	if (!memcmp(buf, anx_oui, 3))
+		return true;
+
+	return false;
+}
+
+static const u8 anx7750[4] = {0x37, 0x37, 0x35, 0x30};
+
+static u8 sp_get_rx_bw(struct anx78xx *anx78xx)
+{
+	u8 bandwidth, max_link_rate;
+	u8 buf[4];
+
+	bandwidth = 0;
+	/*
+	 * When ANX dongle is connected, if CHIP_ID=0x7750 the bandwidth is
+	 * 6.75G because ANX7750 DPCD 0x052x is not available.
+	 */
+	if (is_anx_dongle(anx78xx)) {
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x03, 4, buf);
+		if (!memcmp(buf, anx7750, sizeof(anx7750)))
+			bandwidth = SP_LINK_6P75G;
+		else
+			sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x21,
+					      1, &bandwidth);
+	}
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, SP_DPCD_MAX_LINK_RATE,
+			      1, &max_link_rate);
+	if (bandwidth < max_link_rate)
+		bandwidth = max_link_rate;
+
+	return bandwidth;
+}
+
+static bool sp_get_dp_connection(struct anx78xx *anx78xx)
+{
+	u8 val;
+
+	if (sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, SP_DPCD_SINK_COUNT, 1,
+				  &val))
+		return false;
+
+	if (!(val & 0x1f))
+		return false;
+
+	if (sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x04, 1, &val))
+		return false;
+
+	if (val & 0x20) {
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x06, 0x00, 1, &val);
+		/*
+		 * Bit 5 = SET_DN_DEVICE_DP_PWR_5V
+		 * Bit 6 = SET_DN_DEVICE_DP_PWR_12V
+		 * Bit 7 = SET_DN_DEVICE_DP_PWR_18V
+		 */
+		val &= 0x1f;
+		val |= 0x20;
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x06, 0x00, 1, &val);
+	}
+
+	return true;
+}
+
+/******************start EDID process********************/
+static void sp_enable_video_input(struct anx78xx *anx78xx, bool enable)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_VID_CTRL1_REG, &val);
+	if (enable) {
+		sp_reg_set_bits(anx78xx, TX_P2, SP_VID_CTRL1_REG, SP_VIDEO_EN);
+		dev_dbg(dev, "Slimport video is enabled!\n");
+	} else {
+		sp_reg_clear_bits(anx78xx, TX_P2, SP_VID_CTRL1_REG,
+				  SP_VIDEO_EN);
+		dev_dbg(dev, "Slimport video is disabled!\n");
+	}
+}
+
+static u8 sp_get_edid_bandwidth(u8 *data)
+{
+	u16 pclk;
+
+	pclk = ((u16)data[1] << 8) | ((u16)data[0] & 0xff);
+	if (pclk <= 5300)
+		return SP_LINK_1P62G;
+	else if (pclk <= 8900)
+		return SP_LINK_2P7G;
+	else if (pclk <= 18000)
+		return SP_LINK_5P4G;
+	else
+		return SP_LINK_6P75G;
+}
+
+static u8 sp_parse_edid_to_get_bandwidth(struct anx78xx *anx78xx)
+{
+	int i;
+	u8 bandwidth, temp;
+
+	bandwidth = SP_LINK_1P62G;
+	for (i = 0; i < 4; i++) {
+		if (sp.edid_blocks[0x36 + 0x12 * i] == 0)
+			break;
+		temp = sp_get_edid_bandwidth(sp.edid_blocks + 0x36 + 0x12 * i);
+		dev_dbg(&anx78xx->client->dev, "bandwidth via EDID : %x\n",
+			temp);
+		if (bandwidth < temp)
+			bandwidth = temp;
+		if (bandwidth >= SP_LINK_6P75G)
+			break;
+	}
+
+	return bandwidth;
+}
+
+u8 sp_get_link_bandwidth(struct anx78xx *anx78xx)
+{
+	u8 bandwidth, max_bandwidth;
+
+	bandwidth = sp_get_rx_bw(anx78xx);
+	max_bandwidth = sp_parse_edid_to_get_bandwidth(anx78xx);
+	if (bandwidth > max_bandwidth)
+		return max_bandwidth;
+
+	return bandwidth;
+}
+
+static int sp_tx_aux_wr(struct anx78xx *anx78xx, u8 offset)
+{
+	sp_reg_write(anx78xx, TX_P0, SP_DP_BUF_DATA0_REG, offset);
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, 0x04);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, SP_AUX_EN);
+
+	return sp_wait_aux_op_finish(anx78xx);
+}
+
+static int sp_tx_aux_rd(struct anx78xx *anx78xx, u8 len)
+{
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, len);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, SP_AUX_EN);
+
+	return sp_wait_aux_op_finish(anx78xx);
+}
+
+static u8 sp_tx_get_edid_block(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+
+	sp_tx_aux_wr(anx78xx, 0x7e);
+	sp_tx_aux_rd(anx78xx, 0x01);
+	sp_reg_read(anx78xx, TX_P0, SP_DP_BUF_DATA0_REG, &val);
+	dev_dbg(dev, "EDID Block = %d\n", val + 1);
+
+	if (val > 3)
+		val = 1;
+	return val;
+}
+
+static int sp_edid_read(struct anx78xx *anx78xx, u8 offset,
+			u8 *buf)
+{
+	u8 data_cnt, errcnt;
+	u8 val, ret;
+
+	sp_tx_aux_wr(anx78xx, offset);
+	sp_tx_aux_rd(anx78xx, 0xf5);
+	data_cnt = 0;
+	errcnt = 0;
+
+	while (data_cnt < 16) {
+		sp_reg_read(anx78xx, TX_P0, SP_BUF_DATA_COUNT_REG, &val);
+
+		if (val & 0x1f) {
+			data_cnt = data_cnt + (val & 0x1f);
+			do {
+				sp_reg_read(anx78xx, TX_P0,
+					    SP_DP_BUF_DATA0_REG + val - 1,
+					    &buf[val - 1]);
+			} while (--val);
+		} else {
+			if (errcnt++ <= 2) {
+				sp_reset_aux(anx78xx);
+				val = 0x05 | ((0x0f - data_cnt) << 4);
+				sp_tx_aux_rd(anx78xx, val);
+			} else {
+				return -1;
+			}
+		}
+	}
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, 0x01);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG,
+			SP_ADDR_ONLY | SP_AUX_EN);
+	ret = sp_wait_aux_op_finish(anx78xx);
+	sp_addronly_set(anx78xx, false);
+
+	return ret;
+}
+
+static void sp_tx_edid_read_initial(struct anx78xx *anx78xx)
+{
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_ADDR_7_0_REG, 0x50);
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_ADDR_15_8_REG, 0);
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_AUX_ADDR_19_16_REG, 0xf0);
+}
+
+static int sp_seg_edid_read(struct anx78xx *anx78xx,
+			    u8 segment, u8 offset)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val, errcnt;
+	int i;
+
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, 0x04);
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_ADDR_7_0_REG, 0x30);
+
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG,
+			SP_ADDR_ONLY | SP_AUX_EN);
+
+	if (sp_wait_aux_op_finish(anx78xx))
+		return -1;
+
+	sp_reg_write(anx78xx, TX_P0, SP_DP_BUF_DATA0_REG, segment);
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, 0x04);
+
+	sp_reg_update_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG,
+			   SP_ADDR_ONLY | SP_AUX_EN, SP_AUX_EN);
+
+	errcnt = 10;
+	while (errcnt--) {
+		sp_reg_read(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, &val);
+		if (!(val & SP_AUX_EN))
+			break;
+		usleep_range(1000, 2000);
+	}
+
+	if (!errcnt) {
+		dev_err(dev, "read SP_DP_AUX_CH_CTRL2_REG failed.\n");
+		sp_reset_aux(anx78xx);
+		return -1;
+	}
+
+	sp_reg_write(anx78xx, TX_P0, SP_AUX_ADDR_7_0_REG, 0x50);
+	sp_tx_aux_wr(anx78xx, offset);
+	sp_tx_aux_rd(anx78xx, 0xf5);
+
+	for (i = 0; i < 16; i++) {
+		errcnt = 10;
+		while (errcnt--) {
+			sp_reg_read(anx78xx, TX_P0, SP_BUF_DATA_COUNT_REG,
+				    &val);
+			if (val & 0x1f)
+				break;
+			usleep_range(2000, 4000);
+		}
+
+		if (!errcnt) {
+			dev_err(dev, "read SP_BUF_DATA_COUNT_REG failed.\n");
+			sp_reset_aux(anx78xx);
+			return -1;
+		}
+
+		sp_reg_read(anx78xx, TX_P0, SP_DP_BUF_DATA0_REG + i, &val);
+	}
+
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, 0x01);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG,
+			SP_ADDR_ONLY | SP_AUX_EN);
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG,
+			  SP_ADDR_ONLY);
+	sp_reg_read(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, &val);
+
+	errcnt = 10;
+	while (errcnt--) {
+		sp_reg_read(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, &val);
+		if (!(val & SP_AUX_EN))
+			break;
+		usleep_range(1000, 2000);
+	}
+
+	if (!errcnt) {
+		dev_err(dev, "read SP_DP_AUX_CH_CTRL2_REG failed.\n");
+		sp_reset_aux(anx78xx);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sp_edid_block_checksum(const u8 *raw_edid)
+{
+	int i;
+	u8 csum = 0;
+
+	for (i = 0; i < EDID_LENGTH; i++)
+		csum += raw_edid[i];
+
+	return csum;
+}
+
+static int sp_tx_edid_read(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val, blocks, offset = 0;
+	u8 buf[16];
+	int i, j, count;
+
+	sp_tx_edid_read_initial(anx78xx);
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, 0x04);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, 0x03);
+
+	if (sp_wait_aux_op_finish(anx78xx))
+		return -1;
+
+	sp_addronly_set(anx78xx, false);
+
+	blocks = sp_tx_get_edid_block(anx78xx);
+	/* for every block */
+	for (count = 0; count < blocks; count++) {
+		switch (count) {
+		case 0:
+		case 1:
+			for (i = 0; i < 8; i++) {
+				offset = (i + count * 8) * 16;
+				if (sp_edid_read(anx78xx, offset, buf))
+					return -1;
+				for (j = 0; j < 16; j++)
+					sp.edid_blocks[offset + j] = buf[j];
+			}
+			break;
+		case 2:
+		case 3:
+			offset = (count == 2) ? 0x00 : 0x80;
+			for (j = 0; j < 8; j++) {
+				if (sp_seg_edid_read(anx78xx, count / 2,
+						     offset))
+					return -1;
+				offset = offset + 0x10;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	sp_reset_aux(anx78xx);
+
+	if (!drm_edid_block_valid(sp.edid_blocks, 0, true, NULL)) {
+		dev_err(dev, "EDID block is invalid\n");
+		return -1;
+	}
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x18, 1, &val);
+	if (val & 0x04) {
+		val = sp_edid_block_checksum(sp.edid_blocks);
+		dev_dbg(dev, "EDID checksum is %d\n", val);
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x61, 1, &val);
+		sp.tx_test_edid = true;
+		val = 0x04;
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, &val);
+		dev_dbg(dev, "test EDID done\n");
+	}
+
+	return 0;
+}
+
+static bool sp_check_with_pre_edid(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 i;
+	u8 buf[16];
+	bool ret = false;
+
+	sp_tx_edid_read_initial(anx78xx);
+	sp_reg_write(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL1_REG, 0x04);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUX_CH_CTRL2_REG, 0x03);
+
+	if (sp_wait_aux_op_finish(anx78xx))
+		goto return_point;
+
+	sp_addronly_set(anx78xx, false);
+
+	if (sp_edid_read(anx78xx, 0x70, buf))
+		goto return_point;
+
+	for (i = 0; i < 16; i++) {
+		if (sp.edid_blocks[0x70 + i] != buf[i]) {
+			dev_dbg(dev, "%s\n",
+				"different checksum and blocks num\n");
+			goto return_point;
+		}
+	}
+
+	if (sp_edid_read(anx78xx, 0x08, buf))
+		goto return_point;
+
+	for (i = 0; i < 16; i++) {
+		if (sp.edid_blocks[i + 8] != buf[i]) {
+			dev_dbg(dev, "different edid information\n");
+			goto return_point;
+		}
+	}
+
+	ret = true;
+return_point:
+	sp_reset_aux(anx78xx);
+
+	return ret;
+}
+
+static bool sp_edid_process(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 bw, edid_bw, val;
+	int i;
+
+	if (sp.read_edid_flag) {
+		if (!sp_check_with_pre_edid(anx78xx))
+			sp.read_edid_flag = false;
+	} else {
+		if (sp_tx_edid_read(anx78xx)) {
+			dev_err(dev, "EDID corruption!\n");
+			return false;
+		}
+	}
+
+	/* Release the HPD after the OTP loaddown */
+	for (i = 0; i < 10; i++) {
+		sp_reg_read(anx78xx, TX_P0, SP_HDCP_KEY_STATUS_REG, &val);
+		if (val & 0x01)
+			break;
+
+		dev_dbg(dev, "waiting HDCP KEY loaddown\n");
+		usleep_range(1000, 2000);
+	}
+
+	sp_reg_write(anx78xx, RX_P0, SP_INT_MASK_BASE + 1,
+		     SP_HDMI_DVI | SP_CKDT_CHG | SP_SCDT_CHG |
+		     SP_CABLE_PLUG_CHG);
+
+	if (!sp_hdcp_repeater_mode(anx78xx)) {
+		sp_hdmi_set_hpd(anx78xx, true);
+		sp_hdmi_set_termination(anx78xx, true);
+	}
+
+	bw = sp_get_rx_bw(anx78xx);
+	dev_dbg(dev, "RX BW %x\n", bw);
+
+	edid_bw = sp_parse_edid_to_get_bandwidth(anx78xx);
+	if (bw <= edid_bw)
+		edid_bw = bw;
+
+	dev_dbg(dev, "set link bw in edid %x\n", edid_bw);
+	sp.changed_bandwidth = edid_bw;
+
+	return true;
+}
+
+/******************End EDID process********************/
+
+/******************start Link training process********************/
+static void sp_lvttl_bit_mapping(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val, colorspace;
+	u8 vid_bit;
+
+	vid_bit = 0;
+	sp_reg_read(anx78xx, RX_P1, SP_AVI_INFOFRAME_DATA_BASE, &colorspace);
+	colorspace &= SP_AVI_COLOR_F_MASK;
+	colorspace >>= SP_AVI_COLOR_F_SHIFT;
+
+	sp_reg_read(anx78xx, RX_P0, SP_VIDEO_STATUS_REG, &val);
+	switch ((val & SP_COLOR_DEPTH_MASK) >> SP_COLOR_DEPTH_SHIFT) {
+	default:
+	case HDMI_LEGACY:
+		val = SP_IN_BPC_8BIT;
+		vid_bit = 0;
+		break;
+	case HDMI_24BIT:
+		val = SP_IN_BPC_8BIT;
+		if (colorspace == SP_COLORSPACE_YCBCR422)
+			vid_bit = 5;
+		else
+			vid_bit = 1;
+		break;
+	case HDMI_30BIT:
+		val = SP_IN_BPC_10BIT;
+		if (colorspace == SP_COLORSPACE_YCBCR422)
+			vid_bit = 6;
+		else
+			vid_bit = 2;
+		/*
+		 * For 10bit video must be set this value to 12bit by
+		 * someone
+		 */
+		if (sp.down_sample_en)
+			vid_bit = 3;
+		break;
+	case HDMI_36BIT:
+		val = SP_IN_BPC_12BIT;
+		if (colorspace == SP_COLORSPACE_YCBCR422)
+			vid_bit = 6;
+		else
+			vid_bit = 3;
+		break;
+	}
+
+	/*
+	 * For down sample video (12bit, 10bit ---> 8bit),
+	 * this register doesn't change
+	 */
+	if (!sp.down_sample_en)
+		sp_reg_update_bits(anx78xx, TX_P2, SP_VID_CTRL2_REG,
+				   SP_IN_BPC_MASK | SP_IN_COLOR_F_MASK,
+				   (val << SP_IN_BPC_SHIFT) | colorspace);
+
+	sp_reg_write(anx78xx, TX_P2, SP_BIT_CTRL_SPECIFIC_REG,
+		     SP_ENABLE_BIT_CTRL | vid_bit << SP_BIT_CTRL_SELECT_SHIFT);
+
+	if (sp.tx_test_edid) {
+		/* Set color depth to 6 bpc (18 bpp) for link cts */
+		sp_reg_update_bits(anx78xx, TX_P2, SP_VID_CTRL2_REG,
+				   SP_IN_BPC_MASK, SP_IN_BPC_6BIT);
+		sp.tx_test_edid = false;
+		dev_dbg(dev, "color space is set to 6 bpc (18 bpp)\n");
+	}
+
+	if (colorspace) {
+		/*
+		 * Set video values to default of channel 0, 1 and 2 for HDCP
+		 * embedded "blue screen" when HDCP authentication failed.
+		 */
+		sp_reg_write(anx78xx, TX_P0, SP_HDCP_VID0_BLUE_SCREEN_REG,
+			     0x80);
+		sp_reg_write(anx78xx, TX_P0, SP_HDCP_VID1_BLUE_SCREEN_REG,
+			     0x00);
+		sp_reg_write(anx78xx, TX_P0, SP_HDCP_VID2_BLUE_SCREEN_REG,
+			     0x80);
+	} else {
+		sp_reg_write(anx78xx, TX_P0, SP_HDCP_VID0_BLUE_SCREEN_REG,
+			     0x00);
+		sp_reg_write(anx78xx, TX_P0, SP_HDCP_VID0_BLUE_SCREEN_REG,
+			     0x00);
+		sp_reg_write(anx78xx, TX_P0, SP_HDCP_VID0_BLUE_SCREEN_REG,
+			     0x00);
+	}
+}
+
+static unsigned long sp_pclk_calc(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	unsigned long str_plck;
+	u16 vid_counter;
+	u8 val;
+
+	sp_reg_read(anx78xx, RX_P0, SP_PCLK_HIGHRES_CNT_BASE + 2, &val);
+	vid_counter = val << 8;
+	sp_reg_read(anx78xx, RX_P0, SP_PCLK_HIGHRES_CNT_BASE + 1, &val);
+	vid_counter |= val;
+	str_plck = (vid_counter * XTAL_CLK) >> 12;
+	dev_dbg(dev, "pixel clock is %ld.%ld\n", str_plck / 10, str_plck % 10);
+	return str_plck;
+}
+
+static u8 sp_tx_bw_lc_sel(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	unsigned long pixel_clk;
+	u8 link, val;
+
+	pixel_clk = sp_pclk_calc(anx78xx);
+
+	sp_reg_read(anx78xx, RX_P0, SP_VIDEO_STATUS_REG, &val);
+	switch ((val & SP_COLOR_DEPTH_MASK) >> SP_COLOR_DEPTH_SHIFT) {
+	case HDMI_LEGACY:
+	case HDMI_24BIT:
+	default:
+		break;
+	case HDMI_30BIT:
+		pixel_clk = (pixel_clk * 5) >> 2;
+		break;
+	case HDMI_36BIT:
+		pixel_clk = (pixel_clk * 3) >> 1;
+		break;
+	}
+
+	dev_dbg(dev, "pixel clock is %ld.%ld\n", pixel_clk / 10,
+		pixel_clk % 10);
+
+	sp.down_sample_en = false;
+	if (pixel_clk <= 530) {
+		link = SP_LINK_1P62G;
+	} else if (pixel_clk <= 890) {
+		link = SP_LINK_2P7G;
+	} else if (pixel_clk <= 1800) {
+		link = SP_LINK_5P4G;
+	} else {
+		link = SP_LINK_6P75G;
+		if (pixel_clk > 2240)
+			sp.down_sample_en = true;
+	}
+
+	if (sp_get_link_bw(anx78xx) != link) {
+		sp.changed_bandwidth = link;
+		dev_dbg(dev,
+			"different bandwidth between sink and video %.2x",
+			link);
+		return -1;
+	}
+	return 0;
+}
+
+static void sp_downspeading_enable(struct anx78xx *anx78xx, bool enable)
+{
+	u8 val;
+
+	sp_reg_read(anx78xx, TX_P0, SP_DP_DOWNSPREADING_CTRL1_REG, &val);
+
+	if (enable) {
+		val |= SP_TX_SSC_DOWNSPREADING;
+		sp_reg_write(anx78xx, TX_P0, SP_DP_DOWNSPREADING_CTRL1_REG,
+			     val);
+
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x01,
+				      SP_DPCD_DOWNSPREADING_CTRL, 1, &val);
+		val |= SP_SPREAD_AMPLITUDE;
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x01,
+				       SP_DPCD_DOWNSPREADING_CTRL, 1, &val);
+	} else {
+		val &= ~SP_TX_SSC_DISABLE;
+		sp_reg_write(anx78xx, TX_P0, SP_DP_DOWNSPREADING_CTRL1_REG,
+			     val);
+
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x01,
+				      SP_DPCD_DOWNSPREADING_CTRL, 1, &val);
+		val &= ~SP_SPREAD_AMPLITUDE;
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x01,
+				       SP_DPCD_DOWNSPREADING_CTRL, 1, &val);
+	}
+}
+
+static void sp_config_ssc(struct anx78xx *anx78xx,
+			  enum sp_ssc_dep sscdep)
+{
+	sp_reg_write(anx78xx, TX_P0, SP_DP_DOWNSPREADING_CTRL1_REG, 0x0);
+	sp_reg_write(anx78xx, TX_P0, SP_DP_DOWNSPREADING_CTRL1_REG, sscdep);
+	sp_downspeading_enable(anx78xx, true);
+}
+
+static void sp_enhancemode_set(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, SP_DPCD_MAX_LANE_COUNT, 1,
+			      &val);
+
+	if (val & SP_ENHANCED_FRAME_CAP) {
+		sp_reg_set_bits(anx78xx, TX_P0, SP_DP_SYSTEM_CTRL_BASE + 4,
+				SP_ENHANCED_MODE);
+
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x01,
+				      SP_DPCD_LANE_COUNT_SET, 1, &val);
+		val |= SP_ENHANCED_FRAME_EN;
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x01,
+				       SP_DPCD_LANE_COUNT_SET, 1, &val);
+
+		dev_dbg(dev, "enhance mode enabled\n");
+	} else {
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_SYSTEM_CTRL_BASE + 4,
+				  SP_ENHANCED_MODE);
+
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x01,
+				      SP_DPCD_LANE_COUNT_SET, 1, &val);
+
+		val &= ~SP_ENHANCED_FRAME_EN;
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x01,
+				       SP_DPCD_LANE_COUNT_SET, 1, &val);
+
+		dev_dbg(dev, "enhance mode disabled\n");
+	}
+}
+
+static u16 sp_link_err_check(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u16 err;
+	u8 buf[2];
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x10, 2, buf);
+	usleep_range(5000, 10000);
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x10, 2, buf);
+
+	if (buf[1] & 0x80) {
+		err = ((buf[1] & 0x7f) << 8) + buf[0];
+		dev_err(dev, "error of Lane %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static bool sp_lt_finish(struct anx78xx *anx78xx)
+{
+	u8 val;
+	struct device *dev = &anx78xx->client->dev;
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x02, 1, &val);
+
+	if ((val & 0x07) != 0x07) {
+		dev_dbg(dev, "Lane0 status error %.2x\n", val & 0x07);
+		sp.tx_lt_state = LT_ERROR;
+		return false;
+	}
+
+	/*
+	 * If there is link error, adjust pre-emphasis to check error
+	 * again. If there is no error, keep the setting, otherwise
+	 * use 400mv0db
+	 */
+	if (sp.tx_test_lt) {
+		sp.tx_test_lt = false;
+		sp.tx_lt_state = LT_INIT;
+		return true;
+	}
+
+	if (sp_link_err_check(anx78xx)) {
+		sp_reg_read(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG, &val);
+		if (!(val & SP_MAX_PRE_REACH)) {
+			/* Increase one pre-level */
+			sp_reg_write(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG,
+				     val + 0x08);
+			/*
+			 * If error still exists, return to the link training
+			 * value
+			 */
+			if (sp_link_err_check(anx78xx))
+				sp_reg_write(anx78xx, TX_P0,
+					     SP_DP_LANE0_LT_CTRL_REG, val);
+		}
+	}
+
+	val = sp_get_link_bw(anx78xx);
+	if (val != sp.changed_bandwidth) {
+		dev_dbg(dev, "bandwidth changed, cur:%.2x, per:%.2x\n", val,
+			sp.changed_bandwidth);
+		sp.tx_lt_state = LT_ERROR;
+		return false;
+	}
+
+	dev_dbg(dev, "LT succeed, bandwidth: %.2x", val);
+	sp_reg_read(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG, &val);
+	dev_dbg(dev, "Lane0 set to %.2x\n", val);
+	sp.tx_lt_state = LT_INIT;
+
+	if (sp_hdcp_repeater_mode(anx78xx)) {
+		dev_dbg(dev, "HPD set to 1!\n");
+		sp_hdmi_set_hpd(anx78xx, true);
+		sp_hdmi_set_termination(anx78xx, true);
+	}
+
+	/*
+	 * Under low voltage (DVD10 = 0.97V), some chips cannot output video,
+	 * link down interrupt always happens.
+	 */
+	if (sp_link_err_check(anx78xx) > 200) {
+		dev_dbg(dev, "need to reset Serdes FIFO");
+		sp.tx_lt_state = LT_ERROR;
+	} else {
+		return true;
+	}
+
+	return false;
+}
+
+static bool sp_link_training(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val, version;
+
+	switch (sp.tx_lt_state) {
+	case LT_INIT:
+		sp_block_power_ctrl(anx78xx, SP_TX_PWR_VIDEO, 1);
+		sp_video_mute(anx78xx, true);
+		sp_enable_video_input(anx78xx, false);
+		sp.tx_lt_state = LT_WAIT_PLL_LOCK;
+	/* fallthrough */
+	case LT_WAIT_PLL_LOCK:
+		if (!sp_get_pll_lock_status(anx78xx)) {
+			sp_reg_read(anx78xx, TX_P0, SP_DP_PLL_CTRL_REG,
+				    &val);
+
+			val |= SP_PLL_RST;
+			sp_reg_write(anx78xx, TX_P0, SP_DP_PLL_CTRL_REG,
+				     val);
+
+			val &= ~SP_PLL_RST;
+			sp_reg_write(anx78xx, TX_P0, SP_DP_PLL_CTRL_REG,
+				     val);
+
+			dev_dbg(dev, "PLL not lock!\n");
+			break;
+		} else {
+			sp.tx_lt_state = LT_CHECK_LINK_BW;
+		}
+	/* fallthrough */
+	case LT_CHECK_LINK_BW:
+		val = sp_get_rx_bw(anx78xx);
+		if (val < sp.changed_bandwidth) {
+			dev_dbg(dev, "over bandwidth!\n");
+			sp.changed_bandwidth = val;
+			break;
+		} else {
+			sp.tx_lt_state = LT_START;
+		}
+	/* fallthrough */
+	case LT_START:
+		if (sp.tx_test_lt) {
+			sp.changed_bandwidth = sp.tx_test_bw;
+			sp_reg_clear_bits(anx78xx, TX_P2, SP_VID_CTRL2_REG,
+					  0x70);
+		} else {
+			sp_reg_write(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG,
+				     0x00);
+		}
+
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_ANALOG_POWER_DOWN_REG,
+				  SP_CH0_PD);
+
+		sp_config_ssc(anx78xx, SSC_DEP_4000PPM);
+		sp_set_link_bw(anx78xx, sp.changed_bandwidth);
+		sp_enhancemode_set(anx78xx);
+
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x00, 1,
+				      &version);
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x06, 0x00, 1,
+				      &val);
+		if (version >= 0x12)
+			val &= 0xf8;
+		else
+			val &= 0xfc;
+		val |= 0x01;
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x06, 0x00, 1,
+				       &val);
+
+		sp_reg_write(anx78xx, TX_P0, SP_DP_LT_CTRL_REG, SP_LT_EN);
+		sp.tx_lt_state = LT_WAITING_FINISH;
+	/* fallthrough */
+	case LT_WAITING_FINISH:
+		/* Waiting interrupt to change training state. */
+		break;
+	case LT_ERROR:
+		sp_reg_set_bits(anx78xx, TX_P2, SP_RESET_CTRL2_REG,
+				SP_SERDES_FIFO_RST);
+		msleep(20);
+		sp_reg_clear_bits(anx78xx, TX_P2, SP_RESET_CTRL2_REG,
+				  SP_SERDES_FIFO_RST);
+		dev_err(dev, "LT ERROR reset SERDES FIFO");
+		sp.tx_lt_state = LT_INIT;
+		break;
+	case LT_FINISH:
+		if (sp_lt_finish(anx78xx))
+			return true;
+		break;
+	default:
+		break;
+	}
+
+	return false;
+}
+
+/******************End Link training process********************/
+
+/******************Start Output video process********************/
+static bool sp_match_vic_for_bt709(u8 vic)
+{
+	/* Video Identification Code (VIC) for BT709 */
+	return ((vic == 0x04) || (vic == 0x05) || (vic == 0x10) ||
+		(vic == 0x13) || (vic == 0x14) || (vic == 0x1f) ||
+		(vic == 0x20) || (vic == 0x21) || (vic == 0x22) ||
+		(vic == 0x27) || (vic == 0x28) || (vic == 0x29) ||
+		(vic == 0x2e) || (vic == 0x2f) || (vic == 0x3c) ||
+		(vic == 0x3d) || (vic == 0x3e) || (vic == 0x3f) ||
+		(vic == 0x40));
+}
+
+static void sp_set_colorspace(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 colorspace, val;
+
+	if (sp.down_sample_en) {
+		sp_reg_read(anx78xx, RX_P1, SP_AVI_INFOFRAME_DATA_BASE,
+			    &colorspace);
+		colorspace &= SP_AVI_COLOR_F_MASK;
+		colorspace >>= SP_AVI_COLOR_F_SHIFT;
+		if (colorspace == SP_COLORSPACE_YCBCR422) {
+			dev_dbg(dev, "YCbCr4:2:2 ---> PASS THROUGH.\n");
+			sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL6_REG, 0x00);
+			sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL5_REG, 0x00);
+		} else if (colorspace == SP_COLORSPACE_YCBCR444) {
+			dev_dbg(dev, "YCbCr4:4:4 ---> YCbCr4:2:2\n");
+			sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL6_REG,
+				     SP_VIDEO_PROCESS_EN | SP_UP_SAMPLE);
+			sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL5_REG, 0x00);
+		} else if (colorspace == SP_COLORSPACE_RGB) {
+			dev_dbg(dev, "RGB4:4:4 ---> YCbCr4:2:2\n");
+			sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL6_REG,
+				     SP_VIDEO_PROCESS_EN | SP_UP_SAMPLE);
+			sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL5_REG,
+				     SP_CSC_STD_SEL | SP_RANGE_R2Y |
+				     SP_CSPACE_R2Y);
+		}
+		sp_reg_write(anx78xx, TX_P2, SP_VID_CTRL2_REG,
+			     (SP_IN_BPC_8BIT << SP_IN_BPC_SHIFT) | colorspace);
+	} else {
+		sp_reg_read(anx78xx, TX_P2, SP_VID_CTRL2_REG, &colorspace);
+		colorspace &= SP_IN_COLOR_F_MASK;
+
+		/*
+		 * To change the CSC_STD_SEL bit we need to set
+		 * CSPACE_Y2R and CSPACE_ R2Y, otherwise has no
+		 * effect or is undetermined.
+		 */
+		if (colorspace == SP_COLORSPACE_RGB) {
+			sp_reg_clear_bits(anx78xx, TX_P2, SP_VID_CTRL5_REG,
+					  SP_RANGE_Y2R | SP_CSPACE_Y2R |
+					  SP_CSC_STD_SEL);
+			sp_reg_clear_bits(anx78xx, TX_P2, SP_VID_CTRL6_REG,
+					  SP_VIDEO_PROCESS_EN | SP_UP_SAMPLE);
+		} else {
+			/*
+			 * Colorimetric format of input video is YCbCr422
+			 * or YCbCr444
+			 */
+			sp_reg_set_bits(anx78xx, TX_P2, SP_VID_CTRL5_REG,
+					SP_RANGE_Y2R | SP_CSPACE_Y2R);
+
+			sp_reg_read(anx78xx, RX_P1,
+				    SP_AVI_INFOFRAME_DATA_BASE + 3,
+				    &val);
+
+			if (sp_match_vic_for_bt709(val))
+				sp_reg_set_bits(anx78xx, TX_P2,
+						SP_VID_CTRL5_REG,
+						SP_CSC_STD_SEL);
+			else	/* Convert based on BT601 */
+				sp_reg_clear_bits(anx78xx, TX_P2,
+						  SP_VID_CTRL5_REG,
+						  SP_CSC_STD_SEL);
+			/*
+			 * Enable 4:2:2 to 4:4:4 up sample when is required
+			 * and enable video process function.
+			 */
+			if (colorspace == SP_COLORSPACE_YCBCR422)
+				sp_reg_set_bits(anx78xx, TX_P2,
+						SP_VID_CTRL6_REG,
+						SP_VIDEO_PROCESS_EN |
+						SP_UP_SAMPLE);
+			else	/* YCBCR444 */
+				sp_reg_update_bits(anx78xx, TX_P2,
+						   SP_VID_CTRL6_REG,
+						   SP_VIDEO_PROCESS_EN |
+						   SP_UP_SAMPLE,
+						   SP_VIDEO_PROCESS_EN);
+		}
+	}
+}
+
+static void sp_packet_avi_init(struct anx78xx *anx78xx)
+{
+	u8 val;
+	int i;
+
+	sp.tx_packet_avi.infoframe.type = HDMI_INFOFRAME_TYPE_AVI;
+	sp.tx_packet_avi.infoframe.version = 2;
+	sp.tx_packet_avi.infoframe.length = HDMI_AVI_INFOFRAME_SIZE;
+
+	for (i = 0; i < sp.tx_packet_avi.infoframe.length; i++) {
+		sp_reg_read(anx78xx, RX_P1, SP_AVI_INFOFRAME_DATA_BASE + i,
+			    &val);
+		sp.tx_packet_avi.data[i] = val;
+	}
+
+	sp.tx_packet_avi.data[0] &= ~SP_AVI_COLOR_F_MASK;
+}
+
+static void sp_load_packet(struct anx78xx *anx78xx, enum packets_type type)
+{
+	int i;
+
+	switch (type) {
+	case AVI_PACKETS:
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_AVI_TYPE_REG,
+			     sp.tx_packet_avi.infoframe.type);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_AVI_VER_REG,
+			     sp.tx_packet_avi.infoframe.version);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_AVI_LEN_REG,
+			     sp.tx_packet_avi.infoframe.length);
+
+		for (i = 0; i < sp.tx_packet_avi.infoframe.length; i++) {
+			sp_reg_write(anx78xx, TX_P2,
+				     SP_INFOFRAME_AVI_DB0_REG + i,
+				     sp.tx_packet_avi.data[i]);
+		}
+
+		break;
+	case VSI_PACKETS:
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_MPEG_TYPE_REG,
+			     sp.tx_packet_vsi.infoframe.type);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_MPEG_VER_REG,
+			     sp.tx_packet_vsi.infoframe.version);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_MPEG_LEN_REG,
+			     sp.tx_packet_vsi.infoframe.length);
+
+		for (i = 0; i < sp.tx_packet_vsi.infoframe.length; i++) {
+			sp_reg_write(anx78xx, TX_P2,
+				     SP_INFOFRAME_MPEG_DB0_REG + i,
+				     sp.tx_packet_vsi.data[i]);
+		}
+		break;
+	case MPEG_PACKETS:
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_MPEG_TYPE_REG,
+			     sp.tx_packet_mpeg.infoframe.type);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_MPEG_VER_REG,
+			     sp.tx_packet_mpeg.infoframe.version);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_MPEG_LEN_REG,
+			     sp.tx_packet_mpeg.infoframe.length);
+
+		for (i = 0; i < sp.tx_packet_mpeg.infoframe.length; i++) {
+			sp_reg_write(anx78xx, TX_P2,
+				     SP_INFOFRAME_MPEG_DB0_REG + i,
+				     sp.tx_packet_mpeg.data[i]);
+		}
+		break;
+	case AUDIF_PACKETS:
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_AUD_TYPE_REG,
+			     sp.tx_packet_audio.infoframe.type);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_AUD_VER_REG,
+			     sp.tx_packet_audio.infoframe.version);
+		sp_reg_write(anx78xx, TX_P2, SP_INFOFRAME_AUD_LEN_REG,
+			     sp.tx_packet_audio.infoframe.length);
+		for (i = 0; i < sp.tx_packet_audio.infoframe.length; i++) {
+			sp_reg_write(anx78xx, TX_P2,
+				     SP_INFOFRAME_AUD_DB0_REG + i,
+				     sp.tx_packet_audio.data[i]);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void sp_config_packets(struct anx78xx *anx78xx, enum packets_type type)
+{
+	switch (type) {
+	case AVI_PACKETS:
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				  SP_AVI_IF_EN);
+		sp_load_packet(anx78xx, AVI_PACKETS);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_AVI_IF_UD);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_AVI_IF_EN);
+		break;
+	case VSI_PACKETS:
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				  SP_MPEG_IF_EN);
+		sp_load_packet(anx78xx, VSI_PACKETS);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_MPEG_IF_UD);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_MPEG_IF_EN);
+		break;
+	case MPEG_PACKETS:
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				  SP_MPEG_IF_EN);
+		sp_load_packet(anx78xx, MPEG_PACKETS);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_MPEG_IF_UD);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_MPEG_IF_EN);
+		break;
+	case AUDIF_PACKETS:
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				  SP_AUD_IF_EN);
+		sp_load_packet(anx78xx, AUDIF_PACKETS);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_AUD_IF_UP);
+		sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				SP_AUD_IF_EN);
+		break;
+	default:
+		break;
+	}
+}
+
+static bool sp_config_video_output(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+
+	switch (sp.tx_vo_state) {
+	default:
+	case VO_WAIT_VIDEO_STABLE:
+		sp_reg_read(anx78xx, RX_P0, SP_SYSTEM_STATUS_REG, &val);
+		if ((val & SP_TMDS_DE_DET) && (val & SP_TMDS_CLOCK_DET)) {
+			sp_tx_bw_lc_sel(anx78xx);
+			sp_enable_video_input(anx78xx, false);
+			sp_packet_avi_init(anx78xx);
+			sp_config_packets(anx78xx, AVI_PACKETS);
+			sp_set_colorspace(anx78xx);
+			sp_lvttl_bit_mapping(anx78xx);
+			sp_reg_read(anx78xx, RX_P0,
+				    SP_PACKET_RECEIVING_STATUS_REG, &val);
+			if (val & SP_VSI_RCVD)
+				sp_hdmi_new_vsi_int(anx78xx);
+			sp_enable_video_input(anx78xx, true);
+			sp.tx_vo_state = VO_WAIT_TX_VIDEO_STABLE;
+		} else {
+			dev_dbg(dev, "HDMI input video not stable!\n");
+			break;
+		}
+	/* fallthrough */
+	case VO_WAIT_TX_VIDEO_STABLE:
+		/*
+		 * The flag is write clear and can be latched from last
+		 * status. So the first read and write is to clear the
+		 * previous status.
+		 */
+		sp_reg_read(anx78xx, TX_P0, SP_DP_SYSTEM_CTRL_BASE + 2, &val);
+		sp_reg_write(anx78xx, TX_P0, SP_DP_SYSTEM_CTRL_BASE + 2, val);
+
+		sp_reg_read(anx78xx, TX_P0, SP_DP_SYSTEM_CTRL_BASE + 2, &val);
+		if (val & SP_CHA_STA) {
+			dev_dbg(dev, "stream clock not stable!\n");
+			break;
+		} else {
+			/*
+			 * The flag is write clear and can be latched from
+			 * last status. So the first read and write is to
+			 * clear the previous status.
+			 */
+			sp_reg_read(anx78xx, TX_P0,
+				    SP_DP_SYSTEM_CTRL_BASE + 3,
+				    &val);
+			sp_reg_write(anx78xx, TX_P0,
+				     SP_DP_SYSTEM_CTRL_BASE + 3,
+				     val);
+
+			sp_reg_read(anx78xx, TX_P0,
+				    SP_DP_SYSTEM_CTRL_BASE + 3,
+				    &val);
+			if (val & SP_STRM_VALID) {
+				if (sp.tx_test_lt)
+					sp.tx_test_lt = false;
+				sp.tx_vo_state = VO_FINISH;
+			} else {
+				dev_err(dev, "video stream not valid!\n");
+				break;
+			}
+		}
+	/* fallthrough */
+	case VO_FINISH:
+		sp_block_power_ctrl(anx78xx, SP_TX_PWR_AUDIO, false);
+		sp_hdmi_mute_video(anx78xx, false);
+		sp_video_mute(anx78xx, false);
+		sp_show_information(anx78xx);
+		return true;
+	}
+
+	return false;
+}
+
+/******************End Output video process********************/
+
+/******************Start HDCP process********************/
+static inline void sp_hdcp_encryption_disable(struct anx78xx *anx78xx)
+{
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_HDCP_CTRL0_REG, SP_HDCP_ENC_EN);
+}
+
+static inline void sp_hdcp_encryption_enable(struct anx78xx *anx78xx)
+{
+	sp_reg_set_bits(anx78xx, TX_P0, SP_HDCP_CTRL0_REG, SP_HDCP_ENC_EN);
+}
+
+static void sp_hw_hdcp_enable(struct anx78xx *anx78xx)
+{
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_HDCP_CTRL0_REG,
+			  SP_HDCP_ENC_EN | SP_HARD_AUTH_EN);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_HDCP_CTRL0_REG,
+			SP_HARD_AUTH_EN | SP_BKSV_SRM_PASS |
+			SP_KSVLIST_VLD | SP_HDCP_ENC_EN);
+
+	/*
+	 * Set the wait timing value for R0 checking of HDCP first step
+	 * authentication after write AKSV to receiver. Default value is
+	 * 0x64 (100ms).
+	 */
+	sp_reg_write(anx78xx, TX_P0, SP_HDCP_WAIT_R0_TIME_REG, 0xb0);
+
+	/*
+	 * Set the wait timing value for repeater KSVFIFO ready in HDCP first
+	 * step authentication. Default value is 0x9c (4.2s)
+	 */
+	sp_reg_write(anx78xx, TX_P0, SP_HDCP_RPTR_RDY_WAIT_TIME_REG, 0xc8);
+}
+
+static bool sp_hdcp_process(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+
+	switch (sp.hdcp_state) {
+	case HDCP_CAPABLE_CHECK:
+		sp.hdcp_fail_count = 0;
+		if (is_anx_dongle(anx78xx))
+			sp.hdcp_state = HDCP_WAITING_VID_STB;
+		else
+			sp.hdcp_state = HDCP_HW_ENABLE;
+		if (!sp.hdcp_enabled)
+			sp.hdcp_state = HDCP_NOT_SUPPORTED;
+		if (sp.hdcp_state != HDCP_WAITING_VID_STB)
+			break;
+	/* fallthrough */
+	case HDCP_WAITING_VID_STB:
+		msleep(100);
+		sp.hdcp_state = HDCP_HW_ENABLE;
+	/* fallthrough */
+	case HDCP_HW_ENABLE:
+		sp_video_mute(anx78xx, true);
+		sp_clean_hdcp_status(anx78xx);
+		sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, false);
+		msleep(20);
+		sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, true);
+		sp_reg_write(anx78xx, TX_P2, SP_COMMON_INT_MASK_BASE + 2,
+			     0x01);
+		msleep(50);
+		sp_hw_hdcp_enable(anx78xx);
+		sp.hdcp_state = HDCP_WAITING_FINISH;
+	/* fallthrough */
+	case HDCP_WAITING_FINISH:
+		break;
+	case HDCP_FINISH:
+		sp_hdcp_encryption_enable(anx78xx);
+		sp_hdmi_mute_video(anx78xx, false);
+		sp_video_mute(anx78xx, false);
+		sp.hdcp_state = HDCP_CAPABLE_CHECK;
+		dev_dbg(dev, "HDCP authentication pass\n");
+		return true;
+	case HDCP_FAILED:
+		if (sp.hdcp_fail_count > 5) {
+			sp_reg_hardware_reset(anx78xx);
+			sp.hdcp_state = HDCP_CAPABLE_CHECK;
+			sp.hdcp_fail_count = 0;
+			dev_dbg(dev, "HDCP authentication failed\n");
+		} else {
+			sp.hdcp_fail_count++;
+			sp.hdcp_state = HDCP_WAITING_VID_STB;
+		}
+		break;
+	default:
+	case HDCP_NOT_SUPPORTED:
+		dev_dbg(dev, "sink is not capable HDCP\n");
+		sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, false);
+		sp_video_mute(anx78xx, false);
+		sp.hdcp_state = HDCP_CAPABLE_CHECK;
+		return true;
+	}
+
+	return false;
+}
+
+/******************End HDCP process********************/
+
+/******************Start Audio process********************/
+static void sp_packet_audio_init(struct anx78xx *anx78xx)
+{
+	int i;
+	u8 val;
+
+	sp.tx_packet_audio.infoframe.type = HDMI_INFOFRAME_TYPE_AUDIO;
+	sp.tx_packet_audio.infoframe.version = 1;
+	sp.tx_packet_audio.infoframe.length = HDMI_AUDIO_INFOFRAME_SIZE;
+
+	for (i = 0; i < sp.tx_packet_audio.infoframe.length; i++) {
+		sp_reg_read(anx78xx, RX_P1, SP_AUD_INFOFRAME_DATA_BASE + i,
+			    &val);
+		sp.tx_packet_audio.data[i] = val;
+	}
+}
+
+static void sp_enable_audio_output(struct anx78xx *anx78xx, bool enable)
+{
+	u8 val;
+
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_AUDIO_CTRL_REG, SP_AUD_EN);
+	if (enable) {
+		sp_packet_audio_init(anx78xx);
+		sp_config_packets(anx78xx, AUDIF_PACKETS);
+
+		sp_reg_read(anx78xx, RX_P0, SP_HDMI_STATUS_REG, &val);
+		if (val & SP_HDMI_AUD_LAYOUT) {
+			sp_reg_read(anx78xx, RX_P1, SP_AUD_INFOFRAME_DATA_BASE,
+				    &val);
+			sp_reg_write(anx78xx, TX_P2, SP_AUD_CH_STATUS_BASE + 5,
+				     (val & 0x07) << 5 | SP_AUDIO_LAYOUT);
+		} else {
+			sp_reg_write(anx78xx, TX_P2, SP_AUD_CH_STATUS_BASE + 5,
+				     SP_I2S_CH_NUM_2 & ~SP_AUDIO_LAYOUT);
+		}
+		sp_reg_set_bits(anx78xx, TX_P0, SP_DP_AUDIO_CTRL_REG,
+				SP_AUD_EN);
+	} else {
+		sp_reg_clear_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+				  SP_AUD_IF_EN);
+	}
+}
+
+static int sp_calculate_audio_m_value(struct anx78xx *anx78xx)
+{
+	u8 val;
+	struct device *dev = &anx78xx->client->dev;
+	unsigned long m_aud, ls_clk = 0;
+	unsigned long aud_freq = 0;
+
+	sp_reg_read(anx78xx, RX_P0, SP_AUD_SPDIF_CH_STATUS_BASE + 4, &val);
+
+	switch (val & SP_FS_FREQ_MASK) {
+	case SP_FS_FREQ_44100HZ:
+		aud_freq = 44100;
+		break;
+	case SP_FS_FREQ_48000HZ:
+		aud_freq = 48000;
+		break;
+	case SP_FS_FREQ_32000HZ:
+		aud_freq = 32000;
+		break;
+	case SP_FS_FREQ_88200HZ:
+		aud_freq = 88200;
+		break;
+	case SP_FS_FREQ_96000HZ:
+		aud_freq = 96000;
+		break;
+	case SP_FS_FREQ_176400HZ:
+		aud_freq = 176400;
+		break;
+	case SP_FS_FREQ_192000HZ:
+		aud_freq = 192000;
+		break;
+	default:
+		dev_err(dev, "invalid sampling clock frequency %d\n",
+			val & SP_FS_FREQ_MASK);
+		return -1;
+	}
+
+	switch (sp_get_link_bw(anx78xx)) {
+	case SP_LINK_1P62G:
+		ls_clk = 162000;
+		break;
+	case SP_LINK_2P7G:
+		ls_clk = 270000;
+		break;
+	case SP_LINK_5P4G:
+		ls_clk = 540000;
+		break;
+	case SP_LINK_6P75G:
+		ls_clk = 675000;
+		break;
+	default:
+		dev_err(dev, "invalid main link bandwidth setting\n");
+		return -1;
+	}
+
+	dev_dbg(dev, "aud_freq = %ld , LS_CLK = %ld\n", aud_freq, ls_clk);
+
+	m_aud = (((512 * aud_freq) / ls_clk) * 32768) / 1000;
+	sp_reg_write(anx78xx, TX_P1, SP_AUD_INTERFACE_CTRL4_REG, m_aud & 0xff);
+	m_aud = m_aud >> 8;
+	sp_reg_write(anx78xx, TX_P1, SP_AUD_INTERFACE_CTRL5_REG, m_aud & 0xff);
+	sp_reg_write(anx78xx, TX_P1, SP_AUD_INTERFACE_CTRL6_REG, 0x00);
+
+	return 0;
+}
+
+static void sp_config_audio(struct anx78xx *anx78xx)
+{
+	int i;
+	u8 val;
+
+	sp_block_power_ctrl(anx78xx, SP_TX_PWR_AUDIO, true);
+
+	sp_reg_read(anx78xx, TX_P0, SP_DP_MAIN_LINK_BW_SET_REG, &val);
+	if (val & SP_INITIAL_SLIM_M_AUD_SEL)
+		if (sp_calculate_audio_m_value(anx78xx))
+			return;
+
+	sp_reg_clear_bits(anx78xx, TX_P1, SP_AUD_INTERFACE_CTRL0_REG,
+			  SP_AUD_INTERFACE_DISABLE);
+
+	sp_reg_set_bits(anx78xx, TX_P1, SP_AUD_INTERFACE_CTRL2_REG,
+			SP_M_AUD_ADJUST_ST);
+
+	sp_reg_read(anx78xx, RX_P0, SP_HDMI_STATUS_REG, &val);
+	if (val & SP_HDMI_AUD_LAYOUT)
+		sp_reg_set_bits(anx78xx, TX_P2, SP_AUD_CH_STATUS_BASE + 5,
+				SP_I2S_CH_NUM_8 | SP_AUDIO_LAYOUT);
+	else
+		sp_reg_clear_bits(anx78xx, TX_P2, SP_AUD_CH_STATUS_BASE + 5,
+				  SP_I2S_CHANNEL_NUM_MASK | SP_AUDIO_LAYOUT);
+
+	/* transfer audio channel status from HDMI Rx to Slimport Tx */
+	for (i = 1; i <= SP_AUD_CH_STATUS_REG_NUM; i++) {
+		sp_reg_read(anx78xx, RX_P0, SP_AUD_SPDIF_CH_STATUS_BASE + i,
+			    &val);
+		sp_reg_write(anx78xx, TX_P2, SP_AUD_CH_STATUS_BASE + i,
+			     val);
+	}
+
+	/* enable audio */
+	sp_enable_audio_output(anx78xx, true);
+}
+
+static bool sp_config_audio_output(struct anx78xx *anx78xx)
+{
+	u8 val;
+
+	switch (sp.tx_ao_state) {
+	default:
+	case AO_INIT:
+	case AO_CTS_RCV_INT:
+	case AO_AUDIO_RCV_INT:
+		sp_reg_read(anx78xx, RX_P0, SP_HDMI_STATUS_REG, &val);
+		if (!val & SP_HDMI_MODE) {
+			sp.tx_ao_state = AO_INIT;
+			return true;
+		}
+		break;
+	case AO_RCV_INT_FINISH:
+		if (sp.audio_stable_count++ > 2) {
+			sp.tx_ao_state = AO_OUTPUT;
+		} else {
+			sp.tx_ao_state = AO_INIT;
+			break;
+		}
+	/* fallthrough */
+	case AO_OUTPUT:
+		sp.audio_stable_count = 0;
+		sp.tx_ao_state = AO_INIT;
+		sp_video_mute(anx78xx, false);
+		sp_hdmi_mute_audio(anx78xx, false);
+		sp_config_audio(anx78xx);
+		return true;
+	}
+
+	return false;
+}
+
+/******************End Audio process********************/
+
+static void sp_initialization(struct anx78xx *anx78xx)
+{
+	sp.read_edid_flag = false;
+
+	/* Power on all modules */
+	sp_reg_write(anx78xx, TX_P2, SP_POWERDOWN_CTRL_REG, 0x00);
+	/* Driver Version */
+	sp_reg_write(anx78xx, TX_P1, SP_FW_VER_REG, FW_VERSION);
+	sp_hdmi_initialization(anx78xx);
+	sp_tx_initialization(anx78xx);
+	msleep(200);
+}
+
+/*
+ * Interrupt receiver function, gets the service interrupts and updates the
+ * status of the interrupts so that correct interrupt service routines can
+ * be called in the SlimPort task handler function.
+ */
+static void sp_int_receiver(struct anx78xx *anx78xx)
+{
+	int i;
+
+	/* Common Interrupt Status Registers */
+	for (i = 0; i < ARRAY_SIZE(sp.common_int); i++) {
+		sp_reg_read(anx78xx, TX_P2, SP_COMMON_INT_STATUS_BASE + 1 + i,
+			    &sp.common_int[i]);
+		sp_reg_write(anx78xx, TX_P2, SP_COMMON_INT_STATUS_BASE + 1 + i,
+			     sp.common_int[i]);
+	}
+
+	/* Display Port Interrupt Status Register */
+	sp_reg_read(anx78xx, TX_P2, SP_DP_INT_STATUS_REG, &sp.dp_int);
+	sp_reg_write(anx78xx, TX_P2, SP_DP_INT_STATUS_REG, sp.dp_int);
+
+	/* Interrupt Status Registers */
+	for (i = 0; i < ARRAY_SIZE(sp.sp_hdmi_int); i++) {
+		sp_reg_read(anx78xx, RX_P0, SP_INT_STATUS1_REG + i,
+			    &sp.sp_hdmi_int[i]);
+		sp_reg_write(anx78xx, RX_P0, SP_INT_STATUS1_REG + i,
+			     sp.sp_hdmi_int[i]);
+	}
+}
+
+/******************Start task process********************/
+static void sp_pll_changed_int_handler(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+
+	if (sp.tx_system_state >= STATE_LINK_TRAINING) {
+		if (!sp_get_pll_lock_status(anx78xx)) {
+			dev_dbg(dev, "PLL not lock!\n");
+			sp_set_system_state(anx78xx, STATE_LINK_TRAINING);
+		}
+	}
+}
+
+static void sp_phy_auto_test(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 b_sw;
+	u8 buf[16];
+	int i;
+
+	/* DPCD 0x219 TEST_LINK_RATE */
+	sp_aux_dpcdread_bytes(anx78xx, 0x0, 0x02, 0x19, 1, buf);
+	dev_dbg(dev, "DPCD: 0x00219 = %.2x\n", buf[0]);
+	switch (buf[0]) {
+	case SP_LINK_1P62G:
+	case SP_LINK_2P7G:
+	case SP_LINK_5P4G:
+	case SP_LINK_6P75G:
+		sp_set_link_bw(anx78xx, buf[0]);
+		sp.tx_test_bw = buf[0];
+		break;
+	default:
+		sp_set_link_bw(anx78xx, SP_LINK_6P75G);
+		sp.tx_test_bw = SP_LINK_6P75G;
+		break;
+	}
+
+	/* DPCD 0x248 PHY_TEST_PATTERN */
+	sp_aux_dpcdread_bytes(anx78xx, 0x0, 0x02, 0x48, 1, buf);
+	dev_dbg(dev, "DPCD: 0x00248 = %.2x\n", buf[0]);
+	switch (buf[0]) {
+	case 0:
+		break;
+	case 1:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_TRAINING_PATTERN_SET_REG,
+			     0x04);
+		break;
+	case 2:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_TRAINING_PATTERN_SET_REG,
+			     0x08);
+		break;
+	case 3:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_TRAINING_PATTERN_SET_REG,
+			     0x0c);
+		break;
+	case 4:
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x50, 10,
+				      buf);
+		for (i = 0; i < SP_DP_LT_80BIT_PATTERN_REG_NUM; i++) {
+			sp_reg_write(anx78xx, TX_P1,
+				     SP_DP_LT_80BIT_PATTERN0_REG + i,
+				     buf[0]);
+		}
+		sp_reg_write(anx78xx, TX_P0, SP_DP_TRAINING_PATTERN_SET_REG,
+			     0x30);
+		break;
+	case 5:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_CEP_TRAINING_CTRL0_REG,
+			     0x00);
+		sp_reg_write(anx78xx, TX_P0, SP_DP_CEP_TRAINING_CTRL1_REG,
+			     0x01);
+		sp_reg_write(anx78xx, TX_P0, SP_DP_TRAINING_PATTERN_SET_REG,
+			     0x14);
+		break;
+	}
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x03, 1, buf);
+	dev_dbg(dev, "DPCD: 0x00003 = %.2x\n", buf[0]);
+	if (buf[0] & 0x01)
+		sp_config_ssc(anx78xx, SSC_DEP_4000PPM);
+	else
+		sp_downspeading_enable(anx78xx, false);
+
+	/* get swing and emphasis adjust request */
+	sp_reg_read(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG, &b_sw);
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x06, 1, buf);
+	dev_dbg(dev, "DPCD: 0x00206 = %.2x\n", buf[0]);
+	switch (buf[0] & 0x0f) {
+	case 0x00:
+	case 0x01:
+	case 0x02:
+	case 0x03:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG,
+			     (b_sw & ~SP_TX_SW_SET_MASK) | (buf[0] & 0x0f));
+		break;
+	case 0x04:
+	case 0x05:
+	case 0x06:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG,
+			     (b_sw & ~SP_TX_SW_SET_MASK) |
+			     ((buf[0] & 0x0f) + 4));
+		break;
+	case 0x08:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG,
+			     (b_sw & ~SP_TX_SW_SET_MASK) | 0x10);
+		break;
+	case 0x09:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG,
+			     (b_sw & ~SP_TX_SW_SET_MASK) | 0x11);
+		break;
+	case 0x0c:
+		sp_reg_write(anx78xx, TX_P0, SP_DP_LANE0_LT_CTRL_REG,
+			     (b_sw & ~SP_TX_SW_SET_MASK) | 0x18);
+		break;
+	default:
+		break;
+	}
+}
+
+static void sp_hpd_irq_process(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+	u8 test_vector;
+	u8 buf[6];
+
+	sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x00, 6, buf);
+	dev_dbg(dev, "get HPD IRQ %x\n", buf[1]);
+
+	if (buf[1] != 0)
+		sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02,
+				       SP_DPCD_SERVICE_IRQ_VECTOR, 1,
+				       &buf[1]);
+
+	/* HDCP IRQ */
+	if ((buf[1] & SP_CP_IRQ) &&
+	    (sp.hdcp_state > HDCP_WAITING_FINISH ||
+	     sp.tx_system_state >= STATE_HDCP_AUTH)) {
+		sp_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x29, 1,
+				      &val);
+		if (val & 0x04) {
+			if (!sp_hdcp_repeater_mode(anx78xx)) {
+				sp_set_system_state(anx78xx, STATE_HDCP_AUTH);
+				sp_clean_hdcp_status(anx78xx);
+			} else {
+				sp.repeater_state = HDCP_ERROR;
+			}
+			dev_dbg(dev, "CP_IRQ, HDCP sync lost.\n");
+		}
+	}
+
+	/* PHY and Link CTS test */
+	if (buf[1] & SP_TEST_IRQ) {
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x18, 1,
+				      &test_vector);
+
+		if (test_vector & 0x01) {
+			sp.tx_test_lt = true;
+
+			sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x19, 1,
+					      &val);
+			switch (val) {
+			case SP_LINK_1P62G:
+			case SP_LINK_2P7G:
+			case SP_LINK_5P4G:
+			case SP_LINK_6P75G:
+				sp_set_link_bw(anx78xx, val);
+				sp.tx_test_bw = val;
+				break;
+			default:
+				sp_set_link_bw(anx78xx, SP_LINK_6P75G);
+				sp.tx_test_bw = SP_LINK_6P75G;
+				break;
+			}
+
+			dev_dbg(dev, "Test bandwidth %.2x\n", sp.tx_test_bw);
+
+			sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1,
+					      &val);
+			val = val | SP_TEST_ACK;
+			sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1,
+					       &val);
+
+			dev_dbg(dev, "Set TEST_ACK!\n");
+			if (sp.tx_system_state >= STATE_LINK_TRAINING) {
+				sp.tx_lt_state = LT_INIT;
+				sp_set_system_state(anx78xx,
+						    STATE_LINK_TRAINING);
+			}
+			dev_dbg(dev, "IRQ: test-LT request!\n");
+		}
+
+		if (test_vector & 0x02) {
+			sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1,
+					      &val);
+			val = val | SP_TEST_ACK;
+			sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1,
+					       &val);
+		}
+		if (test_vector & 0x04) {
+			if (sp.tx_system_state > STATE_PARSE_EDID)
+				sp_set_system_state(anx78xx, STATE_PARSE_EDID);
+			sp.tx_test_edid = true;
+			dev_dbg(dev, "test EDID Requested!\n");
+		}
+
+		if (test_vector & 0x08) {
+			sp.tx_test_lt = true;
+
+			sp_phy_auto_test(anx78xx);
+
+			sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1,
+					      &val);
+			val = val | 0x01;
+			sp_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1,
+					       &val);
+		}
+	}
+
+	if (sp.tx_system_state > STATE_LINK_TRAINING) {
+		if ((sp.tx_system_state == STATE_HDCP_AUTH) &&
+		    (buf[1] & SP_CP_IRQ)) {
+			dev_dbg(dev, "CP IRQ!\n");
+		} else if (!(buf[4] & 0x01) || ((buf[2] & 0x05) != 0x05)) {
+			sp_set_system_state(anx78xx, STATE_LINK_TRAINING);
+			dev_dbg(dev, "IRQ: re-LT request!\n");
+			return;
+		}
+
+		dev_dbg(dev, "lane align %x\n", buf[4]);
+		dev_dbg(dev, "lane clock recovery %x\n", buf[2]);
+	}
+}
+
+static void sp_packet_vsi_init(struct anx78xx *anx78xx)
+{
+	u8 val;
+	int i;
+
+	sp.tx_packet_vsi.infoframe.type = HDMI_INFOFRAME_TYPE_VENDOR;
+	sp.tx_packet_vsi.infoframe.version = 1;
+	sp.tx_packet_vsi.infoframe.length = HDMI_VSI_INFOFRAME_SIZE;
+
+	for (i = 0; i < sp.tx_packet_vsi.infoframe.length; i++) {
+		sp_reg_read(anx78xx, RX_P1, SP_MPEG_VS_INFOFRAME_DATA_BASE + i,
+			    &val);
+		sp.tx_packet_mpeg.data[i] = val;
+	}
+}
+
+static void sp_packet_mpeg_init(struct anx78xx *anx78xx)
+{
+	u8 val;
+	int i;
+
+	sp.tx_packet_mpeg.infoframe.type = HDMI_INFOFRAME_TYPE_MPEG;
+	sp.tx_packet_mpeg.infoframe.version = 1;
+	sp.tx_packet_mpeg.infoframe.length = HDMI_MPEG_INFOFRAME_SIZE;
+
+	for (i = 0; i < sp.tx_packet_mpeg.infoframe.length; i++) {
+		sp_reg_read(anx78xx, RX_P1, SP_MPEG_VS_INFOFRAME_DATA_BASE + i,
+			    &val);
+		sp.tx_packet_mpeg.data[i] = val;
+	}
+}
+
+static void sp_auth_done_int_handler(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 buf[2];
+
+	if (sp_hdcp_repeater_mode(anx78xx)) {
+		sp_reg_read(anx78xx, TX_P0, SP_TX_HDCP_STATUS_REG, &buf[0]);
+		if ((buf[0] & SP_AUTHEN_PASS) &&
+		    (sp.repeater_state == HDCP_DOING))
+			sp.repeater_state = HDCP_DONE;
+		else
+			sp.repeater_state = HDCP_ERROR;
+
+		return;
+	}
+
+	if (sp.hdcp_state > HDCP_HW_ENABLE &&
+	    sp.tx_system_state == STATE_HDCP_AUTH) {
+		sp_reg_read(anx78xx, TX_P0, SP_HDCP_RX_BSTATUS0_REG, &buf[0]);
+		sp_reg_read(anx78xx, TX_P0, SP_HDCP_RX_BSTATUS1_REG, &buf[1]);
+		if ((buf[0] & 0x08) || (buf[1] & 0x80)) {
+			dev_dbg(dev, "max cascade/devs exceeded!\n");
+			sp_hdcp_encryption_disable(anx78xx);
+			sp.hdcp_state = HDCP_FINISH;
+		} else {
+			sp_reg_read(anx78xx, TX_P0, SP_TX_HDCP_STATUS_REG,
+				    buf);
+		}
+
+		if (buf[0] & SP_AUTHEN_PASS) {
+			sp_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x2a, 2,
+					      buf);
+			if ((buf[0] & 0x08) || (buf[1] & 0x80)) {
+				dev_dbg(dev, "max cascade/devs exceeded!\n");
+				sp_hdcp_encryption_disable(anx78xx);
+			} else
+				dev_dbg(dev, "%s\n",
+					"authentication pass in Auth Done");
+
+			sp.hdcp_state = HDCP_FINISH;
+		} else {
+			dev_err(dev, "authentication failed in Auth Done\n");
+			sp_video_mute(anx78xx, true);
+			sp_clean_hdcp_status(anx78xx);
+			sp.hdcp_state = HDCP_FAILED;
+		}
+	}
+}
+
+static void sp_lt_done_int_handler(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+
+	if (sp.tx_lt_state == LT_WAITING_FINISH &&
+	    sp.tx_system_state == STATE_LINK_TRAINING) {
+		sp_reg_read(anx78xx, TX_P0, SP_DP_LT_CTRL_REG, &val);
+		if (val & SP_LT_ERROR_TYPE_MASK) {
+			val = (val & SP_LT_ERROR_TYPE_MASK) >> 4;
+			dev_dbg(dev, "LT failed in interrupt %.2x\n",
+				val);
+			sp.tx_lt_state = LT_ERROR;
+		} else {
+			dev_dbg(dev, "LT finish\n");
+			sp.tx_lt_state = LT_FINISH;
+		}
+	}
+}
+
+static void sp_hdmi_clk_det_int(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+
+	dev_dbg(dev, "pixel clock change\n");
+	if (sp.tx_system_state > STATE_VIDEO_OUTPUT) {
+		sp_video_mute(anx78xx, true);
+		sp_enable_audio_output(anx78xx, false);
+		sp_set_system_state(anx78xx, STATE_VIDEO_OUTPUT);
+	}
+}
+
+static void sp_hdmi_dvi_int(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+
+	sp_reg_read(anx78xx, RX_P0, SP_HDMI_STATUS_REG, &val);
+	if ((val & SP_HDMI_DET) == SP_DVI_MODE) {
+		dev_dbg(dev, "detected DVI MODE -> mute audio\n");
+		sp_hdmi_mute_audio(anx78xx, true);
+		sp_set_system_state(anx78xx, STATE_LINK_TRAINING);
+	}
+}
+
+static void sp_hdmi_new_avi_int(struct anx78xx *anx78xx)
+{
+	sp_lvttl_bit_mapping(anx78xx);
+	sp_set_colorspace(anx78xx);
+	sp_packet_avi_init(anx78xx);
+	sp_config_packets(anx78xx, AVI_PACKETS);
+}
+
+static void sp_hdmi_new_vsi_int(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 hdmi_video_format, v3d_structure, mpeg_type, mpeg_ver;
+
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_DP_3D_VSC_CTRL_REG,
+			  SP_INFO_FRAME_VSC_EN);
+
+	/* VSI package header */
+	sp_reg_read(anx78xx, RX_P1, SP_MPEG_VS_INFOFRAME_TYPE_REG, &mpeg_type);
+	sp_reg_read(anx78xx, RX_P1, SP_MPEG_VS_INFOFRAME_VER_REG, &mpeg_ver);
+	if ((mpeg_type || mpeg_ver) != 0x01)
+		return;
+
+	dev_dbg(dev, "setup VSI package!\n");
+
+	sp_packet_vsi_init(anx78xx);
+	sp_config_packets(anx78xx, VSI_PACKETS);
+
+	sp_reg_read(anx78xx, RX_P1, SP_MPEG_VS_INFOFRAME_DATA_BASE + 3,
+		    &hdmi_video_format);
+
+	if ((hdmi_video_format & 0xe0) == 0x40) {
+		dev_dbg(dev, "3D VSI packet detected. Config VSC packet\n");
+
+		sp_reg_read(anx78xx, RX_P1, SP_MPEG_VS_INFOFRAME_DATA_BASE + 5,
+			    &v3d_structure);
+
+		switch (v3d_structure & 0xf0) {
+		case 0x00:
+			v3d_structure = 0x02;
+			break;
+		case 0x20:
+			v3d_structure = 0x03;
+			break;
+		case 0x30:
+			v3d_structure = 0x04;
+			break;
+		default:
+			v3d_structure = 0x00;
+			dev_dbg(dev, "3D structure is not supported\n");
+			break;
+		}
+		sp_reg_write(anx78xx, TX_P0, SP_DP_VSC_DB1_REG, v3d_structure);
+	}
+	sp_reg_set_bits(anx78xx, TX_P0, SP_DP_3D_VSC_CTRL_REG,
+			SP_INFO_FRAME_VSC_EN);
+	sp_reg_clear_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG,
+			  SP_SPD_IF_EN);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG, SP_SPD_IF_UD);
+	sp_reg_set_bits(anx78xx, TX_P0, SP_PACKET_SEND_CTRL_REG, SP_SPD_IF_EN);
+}
+
+static void sp_hdmi_no_vsi_int(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val;
+
+	sp_reg_read(anx78xx, TX_P0, SP_DP_3D_VSC_CTRL_REG, &val);
+	if (val & SP_INFO_FRAME_VSC_EN) {
+		dev_dbg(dev, "no new VSI is received, disable VSC packet\n");
+		val &= ~SP_INFO_FRAME_VSC_EN;
+		sp_reg_write(anx78xx, TX_P0, SP_DP_3D_VSC_CTRL_REG, val);
+		sp_packet_mpeg_init(anx78xx);
+		sp_config_packets(anx78xx, MPEG_PACKETS);
+	}
+}
+
+static inline void sp_hdmi_restart_audio_chk(struct anx78xx *anx78xx)
+{
+	sp_set_system_state(anx78xx, STATE_AUDIO_OUTPUT);
+}
+
+static void sp_hdmi_cts_rcv_int(struct anx78xx *anx78xx)
+{
+	if (sp.tx_ao_state == AO_INIT)
+		sp.tx_ao_state = AO_CTS_RCV_INT;
+	else if (sp.tx_ao_state == AO_AUDIO_RCV_INT)
+		sp.tx_ao_state = AO_RCV_INT_FINISH;
+}
+
+static void sp_hdmi_audio_rcv_int(struct anx78xx *anx78xx)
+{
+	if (sp.tx_ao_state == AO_INIT)
+		sp.tx_ao_state = AO_AUDIO_RCV_INT;
+	else if (sp.tx_ao_state == AO_CTS_RCV_INT)
+		sp.tx_ao_state = AO_RCV_INT_FINISH;
+}
+
+static void sp_hdmi_audio_samplechg_int(struct anx78xx *anx78xx)
+{
+	u16 i;
+	u8 val;
+
+	/* transfer audio channel status from HDMI Rx to Slimport Tx */
+	for (i = 0; i < SP_AUD_CH_STATUS_REG_NUM; i++) {
+		sp_reg_read(anx78xx, RX_P0, SP_AUD_SPDIF_CH_STATUS_BASE + i,
+			    &val);
+		sp_reg_write(anx78xx, TX_P2, SP_AUD_CH_STATUS_BASE + i,
+			     val);
+	}
+}
+
+static void sp_hdmi_hdcp_error_int(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+
+	if (sp.hdcp_error_count >= 40) {
+		sp.hdcp_error_count = 0;
+		dev_dbg(dev, "lots of hdcp error occurred!\n");
+		sp_hdmi_mute_audio(anx78xx, true);
+		sp_hdmi_mute_video(anx78xx, true);
+		sp_hdmi_set_hpd(anx78xx, false);
+		usleep_range(10000, 11000);
+		sp_hdmi_set_hpd(anx78xx, true);
+	} else {
+		sp.hdcp_error_count++;
+	}
+}
+
+static void sp_hdmi_new_gcp_int(struct anx78xx *anx78xx)
+{
+	u8 val;
+
+	sp_reg_read(anx78xx, RX_P1, SP_GENERAL_CTRL_PACKET_REG, &val);
+	if (val & SP_SET_AVMUTE) {
+		sp_hdmi_mute_video(anx78xx, true);
+		sp_hdmi_mute_audio(anx78xx, true);
+	} else if (val & SP_CLEAR_AVMUTE) {
+		sp_hdmi_mute_video(anx78xx, false);
+		sp_hdmi_mute_audio(anx78xx, false);
+	}
+}
+
+static void sp_hpd_int_handler(struct anx78xx *anx78xx, u8 hpd_source)
+{
+	u8 val;
+	struct device *dev = &anx78xx->client->dev;
+
+	switch (hpd_source) {
+	case SP_HPD_LOST:
+		sp_hdmi_set_hpd(anx78xx, false);
+		sp_set_system_state(anx78xx, STATE_WAITING_CABLE_PLUG);
+		break;
+	case SP_HPD_CHG:
+		dev_dbg(dev, "HPD changed!\n");
+		usleep_range(2000, 4000);
+		if (sp.common_int[3] & SP_HPD_IRQ)
+			sp_hpd_irq_process(anx78xx);
+
+		sp_reg_read(anx78xx, TX_P0, SP_DP_SYSTEM_CTRL_BASE + 3, &val);
+		if (val & SP_HPD_STATUS) {
+			if (sp.common_int[3] & SP_HPD_IRQ)
+				sp_hpd_irq_process(anx78xx);
+		} else {
+			sp_reg_read(anx78xx, TX_P0,
+				    SP_DP_SYSTEM_CTRL_BASE + 3,
+				    &val);
+			if (val & SP_HPD_STATUS) {
+				sp_hdmi_set_hpd(anx78xx, false);
+				sp_set_system_state(anx78xx,
+						    STATE_WAITING_CABLE_PLUG);
+			}
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void sp_system_isr_handler(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+
+	if (sp.tx_system_state == STATE_WAITING_CABLE_PLUG) {
+		if (sp.common_int[3] & SP_HPD_PLUG)
+			sp_hpd_int_handler(anx78xx, SP_HPD_PLUG);
+	} else  {
+		if (sp.common_int[3] & SP_HPD_CHG)
+			sp_hpd_int_handler(anx78xx, SP_HPD_CHG);
+		else if (sp.common_int[3] & SP_HPD_LOST)
+			sp_hpd_int_handler(anx78xx, SP_HPD_LOST);
+	}
+
+	if (sp.common_int[0] & SP_PLL_LOCK_CHG)
+		sp_pll_changed_int_handler(anx78xx);
+
+	if (sp.common_int[1] & SP_HDCP_AUTH_DONE)
+		sp_auth_done_int_handler(anx78xx);
+
+	if ((sp.common_int[2] & SP_HDCP_LINK_CHECK_FAIL) &&
+	    !sp_hdcp_repeater_mode(anx78xx)) {
+		sp_set_system_state(anx78xx, STATE_LINK_TRAINING);
+		dev_dbg(dev, "HDCP Sync Lost!\n");
+	}
+
+	if (sp.dp_int & SP_TRAINING_FINISH)
+		sp_lt_done_int_handler(anx78xx);
+
+	if (sp.tx_system_state > STATE_SINK_CONNECTION) {
+		if (sp.sp_hdmi_int[5] & SP_NEW_AVI_PKT)
+			sp_hdmi_new_avi_int(anx78xx);
+	}
+
+	if (sp.tx_system_state > STATE_VIDEO_OUTPUT) {
+		if (sp.sp_hdmi_int[6] & SP_NEW_VS) {
+			sp.sp_hdmi_int[6] &= ~SP_NO_VSI;
+			sp_hdmi_new_vsi_int(anx78xx);
+		}
+		if (sp.sp_hdmi_int[6] & SP_NO_VSI)
+			sp_hdmi_no_vsi_int(anx78xx);
+	}
+
+	if (sp.tx_system_state >= STATE_VIDEO_OUTPUT) {
+		if (sp.sp_hdmi_int[0] & SP_CKDT_CHG)
+			sp_hdmi_clk_det_int(anx78xx);
+
+		if (sp.sp_hdmi_int[0] & SP_SCDT_CHG)
+			dev_dbg(dev, "HDCP Sync Detected\n");
+
+		if (sp.sp_hdmi_int[0] & SP_HDMI_DVI)
+			sp_hdmi_dvi_int(anx78xx);
+
+		if ((sp.sp_hdmi_int[5] & SP_NEW_AUD_PKT) ||
+		    (sp.sp_hdmi_int[2] & SP_AUD_MODE_CHG))
+			sp_hdmi_restart_audio_chk(anx78xx);
+
+		if (sp.sp_hdmi_int[5] & SP_CTS_RCV)
+			sp_hdmi_cts_rcv_int(anx78xx);
+
+		if (sp.sp_hdmi_int[4] & SP_AUDIO_RCV)
+			sp_hdmi_audio_rcv_int(anx78xx);
+
+		if (sp.sp_hdmi_int[1] & SP_HDCP_ERR)
+			sp_hdmi_hdcp_error_int(anx78xx);
+
+		if (sp.sp_hdmi_int[5] & SP_NEW_CP_PKT)
+			sp_hdmi_new_gcp_int(anx78xx);
+
+		if (sp.sp_hdmi_int[1] & SP_AUDIO_SAMPLE_CHG)
+			sp_hdmi_audio_samplechg_int(anx78xx);
+	}
+}
+
+static void sp_show_information(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u8 val, val1;
+	u16 h_res, h_act, v_res, v_act;
+	u16 h_fp, h_sw, h_bp, v_fp, v_sw, v_bp;
+	unsigned long refresh;
+	unsigned long pclk;
+
+	dev_dbg(dev, "\n************* SP Video Information **************\n");
+
+	switch (sp_get_link_bw(anx78xx)) {
+	case SP_LINK_1P62G:
+		dev_dbg(dev, "BW = 1.62G\n");
+		break;
+	case SP_LINK_2P7G:
+		dev_dbg(dev, "BW = 2.7G\n");
+		break;
+	case SP_LINK_5P4G:
+		dev_dbg(dev, "BW = 5.4G\n");
+		break;
+	case SP_LINK_6P75G:
+		dev_dbg(dev, "BW = 6.75G\n");
+		break;
+	default:
+		break;
+	}
+
+	pclk = sp_pclk_calc(anx78xx);
+	pclk = pclk / 10;
+
+	sp_reg_read(anx78xx, TX_P2, SP_TOTAL_LINE_STAL_REG, &val);
+	sp_reg_read(anx78xx, TX_P2, SP_TOTAL_LINE_STAH_REG, &val1);
+
+	v_res = val1;
+	v_res = v_res << 8;
+	v_res = v_res + val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_ACT_LINE_STAL_REG, &val);
+	sp_reg_read(anx78xx, TX_P2, SP_ACT_LINE_STAH_REG, &val1);
+
+	v_act = val1;
+	v_act = v_act << 8;
+	v_act = v_act + val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_TOTAL_PIXEL_STAL_REG, &val);
+	sp_reg_read(anx78xx, TX_P2, SP_TOTAL_PIXEL_STAH_REG, &val1);
+
+	h_res = val1;
+	h_res = h_res << 8;
+	h_res = h_res + val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_ACT_PIXEL_STAL_REG, &val);
+	sp_reg_read(anx78xx, TX_P2, SP_ACT_PIXEL_STAH_REG, &val1);
+
+	h_act = val1;
+	h_act = h_act << 8;
+	h_act = h_act + val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_H_F_PORCH_STAL_REG, &val);
+	sp_reg_read(anx78xx, TX_P2, SP_H_F_PORCH_STAH_REG, &val1);
+
+	h_fp = val1;
+	h_fp = h_fp << 8;
+	h_fp = h_fp + val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_H_SYNC_STAL_REG, &val);
+	sp_reg_read(anx78xx, TX_P2, SP_H_SYNC_STAH_REG, &val1);
+
+	h_sw = val1;
+	h_sw = h_sw << 8;
+	h_sw = h_sw + val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_H_B_PORCH_STAL_REG, &val);
+	sp_reg_read(anx78xx, TX_P2, SP_H_B_PORCH_STAH_REG, &val1);
+
+	h_bp = val1;
+	h_bp = h_bp << 8;
+	h_bp = h_bp + val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_V_F_PORCH_STA_REG, &val);
+	v_fp = val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_V_SYNC_STA_REG, &val);
+	v_sw = val;
+
+	sp_reg_read(anx78xx, TX_P2, SP_V_B_PORCH_STA_REG, &val);
+	v_bp = val;
+
+	dev_dbg(dev, "Total resolution is %d * %d\n", h_res, v_res);
+
+	dev_dbg(dev, "HF=%d, HSW=%d, HBP=%d\n", h_fp, h_sw, h_bp);
+	dev_dbg(dev, "VF=%d, VSW=%d, VBP=%d\n", v_fp, v_sw, v_bp);
+
+	if (h_res == 0 || v_res == 0) {
+		refresh = 0;
+	} else {
+		refresh = pclk * 1000;
+		refresh = refresh / h_res;
+		refresh = refresh * 1000;
+		refresh = refresh / v_res;
+	}
+
+	dev_dbg(dev, "Active resolution is %d * %d @ %ldHz\n", h_act, v_act,
+		refresh);
+
+	sp_reg_read(anx78xx, TX_P0, SP_DP_VIDEO_CTRL_REG, &val);
+
+	val &= SP_COLOR_F_MASK;
+	val >>= SP_COLOR_F_SHIFT;
+	if (val == SP_COLORSPACE_RGB)
+		dev_dbg(dev, "ColorSpace: RGB");
+	else if (val == SP_COLORSPACE_YCBCR422)
+		dev_dbg(dev, "ColorSpace: YCbCr422");
+	else if (val == SP_COLORSPACE_YCBCR444)
+		dev_dbg(dev, "ColorSpace: YCbCr444");
+
+	sp_reg_read(anx78xx, TX_P0, SP_DP_VIDEO_CTRL_REG, &val);
+
+	val &= SP_BPC_MASK;
+	val >>= SP_BPC_SHIFT;
+	if (val  == SP_BPC_6BITS)
+		dev_dbg(dev, "6 BPC\n");
+	else if (val == SP_BPC_8BITS)
+		dev_dbg(dev, "8 BPC\n");
+	else if (val == SP_BPC_10BITS)
+		dev_dbg(dev, "10 BPC\n");
+	else if (val == SP_BPC_12BITS)
+		dev_dbg(dev, "12 BPC\n");
+
+	if (is_anx_dongle(anx78xx)) {
+		sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x23, 1, &val);
+		dev_dbg(dev, "Analogix Dongle FW Ver %.2x\n", val & 0x7f);
+	}
+
+	dev_dbg(dev, "\n**************************************************\n");
+}
+
+static void sp_aux_monitor(struct anx78xx *anx78xx)
+{
+	int i;
+	u8 val;
+
+	for (i = 0; i < 5; i++) {
+		if (sp_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x00,
+					  1, &val) < 0) {
+			anx78xx_poweroff(anx78xx);
+			sp_set_system_state(anx78xx, STATE_WAITING_CABLE_PLUG);
+		} else {
+			return;
+		}
+	}
+}
+
+static void sp_hdcp_repeater_reauth(struct anx78xx *anx78xx)
+{
+	u8 val, ctrl, status;
+	struct device *dev = &anx78xx->client->dev;
+
+	msleep(50);
+	sp_reg_read(anx78xx, RX_P1, SP_RX_HDCP_STATUS_REG, &val);
+
+	if (val & SP_AUTH_EN) {
+		sp_reg_read(anx78xx, TX_P0, SP_HDCP_CTRL0_REG, &ctrl);
+		if (ctrl & SP_HARD_AUTH_EN) {
+			sp_reg_read(anx78xx, TX_P0, SP_TX_HDCP_STATUS_REG,
+				    &status);
+			if (!(status & SP_AUTHEN_PASS) &&
+			    (status & SP_AUTH_FAIL)) {
+				dev_dbg(dev, "clean HDCP and re-auth\n");
+				sp.repeater_state = HDCP_ERROR;
+			}
+		} else {
+			dev_dbg(dev, "repeater mode, enable HW HDCP\n");
+			sp.repeater_state = HDCP_ERROR;
+		}
+	}
+
+	sp_reg_read(anx78xx, TX_P0, SP_HDCP_CTRL0_REG, &ctrl);
+	sp_reg_read(anx78xx, TX_P0, SP_TX_HDCP_STATUS_REG, &status);
+
+	if ((ctrl == SP_HDCP_FUNCTION_ENABLED) && (status & SP_AUTH_FAIL)) {
+		dev_dbg(dev, "HDCP encryption failure 0x%02x\n", status);
+		sp.repeater_state = HDCP_ERROR;
+	}
+
+	if (sp.repeater_state == HDCP_ERROR) {
+		sp_clean_hdcp_status(anx78xx);
+		msleep(50);
+		/* Clear HDCP AUTH interrupt */
+		sp_reg_set_bits(anx78xx, TX_P2, SP_COMMON_INT_STATUS_BASE + 2,
+				SP_HDCP_AUTH_DONE);
+		sp_hw_hdcp_enable(anx78xx);
+		sp.repeater_state = HDCP_DOING;
+	}
+}
+
+static void sp_task_handler(struct anx78xx *anx78xx)
+{
+	sp_aux_monitor(anx78xx);
+
+	if (sp.tx_system_state > STATE_WAITING_CABLE_PLUG)
+		sp_system_isr_handler(anx78xx);
+
+	/* If device supports HDCP repeater function re-auth */
+	if (sp_hdcp_repeater_mode(anx78xx))
+		sp_hdcp_repeater_reauth(anx78xx);
+}
+
+/******************End task process********************/
+
+/**
+ * sp_main_process(): SlimPort Main Process
+ *
+ * SlimPort Main Process States:
+ * 1. SlimPort plug
+ *    - If a SlimPort cable plug is detected:
+ *      - Power on device
+ *    - If a SlimPort cable plug is not detected:
+ *      - Power down device
+ * 2. SlimPort initialization
+ *    - Enable the power supply for downstream
+ *    - Power on the register access
+ *    - Initialize the related registers
+ * 3. Sink connection
+ *     - Get the cable type (HDMI, VGA or MyDP)
+ *     - Check the connection with downstream
+ * 4. Read EDID
+ *    - Read partial EDID data to decide whether to re-read entire EDID
+ *    - EDID read
+ *    - Parse EDID to get the video bandwidth
+ * 5. Link training
+ *    - Check the downstream bandwidth
+ *    - Hardware link training
+ * 6. Video output
+ *    - Verify that input video is stable
+ *    - Order by the input video to calculate the bandwidth
+ *    - Set AVI packet, bit-mapping, color depth, etc.
+ * 7. HDCP authentication
+ *    - Verify that HDCP is supported
+ *    - Enable hardware HDCP
+ * 8. Audio output
+ *    - Automatic audio M valu adjustment
+ *    - Configure audio multichannel
+ *    - Set audio packet
+ * 9. Playback
+ *    - The normal system working state
+ *
+ */
+bool sp_main_process(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+
+	/*
+	 * SlimPort State Process
+	 */
+	switch (sp.tx_system_state) {
+	case STATE_WAITING_CABLE_PLUG:
+		sp_variable_init();
+		if (anx78xx_cable_is_detected(anx78xx)) {
+			anx78xx_poweron(anx78xx);
+			sp.tx_system_state = STATE_SP_INITIALIZED;
+			dev_dbg(dev, ">> System State Transition\n");
+			sp_print_system_state(anx78xx, sp.tx_system_state);
+		} else {
+			anx78xx_poweroff(anx78xx);
+			return false;
+		}
+	/* fallthrough */
+	case STATE_SP_INITIALIZED:
+		sp_initialization(anx78xx);
+		sp.tx_system_state = STATE_SINK_CONNECTION;
+		dev_dbg(dev, ">> System State Transition\n");
+		sp_print_system_state(anx78xx, sp.tx_system_state);
+	/* fallthrough */
+	case STATE_SINK_CONNECTION:
+		if (sp_get_dp_connection(anx78xx)) {
+			sp.tx_system_state = STATE_PARSE_EDID;
+			dev_dbg(dev, ">> System State Transition\n");
+			sp_print_system_state(anx78xx, sp.tx_system_state);
+		} else {
+			break;
+		}
+	/* fallthrough */
+	case STATE_PARSE_EDID:
+		if (sp_edid_process(anx78xx)) {
+			sp.tx_system_state = STATE_LINK_TRAINING;
+			dev_dbg(dev, ">> System State Transition\n");
+			sp_print_system_state(anx78xx, sp.tx_system_state);
+		} else {
+			break;
+		}
+	/* fallthrough */
+	case STATE_LINK_TRAINING:
+		if (sp_link_training(anx78xx)) {
+			sp.tx_system_state = STATE_VIDEO_OUTPUT;
+			dev_dbg(dev, ">> System State Transition\n");
+			sp_print_system_state(anx78xx, sp.tx_system_state);
+		} else {
+			break;
+		}
+	/* fallthrough */
+	case STATE_VIDEO_OUTPUT:
+		if (sp_config_video_output(anx78xx)) {
+			sp.tx_system_state = STATE_HDCP_AUTH;
+			dev_dbg(dev, ">> System State Transition\n");
+			sp_print_system_state(anx78xx, sp.tx_system_state);
+		} else {
+			break;
+		}
+	/* fallthrough */
+	case STATE_HDCP_AUTH:
+		if (!sp_hdcp_repeater_mode(anx78xx)) {
+			if (sp_hdcp_process(anx78xx)) {
+				sp.tx_system_state = STATE_AUDIO_OUTPUT;
+				dev_dbg(dev, ">> System State Transition\n");
+				sp_print_system_state(anx78xx,
+						      sp.tx_system_state);
+			} else {
+				break;
+			}
+		} else {
+			sp.tx_system_state = STATE_AUDIO_OUTPUT;
+		}
+	/* fallthrough */
+	case STATE_AUDIO_OUTPUT:
+		if (sp_config_audio_output(anx78xx)) {
+			sp.tx_system_state = STATE_PLAY_BACK;
+			dev_dbg(dev, ">> System State Transition\n");
+			sp_print_system_state(anx78xx, sp.tx_system_state);
+		} else {
+			break;
+		}
+	/* fallthrough */
+	case STATE_PLAY_BACK:
+	default:
+		break;
+	}
+
+	/* Process the interrupts */
+	if (sp.tx_system_state > STATE_WAITING_CABLE_PLUG) {
+		/*
+		 * Interrupt receiver
+		 */
+		sp_int_receiver(anx78xx);
+
+		/*
+		 * Task handler
+		 */
+		sp_task_handler(anx78xx);
+	}
+
+	return true;
+}
+
+/**
+ * sp_system_init(): System initialization
+ *
+ * @anx78xx: SlimPort device.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int sp_system_init(struct anx78xx *anx78xx)
+{
+	struct device *dev = &anx78xx->client->dev;
+	u16 id;
+	u8 idh = 0, idl = 0;
+	int i;
+
+	anx78xx_poweron(anx78xx);
+
+	/* check chip id */
+	sp_reg_read(anx78xx, TX_P2, SP_DEVICE_IDL_REG, &idl);
+	sp_reg_read(anx78xx, TX_P2, SP_DEVICE_IDH_REG, &idh);
+	id = idl | (idh << 8);
+
+	for (i = 0; i < ARRAY_SIZE(chipid_list); i++) {
+		if (id == chipid_list[i]) {
+			sp_variable_init();
+			return 0;
+		}
+	}
+
+	anx78xx_poweroff(anx78xx);
+
+	dev_err(dev, "failed to detect ANX%x\n", id);
+
+	return -ENODEV;
+}
diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h
new file mode 100644
index 0000000..6eee698
--- /dev/null
+++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright(c) 2015, Analogix Semiconductor. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SLIMPORT_TX_DRV_H
+#define __SLIMPORT_TX_DRV_H
+
+#include <linux/hdmi.h>
+
+#include "anx78xx.h"
+#include "slimport_tx_reg.h"
+
+#define FW_VERSION	0x23
+
+#define HDMI_INFOFRAME_TYPE_MPEG	0x85
+#define HDMI_MPEG_INFOFRAME_SIZE	10
+#define HDMI_VSI_INFOFRAME_SIZE		10
+
+enum sp_tx_state {
+	STATE_WAITING_CABLE_PLUG,
+	STATE_SP_INITIALIZED,
+	STATE_SINK_CONNECTION,
+	STATE_PARSE_EDID,
+	STATE_LINK_TRAINING,
+	STATE_VIDEO_OUTPUT,
+	STATE_HDCP_AUTH,
+	STATE_AUDIO_OUTPUT,
+	STATE_PLAY_BACK
+};
+
+enum sp_tx_power_block {
+	SP_TX_PWR_REG = SP_REGISTER_PD,
+	SP_TX_PWR_HDCP = SP_HDCP_PD,
+	SP_TX_PWR_AUDIO = SP_AUDIO_PD,
+	SP_TX_PWR_VIDEO = SP_VIDEO_PD,
+	SP_TX_PWR_LINK = SP_LINK_PD,
+	SP_TX_PWR_TOTAL = SP_TOTAL_PD,
+	SP_TX_PWR_NUMS
+};
+
+enum hdmi_color_depth {
+	HDMI_LEGACY = 0x00,
+	HDMI_24BIT = 0x04,
+	HDMI_30BIT = 0x05,
+	HDMI_36BIT = 0x06,
+	HDMI_48BIT = 0x07,
+};
+
+enum sp_tx_lt_status {
+	LT_INIT,
+	LT_WAIT_PLL_LOCK,
+	LT_CHECK_LINK_BW,
+	LT_START,
+	LT_WAITING_FINISH,
+	LT_ERROR,
+	LT_FINISH,
+};
+
+enum hdcp_status {
+	HDCP_CAPABLE_CHECK,
+	HDCP_WAITING_VID_STB,
+	HDCP_HW_ENABLE,
+	HDCP_WAITING_FINISH,
+	HDCP_FINISH,
+	HDCP_FAILED,
+	HDCP_NOT_SUPPORTED,
+};
+
+enum repeater_status {
+	HDCP_DONE,
+	HDCP_DOING,
+	HDCP_ERROR,
+};
+
+enum video_output_status {
+	VO_WAIT_VIDEO_STABLE,
+	VO_WAIT_TX_VIDEO_STABLE,
+	VO_CHECK_VIDEO_INFO,
+	VO_FINISH,
+};
+
+enum audio_output_status {
+	AO_INIT,
+	AO_CTS_RCV_INT,
+	AO_AUDIO_RCV_INT,
+	AO_RCV_INT_FINISH,
+	AO_OUTPUT,
+};
+
+struct packet_audio {
+	struct hdmi_any_infoframe infoframe;
+	u8 data[HDMI_AUDIO_INFOFRAME_SIZE];
+};
+
+struct packet_avi {
+	struct hdmi_any_infoframe infoframe;
+	u8 data[HDMI_AVI_INFOFRAME_SIZE];
+};
+
+struct packet_mpeg {
+	struct hdmi_any_infoframe infoframe;
+	u8 data[HDMI_MPEG_INFOFRAME_SIZE];
+};
+
+struct packet_vsi {
+	struct hdmi_any_infoframe infoframe;
+	u8 data[HDMI_VSI_INFOFRAME_SIZE];
+};
+
+enum packets_type {
+	AVI_PACKETS,
+	MPEG_PACKETS,
+	VSI_PACKETS,
+	AUDIF_PACKETS
+};
+
+enum sp_ssc_dep {
+	SSC_DEP_DISABLE = 0x0,
+	SSC_DEP_500PPM,
+	SSC_DEP_1000PPM,
+	SSC_DEP_1500PPM,
+	SSC_DEP_2000PPM,
+	SSC_DEP_2500PPM,
+	SSC_DEP_3000PPM,
+	SSC_DEP_3500PPM,
+	SSC_DEP_4000PPM,
+	SSC_DEP_4500PPM,
+	SSC_DEP_5000PPM,
+	SSC_DEP_5500PPM,
+	SSC_DEP_6000PPM
+};
+
+bool sp_main_process(struct anx78xx *anx78xx);
+
+int sp_system_init(struct anx78xx *anx78xx);
+
+u8 sp_get_link_bandwidth(struct anx78xx *anx78xx);
+
+#endif
diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h
new file mode 100644
index 0000000..2110f84
--- /dev/null
+++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h
@@ -0,0 +1,754 @@
+/*
+ * Copyright(c) 2015, Analogix Semiconductor. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SLIMPORT_TX_REG_DEF_H
+#define __SLIMPORT_TX_REG_DEF_H
+
+#define TX_P0				0x70
+#define TX_P1				0x7a
+#define TX_P2				0x72
+
+#define RX_P0				0x7e
+#define RX_P1				0x80
+
+/***************************************************************/
+/* Register definition of device address 0x7e                  */
+/***************************************************************/
+
+/*
+ * System Control and Status
+ */
+
+/* Software Reset Register 1 */
+#define SP_SOFTWARE_RESET1_REG		0x11
+#define SP_VIDEO_RST			BIT(4)
+#define SP_HDCP_MAN_RST			BIT(2)
+#define SP_TMDS_RST			BIT(1)
+#define SP_SW_MAN_RST			BIT(0)
+
+/* System Status Register */
+#define SP_SYSTEM_STATUS_REG		0x14
+#define SP_TMDS_CLOCK_DET		BIT(1)
+#define SP_TMDS_DE_DET			BIT(0)
+
+/* HDMI Status Register */
+#define SP_HDMI_STATUS_REG		0x15
+#define SP_HDMI_AUD_LAYOUT		BIT(3)
+#define SP_HDMI_DET			BIT(0)
+#  define SP_DVI_MODE			0
+#  define SP_HDMI_MODE			1
+
+/* HDMI Mute Control Register */
+#define SP_HDMI_MUTE_CTRL_REG		0x16
+#define SP_AUD_MUTE			BIT(1)
+#define SP_VID_MUTE			BIT(0)
+
+/* System Power Down Register 1 */
+#define SP_SYSTEM_POWER_DOWN1_REG	0x18
+#define SP_PWDN_CTRL			BIT(0)
+
+/*
+ * Audio and Video Auto Control
+ */
+
+/* Auto Audio and Video Control register */
+#define SP_AUDVID_CTRL_REG		0x20
+#define SP_AVC_OE			BIT(7)
+#define SP_AAC_OE			BIT(6)
+#define SP_AVC_EN			BIT(1)
+#define SP_AAC_EN			BIT(0)
+
+/* Audio Exception Enable Registers */
+#define SP_AUD_EXCEPTION_ENABLE_BASE	(0x24 - 1)
+/* Bits for Audio Exception Enable Register 3 */
+#define SP_AEC_EN21			BIT(5)
+
+/*
+ * Interrupt
+ */
+
+/* Interrupt Status Register 1 */
+#define SP_INT_STATUS1_REG		0x31
+/* Bits for Interrupt Status Register 1 */
+#define SP_HDMI_DVI			BIT(7)
+#define SP_CKDT_CHG			BIT(6)
+#define SP_SCDT_CHG			BIT(5)
+#define SP_PCLK_CHG			BIT(4)
+#define SP_PLL_UNLOCK			BIT(3)
+#define SP_CABLE_PLUG_CHG		BIT(2)
+#define SP_SET_MUTE			BIT(1)
+#define SP_SW_INTR			BIT(0)
+/* Bits for Interrupt Status Register 2 */
+#define SP_HDCP_ERR			BIT(5)
+#define SP_AUDIO_SAMPLE_CHG		BIT(0)	/* undocumented */
+/* Bits for Interrupt Status Register 3 */
+#define SP_AUD_MODE_CHG			BIT(0)
+/* Bits for Interrupt Status Register 5 */
+#define SP_AUDIO_RCV			BIT(0)
+/* Bits for Interrupt Status Register 6 */
+#define SP_CTS_RCV			BIT(7)
+#define SP_NEW_AUD_PKT			BIT(4)
+#define SP_NEW_AVI_PKT			BIT(1)
+#define SP_NEW_CP_PKT			BIT(0)
+/* Bits for Interrupt Status Register 7 */
+#define SP_NO_VSI			BIT(7)
+#define SP_NEW_VS			BIT(4)
+
+/* Interrupt Mask Status Registers */
+#define SP_INT_MASK_BASE		(0x41 - 1)
+
+/* HDMI US TIMER Control Register */
+#define SP_HDMI_US_TIMER_CTRL_REG	0x49
+#define SP_MS_TIMER_MARGIN_10_8_MASK	0x07
+
+/*
+ * TMDS Control
+ */
+
+/* TMDS Control Registers */
+#define SP_TMDS_CTRL_BASE		(0x50 - 1)
+/* Bits for TMDS Control Register 7 */
+#define SP_PD_RT			BIT(0)
+
+/*
+ * Video Control
+ */
+
+/* Video Status Register */
+#define SP_VIDEO_STATUS_REG		0x70
+#define SP_COLOR_DEPTH_MASK		0xf0
+#define SP_COLOR_DEPTH_SHIFT		4
+
+/* Video Data Range Control Register */
+#define SP_VID_DATA_RANGE_CTRL_REG	0x83
+#define SP_R2Y_INPUT_LIMIT		BIT(1)
+
+/* Pixel Clock High Resolution Counter Registers */
+#define SP_PCLK_HIGHRES_CNT_BASE	(0x8c - 1)
+
+/*
+ * Audio Control
+ */
+
+/* Number of Audio Channels Status Registers */
+#define SP_AUD_CH_STATUS_REG_NUM	5
+
+/* Audio IN S/PDIF Channel Status Registers */
+#define SP_AUD_SPDIF_CH_STATUS_BASE	0xc7
+
+/* Audio IN S/PDIF Channel Status Register 4 */
+#define SP_FS_FREQ_MASK			0x0f
+#  define SP_FS_FREQ_44100HZ		0x00
+#  define SP_FS_FREQ_48000HZ		0x02
+#  define SP_FS_FREQ_32000HZ		0x03
+#  define SP_FS_FREQ_88200HZ		0x08
+#  define SP_FS_FREQ_96000HZ		0x0a
+#  define SP_FS_FREQ_176400HZ		0x0c
+#  define SP_FS_FREQ_192000HZ		0x0e
+
+/*
+ * Micellaneous Control Block
+ */
+
+/* CHIP Control Register */
+#define SP_CHIP_CTRL_REG		0xe3
+#define SP_MAN_HDMI5V_DET		BIT(3)
+#define SP_PLLLOCK_CKDT_EN		BIT(2)
+#define SP_ANALOG_CKDT_EN		BIT(1)
+#define SP_DIGITAL_CKDT_EN		BIT(0)
+
+/* Packet Receiving Status Register */
+#define SP_PACKET_RECEIVING_STATUS_REG	0xf3
+#define SP_AVI_RCVD			BIT(5)
+#define SP_VSI_RCVD			BIT(1)
+
+/***************************************************************/
+/* Register definition of device address 0x80                  */
+/***************************************************************/
+
+/* HDCP BCAPS Shadow Register */
+#define SP_HDCP_BCAPS_SHADOW_REG	0x2a
+#define SP_BCAPS_REPEATER		BIT(5)
+
+/* HDCP Status Register */
+#define SP_RX_HDCP_STATUS_REG		0x3f
+#define SP_AUTH_EN			BIT(4)
+
+/*
+ * InfoFrame and Control Packet Registers
+ */
+
+#define SP_COLORSPACE_RGB		0x00
+#define SP_COLORSPACE_YCBCR422		0x01
+#define SP_COLORSPACE_YCBCR444		0x02
+
+/* AVI InfoFrame Registers */
+#define SP_AVI_INFOFRAME_DATA_BASE	0xa4
+
+#define SP_AVI_COLOR_F_MASK		0x60
+#define SP_AVI_COLOR_F_SHIFT		5
+
+/* Audio InfoFrame Registers */
+#define SP_AUD_INFOFRAME_DATA_BASE	0xc4
+#define SP_AUD_INFOFRAME_LAYOUT_MASK	0x0f
+
+/* MPEG/HDMI Vendor Specific InfoFrame Packet type code */
+#define SP_MPEG_VS_INFOFRAME_TYPE_REG	0xe0
+
+/* MPEG/HDMI Vendor Specific InfoFrame Packet version number */
+#define SP_MPEG_VS_INFOFRAME_VER_REG	0xe1
+
+/* MPEG/HDMI Vendor Specific InfoFrame Packet content */
+#define SP_MPEG_VS_INFOFRAME_DATA_BASE	0xe4
+
+/* General Control Packet Register */
+#define SP_GENERAL_CTRL_PACKET_REG	0x9f
+#define SP_CLEAR_AVMUTE			BIT(4)
+#define SP_SET_AVMUTE			BIT(0)
+
+/***************************************************************/
+/* Register definition of device address 0x70                  */
+/***************************************************************/
+
+/* HDCP Status Register */
+#define SP_TX_HDCP_STATUS_REG		0x00
+#define SP_AUTH_FAIL			BIT(5)
+#define SP_AUTHEN_PASS			BIT(1)
+
+/* HDCP Control Register 0 */
+#define SP_HDCP_CTRL0_REG		0x01
+#define SP_RX_REPEATER			BIT(6)
+#define SP_RE_AUTH			BIT(5)
+#define SP_SW_AUTH_OK			BIT(4)
+#define SP_HARD_AUTH_EN			BIT(3)
+#define SP_HDCP_ENC_EN			BIT(2)
+#define SP_BKSV_SRM_PASS		BIT(1)
+#define SP_KSVLIST_VLD			BIT(0)
+/* HDCP Function Enabled */
+#define SP_HDCP_FUNCTION_ENABLED	(BIT(0) | BIT(1) | BIT(2) | BIT(3))
+
+/* HDCP Receiver BSTATUS Register 0 */
+#define	SP_HDCP_RX_BSTATUS0_REG		0x1b
+/* HDCP Receiver BSTATUS Register 1 */
+#define	SP_HDCP_RX_BSTATUS1_REG		0x1c
+
+/* HDCP Embedded "Blue Screen" Content Registers */
+#define SP_HDCP_VID0_BLUE_SCREEN_REG	0x2c
+#define SP_HDCP_VID1_BLUE_SCREEN_REG	0x2d
+#define SP_HDCP_VID2_BLUE_SCREEN_REG	0x2e
+
+/* HDCP Wait R0 Timing Register */
+#define SP_HDCP_WAIT_R0_TIME_REG	0x40
+
+/* HDCP Link Integrity Check Timer Register */
+#define SP_HDCP_LINK_CHECK_TIMER_REG	0x41
+
+/* HDCP Repeater Ready Wait Timer Register */
+#define SP_HDCP_RPTR_RDY_WAIT_TIME_REG	0x42
+
+/* HDCP Auto Timer Register */
+#define SP_HDCP_AUTO_TIMER_REG		0x51
+
+/* HDCP Key Status Register */
+#define SP_HDCP_KEY_STATUS_REG		0x5e
+
+/* HDCP Key Command Register */
+#define SP_HDCP_KEY_COMMAND_REG		0x5f
+#define SP_DISABLE_SYNC_HDCP		BIT(2)
+
+/* OTP Memory Key Protection Registers */
+#define SP_OTP_KEY_PROTECT1_REG		0x60
+#define SP_OTP_KEY_PROTECT2_REG		0x61
+#define SP_OTP_KEY_PROTECT3_REG		0x62
+#define SP_OTP_PSW1			0xa2
+#define SP_OTP_PSW2			0x7e
+#define SP_OTP_PSW3			0xc6
+
+/* DP System Control Registers */
+#define SP_DP_SYSTEM_CTRL_BASE		(0x80 - 1)
+/* Bits for DP System Control Register 2 */
+#define SP_CHA_STA			BIT(2)
+/* Bits for DP System Control Register 3 */
+#define SP_HPD_STATUS			BIT(6)
+#define SP_STRM_VALID			BIT(2)
+/* Bits for DP System Control Register 4 */
+#define SP_ENHANCED_MODE		BIT(3)
+
+/* DP Video Control Register */
+#define SP_DP_VIDEO_CTRL_REG		0x84
+#define SP_COLOR_F_MASK			0x06
+#define SP_COLOR_F_SHIFT		1
+#define SP_BPC_MASK			0xe0
+#define SP_BPC_SHIFT			5
+#  define SP_BPC_6BITS			0x00
+#  define SP_BPC_8BITS			0x01
+#  define SP_BPC_10BITS			0x02
+#  define SP_BPC_12BITS			0x03
+
+/* DP Audio Control Register */
+#define SP_DP_AUDIO_CTRL_REG		0x87
+#define SP_AUD_EN			BIT(0)
+
+/* 10us Pulse Generate Timer Registers */
+#define SP_I2C_GEN_10US_TIMER0_REG	0x88
+#define SP_I2C_GEN_10US_TIMER1_REG	0x89
+
+/* Packet Send Control Register */
+#define SP_PACKET_SEND_CTRL_REG		0x90
+#define SP_AUD_IF_UP			BIT(7)
+#define SP_AVI_IF_UD			BIT(6)
+#define SP_MPEG_IF_UD			BIT(5)
+#define SP_SPD_IF_UD			BIT(4)
+#define SP_AUD_IF_EN			BIT(3)
+#define SP_AVI_IF_EN			BIT(2)
+#define SP_MPEG_IF_EN			BIT(1)
+#define SP_SPD_IF_EN			BIT(0)
+
+/* DP HDCP Control Register */
+#define SP_DP_HDCP_CTRL_REG		0x92
+#define SP_AUTO_EN			BIT(7)
+#define SP_AUTO_START			BIT(5)
+#define SP_LINK_POLLING			BIT(1)
+
+/* DP Main Link Bandwidth Setting Register */
+#define SP_DP_MAIN_LINK_BW_SET_REG	0xa0
+#define SP_LINK_BW_SET_MASK		0x1f
+#  define SP_LINK_6P75G			0x19
+#  define SP_LINK_5P4G			0x14
+#  define SP_LINK_2P7G			0x0a
+#  define SP_LINK_1P62G			0x06
+#define SP_INITIAL_SLIM_M_AUD_SEL	BIT(5)
+
+/* DP Training Pattern Set Register */
+#define SP_DP_TRAINING_PATTERN_SET_REG	0xa2
+
+/* DP Lane 0 Link Training Control Register */
+#define SP_DP_LANE0_LT_CTRL_REG		0xa3
+#define SP_TX_SW_SET_MASK		0x1b
+#define SP_MAX_PRE_REACH		BIT(5)
+#define SP_MAX_DRIVE_REACH		BIT(4)
+#define SP_PRE_EMP_LEVEL1		BIT(3)
+#define SP_DRVIE_CURRENT_LEVEL1		BIT(0)
+
+/* DP Link Training Control Register */
+#define SP_DP_LT_CTRL_REG		0xa8
+#define SP_LT_ERROR_TYPE_MASK		0x70
+#  define SP_LT_NO_ERROR		0x00
+#  define SP_LT_AUX_WRITE_ERROR		0x01
+#  define SP_LT_MAX_DRIVE_REACHED	0x02
+#  define SP_LT_WRONG_LANE_COUNT_SET	0x03
+#  define SP_LT_LOOP_SAME_5_TIME	0x04
+#  define SP_LT_CR_FAIL_IN_EQ		0x05
+#  define SP_LT_EQ_LOOP_5_TIME		0x06
+#define SP_LT_EN			BIT(0)
+
+/* DP CEP Training Control Registers */
+#define SP_DP_CEP_TRAINING_CTRL0_REG	0xa9
+#define SP_DP_CEP_TRAINING_CTRL1_REG	0xaa
+
+/* DP Debug Register 1 */
+#define SP_DP_DEBUG1_REG		0xb0
+#define SP_DEBUG_PLL_LOCK		BIT(4)
+#define SP_POLLING_EN			BIT(1)
+
+/* DP Polling Control Register */
+#define SP_DP_POLLING_CTRL_REG		0xb4
+#define SP_AUTO_POLLING_DISABLE		BIT(0)
+
+/* DP Link Debug Control Register */
+#define SP_DP_LINK_DEBUG_CTRL_REG	0xb8
+#define SP_M_VID_DEBUG			BIT(5)
+#define SP_NEW_PRBS7			BIT(4)
+#define SP_INSERT_ER			BIT(1)
+#define SP_PRBS31_EN			BIT(0)
+
+/* AUX Misc control Register */
+#define SP_AUX_MISC_CTRL_REG		0xbf
+
+/* DP PLL control Register */
+#define SP_DP_PLL_CTRL_REG		0xc7
+#define SP_PLL_RST			BIT(6)
+
+/* DP Analog Power Down Register */
+#define SP_DP_ANALOG_POWER_DOWN_REG	0xc8
+#define SP_CH0_PD			BIT(0)
+
+/* DP Misc Control Register */
+#define SP_DP_MISC_CTRL_REG		0xcd
+#define SP_EQ_TRAINING_LOOP		BIT(6)
+
+/* DP Extra I2C Device Address Register */
+#define SP_DP_EXTRA_I2C_DEV_ADDR_REG	0xce
+#define SP_I2C_STRETCH_DISABLE		BIT(7)
+
+#define SP_I2C_EXTRA_ADDR		0x50
+
+/* DP DownSpreading Control Register 1 */
+#define SP_DP_DOWNSPREADING_CTRL1_REG	0xd0
+#define SP_TX_SSC_DISABLE		0xc0
+#define SP_TX_SSC_DOWNSPREADING		BIT(6)
+
+/* DP M Value Calculation Control Register */
+#define SP_DP_M_CALCULATION_CTRL_REG	0xd9
+#define SP_M_GEN_CLK_SEL		BIT(0)
+
+/* AUX Channel Access Status Register */
+#define SP_AUX_CH_STATUS_REG		0xe0
+#define SP_AUX_STATUS			0x0f
+
+/* AUX Channel DEFER Control Register */
+#define SP_AUX_DEFER_CTRL_REG		0xe2
+#define SP_DEFER_CTRL_EN		BIT(7)
+
+/* DP Buffer Data Count Register */
+#define SP_BUF_DATA_COUNT_REG		0xe4
+#define SP_BUF_CLR			BIT(7)
+
+/* DP AUX Channel Control Register 1 */
+#define SP_DP_AUX_CH_CTRL1_REG		0xe5
+#define SP_AUX_TX_COMM_MASK		0x0f
+#define SP_AUX_LENGTH_MASK		0xf0
+#define SP_AUX_LENGTH_SHIFT		4
+
+/* DP AUX CH Address Register 0 */
+#define SP_AUX_ADDR_7_0_REG		0xe6
+
+/* DP AUX CH Address Register 1 */
+#define SP_AUX_ADDR_15_8_REG		0xe7
+
+/* DP AUX CH Address Register 2 */
+#define SP_AUX_ADDR_19_16_REG		0xe8
+#define SP_AUX_ADDR_19_16_MASK		0x0f
+
+/* DP AUX Channel Control Register 2 */
+#define SP_DP_AUX_CH_CTRL2_REG		0xe9
+#define SP_AUX_SEL_RXCM			BIT(6)
+#define SP_AUX_CHSEL			BIT(3)
+#define SP_AUX_PN_INV			BIT(2)
+#define SP_ADDR_ONLY			BIT(1)
+#define SP_AUX_EN			BIT(0)
+
+/* DP Video Stream Control InfoFrame Register */
+#define SP_DP_3D_VSC_CTRL_REG		0xea
+#define SP_INFO_FRAME_VSC_EN		BIT(0)
+
+/* DP Video Stream Data Byte 1 Register */
+#define SP_DP_VSC_DB1_REG		0xeb
+
+/* DP AUX Channel Control Register 3 */
+#define SP_DP_AUX_CH_CTRL3_REG		0xec
+#define SP_WAIT_COUNTER_7_0_MASK	0xff
+
+/* DP AUX Channel Control Register 4 */
+#define SP_DP_AUX_CH_CTRL4_REG		0xed
+
+/* DP AUX Buffer Data Registers */
+#define SP_DP_BUF_DATA0_REG		0xf0
+
+/***************************************************************/
+/* Register definition of device address 0x72                  */
+/***************************************************************/
+
+/*
+ * Core Register Definitions
+ */
+
+/* Device ID Low Byte Register */
+#define SP_DEVICE_IDL_REG		0x02
+
+/* Device ID High Byte Register */
+#define SP_DEVICE_IDH_REG		0x03
+
+/* Power Down Control Register */
+#define SP_POWERDOWN_CTRL_REG		0x05
+#define SP_REGISTER_PD			BIT(7)
+#define SP_HDCP_PD			BIT(5)
+#define SP_AUDIO_PD			BIT(4)
+#define SP_VIDEO_PD			BIT(3)
+#define SP_LINK_PD			BIT(2)
+#define SP_TOTAL_PD			BIT(1)
+
+/* Reset Control Register 1 */
+#define SP_RESET_CTRL1_REG		0x06
+#define SP_MISC_RST			BIT(7)
+#define SP_VIDCAP_RST			BIT(6)
+#define SP_VIDFIF_RST			BIT(5)
+#define SP_AUDFIF_RST			BIT(4)
+#define SP_AUDCAP_RST			BIT(3)
+#define SP_HDCP_RST			BIT(2)
+#define SP_SW_RST			BIT(1)
+#define SP_HW_RST			BIT(0)
+
+/* Reset Control Register 2 */
+#define SP_RESET_CTRL2_REG		0x07
+#define SP_AUX_RST			BIT(2)
+#define SP_SERDES_FIFO_RST		BIT(1)
+#define SP_I2C_REG_RST			BIT(0)
+
+/* Video Control Register 1 */
+#define SP_VID_CTRL1_REG		0x08
+#define SP_VIDEO_EN			BIT(7)
+#define SP_VIDEO_MUTE			BIT(2)
+#define SP_DE_GEN			BIT(1)
+#define SP_DEMUX			BIT(0)
+
+/* Video Control Register 2 */
+#define SP_VID_CTRL2_REG		0x09
+#define SP_IN_COLOR_F_MASK		0x03
+#define SP_IN_YC_BIT_SEL		BIT(2)
+#define SP_IN_BPC_MASK			0x70
+#define SP_IN_BPC_SHIFT			4
+#  define SP_IN_BPC_12BIT		0x03
+#  define SP_IN_BPC_10BIT		0x02
+#  define SP_IN_BPC_8BIT		0x01
+#  define SP_IN_BPC_6BIT		0x00
+#define SP_IN_D_RANGE			BIT(7)
+
+/* Video Control Register 3 */
+#define SP_VID_CTRL3_REG		0x0a
+#define SP_HPD_OUT			BIT(6)
+
+/* Video Control Register 5 */
+#define SP_VID_CTRL5_REG		0x0c
+#define SP_CSC_STD_SEL			BIT(7)
+#define SP_XVYCC_RNG_LMT		BIT(6)
+#define SP_RANGE_Y2R			BIT(5)
+#define SP_CSPACE_Y2R			BIT(4)
+#define SP_RGB_RNG_LMT			BIT(3)
+#define SP_Y_RNG_LMT			BIT(2)
+#define SP_RANGE_R2Y			BIT(1)
+#define SP_CSPACE_R2Y			BIT(0)
+
+/* Video Control Register 6 */
+#define SP_VID_CTRL6_REG		0x0d
+#define SP_TEST_PATTERN_EN		BIT(7)
+#define SP_VIDEO_PROCESS_EN		BIT(6)
+#define SP_VID_US_MODE			BIT(3)
+#define SP_VID_DS_MODE			BIT(2)
+#define SP_UP_SAMPLE			BIT(1)
+#define SP_DOWN_SAMPLE			BIT(0)
+
+/* Video Control Register 8 */
+#define SP_VID_CTRL8_REG		0x0f
+#define SP_VID_VRES_TH			BIT(0)
+
+/* Total Line Status Low Byte Register */
+#define SP_TOTAL_LINE_STAL_REG		0x24
+
+/* Total Line Status High Byte Register */
+#define SP_TOTAL_LINE_STAH_REG		0x25
+
+/* Active Line Status Low Byte Register */
+#define SP_ACT_LINE_STAL_REG		0x26
+
+/* Active Line Status High Byte Register */
+#define SP_ACT_LINE_STAH_REG		0x27
+
+/* Vertical Front Porch Status Register */
+#define SP_V_F_PORCH_STA_REG		0x28
+
+/* Vertical SYNC Width Status Register */
+#define SP_V_SYNC_STA_REG		0x29
+
+/* Vertical Back Porch Status Register */
+#define SP_V_B_PORCH_STA_REG		0x2a
+
+/* Total Pixel Status Low Byte Register */
+#define SP_TOTAL_PIXEL_STAL_REG		0x2b
+
+/* Total Pixel Status High Byte Register */
+#define SP_TOTAL_PIXEL_STAH_REG		0x2c
+
+/* Active Pixel Status Low Byte Register */
+#define SP_ACT_PIXEL_STAL_REG		0x2d
+
+/* Active Pixel Status High Byte Register */
+#define SP_ACT_PIXEL_STAH_REG		0x2e
+
+/* Horizontal Front Porch Status Low Byte Register */
+#define SP_H_F_PORCH_STAL_REG		0x2f
+
+/* Horizontal Front Porch Statys High Byte Register */
+#define SP_H_F_PORCH_STAH_REG		0x30
+
+/* Horizontal SYNC Width Status Low Byte Register */
+#define SP_H_SYNC_STAL_REG		0x31
+
+/* Horizontal SYNC Width Status High Byte Register */
+#define SP_H_SYNC_STAH_REG		0x32
+
+/* Horizontal Back Porch Status Low Byte Register */
+#define SP_H_B_PORCH_STAL_REG		0x33
+
+/* Horizontal Back Porch Status High Byte Register */
+#define SP_H_B_PORCH_STAH_REG		0x34
+
+/* InfoFrame AVI Packet Type Register */
+#define SP_INFOFRAME_AVI_TYPE_REG	0x70
+
+/* InfoFrame AVI Packet Version Register */
+#define SP_INFOFRAME_AVI_VER_REG	0x71
+
+/* InfoFrame AVI Packet Length Register */
+#define SP_INFOFRAME_AVI_LEN_REG	0x72
+
+/* InfoFrame AVI Packet DB0 Register */
+#define SP_INFOFRAME_AVI_DB0_REG	0x73
+
+/* Bit Control Specific Register */
+#define SP_BIT_CTRL_SPECIFIC_REG	0x80
+#define SP_BIT_CTRL_SELECT_SHIFT	1
+#define SP_ENABLE_BIT_CTRL		BIT(0)
+
+/* InfoFrame Audio Packet Type Register */
+#define SP_INFOFRAME_AUD_TYPE_REG	0x83
+
+/* InfoFrame Audio Packet Version Register */
+#define SP_INFOFRAME_AUD_VER_REG	0x84
+
+/* InfoFrame Audio Packet Length Register */
+#define SP_INFOFRAME_AUD_LEN_REG	0x85
+
+/* InfoFrame Audio Packet DB0 Register */
+#define SP_INFOFRAME_AUD_DB0_REG	0x86
+
+/* InfoFrame MPEG Packet Type Register */
+#define SP_INFOFRAME_MPEG_TYPE_REG	0xb0
+
+/* InfoFrame MPEG Packet Version Register */
+#define SP_INFOFRAME_MPEG_VER_REG	0xb1
+
+/* InfoFrame MPEG Packet Length Register */
+#define SP_INFOFRAME_MPEG_LEN_REG	0xb2
+
+/* InfoFrame MPEG Packet DB0 Register */
+#define SP_INFOFRAME_MPEG_DB0_REG	0xb3
+
+/* Audio Channel Status Registers */
+#define SP_AUD_CH_STATUS_BASE		(0xd0 - 1)
+
+/* Audio Channel Num Register 5 */
+#define SP_I2S_CHANNEL_NUM_MASK		0xe0
+#  define SP_I2S_CH_NUM_1		(0x00 << 5)
+#  define SP_I2S_CH_NUM_2		(0x01 << 5)
+#  define SP_I2S_CH_NUM_3		(0x02 << 5)
+#  define SP_I2S_CH_NUM_4		(0x03 << 5)
+#  define SP_I2S_CH_NUM_5		(0x04 << 5)
+#  define SP_I2S_CH_NUM_6		(0x05 << 5)
+#  define SP_I2S_CH_NUM_7		(0x06 << 5)
+#  define SP_I2S_CH_NUM_8		(0x07 << 5)
+#define SP_EXT_VUCP			BIT(2)
+#define SP_VBIT				BIT(1)
+#define SP_AUDIO_LAYOUT			BIT(0)
+
+/* Analog Debug Register 2 */
+#define SP_ANALOG_DEBUG2_REG		0xdd
+#define SP_FORCE_SW_OFF_BYPASS		0x20
+#define SP_XTAL_FRQ			0x1c
+#  define SP_XTAL_FRQ_19M2		(0x00 << 2)
+#  define SP_XTAL_FRQ_24M		(0x01 << 2)
+#  define SP_XTAL_FRQ_25M		(0x02 << 2)
+#  define SP_XTAL_FRQ_26M		(0x03 << 2)
+#  define SP_XTAL_FRQ_27M		(0x04 << 2)
+#  define SP_XTAL_FRQ_38M4		(0x05 << 2)
+#  define SP_XTAL_FRQ_52M		(0x06 << 2)
+#define SP_POWERON_TIME_1P5MS		0x03
+
+/* Analog Control 0 Register */
+#define SP_ANALOG_CTRL0_REG		0xe1
+
+/* Common Interrupt Status Register 1 */
+#define SP_COMMON_INT_STATUS_BASE	(0xf1 - 1)
+#define SP_PLL_LOCK_CHG			0x40
+
+/* Common Interrupt Status Register 2 */
+#define SP_COMMON_INT_STATUS2		0xf2
+#define SP_HDCP_AUTH_CHG		BIT(1)
+#define SP_HDCP_AUTH_DONE		BIT(0)
+
+#define SP_HDCP_LINK_CHECK_FAIL		BIT(0)
+
+#define SP_HPD_IRQ			BIT(6)
+#define SP_HPD_ESYNC_ERR		BIT(4)
+#define SP_HPD_CHG			BIT(2)
+#define SP_HPD_LOST			BIT(1)
+#define SP_HPD_PLUG			BIT(0)
+
+/* DP Interrupt Status Register */
+#define SP_DP_INT_STATUS_REG		0xf7
+#define SP_TRAINING_FINISH		BIT(5)
+#define SP_POLLING_ERR			BIT(4)
+
+/* Common Interrupt Mask Register */
+#define SP_COMMON_INT_MASK_BASE		(0xf8 - 1)
+
+/* Interrupt Control Register */
+#define SP_INT_CTRL_REG			0xff
+
+/***************************************************************/
+/* Register definition of device address 0x7a                  */
+/***************************************************************/
+
+/* DP TX Link Training Control Register */
+#define SP_DP_TX_LT_CTRL0_REG		0x30
+
+/* PD 1.2 Lint Training 80bit Pattern Register */
+#define SP_DP_LT_80BIT_PATTERN0_REG	0x80
+#define SP_DP_LT_80BIT_PATTERN_REG_NUM	10
+
+/* Audio Interface Control Register 0 */
+#define SP_AUD_INTERFACE_CTRL0_REG	0x5f
+#define SP_AUD_INTERFACE_DISABLE	0x80
+
+/* Audio Interface Control Register 2 */
+#define SP_AUD_INTERFACE_CTRL2_REG	0x60
+#define SP_M_AUD_ADJUST_ST		0x04
+
+/* Audio Interface Control Register 3 */
+#define SP_AUD_INTERFACE_CTRL3_REG	0x62
+
+/* Audio Interface Control Register 4 */
+#define SP_AUD_INTERFACE_CTRL4_REG	0x67
+
+/* Audio Interface Control Register 5 */
+#define SP_AUD_INTERFACE_CTRL5_REG	0x68
+
+/* Audio Interface Control Register 6 */
+#define SP_AUD_INTERFACE_CTRL6_REG	0x69
+
+/* Firmware Version Register */
+#define SP_FW_VER_REG			0xb7
+
+/***************************************************************/
+/* Definition of DPCD                                          */
+/***************************************************************/
+#define SP_DPCD_MAX_LINK_RATE		0x01
+
+#define SP_DPCD_MAX_LANE_COUNT		0x02
+#define SP_ENHANCED_FRAME_CAP		0x80
+
+#define SP_DPCD_LANE_COUNT_SET		0x01
+#define SP_ENHANCED_FRAME_EN		0x80
+
+#define SP_DPCD_DOWNSPREADING_CTRL	0x07
+#define SP_SPREAD_AMPLITUDE		0x10
+
+#define SP_DPCD_SINK_COUNT		0x00
+#define SP_DPCD_SERVICE_IRQ_VECTOR	0x01
+#define SP_TEST_IRQ			0x02
+#define SP_CP_IRQ			0x04
+
+#define SP_TEST_ACK			0x01
+
+#endif
-- 
2.1.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCHv5 2/3] devicetree: Add new ANX7814 SlimPort transmitter binding.
  2015-11-13 12:01 ` [PATCHv5 2/3] devicetree: Add new ANX7814 SlimPort transmitter binding Enric Balletbo i Serra
@ 2015-11-13 14:37   ` Rob Herring
  0 siblings, 0 replies; 8+ messages in thread
From: Rob Herring @ 2015-11-13 14:37 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: devicetree, linux-kernel, dri-devel, devel, pawel.moll,
	mark.rutland, ijc+devicetree, galak, airlied, gregkh,
	sjoerd.simons, javier, span, nathan.chung, djkurtz, drinkcat,
	laurent.pinchart, dan.carpenter, jb.tsai, cawa.cheng, eddie.huang,
	cjiao

On Fri, Nov 13, 2015 at 01:01:03PM +0100, Enric Balletbo i Serra wrote:
> The ANX7814 is an ultra-low power Full-HD (1080p60) SlimPort transmitter
> designed for portable devices.
> 
> You can add support to your board with current binding.
> 
> Example:
> 
> 	anx7814: anx7814@38 {
> 		compatible = "analogix,anx7814";
> 		reg = <0x38>;
> 		pd-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
> 		reset-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
> 	};
> 
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> ---
>  .../devicetree/bindings/video/bridge/anx7814.txt   | 36 ++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/video/bridge/anx7814.txt
> 
> diff --git a/Documentation/devicetree/bindings/video/bridge/anx7814.txt b/Documentation/devicetree/bindings/video/bridge/anx7814.txt
> new file mode 100644
> index 0000000..f7bdca9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/video/bridge/anx7814.txt
> @@ -0,0 +1,36 @@
> +Analogix ANX7814 SlimPort (Full-HD Transmitter)
> +-----------------------------------------------
> +
> +The ANX7814 is an ultra-low power Full-HD (1080p60) SlimPort transmitter
> +designed for portable devices.
> +
> +Required properties:
> +
> + - compatible		: "analogix,anx7814"
> + - reg			: I2C address of the device
> + - cable-det-gpios	: Which GPIO to use for cable detection

Shouldn't this be an interrupt instead (to a gpio controller still)?

> + - pd-gpios		: Which GPIO to use for power down
> + - reset-gpios		: Which GPIO to use for reset
> +
> +Optional properties:
> +
> + - v10-gpios	: Which GPIO to use for V10 control.
> + - video interfaces: Device node can contain video interface port nodes.

Please specify how many ports and how many endpoints for each port.

> +
> +Example:
> +
> +	anx7814: anx7814@38 {
> +		compatible = "analogix,anx7814";
> +		reg = <0x38>;
> +		cable-det-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
> +		pd-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
> +		reset-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
> +		v10-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
> +		ports {
> +			port@0 {

Either simplify to just port (dropping ports) or add a reg property 
here.

> +				anx7814_in: endpoint {
> +					remote-endpoint = <&hdmi0_out>;
> +				};
> +			};
> +		};
> +	};
> -- 
> 2.1.0
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc.
  2015-11-13 12:01 ` [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc Enric Balletbo i Serra
@ 2015-11-13 14:38   ` Rob Herring
  2015-11-13 15:12     ` Enric Balletbo Serra
  0 siblings, 1 reply; 8+ messages in thread
From: Rob Herring @ 2015-11-13 14:38 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: devel, devicetree, drinkcat, pawel.moll, ijc+devicetree, jb.tsai,
	gregkh, cjiao, linux-kernel, dri-devel, cawa.cheng, sjoerd.simons,
	laurent.pinchart, galak, nathan.chung, mark.rutland, eddie.huang,
	javier, dan.carpenter, span

On Fri, Nov 13, 2015 at 01:01:02PM +0100, Enric Balletbo i Serra wrote:
> Analogix Semiconductor develops analog and mixed-signal devices for digital
> media and communications interconnect applications.
> 
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 82d2ac9..8987ee8 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -22,6 +22,7 @@ ampire	Ampire Co., Ltd.
>  ams	AMS AG
>  amstaos	AMS-Taos Inc.
>  apm	Applied Micro Circuits Corporation (APM)
> +analogix	Analogix Semiconductor, Inc.

Not quite alphabetical order.

>  aptina	Aptina Imaging
>  arasan	Arasan Chip Systems
>  arm	ARM Ltd.
> -- 
> 2.1.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc.
  2015-11-13 14:38   ` Rob Herring
@ 2015-11-13 15:12     ` Enric Balletbo Serra
  0 siblings, 0 replies; 8+ messages in thread
From: Enric Balletbo Serra @ 2015-11-13 15:12 UTC (permalink / raw)
  To: Rob Herring
  Cc: devel, devicetree@vger.kernel.org, drinkcat, Pawel Moll,
	Ian Campbell, jb.tsai, Greg Kroah-Hartman, cjiao, linux-kernel,
	dri-devel, cawa.cheng, Sjoerd Simons, Laurent Pinchart,
	Kumar Gala, Nathan Chung, Mark Rutland, eddie.huang,
	Javier Martinez Canillas, dan.carpenter, span

Hi Rob,

2015-11-13 15:38 GMT+01:00 Rob Herring <robh@kernel.org>:
> On Fri, Nov 13, 2015 at 01:01:02PM +0100, Enric Balletbo i Serra wrote:
>> Analogix Semiconductor develops analog and mixed-signal devices for digital
>> media and communications interconnect applications.
>>
>> Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
>> Acked-by: Rob Herring <robh@kernel.org>
>> ---
>>  Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index 82d2ac9..8987ee8 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -22,6 +22,7 @@ ampire      Ampire Co., Ltd.
>>  ams  AMS AG
>>  amstaos      AMS-Taos Inc.
>>  apm  Applied Micro Circuits Corporation (APM)
>> +analogix     Analogix Semiconductor, Inc.
>
> Not quite alphabetical order.
>

Yep, sorry, I think I introduced this mistake rebasing my patches  ...
I will fix in next version, thanks.

>>  aptina       Aptina Imaging
>>  arasan       Arasan Chip Systems
>>  arm  ARM Ltd.
>> --
>> 2.1.0
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCHv5 3/3] drm: bridge: anx78xx: Add anx78xx driver support by analogix.
  2015-11-13 12:01 ` [PATCHv5 3/3] drm: bridge: anx78xx: Add anx78xx driver support by analogix Enric Balletbo i Serra
@ 2015-11-16  1:03   ` kbuild test robot
  0 siblings, 0 replies; 8+ messages in thread
From: kbuild test robot @ 2015-11-16  1:03 UTC (permalink / raw)
  To: Enric Balletbo i Serra
  Cc: mark.rutland, sjoerd.simons, span, devel, drinkcat, cawa.cheng,
	nathan.chung, cjiao, dan.carpenter, devicetree, pawel.moll,
	ijc+devicetree, robh+dt, dri-devel, eddie.huang, javier, jb.tsai,
	gregkh, linux-kernel, kbuild-all, galak, laurent.pinchart

[-- Attachment #1: Type: text/plain, Size: 1396 bytes --]

Hi Enric,

[auto build test ERROR on: robh/for-next]
[also build test ERROR on: v4.3 next-20151115]
[cannot apply to: drm/drm-next]

url:    https://github.com/0day-ci/linux/commits/Enric-Balletbo-i-Serra/Add-initial-support-for-slimport-anx78xx/20151113-200502
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux for-next
config: i386-randconfig-h1-11160814 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c: In function 'anx78xx_i2c_probe':
>> drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c:203:17: error: 'struct drm_bridge' has no member named 'of_node'
     anx78xx->bridge.of_node = client->dev.of_node;
                    ^

vim +203 drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c

   197		anx78xx->pdata = devm_kzalloc(&client->dev,
   198					      sizeof(struct anx78xx_platform_data),
   199					      GFP_KERNEL);
   200		if (!anx78xx->pdata)
   201			return -ENOMEM;
   202	
 > 203		anx78xx->bridge.of_node = client->dev.of_node;
   204		anx78xx->bridge.funcs = &anx78xx_bridge_funcs;
   205		ret = drm_bridge_add(&anx78xx->bridge);
   206		if (ret < 0) {

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 21738 bytes --]

[-- Attachment #3: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2015-11-16  1:03 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-13 12:01 [PATCHv5 0/3] Add initial support for slimport anx78xx Enric Balletbo i Serra
2015-11-13 12:01 ` [PATCHv5 1/3] of: Add vendor prefix for Analogix Semiconductor, Inc Enric Balletbo i Serra
2015-11-13 14:38   ` Rob Herring
2015-11-13 15:12     ` Enric Balletbo Serra
2015-11-13 12:01 ` [PATCHv5 2/3] devicetree: Add new ANX7814 SlimPort transmitter binding Enric Balletbo i Serra
2015-11-13 14:37   ` Rob Herring
2015-11-13 12:01 ` [PATCHv5 3/3] drm: bridge: anx78xx: Add anx78xx driver support by analogix Enric Balletbo i Serra
2015-11-16  1:03   ` kbuild test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).