summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2023-04-20 21:32:27 +0300
committerYuval Adam <_@yuv.al>2023-04-20 21:39:59 +0300
commit3edef309190448fa2ce83e2e4fd7baec17f3d58b (patch)
treeab8f5942fcb42f0954e37ff817e160eb61a8b795
parentd5b5a5e397f5d430439193869bd3dda5c674f34f (diff)
Import patches from reform-debian-packages
-rw-r--r--linux-mnt-pocket-reform/patches/0001-nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS.patch37
-rw-r--r--linux-mnt-pocket-reform/patches/0002-pci-imx6-add-support-for-internal-refclk-imx8mq.patch95
-rw-r--r--linux-mnt-pocket-reform/patches/0003-lcdif-fix-pcie-interference.patch70
-rw-r--r--linux-mnt-pocket-reform/patches/0004-mnt4002-imx-gpcv2-wake-smccc.patch.patch94
-rw-r--r--linux-mnt-pocket-reform/patches/0005-imx8mq-import-HDMI-driver-and-make-DCSS-compatible-w.patch6875
5 files changed, 7171 insertions, 0 deletions
diff --git a/linux-mnt-pocket-reform/patches/0001-nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS.patch b/linux-mnt-pocket-reform/patches/0001-nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS.patch
new file mode 100644
index 0000000..cc85e9f
--- /dev/null
+++ b/linux-mnt-pocket-reform/patches/0001-nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS.patch
@@ -0,0 +1,37 @@
+From c3afae0a6418f8b05c5d1a28442ca6c5dd6bc5d7 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Wed, 7 Sep 2022 06:20:37 +0200
+Subject: [PATCH 1/6] nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS
+
+---
+ drivers/gpu/drm/bridge/nwl-dsi.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
+index 6dc2a4e..13c5ade 100644
+--- a/drivers/gpu/drm/bridge/nwl-dsi.c
++++ b/drivers/gpu/drm/bridge/nwl-dsi.c
+@@ -822,10 +822,17 @@ static int nwl_dsi_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_connector_state *conn_state)
+ {
+ struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
++ struct device_node *remote;
++ struct nwl_dsi *dsi = bridge_to_dsi(bridge);
+
+- /* At least LCDIF + NWL needs active high sync */
+- adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+- adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
++ remote = of_graph_get_remote_node(dsi->dev->of_node, 0,
++ NWL_DSI_ENDPOINT_LCDIF);
++
++ if (remote) {
++ /* At least LCDIF + NWL needs active high sync */
++ adjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
++ adjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
++ }
+
+ /*
+ * Do a full modeset if crtc_state->active is changed to be true.
+--
+2.39.0
+
diff --git a/linux-mnt-pocket-reform/patches/0002-pci-imx6-add-support-for-internal-refclk-imx8mq.patch b/linux-mnt-pocket-reform/patches/0002-pci-imx6-add-support-for-internal-refclk-imx8mq.patch
new file mode 100644
index 0000000..aee1c48
--- /dev/null
+++ b/linux-mnt-pocket-reform/patches/0002-pci-imx6-add-support-for-internal-refclk-imx8mq.patch
@@ -0,0 +1,95 @@
+From 01951ef9d3d9d52640feaf8272c0fea75703bff1 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sat, 22 Oct 2022 17:11:19 +0200
+Subject: [PATCH 2/6] pci-imx6-add-support-for-internal-refclk-imx8mq
+
+---
+ drivers/pci/controller/dwc/pci-imx6.c | 44 +++++++++++++++++++++++++--
+ 1 file changed, 42 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
+index 1dde5c5..58d9591 100644
+--- a/drivers/pci/controller/dwc/pci-imx6.c
++++ b/drivers/pci/controller/dwc/pci-imx6.c
+@@ -69,6 +69,7 @@ struct imx6_pcie {
+ struct dw_pcie *pci;
+ int reset_gpio;
+ bool gpio_active_high;
++ bool internal_refclk;
+ bool link_is_up;
+ struct clk *pcie_bus;
+ struct clk *pcie_phy;
+@@ -157,6 +158,40 @@ static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
+ return imx6_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
+ }
+
++#define IMX8MQ_ANA_PLLOUT_REG 0x74
++#define IMX8MQ_ANA_PLLOUT_CKE BIT(4)
++#define IMX8MQ_ANA_PLLOUT_SEL_MASK 0xF
++#define IMX8MQ_ANA_PLLOUT_SEL_SYSPLL1 0xB
++#define IMX8MQ_ANA_PLLOUT_DIV_REG 0x7C
++#define IMX8MQ_ANA_PLLOUT_SYSPLL1_DIV 0x7
++
++static void imx6_pcie_enable_internal_refclk(void)
++{
++ uint32_t val;
++ struct device_node* np;
++ void __iomem *base;
++
++ np = of_find_compatible_node(NULL, NULL,
++ "fsl,imx8mq-anatop");
++ base = of_iomap(np, 0);
++ WARN_ON(!base);
++
++ val = readl(base + IMX8MQ_ANA_PLLOUT_REG);
++ val &= ~IMX8MQ_ANA_PLLOUT_SEL_MASK;
++ val |= IMX8MQ_ANA_PLLOUT_SEL_SYSPLL1;
++ writel(val, base + IMX8MQ_ANA_PLLOUT_REG);
++ /* SYS_PLL1 is 800M, PCIE REF CLK is 100M */
++ val = readl(base + IMX8MQ_ANA_PLLOUT_DIV_REG);
++ val |= IMX8MQ_ANA_PLLOUT_SYSPLL1_DIV;
++ writel(val, base + IMX8MQ_ANA_PLLOUT_DIV_REG);
++
++ val = readl(base + IMX8MQ_ANA_PLLOUT_REG);
++ val |= IMX8MQ_ANA_PLLOUT_CKE;
++ writel(val, base + IMX8MQ_ANA_PLLOUT_REG);
++
++ usleep_range(9000,10000);
++}
++
+ static void imx6_pcie_configure_type(struct imx6_pcie *imx6_pcie)
+ {
+ unsigned int mask, val;
+@@ -302,6 +337,9 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, u16 data)
+
+ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
+ {
++ if (imx6_pcie->internal_refclk)
++ imx6_pcie_enable_internal_refclk();
++
+ switch (imx6_pcie->drvdata->variant) {
+ case IMX8MM:
+ case IMX8MP:
+@@ -318,7 +356,8 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
+ regmap_update_bits(imx6_pcie->iomuxc_gpr,
+ imx6_pcie_grp_offset(imx6_pcie),
+ IMX8MQ_GPR_PCIE_REF_USE_PAD,
+- IMX8MQ_GPR_PCIE_REF_USE_PAD);
++ (imx6_pcie->internal_refclk ?
++ 0 : IMX8MQ_GPR_PCIE_REF_USE_PAD));
+ /*
+ * Regarding the datasheet, the PCIE_VPH is suggested
+ * to be 1.8V. If the PCIE_VPH is supplied by 3.3V, the
+@@ -1169,7 +1208,8 @@ static int imx6_pcie_probe(struct platform_device *pdev)
+ imx6_pcie->pcie_aux = devm_clk_get(dev, "pcie_aux");
+ if (IS_ERR(imx6_pcie->pcie_aux))
+ return dev_err_probe(dev, PTR_ERR(imx6_pcie->pcie_aux),
+- "pcie_aux clock source missing or invalid\n");
++ "pcie_aux clock source missing or invalid\n");
++ imx6_pcie->internal_refclk = of_property_read_bool(node, "internal-refclk");
+ fallthrough;
+ case IMX7D:
+ if (dbi_base->start == IMX8MQ_PCIE2_BASE_ADDR)
+--
+2.39.0
+
diff --git a/linux-mnt-pocket-reform/patches/0003-lcdif-fix-pcie-interference.patch b/linux-mnt-pocket-reform/patches/0003-lcdif-fix-pcie-interference.patch
new file mode 100644
index 0000000..142cf33
--- /dev/null
+++ b/linux-mnt-pocket-reform/patches/0003-lcdif-fix-pcie-interference.patch
@@ -0,0 +1,70 @@
+From 7ebe66c198cc1f76d55bd42d81ae724f74b832a9 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Wed, 7 Sep 2022 06:23:35 +0200
+Subject: [PATCH 3/6] lcdif-fix-pcie-interference
+
+---
+ drivers/gpu/drm/mxsfb/mxsfb_kms.c | 34 ++++++++++++++++++++++++++++++-
+ 1 file changed, 33 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
+index 3bcc9c0..5b5b01f 100644
+--- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c
++++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
+@@ -303,7 +303,7 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb,
+
+ mxsfb_set_formats(mxsfb, bus_format);
+
+- clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
++ clk_set_rate(mxsfb->clk, m->crtc_clock * 660);
+
+ mxsfb_set_mode(mxsfb, bus_flags);
+ }
+@@ -662,12 +662,44 @@ static const uint64_t mxsfb_modifiers[] = {
+ * Initialization
+ */
+
++void imx8mq_pcie_qos_for_lcdif(void)
++{
++ void __iomem *qosc = ioremap(0x307f0000, 0x2100);
++ // clock and unlock QoSC registers
++ writel(0x0, qosc);
++ writel(0x1, qosc);
++ writel(0x0, qosc+0x60);
++
++ // limit number of outstanding transactions for PCIe1
++ writel(0x0, qosc+0x1000);
++ writel(0x1, qosc+0x1000);
++ writel(0x01010100, qosc+0x1050);
++ writel(0x01010100, qosc+0x1060);
++ writel(0x01010100, qosc+0x1070);
++ writel(0x1, qosc+0x1000);
++
++ // limit number of outstanding transactions for PCIe2
++ writel(0x0, qosc+0x2000);
++ writel(0x1, qosc+0x2000);
++ writel(0x01010100, qosc+0x2050);
++ writel(0x01010100, qosc+0x2060);
++ writel(0x01010100, qosc+0x2070);
++ writel(0x1, qosc+0x2000);
++
++ iounmap(qosc);
++}
++
+ int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
+ {
+ struct drm_encoder *encoder = &mxsfb->encoder;
+ struct drm_crtc *crtc = &mxsfb->crtc;
+ int ret;
+
++ /*
++ FIXME Workaround to fix PCIe interfering with LCDIF refresh (MNT Reform)
++ */
++ imx8mq_pcie_qos_for_lcdif();
++
+ drm_plane_helper_add(&mxsfb->planes.primary,
+ &mxsfb_plane_primary_helper_funcs);
+ ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
+--
+2.39.0
+
diff --git a/linux-mnt-pocket-reform/patches/0004-mnt4002-imx-gpcv2-wake-smccc.patch.patch b/linux-mnt-pocket-reform/patches/0004-mnt4002-imx-gpcv2-wake-smccc.patch.patch
new file mode 100644
index 0000000..d8af63b
--- /dev/null
+++ b/linux-mnt-pocket-reform/patches/0004-mnt4002-imx-gpcv2-wake-smccc.patch.patch
@@ -0,0 +1,94 @@
+From ededab5e560e2143c0277dd230b879f109cfbf5e Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Wed, 7 Sep 2022 06:24:04 +0200
+Subject: [PATCH 4/6] mnt4002-imx-gpcv2-wake-smccc.patch
+
+---
+ drivers/irqchip/irq-imx-gpcv2.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/drivers/irqchip/irq-imx-gpcv2.c b/drivers/irqchip/irq-imx-gpcv2.c
+index b9c22f7..4f6d924 100644
+--- a/drivers/irqchip/irq-imx-gpcv2.c
++++ b/drivers/irqchip/irq-imx-gpcv2.c
+@@ -3,6 +3,7 @@
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ */
+
++#include <linux/arm-smccc.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+ #include <linux/slab.h>
+@@ -17,6 +18,13 @@
+ #define GPC_IMR1_CORE2 0x1c0
+ #define GPC_IMR1_CORE3 0x1d0
+
++#define FSL_SIP_GPC 0xC2000000
++#define FSL_SIP_CONFIG_GPC_MASK 0x00
++#define FSL_SIP_CONFIG_GPC_UNMASK 0x01
++#define FSL_SIP_CONFIG_GPC_SET_WAKE 0x02
++#define FSL_SIP_CONFIG_GPC_PM_DOMAIN 0x03
++#define FSL_SIP_CONFIG_GPC_SET_AFF 0x04
++#define FSL_SIP_CONFIG_GPC_CORE_WAKE 0x05
+
+ struct gpcv2_irqchip_data {
+ struct raw_spinlock rlock;
+@@ -76,12 +84,17 @@ static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on)
+ unsigned int idx = d->hwirq / 32;
+ unsigned long flags;
+ u32 mask, val;
++ struct arm_smccc_res res;
+
+ raw_spin_lock_irqsave(&cd->rlock, flags);
+ mask = BIT(d->hwirq % 32);
+ val = cd->wakeup_sources[idx];
+
+ cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask);
++
++ // save wakeup config in vendor tf-a
++ arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_SET_WAKE, d->hwirq, on, 0, 0, 0, 0, &res);
++
+ raw_spin_unlock_irqrestore(&cd->rlock, flags);
+
+ /*
+@@ -97,6 +110,7 @@ static void imx_gpcv2_irq_unmask(struct irq_data *d)
+ struct gpcv2_irqchip_data *cd = d->chip_data;
+ void __iomem *reg;
+ u32 val;
++ struct arm_smccc_res res;
+
+ raw_spin_lock(&cd->rlock);
+ reg = gpcv2_idx_to_reg(cd, d->hwirq / 32);
+@@ -105,6 +119,10 @@ static void imx_gpcv2_irq_unmask(struct irq_data *d)
+ writel_relaxed(val, reg);
+ raw_spin_unlock(&cd->rlock);
+
++ // call into vendor tf-a
++ //arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_UNMASK,
++ // d->hwirq, 0, 0, 0, 0, 0, &res);
++
+ irq_chip_unmask_parent(d);
+ }
+
+@@ -113,12 +131,18 @@ static void imx_gpcv2_irq_mask(struct irq_data *d)
+ struct gpcv2_irqchip_data *cd = d->chip_data;
+ void __iomem *reg;
+ u32 val;
++ struct arm_smccc_res res;
+
+ raw_spin_lock(&cd->rlock);
+ reg = gpcv2_idx_to_reg(cd, d->hwirq / 32);
+ val = readl_relaxed(reg);
+ val |= BIT(d->hwirq % 32);
+ writel_relaxed(val, reg);
++
++ // call into vendor tf-a
++ //arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_MASK,
++ // d->hwirq, 0, 0, 0, 0, 0, &res);
++
+ raw_spin_unlock(&cd->rlock);
+
+ irq_chip_mask_parent(d);
+--
+2.39.0
+
diff --git a/linux-mnt-pocket-reform/patches/0005-imx8mq-import-HDMI-driver-and-make-DCSS-compatible-w.patch b/linux-mnt-pocket-reform/patches/0005-imx8mq-import-HDMI-driver-and-make-DCSS-compatible-w.patch
new file mode 100644
index 0000000..b07bf0f
--- /dev/null
+++ b/linux-mnt-pocket-reform/patches/0005-imx8mq-import-HDMI-driver-and-make-DCSS-compatible-w.patch
@@ -0,0 +1,6875 @@
+From 7c11eb42d4434c041f00a1f4d9668221d0151687 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sat, 22 Oct 2022 17:12:28 +0200
+Subject: [PATCH 5/6] imx8mq: import HDMI driver and make DCSS compatible with
+ both HDMI and LCDIF
+
+---
+ drivers/gpu/drm/bridge/cadence/Kconfig | 26 +
+ drivers/gpu/drm/bridge/cadence/Makefile | 9 +
+ drivers/gpu/drm/bridge/cadence/cdns-dp-core.c | 590 +++++++++
+ .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 689 +++++++++++
+ .../gpu/drm/bridge/cadence/cdns-mhdp-audio.c | 393 ++++++
+ .../gpu/drm/bridge/cadence/cdns-mhdp-cec.c | 341 ++++++
+ .../gpu/drm/bridge/cadence/cdns-mhdp-common.c | 1059 +++++++++++++++++
+ .../gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c | 357 ++++++
+ drivers/gpu/drm/bridge/cadence/cdns-mhdp.h | 209 ++++
+ drivers/gpu/drm/imx/Kconfig | 9 +
+ drivers/gpu/drm/imx/Makefile | 4 +
+ drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c | 529 ++++++++
+ drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c | 777 ++++++++++++
+ drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c | 211 ++++
+ drivers/gpu/drm/imx/cdn-mhdp-phy.h | 155 +++
+ drivers/gpu/drm/imx/cdns-mhdp-imx.h | 75 ++
+ drivers/gpu/drm/imx/dcss/dcss-dev.c | 20 +-
+ drivers/gpu/drm/imx/dcss/dcss-dev.h | 1 +
+ drivers/gpu/drm/imx/dcss/dcss-drv.c | 103 +-
+ drivers/gpu/drm/imx/dcss/dcss-dtg.c | 25 +-
+ drivers/gpu/drm/imx/dcss/dcss-kms.c | 46 +-
+ drivers/gpu/drm/imx/dcss/dcss-kms.h | 4 +-
+ include/drm/bridge/cdns-mhdp-cbs.h | 29 +
+ include/drm/bridge/cdns-mhdp-common.h | 812 +++++++++++++
+ 24 files changed, 6427 insertions(+), 46 deletions(-)
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
+ create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp.h
+ create mode 100644 drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c
+ create mode 100644 drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c
+ create mode 100644 drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c
+ create mode 100644 drivers/gpu/drm/imx/cdn-mhdp-phy.h
+ create mode 100644 drivers/gpu/drm/imx/cdns-mhdp-imx.h
+ create mode 100644 include/drm/bridge/cdns-mhdp-cbs.h
+ create mode 100644 include/drm/bridge/cdns-mhdp-common.h
+
+diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig
+index 1d06182..5aa976e 100644
+--- a/drivers/gpu/drm/bridge/cadence/Kconfig
++++ b/drivers/gpu/drm/bridge/cadence/Kconfig
+@@ -25,3 +25,29 @@ config DRM_CDNS_MHDP8546_J721E
+ initializes the J721E Display Port and sets up the
+ clock and data muxes.
+ endif
++
++config DRM_CDNS_MHDP
++ tristate "Cadence MHDP COMMON API driver"
++ select DRM_KMS_HELPER
++ select DRM_PANEL_BRIDGE
++ depends on OF
++ help
++ Support Cadence MHDP API library.
++
++config DRM_CDNS_HDMI
++ tristate "Cadence HDMI DRM driver"
++ depends on DRM_CDNS_MHDP
++
++config DRM_CDNS_DP
++ tristate "Cadence DP DRM driver"
++ depends on DRM_CDNS_MHDP
++
++config DRM_CDNS_AUDIO
++ tristate "Cadence MHDP Audio driver"
++ depends on DRM_CDNS_MHDP
++
++config DRM_CDNS_HDMI_CEC
++ tristate "Cadence MHDP HDMI CEC driver"
++ depends on DRM_CDNS_HDMI
++ select CEC_CORE
++ select CEC_NOTIFIER
+diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile
+index 4d2db8d..a83e739 100644
+--- a/drivers/gpu/drm/bridge/cadence/Makefile
++++ b/drivers/gpu/drm/bridge/cadence/Makefile
+@@ -2,3 +2,12 @@
+ obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o
+ cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o
+ cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o
++
++cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-hdmi.o
++
++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o
++#cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o
++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_AUDIO) += cdns-mhdp-audio.o
++cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI_CEC) += cdns-mhdp-cec.o
++
++obj-$(CONFIG_DRM_CDNS_MHDP) += cdns_mhdp_drmcore.o
+diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
+new file mode 100644
+index 0000000..78defa0
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
+@@ -0,0 +1,590 @@
++/*
++ * Cadence Display Port Interface (DP) driver
++ *
++ * Copyright (C) 2019 NXP Semiconductor, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ */
++#include <drm/bridge/cdns-mhdp-common.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_edid.h>
++#include <drm/drm_encoder_slave.h>
++#include <drm/drm_of.h>
++#include <drm/drm_probe_helper.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/of_device.h>
++
++/*
++ * This function only implements native DPDC reads and writes
++ */
++static ssize_t dp_aux_transfer(struct drm_dp_aux *aux,
++ struct drm_dp_aux_msg *msg)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(aux->dev);
++ bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
++ int ret;
++
++ /* Ignore address only message */
++ if ((msg->size == 0) || (msg->buffer == NULL)) {
++ msg->reply = native ?
++ DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
++ return msg->size;
++ }
++
++ if (!native) {
++ dev_err(mhdp->dev, "%s: only native messages supported\n", __func__);
++ return -EINVAL;
++ }
++
++ /* msg sanity check */
++ if (msg->size > DP_AUX_MAX_PAYLOAD_BYTES) {
++ dev_err(mhdp->dev, "%s: invalid msg: size(%zu), request(%x)\n",
++ __func__, msg->size, (unsigned int)msg->request);
++ return -EINVAL;
++ }
++
++ if (msg->request == DP_AUX_NATIVE_WRITE) {
++ const u8 *buf = msg->buffer;
++ int i;
++ for (i = 0; i < msg->size; ++i) {
++ ret = cdns_mhdp_dpcd_write(mhdp,
++ msg->address + i, buf[i]);
++ if (!ret)
++ continue;
++
++ DRM_DEV_ERROR(mhdp->dev, "Failed to write DPCD\n");
++
++ return ret;
++ }
++ }
++
++ if (msg->request == DP_AUX_NATIVE_READ) {
++ ret = cdns_mhdp_dpcd_read(mhdp, msg->address, msg->buffer, msg->size);
++ if (ret < 0)
++ return -EIO;
++ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
++ return msg->size;
++ }
++ return 0;
++}
++
++static int dp_aux_init(struct cdns_mhdp_device *mhdp,
++ struct device *dev)
++{
++ int ret;
++
++ mhdp->dp.aux.name = "imx_dp_aux";
++ mhdp->dp.aux.dev = dev;
++ mhdp->dp.aux.transfer = dp_aux_transfer;
++
++ ret = drm_dp_aux_register(&mhdp->dp.aux);
++
++ return ret;
++}
++
++static int dp_aux_destroy(struct cdns_mhdp_device *mhdp)
++{
++ drm_dp_aux_unregister(&mhdp->dp.aux);
++ return 0;
++}
++
++static void dp_pixel_clk_reset(struct cdns_mhdp_device *mhdp)
++{
++ u32 val;
++
++ /* reset pixel clk */
++ val = cdns_mhdp_reg_read(mhdp, SOURCE_HDTX_CAR);
++ cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, val & 0xFD);
++ cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, val);
++}
++
++static void cdns_dp_mode_set(struct cdns_mhdp_device *mhdp)
++{
++ u32 lane_mapping = mhdp->lane_mapping;
++ struct drm_dp_link *link = &mhdp->dp.link;
++ char linkid[6];
++ int ret;
++
++ cdns_mhdp_plat_call(mhdp, pclk_rate);
++
++ /* delay for DP FW stable after pixel clock relock */
++ msleep(50);
++
++ dp_pixel_clk_reset(mhdp);
++
++ ret = drm_dp_downstream_id(&mhdp->dp.aux, linkid);
++ if (ret < 0) {
++ DRM_INFO("Failed to Get DP link ID: %d\n", ret);
++ return;
++ }
++ DRM_INFO("DP link id: %s, 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
++ linkid, linkid[0], linkid[1], linkid[2], linkid[3], linkid[4],
++ linkid[5]);
++
++ /* Check dp link */
++ ret = drm_dp_link_probe(&mhdp->dp.aux, link);
++ if (ret < 0) {
++ DRM_INFO("Failed to probe DP link: %d\n", ret);
++ return;
++ }
++ DRM_INFO("DP revision: 0x%x\n", link->revision);
++ DRM_INFO("DP rate: %d Mbps\n", link->rate);
++ DRM_INFO("DP number of lanes: %d\n", link->num_lanes);
++ DRM_INFO("DP capabilities: 0x%lx\n", link->capabilities);
++
++ /* check the max link rate */
++ if (link->rate > CDNS_DP_MAX_LINK_RATE)
++ link->rate = CDNS_DP_MAX_LINK_RATE;
++
++ drm_dp_link_power_up(&mhdp->dp.aux, link);
++ if (ret < 0) {
++ DRM_INFO("Failed to power DP link: %d\n", ret);
++ return;
++ }
++
++ /* Initialize link rate/num_lanes as panel max link rate/max_num_lanes */
++ cdns_mhdp_plat_call(mhdp, phy_set);
++
++ /* Video off */
++ ret = cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_IDLE);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret);
++ return;
++ }
++
++ /* Line swaping */
++ cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | lane_mapping);
++
++ /* Set DP host capability */
++ ret = cdns_mhdp_set_host_cap(mhdp, false);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "Failed to set host cap %d\n", ret);
++ return;
++ }
++
++ ret = cdns_mhdp_config_video(mhdp);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "Failed to config video %d\n", ret);
++ return;
++ }
++
++ return;
++}
++
++/* -----------------------------------------------------------------------------
++ * dp TX Setup
++ */
++static enum drm_connector_status
++cdns_dp_connector_detect(struct drm_connector *connector, bool force)
++{
++ struct cdns_mhdp_device *mhdp = container_of(connector,
++ struct cdns_mhdp_device, connector.base);
++ u8 hpd = 0xf;
++
++ hpd = cdns_mhdp_read_hpd(mhdp);
++ if (hpd == 1)
++ /* Cable Connected */
++ return connector_status_connected;
++ else if (hpd == 0)
++ /* Cable Disconnedted */
++ return connector_status_disconnected;
++ else {
++ /* Cable status unknown */
++ DRM_INFO("Unknow cable status, hdp=%u\n", hpd);
++ return connector_status_unknown;
++ }
++}
++
++static int cdns_dp_connector_get_modes(struct drm_connector *connector)
++{
++ struct cdns_mhdp_device *mhdp = container_of(connector,
++ struct cdns_mhdp_device, connector.base);
++ int num_modes = 0;
++ struct edid *edid;
++
++ edid = drm_do_get_edid(&mhdp->connector.base,
++ cdns_mhdp_get_edid_block, mhdp);
++ if (edid) {
++ dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n",
++ edid->header[0], edid->header[1],
++ edid->header[2], edid->header[3],
++ edid->header[4], edid->header[5],
++ edid->header[6], edid->header[7]);
++ drm_connector_update_edid_property(connector, edid);
++ num_modes = drm_add_edid_modes(connector, edid);
++ kfree(edid);
++ }
++
++ if (num_modes == 0)
++ DRM_ERROR("Invalid edid\n");
++ return num_modes;
++}
++
++static const struct drm_connector_funcs cdns_dp_connector_funcs = {
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .detect = cdns_dp_connector_detect,
++ .destroy = drm_connector_cleanup,
++ .reset = drm_atomic_helper_connector_reset,
++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
++};
++
++static const struct drm_connector_helper_funcs cdns_dp_connector_helper_funcs = {
++ .get_modes = cdns_dp_connector_get_modes,
++};
++
++static int cdns_dp_bridge_attach(struct drm_bridge *bridge)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++ struct drm_encoder *encoder = bridge->encoder;
++ struct drm_connector *connector = &mhdp->connector.base;
++
++ connector->interlace_allowed = 1;
++
++ if (mhdp->is_hpd)
++ connector->polled = DRM_CONNECTOR_POLL_HPD;
++ else
++ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
++ DRM_CONNECTOR_POLL_DISCONNECT;
++
++ drm_connector_helper_add(connector, &cdns_dp_connector_helper_funcs);
++
++ drm_connector_init(bridge->dev, connector, &cdns_dp_connector_funcs,
++ DRM_MODE_CONNECTOR_DisplayPort);
++
++ drm_connector_attach_encoder(connector, encoder);
++
++ return 0;
++}
++
++static enum drm_mode_status
++cdns_dp_bridge_mode_valid(struct drm_bridge *bridge,
++ const struct drm_display_mode *mode)
++{
++ enum drm_mode_status mode_status = MODE_OK;
++
++ /* We don't support double-clocked modes */
++ if (mode->flags & DRM_MODE_FLAG_DBLCLK ||
++ mode->flags & DRM_MODE_FLAG_INTERLACE)
++ return MODE_BAD;
++
++ /* MAX support pixel clock rate 594MHz */
++ if (mode->clock > 594000)
++ return MODE_CLOCK_HIGH;
++
++ /* 4096x2160 is not supported now */
++ if (mode->hdisplay > 3840)
++ return MODE_BAD_HVALUE;
++
++ if (mode->vdisplay > 2160)
++ return MODE_BAD_VVALUE;
++
++ return mode_status;
++}
++
++static void cdns_dp_bridge_mode_set(struct drm_bridge *bridge,
++ const struct drm_display_mode *orig_mode,
++ const struct drm_display_mode *mode)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++ struct drm_display_info *display_info = &mhdp->connector.base.display_info;
++ struct video_info *video = &mhdp->video_info;
++
++ switch (display_info->bpc) {
++ case 10:
++ video->color_depth = 10;
++ break;
++ case 6:
++ video->color_depth = 6;
++ break;
++ default:
++ video->color_depth = 8;
++ break;
++ }
++
++ video->color_fmt = PXL_RGB;
++ video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
++ video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
++
++ DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock);
++ memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode));
++
++ mutex_lock(&mhdp->lock);
++ cdns_dp_mode_set(mhdp);
++ mutex_unlock(&mhdp->lock);
++
++ /* reset force mode set flag */
++ mhdp->force_mode_set = false;
++}
++
++static void cdn_dp_bridge_enable(struct drm_bridge *bridge)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++ int ret;
++
++ drm_dp_link_power_up(&mhdp->dp.aux, &mhdp->dp.link);
++
++ /* Link trainning */
++ ret = cdns_mhdp_train_link(mhdp);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "Failed link train %d\n", ret);
++ return;
++ }
++
++ ret = cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_VALID);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret);
++ return;
++ }
++}
++
++static void cdn_dp_bridge_disable(struct drm_bridge *bridge)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++
++ cdns_mhdp_set_video_status(mhdp, CONTROL_VIDEO_IDLE);
++}
++
++static const struct drm_bridge_funcs cdns_dp_bridge_funcs = {
++ .attach = cdns_dp_bridge_attach,
++ .enable = cdn_dp_bridge_enable,
++ .disable = cdn_dp_bridge_disable,
++ .mode_set = cdns_dp_bridge_mode_set,
++ .mode_valid = cdns_dp_bridge_mode_valid,
++};
++
++static void hotplug_work_func(struct work_struct *work)
++{
++ struct cdns_mhdp_device *mhdp = container_of(work,
++ struct cdns_mhdp_device, hotplug_work.work);
++ struct drm_connector *connector = &mhdp->connector.base;
++
++ drm_helper_hpd_irq_event(connector->dev);
++
++ if (connector->status == connector_status_connected) {
++ /* Cable connedted */
++ DRM_INFO("HDMI/DP Cable Plug In\n");
++ enable_irq(mhdp->irq[IRQ_OUT]);
++ } else if (connector->status == connector_status_disconnected) {
++ /* Cable Disconnedted */
++ DRM_INFO("HDMI/DP Cable Plug Out\n");
++ /* force mode set for cable replugin to recovery DP video modes */
++ mhdp->force_mode_set = true;
++ enable_irq(mhdp->irq[IRQ_IN]);
++ }
++}
++
++static irqreturn_t cdns_dp_irq_thread(int irq, void *data)
++{
++ struct cdns_mhdp_device *mhdp = data;
++
++ disable_irq_nosync(irq);
++
++ mod_delayed_work(system_wq, &mhdp->hotplug_work,
++ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
++
++ return IRQ_HANDLED;
++}
++
++static void cdns_dp_parse_dt(struct cdns_mhdp_device *mhdp)
++{
++ struct device_node *of_node = mhdp->dev->of_node;
++ int ret;
++
++ ret = of_property_read_u32(of_node, "lane-mapping",
++ &mhdp->lane_mapping);
++ if (ret) {
++ mhdp->lane_mapping = 0xc6;
++ dev_warn(mhdp->dev, "Failed to get lane_mapping - using default 0xc6\n");
++ }
++ dev_info(mhdp->dev, "lane-mapping 0x%02x\n", mhdp->lane_mapping);
++}
++
++static int __cdns_dp_probe(struct platform_device *pdev,
++ struct cdns_mhdp_device *mhdp)
++{
++ struct device *dev = &pdev->dev;
++ struct resource *iores = NULL;
++ int ret;
++
++ mutex_init(&mhdp->lock);
++ mutex_init(&mhdp->iolock);
++
++ INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func);
++
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (iores) {
++ mhdp->regs_base = devm_ioremap(dev, iores->start,
++ resource_size(iores));
++ if (IS_ERR(mhdp->regs_base))
++ return -ENOMEM;
++ }
++
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ if (iores) {
++ mhdp->regs_sec = devm_ioremap(dev, iores->start,
++ resource_size(iores));
++ if (IS_ERR(mhdp->regs_sec))
++ return -ENOMEM;
++ }
++
++ mhdp->is_hpd = true;
++ mhdp->is_ls1028a = false;
++
++ mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in");
++ if (mhdp->irq[IRQ_IN] < 0) {
++ mhdp->is_hpd = false;
++ dev_info(dev, "No plug_in irq number\n");
++ }
++
++ mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out");
++ if (mhdp->irq[IRQ_OUT] < 0) {
++ mhdp->is_hpd = false;
++ dev_info(dev, "No plug_out irq number\n");
++ }
++
++ cdns_dp_parse_dt(mhdp);
++
++ if (of_device_is_compatible(dev->of_node, "cdn,ls1028a-dp"))
++ mhdp->is_ls1028a = true;
++
++ cdns_mhdp_plat_call(mhdp, power_on);
++
++ cdns_mhdp_plat_call(mhdp, firmware_init);
++
++ /* DP FW alive check */
++ ret = cdns_mhdp_check_alive(mhdp);
++ if (ret == false) {
++ DRM_ERROR("NO dp FW running\n");
++ return -ENXIO;
++ }
++
++ /* DP PHY init before AUX init */
++ cdns_mhdp_plat_call(mhdp, phy_set);
++
++ /* Enable Hotplug Detect IRQ thread */
++ if (mhdp->is_hpd) {
++ irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN);
++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN],
++ NULL, cdns_dp_irq_thread,
++ IRQF_ONESHOT, dev_name(dev),
++ mhdp);
++
++ if (ret) {
++ dev_err(dev, "can't claim irq %d\n",
++ mhdp->irq[IRQ_IN]);
++ return -EINVAL;
++ }
++
++ irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN);
++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT],
++ NULL, cdns_dp_irq_thread,
++ IRQF_ONESHOT, dev_name(dev),
++ mhdp);
++
++ if (ret) {
++ dev_err(dev, "can't claim irq %d\n",
++ mhdp->irq[IRQ_OUT]);
++ return -EINVAL;
++ }
++
++ if (cdns_mhdp_read_hpd(mhdp))
++ enable_irq(mhdp->irq[IRQ_OUT]);
++ else
++ enable_irq(mhdp->irq[IRQ_IN]);
++ }
++
++ mhdp->bridge.base.driver_private = mhdp;
++ mhdp->bridge.base.funcs = &cdns_dp_bridge_funcs;
++#ifdef CONFIG_OF
++ mhdp->bridge.base.of_node = dev->of_node;
++#endif
++
++ dev_set_drvdata(dev, mhdp);
++
++ /* register audio driver */
++ cdns_mhdp_register_audio_driver(dev);
++
++ dp_aux_init(mhdp, dev);
++
++ return 0;
++}
++
++static void __cdns_dp_remove(struct cdns_mhdp_device *mhdp)
++{
++ dp_aux_destroy(mhdp);
++ cdns_mhdp_unregister_audio_driver(mhdp->dev);
++}
++
++/* -----------------------------------------------------------------------------
++ * Probe/remove API, used from platforms based on the DRM bridge API.
++ */
++int cdns_dp_probe(struct platform_device *pdev,
++ struct cdns_mhdp_device *mhdp)
++{
++ int ret;
++
++ ret = __cdns_dp_probe(pdev, mhdp);
++ if (ret)
++ return ret;
++
++ drm_bridge_add(&mhdp->bridge.base);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cdns_dp_probe);
++
++void cdns_dp_remove(struct platform_device *pdev)
++{
++ struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev);
++
++ drm_bridge_remove(&mhdp->bridge.base);
++
++ __cdns_dp_remove(mhdp);
++}
++EXPORT_SYMBOL_GPL(cdns_dp_remove);
++
++/* -----------------------------------------------------------------------------
++ * Bind/unbind API, used from platforms based on the component framework.
++ */
++int cdns_dp_bind(struct platform_device *pdev, struct drm_encoder *encoder,
++ struct cdns_mhdp_device *mhdp)
++{
++ int ret;
++
++ ret = __cdns_dp_probe(pdev, mhdp);
++ if (ret < 0)
++ return ret;
++
++ ret = drm_bridge_attach(encoder, &mhdp->bridge.base, NULL);
++ if (ret) {
++ cdns_dp_remove(pdev);
++ DRM_ERROR("Failed to initialize bridge with drm\n");
++ return ret;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cdns_dp_bind);
++
++void cdns_dp_unbind(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++
++ __cdns_dp_remove(mhdp);
++}
++EXPORT_SYMBOL_GPL(cdns_dp_unbind);
++
++MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>");
++MODULE_DESCRIPTION("Cadence Display Port transmitter driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:cdn-dp");
+diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
+new file mode 100644
+index 0000000..16f4b21
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
+@@ -0,0 +1,689 @@
++/*
++ * Cadence High-Definition Multimedia Interface (HDMI) driver
++ *
++ * Copyright (C) 2019 NXP Semiconductor, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ */
++#include <drm/bridge/cdns-mhdp-common.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_edid.h>
++#include <drm/drm_encoder_slave.h>
++#include <drm/drm_of.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/display/drm_hdmi_helper.h>
++#include <drm/display/drm_scdc_helper.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/hdmi.h>
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/mfd/syscon.h>
++#include <linux/mutex.h>
++#include <linux/of_device.h>
++
++static void hdmi_sink_config(struct cdns_mhdp_device *mhdp)
++{
++ struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc;
++ u8 buff = 0;
++
++ /* Default work in HDMI1.4 */
++ mhdp->hdmi.hdmi_type = MODE_HDMI_1_4;
++
++ /* check sink support SCDC or not */
++ if (scdc->supported != true) {
++ printk(KERN_ALERT "cdns-hdmi-core: Sink Not Support SCDC\n");
++ return;
++ }
++
++ if (mhdp->hdmi.char_rate > 340000) {
++ /*
++ * TMDS Character Rate above 340MHz should working in HDMI2.0
++ * Enable scrambling and TMDS_Bit_Clock_Ratio
++ */
++ buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE;
++ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0;
++ } else if (scdc->scrambling.low_rates) {
++ /*
++ * Enable scrambling and HDMI2.0 when scrambling capability of sink
++ * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit
++ */
++ buff = SCDC_SCRAMBLING_ENABLE;
++ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0;
++ }
++
++ /* TMDS config */
++ cdns_hdmi_scdc_write(mhdp, 0x20, buff);
++}
++
++static void hdmi_lanes_config(struct cdns_mhdp_device *mhdp)
++{
++ /* Line swapping */
++ cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | mhdp->lane_mapping);
++}
++
++static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp,
++ struct drm_display_mode *mode)
++{
++ struct hdmi_avi_infoframe frame;
++ int format = mhdp->video_info.color_fmt;
++ struct drm_connector_state *conn_state = mhdp->connector.base.state;
++ struct drm_display_mode *adj_mode;
++ enum hdmi_quantization_range qr;
++ u8 buf[32];
++ int ret;
++
++ /* Initialise info frame from DRM mode */
++ drm_hdmi_avi_infoframe_from_display_mode(&frame, &mhdp->connector.base,
++ mode);
++
++ switch (format) {
++ case YCBCR_4_4_4:
++ frame.colorspace = HDMI_COLORSPACE_YUV444;
++ break;
++ case YCBCR_4_2_2:
++ frame.colorspace = HDMI_COLORSPACE_YUV422;
++ break;
++ case YCBCR_4_2_0:
++ frame.colorspace = HDMI_COLORSPACE_YUV420;
++ break;
++ default:
++ frame.colorspace = HDMI_COLORSPACE_RGB;
++ break;
++ }
++
++ drm_hdmi_avi_infoframe_colorimetry(&frame, conn_state);
++
++ adj_mode = &mhdp->bridge.base.encoder->crtc->state->adjusted_mode;
++
++ qr = drm_default_rgb_quant_range(adj_mode);
++
++ drm_hdmi_avi_infoframe_quant_range(&frame, &mhdp->connector.base,
++ adj_mode, qr);
++
++ ret = hdmi_avi_infoframe_check(&frame);
++ if (WARN_ON(ret))
++ return false;
++
++ ret = hdmi_avi_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: failed to pack AVI infoframe: %d\n", ret);
++ return -1;
++ }
++
++ buf[0] = 0;
++ cdns_mhdp_infoframe_set(mhdp, 0, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AVI);
++ return 0;
++}
++
++static void hdmi_vendor_info_set(struct cdns_mhdp_device *mhdp,
++ struct drm_display_mode *mode)
++{
++ struct hdmi_vendor_infoframe frame;
++ u8 buf[32];
++ int ret;
++
++ /* Initialise vendor frame from DRM mode */
++ ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, &mhdp->connector.base, mode);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: No vendor infoframe\n");
++ return;
++ }
++
++ ret = hdmi_vendor_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: Unable to pack vendor infoframe: %d\n", ret);
++ return;
++ }
++
++ buf[0] = 0;
++ cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_VENDOR);
++}
++
++static void hdmi_drm_info_set(struct cdns_mhdp_device *mhdp)
++{
++ struct drm_connector_state *conn_state;
++ struct hdmi_drm_infoframe frame;
++ u8 buf[32];
++ int ret;
++
++ conn_state = mhdp->connector.base.state;
++
++ if (!conn_state->hdr_output_metadata)
++ return;
++
++ ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: couldn't set HDR metadata in infoframe\n");
++ return;
++ }
++
++ ret = hdmi_drm_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: couldn't pack HDR infoframe\n");
++ return;
++ }
++
++ buf[0] = 0;
++ cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf),
++ buf, HDMI_INFOFRAME_TYPE_DRM);
++}
++
++void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp)
++{
++ struct drm_display_mode *mode = &mhdp->mode;
++ int ret;
++
++ /* video mode valid check */
++ if (mode->clock == 0 || mode->hdisplay == 0 || mode->vdisplay == 0)
++ return;
++
++ hdmi_lanes_config(mhdp);
++
++ cdns_mhdp_plat_call(mhdp, pclk_rate);
++
++ /* delay for HDMI FW stable after pixel clock relock */
++ msleep(20);
++
++ cdns_mhdp_plat_call(mhdp, phy_set);
++
++ hdmi_sink_config(mhdp);
++
++ ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type, mhdp->hdmi.char_rate);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: %s, ret = %d\n", __func__, ret);
++ return;
++ }
++
++ /* Config GCP */
++ if (mhdp->video_info.color_depth == 8)
++ cdns_hdmi_disable_gcp(mhdp);
++ else
++ cdns_hdmi_enable_gcp(mhdp);
++
++ ret = hdmi_avi_info_set(mhdp, mode);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: %s ret = %d\n", __func__, ret);
++ return;
++ }
++
++ /* vendor info frame is enable only when HDMI1.4 4K mode */
++ hdmi_vendor_info_set(mhdp, mode);
++
++ hdmi_drm_info_set(mhdp);
++
++ ret = cdns_hdmi_mode_config(mhdp, mode, &mhdp->video_info);
++ if (ret < 0) {
++ printk(KERN_ALERT "cdns-hdmi-core: CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret);
++ return;
++ }
++}
++
++static enum drm_connector_status
++cdns_hdmi_connector_detect(struct drm_connector *connector, bool force)
++{
++ struct cdns_mhdp_device *mhdp =
++ container_of(connector, struct cdns_mhdp_device, connector.base);
++
++ u8 hpd = 0xf;
++
++ hpd = cdns_mhdp_read_hpd(mhdp);
++
++ if (hpd == 1)
++ /* Cable Connected */
++ return connector_status_connected;
++ else if (hpd == 0)
++ /* Cable Disconnedted */
++ return connector_status_disconnected;
++ else {
++ /* Cable status unknown */
++ printk(KERN_ALERT "cdns-hdmi-core: Unknown cable status, hdp=%u\n", hpd);
++ return connector_status_unknown;
++ }
++}
++
++static int cdns_hdmi_connector_get_modes(struct drm_connector *connector)
++{
++ struct cdns_mhdp_device *mhdp =
++ container_of(connector, struct cdns_mhdp_device, connector.base);
++ int num_modes = 0;
++ struct edid *edid;
++
++ edid = drm_do_get_edid(&mhdp->connector.base,
++ cdns_hdmi_get_edid_block, mhdp);
++ if (edid) {
++ dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n",
++ edid->header[0], edid->header[1],
++ edid->header[2], edid->header[3],
++ edid->header[4], edid->header[5],
++ edid->header[6], edid->header[7]);
++ drm_connector_update_edid_property(connector, edid);
++ num_modes = drm_add_edid_modes(connector, edid);
++ kfree(edid);
++ }
++
++ if (num_modes == 0)
++ printk(KERN_ALERT "cdns-hdmi-core: Invalid edid\n");
++ return num_modes;
++}
++
++static bool blob_equal(const struct drm_property_blob *a,
++ const struct drm_property_blob *b)
++{
++ if (a && b)
++ return a->length == b->length &&
++ !memcmp(a->data, b->data, a->length);
++
++ return !a == !b;
++}
++
++static int cdns_hdmi_connector_atomic_check(struct drm_connector *connector,
++ struct drm_atomic_state *state)
++{
++ struct drm_connector_state *new_con_state =
++ drm_atomic_get_new_connector_state(state, connector);
++ struct drm_connector_state *old_con_state =
++ drm_atomic_get_old_connector_state(state, connector);
++ struct drm_crtc *crtc = new_con_state->crtc;
++ struct drm_crtc_state *new_crtc_state;
++
++ if (!blob_equal(new_con_state->hdr_output_metadata,
++ old_con_state->hdr_output_metadata) ||
++ new_con_state->colorspace != old_con_state->colorspace) {
++ new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
++ if (IS_ERR(new_crtc_state))
++ return PTR_ERR(new_crtc_state);
++
++ new_crtc_state->mode_changed =
++ !new_con_state->hdr_output_metadata ||
++ !old_con_state->hdr_output_metadata ||
++ new_con_state->colorspace != old_con_state->colorspace;
++ }
++
++ return 0;
++}
++
++static const struct drm_connector_funcs cdns_hdmi_connector_funcs = {
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .detect = cdns_hdmi_connector_detect,
++ .destroy = drm_connector_cleanup,
++ .reset = drm_atomic_helper_connector_reset,
++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
++};
++
++static const struct drm_connector_helper_funcs cdns_hdmi_connector_helper_funcs = {
++ .get_modes = cdns_hdmi_connector_get_modes,
++ .atomic_check = cdns_hdmi_connector_atomic_check,
++};
++
++static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++ struct drm_mode_config *config = &bridge->dev->mode_config;
++ struct drm_encoder *encoder = bridge->encoder;
++ struct drm_connector *connector = &mhdp->connector.base;
++
++ connector->interlace_allowed = 1;
++ connector->polled = DRM_CONNECTOR_POLL_HPD;
++
++ drm_connector_helper_add(connector, &cdns_hdmi_connector_helper_funcs);
++
++ drm_connector_init(bridge->dev, connector, &cdns_hdmi_connector_funcs,
++ DRM_MODE_CONNECTOR_HDMIA);
++
++ if (!strncmp("imx8mq-hdmi", mhdp->plat_data->plat_name, 11)) {
++ drm_object_attach_property(&connector->base,
++ config->hdr_output_metadata_property,
++ 0);
++
++ if (!drm_mode_create_hdmi_colorspace_property(connector))
++ drm_object_attach_property(&connector->base,
++ connector->colorspace_property,
++ 0);
++ }
++
++ drm_connector_attach_encoder(connector, encoder);
++
++ return 0;
++}
++
++static enum drm_mode_status
++cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
++ const struct drm_display_info *info,
++ const struct drm_display_mode *mode)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++ enum drm_mode_status mode_status = MODE_OK;
++ int ret;
++
++ /* We don't support double-clocked and Interlaced modes */
++ if (mode->flags & DRM_MODE_FLAG_DBLCLK ||
++ mode->flags & DRM_MODE_FLAG_INTERLACE)
++ return MODE_BAD;
++
++ /* MAX support pixel clock rate 594MHz */
++ if (mode->clock > 594000)
++ return MODE_CLOCK_HIGH;
++
++ /* 4096x2160 is not supported */
++ if (mode->hdisplay > 3840 || mode->vdisplay > 2160)
++ return MODE_BAD_HVALUE;
++
++ mhdp->valid_mode = mode;
++ ret = cdns_mhdp_plat_call(mhdp, phy_video_valid);
++ if (ret == false)
++ return MODE_CLOCK_RANGE;
++
++ return mode_status;
++}
++
++static void cdns_hdmi_bridge_mode_set(struct drm_bridge *bridge,
++ const struct drm_display_mode *orig_mode,
++ const struct drm_display_mode *mode)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++ struct video_info *video = &mhdp->video_info;
++
++ video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
++ video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
++
++ printk(KERN_ALERT "cdns-hdmi-core: Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock);
++ memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode));
++
++ mutex_lock(&mhdp->lock);
++ cdns_hdmi_mode_set(mhdp);
++ mutex_unlock(&mhdp->lock);
++ /* reset force mode set flag */
++ mhdp->force_mode_set = false;
++}
++
++bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
++ const struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++ struct drm_display_info *di = &mhdp->connector.base.display_info;
++ struct video_info *video = &mhdp->video_info;
++ int vic = drm_match_cea_mode(mode);
++
++ video->color_depth = 8;
++ video->color_fmt = PXL_RGB;
++
++ /* for all other platforms, other than imx8mq */
++ if (strncmp("imx8mq-hdmi", mhdp->plat_data->plat_name, 11)) {
++ if (di->bpc == 10 || di->bpc == 6)
++ video->color_depth = di->bpc;
++
++ return true;
++ }
++
++ /* imx8mq */
++ if (vic == 97 || vic == 96) {
++ if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
++ video->color_depth = 12;
++ else if (di->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
++ video->color_depth = 10;
++
++ if (drm_mode_is_420_only(di, mode) ||
++ (drm_mode_is_420_also(di, mode) &&
++ video->color_depth > 8)) {
++ video->color_fmt = YCBCR_4_2_0;
++
++ // FIXME
++ //adjusted_mode->private_flags = 1;
++ return true;
++ }
++
++ video->color_depth = 8;
++ return true;
++ }
++
++ /* Any defined maximum tmds clock limit we must not exceed*/
++ if ((di->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_36) &&
++ (mode->clock * 3 / 2 <= di->max_tmds_clock))
++ video->color_depth = 12;
++ else if ((di->edid_hdmi_rgb444_dc_modes & DRM_EDID_HDMI_DC_30) &&
++ (mode->clock * 5 / 4 <= di->max_tmds_clock))
++ video->color_depth = 10;
++
++ /* 10-bit color depth for the following modes is not supported */
++ if ((vic == 95 || vic == 94 || vic == 93) && video->color_depth == 10)
++ video->color_depth = 8;
++
++ return true;
++}
++
++static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = {
++ .attach = cdns_hdmi_bridge_attach,
++ .mode_set = cdns_hdmi_bridge_mode_set,
++ .mode_valid = cdns_hdmi_bridge_mode_valid,
++ .mode_fixup = cdns_hdmi_bridge_mode_fixup,
++};
++
++static void hotplug_work_func(struct work_struct *work)
++{
++ struct cdns_mhdp_device *mhdp = container_of(work,
++ struct cdns_mhdp_device, hotplug_work.work);
++ struct drm_connector *connector = &mhdp->connector.base;
++
++ drm_helper_hpd_irq_event(connector->dev);
++
++ if (connector->status == connector_status_connected) {
++ printk(KERN_ALERT "cdns-hdmi-core: HDMI Cable Plug In\n");
++ enable_irq(mhdp->irq[IRQ_OUT]);
++ } else if (connector->status == connector_status_disconnected) {
++ /* Cable Disconnedted */
++ printk(KERN_ALERT "cdns-hdmi-core: HDMI Cable Plug Out\n");
++ /* force mode set for cable replugin to recovery HDMI2.0 video modes */
++ mhdp->force_mode_set = true;
++ enable_irq(mhdp->irq[IRQ_IN]);
++ }
++}
++
++static irqreturn_t cdns_hdmi_irq_thread(int irq, void *data)
++{
++ struct cdns_mhdp_device *mhdp = data;
++
++ disable_irq_nosync(irq);
++
++ mod_delayed_work(system_wq, &mhdp->hotplug_work,
++ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
++
++ return IRQ_HANDLED;
++}
++
++static void cdns_hdmi_parse_dt(struct cdns_mhdp_device *mhdp)
++{
++ struct device_node *of_node = mhdp->dev->of_node;
++ int ret;
++
++ ret = of_property_read_u32(of_node, "lane-mapping", &mhdp->lane_mapping);
++ if (ret) {
++ mhdp->lane_mapping = 0xc6;
++ dev_warn(mhdp->dev, "Failed to get lane_mapping - using default 0xc6\n");
++ }
++ dev_info(mhdp->dev, "lane-mapping 0x%02x\n", mhdp->lane_mapping);
++}
++
++static int __cdns_hdmi_probe(struct platform_device *pdev,
++ struct cdns_mhdp_device *mhdp)
++{
++ struct device *dev = &pdev->dev;
++ struct platform_device_info pdevinfo;
++ struct resource *iores = NULL;
++ int ret;
++
++ mutex_init(&mhdp->lock);
++ mutex_init(&mhdp->iolock);
++
++ INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func);
++
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ mhdp->regs_base = devm_ioremap(dev, iores->start, resource_size(iores));
++ if (IS_ERR(mhdp->regs_base)) {
++ dev_err(dev, "No regs_base memory\n");
++ return -ENOMEM;
++ }
++
++ /* sec register base */
++ iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++ mhdp->regs_sec = devm_ioremap(dev, iores->start, resource_size(iores));
++ if (IS_ERR(mhdp->regs_sec)) {
++ dev_err(dev, "No regs_sec memory\n");
++ return -ENOMEM;
++ }
++
++ mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in");
++ if (mhdp->irq[IRQ_IN] < 0) {
++ dev_info(dev, "No plug_in irq number\n");
++ return -EPROBE_DEFER;
++ }
++
++ mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out");
++ if (mhdp->irq[IRQ_OUT] < 0) {
++ dev_info(dev, "No plug_out irq number\n");
++ return -EPROBE_DEFER;
++ }
++
++ cdns_mhdp_plat_call(mhdp, power_on);
++
++ /* Initialize FW */
++ cdns_mhdp_plat_call(mhdp, firmware_init);
++
++ /* HDMI FW alive check */
++ ret = cdns_mhdp_check_alive(mhdp);
++ if (ret == false) {
++ dev_err(dev, "NO HDMI FW running\n");
++ return -ENXIO;
++ }
++
++ /* Enable Hotplug Detect thread */
++ irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN);
++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN],
++ NULL, cdns_hdmi_irq_thread,
++ IRQF_ONESHOT, dev_name(dev),
++ mhdp);
++ if (ret < 0) {
++ dev_err(dev, "can't claim irq %d\n",
++ mhdp->irq[IRQ_IN]);
++ return -EINVAL;
++ }
++
++ irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN);
++ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT],
++ NULL, cdns_hdmi_irq_thread,
++ IRQF_ONESHOT, dev_name(dev),
++ mhdp);
++ if (ret < 0) {
++ dev_err(dev, "can't claim irq %d\n",
++ mhdp->irq[IRQ_OUT]);
++ return -EINVAL;
++ }
++
++ cdns_hdmi_parse_dt(mhdp);
++
++ if (cdns_mhdp_read_hpd(mhdp))
++ enable_irq(mhdp->irq[IRQ_OUT]);
++ else
++ enable_irq(mhdp->irq[IRQ_IN]);
++
++ mhdp->bridge.base.driver_private = mhdp;
++ mhdp->bridge.base.funcs = &cdns_hdmi_bridge_funcs;
++#ifdef CONFIG_OF
++ mhdp->bridge.base.of_node = dev->of_node;
++#endif
++
++ memset(&pdevinfo, 0, sizeof(pdevinfo));
++ pdevinfo.parent = dev;
++ pdevinfo.id = PLATFORM_DEVID_AUTO;
++
++ dev_set_drvdata(dev, mhdp);
++
++ /* register audio driver */
++ //cdns_mhdp_register_audio_driver(dev);
++
++ /* register cec driver */
++#ifdef CONFIG_DRM_CDNS_HDMI_CEC
++ cdns_mhdp_register_cec_driver(dev);
++#endif
++
++ return 0;
++}
++
++static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp)
++{
++ /* unregister cec driver */
++#ifdef CONFIG_DRM_CDNS_HDMI_CEC
++ cdns_mhdp_unregister_cec_driver(mhdp->dev);
++#endif
++ //cdns_mhdp_unregister_audio_driver(mhdp->dev);
++}
++
++/* -----------------------------------------------------------------------------
++ * Probe/remove API, used from platforms based on the DRM bridge API.
++ */
++int cdns_hdmi_probe(struct platform_device *pdev,
++ struct cdns_mhdp_device *mhdp)
++{
++ int ret;
++
++ ret = __cdns_hdmi_probe(pdev, mhdp);
++ if (ret < 0)
++ return ret;
++
++ drm_bridge_add(&mhdp->bridge.base);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cdns_hdmi_probe);
++
++void cdns_hdmi_remove(struct platform_device *pdev)
++{
++ struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev);
++
++ drm_bridge_remove(&mhdp->bridge.base);
++
++ __cdns_hdmi_remove(mhdp);
++}
++EXPORT_SYMBOL_GPL(cdns_hdmi_remove);
++
++/* -----------------------------------------------------------------------------
++ * Bind/unbind API, used from platforms based on the component framework.
++ */
++int cdns_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
++ struct cdns_mhdp_device *mhdp)
++{
++ int ret;
++
++ ret = __cdns_hdmi_probe(pdev, mhdp);
++ if (ret)
++ return ret;
++
++ ret = drm_bridge_attach(encoder, &mhdp->bridge.base, NULL, 0); // FIXME flags?
++ if (ret) {
++ cdns_hdmi_remove(pdev);
++ printk(KERN_ALERT "cdns-hdmi-core: Failed to initialize bridge with drm\n");
++ return ret;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cdns_hdmi_bind);
++
++void cdns_hdmi_unbind(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++
++ __cdns_hdmi_remove(mhdp);
++}
++EXPORT_SYMBOL_GPL(cdns_hdmi_unbind);
++
++MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>");
++MODULE_DESCRIPTION("Cadence HDMI transmitter driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:cdn-hdmi");
+diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c
+new file mode 100644
+index 0000000..5e71848
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-audio.c
+@@ -0,0 +1,393 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
++ * Author: Chris Zhong <zyw@rock-chips.com>
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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/clk.h>
++#include <linux/reset.h>
++#include <drm/bridge/cdns-mhdp-common.h>
++#include <sound/hdmi-codec.h>
++#include <drm/drm_of.h>
++
++#define CDNS_DP_SPDIF_CLK 200000000
++
++static u32 TMDS_rate_table[7] = {
++ 25200, 27000, 54000, 74250, 148500, 297000, 594000,
++};
++
++static u32 N_table_32k[7] = {
++/* 25200/27000/54000/74250/148500/297000/594000 */
++ 4096, 4096, 4096, 4096, 4096, 3072, 3072,
++};
++
++static u32 N_table_44k[7] = {
++ 6272, 6272, 6272, 6272, 6272, 4704, 9408,
++};
++
++static u32 N_table_48k[7] = {
++ 6144, 6144, 6144, 6144, 6144, 5120, 6144,
++};
++
++static int select_N_index(u32 pclk)
++{
++ int num = sizeof(TMDS_rate_table)/sizeof(int);
++ int i = 0;
++
++ for (i = 0; i < num ; i++)
++ if (pclk == TMDS_rate_table[i])
++ break;
++
++ if (i == num) {
++ printk("cdn-mhdp-audio: pclkc %d is not supported!\n", pclk);
++ return num-1;
++ }
++
++ return i;
++}
++
++static void hdmi_audio_avi_set(struct cdns_mhdp_device *mhdp,
++ u32 channels)
++{
++ struct hdmi_audio_infoframe frame;
++ u8 buf[32];
++ int ret;
++
++ hdmi_audio_infoframe_init(&frame);
++
++ frame.channels = channels;
++ frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
++
++ if (channels == 2)
++ frame.channel_allocation = 0;
++ else if (channels == 4)
++ frame.channel_allocation = 0x3;
++ else if (channels == 8)
++ frame.channel_allocation = 0x13;
++
++ ret = hdmi_audio_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
++ if (ret < 0) {
++ printk("cdns-mhdp-audio: failed to pack audio infoframe: %d\n", ret);
++ return;
++ }
++
++ buf[0] = 0;
++
++ cdns_mhdp_infoframe_set(mhdp, 1, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AUDIO);
++}
++
++int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio)
++{
++ int ret;
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
++ ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0);
++ if (ret) {
++ dev_err(mhdp->dev, "audio stop failed: %d\n", ret);
++ return ret;
++ }
++ }
++
++ cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR);
++
++ /* clearn the audio config and reset */
++ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG);
++ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
++
++ /* reset smpl2pckt component */
++ cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
++ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
++
++ /* reset FIFO */
++ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL);
++ cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL);
++
++ if (audio->format == AFMT_SPDIF_INT)
++ clk_disable_unprepare(mhdp->spdif_clk);
++
++ return 0;
++}
++EXPORT_SYMBOL(cdns_mhdp_audio_stop);
++
++int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable)
++{
++ struct audio_info *audio = &mhdp->audio_info;
++ int ret = true;
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
++ ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable);
++ if (ret)
++ dev_err(mhdp->dev, "audio mute failed: %d\n", ret);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_audio_mute);
++
++static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio)
++{
++ int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
++ int idx = select_N_index(mhdp->mode.clock);
++ u32 val, ncts;
++
++ if (audio->channels == 2) {
++ if (mhdp->dp.link.num_lanes == 1)
++ sub_pckt_num = 2;
++ else
++ sub_pckt_num = 4;
++
++ i2s_port_en_val = 1;
++ } else if (audio->channels == 4) {
++ i2s_port_en_val = 3;
++ }
++
++ cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR);
++
++ cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
++
++ val = MAX_NUM_CH(audio->channels);
++ val |= NUM_OF_I2S_PORTS(audio->channels);
++ val |= AUDIO_TYPE_LPCM;
++ val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
++ cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
++
++ if (audio->sample_width == 16)
++ val = 0;
++ else if (audio->sample_width == 24)
++ val = 1 << 9;
++ else
++ val = 2 << 9;
++
++ val |= AUDIO_CH_NUM(audio->channels);
++ val |= I2S_DEC_PORT_EN(i2s_port_en_val);
++ val |= TRANS_SMPL_WIDTH_32;
++ cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG);
++
++ for (i = 0; i < (audio->channels + 1) / 2; i++) {
++ if (audio->sample_width == 16)
++ val = (0x02 << 8) | (0x02 << 20);
++ else if (audio->sample_width == 24)
++ val = (0x0b << 8) | (0x0b << 20);
++
++ val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
++ cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i));
++ }
++
++ switch (audio->sample_rate) {
++ case 32000:
++ val = SAMPLING_FREQ(3) |
++ ORIGINAL_SAMP_FREQ(0xc);
++ ncts = N_table_32k[idx];
++ break;
++ case 44100:
++ val = SAMPLING_FREQ(0) |
++ ORIGINAL_SAMP_FREQ(0xf);
++ ncts = N_table_44k[idx];
++ break;
++ case 48000:
++ val = SAMPLING_FREQ(2) |
++ ORIGINAL_SAMP_FREQ(0xd);
++ ncts = N_table_48k[idx];
++ break;
++ case 88200:
++ val = SAMPLING_FREQ(8) |
++ ORIGINAL_SAMP_FREQ(0x7);
++ ncts = N_table_44k[idx] * 2;
++ break;
++ case 96000:
++ val = SAMPLING_FREQ(0xa) |
++ ORIGINAL_SAMP_FREQ(5);
++ ncts = N_table_48k[idx] * 2;
++ break;
++ case 176400:
++ val = SAMPLING_FREQ(0xc) |
++ ORIGINAL_SAMP_FREQ(3);
++ ncts = N_table_44k[idx] * 4;
++ break;
++ case 192000:
++ default:
++ val = SAMPLING_FREQ(0xe) |
++ ORIGINAL_SAMP_FREQ(1);
++ ncts = N_table_48k[idx] * 4;
++ break;
++ }
++ val |= 4;
++ cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS);
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_HDMIA)
++ cdns_mhdp_reg_write(mhdp, CM_I2S_CTRL, ncts | 0x4000000);
++
++ cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
++ cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL);
++}
++
++static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp)
++{
++ u32 val;
++
++ cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
++
++ val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
++ cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
++ cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
++
++ val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
++ cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR);
++
++ clk_prepare_enable(mhdp->spdif_clk);
++ clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK);
++}
++
++int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio)
++{
++ int ret;
++
++ /* reset the spdif clk before config */
++ if (audio->format == AFMT_SPDIF_INT) {
++ reset_control_assert(mhdp->spdif_rst);
++ reset_control_deassert(mhdp->spdif_rst);
++ }
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
++ ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC);
++ if (ret)
++ goto err_audio_config;
++
++ ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0);
++ if (ret)
++ goto err_audio_config;
++ } else {
++ /* HDMI Mode */
++ ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 8);
++ if (ret)
++ goto err_audio_config;
++ }
++
++ if (audio->format == AFMT_I2S)
++ cdns_mhdp_audio_config_i2s(mhdp, audio);
++ else if (audio->format == AFMT_SPDIF_INT)
++ cdns_mhdp_audio_config_spdif(mhdp);
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_DisplayPort)
++ ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
++
++ if (audio->connector_type == DRM_MODE_CONNECTOR_HDMIA)
++ hdmi_audio_avi_set(mhdp, audio->channels);
++
++err_audio_config:
++ if (ret)
++ dev_err(mhdp->dev, "audio config failed: %d\n", ret);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_audio_config);
++
++static int audio_hw_params(struct device *dev, void *data,
++ struct hdmi_codec_daifmt *daifmt,
++ struct hdmi_codec_params *params)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ struct audio_info audio = {
++ .sample_width = params->sample_width,
++ .sample_rate = params->sample_rate,
++ .channels = params->channels,
++ .connector_type = mhdp->connector.base.connector_type,
++ };
++ int ret;
++
++ switch (daifmt->fmt) {
++ case HDMI_I2S:
++ audio.format = AFMT_I2S;
++ break;
++ case HDMI_SPDIF:
++ audio.format = AFMT_SPDIF_EXT;
++ break;
++ default:
++ dev_err(dev, "Invalid format %d\n", daifmt->fmt);
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ret = cdns_mhdp_audio_config(mhdp, &audio);
++ if (!ret)
++ mhdp->audio_info = audio;
++
++out:
++ return ret;
++}
++
++static void audio_shutdown(struct device *dev, void *data)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ int ret;
++
++ ret = cdns_mhdp_audio_stop(mhdp, &mhdp->audio_info);
++ if (!ret)
++ mhdp->audio_info.format = AFMT_UNUSED;
++}
++
++static int audio_digital_mute(struct device *dev, void *data,
++ bool enable)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ int ret;
++
++ ret = cdns_mhdp_audio_mute(mhdp, enable);
++
++ return ret;
++}
++
++static int audio_get_eld(struct device *dev, void *data,
++ u8 *buf, size_t len)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++
++ memcpy(buf, mhdp->connector.base.eld,
++ min(sizeof(mhdp->connector.base.eld), len));
++
++ return 0;
++}
++
++static const struct hdmi_codec_ops audio_codec_ops = {
++ .hw_params = audio_hw_params,
++ .audio_shutdown = audio_shutdown,
++//.digital_mute = audio_digital_mute,
++ .get_eld = audio_get_eld,
++};
++
++int cdns_mhdp_register_audio_driver(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ struct hdmi_codec_pdata codec_data = {
++ .i2s = 1,
++ .spdif = 1,
++ .ops = &audio_codec_ops,
++ .max_i2s_channels = 8,
++ };
++
++ mhdp->audio_pdev = platform_device_register_data(
++ dev, HDMI_CODEC_DRV_NAME, 1,
++ &codec_data, sizeof(codec_data));
++
++ return PTR_ERR_OR_ZERO(mhdp->audio_pdev);
++}
++
++void cdns_mhdp_unregister_audio_driver(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++
++ platform_device_unregister(mhdp->audio_pdev);
++}
+diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c
+new file mode 100644
+index 0000000..af85064
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-cec.c
+@@ -0,0 +1,341 @@
++/*
++ * Copyright 2019 NXP
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * 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/module.h>
++#include <linux/workqueue.h>
++#include <linux/kthread.h>
++#include <linux/freezer.h>
++#include <drm/bridge/cdns-mhdp-common.h>
++
++#define CEC_NAME "cdns-mhdp-cec"
++
++#define REG_ADDR_OFF 4
++
++/* regsiter define */
++#define TX_MSG_HEADER 0x33800
++#define TX_MSG_LENGTH 0x33840
++#define TX_MSG_CMD 0x33844
++#define RX_MSG_CMD 0x33850
++#define RX_CLEAR_BUF 0x33854
++#define LOGICAL_ADDRESS_LA0 0x33858
++
++#define CLK_DIV_MSB 0x3386c
++#define CLK_DIV_LSB 0x33870
++#define RX_MSG_DATA1 0x33900
++#define RX_MSG_LENGTH 0x33940
++#define RX_MSG_STATUS 0x33944
++#define NUM_OF_MSG_RX_BUF 0x33948
++#define TX_MSG_STATUS 0x3394c
++#define DB_L_TIMER 0x33980
++
++/**
++ * CEC Transceiver operation.
++ */
++enum {
++ CEC_TX_STOP,
++ CEC_TX_TRANSMIT,
++ CEC_TX_ABORT,
++ CEC_TX_ABORT_AND_TRANSMIT
++};
++
++/**
++ * CEC Transceiver status.
++ */
++enum {
++ CEC_STS_IDLE,
++ CEC_STS_BUSY,
++ CEC_STS_SUCCESS,
++ CEC_STS_ERROR
++};
++
++/**
++ * CEC Receiver operation.
++ */
++enum {
++ CEC_RX_STOP,
++ CEC_RX_READ,
++ CEC_RX_DISABLE,
++ CEC_RX_ABORT_AND_CLR_FIFO
++};
++/**
++ * Maximum number of Messages in the RX Buffers.
++ */
++#define CEC_MAX_RX_MSGS 2
++
++static u32 mhdp_cec_read(struct cdns_mhdp_cec *cec, u32 offset)
++{
++ struct cdns_mhdp_device *mhdp =
++ container_of(cec, struct cdns_mhdp_device, hdmi.cec);
++ return cdns_mhdp_bus_read(mhdp, offset);
++}
++
++static void mhdp_cec_write(struct cdns_mhdp_cec *cec, u32 offset, u32 val)
++{
++ struct cdns_mhdp_device *mhdp =
++ container_of(cec, struct cdns_mhdp_device, hdmi.cec);
++ cdns_mhdp_bus_write(val, mhdp, offset);
++}
++
++static void mhdp_cec_clear_rx_buffer(struct cdns_mhdp_cec *cec)
++{
++ mhdp_cec_write(cec, RX_CLEAR_BUF, 1);
++ mhdp_cec_write(cec, RX_CLEAR_BUF, 0);
++}
++
++static void mhdp_cec_set_divider(struct cdns_mhdp_cec *cec)
++{
++ struct cdns_mhdp_device *mhdp =
++ container_of(cec, struct cdns_mhdp_device, hdmi.cec);
++ u32 clk_div;
++
++ /* Set clock divider */
++ clk_div = cdns_mhdp_get_fw_clk(mhdp) * 10;
++
++ mhdp_cec_write(cec, CLK_DIV_MSB,
++ (clk_div >> 8) & 0xFF);
++ mhdp_cec_write(cec, CLK_DIV_LSB, clk_div & 0xFF);
++}
++
++static u32 mhdp_cec_read_message(struct cdns_mhdp_cec *cec)
++{
++ struct cec_msg *msg = &cec->msg;
++ int len;
++ int i;
++
++ mhdp_cec_write(cec, RX_MSG_CMD, CEC_RX_READ);
++
++ len = mhdp_cec_read(cec, RX_MSG_LENGTH);
++ msg->len = len + 1;
++ dev_dbg(cec->dev, "RX MSG len =%d\n", len);
++
++ /* Read RX MSG bytes */
++ for (i = 0; i < msg->len; ++i) {
++ msg->msg[i] = (u8) mhdp_cec_read(cec, RX_MSG_DATA1 + (i * REG_ADDR_OFF));
++ dev_dbg(cec->dev, "RX MSG[%d]=0x%x\n", i, msg->msg[i]);
++ }
++
++ mhdp_cec_write(cec, RX_MSG_CMD, CEC_RX_STOP);
++
++ return true;
++}
++
++static u32 mhdp_cec_write_message(struct cdns_mhdp_cec *cec, struct cec_msg *msg)
++{
++ u8 i;
++
++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP);
++
++ if (msg->len > CEC_MAX_MSG_SIZE) {
++ dev_err(cec->dev, "Invalid MSG size!\n");
++ return -EINVAL;
++ }
++
++ for (i = 0; i < msg->len; ++i)
++ printk("msg[%d]=0x%x\n",i, msg->msg[i]);
++
++ /* Write Message to register */
++ for (i = 0; i < msg->len; ++i) {
++ mhdp_cec_write(cec, TX_MSG_HEADER + (i * REG_ADDR_OFF),
++ msg->msg[i]);
++ }
++ /* Write Message Length (payload + opcode) */
++ mhdp_cec_write(cec, TX_MSG_LENGTH, msg->len - 1);
++
++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_TRANSMIT);
++
++ return true;
++}
++
++static int mhdp_cec_set_logical_addr(struct cdns_mhdp_cec *cec, u32 la)
++{
++ u8 la_reg;
++ u8 i;
++
++ if (la == CEC_LOG_ADDR_INVALID)
++ /* invalid all LA address */
++ for (i = 0; i < CEC_MAX_LOG_ADDRS; ++i) {
++ mhdp_cec_write(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF), 0);
++ return 0;
++ }
++
++ /* In fact cdns mhdp cec could support max 5 La address */
++ for (i = 0; i < CEC_MAX_LOG_ADDRS; ++i) {
++ la_reg = mhdp_cec_read(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF));
++ /* Check LA already used */
++ if (la_reg & 0x10)
++ continue;
++
++ if ((la_reg & 0xF) == la) {
++ dev_warn(cec->dev, "Warning. LA already in use.\n");
++ return 0;
++ }
++
++ la = (la & 0xF) | (1 << 4);
++
++ mhdp_cec_write(cec, LOGICAL_ADDRESS_LA0 + (i * REG_ADDR_OFF), la);
++ return 0;
++ }
++
++ dev_warn(cec->dev, "All LA in use\n");
++
++ return -ENXIO;
++}
++
++static int mhdp_cec_poll_worker(void *_cec)
++{
++ struct cdns_mhdp_cec *cec = (struct cdns_mhdp_cec *)_cec;
++ int num_rx_msgs, i;
++ int sts;
++
++ set_freezable();
++
++ for (;;) {
++ if (kthread_freezable_should_stop(NULL))
++ break;
++
++ /* Check TX State */
++ sts = mhdp_cec_read(cec, TX_MSG_STATUS);
++ switch (sts) {
++ case CEC_STS_SUCCESS:
++ cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0,
++ 0);
++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP);
++ break;
++ case CEC_STS_ERROR:
++ mhdp_cec_write(cec, TX_MSG_CMD, CEC_TX_STOP);
++ cec_transmit_done(cec->adap,
++ CEC_TX_STATUS_MAX_RETRIES |
++ CEC_TX_STATUS_NACK, 0, 1, 0, 0);
++ break;
++ case CEC_STS_BUSY:
++ default:
++ break;
++ }
++
++ /* Check RX State */
++ sts = mhdp_cec_read(cec, RX_MSG_STATUS);
++ num_rx_msgs = mhdp_cec_read(cec, NUM_OF_MSG_RX_BUF);
++ switch (sts) {
++ case CEC_STS_SUCCESS:
++ if (num_rx_msgs == 0xf)
++ num_rx_msgs = CEC_MAX_RX_MSGS;
++
++ if (num_rx_msgs > CEC_MAX_RX_MSGS) {
++ dev_err(cec->dev, "Error rx msg num %d\n",
++ num_rx_msgs);
++ mhdp_cec_clear_rx_buffer(cec);
++ break;
++ }
++
++ /* Rx FIFO Depth 2 RX MSG */
++ for (i = 0; i < num_rx_msgs; i++) {
++ mhdp_cec_read_message(cec);
++ cec->msg.rx_status = CEC_RX_STATUS_OK;
++ cec_received_msg(cec->adap, &cec->msg);
++ }
++ break;
++ default:
++ break;
++ }
++
++ if (!kthread_should_stop())
++ schedule_timeout_idle(20);
++ }
++
++ return 0;
++}
++
++static int mhdp_cec_adap_enable(struct cec_adapter *adap, bool enable)
++{
++ struct cdns_mhdp_cec *cec = cec_get_drvdata(adap);
++
++ if (enable) {
++ mhdp_cec_write(cec, DB_L_TIMER, 0x10);
++ mhdp_cec_set_divider(cec);
++ } else
++ mhdp_cec_set_divider(cec);
++
++ return 0;
++}
++
++static int mhdp_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
++{
++ struct cdns_mhdp_cec *cec = cec_get_drvdata(adap);
++
++ return mhdp_cec_set_logical_addr(cec, addr);
++}
++
++static int mhdp_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
++ u32 signal_free_time, struct cec_msg *msg)
++{
++ struct cdns_mhdp_cec *cec = cec_get_drvdata(adap);
++
++ mhdp_cec_write_message(cec, msg);
++
++ return 0;
++}
++
++static const struct cec_adap_ops cdns_mhdp_cec_adap_ops = {
++ .adap_enable = mhdp_cec_adap_enable,
++ .adap_log_addr = mhdp_cec_adap_log_addr,
++ .adap_transmit = mhdp_cec_adap_transmit,
++};
++
++int cdns_mhdp_register_cec_driver(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec;
++ int ret;
++
++ cec->adap = cec_allocate_adapter(&cdns_mhdp_cec_adap_ops, cec,
++ CEC_NAME,
++ CEC_CAP_PHYS_ADDR | CEC_CAP_LOG_ADDRS |
++ CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH
++ | CEC_CAP_RC, CEC_MAX_LOG_ADDRS);
++ ret = PTR_ERR_OR_ZERO(cec->adap);
++ if (ret)
++ return ret;
++ ret = cec_register_adapter(cec->adap, dev);
++ if (ret) {
++ cec_delete_adapter(cec->adap);
++ return ret;
++ }
++
++ cec->dev = dev;
++
++ cec->cec_worker = kthread_create(mhdp_cec_poll_worker, cec, "cdns-mhdp-cec");
++ if (IS_ERR(cec->cec_worker))
++ dev_err(cec->dev, "failed create hdp cec thread\n");
++
++ wake_up_process(cec->cec_worker);
++
++ dev_dbg(dev, "CEC successfuly probed\n");
++ return 0;
++}
++
++int cdns_mhdp_unregister_cec_driver(struct device *dev)
++{
++ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
++ struct cdns_mhdp_cec *cec = &mhdp->hdmi.cec;
++
++ if (cec->cec_worker) {
++ kthread_stop(cec->cec_worker);
++ cec->cec_worker = NULL;
++ }
++ cec_unregister_adapter(cec->adap);
++ return 0;
++}
++
++MODULE_AUTHOR("Sandor.Yu@NXP.com");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("NXP CDNS MHDP HDMI CEC driver");
+diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
+new file mode 100644
+index 0000000..0c61838
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
+@@ -0,0 +1,1059 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
++ * Author: Chris Zhong <zyw@rock-chips.com>
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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/clk.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/reset.h>
++
++#include <asm/unaligned.h>
++
++#include <drm/bridge/cdns-mhdp-common.h>
++#include <drm/drm_modes.h>
++#include <drm/drm_print.h>
++#include <linux/regmap.h>
++
++#define CDNS_DP_SPDIF_CLK 200000000
++#define FW_ALIVE_TIMEOUT_US 1000000
++#define MAILBOX_RETRY_US 1000
++#define MAILBOX_TIMEOUT_US 5000000
++#define LINK_TRAINING_RETRY_MS 20
++#define LINK_TRAINING_TIMEOUT_MS 500
++
++#define mhdp_readx_poll_timeout(op, addr, offset, val, cond, sleep_us, timeout_us) \
++({ \
++ u64 __timeout_us = (timeout_us); \
++ unsigned long __sleep_us = (sleep_us); \
++ ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
++ might_sleep_if((__sleep_us) != 0); \
++ for (;;) { \
++ (val) = op(addr, offset); \
++ if (cond) \
++ break; \
++ if (__timeout_us && \
++ ktime_compare(ktime_get(), __timeout) > 0) { \
++ (val) = op(addr, offset); \
++ break; \
++ } \
++ if (__sleep_us) \
++ usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
++ } \
++ (cond) ? 0 : -ETIMEDOUT; \
++})
++
++/*static inline u32 get_unaligned_be24(const void *p)
++{
++ const u8 *_p = p;
++
++ return _p[0] << 16 | _p[1] << 8 | _p[2];
++}
++
++static inline void put_unaligned_be24(u32 val, void *p)
++{
++ u8 *_p = p;
++
++ _p[0] = val >> 16;
++ _p[1] = val >> 8;
++ _p[2] = val;
++}*/
++
++u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset)
++{
++ u32 val;
++
++ mutex_lock(&mhdp->iolock);
++
++ if (mhdp->bus_type == BUS_TYPE_LOW4K_SAPB) {
++ /* Remap address to low 4K SAPB bus */
++ writel(offset >> 12, mhdp->regs_sec + 0xc);
++ val = readl((offset & 0xfff) + mhdp->regs_base);
++ } else if (mhdp->bus_type == BUS_TYPE_LOW4K_APB) {
++ /* Remap address to low 4K memory */
++ writel(offset >> 12, mhdp->regs_sec + 8);
++ val = readl((offset & 0xfff) + mhdp->regs_base);
++ } else if (mhdp->bus_type == BUS_TYPE_NORMAL_SAPB)
++ val = readl(mhdp->regs_sec + offset);
++ else
++ val = readl(mhdp->regs_base + offset);
++
++ mutex_unlock(&mhdp->iolock);
++
++ return val;
++}
++EXPORT_SYMBOL(cdns_mhdp_bus_read);
++
++void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset)
++{
++ mutex_lock(&mhdp->iolock);
++
++ if (mhdp->bus_type == BUS_TYPE_LOW4K_SAPB) {
++ /* Remap address to low 4K SAPB bus */
++ writel(offset >> 12, mhdp->regs_sec + 0xc);
++ writel(val, (offset & 0xfff) + mhdp->regs_base);
++ } else if (mhdp->bus_type == BUS_TYPE_LOW4K_APB) {
++ /* Remap address to low 4K memory */
++ writel(offset >> 12, mhdp->regs_sec + 8);
++ writel(val, (offset & 0xfff) + mhdp->regs_base);
++ } else if (mhdp->bus_type == BUS_TYPE_NORMAL_SAPB)
++ writel(val, mhdp->regs_sec + offset);
++ else
++ writel(val, mhdp->regs_base + offset);
++
++ mutex_unlock(&mhdp->iolock);
++}
++EXPORT_SYMBOL(cdns_mhdp_bus_write);
++
++u32 cdns_mhdp_get_fw_clk(struct cdns_mhdp_device *mhdp)
++{
++ return cdns_mhdp_bus_read(mhdp, SW_CLK_H);
++}
++EXPORT_SYMBOL(cdns_mhdp_get_fw_clk);
++
++void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk)
++{
++ cdns_mhdp_bus_write(clk / 1000000, mhdp, SW_CLK_H);
++}
++EXPORT_SYMBOL(cdns_mhdp_set_fw_clk);
++
++void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp)
++{
++ u32 val;
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ val = DPTX_FRMR_DATA_CLK_RSTN_EN |
++ DPTX_FRMR_DATA_CLK_EN |
++ DPTX_PHY_DATA_RSTN_EN |
++ DPTX_PHY_DATA_CLK_EN |
++ DPTX_PHY_CHAR_RSTN_EN |
++ DPTX_PHY_CHAR_CLK_EN |
++ SOURCE_AUX_SYS_CLK_RSTN_EN |
++ SOURCE_AUX_SYS_CLK_EN |
++ DPTX_SYS_CLK_RSTN_EN |
++ DPTX_SYS_CLK_EN |
++ CFG_DPTX_VIF_CLK_RSTN_EN |
++ CFG_DPTX_VIF_CLK_EN;
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_DPTX_CAR);
++
++ val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PHY_CAR);
++
++ val = SOURCE_PKT_SYS_RSTN_EN |
++ SOURCE_PKT_SYS_CLK_EN |
++ SOURCE_PKT_DATA_RSTN_EN |
++ SOURCE_PKT_DATA_CLK_EN;
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PKT_CAR);
++
++ val = SPDIF_CDR_CLK_RSTN_EN |
++ SPDIF_CDR_CLK_EN |
++ SOURCE_AIF_SYS_RSTN_EN |
++ SOURCE_AIF_SYS_CLK_EN |
++ SOURCE_AIF_CLK_RSTN_EN |
++ SOURCE_AIF_CLK_EN;
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_AIF_CAR);
++
++ val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
++ SOURCE_CIPHER_SYS_CLK_EN |
++ SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
++ SOURCE_CIPHER_CHAR_CLK_EN;
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_CIPHER_CAR);
++
++ val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
++ SOURCE_CRYPTO_SYS_CLK_EN;
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_CRYPTO_CAR);
++
++ /* enable Mailbox and PIF interrupt */
++ cdns_mhdp_bus_write(0, mhdp, APB_INT_MASK);
++}
++EXPORT_SYMBOL(cdns_mhdp_clock_reset);
++
++int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp)
++{
++ int val, ret;
++
++ ret = mhdp_readx_poll_timeout(cdns_mhdp_bus_read, mhdp, MAILBOX_EMPTY_ADDR,
++ val, !val, MAILBOX_RETRY_US,
++ MAILBOX_TIMEOUT_US);
++ if (ret < 0)
++ return ret;
++
++ return cdns_mhdp_bus_read(mhdp, MAILBOX0_RD_DATA) & 0xff;
++}
++EXPORT_SYMBOL(cdns_mhdp_mailbox_read);
++
++static int cdp_dp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val)
++{
++ int ret, full;
++
++ ret = mhdp_readx_poll_timeout(cdns_mhdp_bus_read, mhdp, MAILBOX_FULL_ADDR,
++ full, !full, MAILBOX_RETRY_US,
++ MAILBOX_TIMEOUT_US);
++ if (ret < 0)
++ return ret;
++
++ cdns_mhdp_bus_write(val, mhdp, MAILBOX0_WR_DATA);
++
++ return 0;
++}
++
++int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp,
++ u8 module_id, u8 opcode,
++ u16 req_size)
++{
++ u32 mbox_size, i;
++ u8 header[4];
++ int ret;
++
++ /* read the header of the message */
++ for (i = 0; i < 4; i++) {
++ ret = cdns_mhdp_mailbox_read(mhdp);
++ if (ret < 0)
++ return ret;
++
++ header[i] = ret;
++ }
++
++ mbox_size = get_unaligned_be16(header + 2);
++
++ if (opcode != header[0] || module_id != header[1] ||
++ req_size != mbox_size) {
++ /*
++ * If the message in mailbox is not what we want, we need to
++ * clear the mailbox by reading its contents.
++ */
++ for (i = 0; i < mbox_size; i++)
++ if (cdns_mhdp_mailbox_read(mhdp) < 0)
++ break;
++
++ return -EINVAL;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(cdns_mhdp_mailbox_validate_receive);
++
++int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp,
++ u8 *buff, u16 buff_size)
++{
++ u32 i;
++ int ret;
++
++ for (i = 0; i < buff_size; i++) {
++ ret = cdns_mhdp_mailbox_read(mhdp);
++ if (ret < 0)
++ return ret;
++
++ buff[i] = ret;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(cdns_mhdp_mailbox_read_receive);
++
++int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id,
++ u8 opcode, u16 size, u8 *message)
++{
++ u8 header[4];
++ int ret, i;
++
++ header[0] = opcode;
++ header[1] = module_id;
++ put_unaligned_be16(size, header + 2);
++
++ for (i = 0; i < 4; i++) {
++ ret = cdp_dp_mailbox_write(mhdp, header[i]);
++ if (ret)
++ return ret;
++ }
++
++ for (i = 0; i < size; i++) {
++ ret = cdp_dp_mailbox_write(mhdp, message[i]);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(cdns_mhdp_mailbox_send);
++
++int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr)
++{
++ u8 msg[4], resp[8];
++ u32 val;
++ int ret;
++
++ if (addr == 0) {
++ ret = -EINVAL;
++ goto err_reg_read;
++ }
++
++ put_unaligned_be32(addr, msg);
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL,
++ GENERAL_READ_REGISTER,
++ sizeof(msg), msg);
++ if (ret)
++ goto err_reg_read;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL,
++ GENERAL_READ_REGISTER,
++ sizeof(resp));
++ if (ret)
++ goto err_reg_read;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, resp, sizeof(resp));
++ if (ret)
++ goto err_reg_read;
++
++ /* Returned address value should be the same as requested */
++ if (memcmp(msg, resp, sizeof(msg))) {
++ ret = -EINVAL;
++ goto err_reg_read;
++ }
++
++ val = get_unaligned_be32(resp + 4);
++
++ return val;
++err_reg_read:
++ DRM_DEV_ERROR(mhdp->dev, "Failed to read register.\n");
++
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_reg_read);
++
++int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val)
++{
++ u8 msg[8];
++
++ put_unaligned_be32(addr, msg);
++ put_unaligned_be32(val, msg + 4);
++
++ return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL,
++ GENERAL_WRITE_REGISTER, sizeof(msg), msg);
++}
++EXPORT_SYMBOL(cdns_mhdp_reg_write);
++
++int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr,
++ u8 start_bit, u8 bits_no, u32 val)
++{
++ u8 field[8];
++
++ put_unaligned_be16(addr, field);
++ field[2] = start_bit;
++ field[3] = bits_no;
++ put_unaligned_be32(val, field + 4);
++
++ return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_WRITE_FIELD, sizeof(field), field);
++}
++EXPORT_SYMBOL(cdns_mhdp_reg_write_bit);
++
++int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp,
++ u32 addr, u8 *data, u16 len)
++{
++ u8 msg[5], reg[5];
++ int ret;
++
++ put_unaligned_be16(len, msg);
++ put_unaligned_be24(addr, msg + 2);
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_READ_DPCD, sizeof(msg), msg);
++ if (ret)
++ goto err_dpcd_read;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_READ_DPCD,
++ sizeof(reg) + len);
++ if (ret)
++ goto err_dpcd_read;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
++ if (ret)
++ goto err_dpcd_read;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, data, len);
++
++err_dpcd_read:
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_dpcd_read);
++
++int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value)
++{
++ u8 msg[6], reg[5];
++ int ret;
++
++ put_unaligned_be16(1, msg);
++ put_unaligned_be24(addr, msg + 2);
++ msg[5] = value;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_WRITE_DPCD, sizeof(msg), msg);
++ if (ret)
++ goto err_dpcd_write;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_WRITE_DPCD, sizeof(reg));
++ if (ret)
++ goto err_dpcd_write;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
++ if (ret)
++ goto err_dpcd_write;
++
++ if (addr != get_unaligned_be24(reg + 2))
++ ret = -EINVAL;
++
++err_dpcd_write:
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "dpcd write failed: %d\n", ret);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_dpcd_write);
++
++int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem,
++ u32 i_size, const u32 *d_mem, u32 d_size)
++{
++ u32 reg;
++ int i, ret;
++
++ /* reset ucpu before load firmware*/
++ cdns_mhdp_bus_write(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
++ mhdp, APB_CTRL);
++
++ for (i = 0; i < i_size; i += 4)
++ cdns_mhdp_bus_write(*i_mem++, mhdp, ADDR_IMEM + i);
++
++ for (i = 0; i < d_size; i += 4)
++ cdns_mhdp_bus_write(*d_mem++, mhdp, ADDR_DMEM + i);
++
++ /* un-reset ucpu */
++ cdns_mhdp_bus_write(0, mhdp, APB_CTRL);
++
++ /* check the keep alive register to make sure fw working */
++ ret = mhdp_readx_poll_timeout(cdns_mhdp_bus_read, mhdp, KEEP_ALIVE,
++ reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
++ if (ret < 0) {
++ DRM_DEV_ERROR(mhdp->dev, "failed to loaded the FW reg = %x\n",
++ reg);
++ return -EINVAL;
++ }
++
++ reg = cdns_mhdp_bus_read(mhdp, VER_L) & 0xff;
++ mhdp->fw_version = reg;
++ reg = cdns_mhdp_bus_read(mhdp, VER_H) & 0xff;
++ mhdp->fw_version |= reg << 8;
++ reg = cdns_mhdp_bus_read(mhdp, VER_LIB_L_ADDR) & 0xff;
++ mhdp->fw_version |= reg << 16;
++ reg = cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) & 0xff;
++ mhdp->fw_version |= reg << 24;
++
++ DRM_DEV_DEBUG(mhdp->dev, "firmware version: %x\n", mhdp->fw_version);
++
++ return 0;
++}
++EXPORT_SYMBOL(cdns_mhdp_load_firmware);
++
++int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable)
++{
++ u8 msg[5];
++ int ret, i;
++
++ msg[0] = GENERAL_MAIN_CONTROL;
++ msg[1] = MB_MODULE_ID_GENERAL;
++ msg[2] = 0;
++ msg[3] = 1;
++ msg[4] = enable ? FW_ACTIVE : FW_STANDBY;
++
++ for (i = 0; i < sizeof(msg); i++) {
++ ret = cdp_dp_mailbox_write(mhdp, msg[i]);
++ if (ret)
++ goto err_set_firmware_active;
++ }
++
++ /* read the firmware state */
++ for (i = 0; i < sizeof(msg); i++) {
++ ret = cdns_mhdp_mailbox_read(mhdp);
++ if (ret < 0)
++ goto err_set_firmware_active;
++
++ msg[i] = ret;
++ }
++
++ ret = 0;
++
++err_set_firmware_active:
++ if (ret < 0)
++ DRM_DEV_ERROR(mhdp->dev, "set firmware active failed\n");
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_set_firmware_active);
++
++int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip)
++{
++ u8 msg[8];
++ int ret;
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.link.rate);
++ msg[1] = mhdp->dp.link.num_lanes | SCRAMBLER_EN;
++ msg[2] = VOLTAGE_LEVEL_2;
++ msg[3] = PRE_EMPHASIS_LEVEL_3;
++ msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
++ msg[5] = FAST_LT_NOT_SUPPORT;
++ msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL;
++ msg[7] = ENHANCED;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_SET_HOST_CAPABILITIES,
++ sizeof(msg), msg);
++ if (ret)
++ goto err_set_host_cap;
++
++/* TODO Sandor */
++// ret = cdns_mhdp_reg_write(mhdp, DP_AUX_SWAP_INVERSION_CONTROL,
++// AUX_HOST_INVERT);
++
++err_set_host_cap:
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_set_host_cap);
++
++int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp)
++{
++ u8 msg[5];
++ int ret;
++
++ memset(msg, 0, sizeof(msg));
++
++ msg[0] = MHDP_EVENT_ENABLE_HPD | MHDP_EVENT_ENABLE_TRAINING;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_ENABLE_EVENT, sizeof(msg), msg);
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "set event config failed: %d\n", ret);
++
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_event_config);
++
++u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp)
++{
++ return cdns_mhdp_bus_read(mhdp, SW_EVENTS0);
++}
++EXPORT_SYMBOL(cdns_mhdp_get_event);
++
++int cdns_mhdp_get_hpd_status(struct cdns_mhdp_device *mhdp)
++{
++ u8 status;
++ int ret;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_HPD_STATE, 0, NULL);
++ if (ret)
++ goto err_get_hpd;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_HPD_STATE,
++ sizeof(status));
++ if (ret)
++ goto err_get_hpd;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, &status, sizeof(status));
++ if (ret)
++ goto err_get_hpd;
++
++ return status;
++
++err_get_hpd:
++ DRM_DEV_ERROR(mhdp->dev, "get hpd status failed: %d\n", ret);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_get_hpd_status);
++
++int cdns_mhdp_get_edid_block(void *data, u8 *edid,
++ unsigned int block, size_t length)
++{
++ struct cdns_mhdp_device *mhdp = data;
++ u8 msg[2], reg[2], i;
++ int ret;
++
++ for (i = 0; i < 4; i++) {
++ msg[0] = block / 2;
++ msg[1] = block % 2;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_GET_EDID, sizeof(msg), msg);
++ if (ret)
++ continue;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp,
++ MB_MODULE_ID_DP_TX,
++ DPTX_GET_EDID,
++ sizeof(reg) + length);
++ if (ret)
++ continue;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
++ if (ret)
++ continue;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length);
++ if (ret)
++ continue;
++
++ if (reg[0] == length && reg[1] == block / 2)
++ break;
++ }
++
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "get block[%d] edid failed: %d\n",
++ block, ret);
++
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_get_edid_block);
++
++static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp)
++{
++ unsigned long timeout;
++ u8 msg, event[2];
++ int ret;
++
++ msg = LINK_TRAINING_RUN;
++
++ /* start training */
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_TRAINING_CONTROL, sizeof(msg), &msg);
++ if (ret)
++ goto err_training_start;
++
++ timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
++ while (time_before(jiffies, timeout)) {
++ msleep(LINK_TRAINING_RETRY_MS);
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_READ_EVENT, 0, NULL);
++ if (ret)
++ goto err_training_start;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp,
++ MB_MODULE_ID_DP_TX,
++ DPTX_READ_EVENT,
++ sizeof(event));
++ if (ret)
++ goto err_training_start;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, event,
++ sizeof(event));
++ if (ret)
++ goto err_training_start;
++
++ if (event[1] & EQ_PHASE_FINISHED)
++ return 0;
++ }
++
++ ret = -ETIMEDOUT;
++
++err_training_start:
++ DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret);
++ return ret;
++}
++
++static int cdns_mhdp_get_training_status(struct cdns_mhdp_device *mhdp)
++{
++ u8 status[10];
++ int ret;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_READ_LINK_STAT, 0, NULL);
++ if (ret)
++ goto err_get_training_status;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_READ_LINK_STAT,
++ sizeof(status));
++ if (ret)
++ goto err_get_training_status;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, status, sizeof(status));
++ if (ret)
++ goto err_get_training_status;
++
++ mhdp->dp.link.rate = drm_dp_bw_code_to_link_rate(status[0]);
++ mhdp->dp.link.num_lanes = status[1];
++
++err_get_training_status:
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "get training status failed: %d\n",
++ ret);
++ return ret;
++}
++
++int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp)
++{
++ int ret;
++
++ ret = cdns_mhdp_training_start(mhdp);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "Failed to start training %d\n",
++ ret);
++ return ret;
++ }
++
++ ret = cdns_mhdp_get_training_status(mhdp);
++ if (ret) {
++ DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat %d\n",
++ ret);
++ return ret;
++ }
++
++ DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", mhdp->dp.link.rate,
++ mhdp->dp.link.num_lanes);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_train_link);
++
++int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active)
++{
++ u8 msg;
++ int ret;
++
++ msg = !!active;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_SET_VIDEO, sizeof(msg), &msg);
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "set video status failed: %d\n", ret);
++
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_set_video_status);
++
++static int cdns_mhdp_get_msa_misc(struct video_info *video,
++ struct drm_display_mode *mode)
++{
++ u32 msa_misc;
++ u8 val[2] = {0};
++
++ switch (video->color_fmt) {
++ case PXL_RGB:
++ case Y_ONLY:
++ val[0] = 0;
++ break;
++ /* set YUV default color space conversion to BT601 */
++ case YCBCR_4_4_4:
++ val[0] = 6 + BT_601 * 8;
++ break;
++ case YCBCR_4_2_2:
++ val[0] = 5 + BT_601 * 8;
++ break;
++ case YCBCR_4_2_0:
++ val[0] = 5;
++ break;
++ };
++
++ switch (video->color_depth) {
++ case 6:
++ val[1] = 0;
++ break;
++ case 8:
++ val[1] = 1;
++ break;
++ case 10:
++ val[1] = 2;
++ break;
++ case 12:
++ val[1] = 3;
++ break;
++ case 16:
++ val[1] = 4;
++ break;
++ };
++
++ msa_misc = 2 * val[0] + 32 * val[1] +
++ ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
++
++ return msa_misc;
++}
++
++int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp)
++{
++ struct video_info *video = &mhdp->video_info;
++ struct drm_display_mode *mode = &mhdp->mode;
++ u64 symbol;
++ u32 val, link_rate, rem;
++ u8 bit_per_pix, tu_size_reg = TU_SIZE;
++ int ret;
++
++ bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
++ (video->color_depth * 2) : (video->color_depth * 3);
++
++ link_rate = mhdp->dp.link.rate / 1000;
++
++ ret = cdns_mhdp_reg_write(mhdp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE);
++ if (ret)
++ goto err_config_video;
++
++ ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, 0);
++ if (ret)
++ goto err_config_video;
++
++ /*
++ * get a best tu_size and valid symbol:
++ * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32
++ * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes)
++ * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set
++ * TU += 2 and repeat 2nd step.
++ */
++ do {
++ tu_size_reg += 2;
++ symbol = tu_size_reg * mode->clock * bit_per_pix;
++ do_div(symbol, mhdp->dp.link.num_lanes * link_rate * 8);
++ rem = do_div(symbol, 1000);
++ if (tu_size_reg > 64) {
++ ret = -EINVAL;
++ DRM_DEV_ERROR(mhdp->dev,
++ "tu error, clk:%d, lanes:%d, rate:%d\n",
++ mode->clock, mhdp->dp.link.num_lanes,
++ link_rate);
++ goto err_config_video;
++ }
++ } while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
++ (rem > 850) || (rem < 100));
++
++ val = symbol + (tu_size_reg << 8);
++ val |= TU_CNT_RST_EN;
++ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_TU, val);
++ if (ret)
++ goto err_config_video;
++
++ /* set the FIFO Buffer size */
++ val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate;
++ val /= (mhdp->dp.link.num_lanes * link_rate);
++ val = div_u64(8 * (symbol + 1), bit_per_pix) - val;
++ val += 2;
++ ret = cdns_mhdp_reg_write(mhdp, DP_VC_TABLE(15), val);
++
++ switch (video->color_depth) {
++ case 6:
++ val = BCS_6;
++ break;
++ case 8:
++ val = BCS_8;
++ break;
++ case 10:
++ val = BCS_10;
++ break;
++ case 12:
++ val = BCS_12;
++ break;
++ case 16:
++ val = BCS_16;
++ break;
++ };
++
++ val += video->color_fmt << 8;
++ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_PXL_REPR, val);
++ if (ret)
++ goto err_config_video;
++
++ val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
++ val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
++ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_SP, val);
++ if (ret)
++ goto err_config_video;
++
++ val = (mode->hsync_start - mode->hdisplay) << 16;
++ val |= mode->htotal - mode->hsync_end;
++ ret = cdns_mhdp_reg_write(mhdp, DP_FRONT_BACK_PORCH, val);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->hdisplay * bit_per_pix / 8;
++ ret = cdns_mhdp_reg_write(mhdp, DP_BYTE_COUNT, val);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
++ ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_0, val);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->hsync_end - mode->hsync_start;
++ val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
++ ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_1, val);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->vtotal;
++ val |= (mode->vtotal - mode->vsync_start) << 16;
++ ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_0, val);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->vsync_end - mode->vsync_start;
++ val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15);
++ ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_1, val);
++ if (ret)
++ goto err_config_video;
++
++ val = cdns_mhdp_get_msa_misc(video, mode);
++ ret = cdns_mhdp_reg_write(mhdp, MSA_MISC, val);
++ if (ret)
++ goto err_config_video;
++
++ ret = cdns_mhdp_reg_write(mhdp, STREAM_CONFIG, 1);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->hsync_end - mode->hsync_start;
++ val |= mode->hdisplay << 16;
++ ret = cdns_mhdp_reg_write(mhdp, DP_HORIZONTAL, val);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->vdisplay;
++ val |= (mode->vtotal - mode->vsync_start) << 16;
++ ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_0, val);
++ if (ret)
++ goto err_config_video;
++
++ val = mode->vtotal;
++ ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_1, val);
++ if (ret)
++ goto err_config_video;
++
++ ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 2, 1, 0);
++
++err_config_video:
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "config video failed: %d\n", ret);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_config_video);
++
++int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp,
++ u8 nlanes, u16 udelay, u8 *lanes_data, u8 *dpcd)
++{
++ u8 payload[7];
++ u8 hdr[5]; /* For DPCD read response header */
++ u32 addr;
++ u8 const nregs = 6; /* Registers 0x202-0x207 */
++ int ret;
++
++ if (nlanes != 4 && nlanes != 2 && nlanes != 1) {
++ DRM_DEV_ERROR(mhdp->dev, "invalid number of lanes: %d\n",
++ nlanes);
++ ret = -EINVAL;
++ goto err_adjust_lt;
++ }
++
++ payload[0] = nlanes;
++ put_unaligned_be16(udelay, payload + 1);
++ memcpy(payload + 3, lanes_data, nlanes);
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_ADJUST_LT,
++ sizeof(payload), payload);
++ if (ret)
++ goto err_adjust_lt;
++
++ /* Yes, read the DPCD read command response */
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
++ DPTX_READ_DPCD,
++ sizeof(hdr) + nregs);
++ if (ret)
++ goto err_adjust_lt;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, hdr, sizeof(hdr));
++ if (ret)
++ goto err_adjust_lt;
++
++ addr = get_unaligned_be24(hdr + 2);
++ if (addr != DP_LANE0_1_STATUS)
++ goto err_adjust_lt;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, dpcd, nregs);
++
++err_adjust_lt:
++ if (ret)
++ DRM_DEV_ERROR(mhdp->dev, "Failed to adjust Link Training.\n");
++
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_adjust_lt);
++
++int cdns_phy_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val)
++{
++ return cdns_mhdp_reg_write(mhdp, ADDR_PHY_AFE + (addr << 2), val);
++}
++EXPORT_SYMBOL(cdns_phy_reg_write);
++
++u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr)
++{
++ return cdns_mhdp_reg_read(mhdp, ADDR_PHY_AFE + (addr << 2));
++}
++EXPORT_SYMBOL(cdns_phy_reg_read);
++
++int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp)
++{
++ u8 status;
++ int ret;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, GENERAL_GET_HPD_STATE,
++ 0, NULL);
++ if (ret)
++ goto err_get_hpd;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL,
++ GENERAL_GET_HPD_STATE, sizeof(status));
++ if (ret)
++ goto err_get_hpd;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, &status, sizeof(status));
++ if (ret)
++ goto err_get_hpd;
++
++ return status;
++
++err_get_hpd:
++ DRM_ERROR("read hpd failed: %d\n", ret);
++ return ret;
++}
++EXPORT_SYMBOL(cdns_mhdp_read_hpd);
++
++bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp)
++{
++ u32 alive, newalive;
++ u8 retries_left = 50;
++
++ alive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE);
++
++ while (retries_left--) {
++ udelay(2);
++
++ newalive = cdns_mhdp_bus_read(mhdp, KEEP_ALIVE);
++ if (alive == newalive)
++ continue;
++ return true;
++ }
++ return false;
++}
++EXPORT_SYMBOL(cdns_mhdp_check_alive);
+diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
+new file mode 100644
+index 0000000..39546fe
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
+@@ -0,0 +1,357 @@
++/*
++ * Copyright (C) 2019 NXP Semiconductor, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/io.h>
++#include <drm/bridge/cdns-mhdp-common.h>
++#include <linux/regmap.h>
++
++void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp,
++ u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type)
++{
++ u32 *packet32, len32;
++ u32 val, i;
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ /* invalidate entry */
++ val = F_ACTIVE_IDLE_TYPE(1) | F_PKT_ALLOC_ADDRESS(entry_id);
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG);
++ cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN);
++
++ /* flush fifo 1 */
++ cdns_mhdp_bus_write(F_FIFO1_FLUSH(1), mhdp, SOURCE_PIF_FIFO1_FLUSH);
++
++ /* write packet into memory */
++ packet32 = (u32 *)packet;
++ len32 = packet_len / 4;
++ for (i = 0; i < len32; i++)
++ cdns_mhdp_bus_write(F_DATA_WR(packet32[i]), mhdp, SOURCE_PIF_DATA_WR);
++
++ /* write entry id */
++ cdns_mhdp_bus_write(F_WR_ADDR(entry_id), mhdp, SOURCE_PIF_WR_ADDR);
++
++ /* write request */
++ cdns_mhdp_bus_write(F_HOST_WR(1), mhdp, SOURCE_PIF_WR_REQ);
++
++ /* update entry */
++ val = F_ACTIVE_IDLE_TYPE(1) | F_TYPE_VALID(1) |
++ F_PACKET_TYPE(packet_type) | F_PKT_ALLOC_ADDRESS(entry_id);
++ cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG);
++
++ cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN);
++}
++
++int cdns_hdmi_get_edid_block(void *data, u8 *edid,
++ u32 block, size_t length)
++{
++ struct cdns_mhdp_device *mhdp = data;
++ u8 msg[2], reg[5], i;
++ int ret;
++
++ for (i = 0; i < 4; i++) {
++ msg[0] = block / 2;
++ msg[1] = block % 2;
++
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_EDID,
++ sizeof(msg), msg);
++ if (ret)
++ continue;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
++ HDMI_TX_EDID, sizeof(reg) + length);
++ if (ret)
++ continue;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
++ if (ret)
++ continue;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length);
++ if (ret)
++ continue;
++
++ if ((reg[3] << 8 | reg[4]) == length)
++ break;
++ }
++
++ if (ret)
++ printk(KERN_ALERT "cdns-mhdp-hdmi: get block[%d] edid failed: %d\n", block, ret);
++ //DRM_ERROR("get block[%d] edid failed: %d\n", block, ret);
++ return ret;
++}
++
++int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data)
++{
++ u8 msg[4], reg[6];
++ int ret;
++
++ msg[0] = 0x54;
++ msg[1] = addr;
++ msg[2] = 0;
++ msg[3] = 1;
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_READ,
++ sizeof(msg), msg);
++ if (ret)
++ goto err_scdc_read;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
++ HDMI_TX_READ, sizeof(reg));
++ if (ret)
++ goto err_scdc_read;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
++ if (ret)
++ goto err_scdc_read;
++
++ *data = reg[5];
++
++err_scdc_read:
++ if (ret)
++ printk(KERN_ALERT "cdns-mhdp-hdmi: scdc read failed: %d\n", ret);
++ //DRM_ERROR("scdc read failed: %d\n", ret);
++ return ret;
++}
++
++int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value)
++{
++ u8 msg[5], reg[5];
++ int ret;
++
++ msg[0] = 0x54;
++ msg[1] = addr;
++ msg[2] = 0;
++ msg[3] = 1;
++ msg[4] = value;
++ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_WRITE,
++ sizeof(msg), msg);
++ if (ret)
++ goto err_scdc_write;
++
++ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
++ HDMI_TX_WRITE, sizeof(reg));
++ if (ret)
++ goto err_scdc_write;
++
++ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
++ if (ret)
++ goto err_scdc_write;
++
++ if (reg[0] != 0)
++ ret = -EINVAL;
++
++err_scdc_write:
++ if (ret)
++ printk(KERN_ALERT "cdns-mhdp-hdmi: scdc write failed: %d\n", ret);
++ //DRM_ERROR("scdc write failed: %d\n", ret);
++ return ret;
++}
++
++int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp,
++ int protocol,
++ u32 char_rate)
++{
++ u32 reg0;
++ u32 reg1;
++ u32 val;
++ int ret;
++
++ printk(KERN_ALERT "DEBUG: %s %d proto: %d char_rate: %ul \n",__FUNCTION__,__LINE__,protocol,char_rate);
++
++ /* Set PHY to HDMI data */
++ ret = cdns_mhdp_reg_write(mhdp, PHY_DATA_SEL, F_SOURCE_PHY_MHDP_SEL(1));
++ if (ret < 0)
++ return ret;
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_HPD,
++ F_HPD_VALID_WIDTH(4) | F_HPD_GLITCH_WIDTH(0));
++ if (ret < 0)
++ return ret;
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ /* open CARS */
++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_PHY_CAR, 0xF);
++ if (ret < 0)
++ return ret;
++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, 0xFF);
++ if (ret < 0)
++ return ret;
++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_PKT_CAR, 0xF);
++ if (ret < 0)
++ return ret;
++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_AIF_CAR, 0xF);
++ if (ret < 0)
++ return ret;
++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CIPHER_CAR, 0xF);
++ if (ret < 0)
++ return ret;
++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CRYPTO_CAR, 0xF);
++ if (ret < 0)
++ return ret;
++ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CEC_CAR, 3);
++ if (ret < 0)
++ return ret;
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ reg0 = reg1 = 0x7c1f;
++ if (protocol == MODE_HDMI_2_0 && char_rate >= 340000) {
++ reg0 = 0;
++ reg1 = 0xFFFFF;
++ }
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_0, reg0);
++ if (ret < 0)
++ return ret;
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_1, reg1);
++ if (ret < 0)
++ return ret;
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ /* set hdmi mode and preemble mode data enable */
++ val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | F_DATA_EN(1) |
++ F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF);
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ return ret;
++}
++
++int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp,
++ struct drm_display_mode *mode,
++ struct video_info *video_info)
++{
++ int ret;
++ u32 val;
++ u32 vsync_lines = mode->vsync_end - mode->vsync_start;
++ u32 eof_lines = mode->vsync_start - mode->vdisplay;
++ u32 sof_lines = mode->vtotal - mode->vsync_end;
++ u32 hblank = mode->htotal - mode->hdisplay;
++ u32 hactive = mode->hdisplay;
++ u32 vblank = mode->vtotal - mode->vdisplay;
++ u32 vactive = mode->vdisplay;
++ u32 hfront = mode->hsync_start - mode->hdisplay;
++ u32 hback = mode->htotal - mode->hsync_end;
++ u32 vfront = eof_lines;
++ u32 hsync = hblank - hfront - hback;
++ u32 vsync = vsync_lines;
++ u32 vback = sof_lines;
++ u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1) +
++ ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : 2);
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_H_SIZE, (hactive << 16) + hblank);
++ if (ret < 0)
++ return ret;
++
++ ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_V_SIZE, (vactive << 16) + vblank);
++ if (ret < 0)
++ return ret;
++
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_FRONT_WIDTH, (vfront << 16) + hfront);
++ if (ret < 0)
++ return ret;
++
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_SYNC_WIDTH, (vsync << 16) + hsync);
++ if (ret < 0)
++ return ret;
++
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_BACK_WIDTH, (vback << 16) + hback);
++ if (ret < 0)
++ return ret;
++
++ ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, v_h_polarity);
++ if (ret < 0)
++ return ret;
++
++ printk(KERN_ALERT "DEBUG: %s %d mode: %d x %d h: %d %d %d v: %d %d %d pol: %d\n",__FUNCTION__,__LINE__,hactive,vactive,hfront,hsync,hback,vfront,
++ vsync,vback,v_h_polarity);
++
++ /* Reset Data Enable */
++ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
++ val &= ~F_DATA_EN(1);
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
++ if (ret < 0)
++ return ret;
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ /* Set bpc */
++ val &= ~F_VIF_DATA_WIDTH(3);
++ switch (video_info->color_depth) {
++ case 10:
++ val |= F_VIF_DATA_WIDTH(1);
++ break;
++ case 12:
++ val |= F_VIF_DATA_WIDTH(2);
++ break;
++ case 16:
++ val |= F_VIF_DATA_WIDTH(3);
++ break;
++ case 8:
++ default:
++ val |= F_VIF_DATA_WIDTH(0);
++ break;
++ }
++ printk(KERN_ALERT "DEBUG: %s %d BPC: %d\n",__FUNCTION__,__LINE__,video_info->color_depth);
++
++ /* select color encoding */
++ val &= ~F_HDMI_ENCODING(3);
++ switch (video_info->color_fmt) {
++ case YCBCR_4_4_4:
++ val |= F_HDMI_ENCODING(2);
++ break;
++ case YCBCR_4_2_2:
++ val |= F_HDMI_ENCODING(1);
++ break;
++ case YCBCR_4_2_0:
++ val |= F_HDMI_ENCODING(3);
++ break;
++ case PXL_RGB:
++ default:
++ val |= F_HDMI_ENCODING(0);
++ break;
++ }
++ printk(KERN_ALERT "DEBUG: %s %d color: %d\n",__FUNCTION__,__LINE__,video_info->color_fmt);
++
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
++ if (ret < 0)
++ return ret;
++
++ /* set data enable */
++ val |= F_DATA_EN(1);
++ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ return ret;
++}
++
++int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp)
++{
++ u32 val;
++
++ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
++ val &= ~F_GCP_EN(1);
++
++ return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
++}
++
++int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp)
++{
++ u32 val;
++
++ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
++ val |= F_GCP_EN(1);
++
++ return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
++}
+diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h
+new file mode 100644
+index 0000000..94d2b25
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp.h
+@@ -0,0 +1,209 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Cadence MHDP DP MST bridge driver.
++ *
++ * Copyright: 2018 Cadence Design Systems, Inc.
++ *
++ * Author: Quentin Schulz <quentin.schulz@free-electrons.com>
++ */
++
++
++#ifndef CDNS_MHDP_H
++#define CDNS_MHDP_H
++
++#include <drm/display/drm_dp_mst_helper.h>
++
++#define CDNS_APB_CFG 0x00000
++#define CDNS_APB_CTRL (CDNS_APB_CFG + 0x00)
++#define CDNS_MAILBOX_FULL (CDNS_APB_CFG + 0x08)
++#define CDNS_MAILBOX_EMPTY (CDNS_APB_CFG + 0x0c)
++#define CDNS_MAILBOX_TX_DATA (CDNS_APB_CFG + 0x10)
++#define CDNS_MAILBOX_RX_DATA (CDNS_APB_CFG + 0x14)
++#define CDNS_KEEP_ALIVE (CDNS_APB_CFG + 0x18)
++#define CDNS_KEEP_ALIVE_MASK GENMASK(7, 0)
++
++#define CDNS_MB_INT_MASK (CDNS_APB_CFG + 0x34)
++
++#define CDNS_SW_CLK_L (CDNS_APB_CFG + 0x3c)
++#define CDNS_SW_CLK_H (CDNS_APB_CFG + 0x40)
++#define CDNS_SW_EVENT0 (CDNS_APB_CFG + 0x44)
++#define CDNS_DPTX_HPD BIT(0)
++
++#define CDNS_SW_EVENT1 (CDNS_APB_CFG + 0x48)
++#define CDNS_SW_EVENT2 (CDNS_APB_CFG + 0x4c)
++#define CDNS_SW_EVENT3 (CDNS_APB_CFG + 0x50)
++
++#define CDNS_APB_INT_MASK (CDNS_APB_CFG + 0x6C)
++#define CDNS_APB_INT_MASK_MAILBOX_INT BIT(0)
++#define CDNS_APB_INT_MASK_SW_EVENT_INT BIT(1)
++
++#define CDNS_DPTX_CAR (CDNS_APB_CFG + 0x904)
++#define CDNS_VIF_CLK_EN BIT(0)
++#define CDNS_VIF_CLK_RSTN BIT(1)
++
++#define CDNS_SOURCE_VIDEO_IF(s) (0x00b00 + (s * 0x20))
++#define CDNS_BND_HSYNC2VSYNC(s) (CDNS_SOURCE_VIDEO_IF(s) + \
++ 0x00)
++#define CDNS_IP_DTCT_WIN GENMASK(11, 0)
++#define CDNS_IP_DET_INTERLACE_FORMAT BIT(12)
++#define CDNS_IP_BYPASS_V_INTERFACE BIT(13)
++
++#define CDNS_HSYNC2VSYNC_POL_CTRL(s) (CDNS_SOURCE_VIDEO_IF(s) + \
++ 0x10)
++#define CDNS_H2V_HSYNC_POL_ACTIVE_LOW BIT(1)
++#define CDNS_H2V_VSYNC_POL_ACTIVE_LOW BIT(2)
++
++#define CDNS_DPTX_PHY_CONFIG 0x02000
++#define CDNS_PHY_TRAINING_EN BIT(0)
++#define CDNS_PHY_TRAINING_TYPE(x) (((x) & GENMASK(3, 0)) << 1)
++#define CDNS_PHY_SCRAMBLER_BYPASS BIT(5)
++#define CDNS_PHY_ENCODER_BYPASS BIT(6)
++#define CDNS_PHY_SKEW_BYPASS BIT(7)
++#define CDNS_PHY_TRAINING_AUTO BIT(8)
++#define CDNS_PHY_LANE0_SKEW(x) (((x) & GENMASK(2, 0)) << 9)
++#define CDNS_PHY_LANE1_SKEW(x) (((x) & GENMASK(2, 0)) << 12)
++#define CDNS_PHY_LANE2_SKEW(x) (((x) & GENMASK(2, 0)) << 15)
++#define CDNS_PHY_LANE3_SKEW(x) (((x) & GENMASK(2, 0)) << 18)
++#define CDNS_PHY_COMMON_CONFIG (CDNS_PHY_LANE1_SKEW(1) | \
++ CDNS_PHY_LANE2_SKEW(2) | \
++ CDNS_PHY_LANE3_SKEW(3))
++#define CDNS_PHY_10BIT_EN BIT(21)
++
++#define CDNS_DPTX_FRAMER 0x02200
++#define CDNS_DP_FRAMER_GLOBAL_CONFIG (CDNS_DPTX_FRAMER + 0x00)
++#define CDNS_DP_NUM_LANES(x) (x - 1)
++#define CDNS_DP_MST_EN BIT(2)
++#define CDNS_DP_FRAMER_EN BIT(3)
++#define CDNS_DP_RATE_GOVERNOR_EN BIT(4)
++#define CDNS_DP_NO_VIDEO_MODE BIT(5)
++#define CDNS_DP_DISABLE_PHY_RST BIT(6)
++#define CDNS_DP_WR_FAILING_EDGE_VSYNC BIT(7)
++
++#define CDNS_DP_SW_RESET (CDNS_DPTX_FRAMER + 0x04)
++#define CDNS_DP_FRAMER_TU (CDNS_DPTX_FRAMER + 0x08)
++#define CDNS_DP_FRAMER_TU_SIZE(x) (((x) & GENMASK(6, 0)) << 8)
++#define CDNS_DP_FRAMER_TU_VS(x) ((x) & GENMASK(5, 0))
++#define CDNS_DP_FRAMER_TU_CNT_RST_EN BIT(15)
++
++#define CDNS_DPTX_STREAM(s) (0x03000 + s * 0x80)
++#define CDNS_DP_MSA_HORIZONTAL_0(s) (CDNS_DPTX_STREAM(s) + 0x00)
++#define CDNS_DP_MSAH0_H_TOTAL(x) (x)
++#define CDNS_DP_MSAH0_HSYNC_START(x) ((x) << 16)
++
++#define CDNS_DP_MSA_HORIZONTAL_1(s) (CDNS_DPTX_STREAM(s) + 0x04)
++#define CDNS_DP_MSAH1_HSYNC_WIDTH(x) (x)
++#define CDNS_DP_MSAH1_HSYNC_POL_LOW BIT(15)
++#define CDNS_DP_MSAH1_HDISP_WIDTH(x) ((x) << 16)
++
++#define CDNS_DP_MSA_VERTICAL_0(s) (CDNS_DPTX_STREAM(s) + 0x08)
++#define CDNS_DP_MSAV0_V_TOTAL(x) (x)
++#define CDNS_DP_MSAV0_VSYNC_START(x) ((x) << 16)
++
++#define CDNS_DP_MSA_VERTICAL_1(s) (CDNS_DPTX_STREAM(s) + 0x0c)
++#define CDNS_DP_MSAV1_VSYNC_WIDTH(x) (x)
++#define CDNS_DP_MSAV1_VSYNC_POL_LOW BIT(15)
++#define CDNS_DP_MSAV1_VDISP_WIDTH(x) ((x) << 16)
++
++#define CDNS_DP_MSA_MISC(s) (CDNS_DPTX_STREAM(s) + 0x10)
++#define CDNS_DP_STREAM_CONFIGs(s) (CDNS_DPTX_STREAM(s) + 0x14)
++#define CDNS_DP_STREAM_CONFIG_2(s) (CDNS_DPTX_STREAM(s) + 0x2c)
++#define CDNS_DP_SC2_TU_VS_DIFF(x) ((x) << 8)
++
++#define CDNS_DP_HORIZONTAL(s) (CDNS_DPTX_STREAM(s) + 0x30)
++#define CDNS_DP_H_HSYNC_WIDTH(x) (x)
++#define CDNS_DP_H_H_TOTAL(x) ((x) << 16)
++
++#define CDNS_DP_VERTICAL_0(s) (CDNS_DPTX_STREAM(s) + 0x34)
++#define CDNS_DP_V0_VHEIGHT(x) (x)
++#define CDNS_DP_V0_VSTART(x) ((x) << 16)
++
++#define CDNS_DP_VERTICAL_1(s) (CDNS_DPTX_STREAM(s) + 0x38)
++#define CDNS_DP_V1_VTOTAL(x) (x)
++#define CDNS_DP_V1_VTOTAL_EVEN BIT(16)
++
++#define CDNS_DP_FRAMER_PXL_REPR(s) (CDNS_DPTX_STREAM(s) + 0x4c)
++#define CDNS_DP_FRAMER_6_BPC BIT(0)
++#define CDNS_DP_FRAMER_8_BPC BIT(1)
++#define CDNS_DP_FRAMER_10_BPC BIT(2)
++#define CDNS_DP_FRAMER_12_BPC BIT(3)
++#define CDNS_DP_FRAMER_16_BPC BIT(4)
++#define CDNS_DP_FRAMER_PXL_FORMAT 0x8
++#define CDNS_DP_FRAMER_RGB BIT(0)
++#define CDNS_DP_FRAMER_YCBCR444 BIT(1)
++#define CDNS_DP_FRAMER_YCBCR422 BIT(2)
++#define CDNS_DP_FRAMER_YCBCR420 BIT(3)
++#define CDNS_DP_FRAMER_Y_ONLY BIT(4)
++
++#define CDNS_DP_FRAMER_SP(s) (CDNS_DPTX_STREAM(s) + 0x10)
++#define CDNS_DP_FRAMER_VSYNC_POL_LOW BIT(0)
++#define CDNS_DP_FRAMER_HSYNC_POL_LOW BIT(1)
++#define CDNS_DP_FRAMER_INTERLACE BIT(2)
++
++#define CDNS_DP_LINE_THRESH(s) (CDNS_DPTX_STREAM(s) + 0x64)
++#define CDNS_DP_ACTIVE_LINE_THRESH(x) (x)
++
++#define CDNS_DP_VB_ID(s) (CDNS_DPTX_STREAM(s) + 0x68)
++#define CDNS_DP_VB_ID_INTERLACED BIT(2)
++#define CDNS_DP_VB_ID_COMPRESSED BIT(6)
++
++#define CDNS_DP_FRONT_BACK_PORCH(s) (CDNS_DPTX_STREAM(s) + 0x78)
++#define CDNS_DP_BACK_PORCH(x) (x)
++#define CDNS_DP_FRONT_PORCH(x) ((x) << 16)
++
++#define CDNS_DP_BYTE_COUNT(s) (CDNS_DPTX_STREAM(s) + 0x7c)
++#define CDNS_DP_BYTE_COUNT_BYTES_IN_CHUNK_SHIFT 16
++
++#define CDNS_DP_MST_STREAM_CONFIG(s) (CDNS_DPTX_STREAM(s) + 0x14)
++#define CDNS_DP_MST_STRM_CFG_STREAM_EN BIT(0)
++#define CDNS_DP_MST_STRM_CFG_NO_VIDEO BIT(1)
++
++#define CDNS_DP_MST_SLOT_ALLOCATE(s) (CDNS_DPTX_STREAM(s) + 0x44)
++#define CDNS_DP_S_ALLOC_START_SLOT(x) (x)
++#define CDNS_DP_S_ALLOC_END_SLOT(x) ((x) << 8)
++
++#define CDNS_DP_RATE_GOVERNING(s) (CDNS_DPTX_STREAM(s) + 0x48)
++#define CDNS_DP_RG_TARG_AV_SLOTS_Y(x) (x)
++#define CDNS_DP_RG_TARG_AV_SLOTS_X(x) (x << 4)
++#define CDNS_DP_RG_ENABLE BIT(10)
++
++#define CDNS_DP_MTPH_CONTROL 0x2264
++#define CDNS_DP_MTPH_ECF_EN BIT(0)
++#define CDNS_DP_MTPH_ACT_EN BIT(1)
++#define CDNS_DP_MTPH_LVP_EN BIT(2)
++
++#define CDNS_DP_MTPH_STATUS 0x226C
++#define CDNS_DP_MTPH_ACT_STATUS BIT(0)
++
++
++#define CDNS_DPTX_GLOBAL 0x02300
++#define CDNS_DP_LANE_EN (CDNS_DPTX_GLOBAL + 0x00)
++#define CDNS_DP_LANE_EN_LANES(x) GENMASK(x - 1, 0)
++#define CDNS_DP_ENHNCD (CDNS_DPTX_GLOBAL + 0x04)
++
++
++#define to_mhdp_connector(x) container_of(x, struct cdns_mhdp_connector, base)
++#define to_mhdp_bridge(x) container_of(x, struct cdns_mhdp_bridge, base)
++#define mgr_to_mhdp(x) container_of(x, struct cdns_mhdp_device, mst_mgr)
++
++#define CDNS_MHDP_MAX_STREAMS 4
++
++enum pixel_format {
++ PIXEL_FORMAT_RGB = 1,
++ PIXEL_FORMAT_YCBCR_444 = 2,
++ PIXEL_FORMAT_YCBCR_422 = 4,
++ PIXEL_FORMAT_YCBCR_420 = 8,
++ PIXEL_FORMAT_Y_ONLY = 16,
++};
++
++
++int cdns_mhdp_mst_init(struct cdns_mhdp_device *mhdp);
++void cdns_mhdp_mst_deinit(struct cdns_mhdp_device *mhdp);
++bool cdns_mhdp_mst_probe(struct cdns_mhdp_device *mhdp);
++enum pixel_format cdns_mhdp_get_pxlfmt(u32 color_formats);
++u32 cdns_mhdp_get_bpp(u32 bpc, u32 color_formats);
++void cdns_mhdp_configure_video(struct drm_bridge *bridge);
++void cdns_mhdp_mst_enable(struct drm_bridge *bridge);
++void cdns_mhdp_mst_disable(struct drm_bridge *bridge);
++void cdns_mhdp_enable(struct drm_bridge *bridge);
++
++#endif
+diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
+index fd5b247..1f31495 100644
+--- a/drivers/gpu/drm/imx/Kconfig
++++ b/drivers/gpu/drm/imx/Kconfig
+@@ -40,4 +40,13 @@ config DRM_IMX_HDMI
+ help
+ Choose this if you want to use HDMI on i.MX6.
+
++config DRM_IMX_CDNS_MHDP
++ tristate "NXP i.MX MX8 DRM HDMI/DP"
++ select DRM_CDNS_MHDP
++ select DRM_CDNS_DP
++ select DRM_CDNS_HDMI
++ select DRM_CDNS_AUDIO
++ help
++ Choose this if you want to use HDMI on i.MX8.
++
+ source "drivers/gpu/drm/imx/dcss/Kconfig"
+diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
+index b644def..b43e325 100644
+--- a/drivers/gpu/drm/imx/Makefile
++++ b/drivers/gpu/drm/imx/Makefile
+@@ -10,3 +10,7 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
+
+ obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
+ obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
++
++cdns_mhdp_imx-objs := cdn-mhdp-imxdrv.o cdn-mhdp-dp-phy.o cdn-mhdp-hdmi-phy.o
++obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o
++
+diff --git a/drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c b/drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c
+new file mode 100644
+index 0000000..ab155d6
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdn-mhdp-dp-phy.c
+@@ -0,0 +1,529 @@
++/*
++ * Cadence Display Port Interface (DP) PHY driver
++ *
++ * Copyright (C) 2019 NXP Semiconductor, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ */
++#include <linux/clk.h>
++#include <linux/kernel.h>
++#include <drm/display/drm_dp_helper.h>
++#include <drm/bridge/cdns-mhdp-common.h>
++#include "cdn-mhdp-phy.h"
++
++enum dp_link_rate {
++ RATE_1_6 = 162000,
++ RATE_2_1 = 216000,
++ RATE_2_4 = 243000,
++ RATE_2_7 = 270000,
++ RATE_3_2 = 324000,
++ RATE_4_3 = 432000,
++ RATE_5_4 = 540000,
++ RATE_8_1 = 810000,
++};
++
++struct phy_pll_reg {
++ u16 val[7];
++ u32 addr;
++};
++
++static const struct phy_pll_reg phy_pll_27m_cfg[] = {
++ /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 register address */
++ {{ 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E }, CMN_PLL0_VCOCAL_INIT_TMR },
++ {{ 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B }, CMN_PLL0_VCOCAL_ITER_TMR },
++ {{ 0x30B9, 0x3087, 0x3096, 0x30B4, 0x30B9, 0x3087, 0x30B4 }, CMN_PLL0_VCOCAL_START },
++ {{ 0x0077, 0x009F, 0x00B3, 0x00C7, 0x0077, 0x009F, 0x00C7 }, CMN_PLL0_INTDIV },
++ {{ 0xF9DA, 0xF7CD, 0xF6C7, 0xF5C1, 0xF9DA, 0xF7CD, 0xF5C1 }, CMN_PLL0_FRACDIV },
++ {{ 0x001E, 0x0028, 0x002D, 0x0032, 0x001E, 0x0028, 0x0032 }, CMN_PLL0_HIGH_THR },
++ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_PLL0_DSM_DIAG },
++ {{ 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL },
++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD },
++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBH_OVRD },
++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBL_OVRD },
++ {{ 0x0006, 0x0007, 0x0007, 0x0007, 0x0006, 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE },
++ {{ 0x0043, 0x0043, 0x0043, 0x0042, 0x0043, 0x0043, 0x0042 }, CMN_DIAG_PLL0_CP_TUNE },
++ {{ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, CMN_DIAG_PLL0_LF_PROG },
++ {{ 0x0100, 0x0001, 0x0001, 0x0001, 0x0100, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE1 },
++ {{ 0x0007, 0x0001, 0x0001, 0x0001, 0x0007, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE2 },
++ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_DIAG_PLL0_TEST_MODE},
++ {{ 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, CMN_PSM_CLK_CTRL }
++};
++
++static const struct phy_pll_reg phy_pll_24m_cfg[] = {
++ /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 register address */
++ {{ 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0, 0x00F0 }, CMN_PLL0_VCOCAL_INIT_TMR },
++ {{ 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018 }, CMN_PLL0_VCOCAL_ITER_TMR },
++ {{ 0x3061, 0x3092, 0x30B3, 0x30D0, 0x3061, 0x3092, 0x30D0 }, CMN_PLL0_VCOCAL_START },
++ {{ 0x0086, 0x00B3, 0x00CA, 0x00E0, 0x0086, 0x00B3, 0x00E0 }, CMN_PLL0_INTDIV },
++ {{ 0xF917, 0xF6C7, 0x75A1, 0xF479, 0xF917, 0xF6C7, 0xF479 }, CMN_PLL0_FRACDIV },
++ {{ 0x0022, 0x002D, 0x0033, 0x0038, 0x0022, 0x002D, 0x0038 }, CMN_PLL0_HIGH_THR },
++ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_PLL0_DSM_DIAG },
++ {{ 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL },
++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD },
++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBH_OVRD },
++ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBL_OVRD },
++ {{ 0x0006, 0x0007, 0x0007, 0x0007, 0x0006, 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE },
++ {{ 0x0026, 0x0029, 0x0029, 0x0029, 0x0026, 0x0029, 0x0029 }, CMN_DIAG_PLL0_CP_TUNE },
++ {{ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, CMN_DIAG_PLL0_LF_PROG },
++ {{ 0x008C, 0x008C, 0x008C, 0x008C, 0x008C, 0x008C, 0x008C }, CMN_DIAG_PLL0_PTATIS_TUNE1 },
++ {{ 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E, 0x002E }, CMN_DIAG_PLL0_PTATIS_TUNE2 },
++ {{ 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022, 0x0022 }, CMN_DIAG_PLL0_TEST_MODE},
++ {{ 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, CMN_PSM_CLK_CTRL }
++};
++
++static int link_rate_index(u32 rate)
++{
++ switch (rate) {
++ case RATE_1_6:
++ return 0;
++ case RATE_2_1:
++ return 1;
++ case RATE_2_4:
++ return 2;
++ case RATE_2_7:
++ return 3;
++ case RATE_3_2:
++ return 4;
++ case RATE_4_3:
++ return 5;
++ case RATE_5_4:
++ return 6;
++ default:
++ return -1;
++ }
++}
++
++static void dp_aux_cfg(struct cdns_mhdp_device *mhdp)
++{
++ /* Power up Aux */
++ cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 1);
++
++ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, 36);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA018);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0000);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x1001);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA098);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA198);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030d);
++ ndelay(150);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030f);
++}
++
++/* PMA common configuration for 24MHz */
++static void dp_phy_pma_cmn_cfg_24mhz(struct cdns_mhdp_device *mhdp)
++{
++ int k;
++ u32 num_lanes = mhdp->dp.link.num_lanes;
++ u16 val;
++
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ val &= 0xFFF7;
++ val |= 0x0008;
++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
++
++ for (k = 0; k < num_lanes; k++) {
++ /* Transceiver control and diagnostic registers */
++ cdns_phy_reg_write(mhdp, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), 0x0090);
++ /* Transmitter receiver detect registers */
++ cdns_phy_reg_write(mhdp, TX_RCVDET_EN_TMR | (k << 9), 0x0960);
++ cdns_phy_reg_write(mhdp, TX_RCVDET_ST_TMR | (k << 9), 0x0030);
++ }
++}
++
++/* Valid for 24 MHz only */
++static void dp_phy_pma_cmn_pll0_24mhz(struct cdns_mhdp_device *mhdp)
++{
++ u32 num_lanes = mhdp->dp.link.num_lanes;
++ u32 link_rate = mhdp->dp.link.rate;
++ u16 val;
++ int index, i, k;
++
++ /*
++ * PLL reference clock source select
++ * for single ended reference clock val |= 0x0030;
++ * for differential clock val |= 0x0000;
++ */
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ val = val & 0xFF8F;
++ val = val | 0x0030;
++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
++
++ /* DP PLL data rate 0/1 clock divider value */
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ val &= 0x00FF;
++ if (link_rate <= RATE_2_7)
++ val |= 0x2400;
++ else
++ val |= 0x1200;
++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
++
++ /* High speed clock 0/1 div */
++ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL);
++ val &= 0xFFCC;
++ if (link_rate <= RATE_2_7)
++ val |= 0x0011;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val);
++
++ for (k = 0; k < num_lanes; k = k + 1) {
++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)));
++ val &= 0xCFFF;
++ if (link_rate <= RATE_2_7)
++ val |= 0x1000;
++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val);
++ }
++
++ /* DP PHY PLL 24MHz configuration */
++ index = link_rate_index(link_rate);
++ for (i = 0; i < ARRAY_SIZE(phy_pll_24m_cfg); i++)
++ cdns_phy_reg_write(mhdp, phy_pll_24m_cfg[i].addr, phy_pll_24m_cfg[i].val[index]);
++
++ /* Transceiver control and diagnostic registers */
++ for (k = 0; k < num_lanes; k = k + 1) {
++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)));
++ val &= 0x8FFF;
++ if (link_rate <= RATE_2_7)
++ val |= 0x2000;
++ else
++ val |= 0x1000;
++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val);
++ }
++
++ for (k = 0; k < num_lanes; k = k + 1) {
++ cdns_phy_reg_write(mhdp, (XCVR_PSM_RCTRL | (k << 9)), 0xBEFC);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A0 | (k << 9)), 0x6799);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A1 | (k << 9)), 0x6798);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A2 | (k << 9)), 0x0098);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A3 | (k << 9)), 0x0098);
++ }
++}
++
++/* PMA common configuration for 27MHz */
++static void dp_phy_pma_cmn_cfg_27mhz(struct cdns_mhdp_device *mhdp)
++{
++ u32 num_lanes = mhdp->dp.link.num_lanes;
++ u16 val;
++ int k;
++
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ val &= 0xFFF7;
++ val |= 0x0008;
++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
++
++ /* Startup state machine registers */
++ cdns_phy_reg_write(mhdp, CMN_SSM_BIAS_TMR, 0x0087);
++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLEN_TMR, 0x001B);
++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLPRE_TMR, 0x0036);
++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLVREF_TMR, 0x001B);
++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLLOCK_TMR, 0x006C);
++
++ /* Current calibration registers */
++ cdns_phy_reg_write(mhdp, CMN_ICAL_INIT_TMR, 0x0044);
++ cdns_phy_reg_write(mhdp, CMN_ICAL_ITER_TMR, 0x0006);
++ cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_INIT_TMR, 0x0022);
++ cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_ITER_TMR, 0x0006);
++
++ /* Resistor calibration registers */
++ cdns_phy_reg_write(mhdp, CMN_TXPUCAL_INIT_TMR, 0x0022);
++ cdns_phy_reg_write(mhdp, CMN_TXPUCAL_ITER_TMR, 0x0006);
++ cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_INIT_TMR, 0x0022);
++ cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_ITER_TMR, 0x0006);
++ cdns_phy_reg_write(mhdp, CMN_TXPDCAL_INIT_TMR, 0x0022);
++ cdns_phy_reg_write(mhdp, CMN_TXPDCAL_ITER_TMR, 0x0006);
++ cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_INIT_TMR, 0x0022);
++ cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_ITER_TMR, 0x0006);
++ cdns_phy_reg_write(mhdp, CMN_RXCAL_INIT_TMR, 0x0022);
++ cdns_phy_reg_write(mhdp, CMN_RXCAL_ITER_TMR, 0x0006);
++ cdns_phy_reg_write(mhdp, CMN_RX_ADJ_INIT_TMR, 0x0022);
++ cdns_phy_reg_write(mhdp, CMN_RX_ADJ_ITER_TMR, 0x0006);
++
++ for (k = 0; k < num_lanes; k = k + 1) {
++ /* Power state machine registers */
++ cdns_phy_reg_write(mhdp, XCVR_PSM_CAL_TMR | (k << 9), 0x016D);
++ cdns_phy_reg_write(mhdp, XCVR_PSM_A0IN_TMR | (k << 9), 0x016D);
++ /* Transceiver control and diagnostic registers */
++ cdns_phy_reg_write(mhdp, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), 0x00A2);
++ cdns_phy_reg_write(mhdp, TX_DIAG_BGREF_PREDRV_DELAY | (k << 9), 0x0097);
++ /* Transmitter receiver detect registers */
++ cdns_phy_reg_write(mhdp, TX_RCVDET_EN_TMR | (k << 9), 0x0A8C);
++ cdns_phy_reg_write(mhdp, TX_RCVDET_ST_TMR | (k << 9), 0x0036);
++ }
++}
++
++static void dp_phy_pma_cmn_pll0_27mhz(struct cdns_mhdp_device *mhdp)
++{
++ u32 num_lanes = mhdp->dp.link.num_lanes;
++ u32 link_rate = mhdp->dp.link.rate;
++ u16 val;
++ int index, i, k;
++
++ /*
++ * PLL reference clock source select
++ * for single ended reference clock val |= 0x0030;
++ * for differential clock val |= 0x0000;
++ */
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ val &= 0xFF8F;
++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
++
++ /* for differential clock on the refclk_p and refclk_m off chip pins:
++ * CMN_DIAG_ACYA[8]=1'b1
++ */
++ cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100);
++
++ /* DP PLL data rate 0/1 clock divider value */
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ val &= 0x00FF;
++ if (link_rate <= RATE_2_7)
++ val |= 0x2400;
++ else
++ val |= 0x1200;
++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
++
++ /* High speed clock 0/1 div */
++ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL);
++ val &= 0xFFCC;
++ if (link_rate <= RATE_2_7)
++ val |= 0x0011;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val);
++
++ for (k = 0; k < num_lanes; k++) {
++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)));
++ val = val & 0xCFFF;
++ if (link_rate <= RATE_2_7)
++ val |= 0x1000;
++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val);
++ }
++
++ /* DP PHY PLL 27MHz configuration */
++ index = link_rate_index(link_rate);
++ for (i = 0; i < ARRAY_SIZE(phy_pll_27m_cfg); i++)
++ cdns_phy_reg_write(mhdp, phy_pll_27m_cfg[i].addr, phy_pll_27m_cfg[i].val[index]);
++
++ /* Transceiver control and diagnostic registers */
++ for (k = 0; k < num_lanes; k++) {
++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)));
++ val = val & 0x8FFF;
++ if (link_rate <= RATE_2_7)
++ val |= 0x2000;
++ else
++ val |= 0x1000;
++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val);
++ }
++
++ for (k = 0; k < num_lanes; k = k + 1) {
++ /* Power state machine registers */
++ cdns_phy_reg_write(mhdp, (XCVR_PSM_RCTRL | (k << 9)), 0xBEFC);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A0 | (k << 9)), 0x6799);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A1 | (k << 9)), 0x6798);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A2 | (k << 9)), 0x0098);
++ cdns_phy_reg_write(mhdp, (TX_PSC_A3 | (k << 9)), 0x0098);
++ /* Receiver calibration power state definition register */
++ val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9));
++ val &= 0xFFBB;
++ cdns_phy_reg_write(mhdp, (RX_PSC_CAL | (k << 9)), val);
++ val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9));
++ val &= 0xFFBB;
++ cdns_phy_reg_write(mhdp, (RX_PSC_A0 | (k << 9)), val);
++ }
++}
++
++static void dp_phy_power_down(struct cdns_mhdp_device *mhdp)
++{
++ u16 val;
++ int i;
++
++ if (!mhdp->power_up)
++ return;
++
++ /* Place the PHY lanes in the A3 power state. */
++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x8);
++ /* Wait for Power State A3 Ack */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
++ if (val & (1 << 7))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait A3 Ack failed\n");
++ return;
++ }
++
++ /* Disable HDP PLL’s data rate and full rate clocks out of PMA. */
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ val &= ~(1 << 2);
++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
++ /* Wait for PLL clock gate ACK */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ if (!(val & (1 << 3)))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait PLL clock gate Ack failed\n");
++ return;
++ }
++
++ /* Disable HDP PLL’s for high speed clocks */
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ val &= ~(1 << 0);
++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
++ /* Wait for PLL disable ACK */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ if (!(val & (1 << 1)))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait PLL disable Ack failed\n");
++ return;
++ }
++}
++
++static int dp_phy_power_up(struct cdns_mhdp_device *mhdp)
++{
++ u32 val, i;
++
++ /* Enable HDP PLL’s for high speed clocks */
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ val |= (1 << 0);
++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
++ /* Wait for PLL ready ACK */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ if (val & (1 << 1))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait PLL Ack failed\n");
++ return -1;
++ }
++
++ /* Enable HDP PLL’s data rate and full rate clocks out of PMA. */
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ val |= (1 << 2);
++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
++ /* Wait for PLL clock enable ACK */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ if (val & (1 << 3))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait PLL clock enable ACk failed\n");
++ return -1;
++ }
++
++ /* Configure PHY in A2 Mode */
++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004);
++ /* Wait for Power State A2 Ack */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
++ if (val & (1 << 6))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait A2 Ack failed\n");
++ return -1;
++ }
++
++ /* Configure PHY in A0 mode (PHY must be in the A0 power
++ * state in order to transmit data)
++ */
++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0101);
++
++ /* Wait for Power State A0 Ack */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
++ if (val & (1 << 4))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait A0 Ack failed\n");
++ return -1;
++ }
++
++ mhdp->power_up = true;
++
++ return 0;
++}
++
++int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *mhdp)
++{
++ int ret;
++
++ /* Disable phy clock if PHY in power up state */
++ dp_phy_power_down(mhdp);
++
++ dp_phy_pma_cmn_cfg_27mhz(mhdp);
++
++ dp_phy_pma_cmn_pll0_27mhz(mhdp);
++
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1);
++
++ /* PHY power up */
++ ret = dp_phy_power_up(mhdp);
++ if (ret < 0)
++ return ret;
++
++ dp_aux_cfg(mhdp);
++
++ return ret;
++}
++
++int cdns_dp_phy_set_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++ int ret;
++
++ /* Disable phy clock if PHY in power up state */
++ dp_phy_power_down(mhdp);
++
++ dp_phy_pma_cmn_cfg_24mhz(mhdp);
++
++ dp_phy_pma_cmn_pll0_24mhz(mhdp);
++
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1);
++
++ /* PHY power up */
++ ret = dp_phy_power_up(mhdp);
++ if (ret < 0)
++ return ret;
++
++ dp_aux_cfg(mhdp);
++
++ return true;
++}
+diff --git a/drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c b/drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c
+new file mode 100644
+index 0000000..d7b5034
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdn-mhdp-hdmi-phy.c
+@@ -0,0 +1,777 @@
++/*
++ * Cadence High-Definition Multimedia Interface (HDMI) driver
++ *
++ * Copyright (C) 2019 NXP Semiconductor, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ */
++#include <drm/drm_of.h>
++#include <drm/drm_crtc_helper.h>
++#include <linux/io.h>
++#include <drm/drm_edid.h>
++#include <drm/drm_encoder_slave.h>
++#include <drm/drm_atomic.h>
++#include <linux/io.h>
++
++#include <drm/bridge/cdns-mhdp-common.h>
++#include "cdn-mhdp-phy.h"
++
++/* HDMI TX clock control settings */
++struct hdmi_ctrl {
++ u32 pixel_clk_freq_min;
++ u32 pixel_clk_freq_max;
++ u32 feedback_factor;
++ u32 data_range_kbps_min;
++ u32 data_range_kbps_max;
++ u32 cmnda_pll0_ip_div;
++ u32 cmn_ref_clk_dig_div;
++ u32 ref_clk_divider_scaler;
++ u32 pll_fb_div_total;
++ u32 cmnda_pll0_fb_div_low;
++ u32 cmnda_pll0_fb_div_high;
++ u32 pixel_div_total;
++ u32 cmnda_pll0_pxdiv_low;
++ u32 cmnda_pll0_pxdiv_high;
++ u32 vco_freq_min;
++ u32 vco_freq_max;
++ u32 vco_ring_select;
++ u32 cmnda_hs_clk_0_sel;
++ u32 cmnda_hs_clk_1_sel;
++ u32 hsclk_div_at_xcvr;
++ u32 hsclk_div_tx_sub_rate;
++ u32 cmnda_pll0_hs_sym_div_sel;
++ u32 cmnda_pll0_clk_freq_min;
++ u32 cmnda_pll0_clk_freq_max;
++};
++
++/* HDMI TX clock control settings, pixel clock is output */
++static const struct hdmi_ctrl imx8mq_ctrl_table[] = {
++/*Minclk Maxclk Fdbak DR_min DR_max ip_d dig DS Totl */
++{ 27000, 27000, 1000, 270000, 270000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x3, 27000, 27000},
++{ 27000, 27000, 1250, 337500, 337500, 0x03, 0x1, 0x1, 300, 0x0EC, 0x03C, 100, 0x030, 0x030, 2700000, 2700000, 0, 2, 2, 2, 4, 0x3, 33750, 33750},
++{ 27000, 27000, 1500, 405000, 405000, 0x03, 0x1, 0x1, 360, 0x11C, 0x048, 120, 0x03A, 0x03A, 3240000, 3240000, 0, 2, 2, 2, 4, 0x3, 40500, 40500},
++{ 27000, 27000, 2000, 540000, 540000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x2, 54000, 54000},
++{ 29700, 29700, 1000, 297000, 297000, 0x03, 0x1, 0x1, 264, 0x0d0, 0x034, 80, 0x026, 0x026, 2376000, 2376000, 0, 2, 2, 2, 4, 0x3, 29700, 29700},
++
++{ 54000, 54000, 1000, 540000, 540000, 0x03, 0x1, 0x1, 480, 0x17C, 0x060, 80, 0x026, 0x026, 4320000, 4320000, 1, 2, 2, 2, 4, 0x3, 54000, 54000},
++{ 54000, 54000, 1250, 675000, 675000, 0x04, 0x1, 0x1, 400, 0x13C, 0x050, 50, 0x017, 0x017, 2700000, 2700000, 0, 1, 1, 2, 4, 0x2, 67500, 67500},
++{ 54000, 54000, 1500, 810000, 810000, 0x04, 0x1, 0x1, 480, 0x17C, 0x060, 60, 0x01C, 0x01C, 3240000, 3240000, 0, 2, 2, 2, 2, 0x2, 81000, 81000},
++{ 54000, 54000, 2000, 1080000, 1080000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 40, 0x012, 0x012, 2160000, 2160000, 0, 2, 2, 2, 1, 0x1, 108000, 108000},
++{ 74250, 74250, 1000, 742500, 742500, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 80, 0x026, 0x026, 5940000, 5940000, 1, 2, 2, 2, 4, 0x3, 74250, 74250},
++{ 74250, 74250, 1250, 928125, 928125, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 50, 0x017, 0x017, 3712500, 3712500, 1, 1, 1, 2, 4, 0x2, 92812, 92812},
++{ 74250, 74250, 1500, 1113750, 1113750, 0x04, 0x1, 0x1, 660, 0x20C, 0x084, 60, 0x01C, 0x01C, 4455000, 4455000, 1, 2, 2, 2, 2, 0x2, 111375, 111375},
++{ 74250, 74250, 2000, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 40, 0x012, 0x012, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500},
++{ 99000, 99000, 1000, 990000, 990000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 2, 0x2, 99000, 99000},
++{ 99000, 99000, 1250, 1237500, 1237500, 0x03, 0x1, 0x1, 275, 0x0D8, 0x037, 25, 0x00B, 0x00A, 2475000, 2475000, 0, 1, 1, 2, 2, 0x1, 123750, 123750},
++{ 99000, 99000, 1500, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 30, 0x00D, 0x00D, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500},
++{ 99000, 99000, 2000, 1980000, 1980000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 1, 0x1, 198000, 198000},
++{148500, 148500, 1000, 1485000, 1485000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 2, 0x2, 148500, 148500},
++{148500, 148500, 1250, 1856250, 1856250, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 3712500, 3712500, 1, 1, 1, 2, 2, 0x1, 185625, 185625},
++{148500, 148500, 1500, 2227500, 2227500, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 30, 0x00D, 0x00D, 4455000, 4455000, 1, 1, 1, 2, 2, 0x1, 222750, 222750},
++{148500, 148500, 2000, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 1, 0x1, 297000, 297000},
++{198000, 198000, 1000, 1980000, 1980000, 0x03, 0x1, 0x1, 220, 0x0AC, 0x02C, 10, 0x003, 0x003, 1980000, 1980000, 0, 1, 1, 2, 1, 0x0, 198000, 198000},
++{198000, 198000, 1250, 2475000, 2475000, 0x03, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 4950000, 4950000, 1, 1, 1, 2, 2, 0x1, 247500, 247500},
++{198000, 198000, 1500, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 15, 0x006, 0x005, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000},
++{198000, 198000, 2000, 3960000, 3960000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 20, 0x008, 0x008, 3960000, 3960000, 1, 1, 1, 2, 1, 0x0, 396000, 396000},
++{297000, 297000, 1000, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 10, 0x003, 0x003, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000},
++{297000, 297000, 1500, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 15, 0x006, 0x005, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500},
++{297000, 297000, 2000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 20, 0x008, 0x008, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000},
++{594000, 594000, 1000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000},
++{594000, 594000, 750, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 10, 0x003, 0x003, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500},
++{594000, 594000, 625, 3712500, 3712500, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 10, 0x003, 0x003, 3712500, 3712500, 1, 1, 1, 2, 1, 0x0, 371250, 371250},
++{594000, 594000, 500, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 2, 0x1, 297000, 297000},
++};
++
++/* HDMI TX clock control settings, pixel clock is input */
++static const struct hdmi_ctrl imx8qm_ctrl_table[] = {
++/*pclk_l pclk_h fd DRR_L DRR_H PLLD */
++{ 25000, 42500, 1000, 250000, 425000, 0x05, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2000000, 3400000, 0, 2, 2, 2, 4, 0x03, 25000, 42500},
++{ 42500, 85000, 1000, 425000, 850000, 0x08, 0x03, 0x01, 320, 0x132, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 4, 0x02, 42500, 85000},
++{ 85000, 170000, 1000, 850000, 1700000, 0x11, 0x00, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000},
++{170000, 340000, 1000, 1700000, 3400000, 0x22, 0x01, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000},
++{340000, 600000, 1000, 3400000, 6000000, 0x3C, 0x03, 0x06, 600, 0x24A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000},
++{ 25000, 34000, 1205, 312500, 425000, 0x04, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2500000, 3400000, 0, 2, 2, 2, 4, 0x03, 31250, 42500},
++{ 34000, 68000, 1205, 425000, 850000, 0x06, 0x02, 0x01, 300, 0x11E, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 4, 0x02, 42500, 85000},
++{ 68000, 136000, 1205, 850000, 1700000, 0x0D, 0x02, 0x02, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000},
++{136000, 272000, 1205, 1700000, 3400000, 0x1A, 0x02, 0x04, 325, 0x137, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000},
++{272000, 480000, 1205, 3400000, 6000000, 0x30, 0x03, 0x05, 600, 0x24A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000},
++{ 25000, 28000, 1500, 375000, 420000, 0x03, 0x01, 0x01, 360, 0x15A, 0x00A, 0, 0, 0, 3000000, 3360000, 0, 2, 2, 2, 4, 0x03, 37500, 42000},
++{ 28000, 56000, 1500, 420000, 840000, 0x06, 0x02, 0x01, 360, 0x15A, 0x00A, 0, 0, 0, 1680000, 3360000, 0, 1, 1, 2, 4, 0x02, 42000, 84000},
++{ 56000, 113000, 1500, 840000, 1695000, 0x0B, 0x00, 0x05, 330, 0x13C, 0x00A, 0, 0, 0, 1680000, 3390000, 0, 1, 1, 2, 2, 0x01, 84000, 169500},
++{113000, 226000, 1500, 1695000, 3390000, 0x16, 0x01, 0x05, 330, 0x13C, 0x00A, 0, 0, 0, 1695000, 3390000, 0, 1, 1, 2, 1, 0x00, 169500, 339000},
++{226000, 400000, 1500, 3390000, 6000000, 0x28, 0x03, 0x04, 600, 0x24A, 0x00A, 0, 0, 0, 3390000, 6000000, 1, 1, 1, 2, 1, 0x00, 339000, 600000},
++{ 25000, 42500, 2000, 500000, 850000, 0x05, 0x01, 0x01, 400, 0x182, 0x00A, 0, 0, 0, 2000000, 3400000, 0, 1, 1, 2, 4, 0x02, 50000, 85000},
++{ 42500, 85000, 2000, 850000, 1700000, 0x08, 0x03, 0x01, 320, 0x132, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 2, 0x01, 85000, 170000},
++{ 85000, 170000, 2000, 1700000, 3400000, 0x11, 0x00, 0x07, 340, 0x146, 0x00A, 0, 0, 0, 1700000, 3400000, 0, 1, 1, 2, 1, 0x00, 170000, 340000},
++{170000, 300000, 2000, 3400000, 6000000, 0x22, 0x01, 0x06, 680, 0x29A, 0x00A, 0, 0, 0, 3400000, 6000000, 1, 1, 1, 2, 1, 0x00, 340000, 600000},
++{594000, 594000, 5000, 2970000, 2970000, 0x3C, 0x03, 0x06, 600, 0x24A, 0x00A, 0, 0, 0, 5940000, 5940000, 1, 1, 1, 2, 2, 0x01, 297000, 297000},
++{594000, 594000, 6250, 3712500, 3712500, 0x3C, 0x03, 0x06, 375, 0x169, 0x00A, 0, 0, 0, 3712500, 3712500, 1, 1, 1, 2, 1, 0x00, 371250, 371250},
++{594000, 594000, 7500, 4455000, 4455000, 0x3C, 0x03, 0x06, 450, 0x1B4, 0x00A, 0, 0, 0, 4455000, 4455000, 1, 1, 1, 2, 1, 0x00, 445500, 445500},
++};
++
++/* HDMI TX PLL tuning settings */
++struct hdmi_pll_tuning {
++ u32 vco_freq_bin;
++ u32 vco_freq_min;
++ u32 vco_freq_max;
++ u32 volt_to_current_coarse;
++ u32 volt_to_current;
++ u32 ndac_ctrl;
++ u32 pmos_ctrl;
++ u32 ptat_ndac_ctrl;
++ u32 feedback_div_total;
++ u32 charge_pump_gain;
++ u32 coarse_code;
++ u32 v2i_code;
++ u32 vco_cal_code;
++};
++
++/* HDMI TX PLL tuning settings, pixel clock is output */
++static const struct hdmi_pll_tuning imx8mq_pll_table[] = {
++/* bin VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain Coa V2I CAL */
++ { 1, 1980000, 1980000, 0x4, 0x3, 0x0, 0x09, 0x09, 220, 0x42, 160, 5, 183 },
++ { 2, 2160000, 2160000, 0x4, 0x3, 0x0, 0x09, 0x09, 240, 0x42, 166, 6, 208 },
++ { 2, 2376000, 2376000, 0x4, 0x3, 0x0, 0x09, 0x09, 264, 0x42, 166, 6, 208 },
++ { 3, 2475000, 2475000, 0x5, 0x3, 0x1, 0x00, 0x07, 275, 0x42, 167, 6, 209 },
++ { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 300, 0x42, 188, 6, 230 },
++ { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 400, 0x4C, 188, 6, 230 },
++ { 5, 2970000, 2970000, 0x6, 0x3, 0x1, 0x00, 0x07, 330, 0x42, 183, 6, 225 },
++ { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 360, 0x42, 203, 7, 256 },
++ { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 480, 0x4C, 203, 7, 256 },
++ { 7, 3712500, 3712500, 0x4, 0x3, 0x0, 0x07, 0x0F, 550, 0x4C, 212, 7, 257 },
++ { 8, 3960000, 3960000, 0x5, 0x3, 0x0, 0x07, 0x0F, 440, 0x42, 184, 6, 226 },
++ { 9, 4320000, 4320000, 0x5, 0x3, 0x1, 0x07, 0x0F, 480, 0x42, 205, 7, 258 },
++ { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 495, 0x42, 219, 7, 272 },
++ { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 660, 0x4C, 219, 7, 272 },
++ { 11, 4950000, 4950000, 0x6, 0x3, 0x1, 0x00, 0x07, 550, 0x42, 213, 7, 258 },
++ { 12, 5940000, 5940000, 0x7, 0x3, 0x1, 0x00, 0x07, 660, 0x42, 244, 8, 292 },
++};
++
++/* HDMI TX PLL tuning settings, pixel clock is input */
++static const struct hdmi_pll_tuning imx8qm_pll_table[] = {
++/* bin VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain pad only */
++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 300, 0x08D, 0, 0, 0 },
++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 320, 0x08E, 0, 0, 0 },
++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 325, 0x08E, 0, 0, 0 },
++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 330, 0x08E, 0, 0, 0 },
++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 340, 0x08F, 0, 0, 0 },
++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 360, 0x0A7, 0, 0, 0 },
++ { 0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 400, 0x0C5, 0, 0, 0 },
++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 300, 0x086, 0, 0, 0 },
++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 320, 0x087, 0, 0, 0 },
++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 325, 0x087, 0, 0, 0 },
++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 330, 0x104, 0, 0, 0 },
++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 340, 0x08B, 0, 0, 0 },
++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 360, 0x08D, 0, 0, 0 },
++ { 1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 400, 0x0A6, 0, 0, 0 },
++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 300, 0x04E, 0, 0, 0 },
++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 320, 0x04F, 0, 0, 0 },
++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 325, 0x04F, 0, 0, 0 },
++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 330, 0x085, 0, 0, 0 },
++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 340, 0x085, 0, 0, 0 },
++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 360, 0x086, 0, 0, 0 },
++ { 2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 400, 0x08B, 0, 0, 0 },
++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 300, 0x047, 0, 0, 0 },
++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 320, 0x04B, 0, 0, 0 },
++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 325, 0x04B, 0, 0, 0 },
++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 330, 0x04B, 0, 0, 0 },
++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 340, 0x04D, 0, 0, 0 },
++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 360, 0x04E, 0, 0, 0 },
++ { 3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 400, 0x085, 0, 0, 0 },
++ { 4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 375, 0x041, 0, 0, 0 },
++ { 4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 600, 0x08D, 0, 0, 0 },
++ { 4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 680, 0x0A6, 0, 0, 0 },
++ { 5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 450, 0x041, 0, 0, 0 },
++ { 5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 600, 0x087, 0, 0, 0 },
++ { 5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 680, 0x0A4, 0, 0, 0 },
++ { 6, 4500000, 5200000, 0x7, 0x1, 0x0, 0x04, 0x0D, 600, 0x04F, 0, 0, 0 },
++ { 6, 4500000, 5200000, 0x7, 0x1, 0x0, 0x04, 0x0D, 680, 0x086, 0, 0, 0 },
++ { 7, 5200000, 6000000, 0x7, 0x1, 0x0, 0x04, 0x0D, 600, 0x04D, 0, 0, 0 },
++ { 7, 5200000, 6000000, 0x7, 0x1, 0x0, 0x04, 0x0D, 680, 0x04F, 0, 0, 0 }
++};
++
++static void hdmi_arc_config(struct cdns_mhdp_device *mhdp)
++{
++ u16 txpu_calib_code;
++ u16 txpd_calib_code;
++ u16 txpu_adj_calib_code;
++ u16 txpd_adj_calib_code;
++ u16 prev_calib_code;
++ u16 new_calib_code;
++ u16 rdata;
++
++ /* Power ARC */
++ cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 0x0001);
++
++ prev_calib_code = cdns_phy_reg_read(mhdp, TX_DIG_CTRL_REG_2);
++ txpu_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPUCAL_CTRL);
++ txpd_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPDCAL_CTRL);
++ txpu_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPU_ADJ_CTRL);
++ txpd_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPD_ADJ_CTRL);
++
++ new_calib_code = ((txpu_calib_code + txpd_calib_code) / 2)
++ + txpu_adj_calib_code + txpd_adj_calib_code;
++
++ if (new_calib_code != prev_calib_code) {
++ rdata = cdns_phy_reg_read(mhdp, TX_ANA_CTRL_REG_1);
++ rdata &= 0xDFFF;
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata);
++ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, new_calib_code);
++ mdelay(10);
++ rdata |= 0x2000;
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata);
++ udelay(150);
++ }
++
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100);
++ udelay(100);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300);
++ udelay(100);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000);
++ udelay(100);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008);
++ udelay(100);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018);
++ udelay(100);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2098);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0010);
++ udelay(100);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x4001);
++ mdelay(5);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2198);
++ mdelay(5);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030D);
++ udelay(100);
++ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030F);
++}
++
++static void hdmi_phy_set_vswing(struct cdns_mhdp_device *mhdp)
++{
++ const u32 num_lanes = 4;
++ u32 k;
++
++ for (k = 0; k < num_lanes; k++) {
++ cdns_phy_reg_write(mhdp, (TX_DIAG_TX_DRV | (k << 9)), 0x7c0);
++ cdns_phy_reg_write(mhdp, (TX_TXCC_CPOST_MULT_00_0 | (k << 9)), 0x0);
++ cdns_phy_reg_write(mhdp, (TX_TXCC_CAL_SCLR_MULT_0 | (k << 9)), 0x120);
++ }
++}
++
++static int hdmi_feedback_factor(struct cdns_mhdp_device *mhdp)
++{
++ u32 feedback_factor;
++
++ switch (mhdp->video_info.color_fmt) {
++ case YCBCR_4_2_2:
++ feedback_factor = 1000;
++ break;
++ case YCBCR_4_2_0:
++ switch (mhdp->video_info.color_depth) {
++ case 8:
++ feedback_factor = 500;
++ break;
++ case 10:
++ feedback_factor = 625;
++ break;
++ case 12:
++ feedback_factor = 750;
++ break;
++ case 16:
++ feedback_factor = 1000;
++ break;
++ default:
++ //DRM_ERROR("Invalid ColorDepth\n");
++ printk(KERN_ALERT "Invalid ColorDepth %s %d \n",__FUNCTION__,__LINE__);
++ return 0;
++ }
++ break;
++ default:
++ /* Assume RGB/YUV444 */
++ switch (mhdp->video_info.color_depth) {
++ case 10:
++ feedback_factor = 1250;
++ break;
++ case 12:
++ feedback_factor = 1500;
++ break;
++ case 16:
++ feedback_factor = 2000;
++ break;
++ default:
++ feedback_factor = 1000;
++ }
++ }
++ return feedback_factor;
++}
++
++static int hdmi_phy_config(struct cdns_mhdp_device *mhdp,
++ const struct hdmi_ctrl *p_ctrl_table,
++ const struct hdmi_pll_tuning *p_pll_table,
++ char pclk_in)
++{
++ const u32 num_lanes = 4;
++ u32 val, i, k;
++
++ /* enable PHY isolation mode only for CMN */
++ cdns_phy_reg_write(mhdp, PHY_PMA_ISOLATION_CTRL, 0xD000);
++
++ /* set cmn_pll0_clk_datart1_div/cmn_pll0_clk_datart0_div dividers */
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_PLL_CTRL1);
++ val &= 0xFF00;
++ val |= 0x0012;
++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_PLL_CTRL1, val);
++
++ /* assert PHY reset from isolation register */
++ cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0000);
++ /* assert PMA CMN reset */
++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0000);
++
++ /* register XCVR_DIAG_BIDI_CTRL */
++ for (k = 0; k < num_lanes; k++)
++ cdns_phy_reg_write(mhdp, XCVR_DIAG_BIDI_CTRL | (k << 9), 0x00FF);
++
++ /* Describing Task phy_cfg_hdp */
++
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ val &= 0xFFF7;
++ val |= 0x0008;
++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
++
++ /* PHY Registers */
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ val &= 0xCFFF;
++ val |= p_ctrl_table->cmn_ref_clk_dig_div << 12;
++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
++
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
++ val &= 0x00FF;
++ val |= 0x1200;
++ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
++
++ /* Common control module control and diagnostic registers */
++ val = cdns_phy_reg_read(mhdp, CMN_CDIAG_REFCLK_CTRL);
++ val &= 0x8FFF;
++ val |= p_ctrl_table->ref_clk_divider_scaler << 12;
++ val |= 0x00C0;
++ cdns_phy_reg_write(mhdp, CMN_CDIAG_REFCLK_CTRL, val);
++
++ /* High speed clock used */
++ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL);
++ val &= 0xFF00;
++ val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 0;
++ val |= (p_ctrl_table->cmnda_hs_clk_1_sel >> 1) << 4;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val);
++
++ for (k = 0; k < num_lanes; k++) {
++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)));
++ val &= 0xCFFF;
++ val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 12;
++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val);
++ }
++
++ /* PLL 0 control state machine registers */
++ val = p_ctrl_table->vco_ring_select << 12;
++ cdns_phy_reg_write(mhdp, CMN_PLLSM0_USER_DEF_CTRL, val);
++
++ if (pclk_in == true)
++ val = 0x30A0;
++ else {
++ val = cdns_phy_reg_read(mhdp, CMN_PLL0_VCOCAL_START);
++ val &= 0xFE00;
++ val |= p_pll_table->vco_cal_code;
++ }
++ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_START, val);
++
++ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_INIT_TMR, 0x0064);
++ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_ITER_TMR, 0x000A);
++
++ /* Common functions control and diagnostics registers */
++ val = p_ctrl_table->cmnda_pll0_hs_sym_div_sel << 8;
++ val |= p_ctrl_table->cmnda_pll0_ip_div;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_INCLK_CTRL, val);
++
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_OVRD, 0x0000);
++
++ val = p_ctrl_table->cmnda_pll0_fb_div_high;
++ val |= (1 << 15);
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBH_OVRD, val);
++
++ val = p_ctrl_table->cmnda_pll0_fb_div_low;
++ val |= (1 << 15);
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBL_OVRD, val);
++
++ if (pclk_in == false) {
++ val = p_ctrl_table->cmnda_pll0_pxdiv_low;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVL, val);
++
++ val = p_ctrl_table->cmnda_pll0_pxdiv_high;
++ val |= (1 << 15);
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVH, val);
++ }
++
++ val = p_pll_table->volt_to_current_coarse;
++ val |= (p_pll_table->volt_to_current) << 4;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_V2I_TUNE, val);
++
++ val = p_pll_table->charge_pump_gain;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_CP_TUNE, val);
++
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_LF_PROG, 0x0008);
++
++ val = p_pll_table->pmos_ctrl;
++ val |= (p_pll_table->ndac_ctrl) << 8;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE1, val);
++
++ val = p_pll_table->ptat_ndac_ctrl;
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE2, val);
++
++ if (pclk_in == true)
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0022);
++ else
++ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0020);
++ cdns_phy_reg_write(mhdp, CMN_PSM_CLK_CTRL, 0x0016);
++
++ /* Transceiver control and diagnostic registers */
++ for (k = 0; k < num_lanes; k++) {
++ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)));
++ val &= 0xBFFF;
++ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val);
++ }
++
++ for (k = 0; k < num_lanes; k++) {
++ val = cdns_phy_reg_read(mhdp, (TX_DIAG_TX_CTRL | (k << 9)));
++ val &= 0xFF3F;
++ val |= (p_ctrl_table->hsclk_div_tx_sub_rate >> 1) << 6;
++ cdns_phy_reg_write(mhdp, (TX_DIAG_TX_CTRL | (k << 9)), val);
++ }
++
++ /*
++ * for single ended reference clock val |= 0x0030;
++ * for differential clock val |= 0x0000;
++ */
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ val &= 0xFF8F;
++ if (pclk_in == true)
++ val |= 0x0030;
++ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
++
++ /* for differential clock on the refclk_p and
++ * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 */
++ cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100);
++
++ /* Deassert PHY reset */
++ cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0001);
++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0003);
++
++ /* Power state machine registers */
++ for (k = 0; k < num_lanes; k++)
++ cdns_phy_reg_write(mhdp, XCVR_PSM_RCTRL | (k << 9), 0xFEFC);
++
++ /* Assert cmn_macro_pwr_en */
++ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0013);
++
++ /* wait for cmn_macro_pwr_en_ack */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_CMN_CTRL);
++ if (val & (1 << 5))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ //DRM_ERROR("PMA ouput macro power up failed\n");
++
++ printk(KERN_ALERT "PMA ouput macro power up failed %s %d \n",__FUNCTION__,__LINE__);
++ return false;
++ }
++
++ /* wait for cmn_ready */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
++ if (val & (1 << 0))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ //DRM_ERROR("PMA output ready failed\n");
++
++ printk(KERN_ALERT "PMA output ready failed %s %d \n",__FUNCTION__,__LINE__);
++ return false;
++ }
++
++ for (k = 0; k < num_lanes; k++) {
++ cdns_phy_reg_write(mhdp, TX_PSC_A0 | (k << 9), 0x6791);
++ cdns_phy_reg_write(mhdp, TX_PSC_A1 | (k << 9), 0x6790);
++ cdns_phy_reg_write(mhdp, TX_PSC_A2 | (k << 9), 0x0090);
++ cdns_phy_reg_write(mhdp, TX_PSC_A3 | (k << 9), 0x0090);
++
++ val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9));
++ val &= 0xFFBB;
++ cdns_phy_reg_write(mhdp, RX_PSC_CAL | (k << 9), val);
++
++ val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9));
++ val &= 0xFFBB;
++ cdns_phy_reg_write(mhdp, RX_PSC_A0 | (k << 9), val);
++ }
++ return true;
++}
++
++static int hdmi_phy_cfg_t28hpc(struct cdns_mhdp_device *mhdp,
++ struct drm_display_mode *mode)
++{
++ const struct hdmi_ctrl *p_ctrl_table;
++ const struct hdmi_pll_tuning *p_pll_table;
++ const u32 refclk_freq_khz = 27000;
++ const u8 pclk_in = false;
++ u32 pixel_freq = mode->clock;
++ u32 vco_freq, char_freq;
++ u32 div_total, feedback_factor;
++ u32 i, ret;
++
++ feedback_factor = hdmi_feedback_factor(mhdp);
++
++ char_freq = pixel_freq * feedback_factor / 1000;
++
++ dev_info(mhdp->dev, "Pixel clock: %d KHz, character clock: %d, bpc is %0d-bit.\n",
++ pixel_freq, char_freq, mhdp->video_info.color_depth);
++
++ /* Get right row from the ctrl_table table.
++ * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ column.
++ * Consider only the rows with FEEDBACK_FACTOR column matching feedback_factor. */
++ for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++) {
++ if (feedback_factor == imx8mq_ctrl_table[i].feedback_factor &&
++ pixel_freq == imx8mq_ctrl_table[i].pixel_clk_freq_min) {
++ p_ctrl_table = &imx8mq_ctrl_table[i];
++ break;
++ }
++ }
++ if (i == ARRAY_SIZE(imx8mq_ctrl_table)) {
++ dev_warn(mhdp->dev,"Pixel clk (%d KHz) not supported, color depth (%0d-bit)\n",
++ pixel_freq, mhdp->video_info.color_depth);
++ return 0;
++ }
++
++ div_total = p_ctrl_table->pll_fb_div_total;
++ vco_freq = refclk_freq_khz * div_total / p_ctrl_table->cmnda_pll0_ip_div;
++
++ /* Get right row from the imx8mq_pll_table table.
++ * Check if vco_freq_khz and feedback_div_total
++ * column matching with imx8mq_pll_table. */
++ for (i = 0; i < ARRAY_SIZE(imx8mq_pll_table); i++) {
++ if (vco_freq == imx8mq_pll_table[i].vco_freq_min &&
++ div_total == imx8mq_pll_table[i].feedback_div_total) {
++ p_pll_table = &imx8mq_pll_table[i];
++ break;
++ }
++ }
++ if (i == ARRAY_SIZE(imx8mq_pll_table)) {
++ dev_warn(mhdp->dev,"VCO (%d KHz) not supported\n", vco_freq);
++ return 0;
++ }
++ dev_info(mhdp->dev, "VCO frequency is %d KHz\n", vco_freq);
++
++ ret = hdmi_phy_config(mhdp, p_ctrl_table, p_pll_table, pclk_in);
++ if (ret == false)
++ return 0;
++
++ return char_freq;
++}
++
++static int hdmi_phy_cfg_ss28fdsoi(struct cdns_mhdp_device *mhdp,
++ struct drm_display_mode *mode)
++{
++ const struct hdmi_ctrl *p_ctrl_table;
++ const struct hdmi_pll_tuning *p_pll_table;
++ const u8 pclk_in = true;
++ u32 pixel_freq = mode->clock;
++ u32 vco_freq, char_freq;
++ u32 div_total, feedback_factor;
++ u32 ret, i;
++
++ feedback_factor = hdmi_feedback_factor(mhdp);
++
++ char_freq = pixel_freq * feedback_factor / 1000;
++
++ dev_info(mhdp->dev, "Pixel clock: %d KHz, character clock: %d, bpc is %0d-bit.\n",
++ pixel_freq, char_freq, mhdp->video_info.color_depth);
++
++ /* Get right row from the ctrl_table table.
++ * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ column.
++ * Consider only the rows with FEEDBACK_FACTOR column matching feedback_factor. */
++ for (i = 0; i < ARRAY_SIZE(imx8qm_ctrl_table); i++) {
++ if (feedback_factor == imx8qm_ctrl_table[i].feedback_factor &&
++ pixel_freq >= imx8qm_ctrl_table[i].pixel_clk_freq_min &&
++ pixel_freq <= imx8qm_ctrl_table[i].pixel_clk_freq_max) {
++ p_ctrl_table = &imx8qm_ctrl_table[i];
++ break;
++ }
++ }
++ if (i == ARRAY_SIZE(imx8qm_ctrl_table)) {
++ dev_warn(mhdp->dev,"Pixel clk (%d KHz) not supported, color depth (%0d-bit)\n",
++ pixel_freq, mhdp->video_info.color_depth);
++ return 0;
++ }
++
++ div_total = p_ctrl_table->pll_fb_div_total;
++ vco_freq = pixel_freq * div_total / p_ctrl_table->cmnda_pll0_ip_div;
++
++ /* Get right row from the imx8mq_pll_table table.
++ * Check if vco_freq_khz and feedback_div_total
++ * column matching with imx8mq_pll_table. */
++ for (i = 0; i < ARRAY_SIZE(imx8qm_pll_table); i++) {
++ if (vco_freq >= imx8qm_pll_table[i].vco_freq_min &&
++ vco_freq < imx8qm_pll_table[i].vco_freq_max &&
++ div_total == imx8qm_pll_table[i].feedback_div_total) {
++ p_pll_table = &imx8qm_pll_table[i];
++ break;
++ }
++ }
++ if (i == ARRAY_SIZE(imx8qm_pll_table)) {
++ dev_warn(mhdp->dev,"VCO (%d KHz) not supported\n", vco_freq);
++ return 0;
++ }
++
++ dev_info(mhdp->dev, "VCO frequency is %d KHz\n", vco_freq);
++
++ ret = hdmi_phy_config(mhdp, p_ctrl_table, p_pll_table, pclk_in);
++ if (ret == false)
++ return 0;
++
++ return char_freq;
++}
++
++static int hdmi_phy_power_up(struct cdns_mhdp_device *mhdp)
++{
++ u32 val, i;
++
++ /* set Power State to A2 */
++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004);
++
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1);
++ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1);
++
++ /* Wait for Power State A2 Ack */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
++ if (val & (1 << 6))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait A2 Ack failed\n");
++ return -1;
++ }
++
++ /* Power up ARC */
++ hdmi_arc_config(mhdp);
++
++ /* Configure PHY in A0 mode (PHY must be in the A0 power
++ * state in order to transmit data)
++ */
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++
++ // FIXME
++ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0101); //imx8mq
++ //cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0001);
++
++ /* Wait for Power State A0 Ack */
++ for (i = 0; i < 10; i++) {
++ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
++ if (val & (1 << 4))
++ break;
++ msleep(20);
++ }
++ if (i == 10) {
++ dev_err(mhdp->dev, "Wait A0 Ack failed\n");
++ return -1;
++ }
++
++ printk(KERN_ALERT "DEBUG: %s %d \n",__FUNCTION__,__LINE__);
++ return 0;
++}
++
++bool cdns_hdmi_phy_video_valid_imx8mq(struct cdns_mhdp_device *mhdp)
++{
++ u32 rate = mhdp->valid_mode->clock;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++)
++ if(rate == imx8mq_ctrl_table[i].pixel_clk_freq_min)
++ return true;
++ return false;
++}
++
++int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *mhdp)
++{
++ struct drm_display_mode *mode = &mhdp->mode;
++ int ret;
++
++ /* Check HDMI FW alive before HDMI PHY init */
++ ret = cdns_mhdp_check_alive(mhdp);
++ if (ret == false) {
++ dev_err(mhdp->dev, "NO HDMI FW running\n");
++ return -ENXIO;
++ }
++
++ /* Configure PHY */
++ mhdp->hdmi.char_rate = hdmi_phy_cfg_t28hpc(mhdp, mode);
++ if (mhdp->hdmi.char_rate == 0) {
++ dev_err(mhdp->dev, "failed to set phy pclock\n");
++ return -EINVAL;
++ }
++
++ ret = hdmi_phy_power_up(mhdp);
++ if (ret < 0)
++ return ret;
++
++ hdmi_phy_set_vswing(mhdp);
++
++ return true;
++}
++
++bool cdns_hdmi_phy_video_valid_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++ u32 rate = mhdp->valid_mode->clock;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(imx8qm_ctrl_table); i++)
++ if(rate >= imx8qm_ctrl_table[i].pixel_clk_freq_min &&
++ rate <= imx8qm_ctrl_table[i].pixel_clk_freq_max)
++ return true;
++ return false;
++}
++
++int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++ struct drm_display_mode *mode = &mhdp->mode;
++ int ret;
++
++ /* Check HDMI FW alive before HDMI PHY init */
++ ret = cdns_mhdp_check_alive(mhdp);
++ if (ret == false) {
++ dev_err(mhdp->dev, "NO HDMI FW running\n");
++ return -ENXIO;
++ }
++
++ /* Configure PHY */
++ mhdp->hdmi.char_rate = hdmi_phy_cfg_ss28fdsoi(mhdp, mode);
++ if (mhdp->hdmi.char_rate == 0) {
++ dev_err(mhdp->dev, "failed to set phy pclock\n");
++ return -EINVAL;
++ }
++
++ ret = hdmi_phy_power_up(mhdp);
++ if (ret < 0)
++ return ret;
++
++ hdmi_phy_set_vswing(mhdp);
++
++ return true;
++}
+diff --git a/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c b/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c
+new file mode 100644
+index 0000000..daee92c
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdn-mhdp-imxdrv.c
+@@ -0,0 +1,211 @@
++/*
++ * copyright (c) 2019 nxp semiconductor, inc.
++ *
++ * this program is free software; you can redistribute it and/or modify
++ * it under the terms of the gnu general public license version 2 as
++ * published by the free software foundation.
++ */
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/component.h>
++#include <drm/drm_of.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_encoder_slave.h>
++
++#include "cdns-mhdp-imx.h"
++#include "cdn-mhdp-phy.h"
++#include "imx-drm.h"
++
++static void cdns_mhdp_imx_encoder_disable(struct drm_encoder *encoder)
++{
++ // FIXME
++ struct drm_bridge* bridge = drm_bridge_chain_get_first_bridge(encoder);
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++
++ cdns_mhdp_plat_call(mhdp, plat_init);
++}
++
++static void cdns_mhdp_imx_encoder_enable(struct drm_encoder *encoder)
++{
++ // FIXME
++ struct drm_bridge* bridge = drm_bridge_chain_get_first_bridge(encoder);
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++
++ cdns_mhdp_plat_call(mhdp, plat_deinit);
++}
++
++static int cdns_mhdp_imx_encoder_atomic_check(struct drm_encoder *encoder,
++ struct drm_crtc_state *crtc_state,
++ struct drm_connector_state *conn_state)
++{
++ // FIXME
++ struct drm_bridge* bridge = drm_bridge_chain_get_first_bridge(encoder);
++ struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
++ struct cdns_mhdp_device *mhdp = bridge->driver_private;
++
++ if (mhdp->plat_data->video_format != 0)
++ imx_crtc_state->bus_format = mhdp->plat_data->video_format;
++
++ if (mhdp->force_mode_set)
++ crtc_state->mode_changed = true;
++
++ return 0;
++}
++
++static const struct drm_encoder_helper_funcs cdns_mhdp_imx_encoder_helper_funcs = {
++ .enable = cdns_mhdp_imx_encoder_enable,
++ .disable = cdns_mhdp_imx_encoder_disable,
++ .atomic_check = cdns_mhdp_imx_encoder_atomic_check,
++};
++
++static const struct drm_encoder_funcs cdns_mhdp_imx_encoder_funcs = {
++ .destroy = drm_encoder_cleanup,
++};
++
++static struct cdns_plat_data imx8mq_hdmi_drv_data = {
++ .plat_name = "imx8mq-hdmi",
++ .bind = cdns_hdmi_bind,
++ .unbind = cdns_hdmi_unbind,
++ .phy_set = cdns_hdmi_phy_set_imx8mq,
++ .phy_video_valid = cdns_hdmi_phy_video_valid_imx8mq,
++ .bus_type = BUS_TYPE_NORMAL_APB,
++};
++
++static struct cdns_plat_data imx8mq_dp_drv_data = {
++ .plat_name = "imx8mq-dp",
++ .bind = cdns_dp_bind,
++ .unbind = cdns_dp_unbind,
++ .phy_set = cdns_dp_phy_set_imx8mq,
++ .bus_type = BUS_TYPE_NORMAL_APB,
++};
++
++static const struct of_device_id cdns_mhdp_imx_dt_ids[] = {
++ { .compatible = "cdn,imx8mq-hdmi",
++ .data = &imx8mq_hdmi_drv_data
++ },
++ /*{ .compatible = "cdn,imx8mq-dp",
++ .data = &imx8mq_dp_drv_data
++ }*/
++ {},
++};
++MODULE_DEVICE_TABLE(of, cdns_mhdp_imx_dt_ids);
++
++static int cdns_mhdp_imx_bind(struct device *dev, struct device *master,
++ void *data)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ const struct cdns_plat_data *plat_data;
++ const struct of_device_id *match;
++ struct drm_device *drm = data;
++ struct drm_encoder *encoder;
++ struct imx_mhdp_device *imx_mhdp;
++ int ret;
++
++ if (!pdev->dev.of_node)
++ return -ENODEV;
++
++ imx_mhdp = devm_kzalloc(&pdev->dev, sizeof(*imx_mhdp), GFP_KERNEL);
++ if (!imx_mhdp)
++ return -ENOMEM;
++
++ match = of_match_node(cdns_mhdp_imx_dt_ids, pdev->dev.of_node);
++ plat_data = match->data;
++ encoder = &imx_mhdp->encoder;
++
++ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
++
++ ret = of_property_read_string(pdev->dev.of_node, "firmware-name",
++ &imx_mhdp->firmware_name);
++ /*
++ * If we failed to find the CRTC(s) which this encoder is
++ * supposed to be connected to, it's because the CRTC has
++ * not been registered yet. Defer probing, and hope that
++ * the required CRTC is added later.
++ */
++ if (encoder->possible_crtcs == 0) {
++ printk(KERN_ALERT "DEBUG: %s %d encoder crtcs not found.\n",__FUNCTION__,__LINE__);
++
++ return -EPROBE_DEFER;
++ }
++
++ drm_encoder_helper_add(encoder, &cdns_mhdp_imx_encoder_helper_funcs);
++ drm_encoder_init(drm, encoder, &cdns_mhdp_imx_encoder_funcs,
++ DRM_MODE_ENCODER_TMDS, NULL);
++
++
++ imx_mhdp->mhdp.plat_data = plat_data;
++ imx_mhdp->mhdp.dev = dev;
++ imx_mhdp->mhdp.bus_type = plat_data->bus_type;
++ ret = plat_data->bind(pdev, encoder, &imx_mhdp->mhdp);
++ /*
++ * If cdns_mhdp_bind() fails we'll never call cdns_mhdp_unbind(),
++ * which would have called the encoder cleanup. Do it manually.
++ */
++ if (ret < 0)
++ drm_encoder_cleanup(encoder);
++
++ return ret;
++}
++
++static void cdns_mhdp_imx_unbind(struct device *dev, struct device *master,
++ void *data)
++{
++ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
++
++ imx_mhdp->mhdp.plat_data->unbind(dev);
++}
++
++static const struct component_ops cdns_mhdp_imx_ops = {
++ .bind = cdns_mhdp_imx_bind,
++ .unbind = cdns_mhdp_imx_unbind,
++};
++
++static int cdns_mhdp_imx_suspend(struct device *dev)
++{
++ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
++
++ cdns_mhdp_plat_call(&imx_mhdp->mhdp, suspend);
++
++ return 0;
++}
++
++static int cdns_mhdp_imx_resume(struct device *dev)
++{
++ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
++
++ cdns_mhdp_plat_call(&imx_mhdp->mhdp, resume);
++
++ return 0;
++}
++
++static int cdns_mhdp_imx_probe(struct platform_device *pdev)
++{
++ return component_add(&pdev->dev, &cdns_mhdp_imx_ops);
++}
++
++static int cdns_mhdp_imx_remove(struct platform_device *pdev)
++{
++ component_del(&pdev->dev, &cdns_mhdp_imx_ops);
++
++ return 0;
++}
++
++static const struct dev_pm_ops cdns_mhdp_imx_pm_ops = {
++ SET_LATE_SYSTEM_SLEEP_PM_OPS(cdns_mhdp_imx_suspend, cdns_mhdp_imx_resume)
++};
++
++static struct platform_driver cdns_mhdp_imx_platform_driver = {
++ .probe = cdns_mhdp_imx_probe,
++ .remove = cdns_mhdp_imx_remove,
++ .driver = {
++ .name = "cdns-mhdp-imx",
++ .of_match_table = cdns_mhdp_imx_dt_ids,
++ .pm = &cdns_mhdp_imx_pm_ops,
++ },
++};
++
++module_platform_driver(cdns_mhdp_imx_platform_driver);
++
++MODULE_AUTHOR("Sandor YU <sandor.yu@nxp.com>");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:cdnhdmi-imx");
+diff --git a/drivers/gpu/drm/imx/cdn-mhdp-phy.h b/drivers/gpu/drm/imx/cdn-mhdp-phy.h
+new file mode 100644
+index 0000000..6ad1f37
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdn-mhdp-phy.h
+@@ -0,0 +1,155 @@
++/*
++ * Copyright (C) 2019 NXP Semiconductor, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#ifndef _CDN_DP_PHY_H
++#define _CDN_DP_PHY_H
++
++#include <drm/bridge/cdns-mhdp-common.h>
++
++#define CMN_SSM_BIAS_TMR 0x0022
++#define CMN_PLLSM0_PLLEN_TMR 0x0029
++#define CMN_PLLSM0_PLLPRE_TMR 0x002A
++#define CMN_PLLSM0_PLLVREF_TMR 0x002B
++#define CMN_PLLSM0_PLLLOCK_TMR 0x002C
++#define CMN_PLLSM0_USER_DEF_CTRL 0x002F
++#define CMN_PSM_CLK_CTRL 0x0061
++#define CMN_CDIAG_REFCLK_CTRL 0x0062
++#define CMN_PLL0_VCOCAL_START 0x0081
++#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084
++#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085
++#define CMN_PLL0_INTDIV 0x0094
++#define CMN_PLL0_FRACDIV 0x0095
++#define CMN_PLL0_HIGH_THR 0x0096
++#define CMN_PLL0_DSM_DIAG 0x0097
++#define CMN_PLL0_SS_CTRL1 0x0098
++#define CMN_PLL0_SS_CTRL2 0x0099
++#define CMN_ICAL_INIT_TMR 0x00C4
++#define CMN_ICAL_ITER_TMR 0x00C5
++#define CMN_RXCAL_INIT_TMR 0x00D4
++#define CMN_RXCAL_ITER_TMR 0x00D5
++#define CMN_TXPUCAL_CTRL 0x00E0
++#define CMN_TXPUCAL_INIT_TMR 0x00E4
++#define CMN_TXPUCAL_ITER_TMR 0x00E5
++#define CMN_TXPDCAL_CTRL 0x00F0
++#define CMN_TXPDCAL_INIT_TMR 0x00F4
++#define CMN_TXPDCAL_ITER_TMR 0x00F5
++#define CMN_ICAL_ADJ_INIT_TMR 0x0102
++#define CMN_ICAL_ADJ_ITER_TMR 0x0103
++#define CMN_RX_ADJ_INIT_TMR 0x0106
++#define CMN_RX_ADJ_ITER_TMR 0x0107
++#define CMN_TXPU_ADJ_CTRL 0x0108
++#define CMN_TXPU_ADJ_INIT_TMR 0x010A
++#define CMN_TXPU_ADJ_ITER_TMR 0x010B
++#define CMN_TXPD_ADJ_CTRL 0x010c
++#define CMN_TXPD_ADJ_INIT_TMR 0x010E
++#define CMN_TXPD_ADJ_ITER_TMR 0x010F
++#define CMN_DIAG_PLL0_FBH_OVRD 0x01C0
++#define CMN_DIAG_PLL0_FBL_OVRD 0x01C1
++#define CMN_DIAG_PLL0_OVRD 0x01C2
++#define CMN_DIAG_PLL0_TEST_MODE 0x01C4
++#define CMN_DIAG_PLL0_V2I_TUNE 0x01C5
++#define CMN_DIAG_PLL0_CP_TUNE 0x01C6
++#define CMN_DIAG_PLL0_LF_PROG 0x01C7
++#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01C8
++#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01C9
++#define CMN_DIAG_PLL0_INCLK_CTRL 0x01CA
++#define CMN_DIAG_PLL0_PXL_DIVH 0x01CB
++#define CMN_DIAG_PLL0_PXL_DIVL 0x01CC
++#define CMN_DIAG_HSCLK_SEL 0x01E0
++#define CMN_DIAG_PER_CAL_ADJ 0x01EC
++#define CMN_DIAG_CAL_CTRL 0x01ED
++#define CMN_DIAG_ACYA 0x01FF
++#define XCVR_PSM_RCTRL 0x4001
++#define XCVR_PSM_CAL_TMR 0x4002
++#define XCVR_PSM_A0IN_TMR 0x4003
++#define TX_TXCC_CAL_SCLR_MULT_0 0x4047
++#define TX_TXCC_CPOST_MULT_00_0 0x404C
++#define TX_TXCC_MGNFS_MULT_000_0 0x4050
++#define XCVR_DIAG_PLLDRC_CTRL 0x40E0
++#define XCVR_DIAG_PLLDRC_CTRL 0x40E0
++#define XCVR_DIAG_HSCLK_SEL 0x40E1
++#define XCVR_DIAG_BIDI_CTRL 0x40E8
++#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40F2
++#define XCVR_DIAG_LANE_FCM_EN_MGN 0x40F2
++#define TX_PSC_A0 0x4100
++#define TX_PSC_A1 0x4101
++#define TX_PSC_A2 0x4102
++#define TX_PSC_A3 0x4103
++#define TX_RCVDET_CTRL 0x4120
++#define TX_RCVDET_EN_TMR 0x4122
++#define TX_RCVDET_EN_TMR 0x4122
++#define TX_RCVDET_ST_TMR 0x4123
++#define TX_RCVDET_ST_TMR 0x4123
++#define TX_BIST_CTRL 0x4140
++#define TX_BIST_UDDWR 0x4141
++#define TX_DIAG_TX_CTRL 0x41E0
++#define TX_DIAG_TX_DRV 0x41E1
++#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7
++#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7
++#define XCVR_PSM_RCTRL_1 0x4201
++#define TX_TXCC_CAL_SCLR_MULT_1 0x4247
++#define TX_TXCC_CPOST_MULT_00_1 0x424C
++#define TX_TXCC_MGNFS_MULT_000_1 0x4250
++#define XCVR_DIAG_PLLDRC_CTRL_1 0x42E0
++#define XCVR_DIAG_HSCLK_SEL_1 0x42E1
++#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_1 0x42F2
++#define TX_RCVDET_EN_TMR_1 0x4322
++#define TX_RCVDET_ST_TMR_1 0x4323
++#define TX_DIAG_ACYA_0 0x41FF
++#define TX_DIAG_ACYA_1 0x43FF
++#define TX_DIAG_ACYA_2 0x45FF
++#define TX_DIAG_ACYA_3 0x47FF
++#define TX_ANA_CTRL_REG_1 0x5020
++#define TX_ANA_CTRL_REG_2 0x5021
++#define TXDA_COEFF_CALC 0x5022
++#define TX_DIG_CTRL_REG_1 0x5023
++#define TX_DIG_CTRL_REG_2 0x5024
++#define TXDA_CYA_AUXDA_CYA 0x5025
++#define TX_ANA_CTRL_REG_3 0x5026
++#define TX_ANA_CTRL_REG_4 0x5027
++#define TX_ANA_CTRL_REG_5 0x5029
++#define RX_PSC_A0 0x8000
++#define RX_PSC_CAL 0x8006
++#define PMA_LANE_CFG 0xC000
++#define PIPE_CMN_CTRL1 0xC001
++#define PIPE_CMN_CTRL2 0xC002
++#define PIPE_COM_LOCK_CFG1 0xC003
++#define PIPE_COM_LOCK_CFG2 0xC004
++#define PIPE_RCV_DET_INH 0xC005
++#define PHY_HDP_MODE_CTRL 0xC008
++#define PHY_HDP_CLK_CTL 0xC009
++#define STS 0xC00F
++#define PHY_ISO_CMN_CTRL 0xC010
++#define PHY_ISO_CMN_CTRL 0xC010
++#define PHY_HDP_TX_CTL_L0 0xC408
++#define PHY_DP_TX_CTL 0xC408
++#define PHY_HDP_TX_CTL_L1 0xC448
++#define PHY_HDP_TX_CTL_L2 0xC488
++#define PHY_HDP_TX_CTL_L3 0xC4C8
++#define PHY_PMA_CMN_CTRL1 0xC800
++#define PMA_CMN_CTRL1 0xC800
++#define PHY_PMA_ISO_CMN_CTRL 0xC810
++#define PHY_PMA_ISO_PLL_CTRL1 0xC812
++#define PHY_PMA_ISOLATION_CTRL 0xC81F
++#define PHY_ISOLATION_CTRL 0xC81F
++#define PHY_PMA_ISO_XCVR_CTRL 0xCC11
++#define PHY_PMA_ISO_LINK_MODE 0xCC12
++#define PHY_PMA_ISO_PWRST_CTRL 0xCC13
++#define PHY_PMA_ISO_TX_DATA_LO 0xCC14
++#define PHY_PMA_ISO_TX_DATA_HI 0xCC15
++#define PHY_PMA_ISO_RX_DATA_LO 0xCC16
++#define PHY_PMA_ISO_RX_DATA_HI 0xCC17
++
++int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *hdp);
++int cdns_dp_phy_set_imx8qm(struct cdns_mhdp_device *hdp);
++bool cdns_hdmi_phy_video_valid_imx8mq(struct cdns_mhdp_device *hdp);
++bool cdns_hdmi_phy_video_valid_imx8qm(struct cdns_mhdp_device *hdp);
++int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *hdp);
++int cdns_hdmi_phy_set_imx8qm(struct cdns_mhdp_device *hdp);
++#endif /* _CDNS_MHDP_PHY_H */
+diff --git a/drivers/gpu/drm/imx/cdns-mhdp-imx.h b/drivers/gpu/drm/imx/cdns-mhdp-imx.h
+new file mode 100644
+index 0000000..b95907d
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns-mhdp-imx.h
+@@ -0,0 +1,75 @@
++/*
++ * Cadence High-Definition Multimedia Interface (HDMI) driver
++ *
++ * Copyright (C) 2019 NXP Semiconductor, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ */
++#ifndef CDNS_MHDP_IMX_H_
++#define CDNS_MHDP_IMX_H_
++
++#include <drm/bridge/cdns-mhdp-common.h>
++#include <drm/drm_encoder_slave.h>
++
++
++struct imx_mhdp_device;
++
++struct imx_hdp_clks {
++ struct clk *av_pll;
++ struct clk *dig_pll;
++ struct clk *clk_ipg;
++ struct clk *clk_core;
++ struct clk *clk_pxl;
++ struct clk *clk_pxl_mux;
++ struct clk *clk_pxl_link;
++
++ struct clk *lpcg_hdp;
++ struct clk *lpcg_msi;
++ struct clk *lpcg_pxl;
++ struct clk *lpcg_vif;
++ struct clk *lpcg_lis;
++ struct clk *lpcg_apb;
++ struct clk *lpcg_apb_csr;
++ struct clk *lpcg_apb_ctrl;
++
++ struct clk *lpcg_i2s;
++ struct clk *clk_i2s_bypass;
++};
++
++struct imx_mhdp_device {
++ struct cdns_mhdp_device mhdp;
++ struct drm_encoder encoder;
++
++ struct mutex audio_mutex;
++ spinlock_t audio_lock;
++ bool connected;
++ bool active;
++ bool suspended;
++ struct imx_hdp_clks clks;
++ const struct firmware *fw;
++ const char *firmware_name;
++
++ int bus_type;
++
++ struct device *pd_mhdp_dev;
++ struct device *pd_pll0_dev;
++ struct device *pd_pll1_dev;
++ struct device_link *pd_mhdp_link;
++ struct device_link *pd_pll0_link;
++ struct device_link *pd_pll1_link;
++};
++
++void cdns_mhdp_plat_init_imx8qm(struct cdns_mhdp_device *mhdp);
++void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp);
++void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp);
++void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp);
++#endif /* CDNS_MHDP_IMX_H_ */
+diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.c b/drivers/gpu/drm/imx/dcss/dcss-dev.c
+index 3f5750c..6c12880 100644
+--- a/drivers/gpu/drm/imx/dcss/dcss-dev.c
++++ b/drivers/gpu/drm/imx/dcss/dcss-dev.c
+@@ -17,6 +17,11 @@
+
+ static void dcss_clocks_enable(struct dcss_dev *dcss)
+ {
++ if (dcss->hdmi_output) {
++ clk_prepare_enable(dcss->pll_phy_ref_clk);
++ clk_prepare_enable(dcss->pll_src_clk);
++ }
++
+ clk_prepare_enable(dcss->axi_clk);
+ clk_prepare_enable(dcss->apb_clk);
+ clk_prepare_enable(dcss->rtrm_clk);
+@@ -31,6 +36,11 @@ static void dcss_clocks_disable(struct dcss_dev *dcss)
+ clk_disable_unprepare(dcss->rtrm_clk);
+ clk_disable_unprepare(dcss->apb_clk);
+ clk_disable_unprepare(dcss->axi_clk);
++
++ if (dcss->hdmi_output) {
++ clk_disable_unprepare(dcss->pll_src_clk);
++ clk_disable_unprepare(dcss->pll_phy_ref_clk);
++ }
+ }
+
+ static void dcss_disable_dtg_and_ss_cb(void *data)
+@@ -139,6 +149,8 @@ static int dcss_clks_init(struct dcss_dev *dcss)
+ {"pix", &dcss->pix_clk},
+ {"rtrm", &dcss->rtrm_clk},
+ {"dtrc", &dcss->dtrc_clk},
++ {"pll_src", &dcss->pll_src_clk},
++ {"pll_phy_ref", &dcss->pll_phy_ref_clk},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(clks); i++) {
+@@ -160,6 +172,8 @@ static void dcss_clks_release(struct dcss_dev *dcss)
+ devm_clk_put(dcss->dev, dcss->pix_clk);
+ devm_clk_put(dcss->dev, dcss->axi_clk);
+ devm_clk_put(dcss->dev, dcss->apb_clk);
++ devm_clk_put(dcss->dev, dcss->pll_src_clk);
++ devm_clk_put(dcss->dev, dcss->pll_phy_ref_clk);
+ }
+
+ struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output)
+@@ -257,7 +271,8 @@ int dcss_dev_suspend(struct device *dev)
+ struct dcss_kms_dev *kms = container_of(ddev, struct dcss_kms_dev, base);
+ int ret;
+
+- drm_bridge_connector_disable_hpd(kms->connector);
++ if (!dcss_drv_is_componentized(dev))
++ drm_bridge_connector_disable_hpd(kms->connector);
+
+ drm_mode_config_helper_suspend(ddev);
+
+@@ -292,7 +307,8 @@ int dcss_dev_resume(struct device *dev)
+
+ drm_mode_config_helper_resume(ddev);
+
+- drm_bridge_connector_enable_hpd(kms->connector);
++ if (!dcss_drv_is_componentized(dev))
++ drm_bridge_connector_enable_hpd(kms->connector);
+
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.h b/drivers/gpu/drm/imx/dcss/dcss-dev.h
+index 1e58227..083edf6 100644
+--- a/drivers/gpu/drm/imx/dcss/dcss-dev.h
++++ b/drivers/gpu/drm/imx/dcss/dcss-dev.h
+@@ -93,6 +93,7 @@ struct dcss_dev {
+
+ struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev);
+ struct drm_device *dcss_drv_dev_to_drm(struct device *dev);
++bool dcss_drv_is_componentized(struct device *dev);
+ struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output);
+ void dcss_dev_destroy(struct dcss_dev *dcss);
+ int dcss_dev_runtime_suspend(struct device *dev);
+diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c
+index 1c70f70..d6960da 100644
+--- a/drivers/gpu/drm/imx/dcss/dcss-drv.c
++++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c
+@@ -7,6 +7,7 @@
+ #include <linux/kernel.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
++#include <linux/component.h>
+ #include <drm/drm_module.h>
+ #include <drm/drm_of.h>
+
+@@ -16,6 +17,8 @@
+ struct dcss_drv {
+ struct dcss_dev *dcss;
+ struct dcss_kms_dev *kms;
++
++ bool is_componentized;
+ };
+
+ struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev)
+@@ -32,30 +35,24 @@ struct drm_device *dcss_drv_dev_to_drm(struct device *dev)
+ return mdrv ? &mdrv->kms->base : NULL;
+ }
+
+-static int dcss_drv_platform_probe(struct platform_device *pdev)
++bool dcss_drv_is_componentized(struct device *dev)
++{
++ struct dcss_drv *mdrv = dev_get_drvdata(dev);
++ return mdrv->is_componentized;
++}
++
++static int dcss_drv_init(struct device *dev, bool componentized)
+ {
+- struct device *dev = &pdev->dev;
+- struct device_node *remote;
+ struct dcss_drv *mdrv;
+ int err = 0;
+- bool hdmi_output = true;
+-
+- if (!dev->of_node)
+- return -ENODEV;
+-
+- remote = of_graph_get_remote_node(dev->of_node, 0, 0);
+- if (!remote)
+- return -ENODEV;
+-
+- hdmi_output = !of_device_is_compatible(remote, "fsl,imx8mq-nwl-dsi");
+-
+- of_node_put(remote);
+
+ mdrv = kzalloc(sizeof(*mdrv), GFP_KERNEL);
+ if (!mdrv)
+ return -ENOMEM;
+
+- mdrv->dcss = dcss_dev_create(dev, hdmi_output);
++ mdrv->is_componentized = componentized;
++
++ mdrv->dcss = dcss_dev_create(dev, componentized);
+ if (IS_ERR(mdrv->dcss)) {
+ err = PTR_ERR(mdrv->dcss);
+ goto err;
+@@ -63,7 +60,7 @@ static int dcss_drv_platform_probe(struct platform_device *pdev)
+
+ dev_set_drvdata(dev, mdrv);
+
+- mdrv->kms = dcss_kms_attach(mdrv->dcss);
++ mdrv->kms = dcss_kms_attach(mdrv->dcss, componentized);
+ if (IS_ERR(mdrv->kms)) {
+ err = PTR_ERR(mdrv->kms);
+ goto dcss_shutoff;
+@@ -81,19 +78,73 @@ static int dcss_drv_platform_probe(struct platform_device *pdev)
+ return err;
+ }
+
++static void dcss_drv_deinit(struct device *dev, bool componentized)
++{
++ struct dcss_drv *mdrv = dev_get_drvdata(dev);
++
++ if (!mdrv)
++ return;
++
++ dcss_kms_detach(mdrv->kms, componentized);
++ dcss_dev_destroy(mdrv->dcss);
++
++ dev_set_drvdata(dev, NULL);
++
++ kfree(mdrv);
++}
++
++static int dcss_drv_bind(struct device *dev)
++{
++ return dcss_drv_init(dev, true);
++}
++
++static void dcss_drv_unbind(struct device *dev)
++{
++ return dcss_drv_deinit(dev, true);
++}
++
++static const struct component_master_ops dcss_master_ops = {
++ .bind = dcss_drv_bind,
++ .unbind = dcss_drv_unbind,
++};
++
++static int compare_of(struct device *dev, void *data)
++{
++ return dev->of_node == data;
++}
++
++static int dcss_drv_platform_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct component_match *match = NULL;
++ struct device_node *remote;
++
++ if (!dev->of_node)
++ return -ENODEV;
++
++ remote = of_graph_get_remote_node(dev->of_node, 0, 0);
++ if (!remote)
++ return -ENODEV;
++
++ if (of_device_is_compatible(remote, "fsl,imx8mq-nwl-dsi")) {
++ of_node_put(remote);
++ return dcss_drv_init(dev, false);
++ }
++
++ drm_of_component_match_add(dev, &match, compare_of, remote);
++ of_node_put(remote);
++
++ return component_master_add_with_match(dev, &dcss_master_ops, match);
++}
++
+ static int dcss_drv_platform_remove(struct platform_device *pdev)
+ {
+ struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev);
+
+- if (!mdrv)
+- return 0;
+-
+- dcss_kms_detach(mdrv->kms);
+- dcss_dev_destroy(mdrv->dcss);
+-
+- dev_set_drvdata(&pdev->dev, NULL);
+-
+- kfree(mdrv);
++ if (mdrv->is_componentized)
++ component_master_del(&pdev->dev, &dcss_master_ops);
++ else
++ dcss_drv_deinit(&pdev->dev, false);
+
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/imx/dcss/dcss-dtg.c b/drivers/gpu/drm/imx/dcss/dcss-dtg.c
+index 30de005..c7a688b 100644
+--- a/drivers/gpu/drm/imx/dcss/dcss-dtg.c
++++ b/drivers/gpu/drm/imx/dcss/dcss-dtg.c
+@@ -83,6 +83,7 @@ struct dcss_dtg {
+ u32 ctx_id;
+
+ bool in_use;
++ bool hdmi_output;
+
+ u32 dis_ulc_x;
+ u32 dis_ulc_y;
+@@ -93,6 +94,9 @@ struct dcss_dtg {
+
+ int ctxld_kick_irq;
+ bool ctxld_kick_irq_en;
++
++ struct clk *pll_src_clk;
++ struct clk *pll_phy_ref_clk;
+ };
+
+ static void dcss_dtg_write(struct dcss_dtg *dtg, u32 val, u32 ofs)
+@@ -159,6 +163,7 @@ int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
+ dcss->dtg = dtg;
+ dtg->dev = dcss->dev;
+ dtg->ctxld = dcss->ctxld;
++ dtg->hdmi_output = dcss->hdmi_output;
+
+ dtg->base_reg = ioremap(dtg_base, SZ_4K);
+ if (!dtg->base_reg) {
+@@ -170,6 +175,9 @@ int dcss_dtg_init(struct dcss_dev *dcss, unsigned long dtg_base)
+ dtg->base_ofs = dtg_base;
+ dtg->ctx_id = CTX_DB;
+
++ dtg->pll_src_clk = dcss->pll_src_clk;
++ dtg->pll_phy_ref_clk = dcss->pll_phy_ref_clk;
++
+ dtg->alpha = 255;
+
+ dtg->control_status |= OVL_DATA_MODE | BLENDER_VIDEO_ALPHA_SEL |
+@@ -220,8 +228,22 @@ void dcss_dtg_sync_set(struct dcss_dtg *dtg, struct videomode *vm)
+ dis_lrc_y = vm->vsync_len + vm->vfront_porch + vm->vback_porch +
+ vm->vactive - 1;
+
++ if (dtg->hdmi_output) {
++ int err;
++
++ clk_disable_unprepare(dtg->pll_src_clk);
++ err = clk_set_parent(dtg->pll_src_clk, dtg->pll_phy_ref_clk);
++ if (err < 0)
++ dev_warn(dtg->dev, "clk_set_parent() returned %d", err);
++ clk_prepare_enable(dtg->pll_src_clk);
++ }
++
+ clk_disable_unprepare(dcss->pix_clk);
+- clk_set_rate(dcss->pix_clk, vm->pixelclock);
++ if (dtg->hdmi_output) {
++ clk_set_rate(dcss->pix_clk, vm->pixelclock);
++ } else {
++ clk_set_rate(dcss->pix_clk, (vm->pixelclock * 700)/1000);
++ }
+ clk_prepare_enable(dcss->pix_clk);
+
+ actual_clk = clk_get_rate(dcss->pix_clk);
+@@ -406,4 +428,3 @@ bool dcss_dtg_vblank_irq_valid(struct dcss_dtg *dtg)
+ {
+ return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
+ }
+-
+diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c
+index b4f82eb..9bedf12 100644
+--- a/drivers/gpu/drm/imx/dcss/dcss-kms.c
++++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c
+@@ -13,6 +13,7 @@
+ #include <drm/drm_of.h>
+ #include <drm/drm_probe_helper.h>
+ #include <drm/drm_vblank.h>
++#include <linux/component.h>
+
+ #include "dcss-dev.h"
+ #include "dcss-kms.h"
+@@ -69,6 +70,7 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms)
+ struct drm_crtc *crtc = (struct drm_crtc *)&kms->crtc;
+ struct drm_panel *panel;
+ struct drm_bridge *bridge;
++ struct drm_connector_list_iter iter;
+ int ret;
+
+ ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0,
+@@ -91,23 +93,23 @@ static int dcss_kms_bridge_connector_init(struct dcss_kms_dev *kms)
+ return ret;
+ }
+
+- ret = drm_bridge_attach(encoder, bridge, NULL,
+- DRM_BRIDGE_ATTACH_NO_CONNECTOR);
++ ret = drm_bridge_attach(encoder, bridge, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+- kms->connector = drm_bridge_connector_init(ddev, encoder);
+- if (IS_ERR(kms->connector)) {
+- dev_err(ddev->dev, "Unable to create bridge connector.\n");
+- return PTR_ERR(kms->connector);
+- }
++ /*
++ * FIXME: This hack to look up the connector is copied from mxsfb.
++ */
++ drm_connector_list_iter_begin(ddev, &iter);
++ kms->connector = drm_connector_list_iter_next(&iter);
++ drm_connector_list_iter_end(&iter);
+
+ drm_connector_attach_encoder(kms->connector, encoder);
+
+ return 0;
+ }
+
+-struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss)
++struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss, bool componentized)
+ {
+ struct dcss_kms_dev *kms;
+ struct drm_device *drm;
+@@ -130,18 +132,29 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss)
+ if (ret)
+ goto cleanup_mode_config;
+
+- ret = dcss_kms_bridge_connector_init(kms);
+- if (ret)
+- goto cleanup_mode_config;
++ if (!componentized) {
++ ret = dcss_kms_bridge_connector_init(kms);
++ if (ret)
++ goto cleanup_mode_config;
++ }
+
+ ret = dcss_crtc_init(crtc, drm);
+ if (ret)
+ goto cleanup_mode_config;
+
++ if (componentized) {
++ ret = component_bind_all(dcss->dev, kms);
++ if (ret)
++ goto cleanup_crtc;
++ }
++
+ drm_mode_config_reset(drm);
+
+ drm_kms_helper_poll_init(drm);
+
++ if (!componentized)
++ drm_bridge_connector_enable_hpd(kms->connector);
++
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto cleanup_crtc;
+@@ -151,7 +164,8 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss)
+ return kms;
+
+ cleanup_crtc:
+- drm_bridge_connector_disable_hpd(kms->connector);
++ if (!componentized)
++ drm_bridge_connector_disable_hpd(kms->connector);
+ drm_kms_helper_poll_fini(drm);
+ dcss_crtc_deinit(crtc, drm);
+
+@@ -162,16 +176,20 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss)
+ return ERR_PTR(ret);
+ }
+
+-void dcss_kms_detach(struct dcss_kms_dev *kms)
++void dcss_kms_detach(struct dcss_kms_dev *kms, bool componentized)
+ {
+ struct drm_device *drm = &kms->base;
++ struct dcss_dev *dcss = drm->dev_private;
+
+ drm_dev_unregister(drm);
+- drm_bridge_connector_disable_hpd(kms->connector);
++ if (!componentized)
++ drm_bridge_connector_disable_hpd(kms->connector);
+ drm_kms_helper_poll_fini(drm);
+ drm_atomic_helper_shutdown(drm);
+ drm_crtc_vblank_off(&kms->crtc.base);
+ drm_mode_config_cleanup(drm);
+ dcss_crtc_deinit(&kms->crtc, drm);
++ if (componentized)
++ component_unbind_all(dcss->dev, drm);
+ drm->dev_private = NULL;
+ }
+diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.h b/drivers/gpu/drm/imx/dcss/dcss-kms.h
+index dfe5dd9..c3e0d06 100644
+--- a/drivers/gpu/drm/imx/dcss/dcss-kms.h
++++ b/drivers/gpu/drm/imx/dcss/dcss-kms.h
+@@ -32,8 +32,8 @@ struct dcss_kms_dev {
+ struct drm_connector *connector;
+ };
+
+-struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss);
+-void dcss_kms_detach(struct dcss_kms_dev *kms);
++struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss, bool componetized);
++void dcss_kms_detach(struct dcss_kms_dev *kms, bool componetized);
+ int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm);
+ void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm);
+ struct dcss_plane *dcss_plane_init(struct drm_device *drm,
+diff --git a/include/drm/bridge/cdns-mhdp-cbs.h b/include/drm/bridge/cdns-mhdp-cbs.h
+new file mode 100644
+index 0000000..ed67e18
+--- /dev/null
++++ b/include/drm/bridge/cdns-mhdp-cbs.h
+@@ -0,0 +1,29 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Cadence MHDP DP bridge callbacks.
++ *
++ * Copyright: 2018 Cadence Design Systems, Inc.
++ *
++ * Author: Piotr Sroka <piotrs@cadence.com>
++ */
++
++#ifndef CDNS_MHDP_CBS_H
++#define CDNS_MHDP_CBS_H
++
++#include <drm/drm_bridge.h>
++
++struct cdns_mhdp_mst_cbs_funcs {
++ struct drm_encoder *(*create_mst_encoder)(void *priv_data,
++ struct drm_bridge *bridge);
++ void (*destroy_mst_encoder)(void *priv_data, struct drm_bridge *bridge);
++};
++
++struct cdns_mhdp_mst_cbs {
++ struct cdns_mhdp_mst_cbs_funcs funcs;
++ void *priv_data;
++};
++
++int mhdp_bridge_attach_mst_cbs(struct drm_bridge *bridge,
++ struct cdns_mhdp_mst_cbs *cbs);
++
++#endif
+diff --git a/include/drm/bridge/cdns-mhdp-common.h b/include/drm/bridge/cdns-mhdp-common.h
+new file mode 100644
+index 0000000..77a99db
+--- /dev/null
++++ b/include/drm/bridge/cdns-mhdp-common.h
+@@ -0,0 +1,812 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
++ * Author: Chris Zhong <zyw@rock-chips.com>
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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 CDNS_MHDP_COMMON_H_
++#define CDNS_MHDP_COMMON_H_
++
++#include <drm/bridge/cdns-mhdp-cbs.h>
++#include <drm/drm_bridge.h>
++#include <drm/drm_connector.h>
++#include <drm/display/drm_dp_helper.h>
++#include <drm/display/drm_dp_mst_helper.h>
++#include <media/cec.h>
++#include <linux/bitops.h>
++
++#define ADDR_IMEM 0x10000
++#define ADDR_DMEM 0x20000
++#define ADDR_PHY_AFE 0x80000
++
++/* APB CFG addr */
++#define APB_CTRL 0
++#define XT_INT_CTRL 0x04
++#define MAILBOX_FULL_ADDR 0x08
++#define MAILBOX_EMPTY_ADDR 0x0c
++#define MAILBOX0_WR_DATA 0x10
++#define MAILBOX0_RD_DATA 0x14
++#define KEEP_ALIVE 0x18
++#define VER_L 0x1c
++#define VER_H 0x20
++#define VER_LIB_L_ADDR 0x24
++#define VER_LIB_H_ADDR 0x28
++#define SW_DEBUG_L 0x2c
++#define SW_DEBUG_H 0x30
++#define MAILBOX_INT_MASK 0x34
++#define MAILBOX_INT_STATUS 0x38
++#define SW_CLK_L 0x3c
++#define SW_CLK_H 0x40
++#define SW_EVENTS0 0x44
++#define SW_EVENTS1 0x48
++#define SW_EVENTS2 0x4c
++#define SW_EVENTS3 0x50
++#define XT_OCD_CTRL 0x60
++#define APB_INT_MASK 0x6c
++#define APB_STATUS_MASK 0x70
++
++/* audio decoder addr */
++#define AUDIO_SRC_CNTL 0x30000
++#define AUDIO_SRC_CNFG 0x30004
++#define COM_CH_STTS_BITS 0x30008
++#define STTS_BIT_CH(x) (0x3000c + ((x) << 2))
++#define SPDIF_CTRL_ADDR 0x3004c
++#define SPDIF_CH1_CS_3100_ADDR 0x30050
++#define SPDIF_CH1_CS_6332_ADDR 0x30054
++#define SPDIF_CH1_CS_9564_ADDR 0x30058
++#define SPDIF_CH1_CS_12796_ADDR 0x3005c
++#define SPDIF_CH1_CS_159128_ADDR 0x30060
++#define SPDIF_CH1_CS_191160_ADDR 0x30064
++#define SPDIF_CH2_CS_3100_ADDR 0x30068
++#define SPDIF_CH2_CS_6332_ADDR 0x3006c
++#define SPDIF_CH2_CS_9564_ADDR 0x30070
++#define SPDIF_CH2_CS_12796_ADDR 0x30074
++#define SPDIF_CH2_CS_159128_ADDR 0x30078
++#define SPDIF_CH2_CS_191160_ADDR 0x3007c
++#define SMPL2PKT_CNTL 0x30080
++#define SMPL2PKT_CNFG 0x30084
++#define FIFO_CNTL 0x30088
++#define FIFO_STTS 0x3008c
++
++/* source pif addr */
++#define SOURCE_PIF_WR_ADDR 0x30800
++#define SOURCE_PIF_WR_REQ 0x30804
++#define SOURCE_PIF_RD_ADDR 0x30808
++#define SOURCE_PIF_RD_REQ 0x3080c
++#define SOURCE_PIF_DATA_WR 0x30810
++#define SOURCE_PIF_DATA_RD 0x30814
++#define SOURCE_PIF_FIFO1_FLUSH 0x30818
++#define SOURCE_PIF_FIFO2_FLUSH 0x3081c
++#define SOURCE_PIF_STATUS 0x30820
++#define SOURCE_PIF_INTERRUPT_SOURCE 0x30824
++#define SOURCE_PIF_INTERRUPT_MASK 0x30828
++#define SOURCE_PIF_PKT_ALLOC_REG 0x3082c
++#define SOURCE_PIF_PKT_ALLOC_WR_EN 0x30830
++#define SOURCE_PIF_SW_RESET 0x30834
++
++/* bellow registers need access by mailbox */
++/* source phy comp */
++#define PHY_DATA_SEL 0x0818
++#define LANES_CONFIG 0x0814
++
++/* source car addr */
++#define SOURCE_HDTX_CAR 0x0900
++#define SOURCE_DPTX_CAR 0x0904
++#define SOURCE_PHY_CAR 0x0908
++#define SOURCE_CEC_CAR 0x090c
++#define SOURCE_CBUS_CAR 0x0910
++#define SOURCE_PKT_CAR 0x0918
++#define SOURCE_AIF_CAR 0x091c
++#define SOURCE_CIPHER_CAR 0x0920
++#define SOURCE_CRYPTO_CAR 0x0924
++
++/* mhdp tx_top_comp */
++#define SCHEDULER_H_SIZE 0x1000
++#define SCHEDULER_V_SIZE 0x1004
++#define HDTX_SIGNAL_FRONT_WIDTH 0x100c
++#define HDTX_SIGNAL_SYNC_WIDTH 0x1010
++#define HDTX_SIGNAL_BACK_WIDTH 0x1014
++#define HDTX_CONTROLLER 0x1018
++#define HDTX_HPD 0x1020
++#define HDTX_CLOCK_REG_0 0x1024
++#define HDTX_CLOCK_REG_1 0x1028
++
++/* clock meters addr */
++#define CM_CTRL 0x0a00
++#define CM_I2S_CTRL 0x0a04
++#define CM_SPDIF_CTRL 0x0a08
++#define CM_VID_CTRL 0x0a0c
++#define CM_LANE_CTRL 0x0a10
++#define I2S_NM_STABLE 0x0a14
++#define I2S_NCTS_STABLE 0x0a18
++#define SPDIF_NM_STABLE 0x0a1c
++#define SPDIF_NCTS_STABLE 0x0a20
++#define NMVID_MEAS_STABLE 0x0a24
++#define I2S_MEAS 0x0a40
++#define SPDIF_MEAS 0x0a80
++#define NMVID_MEAS 0x0ac0
++
++/* source vif addr */
++#define BND_HSYNC2VSYNC 0x0b00
++#define HSYNC2VSYNC_F1_L1 0x0b04
++#define HSYNC2VSYNC_F2_L1 0x0b08
++#define HSYNC2VSYNC_STATUS 0x0b0c
++#define HSYNC2VSYNC_POL_CTRL 0x0b10
++
++/* dptx phy addr */
++#define DP_TX_PHY_CONFIG_REG 0x2000
++#define DP_TX_PHY_SW_RESET 0x2004
++#define DP_TX_PHY_SCRAMBLER_SEED 0x2008
++#define DP_TX_PHY_TRAINING_01_04 0x200c
++#define DP_TX_PHY_TRAINING_05_08 0x2010
++#define DP_TX_PHY_TRAINING_09_10 0x2014
++#define TEST_COR 0x23fc
++
++/* dptx hpd addr */
++#define HPD_IRQ_DET_MIN_TIMER 0x2100
++#define HPD_IRQ_DET_MAX_TIMER 0x2104
++#define HPD_UNPLGED_DET_MIN_TIMER 0x2108
++#define HPD_STABLE_TIMER 0x210c
++#define HPD_FILTER_TIMER 0x2110
++#define HPD_EVENT_MASK 0x211c
++#define HPD_EVENT_DET 0x2120
++
++/* dpyx framer addr */
++#define DP_FRAMER_GLOBAL_CONFIG 0x2200
++#define DP_SW_RESET 0x2204
++#define DP_FRAMER_TU 0x2208
++#define DP_FRAMER_PXL_REPR 0x220c
++#define DP_FRAMER_SP 0x2210
++#define AUDIO_PACK_CONTROL 0x2214
++#define DP_VC_TABLE(x) (0x2218 + ((x) << 2))
++#define DP_VB_ID 0x2258
++#define DP_MTPH_LVP_CONTROL 0x225c
++#define DP_MTPH_SYMBOL_VALUES 0x2260
++#define DP_MTPH_ECF_CONTROL 0x2264
++#define DP_MTPH_ACT_CONTROL 0x2268
++#define DP_MTPH_STATUS 0x226c
++#define DP_INTERRUPT_SOURCE 0x2270
++#define DP_INTERRUPT_MASK 0x2274
++#define DP_FRONT_BACK_PORCH 0x2278
++#define DP_BYTE_COUNT 0x227c
++
++/* dptx stream addr */
++#define MSA_HORIZONTAL_0 0x2280
++#define MSA_HORIZONTAL_1 0x2284
++#define MSA_VERTICAL_0 0x2288
++#define MSA_VERTICAL_1 0x228c
++#define MSA_MISC 0x2290
++#define STREAM_CONFIG 0x2294
++#define AUDIO_PACK_STATUS 0x2298
++#define VIF_STATUS 0x229c
++#define PCK_STUFF_STATUS_0 0x22a0
++#define PCK_STUFF_STATUS_1 0x22a4
++#define INFO_PACK_STATUS 0x22a8
++#define RATE_GOVERNOR_STATUS 0x22ac
++#define DP_HORIZONTAL 0x22b0
++#define DP_VERTICAL_0 0x22b4
++#define DP_VERTICAL_1 0x22b8
++#define DP_BLOCK_SDP 0x22bc
++
++/* dptx glbl addr */
++#define DPTX_LANE_EN 0x2300
++#define DPTX_ENHNCD 0x2304
++#define DPTX_INT_MASK 0x2308
++#define DPTX_INT_STATUS 0x230c
++
++/* dp aux addr */
++#define DP_AUX_HOST_CONTROL 0x2800
++#define DP_AUX_INTERRUPT_SOURCE 0x2804
++#define DP_AUX_INTERRUPT_MASK 0x2808
++#define DP_AUX_SWAP_INVERSION_CONTROL 0x280c
++#define DP_AUX_SEND_NACK_TRANSACTION 0x2810
++#define DP_AUX_CLEAR_RX 0x2814
++#define DP_AUX_CLEAR_TX 0x2818
++#define DP_AUX_TIMER_STOP 0x281c
++#define DP_AUX_TIMER_CLEAR 0x2820
++#define DP_AUX_RESET_SW 0x2824
++#define DP_AUX_DIVIDE_2M 0x2828
++#define DP_AUX_TX_PREACHARGE_LENGTH 0x282c
++#define DP_AUX_FREQUENCY_1M_MAX 0x2830
++#define DP_AUX_FREQUENCY_1M_MIN 0x2834
++#define DP_AUX_RX_PRE_MIN 0x2838
++#define DP_AUX_RX_PRE_MAX 0x283c
++#define DP_AUX_TIMER_PRESET 0x2840
++#define DP_AUX_NACK_FORMAT 0x2844
++#define DP_AUX_TX_DATA 0x2848
++#define DP_AUX_RX_DATA 0x284c
++#define DP_AUX_TX_STATUS 0x2850
++#define DP_AUX_RX_STATUS 0x2854
++#define DP_AUX_RX_CYCLE_COUNTER 0x2858
++#define DP_AUX_MAIN_STATES 0x285c
++#define DP_AUX_MAIN_TIMER 0x2860
++#define DP_AUX_AFE_OUT 0x2864
++
++/* crypto addr */
++#define CRYPTO_HDCP_REVISION 0x5800
++#define HDCP_CRYPTO_CONFIG 0x5804
++#define CRYPTO_INTERRUPT_SOURCE 0x5808
++#define CRYPTO_INTERRUPT_MASK 0x580c
++#define CRYPTO22_CONFIG 0x5818
++#define CRYPTO22_STATUS 0x581c
++#define SHA_256_DATA_IN 0x583c
++#define SHA_256_DATA_OUT_(x) (0x5850 + ((x) << 2))
++#define AES_32_KEY_(x) (0x5870 + ((x) << 2))
++#define AES_32_DATA_IN 0x5880
++#define AES_32_DATA_OUT_(x) (0x5884 + ((x) << 2))
++#define CRYPTO14_CONFIG 0x58a0
++#define CRYPTO14_STATUS 0x58a4
++#define CRYPTO14_PRNM_OUT 0x58a8
++#define CRYPTO14_KM_0 0x58ac
++#define CRYPTO14_KM_1 0x58b0
++#define CRYPTO14_AN_0 0x58b4
++#define CRYPTO14_AN_1 0x58b8
++#define CRYPTO14_YOUR_KSV_0 0x58bc
++#define CRYPTO14_YOUR_KSV_1 0x58c0
++#define CRYPTO14_MI_0 0x58c4
++#define CRYPTO14_MI_1 0x58c8
++#define CRYPTO14_TI_0 0x58cc
++#define CRYPTO14_KI_0 0x58d0
++#define CRYPTO14_KI_1 0x58d4
++#define CRYPTO14_BLOCKS_NUM 0x58d8
++#define CRYPTO14_KEY_MEM_DATA_0 0x58dc
++#define CRYPTO14_KEY_MEM_DATA_1 0x58e0
++#define CRYPTO14_SHA1_MSG_DATA 0x58e4
++#define CRYPTO14_SHA1_V_VALUE_(x) (0x58e8 + ((x) << 2))
++#define TRNG_CTRL 0x58fc
++#define TRNG_DATA_RDY 0x5900
++#define TRNG_DATA 0x5904
++
++/* cipher addr */
++#define HDCP_REVISION 0x60000
++#define INTERRUPT_SOURCE 0x60004
++#define INTERRUPT_MASK 0x60008
++#define HDCP_CIPHER_CONFIG 0x6000c
++#define AES_128_KEY_0 0x60010
++#define AES_128_KEY_1 0x60014
++#define AES_128_KEY_2 0x60018
++#define AES_128_KEY_3 0x6001c
++#define AES_128_RANDOM_0 0x60020
++#define AES_128_RANDOM_1 0x60024
++#define CIPHER14_KM_0 0x60028
++#define CIPHER14_KM_1 0x6002c
++#define CIPHER14_STATUS 0x60030
++#define CIPHER14_RI_PJ_STATUS 0x60034
++#define CIPHER_MODE 0x60038
++#define CIPHER14_AN_0 0x6003c
++#define CIPHER14_AN_1 0x60040
++#define CIPHER22_AUTH 0x60044
++#define CIPHER14_R0_DP_STATUS 0x60048
++#define CIPHER14_BOOTSTRAP 0x6004c
++
++#define DPTX_FRMR_DATA_CLK_RSTN_EN BIT(11)
++#define DPTX_FRMR_DATA_CLK_EN BIT(10)
++#define DPTX_PHY_DATA_RSTN_EN BIT(9)
++#define DPTX_PHY_DATA_CLK_EN BIT(8)
++#define DPTX_PHY_CHAR_RSTN_EN BIT(7)
++#define DPTX_PHY_CHAR_CLK_EN BIT(6)
++#define SOURCE_AUX_SYS_CLK_RSTN_EN BIT(5)
++#define SOURCE_AUX_SYS_CLK_EN BIT(4)
++#define DPTX_SYS_CLK_RSTN_EN BIT(3)
++#define DPTX_SYS_CLK_EN BIT(2)
++#define CFG_DPTX_VIF_CLK_RSTN_EN BIT(1)
++#define CFG_DPTX_VIF_CLK_EN BIT(0)
++
++#define SOURCE_PHY_RSTN_EN BIT(1)
++#define SOURCE_PHY_CLK_EN BIT(0)
++
++#define SOURCE_PKT_SYS_RSTN_EN BIT(3)
++#define SOURCE_PKT_SYS_CLK_EN BIT(2)
++#define SOURCE_PKT_DATA_RSTN_EN BIT(1)
++#define SOURCE_PKT_DATA_CLK_EN BIT(0)
++
++#define SPDIF_CDR_CLK_RSTN_EN BIT(5)
++#define SPDIF_CDR_CLK_EN BIT(4)
++#define SOURCE_AIF_SYS_RSTN_EN BIT(3)
++#define SOURCE_AIF_SYS_CLK_EN BIT(2)
++#define SOURCE_AIF_CLK_RSTN_EN BIT(1)
++#define SOURCE_AIF_CLK_EN BIT(0)
++
++#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN BIT(3)
++#define SOURCE_CIPHER_SYS_CLK_EN BIT(2)
++#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN BIT(1)
++#define SOURCE_CIPHER_CHAR_CLK_EN BIT(0)
++
++#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN BIT(1)
++#define SOURCE_CRYPTO_SYS_CLK_EN BIT(0)
++
++#define APB_IRAM_PATH BIT(2)
++#define APB_DRAM_PATH BIT(1)
++#define APB_XT_RESET BIT(0)
++
++#define MAILBOX_INT_MASK_BIT BIT(1)
++#define PIF_INT_MASK_BIT BIT(0)
++#define ALL_INT_MASK 3
++
++/* mailbox */
++#define MB_OPCODE_ID 0
++#define MB_MODULE_ID 1
++#define MB_SIZE_MSB_ID 2
++#define MB_SIZE_LSB_ID 3
++#define MB_DATA_ID 4
++
++#define MB_MODULE_ID_DP_TX 0x01
++#define MB_MODULE_ID_HDMI_TX 0x03
++#define MB_MODULE_ID_HDCP_TX 0x07
++#define MB_MODULE_ID_HDCP_RX 0x08
++#define MB_MODULE_ID_HDCP_GENERAL 0x09
++#define MB_MODULE_ID_GENERAL 0x0A
++
++/* general opcode */
++#define GENERAL_MAIN_CONTROL 0x01
++#define GENERAL_TEST_ECHO 0x02
++#define GENERAL_BUS_SETTINGS 0x03
++#define GENERAL_TEST_ACCESS 0x04
++#define GENERAL_WRITE_REGISTER 0x05
++#define GENERAL_WRITE_FIELD 0x06
++#define GENERAL_READ_REGISTER 0x07
++#define GENERAL_GET_HPD_STATE 0x11
++
++/* DPTX opcode */
++#define DPTX_SET_POWER_MNG 0x00
++#define DPTX_SET_HOST_CAPABILITIES 0x01
++#define DPTX_GET_EDID 0x02
++#define DPTX_READ_DPCD 0x03
++#define DPTX_WRITE_DPCD 0x04
++#define DPTX_ENABLE_EVENT 0x05
++#define DPTX_WRITE_REGISTER 0x06
++#define DPTX_READ_REGISTER 0x07
++#define DPTX_WRITE_FIELD 0x08
++#define DPTX_TRAINING_CONTROL 0x09
++#define DPTX_READ_EVENT 0x0a
++#define DPTX_READ_LINK_STAT 0x0b
++#define DPTX_SET_VIDEO 0x0c
++#define DPTX_SET_AUDIO 0x0d
++#define DPTX_GET_LAST_AUX_STAUS 0x0e
++#define DPTX_SET_LINK_BREAK_POINT 0x0f
++#define DPTX_FORCE_LANES 0x10
++#define DPTX_HPD_STATE 0x11
++#define DPTX_ADJUST_LT 0x12
++
++/* HDMI TX opcode */
++#define HDMI_TX_READ 0x00
++#define HDMI_TX_WRITE 0x01
++#define HDMI_TX_UPDATE_READ 0x02
++#define HDMI_TX_EDID 0x03
++#define HDMI_TX_EVENTS 0x04
++#define HDMI_TX_HPD_STATUS 0x05
++#define HDMI_TX_DEBUG_ECHO 0xAA
++#define HDMI_TX_TEST 0xBB
++#define HDMI_TX_EDID_INTERNAL 0xF0
++
++#define FW_STANDBY 0
++#define FW_ACTIVE 1
++
++#define MHDP_EVENT_ENABLE_HPD BIT(0)
++#define MHDP_EVENT_ENABLE_TRAINING BIT(1)
++
++#define LINK_TRAINING_NOT_ACTIVE 0
++#define LINK_TRAINING_RUN 1
++#define LINK_TRAINING_RESTART 2
++
++#define CONTROL_VIDEO_IDLE 0
++#define CONTROL_VIDEO_VALID 1
++
++#define TU_CNT_RST_EN BIT(15)
++#define VIF_BYPASS_INTERLACE BIT(13)
++#define INTERLACE_FMT_DET BIT(12)
++#define INTERLACE_DTCT_WIN 0x20
++
++#define DP_FRAMER_SP_INTERLACE_EN BIT(2)
++#define DP_FRAMER_SP_HSP BIT(1)
++#define DP_FRAMER_SP_VSP BIT(0)
++
++/* capability */
++#define AUX_HOST_INVERT 3
++#define FAST_LT_SUPPORT 1
++#define FAST_LT_NOT_SUPPORT 0
++#define LANE_MAPPING_NORMAL 0x1b
++#define LANE_MAPPING_FLIPPED 0xe4
++#define ENHANCED 1
++#define SCRAMBLER_EN BIT(4)
++
++#define FULL_LT_STARTED BIT(0)
++#define FASE_LT_STARTED BIT(1)
++#define CLK_RECOVERY_FINISHED BIT(2)
++#define EQ_PHASE_FINISHED BIT(3)
++#define FASE_LT_START_FINISHED BIT(4)
++#define CLK_RECOVERY_FAILED BIT(5)
++#define EQ_PHASE_FAILED BIT(6)
++#define FASE_LT_FAILED BIT(7)
++
++#define DPTX_HPD_EVENT BIT(0)
++#define DPTX_TRAINING_EVENT BIT(1)
++#define HDCP_TX_STATUS_EVENT BIT(4)
++#define HDCP2_TX_IS_KM_STORED_EVENT BIT(5)
++#define HDCP2_TX_STORE_KM_EVENT BIT(6)
++#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT BIT(7)
++
++#define TU_SIZE 30
++#define CDNS_DP_MAX_LINK_RATE 540000
++
++#define F_HDMI_ENCODING(x) (((x) & ((1 << 2) - 1)) << 16)
++#define F_VIF_DATA_WIDTH(x) (((x) & ((1 << 2) - 1)) << 2)
++#define F_HDMI_MODE(x) (((x) & ((1 << 2) - 1)) << 0)
++#define F_GCP_EN(x) (((x) & ((1 << 1) - 1)) << 12)
++#define F_DATA_EN(x) (((x) & ((1 << 1) - 1)) << 15)
++#define F_HDMI2_PREAMBLE_EN(x) (((x) & ((1 << 1) - 1)) << 18)
++#define F_PIC_3D(x) (((x) & ((1 << 4) - 1)) << 7)
++#define F_BCH_EN(x) (((x) & ((1 << 1) - 1)) << 11)
++#define F_SOURCE_PHY_MHDP_SEL(x) (((x) & ((1 << 2) - 1)) << 3)
++#define F_HPD_VALID_WIDTH(x) (((x) & ((1 << 12) - 1)) << 0)
++#define F_HPD_GLITCH_WIDTH(x) (((x) & ((1 << 8) - 1)) << 12)
++#define F_HDMI2_CTRL_IL_MODE(x) (((x) & ((1 << 1) - 1)) << 19)
++#define F_SOURCE_PHY_LANE0_SWAP(x) (((x) & ((1 << 2) - 1)) << 0)
++#define F_SOURCE_PHY_LANE1_SWAP(x) (((x) & ((1 << 2) - 1)) << 2)
++#define F_SOURCE_PHY_LANE2_SWAP(x) (((x) & ((1 << 2) - 1)) << 4)
++#define F_SOURCE_PHY_LANE3_SWAP(x) (((x) & ((1 << 2) - 1)) << 6)
++#define F_SOURCE_PHY_COMB_BYPASS(x) (((x) & ((1 << 1) - 1)) << 21)
++#define F_SOURCE_PHY_20_10(x) (((x) & ((1 << 1) - 1)) << 22)
++#define F_PKT_ALLOC_ADDRESS(x) (((x) & ((1 << 4) - 1)) << 0)
++#define F_ACTIVE_IDLE_TYPE(x) (((x) & ((1 << 1) - 1)) << 17)
++#define F_FIFO1_FLUSH(x) (((x) & ((1 << 1) - 1)) << 0)
++#define F_PKT_ALLOC_WR_EN(x) (((x) & ((1 << 1) - 1)) << 0)
++#define F_DATA_WR(x) (x)
++#define F_WR_ADDR(x) (((x) & ((1 << 4) - 1)) << 0)
++#define F_HOST_WR(x) (((x) & ((1 << 1) - 1)) << 0)
++#define F_TYPE_VALID(x) (((x) & ((1 << 1) - 1)) << 16)
++#define F_PACKET_TYPE(x) (((x) & ((1 << 8) - 1)) << 8)
++
++/* audio */
++#define AUDIO_PACK_EN BIT(8)
++#define SAMPLING_FREQ(x) (((x) & 0xf) << 16)
++#define ORIGINAL_SAMP_FREQ(x) (((x) & 0xf) << 24)
++#define SYNC_WR_TO_CH_ZERO BIT(1)
++#define I2S_DEC_START BIT(1)
++#define AUDIO_SW_RST BIT(0)
++#define SMPL2PKT_EN BIT(1)
++#define MAX_NUM_CH(x) (((x) & 0x1f) - 1)
++#define NUM_OF_I2S_PORTS(x) ((((x) / 2 - 1) & 0x3) << 5)
++#define AUDIO_TYPE_LPCM (2 << 7)
++#define CFG_SUB_PCKT_NUM(x) ((((x) - 1) & 0x7) << 11)
++#define AUDIO_CH_NUM(x) ((((x) - 1) & 0x1f) << 2)
++#define TRANS_SMPL_WIDTH_16 0
++#define TRANS_SMPL_WIDTH_24 BIT(11)
++#define TRANS_SMPL_WIDTH_32 (2 << 11)
++#define I2S_DEC_PORT_EN(x) (((x) & 0xf) << 17)
++#define SPDIF_ENABLE BIT(21)
++#define SPDIF_AVG_SEL BIT(20)
++#define SPDIF_JITTER_BYPASS BIT(19)
++#define SPDIF_FIFO_MID_RANGE(x) (((x) & 0xff) << 11)
++#define SPDIF_JITTER_THRSH(x) (((x) & 0xff) << 3)
++#define SPDIF_JITTER_AVG_WIN(x) ((x) & 0x7)
++
++/* Reference cycles when using lane clock as reference */
++#define LANE_REF_CYC 0x8000
++
++#define HOTPLUG_DEBOUNCE_MS 200
++
++#define IRQ_IN 0
++#define IRQ_OUT 1
++#define IRQ_NUM 2
++
++#define cdns_mhdp_plat_call(mhdp, operation) \
++ (!(mhdp) ? -ENODEV : (((mhdp)->plat_data && (mhdp)->plat_data->operation) ? \
++ (mhdp)->plat_data->operation(mhdp) : ENOIOCTLCMD))
++
++/* bus access type */
++enum {
++ BUS_TYPE_NORMAL_APB = 0,
++ BUS_TYPE_NORMAL_SAPB = 1,
++ BUS_TYPE_LOW4K_APB = 2,
++ BUS_TYPE_LOW4K_SAPB = 3,
++};
++
++enum voltage_swing_level {
++ VOLTAGE_LEVEL_0,
++ VOLTAGE_LEVEL_1,
++ VOLTAGE_LEVEL_2,
++ VOLTAGE_LEVEL_3,
++};
++
++enum pre_emphasis_level {
++ PRE_EMPHASIS_LEVEL_0,
++ PRE_EMPHASIS_LEVEL_1,
++ PRE_EMPHASIS_LEVEL_2,
++ PRE_EMPHASIS_LEVEL_3,
++};
++
++enum pattern_set {
++ PTS1 = BIT(0),
++ PTS2 = BIT(1),
++ PTS3 = BIT(2),
++ PTS4 = BIT(3),
++ DP_NONE = BIT(4)
++};
++
++enum vic_color_depth {
++ BCS_6 = 0x1,
++ BCS_8 = 0x2,
++ BCS_10 = 0x4,
++ BCS_12 = 0x8,
++ BCS_16 = 0x10,
++};
++
++enum vic_bt_type {
++ BT_601 = 0x0,
++ BT_709 = 0x1,
++};
++
++enum audio_format {
++ AFMT_I2S = 0,
++ AFMT_SPDIF_INT = 1,
++ AFMT_SPDIF_EXT = 2,
++ AFMT_UNUSED,
++};
++
++enum {
++ MODE_DVI,
++ MODE_HDMI_1_4,
++ MODE_HDMI_2_0,
++};
++
++struct audio_info {
++ enum audio_format format;
++ int sample_rate;
++ int channels;
++ int sample_width;
++ int connector_type;
++};
++
++enum vic_pxl_encoding_format {
++ PXL_RGB = 0x1,
++ YCBCR_4_4_4 = 0x2,
++ YCBCR_4_2_2 = 0x4,
++ YCBCR_4_2_0 = 0x8,
++ Y_ONLY = 0x10,
++};
++
++struct video_info {
++ bool h_sync_polarity;
++ bool v_sync_polarity;
++ bool interlaced;
++ int color_depth;
++ enum vic_pxl_encoding_format color_fmt;
++};
++
++struct cdns_mhdp_host {
++ unsigned int link_rate;
++ u8 lanes_cnt;
++ u8 volt_swing;
++ u8 pre_emphasis;
++ u8 pattern_supp;
++ u8 fast_link;
++ u8 lane_mapping;
++ u8 enhanced;
++};
++
++struct cdns_mhdp_sink {
++ unsigned int link_rate;
++ u8 lanes_cnt;
++ u8 pattern_supp;
++ u8 fast_link;
++ u8 enhanced;
++};
++
++struct cdns_mhdp_bridge;
++struct cdns_mhdp_connector;
++
++struct cdns_mhdp_bridge {
++ struct cdns_mhdp_device *mhdp;
++ struct drm_bridge base;
++ int pbn;
++ int8_t stream_id;
++ struct cdns_mhdp_connector *connector;
++ bool is_active;
++};
++
++struct cdns_mhdp_connector {
++ struct drm_connector base;
++ bool is_mst_connector;
++ struct drm_dp_mst_port *port;
++ struct cdns_mhdp_bridge *bridge;
++};
++
++struct cdns_mhdp_cec {
++ struct cec_adapter *adap;
++ struct device *dev;
++ struct mutex lock;
++
++ struct cec_msg msg;
++ struct task_struct *cec_worker;
++};
++
++struct cdns_plat_data {
++ /* Vendor PHY support */
++ int (*bind)(struct platform_device *pdev,
++ struct drm_encoder *encoder,
++ struct cdns_mhdp_device *mhdp);
++ void (*unbind)(struct device *dev);
++
++ void (*plat_init)(struct cdns_mhdp_device *mhdp);
++ void (*plat_deinit)(struct cdns_mhdp_device *mhdp);
++
++ int (*phy_set)(struct cdns_mhdp_device *mhdp);
++ bool (*phy_video_valid)(struct cdns_mhdp_device *mhdp);
++ int (*firmware_init)(struct cdns_mhdp_device *mhdp);
++ void (*pclk_rate)(struct cdns_mhdp_device *mhdp);
++
++ int (*suspend)(struct cdns_mhdp_device *mhdp);
++ int (*resume)(struct cdns_mhdp_device *mhdp);
++
++ int (*power_on)(struct cdns_mhdp_device *mhdp);
++ int (*power_off)(struct cdns_mhdp_device *mhdp);
++
++ int bus_type;
++ int video_format;
++ char is_dp;
++ char *plat_name;
++};
++
++struct drm_dp_link {
++ unsigned char revision;
++ unsigned int rate;
++ unsigned int num_lanes;
++ unsigned long capabilities;
++};
++
++struct cdns_mhdp_device {
++ void __iomem *regs_base;
++ void __iomem *regs_sec;
++
++ int bus_type;
++
++ struct device *dev;
++
++ struct cdns_mhdp_connector connector;
++ struct clk *spdif_clk;
++ struct reset_control *spdif_rst;
++
++ struct platform_device *audio_pdev;
++ struct audio_info audio_info;
++
++ struct cdns_mhdp_bridge bridge;
++ struct phy *phy;
++
++ struct video_info video_info;
++ struct drm_display_mode mode;
++ const struct drm_display_mode *valid_mode;
++ unsigned int fw_version;
++
++ struct drm_dp_mst_topology_mgr mst_mgr;
++ struct delayed_work hotplug_work;
++
++ u32 lane_mapping;
++ bool link_up;
++ bool power_up;
++ bool plugged;
++ bool force_mode_set;
++ bool is_hpd;
++ bool is_ls1028a;
++ struct mutex lock;
++ struct mutex iolock;
++
++ int irq[IRQ_NUM];
++
++ union {
++ struct _dp_data {
++ struct drm_dp_link link;
++ struct drm_dp_aux aux;
++ struct cdns_mhdp_host host;
++ struct cdns_mhdp_sink sink;
++ struct cdns_mhdp_mst_cbs cbs;
++ bool is_mst;
++ bool can_mst;
++ } dp;
++ struct _hdmi_data {
++ struct cdns_mhdp_cec cec;
++ u32 char_rate;
++ u32 hdmi_type;
++ } hdmi;
++ };
++ const struct cdns_plat_data *plat_data;
++
++};
++
++u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset);
++void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset);
++void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp);
++void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk);
++u32 cdns_mhdp_get_fw_clk(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem,
++ u32 i_size, const u32 *d_mem, u32 d_size);
++int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable);
++int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip);
++int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp);
++u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_get_hpd_status(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value);
++int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp,
++ u32 addr, u8 *data, u16 len);
++int cdns_mhdp_get_edid_block(void *mhdp, u8 *edid,
++ unsigned int block, size_t length);
++int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp);
++int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active);
++int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp);
++
++/* Audio */
++int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio);
++int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable);
++int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp,
++ struct audio_info *audio);
++int cdns_mhdp_register_audio_driver(struct device *dev);
++void cdns_mhdp_unregister_audio_driver(struct device *dev);
++
++int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr);
++int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val);
++int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr,
++ u8 start_bit, u8 bits_no, u32 val);
++int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp, u8 nlanes,
++ u16 udelay, u8 *lanes_data,
++ u8 *dpcd);
++
++int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp);
++u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr);
++int cdns_phy_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val);
++int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id,
++ u8 opcode, u16 size, u8 *message);
++int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp,
++ u8 *buff, u16 buff_size);
++int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp,
++ u8 module_id, u8 opcode,
++ u16 req_size);
++int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp);
++
++void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp,
++ u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type);
++int cdns_hdmi_get_edid_block(void *data, u8 *edid, u32 block, size_t length);
++int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data);
++int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value);
++int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, int protocol, u32 char_rate);
++int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp, struct drm_display_mode *mode,
++ struct video_info *video_info);
++int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp);
++int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp);
++
++bool cdns_mhdp_check_alive(struct cdns_mhdp_device *mhdp);
++
++/* HDMI */
++int cdns_hdmi_probe(struct platform_device *pdev,
++ struct cdns_mhdp_device *mhdp);
++void cdns_hdmi_remove(struct platform_device *pdev);
++void cdns_hdmi_unbind(struct device *dev);
++int cdns_hdmi_bind(struct platform_device *pdev,
++ struct drm_encoder *encoder, struct cdns_mhdp_device *mhdp);
++void cdns_hdmi_set_sample_rate(struct cdns_mhdp_device *mhdp, unsigned int rate);
++void cdns_hdmi_audio_enable(struct cdns_mhdp_device *mhdp);
++void cdns_hdmi_audio_disable(struct cdns_mhdp_device *mhdp);
++/* DP */
++int cdns_dp_probe(struct platform_device *pdev,
++ struct cdns_mhdp_device *mhdp);
++void cdns_dp_remove(struct platform_device *pdev);
++void cdns_dp_unbind(struct device *dev);
++int cdns_dp_bind(struct platform_device *pdev,
++ struct drm_encoder *encoder, struct cdns_mhdp_device *mhdp);
++
++/* CEC */
++#ifdef CONFIG_DRM_CDNS_HDMI_CEC
++int cdns_mhdp_register_cec_driver(struct device *dev);
++int cdns_mhdp_unregister_cec_driver(struct device *dev);
++#endif
++
++#endif /* CDNS_MHDP_COMMON_H_ */
+--
+2.39.0
+