* [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).