diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f6f0126a1aa21e5141917680210fa722b2b78741..8f157ee3772f3d9ec7b0ba3b899985a8711c8949 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -20,7 +20,7 @@ stages:
   before_script: |
     set -x
     apt-get update -o Acquire::AllowReleaseInfoChange=true -o quiet::ReleaseInfoChange=true
-    apt-get --no-install-recommends -y install devscripts reprepro sbuild debhelper debian-keyring quilt pristine-tar rsync git python3-debian faketime python3-jinja2 uidmap debian-archive-keyring curl
+    apt-get --no-install-recommends -y install devscripts reprepro sbuild debhelper debian-keyring quilt pristine-tar rsync git python3-debian faketime python3-jinja2 python3-dacite uidmap debian-archive-keyring curl
     if [ ! -e /proc/sys/fs/binfmt_misc/status ]; then
       mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
     fi
diff --git a/linux/build.sh b/linux/build.sh
index 1ea1993cca0f37b811d8bbdd2431598752d20636..49da5e0755a6251c251396002ebaffc97e18a884 100755
--- a/linux/build.sh
+++ b/linux/build.sh
@@ -47,33 +47,70 @@ faketime=
 if command -v faketime >/dev/null && [ -n "${SOURCE_DATE_EPOCH:+x}" ]; then
 	faketime="faketime @$SOURCE_DATE_EPOCH"
 fi
-env --chdir=linux TZ=UTC $faketime dch --local "+$VERSUFFIX$datesuffix" "apply mnt reform patch"
+
+DEB_VERSION="$(dpkg-parsechangelog --show-field Version --file linux/debian/changelog)"
+DEB_VERSION_UPSTREAM="$(echo "$DEB_VERSION" | sed -e 's/-[^-]*$//')"
+KVER=$(echo "$DEB_VERSION" | sed 's/\([0-9]\+\.[0-9]\+\).*/\1/')
+if dpkg --compare-versions "$KVER" ge "6.7"; then
+	oldversion="$(dpkg-parsechangelog --show-field=Version --file linux/debian/changelog)"
+	newversion="$(echo "$oldversion" | sed 's/\([0-9.]\+\)\(.*\)/\1-reform2\2/')"
+	env --chdir=linux TZ=UTC $faketime dch --newversion "$newversion+$VERSUFFIX$datesuffix" "apply mnt reform patch"
+	mv "linux_$DEB_VERSION_UPSTREAM.orig.tar.xz" "linux_$DEB_VERSION_UPSTREAM-reform2.orig.tar.xz"
+else
+	env --chdir=linux TZ=UTC $faketime dch --local "+$VERSUFFIX$datesuffix" "apply mnt reform patch"
+fi
 env --chdir=linux TZ=UTC $faketime dch --force-distribution --distribution="$OURSUITE" --release ""
 
 env --chdir=linux patch -p1 < packaging.diff
 
-mkdir -p linux/debian/config.local/arm64/none
-cat << END >> linux/debian/config.local/defines
+# new toml config format since 6.7
+if dpkg --compare-versions "$KVER" ge "6.7"; then
+	mkdir -p linux/debian/config.local/arm64
+	cat << END >> linux/debian/config.local/arm64/defines.toml
+[[flavour]]
+name = 'arm64'
+[flavour.defs]
+is_default = true
+[flavour.packages]
+installer = false
+docs = false
+
+[[featureset]]
+name = 'none'
+
+[[featureset.flavour]]
+name = 'arm64'
+
+[build]
+enable_signed = false
+END
+else
+	mkdir -p linux/debian/config.local/arm64/none
+	cat << END >> linux/debian/config.local/defines
 [packages]
 installer: false
 docs: false
 END
-cat << END >> linux/debian/config.local/arm64/defines
+	cat << END >> linux/debian/config.local/arm64/defines
 [base]
 featuresets: none
 
 [build]
 signed-code: false
 END
-cat << END >> linux/debian/config.local/arm64/none/defines
+	cat << END >> linux/debian/config.local/arm64/none/defines
 [base]
 flavours: arm64
 END
+fi
 
 KVER=$(dpkg-parsechangelog --show-field Version --file linux/debian/changelog | sed 's/\([0-9]\+\.[0-9]\+\).*/\1/')
 
 # the abiname field was dropped in 6.6 with commit 3282bf29846a0c47a8e01c60c038d29ad17c573d
-if dpkg --compare-versions "$KVER" ge "6.6"; then
+# since 6.7 there is the new toml config format
+if dpkg --compare-versions "$KVER" ge "6.7"; then
+	: # nothing to do
+elif test "$KVER" = 6.6; then
 	# apply https://salsa.debian.org/kernel-team/linux/-/merge_requests/957
 	cat << END | env --chdir=linux patch -p1
 --- a/debian/bin/gencontrol.py
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/2ghz/0001-imx8mp-2ghz-clk.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/2ghz/0001-imx8mp-2ghz-clk.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6bf30fff71a10760eef859ea294e1cab44d20b57
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/2ghz/0001-imx8mp-2ghz-clk.patch
@@ -0,0 +1,29 @@
+From 02e8c29cac92595796138bf756006635e404b507 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 22:19:22 +0200
+Subject: [PATCH 1/2] imx8mp-2ghz-clk
+
+---
+ drivers/clk/imx/clk-pll14xx.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/clk/imx/clk-pll14xx.c
++++ b/drivers/clk/imx/clk-pll14xx.c
+@@ -45,6 +45,8 @@ struct clk_pll14xx {
+ #define to_clk_pll14xx(_hw) container_of(_hw, struct clk_pll14xx, hw)
+ 
+ static const struct imx_pll14xx_rate_table imx_pll1416x_tbl[] = {
++	PLL_1416X_RATE(2400000000U, 300, 3, 0),
++	PLL_1416X_RATE(2000000000U, 250, 3, 0),
+ 	PLL_1416X_RATE(1800000000U, 225, 3, 0),
+ 	PLL_1416X_RATE(1600000000U, 200, 3, 0),
+ 	PLL_1416X_RATE(1500000000U, 375, 3, 1),
+@@ -253,7 +255,7 @@ static unsigned long clk_pll14xx_recalc_
+ 
+ 	if (pll->type == PLL_1443X) {
+ 		pll_div_ctl1 = readl_relaxed(pll->base + DIV_CTL1);
+-		kdiv = (s16)FIELD_GET(KDIV_MASK, pll_div_ctl1);
++		kdiv = FIELD_GET(KDIV_MASK, pll_div_ctl1);
+ 	} else {
+ 		kdiv = 0;
+ 	}
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/2ghz/0002-imx8mp-2ghz-opp.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/2ghz/0002-imx8mp-2ghz-opp.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4288f6cc028f3648ab659c1fbd6c63b80015e9b3
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/2ghz/0002-imx8mp-2ghz-opp.patch
@@ -0,0 +1,31 @@
+From 33e011e724aa70cb19c8c1fd6aa69a1addebe6de Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 22:19:23 +0200
+Subject: [PATCH 2/2] imx8mp-2ghz-opp
+
+---
+ arch/arm64/boot/dts/freescale/imx8mp.dtsi | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+index 3dc2102cbb3d..d55db50df4a3 100644
+--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
++++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+@@ -158,6 +158,14 @@ opp-1800000000 {
+ 			clock-latency-ns = <150000>;
+ 			opp-suspend;
+ 		};
++
++		opp-2000000000 {
++			opp-hz = /bits/ 64 <1800000000>;
++			opp-microvolt = <1000000>;
++			opp-supported-hw = <0x20>, <0x3>;
++			clock-latency-ns = <150000>;
++			opp-suspend;
++		};
+ 	};
+ 
+ 	osc_32k: clock-osc-32k {
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0001-arm64-dts-imx8mp-add-HDMI-display-pipeline.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0001-arm64-dts-imx8mp-add-HDMI-display-pipeline.patch
new file mode 100644
index 0000000000000000000000000000000000000000..19f00f0ed1265c7cdcf065f7bc08a9fb6e81cccb
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0001-arm64-dts-imx8mp-add-HDMI-display-pipeline.patch
@@ -0,0 +1,105 @@
+From d582ee5aaa6ba28ab44a0fadd061d3a288a273b7 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Thu, 31 Mar 2022 22:53:14 +0200
+Subject: [PATCH 01/11] arm64: dts: imx8mp: add HDMI display pipeline
+
+This adds the DT nodes for all the peripherals that make up the
+HDMI display pipeline.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+---
+ arch/arm64/boot/dts/freescale/imx8mp.dtsi | 83 +++++++++++++++++++++++
+ 1 file changed, 83 insertions(+)
+
+--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
++++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+@@ -1569,6 +1569,89 @@
+ 					opp-hz = /bits/ 64 <1000000000>;
+ 				};
+ 			};
++
++			hdmi_pvi: display-bridge@32fc4000 {
++				compatible = "fsl,imx8mp-hdmi-pvi";
++				reg = <0x32fc4000 0x40>;
++				power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_PVI>;
++				status = "disabled";
++
++				ports {
++					#address-cells = <1>;
++					#size-cells = <0>;
++
++					port@0 {
++						reg = <0>;
++						pvi_from_lcdif3: endpoint {
++							remote-endpoint = <&lcdif3_to_pvi>;
++						};
++					};
++
++					port@1 {
++						reg = <1>;
++						pvi_to_hdmi_tx: endpoint {
++							remote-endpoint = <&hdmi_tx_from_pvi>;
++						};
++					};
++				};
++			};
++
++			lcdif3: display-controller@32fc6000 {
++				compatible = "fsl,imx8mp-lcdif";
++				reg = <0x32fc6000 0x238>;
++				interrupts = <8 IRQ_TYPE_LEVEL_HIGH>;
++				interrupt-parent = <&irqsteer_hdmi>;
++				clocks = <&hdmi_tx_phy>,
++					 <&clk IMX8MP_CLK_HDMI_APB>,
++					 <&clk IMX8MP_CLK_HDMI_ROOT>;
++				clock-names = "pix", "axi", "disp_axi";
++				power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_LCDIF>;
++				status = "disabled";
++
++				port {
++					lcdif3_to_pvi: endpoint {
++						remote-endpoint = <&pvi_from_lcdif3>;
++					};
++				};
++			};
++
++			hdmi_tx: hdmi@32fd8000 {
++				compatible = "fsl,imx8mp-hdmi";
++				reg = <0x32fd8000 0x7eff>;
++				interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
++				interrupt-parent = <&irqsteer_hdmi>;
++				clocks = <&clk IMX8MP_CLK_HDMI_APB>,
++					 <&clk IMX8MP_CLK_HDMI_REF_266M>,
++					 <&clk IMX8MP_CLK_HDMI_FDCC_TST>,
++					 <&clk IMX8MP_CLK_32K>,
++					 <&hdmi_tx_phy>;
++				clock-names = "iahb", "isfr", "fdcc", "cec", "pix";
++				assigned-clocks = <&clk IMX8MP_CLK_HDMI_REF_266M>;
++				assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_266M>;
++				power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_HDMI_TX>;
++				reg-io-width = <1>;
++				status = "disabled";
++
++				port {
++					hdmi_tx_from_pvi: endpoint {
++						remote-endpoint = <&pvi_to_hdmi_tx>;
++					};
++				};
++			};
++
++			hdmi_tx_phy: phy@32fdff00 {
++				compatible = "fsl,imx8mp-hdmi-phy";
++				reg = <0x32fdff00 0x100>;
++				clocks = <&clk IMX8MP_CLK_HDMI_APB>,
++					 <&clk IMX8MP_CLK_HDMI_24M>;
++				clock-names = "apb", "ref";
++				assigned-clocks = <&clk IMX8MP_CLK_HDMI_24M>;
++				assigned-clock-parents = <&clk IMX8MP_CLK_24M>;
++				power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_HDMI_TX_PHY>;
++				#clock-cells = <0>;
++				#phy-cells = <0>;
++				status = "disabled";
++			};
+ 		};
+ 
+ 		aips4: bus@32c00000 {
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0002-arm64-dts-imx8mp-evk-enable-HDMI.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0002-arm64-dts-imx8mp-evk-enable-HDMI.patch
new file mode 100644
index 0000000000000000000000000000000000000000..20f5c5181bb911d2dcbefd95ba10bd164c345d92
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0002-arm64-dts-imx8mp-evk-enable-HDMI.patch
@@ -0,0 +1,63 @@
+From c658e0a392bd9887ccd4f45179e08adfa9b695b0 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Mon, 4 Apr 2022 19:45:03 +0200
+Subject: [PATCH 02/11] arm64: dts: imx8mp-evk: enable HDMI
+
+Enable the DT nodes for HDMI TX and PHY and add the pinctrl for the few
+involved pins that are configurable.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+---
+ arch/arm64/boot/dts/freescale/imx8mp-evk.dts | 27 ++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+--- a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
++++ b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
+@@ -299,6 +299,20 @@
+ 	status = "disabled";/* can2 pin conflict with pdm */
+ };
+ 
++&hdmi_pvi {
++	status = "okay";
++};
++
++&hdmi_tx {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pinctrl_hdmi>;
++	status = "okay";
++};
++
++&hdmi_tx_phy {
++	status = "okay";
++};
++
+ &i2c1 {
+ 	clock-frequency = <400000>;
+ 	pinctrl-names = "default";
+@@ -552,6 +566,10 @@
+ 	status = "okay";
+ };
+ 
++&lcdif3 {
++	status = "okay";
++};
++
+ &snvs_pwrkey {
+ 	status = "okay";
+ };
+@@ -718,6 +736,15 @@
+ 		>;
+ 	};
+ 
++	pinctrl_hdmi: hdmigrp {
++		fsl,pins = <
++			MX8MP_IOMUXC_HDMI_DDC_SCL__HDMIMIX_HDMI_SCL	0x1c3
++			MX8MP_IOMUXC_HDMI_DDC_SDA__HDMIMIX_HDMI_SDA	0x1c3
++			MX8MP_IOMUXC_HDMI_HPD__HDMIMIX_HDMI_HPD		0x19
++			MX8MP_IOMUXC_HDMI_CEC__HDMIMIX_HDMI_CEC		0x19
++		>;
++	};
++
+ 	pinctrl_i2c1: i2c1grp {
+ 		fsl,pins = <
+ 			MX8MP_IOMUXC_I2C1_SCL__I2C1_SCL		0x400001c2
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0003-phy-freescale-add-Samsung-HDMI-PHY.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0003-phy-freescale-add-Samsung-HDMI-PHY.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8afe8663b0b3a3c482d288ea5140d2b984d67b79
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0003-phy-freescale-add-Samsung-HDMI-PHY.patch
@@ -0,0 +1,754 @@
+From 5b1cec07bb9a63bb59dc88e71e15ab750797f5fb Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Fri, 1 Apr 2022 23:02:58 +0200
+Subject: [PATCH 03/11] phy: freescale: add Samsung HDMI PHY
+
+This adds the driver for the Samsung HDMI PHY found on the
+i.MX8MP SoC. Based on downstream implementation from
+Sandor Yu <Sandor.yu@nxp.com>.
+
+Co-developed-by: Marco Felsch <m.felsch@pengutronix.de>
+Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+---
+ drivers/phy/freescale/Kconfig                |   7 +
+ drivers/phy/freescale/Makefile               |   1 +
+ drivers/phy/freescale/phy-fsl-samsung-hdmi.c | 699 +++++++++++++++++++
+ 3 files changed, 707 insertions(+)
+ create mode 100644 drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+
+diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
+index 853958fb2c06..d801451fbb59 100644
+--- a/drivers/phy/freescale/Kconfig
++++ b/drivers/phy/freescale/Kconfig
+@@ -35,6 +35,13 @@ config PHY_FSL_IMX8M_PCIE
+ 	  Enable this to add support for the PCIE PHY as found on
+ 	  i.MX8M family of SOCs.
+ 
++config PHY_FSL_SAMSUNG_HDMI_PHY
++	tristate "Samsung HDMI PHY support"
++	depends on OF && HAS_IOMEM
++	select GENERIC_PHY
++	help
++	  Enable this to add support for the Samsung HDMI PHY in i.MX8MP.
++
+ endif
+ 
+ config PHY_FSL_LYNX_28G
+diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
+index cedb328bc4d2..c4386bfdb853 100644
+--- a/drivers/phy/freescale/Makefile
++++ b/drivers/phy/freescale/Makefile
+@@ -4,3 +4,4 @@ obj-$(CONFIG_PHY_MIXEL_LVDS_PHY)	+= phy-fsl-imx8qm-lvds-phy.o
+ obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY)	+= phy-fsl-imx8-mipi-dphy.o
+ obj-$(CONFIG_PHY_FSL_IMX8M_PCIE)	+= phy-fsl-imx8m-pcie.o
+ obj-$(CONFIG_PHY_FSL_LYNX_28G)		+= phy-fsl-lynx-28g.o
++obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY)	+= phy-fsl-samsung-hdmi.o
+diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+new file mode 100644
+index 000000000000..e8736b0683a4
+--- /dev/null
++++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+@@ -0,0 +1,699 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright 2020 NXP
++ * Copyright 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++
++#define PHY_REG_00		0x00
++#define PHY_REG_01		0x04
++#define PHY_REG_02		0x08
++#define PHY_REG_08		0x20
++#define PHY_REG_09		0x24
++#define PHY_REG_10		0x28
++#define PHY_REG_11		0x2c
++
++#define PHY_REG_12		0x30
++#define  REG12_FLD_CK_DIV(x)	(((x) & 0x3) << 4)
++#define  REG12_TMDS_CLK		0x0
++#define  REG12_TMDS_CLK_DIV2	0x1
++#define  REG12_TMDS_CLK_DIV4	0x2
++#define  REG12_TMDS_CLK_DIV8	0x3
++
++#define PHY_REG_13		0x34
++#define  REG13_FLD_TG_CODE_LOW(x) (x & 0xff)
++
++#define PHY_REG_14		0x38
++#define  REG14_FLD_TOL(x)	((x & 0xf) << 4)
++#define  REG14_FLD_RP_CODE(x)	((x & 0x3) << 1)
++#define  REG14_FLD_TG_CODE_HIGH(x) ((x >> 8) & 0x1)
++
++#define PHY_REG_15		0x3c
++#define PHY_REG_16		0x40
++#define PHY_REG_17		0x44
++#define PHY_REG_18		0x48
++#define PHY_REG_19		0x4c
++#define PHY_REG_20		0x50
++
++#define PHY_REG_21		0x54
++#define  REG21_SEL_TX_CK_INV	BIT(7)
++#define  REG21_PMS_S(x)		(x & 0xf)
++
++#define PHY_REG_22		0x58
++#define PHY_REG_23		0x5c
++#define PHY_REG_24		0x60
++#define PHY_REG_25		0x64
++#define PHY_REG_26		0x68
++#define PHY_REG_27		0x6c
++#define PHY_REG_28		0x70
++#define PHY_REG_29		0x74
++#define PHY_REG_30		0x78
++#define PHY_REG_31		0x7c
++#define PHY_REG_32		0x80
++
++#define PHY_REG_33		0x84
++#define  REG33_MODE_SET_DONE	BIT(7)
++#define  REG33_FIX_DA		BIT(1)
++
++#define PHY_REG_34		0x88
++#define  REG34_PHY_READY	BIT(7)
++#define  REG34_PLL_LOCK		BIT(6)
++#define  REG34_PHY_CLK_READY	BIT(5)
++
++#define PHY_REG_35		0x8c
++#define PHY_REG_36		0x90
++#define PHY_REG_37		0x94
++#define PHY_REG_38		0x98
++#define PHY_REG_39		0x9c
++#define PHY_REG_40		0xa0
++#define PHY_REG_41		0xa4
++#define PHY_REG_42		0xa8
++#define PHY_REG_43		0xac
++#define PHY_REG_44		0xb0
++#define PHY_REG_45		0xb4
++#define PHY_REG_46		0xb8
++#define PHY_REG_47		0xbc
++
++#define PHY_PLL_DIV_REGS_NUM 6
++
++struct phy_config {
++	u32	pixclk;
++	u8	pll_div_regs[PHY_PLL_DIV_REGS_NUM];
++};
++
++const struct phy_config phy_pll_cfg[] = {
++	{
++		.pixclk = 22250000,
++		.pll_div_regs = { 0x4B, 0xF1, 0x89, 0x88, 0x80, 0x40 },
++	}, {
++		.pixclk = 23750000,
++		.pll_div_regs = { 0x50, 0xF1, 0x86, 0x85, 0x80, 0x40 },
++	},{
++		.pixclk = 24000000,
++		.pll_div_regs = { 0x50, 0xF0, 0x00, 0x00, 0x80, 0x00 },
++	},{
++		.pixclk = 24024000,
++		.pll_div_regs = { 0x50, 0xF1, 0x99, 0x02, 0x80, 0x40 },
++	}, {
++		.pixclk = 25175000,
++		.pll_div_regs = { 0x54, 0xFC, 0xCC, 0x91, 0x80, 0x40 },
++	}, {
++		.pixclk = 25200000,
++		.pll_div_regs = { 0x54, 0xF0, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 26750000,
++		.pll_div_regs = { 0x5A, 0xF2, 0x89, 0x88, 0x80, 0x40 },
++	}, {
++		.pixclk = 27000000,
++		.pll_div_regs = { 0x5A, 0xF0, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 27027000,
++		.pll_div_regs = { 0x5A, 0xF2, 0xFD, 0x0C, 0x80, 0x40 },
++	}, {
++		.pixclk = 29500000,
++		.pll_div_regs = { 0x62, 0xF4, 0x95, 0x08, 0x80, 0x40 },
++	}, {
++		.pixclk = 30750000,
++		.pll_div_regs = { 0x66, 0xF4, 0x82, 0x01, 0x88, 0x45 },
++	}, {
++		.pixclk = 30888000,
++		.pll_div_regs = { 0x66, 0xF4, 0x99, 0x18, 0x88, 0x45 },
++	}, {
++		.pixclk = 33750000,
++		.pll_div_regs = { 0x70, 0xF4, 0x82, 0x01, 0x80, 0x40 },
++	}, {
++		.pixclk = 35000000,
++		.pll_div_regs = { 0x58, 0xB8, 0x8B, 0x88, 0x80, 0x40 },
++	}, {
++		.pixclk = 36000000,
++		.pll_div_regs = { 0x5A, 0xB0, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 36036000,
++		.pll_div_regs = { 0x5A, 0xB2, 0xFD, 0x0C, 0x80, 0x40 },
++	}, {
++		.pixclk = 40000000,
++		.pll_div_regs = { 0x64, 0xB0, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 43200000,
++		.pll_div_regs = { 0x5A, 0x90, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 43243200,
++		.pll_div_regs = { 0x5A, 0x92, 0xFD, 0x0C, 0x80, 0x40 },
++	}, {
++		.pixclk = 44500000,
++		.pll_div_regs = { 0x5C, 0x92, 0x98, 0x11, 0x84, 0x41 },
++	}, {
++		.pixclk = 47000000,
++		.pll_div_regs = { 0x62, 0x94, 0x95, 0x82, 0x80, 0x40 },
++	}, {
++		.pixclk = 47500000,
++		.pll_div_regs = { 0x63, 0x96, 0xA1, 0x82, 0x80, 0x40 },
++	}, {
++		.pixclk = 50349650,
++		.pll_div_regs = { 0x54, 0x7C, 0xC3, 0x8F, 0x80, 0x40 },
++	}, {
++		.pixclk = 50400000,
++		.pll_div_regs = { 0x54, 0x70, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 53250000,
++		.pll_div_regs = { 0x58, 0x72, 0x84, 0x03, 0x82, 0x41 },
++	}, {
++		.pixclk = 53500000,
++		.pll_div_regs = { 0x5A, 0x72, 0x89, 0x88, 0x80, 0x40 },
++	}, {
++		.pixclk = 54000000,
++		.pll_div_regs = { 0x5A, 0x70, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 54054000,
++		.pll_div_regs = { 0x5A, 0x72, 0xFD, 0x0C, 0x80, 0x40 },
++	}, {
++		.pixclk = 59000000,
++		.pll_div_regs = { 0x62, 0x74, 0x95, 0x08, 0x80, 0x40 },
++	}, {
++		.pixclk = 59340659,
++		.pll_div_regs = { 0x62, 0x74, 0xDB, 0x52, 0x88, 0x47 },
++	}, {
++		.pixclk = 59400000,
++		.pll_div_regs = { 0x63, 0x70, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 61500000,
++		.pll_div_regs = { 0x66, 0x74, 0x82, 0x01, 0x88, 0x45 },
++	}, {
++		.pixclk = 63500000,
++		.pll_div_regs = { 0x69, 0x74, 0x89, 0x08, 0x80, 0x40 },
++	}, {
++		.pixclk = 67500000,
++		.pll_div_regs = { 0x54, 0x52, 0x87, 0x03, 0x80, 0x40 },
++	}, {
++		.pixclk = 70000000,
++		.pll_div_regs = { 0x58, 0x58, 0x8B, 0x88, 0x80, 0x40 },
++	}, {
++		.pixclk = 72000000,
++		.pll_div_regs = { 0x5A, 0x50, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 72072000,
++		.pll_div_regs = { 0x5A, 0x52, 0xFD, 0x0C, 0x80, 0x40 },
++	}, {
++		.pixclk = 74176000,
++		.pll_div_regs = { 0x5D, 0x58, 0xDB, 0xA2, 0x88, 0x41 },
++	}, {
++		.pixclk = 74250000,
++		.pll_div_regs = { 0x5C, 0x52, 0x90, 0x0D, 0x84, 0x41 },
++	}, {
++		.pixclk = 78500000,
++		.pll_div_regs = { 0x62, 0x54, 0x87, 0x01, 0x80, 0x40 },
++	}, {
++		.pixclk = 80000000,
++		.pll_div_regs = { 0x64, 0x50, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 82000000,
++		.pll_div_regs = { 0x66, 0x54, 0x82, 0x01, 0x88, 0x45 },
++	}, {
++		.pixclk = 82500000,
++		.pll_div_regs = { 0x67, 0x54, 0x88, 0x01, 0x90, 0x49 },
++	}, {
++		.pixclk = 89000000,
++		.pll_div_regs = { 0x70, 0x54, 0x84, 0x83, 0x80, 0x40 },
++	}, {
++		.pixclk = 90000000,
++		.pll_div_regs = { 0x70, 0x54, 0x82, 0x01, 0x80, 0x40 },
++	}, {
++		.pixclk = 94000000,
++		.pll_div_regs = { 0x4E, 0x32, 0xA7, 0x10, 0x80, 0x40 },
++	}, {
++		.pixclk = 95000000,
++		.pll_div_regs = { 0x50, 0x31, 0x86, 0x85, 0x80, 0x40 },
++	}, {
++		.pixclk = 98901099,
++		.pll_div_regs = { 0x52, 0x3A, 0xDB, 0x4C, 0x88, 0x47 },
++	}, {
++		.pixclk = 99000000,
++		.pll_div_regs = { 0x52, 0x32, 0x82, 0x01, 0x88, 0x47 },
++	}, {
++		.pixclk = 100699300,
++		.pll_div_regs = { 0x54, 0x3C, 0xC3, 0x8F, 0x80, 0x40 },
++	}, {
++		.pixclk = 100800000,
++		.pll_div_regs = { 0x54, 0x30, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 102500000,
++		.pll_div_regs = { 0x55, 0x32, 0x8C, 0x05, 0x90, 0x4B },
++	}, {
++		.pixclk = 104750000,
++		.pll_div_regs = { 0x57, 0x32, 0x98, 0x07, 0x90, 0x49 },
++	}, {
++		.pixclk = 106500000,
++		.pll_div_regs = { 0x58, 0x32, 0x84, 0x03, 0x82, 0x41 },
++	}, {
++		.pixclk = 107000000,
++		.pll_div_regs = { 0x5A, 0x32, 0x89, 0x88, 0x80, 0x40 },
++	}, {
++		.pixclk = 108000000,
++		.pll_div_regs = { 0x5A, 0x30, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 108108000,
++		.pll_div_regs = { 0x5A, 0x32, 0xFD, 0x0C, 0x80, 0x40 },
++	}, {
++		.pixclk = 118000000,
++		.pll_div_regs = { 0x62, 0x34, 0x95, 0x08, 0x80, 0x40 },
++	}, {
++		.pixclk = 118800000,
++		.pll_div_regs = { 0x63, 0x30, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 123000000,
++		.pll_div_regs = { 0x66, 0x34, 0x82, 0x01, 0x88, 0x45 },
++	}, {
++		.pixclk = 127000000,
++		.pll_div_regs = { 0x69, 0x34, 0x89, 0x08, 0x80, 0x40 },
++	}, {
++		.pixclk = 135000000,
++		.pll_div_regs = { 0x70, 0x34, 0x82, 0x01, 0x80, 0x40 },
++	}, {
++		.pixclk = 135580000,
++		.pll_div_regs = { 0x71, 0x39, 0xE9, 0x82, 0x9C, 0x5B },
++	}, {
++		.pixclk = 137520000,
++		.pll_div_regs = { 0x72, 0x38, 0x99, 0x10, 0x85, 0x41 },
++	}, {
++		.pixclk = 138750000,
++		.pll_div_regs = { 0x73, 0x35, 0x88, 0x05, 0x90, 0x4D },
++	}, {
++		.pixclk = 140000000,
++		.pll_div_regs = { 0x75, 0x36, 0xA7, 0x90, 0x80, 0x40 },
++	}, {
++		.pixclk = 144000000,
++		.pll_div_regs = { 0x78, 0x30, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 148352000,
++		.pll_div_regs = { 0x7B, 0x35, 0xDB, 0x39, 0x90, 0x45 },
++	}, {
++		.pixclk = 148500000,
++		.pll_div_regs = { 0x7B, 0x35, 0x84, 0x03, 0x90, 0x45 },
++	}, {
++		.pixclk = 154000000,
++		.pll_div_regs = { 0x40, 0x18, 0x83, 0x01, 0x00, 0x40 },
++	}, {
++		.pixclk = 157000000,
++		.pll_div_regs = { 0x41, 0x11, 0xA7, 0x14, 0x80, 0x40 },
++	}, {
++		.pixclk = 160000000,
++		.pll_div_regs = { 0x42, 0x12, 0xA1, 0x20, 0x80, 0x40 },
++	}, {
++		.pixclk = 162000000,
++		.pll_div_regs = { 0x43, 0x18, 0x8B, 0x08, 0x96, 0x55 },
++	}, {
++		.pixclk = 164000000,
++		.pll_div_regs = { 0x45, 0x11, 0x83, 0x82, 0x90, 0x4B },
++	}, {
++		.pixclk = 165000000,
++		.pll_div_regs = { 0x45, 0x11, 0x84, 0x81, 0x90, 0x4B },
++	}, {
++		.pixclk = 180000000,
++		.pll_div_regs = { 0x4B, 0x10, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 185625000,
++		.pll_div_regs = { 0x4E, 0x12, 0x9A, 0x95, 0x80, 0x40 },
++	}, {
++		.pixclk = 188000000,
++		.pll_div_regs = { 0x4E, 0x12, 0xA7, 0x10, 0x80, 0x40 },
++	}, {
++		.pixclk = 198000000,
++		.pll_div_regs = { 0x52, 0x12, 0x82, 0x01, 0x88, 0x47 },
++	}, {
++		.pixclk = 205000000,
++		.pll_div_regs = { 0x55, 0x12, 0x8C, 0x05, 0x90, 0x4B },
++	}, {
++		.pixclk = 209500000,
++		.pll_div_regs = { 0x57, 0x12, 0x98, 0x07, 0x90, 0x49 },
++	}, {
++		.pixclk = 213000000,
++		.pll_div_regs = { 0x58, 0x12, 0x84, 0x03, 0x82, 0x41 },
++	}, {
++		.pixclk = 216000000,
++		.pll_div_regs = { 0x5A, 0x10, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 216216000,
++		.pll_div_regs = { 0x5A, 0x12, 0xFD, 0x0C, 0x80, 0x40 },
++	}, {
++		.pixclk = 237600000,
++		.pll_div_regs = { 0x63, 0x10, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 254000000,
++		.pll_div_regs = { 0x69, 0x14, 0x89, 0x08, 0x80, 0x40 },
++	}, {
++		.pixclk = 277500000,
++		.pll_div_regs = { 0x73, 0x15, 0x88, 0x05, 0x90, 0x4D },
++	}, {
++		.pixclk = 288000000,
++		.pll_div_regs = { 0x78, 0x10, 0x00, 0x00, 0x80, 0x00 },
++	}, {
++		.pixclk = 297000000,
++		.pll_div_regs = { 0x7B, 0x15, 0x84, 0x03, 0x90, 0x45 },
++	},
++};
++
++struct reg_settings {
++	u8 reg;
++	u8 val;
++};
++
++const struct reg_settings common_phy_cfg[] = {
++	{ PHY_REG_00, 0x00 }, { PHY_REG_01, 0xD1 },
++	{ PHY_REG_08, 0x4f }, { PHY_REG_09, 0x30 },
++	{ PHY_REG_10, 0x33 }, { PHY_REG_11, 0x65 },
++	/* REG12 pixclk specific */
++	/* REG13 pixclk specific */
++	/* REG14 pixclk specific */
++	{ PHY_REG_15, 0x80 }, { PHY_REG_16, 0x6C },
++	{ PHY_REG_17, 0xF2 }, { PHY_REG_18, 0x67 },
++	{ PHY_REG_19, 0x00 }, { PHY_REG_20, 0x10 },
++	/* REG21 pixclk specific */
++	{ PHY_REG_22, 0x30 }, { PHY_REG_23, 0x32 },
++	{ PHY_REG_24, 0x60 }, { PHY_REG_25, 0x8F },
++	{ PHY_REG_26, 0x00 }, { PHY_REG_27, 0x00 },
++	{ PHY_REG_28, 0x08 }, { PHY_REG_29, 0x00 },
++	{ PHY_REG_30, 0x00 }, { PHY_REG_31, 0x00 },
++	{ PHY_REG_32, 0x00 }, { PHY_REG_33, 0x80 },
++	{ PHY_REG_34, 0x00 }, { PHY_REG_35, 0x00 },
++	{ PHY_REG_36, 0x00 }, { PHY_REG_37, 0x00 },
++	{ PHY_REG_38, 0x00 }, { PHY_REG_39, 0x00 },
++	{ PHY_REG_40, 0x00 }, { PHY_REG_41, 0xE0 },
++	{ PHY_REG_42, 0x83 }, { PHY_REG_43, 0x0F },
++	{ PHY_REG_44, 0x3E }, { PHY_REG_45, 0xF8 },
++	{ PHY_REG_46, 0x00 }, { PHY_REG_47, 0x00 }
++};
++
++struct fsl_samsung_hdmi_phy {
++	struct device *dev;
++	void __iomem *regs;
++	struct clk *apbclk;
++	struct clk *refclk;
++
++	/* clk provider */
++	struct clk_hw hw;
++	const struct phy_config *cur_cfg;
++};
++
++static inline struct fsl_samsung_hdmi_phy *
++to_fsl_samsung_hdmi_phy(struct clk_hw *hw)
++{
++	return container_of(hw, struct fsl_samsung_hdmi_phy, hw);
++}
++
++static void
++fsl_samsung_hdmi_phy_configure_pixclk(struct fsl_samsung_hdmi_phy *phy,
++				      const struct phy_config *cfg)
++{
++	u8 div;
++
++	switch (cfg->pixclk) {
++	case  22250000 ...  33750000:	div = 0xf; break;
++	case  35000000 ...  40000000:	div = 0xb; break;
++	case  43200000 ...  47500000:	div = 0x9; break;
++	case  50349650 ...  63500000:	div = 0x7; break;
++	case  67500000 ...  90000000:	div = 0x5; break;
++	case  94000000 ... 148500000:	div = 0x3; break;
++	case 154000000 ... 297000000:	div = 0x1; break;
++	}
++
++	writeb(REG21_SEL_TX_CK_INV | REG21_PMS_S(div), phy->regs + PHY_REG_21);
++}
++
++static void
++fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy,
++					    const struct phy_config *cfg)
++{
++	u32 pclk = cfg->pixclk;
++	u32 fld_tg_code;
++	u32 pclk_khz;
++	u8 div;
++
++	switch (cfg->pixclk) {
++	case  22250000 ...  47500000:	div = 1; break;
++	case  50349650 ...  99000000:	div = 2; break;
++	case 100699300 ... 198000000:	div = 4; break;
++	case 205000000 ... 297000000:	div = 8; break;
++	}
++
++	writeb(REG12_FLD_CK_DIV(fls(div) - 1), phy->regs + PHY_REG_12);
++
++	/*
++	 * Calculation for the frequency lock detector target code (fld_tg_code)
++	 * is based on reference manual register description of PHY_REG13
++	 * (13.10.3.1.14.2):
++	 *   1st) Calculate int_pllclk which is determinded by FLD_CK_DIV
++	 *   2nd) Increase resolution to avoid rounding issues
++	 *   3th) Do the div (256 / Freq. of int_pllclk) * 24
++	 *   4th) Reduce the resolution and always round up since the NXP
++	 *        settings rounding up always too. TODO: Check if that is
++	 *        correct.
++	 */
++	pclk /= div;
++	pclk_khz = pclk / 1000;
++	fld_tg_code = 256 * 1000 * 1000 / pclk_khz * 24;
++	fld_tg_code = DIV_ROUND_UP(fld_tg_code, 1000);
++
++	/* FLD_TOL and FLD_RP_CODE taken from downstream driver */
++	writeb(REG14_FLD_TOL(2) | REG14_FLD_RP_CODE(2) |
++	       REG14_FLD_TG_CODE_HIGH(fld_tg_code), phy->regs + PHY_REG_14);
++	writeb(REG13_FLD_TG_CODE_LOW(fld_tg_code), phy->regs + PHY_REG_13);
++}
++
++static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy,
++					  const struct phy_config *cfg)
++{
++	int i, ret;
++	u8 val;
++
++	/* HDMI PHY init */
++	writeb(REG33_FIX_DA, phy->regs + PHY_REG_33);
++
++	/* Common PHY registers registers */
++	for (i = 0; i < ARRAY_SIZE(common_phy_cfg); i++)
++		writeb(common_phy_cfg[i].val, phy->regs + common_phy_cfg[i].reg);
++
++	/* Set individual PLL registers PHY_REG2 ... PHY_REG7 */
++	for (i = 0; i < PHY_PLL_DIV_REGS_NUM; i++)
++		writeb(cfg->pll_div_regs[i], phy->regs + PHY_REG_02 + i * 4);
++
++	fsl_samsung_hdmi_phy_configure_pixclk(phy, cfg);
++	fsl_samsung_hdmi_phy_configure_pll_lock_det(phy, cfg);
++
++	writeb(REG33_FIX_DA | REG33_MODE_SET_DONE , phy->regs + PHY_REG_33);
++
++	ret = readb_poll_timeout(phy->regs + PHY_REG_34, val,
++					 val & REG34_PLL_LOCK,
++					 50, 20000);
++	if (ret)
++		dev_err(phy->dev, "PLL failed to lock\n");
++
++	return ret;
++}
++
++static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
++					 unsigned long parent_rate)
++{
++	struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
++
++	if (!phy->cur_cfg)
++		return 74250000;
++
++	return phy->cur_cfg->pixclk;
++}
++
++static long phy_clk_round_rate(struct clk_hw *hw,
++			       unsigned long rate, unsigned long *parent_rate)
++{
++	int i;
++
++	for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--)
++		if (phy_pll_cfg[i].pixclk <= rate)
++			return phy_pll_cfg[i].pixclk;
++
++	return -EINVAL;
++}
++
++static int phy_clk_set_rate(struct clk_hw *hw,
++			    unsigned long rate, unsigned long parent_rate)
++{
++	struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
++	int i;
++
++	for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--)
++		if (phy_pll_cfg[i].pixclk <= rate)
++			break;
++
++	if (i < 0)
++		return -EINVAL;
++
++	phy->cur_cfg = &phy_pll_cfg[i];
++
++	return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
++}
++
++static const struct clk_ops phy_clk_ops = {
++	.recalc_rate = phy_clk_recalc_rate,
++	.round_rate = phy_clk_round_rate,
++	.set_rate = phy_clk_set_rate,
++};
++
++static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy)
++{
++	struct device *dev = phy->dev;
++	struct device_node *np = dev->of_node;
++	struct clk_init_data init;
++	const char *parent_name;
++	struct clk *phyclk;
++	int ret;
++
++	parent_name = __clk_get_name(phy->refclk);
++
++	init.parent_names = &parent_name;
++	init.num_parents = 1;
++	init.flags = 0;
++	init.name = "hdmi_pclk";
++	init.ops = &phy_clk_ops;
++
++	phy->hw.init = &init;
++
++	phyclk = devm_clk_register(dev, &phy->hw);
++	if (IS_ERR(phyclk))
++		return dev_err_probe(dev, PTR_ERR(phyclk),
++				     "failed to register clock\n");
++
++	ret = of_clk_add_provider(np, of_clk_src_simple_get, phyclk);
++	if (ret)
++		return dev_err_probe(dev, ret,
++				     "failed to register clock provider\n");
++
++	return 0;
++}
++
++static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
++{
++	struct fsl_samsung_hdmi_phy *phy;
++	int ret;
++
++	phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++	if (!phy)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, phy);
++	phy->dev = &pdev->dev;
++
++	phy->regs = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(phy->regs))
++		return PTR_ERR(phy->regs);
++
++	phy->apbclk = devm_clk_get(phy->dev, "apb");
++	if (IS_ERR(phy->apbclk))
++		return dev_err_probe(phy->dev, PTR_ERR(phy->apbclk),
++				     "failed to get apb clk\n");
++
++	phy->refclk = devm_clk_get(phy->dev, "ref");
++	if (IS_ERR(phy->refclk))
++		return dev_err_probe(phy->dev, PTR_ERR(phy->refclk),
++				     "failed to get ref clk\n");
++
++	ret = clk_prepare_enable(phy->apbclk);
++	if (ret) {
++		dev_err(phy->dev, "failed to enable apbclk\n");
++		return ret;
++	}
++
++	pm_runtime_get_noresume(phy->dev);
++	pm_runtime_set_active(phy->dev);
++	pm_runtime_enable(phy->dev);
++
++	ret = phy_clk_register(phy);
++	if (ret) {
++		dev_err(&pdev->dev, "register clk failed\n");
++		goto register_clk_failed;
++	}
++
++	pm_runtime_put(phy->dev);
++
++	return 0;
++
++register_clk_failed:
++	clk_disable_unprepare(phy->apbclk);
++
++	return ret;
++}
++
++static int fsl_samsung_hdmi_phy_remove(struct platform_device *pdev)
++{
++	of_clk_del_provider(pdev->dev.of_node);
++
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int fsl_samsung_hdmi_phy_suspend(struct device *dev)
++{
++	struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev);
++
++	clk_disable_unprepare(phy->apbclk);
++
++	return 0;
++}
++
++static int fsl_samsung_hdmi_phy_resume(struct device *dev)
++{
++	struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev);
++	int ret = 0;
++
++	ret = clk_prepare_enable(phy->apbclk);
++	if (ret) {
++		dev_err(phy->dev, "failed to enable apbclk\n");
++		return ret;
++	}
++
++	if (phy->cur_cfg)
++		ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
++
++	return ret;
++
++}
++#endif
++
++static const struct dev_pm_ops fsl_samsung_hdmi_phy_pm_ops = {
++	SET_RUNTIME_PM_OPS(fsl_samsung_hdmi_phy_suspend,
++			   fsl_samsung_hdmi_phy_resume, NULL)
++	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
++				pm_runtime_force_resume)
++};
++
++static const struct of_device_id fsl_samsung_hdmi_phy_of_match[] = {
++	{
++		.compatible = "fsl,imx8mp-hdmi-phy",
++	}, {
++		/* sentinel */
++	}
++};
++MODULE_DEVICE_TABLE(of, fsl_samsung_hdmi_phy_of_match);
++
++static struct platform_driver fsl_samsung_hdmi_phy_driver = {
++	.probe  = fsl_samsung_hdmi_phy_probe,
++	.remove = fsl_samsung_hdmi_phy_remove,
++	.driver = {
++		.name = "fsl-samsung-hdmi-phy",
++		.of_match_table = fsl_samsung_hdmi_phy_of_match,
++		.pm = &fsl_samsung_hdmi_phy_pm_ops,
++	},
++};
++module_platform_driver(fsl_samsung_hdmi_phy_driver);
++
++MODULE_AUTHOR("Sandor Yu <Sandor.yu@nxp.com>");
++MODULE_DESCRIPTION("SAMSUNG HDMI 2.0 Transmitter PHY Driver");
++MODULE_LICENSE("GPL v2");
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0004-dt-bindings-display-imx-add-binding-for-i.MX8MP-HDMI.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0004-dt-bindings-display-imx-add-binding-for-i.MX8MP-HDMI.patch
new file mode 100644
index 0000000000000000000000000000000000000000..976c4c115bfb8597cdbe7727a3a053b7e5cd3b1a
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0004-dt-bindings-display-imx-add-binding-for-i.MX8MP-HDMI.patch
@@ -0,0 +1,107 @@
+From 7b1066c335ef49406b623dde6a70d39d8c58dde8 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Tue, 5 Apr 2022 21:18:30 +0200
+Subject: [PATCH 04/11] dt-bindings: display: imx: add binding for i.MX8MP HDMI
+ PVI
+
+Add binding for the i.MX8MP HDMI parallel video interface block.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+Tested-by: Marek Vasut <marex@denx.de>
+---
+ .../display/imx/fsl,imx8mp-hdmi-pvi.yaml      | 83 +++++++++++++++++++
+ 1 file changed, 83 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pvi.yaml
+
+diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pvi.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pvi.yaml
+new file mode 100644
+index 000000000000..bf25d29c03ab
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pvi.yaml
+@@ -0,0 +1,83 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/imx/fsl,imx8mp-hdmi-pvi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Freescale i.MX8MP HDMI Parallel Video Interface
++
++maintainers:
++  - Lucas Stach <l.stach@pengutronix.de>
++
++description: |
++  The HDMI parallel video interface is timing and sync generator block in the
++  i.MX8MP SoC, that sits between the video source and the HDMI TX controller.
++
++properties:
++  compatible:
++    enum:
++      - fsl,imx8mp-hdmi-pvi
++
++  reg:
++    maxItems: 1
++
++  power-domains:
++    maxItems: 1
++
++  ports:
++    $ref: /schemas/graph.yaml#/properties/ports
++    description: |
++      This device has two video ports.
++
++    properties:
++      port@0:
++        $ref: /schemas/graph.yaml#/properties/port
++        description: Input from the LCDIF controller.
++
++      port@1:
++        $ref: /schemas/graph.yaml#/properties/port
++        description: Output to the HDMI TX controller
++
++    anyOf:
++      - required:
++          - port@0
++      - required:
++          - port@1
++
++required:
++  - compatible
++  - reg
++  - power-domains
++  - ports
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/imx8mp-clock.h>
++    #include <dt-bindings/power/imx8mp-power.h>
++
++    display-bridge@32fc4000 {
++        compatible = "fsl,imx8mp-hdmi-pvi";
++        reg = <0x32fc4000 0x40>;
++        power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_PVI>;
++
++        ports {
++            #address-cells = <1>;
++            #size-cells = <0>;
++
++            port@0 {
++                reg = <0>;
++                pvi_from_lcdif3: endpoint {
++                    remote-endpoint = <&lcdif3_to_pvi>;
++                };
++            };
++
++            port@1 {
++                reg = <1>;
++                pvi_to_hdmi_tx: endpoint {
++                    remote-endpoint = <&hdmi_tx_from_pvi>;
++                };
++            };
++        };
++    };
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0005-drm-imx-add-driver-for-HDMI-TX-Parallel-Video-Interf.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0005-drm-imx-add-driver-for-HDMI-TX-Parallel-Video-Interf.patch
new file mode 100644
index 0000000000000000000000000000000000000000..68de81d2e129e7696d8f9304902ae86a8fc47c76
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0005-drm-imx-add-driver-for-HDMI-TX-Parallel-Video-Interf.patch
@@ -0,0 +1,238 @@
+From 7a0a27bc5419b48b20b7a18b555bd33ae20a5c52 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Mon, 4 Apr 2022 19:18:30 +0200
+Subject: [PATCH 05/11] drm/imx: add driver for HDMI TX Parallel Video
+ Interface
+
+This IP block is found in the HDMI subsystem of the i.MX8MP SoC. It has a
+full timing generator and can switch between different video sources. On
+the i.MX8MP however the only supported source is the LCDIF. The block
+just needs to be powered up and told about the polarity of the video
+sync signals to act in bypass mode.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+Tested-by: Marek Vasut <marex@denx.de>
+---
+ drivers/gpu/drm/bridge/imx/Kconfig           |   7 +
+ drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c | 201 +++++++++++++++++++
+ 2 files changed, 208 insertions(+)
+ create mode 100644 drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
+
+--- a/drivers/gpu/drm/bridge/imx/Kconfig
++++ b/drivers/gpu/drm/bridge/imx/Kconfig
+@@ -60,4 +60,11 @@ config DRM_IMX93_MIPI_DSI
+ 	  Choose this to enable MIPI DSI controller found in Freescale i.MX93
+ 	  processor.
+ 
++config DRM_IMX8MP_HDMI_PVI
++	tristate "i.MX8MP HDMI PVI bridge support"
++	depends on OF
++	help
++	  Choose this to enable support for the internal HDMI TX Parallel
++	  Video Interface found on the i.MX8MP SoC.
++
+ endif # ARCH_MXC || COMPILE_TEST
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
+@@ -0,0 +1,201 @@
++// SPDX-License-Identifier: GPL-2.0+
++
++/*
++ * Copyright (C) 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
++ */
++
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_bridge.h>
++#include <drm/drm_crtc.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/of_graph.h>
++#include <linux/pm_runtime.h>
++
++#define HTX_PVI_CTL	0x0
++#define  PVI_CTL_OP_VSYNC_POL	BIT(18)
++#define  PVI_CTL_OP_HSYNC_POL	BIT(17)
++#define  PVI_CTL_OP_DE_POL	BIT(16)
++#define  PVI_CTL_INP_VSYNC_POL	BIT(14)
++#define  PVI_CTL_INP_HSYNC_POL	BIT(13)
++#define  PVI_CTL_INP_DE_POL	BIT(12)
++#define  PVI_CTL_INPUT_LCDIF	BIT(2)
++#define  PVI_CTL_EN		BIT(0)
++
++struct imx_hdmi_pvi {
++	struct drm_bridge	bridge;
++	struct device		*dev;
++	struct drm_bridge	*next_bridge;
++	void __iomem		*regs;
++};
++
++static inline struct imx_hdmi_pvi *
++to_imx_hdmi_pvi(struct drm_bridge *bridge)
++{
++	return container_of(bridge, struct imx_hdmi_pvi, bridge);
++}
++
++static int imx_hdmi_pvi_bridge_attach(struct drm_bridge *bridge,
++				enum drm_bridge_attach_flags flags)
++{
++	struct imx_hdmi_pvi *pvi = to_imx_hdmi_pvi(bridge);
++
++	return drm_bridge_attach(bridge->encoder, pvi->next_bridge, bridge, flags);
++}
++
++static void imx_hdmi_pvi_bridge_enable(struct drm_bridge *bridge,
++				       struct drm_bridge_state *bridge_state)
++{
++	struct drm_atomic_state *state = bridge_state->base.state;
++	struct imx_hdmi_pvi *pvi = to_imx_hdmi_pvi(bridge);
++	struct drm_connector_state *conn_state;
++	const struct drm_display_mode *mode;
++	struct drm_crtc_state *crtc_state;
++	struct drm_connector *connector;
++	u32 bus_flags, val;
++
++	connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
++	conn_state = drm_atomic_get_new_connector_state(state, connector);
++	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
++
++	if (WARN_ON(pm_runtime_resume_and_get(pvi->dev)))
++		return;
++
++	mode = &crtc_state->adjusted_mode;
++
++	val = PVI_CTL_INPUT_LCDIF;
++
++	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
++		val |= PVI_CTL_OP_VSYNC_POL | PVI_CTL_INP_VSYNC_POL;
++
++	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
++		val |= PVI_CTL_OP_HSYNC_POL | PVI_CTL_INP_HSYNC_POL;
++
++	if (pvi->next_bridge->timings)
++		bus_flags = pvi->next_bridge->timings->input_bus_flags;
++	else if (bridge_state)
++		bus_flags = bridge_state->input_bus_cfg.flags;
++
++	if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
++		val |= PVI_CTL_OP_DE_POL | PVI_CTL_INP_DE_POL;
++
++	writel(val, pvi->regs + HTX_PVI_CTL);
++	val |= PVI_CTL_EN;
++	writel(val, pvi->regs + HTX_PVI_CTL);
++}
++
++static void imx_hdmi_pvi_bridge_disable(struct drm_bridge *bridge,
++					struct drm_bridge_state *bridge_state)
++{
++	struct imx_hdmi_pvi *pvi = to_imx_hdmi_pvi(bridge);
++
++	writel(0x0, pvi->regs + HTX_PVI_CTL);
++
++	pm_runtime_put(pvi->dev);
++}
++
++static u32 *pvi_bridge_get_input_bus_fmts(struct drm_bridge *bridge,
++					  struct drm_bridge_state *bridge_state,
++					  struct drm_crtc_state *crtc_state,
++					  struct drm_connector_state *conn_state,
++					  u32 output_fmt,
++					  unsigned int *num_input_fmts)
++{
++	struct imx_hdmi_pvi *pvi = to_imx_hdmi_pvi(bridge);
++	struct drm_bridge *next_bridge = pvi->next_bridge;
++	struct drm_bridge_state *next_state;
++
++	if (!next_bridge->funcs->atomic_get_input_bus_fmts)
++		return 0;
++
++	next_state = drm_atomic_get_new_bridge_state(crtc_state->state,
++						     next_bridge);
++
++	return next_bridge->funcs->atomic_get_input_bus_fmts(next_bridge,
++							     next_state,
++							     crtc_state,
++							     conn_state,
++							     output_fmt,
++							     num_input_fmts);
++}
++
++static const struct drm_bridge_funcs imx_hdmi_pvi_bridge_funcs = {
++	.attach		= imx_hdmi_pvi_bridge_attach,
++	.atomic_enable	= imx_hdmi_pvi_bridge_enable,
++	.atomic_disable	= imx_hdmi_pvi_bridge_disable,
++	.atomic_get_input_bus_fmts = pvi_bridge_get_input_bus_fmts,
++	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
++	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
++	.atomic_reset = drm_atomic_helper_bridge_reset,
++};
++
++static int imx_hdmi_pvi_probe(struct platform_device *pdev)
++{
++	struct device_node *remote;
++	struct imx_hdmi_pvi *pvi;
++
++	pvi = devm_kzalloc(&pdev->dev, sizeof(*pvi), GFP_KERNEL);
++	if (!pvi)
++		return -ENOMEM;
++
++	platform_set_drvdata(pdev, pvi);
++	pvi->dev = &pdev->dev;
++
++	pvi->regs = devm_platform_ioremap_resource(pdev, 0);
++	if (IS_ERR(pvi->regs))
++		return PTR_ERR(pvi->regs);
++
++	/* Get the next bridge in the pipeline. */
++	remote = of_graph_get_remote_node(pdev->dev.of_node, 1, -1);
++	if (!remote)
++		return -EINVAL;
++
++	pvi->next_bridge = of_drm_find_bridge(remote);
++	of_node_put(remote);
++
++	if (!pvi->next_bridge)
++		return dev_err_probe(&pdev->dev, -EPROBE_DEFER,
++				     "could not find next bridge\n");
++
++	/* Register the bridge. */
++	pvi->bridge.funcs = &imx_hdmi_pvi_bridge_funcs;
++	pvi->bridge.of_node = pdev->dev.of_node;
++	pvi->bridge.timings = pvi->next_bridge->timings;
++
++	drm_bridge_add(&pvi->bridge);
++
++	pm_runtime_enable(&pdev->dev);
++
++	return 0;
++}
++
++static int imx_hdmi_pvi_remove(struct platform_device *pdev)
++{
++	struct imx_hdmi_pvi *pvi = platform_get_drvdata(pdev);
++
++	drm_bridge_remove(&pvi->bridge);
++
++	return 0;
++}
++
++static const struct of_device_id imx_hdmi_pvi_match[] = {
++	{
++		.compatible = "fsl,imx8mp-hdmi-pvi",
++	}, {
++		/* sentinel */
++	},
++};
++MODULE_DEVICE_TABLE(of, imx_hdmi_pvi_match);
++
++static struct platform_driver imx_hdmi_pvi_driver = {
++	.probe	= imx_hdmi_pvi_probe,
++	.remove	= imx_hdmi_pvi_remove,
++	.driver		= {
++		.name = "imx-hdmi-pvi",
++		.of_match_table	= imx_hdmi_pvi_match,
++	},
++};
++module_platform_driver(imx_hdmi_pvi_driver);
++
++MODULE_DESCRIPTION("i.MX8MP HDMI TX Parallel Video Interface bridge driver");
++MODULE_LICENSE("GPL");
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0006-arm64-dts-imx8mp-add-HDMI-irqsteer.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0006-arm64-dts-imx8mp-add-HDMI-irqsteer.patch
new file mode 100644
index 0000000000000000000000000000000000000000..909b6de68aae76903a73e9ee8a5842aad414c7b6
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0006-arm64-dts-imx8mp-add-HDMI-irqsteer.patch
@@ -0,0 +1,36 @@
+From 613178332bc0953c9593fab7f2526c86ae6d8d35 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Wed, 30 Mar 2022 18:41:09 +0200
+Subject: [PATCH 06/11] arm64: dts: imx8mp: add HDMI irqsteer
+
+The HDMI irqsteer is a secondary interrupt controller within the HDMI
+subsystem that maps all HDMI peripheral IRQs into a single upstream
+IRQ line.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+---
+ arch/arm64/boot/dts/freescale/imx8mp.dtsi | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
++++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+@@ -1357,6 +1357,19 @@
+ 				intf_mode = <&gpr 0x4>;
+ 				status = "disabled";
+ 			};
++
++			irqsteer_hdmi: interrupt-controller@32fc2000 {
++				compatible = "fsl,imx-irqsteer";
++				reg = <0x32fc2000 0x44>;
++				interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>;
++				interrupt-controller;
++				#interrupt-cells = <1>;
++				fsl,channel = <1>;
++				fsl,num-irqs = <64>;
++				clocks = <&clk IMX8MP_CLK_HDMI_APB>;
++				clock-names = "ipg";
++				power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_IRQSTEER>;
++			};
+ 		};
+ 
+ 		aips5: bus@30c00000 {
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0007-dt-bindings-display-imx-add-binding-for-i.MX8MP-HDMI.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0007-dt-bindings-display-imx-add-binding-for-i.MX8MP-HDMI.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a1cc6d8a996b6941dc1c813869ff7763aaca5074
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0007-dt-bindings-display-imx-add-binding-for-i.MX8MP-HDMI.patch
@@ -0,0 +1,99 @@
+From 6f262cac5afa2366d1ab78b95c23076a18f68d7c Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Tue, 5 Apr 2022 21:09:13 +0200
+Subject: [PATCH 07/11] dt-bindings: display: imx: add binding for i.MX8MP HDMI
+ TX
+
+The HDMI TX controller on the i.MX8MP SoC is a Synopsys designware IP
+core with a little bit of SoC integration around it.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+Tested-by: Marek Vasut <marex@denx.de>
+---
+ .../bindings/display/imx/fsl,imx8mp-hdmi.yaml | 74 +++++++++++++++++++
+ 1 file changed, 74 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi.yaml
+
+diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi.yaml
+new file mode 100644
+index 000000000000..14f7cd47209c
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi.yaml
+@@ -0,0 +1,74 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/imx/fsl,imx8mp-hdmi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Freescale i.MX8MP DWC HDMI TX Encoder
++
++maintainers:
++  - Lucas Stach <l.stach@pengutronix.de>
++
++description: |
++  The HDMI transmitter is a Synopsys DesignWare HDMI 2.0 TX controller IP.
++
++allOf:
++  - $ref: ../bridge/synopsys,dw-hdmi.yaml#
++
++properties:
++  compatible:
++    enum:
++      - fsl,imx8mp-hdmi
++
++  reg:
++    maxItems: 1
++
++  reg-io-width:
++    const: 1
++
++  clocks:
++    maxItems: 5
++
++  clock-names:
++    items:
++      - {}
++      - {}
++      - const: cec
++      - const: pix
++      - const: fdcc
++
++  interrupts:
++    maxItems: 1
++
++  power-domains:
++    maxItems: 1
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - clock-names
++  - interrupts
++  - power-domains
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/interrupt-controller/irq.h>
++    #include <dt-bindings/clock/imx8mp-clock.h>
++    #include <dt-bindings/power/imx8mp-power.h>
++
++    hdmi@32fd8000 {
++        compatible = "fsl,imx8mp-hdmi";
++        reg = <0x32fd8000 0x7eff>;
++        interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
++        clocks = <&clk IMX8MP_CLK_HDMI_APB>,
++                 <&clk IMX8MP_CLK_HDMI_REF_266M>,
++                 <&clk IMX8MP_CLK_HDMI_FDCC_TST>,
++                 <&clk IMX8MP_CLK_32K>,
++                 <&hdmi_tx_phy>;
++        clock-names = "iahb", "isfr", "fdcc", "cec", "pix";
++        power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_HDMI_TX>;
++        reg-io-width = <1>;
++    };
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0008-drm-imx-add-bridge-wrapper-driver-for-i.MX8MP-DWC-HD.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0008-drm-imx-add-bridge-wrapper-driver-for-i.MX8MP-DWC-HD.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d89d5d100d3c7f76931225bba4ddab96c83f2137
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0008-drm-imx-add-bridge-wrapper-driver-for-i.MX8MP-DWC-HD.patch
@@ -0,0 +1,177 @@
+From 854ae91ceec110b553302ba5954996e0ca3996a3 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Fri, 1 Apr 2022 22:11:44 +0200
+Subject: [PATCH 08/11] drm/imx: add bridge wrapper driver for i.MX8MP DWC HDMI
+
+Add a simple wrapper driver for the DWC HDMI bridge driver that
+implements the few bits that are necessary to abstract the i.MX8MP
+SoC integration.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+Tested-by: Marek Vasut <marex@denx.de>
+---
+ drivers/gpu/drm/bridge/imx/Kconfig       |   9 ++
+ drivers/gpu/drm/bridge/imx/imx8mp-hdmi.c | 141 +++++++++++++++++++++++
+ 2 files changed, 150 insertions(+)
+ create mode 100644 drivers/gpu/drm/bridge/imx/imx8mp-hdmi.c
+
+--- a/drivers/gpu/drm/bridge/imx/Kconfig
++++ b/drivers/gpu/drm/bridge/imx/Kconfig
+@@ -67,4 +67,13 @@ config DRM_IMX8MP_HDMI_PVI
+ 	  Choose this to enable support for the internal HDMI TX Parallel
+ 	  Video Interface found on the i.MX8MP SoC.
+ 
++config DRM_IMX8MP_DW_HDMI_BRIDGE
++	tristate "i.MX8MP HDMI bridge support"
++	depends on OF
++	depends on COMMON_CLK
++	select DRM_DW_HDMI
++	help
++	  Choose this to enable support for the internal HDMI encoder found
++	  on the i.MX8MP SoC.
++
+ endif # ARCH_MXC || COMPILE_TEST
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi.c
+@@ -0,0 +1,141 @@
++// SPDX-License-Identifier: GPL-2.0+
++
++/*
++ * Copyright (C) 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
++ */
++
++#include <drm/bridge/dw_hdmi.h>
++#include <drm/drm_modes.h>
++#include <linux/clk.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++struct imx_hdmi {
++	struct dw_hdmi_plat_data plat_data;
++	struct dw_hdmi *dw_hdmi;
++	struct clk *pixclk;
++	struct clk *fdcc;
++};
++
++static enum drm_mode_status
++imx8mp_hdmi_mode_valid(struct dw_hdmi *dw_hdmi, void *data,
++		       const struct drm_display_info *info,
++		       const struct drm_display_mode *mode)
++{
++	struct imx_hdmi *hdmi = (struct imx_hdmi *)data;
++
++	if (mode->clock < 13500)
++		return MODE_CLOCK_LOW;
++
++	if (mode->clock > 297000)
++		return MODE_CLOCK_HIGH;
++
++	if (clk_round_rate(hdmi->pixclk, mode->clock * 1000) !=
++	    mode->clock * 1000)
++		return MODE_CLOCK_RANGE;
++
++	/* 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;
++
++	return MODE_OK;
++}
++
++static int imx8mp_hdmi_phy_init(struct dw_hdmi *dw_hdmi, void *data,
++				const struct drm_display_info *display,
++				const struct drm_display_mode *mode)
++{
++	return 0;
++}
++
++static void imx8mp_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data)
++{
++}
++
++static const struct dw_hdmi_phy_ops imx8mp_hdmi_phy_ops = {
++	.init		= imx8mp_hdmi_phy_init,
++	.disable	= imx8mp_hdmi_phy_disable,
++	.read_hpd	= dw_hdmi_phy_read_hpd,
++	.update_hpd	= dw_hdmi_phy_update_hpd,
++	.setup_hpd	= dw_hdmi_phy_setup_hpd,
++};
++
++static int imx_dw_hdmi_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct dw_hdmi_plat_data *plat_data;
++	struct imx_hdmi *hdmi;
++	int ret;
++
++	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
++	if (!hdmi)
++		return -ENOMEM;
++
++	plat_data = &hdmi->plat_data;
++
++	hdmi->pixclk = devm_clk_get(dev, "pix");
++	if (IS_ERR(hdmi->pixclk))
++		return dev_err_probe(dev, PTR_ERR(hdmi->pixclk),
++				     "Unable to get pixel clock\n");
++
++	hdmi->fdcc = devm_clk_get(dev, "fdcc");
++	if (IS_ERR(hdmi->fdcc))
++		return dev_err_probe(dev, PTR_ERR(hdmi->fdcc),
++				     "Unable to get FDCC clock\n");
++
++	ret = clk_prepare_enable(hdmi->fdcc);
++	if (ret)
++		return dev_err_probe(dev, ret, "Unable to enable FDCC clock\n");
++
++	plat_data->mode_valid = imx8mp_hdmi_mode_valid;
++	plat_data->phy_ops = &imx8mp_hdmi_phy_ops;
++	plat_data->phy_name = "SAMSUNG HDMI TX PHY";
++	plat_data->priv_data = hdmi;
++
++	hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data);
++	if (IS_ERR(hdmi->dw_hdmi))
++		return PTR_ERR(hdmi->dw_hdmi);
++
++	/*
++	 * Just release PHY core from reset, all other power management is done
++	 * by the PHY driver.
++	 */
++	dw_hdmi_phy_gen1_reset(hdmi->dw_hdmi);
++
++	platform_set_drvdata(pdev, hdmi);
++
++	return 0;
++}
++
++static int imx_dw_hdmi_remove(struct platform_device *pdev)
++{
++	struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
++
++	dw_hdmi_remove(hdmi->dw_hdmi);
++
++	clk_disable_unprepare(hdmi->fdcc);
++
++	return 0;
++}
++
++static const struct of_device_id imx_dw_hdmi_of_table[] = {
++	{ .compatible = "fsl,imx8mp-hdmi" },
++	{ /* Sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, imx_dw_hdmi_of_table);
++
++static struct platform_driver im_dw_hdmi_platform_driver = {
++	.probe		= imx_dw_hdmi_probe,
++	.remove		= imx_dw_hdmi_remove,
++	.driver		= {
++		.name	= "imx-dw-hdmi",
++		.of_match_table = imx_dw_hdmi_of_table,
++	},
++};
++
++module_platform_driver(im_dw_hdmi_platform_driver);
++
++MODULE_DESCRIPTION("i.MX8M HDMI encoder driver");
++MODULE_LICENSE("GPL");
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0009-dt-bindings-phy-add-binding-for-the-i.MX8MP-HDMI-PHY.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0009-dt-bindings-phy-add-binding-for-the-i.MX8MP-HDMI-PHY.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3c8c55afb779d9729ae2ca4ba8f63b00bb22062d
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0009-dt-bindings-phy-add-binding-for-the-i.MX8MP-HDMI-PHY.patch
@@ -0,0 +1,85 @@
+From 73c43eb4b3bff11a47a6829aad3173c8007c18a2 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Tue, 5 Apr 2022 20:54:27 +0200
+Subject: [PATCH 09/11] dt-bindings: phy: add binding for the i.MX8MP HDMI PHY
+
+Add a DT binding for the HDMI PHY found on the i.MX8MP SoC.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+Tested-by: Marek Vasut <marex@denx.de>
+---
+ .../bindings/phy/fsl,imx8mp-hdmi-phy.yaml     | 62 +++++++++++++++++++
+ 1 file changed, 62 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/phy/fsl,imx8mp-hdmi-phy.yaml
+
+diff --git a/Documentation/devicetree/bindings/phy/fsl,imx8mp-hdmi-phy.yaml b/Documentation/devicetree/bindings/phy/fsl,imx8mp-hdmi-phy.yaml
+new file mode 100644
+index 000000000000..bc21c073e92a
+--- /dev/null
++++ b/Documentation/devicetree/bindings/phy/fsl,imx8mp-hdmi-phy.yaml
+@@ -0,0 +1,62 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/phy/fsl,imx8mp-hdmi-phy.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Freescale i.MX8MP HDMI PHY binding
++
++maintainers:
++  - Lucas Stach <l.stach@pengutronix.de>
++
++properties:
++  compatible:
++    enum:
++      - fsl,imx8mp-hdmi-phy
++
++  reg:
++    maxItems: 1
++
++  "#clock-cells":
++    const: 0
++
++  clocks:
++    minItems: 2
++    maxItems: 2
++
++  clock-names:
++    items:
++      - const: apb
++      - const: ref
++
++  "#phy-cells":
++    const: 0
++
++  power-domains:
++    maxItems: 1
++
++required:
++  - compatible
++  - reg
++  - "#clock-cells"
++  - clocks
++  - clock-names
++  - power-domains
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/imx8mp-clock.h>
++    #include <dt-bindings/power/imx8mp-power.h>
++
++    phy@32fdff00 {
++        compatible = "fsl,imx8mp-hdmi-phy";
++        reg = <0x32fdff00 0x100>;
++        clocks = <&clk IMX8MP_CLK_HDMI_APB>,
++                 <&clk IMX8MP_CLK_HDMI_24M>;
++        clock-names = "apb", "ref";
++        power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_HDMI_TX_PHY>;
++        #clock-cells = <0>;
++        #phy-cells = <0>;
++    };
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0010-arm64-dts-imx8mp-add-HDMI-power-domains.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0010-arm64-dts-imx8mp-add-HDMI-power-domains.patch
new file mode 100644
index 0000000000000000000000000000000000000000..565bb9aaf2fe3681fedcccab8b03b739a071062c
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0010-arm64-dts-imx8mp-add-HDMI-power-domains.patch
@@ -0,0 +1,64 @@
+From d1f84163b33b4fed1a4638f5d51abefc5fe0ac25 Mon Sep 17 00:00:00 2001
+From: Lucas Stach <l.stach@pengutronix.de>
+Date: Wed, 30 Mar 2022 18:24:43 +0200
+Subject: [PATCH 10/11] arm64: dts: imx8mp: add HDMI power-domains
+
+This adds the PGC and HDMI blk-ctrl nodes providing power control for
+HDMI subsystem peripherals.
+
+Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
+---
+ arch/arm64/boot/dts/freescale/imx8mp.dtsi | 35 +++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
++++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+@@ -841,6 +841,23 @@
+ 							 <&clk IMX8MP_CLK_MEDIA_APB_ROOT>;
+ 					};
+ 
++					pgc_hdmimix: power-domains@14 {
++						#power-domain-cells = <0>;
++						reg = <IMX8MP_POWER_DOMAIN_HDMIMIX>;
++						clocks = <&clk IMX8MP_CLK_HDMI_ROOT>,
++							 <&clk IMX8MP_CLK_HDMI_APB>;
++						assigned-clocks = <&clk IMX8MP_CLK_HDMI_AXI>,
++								  <&clk IMX8MP_CLK_HDMI_APB>;
++						assigned-clock-parents = <&clk IMX8MP_SYS_PLL2_500M>,
++									 <&clk IMX8MP_SYS_PLL1_133M>;
++						assigned-clock-rates = <500000000>, <133000000>;
++					};
++
++					pgc_hdmi_phy: power-domains@15 {
++						#power-domain-cells = <0>;
++						reg = <IMX8MP_POWER_DOMAIN_HDMI_PHY>;
++					};
++
+ 					pgc_mipi_phy2: power-domain@16 {
+ 						#power-domain-cells = <0>;
+ 						reg = <IMX8MP_POWER_DOMAIN_MIPI_PHY2>;
+@@ -1370,6 +1387,24 @@
+ 				clock-names = "ipg";
+ 				power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_IRQSTEER>;
+ 			};
++
++			hdmi_blk_ctrl: blk-ctrl@32fc0000 {
++				compatible = "fsl,imx8mp-hdmi-blk-ctrl", "syscon";
++				reg = <0x32fc0000 0x23c>;
++				clocks = <&clk IMX8MP_CLK_HDMI_APB>,
++					 <&clk IMX8MP_CLK_HDMI_ROOT>,
++					 <&clk IMX8MP_CLK_HDMI_REF_266M>,
++					 <&clk IMX8MP_CLK_HDMI_24M>;
++				clock-names = "apb", "axi", "ref_266m", "ref_24m";
++				power-domains = <&pgc_hdmimix>, <&pgc_hdmimix>,
++						<&pgc_hdmimix>, <&pgc_hdmimix>,
++						<&pgc_hdmimix>, <&pgc_hdmimix>,
++						<&pgc_hdmimix>, <&pgc_hdmi_phy>;
++				power-domain-names = "bus", "irqsteer", "lcdif",
++						     "pai", "pvi", "trng",
++						     "hdmi-tx", "hdmi-tx-phy";
++				#power-domain-cells = <1>;
++			};
+ 		};
+ 
+ 		aips5: bus@30c00000 {
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0011-hdmi-pvi-makefile.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0011-hdmi-pvi-makefile.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0b3d8a73ebaba1f83d2de0ac62f12a1f7789a064
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/hdmi/0011-hdmi-pvi-makefile.patch
@@ -0,0 +1,18 @@
+From df862bd710dd94cea3848176dc13e30ba3d87fbd Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 22:14:11 +0200
+Subject: [PATCH 11/11] hdmi-pvi-makefile
+
+---
+ drivers/gpu/drm/bridge/imx/Makefile | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/bridge/imx/Makefile
++++ b/drivers/gpu/drm/bridge/imx/Makefile
+@@ -5,3 +5,6 @@ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_COMBINER)
+ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK) += imx8qxp-pixel-link.o
+ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI) += imx8qxp-pxl2dpi.o
+ obj-$(CONFIG_DRM_IMX93_MIPI_DSI) += imx93-mipi-dsi.o
++
++obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE) += imx8mp-hdmi.o
++obj-$(CONFIG_DRM_IMX8MP_HDMI_PVI) += imx8mp-hdmi-pvi.o
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0001-sdhci-add-no-sd-uhs-sdr104-devicetree-property.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0001-sdhci-add-no-sd-uhs-sdr104-devicetree-property.patch
new file mode 100644
index 0000000000000000000000000000000000000000..84494723065cf40744cbba3b3e0da93692093c8e
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0001-sdhci-add-no-sd-uhs-sdr104-devicetree-property.patch
@@ -0,0 +1,45 @@
+From 9f11ce621bccb07064ccc9c564fb0cb20612e1cd Mon Sep 17 00:00:00 2001
+From: Troy Kisky <troy.kisky@boundarydevices.com>
+Date: Fri, 1 Dec 2017 18:32:51 -0700
+Subject: [PATCH 1/3] sdhci: add no-sd-uhs-sdr104 devicetree property
+
+Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
+---
+ drivers/mmc/host/sdhci.c | 18 +++++++++++++-----
+ 1 file changed, 13 insertions(+), 5 deletions(-)
+
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -16,6 +16,7 @@
+ #include <linux/highmem.h>
+ #include <linux/io.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/slab.h>
+ #include <linux/scatterlist.h>
+@@ -4550,12 +4551,19 @@ int sdhci_setup_host(struct sdhci_host *
+ 
+ 	/* SDR104 supports also implies SDR50 support */
+ 	if (host->caps1 & SDHCI_SUPPORT_SDR104) {
++		struct device_node *np;
++
+ 		mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
+-		/* SD3.0: SDR104 is supported so (for eMMC) the caps2
+-		 * field can be promoted to support HS200.
+-		 */
+-		if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
+-			mmc->caps2 |= MMC_CAP2_HS200;
++		np = mmc->parent->of_node;
++		if (of_property_read_bool(np, "no-sd-uhs-sdr104")) {
++			mmc->caps &= ~MMC_CAP_UHS_SDR104;
++		} else {
++			/* SD3.0: SDR104 is supported so (for eMMC) the caps2
++			 * field can be promoted to support HS200.
++			 */
++			if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
++				mmc->caps2 |= MMC_CAP2_HS200;
++		}
+ 	} else if (host->caps1 & SDHCI_SUPPORT_SDR50) {
+ 		mmc->caps |= MMC_CAP_UHS_SDR50;
+ 	}
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0002-During-the-card-init-the-host-side-sometimes-may-nee.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0002-During-the-card-init-the-host-side-sometimes-may-nee.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b451da129f6959ec019730a07802f6abcfccc6bd
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0002-During-the-card-init-the-host-side-sometimes-may-nee.patch
@@ -0,0 +1,109 @@
+From 59e34fd41e32a11bd05494c44402ff202ef9e570 Mon Sep 17 00:00:00 2001
+From: Haibo Chen <haibo.chen@xxxxxxx>
+Date: Sun, 9 Jul 2023 22:16:01 +0200
+Subject: [PATCH 2/3] During the card init, the host side sometimes may need to
+ distinguish the card type to handle accordingly. So need to give host->card
+ value earlier.
+
+Signed-off-by: Haibo Chen <haibo.chen@xxxxxxx>
+---
+ drivers/mmc/core/mmc.c  |  9 +++++----
+ drivers/mmc/core/sd.c   |  7 +++++--
+ drivers/mmc/core/sdio.c | 10 ++++++----
+ 3 files changed, 16 insertions(+), 10 deletions(-)
+
+--- a/drivers/mmc/core/mmc.c
++++ b/drivers/mmc/core/mmc.c
+@@ -1657,6 +1657,8 @@ static int mmc_init_card(struct mmc_host
+ 			goto err;
+ 		}
+ 
++		host->card = card;
++
+ 		card->ocr = ocr;
+ 		card->type = MMC_TYPE_MMC;
+ 		card->rca = 1;
+@@ -1931,14 +1933,13 @@ static int mmc_init_card(struct mmc_host
+ 		goto free_card;
+ 	}
+ 
+-	if (!oldcard)
+-		host->card = card;
+-
+ 	return 0;
+ 
+ free_card:
+-	if (!oldcard)
++	if (!oldcard) {
+ 		mmc_remove_card(card);
++		host->card = NULL;
++	}
+ err:
+ 	return err;
+ }
+--- a/drivers/mmc/core/sd.c
++++ b/drivers/mmc/core/sd.c
+@@ -1431,6 +1431,8 @@ retry:
+ 		if (IS_ERR(card))
+ 			return PTR_ERR(card);
+ 
++		host->card = card;
++
+ 		card->ocr = ocr;
+ 		card->type = MMC_TYPE_SD;
+ 		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+@@ -1577,12 +1579,13 @@ cont:
+ 		goto free_card;
+ 	}
+ 
+-	host->card = card;
+ 	return 0;
+ 
+ free_card:
+-	if (!oldcard)
++	if (!oldcard) {
+ 		mmc_remove_card(card);
++		host->card = NULL;
++	}
+ 
+ 	return err;
+ }
+--- a/drivers/mmc/core/sdio.c
++++ b/drivers/mmc/core/sdio.c
+@@ -699,6 +699,9 @@ try_again:
+ 	if (IS_ERR(card))
+ 		return PTR_ERR(card);
+ 
++	if (!oldcard)
++		host->card = card;
++
+ 	if ((rocr & R4_MEMORY_PRESENT) &&
+ 	    mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
+ 		card->type = MMC_TYPE_SD_COMBO;
+@@ -800,8 +803,6 @@ try_again:
+ 
+ 		if (oldcard)
+ 			mmc_remove_card(card);
+-		else
+-			host->card = card;
+ 
+ 		return 0;
+ 	}
+@@ -898,14 +899,15 @@ try_again:
+ 		goto remove;
+ 	}
+ 
+-	host->card = card;
+ 	return 0;
+ 
+ mismatch:
+ 	pr_debug("%s: Perhaps the card was replaced\n", mmc_hostname(host));
+ remove:
+-	if (oldcard != card)
++	if (oldcard != card) {
+ 		mmc_remove_card(card);
++		host->card = NULL;
++	}
+ 	return err;
+ }
+ 
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0003-USDHC-IP-has-one-limitation-the-tuning-circuit-can-t.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0003-USDHC-IP-has-one-limitation-the-tuning-circuit-can-t.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3b16d315ac216e3ca822df0513d1d2ab66f77a11
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/mmc-sdio/0003-USDHC-IP-has-one-limitation-the-tuning-circuit-can-t.patch
@@ -0,0 +1,44 @@
+From c39839e4da6c0a0f24ff2532a793b5829ce28e82 Mon Sep 17 00:00:00 2001
+From: Haibo Chen <haibo.chen@xxxxxxx>
+Date: Sun, 9 Jul 2023 22:16:02 +0200
+Subject: [PATCH 3/3] USDHC IP has one limitation: the tuning circuit can't
+ handle the async sdio device interrupt correctly. When sdio device use 4 data
+ lines, async sdio interrupt will use the shared DAT[1], if enable auto tuning
+ circuit to check these 4 data lines, include the DAT[1], this circuit will
+ detect this interrupt, take this as data on DAT[1], and adjust the delay cell
+ wrongly, finally will cause the DATA/CMD CRC error. So for SDIO device, only
+ enable DAT[0] and CMD line for auto tuning.
+
+Signed-off-by: Haibo Chen <haibo.chen@xxxxxxx>
+---
+ drivers/mmc/host/sdhci-esdhc-imx.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
+index 03fe21a89021..de7030a09711 100644
+--- a/drivers/mmc/host/sdhci-esdhc-imx.c
++++ b/drivers/mmc/host/sdhci-esdhc-imx.c
+@@ -475,6 +475,20 @@ static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
+ 	if (imx_data->init_card_type == MMC_TYPE_SDIO)
+ 		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
+ 
++	/*
++	 * For USDHC, auto tuning circuit can not handle the async sdio
++	 * device interrupt correctly. When sdio device use 4 data lines,
++	 * async sdio interrupt will use the shared DAT[1], if enable auto
++	 * tuning circuit check these 4 data lines, include the DAT[1],
++	 * this circuit will detect this interrupt, take this as a data on
++	 * DAT[1], and adjust the delay cell wrongly.
++	 * This is the hardware design limitation, to avoid this, for sdio
++	 * device, config the auto tuning circuit only check DAT[0] and CMD
++	 * line.
++	 */
++	if (!host->mmc->card && mmc_card_sdio(host->mmc->card))
++		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
++
+ 	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
+ 			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
+ 			ESDHC_VEND_SPEC2);
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/imx8mp-mnt-pocket-reform/pocket-panel/0001-panel-jdi-lt070me05000-pocket-reform.patch b/linux/patches6.7/imx8mp-mnt-pocket-reform/pocket-panel/0001-panel-jdi-lt070me05000-pocket-reform.patch
new file mode 100644
index 0000000000000000000000000000000000000000..435f4fcd1fc2d0032967c02bb3cd52c3002f72e4
--- /dev/null
+++ b/linux/patches6.7/imx8mp-mnt-pocket-reform/pocket-panel/0001-panel-jdi-lt070me05000-pocket-reform.patch
@@ -0,0 +1,309 @@
+From 0c540508d878b5cc4fbe4943e0073b4dd61fb0e9 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 22:22:08 +0200
+Subject: [PATCH] panel-jdi-lt070me05000-pocket-reform
+
+---
+ .../gpu/drm/panel/panel-jdi-lt070me05000.c    | 176 +++++-------------
+ 1 file changed, 45 insertions(+), 131 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
++++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
+@@ -56,11 +56,8 @@ static int jdi_panel_init(struct jdi_pan
+ 
+ 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+ 
+-	ret = mipi_dsi_dcs_soft_reset(dsi);
+-	if (ret < 0)
+-		return ret;
+-
+-	usleep_range(10000, 20000);
++	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
++				 (u8[]){ 0x2c }, 1);
+ 
+ 	ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4);
+ 	if (ret < 0) {
+@@ -68,38 +65,11 @@ static int jdi_panel_init(struct jdi_pan
+ 		return ret;
+ 	}
+ 
+-	ret = mipi_dsi_dcs_set_column_address(dsi, 0, jdi->mode->hdisplay - 1);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to set column address: %d\n", ret);
+-		return ret;
+-	}
+-
+-	ret = mipi_dsi_dcs_set_page_address(dsi, 0, jdi->mode->vdisplay - 1);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to set page address: %d\n", ret);
+-		return ret;
+-	}
+-
+-	/*
+-	 * BIT(5) BCTRL = 1 Backlight Control Block On, Brightness registers
+-	 *                  are active
+-	 * BIT(3) BL = 1    Backlight Control On
+-	 * BIT(2) DD = 0    Display Dimming is Off
+-	 */
+-	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+-				 (u8[]){ 0x24 }, 1);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to write control display: %d\n", ret);
+-		return ret;
+-	}
+-
+-	/* CABC off */
+-	ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
+-				 (u8[]){ 0x00 }, 1);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to set cabc off: %d\n", ret);
+-		return ret;
+-	}
++	// write_memory_start
++	ret = mipi_dsi_generic_write(dsi, (u8[])
++						 {0x2c}, 1);
++	ret = mipi_dsi_generic_write(dsi, (u8[])
++						 {0x00}, 0);
+ 
+ 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ 	if (ret < 0) {
+@@ -107,69 +77,39 @@ static int jdi_panel_init(struct jdi_pan
+ 		return ret;
+ 	}
+ 
+-	msleep(120);
++	// required delay
++	msleep(200);
+ 
++	// MCAP off
+ 	ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2);
+ 	if (ret < 0) {
+ 		dev_err(dev, "failed to set mcap: %d\n", ret);
+ 		return ret;
+ 	}
+ 
+-	mdelay(10);
++	// required delay
++	mdelay(200);
+ 
+ 	/* Interface setting, video mode */
+ 	ret = mipi_dsi_generic_write(dsi, (u8[])
+-				     {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6);
++						 {0xB3, 0x14, 0x08, 0x00, 0x22, 0x00}, 6);
+ 	if (ret < 0) {
+ 		dev_err(dev, "failed to set display interface setting: %d\n"
+ 			, ret);
+ 		return ret;
+ 	}
+ 
+-	mdelay(20);
+-
+-	ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to set default values for mcap: %d\n"
+-			, ret);
+-		return ret;
+-	}
+-
+-	return 0;
+-}
+-
+-static int jdi_panel_on(struct jdi_panel *jdi)
+-{
+-	struct mipi_dsi_device *dsi = jdi->dsi;
+-	struct device *dev = &jdi->dsi->dev;
+-	int ret;
++	// interface ID setting
++	ret = mipi_dsi_generic_write(dsi, (u8[])
++						 {0xb4, 0x0c}, 2);
+ 
+-	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
++	// DSI control
++	ret = mipi_dsi_generic_write(dsi, (u8[])
++						 {0xb6, 0x3a, 0xd3}, 3);
+ 
+ 	ret = mipi_dsi_dcs_set_display_on(dsi);
+-	if (ret < 0)
+-		dev_err(dev, "failed to set display on: %d\n", ret);
+-
+-	return ret;
+-}
+-
+-static void jdi_panel_off(struct jdi_panel *jdi)
+-{
+-	struct mipi_dsi_device *dsi = jdi->dsi;
+-	struct device *dev = &jdi->dsi->dev;
+-	int ret;
+ 
+-	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+-
+-	ret = mipi_dsi_dcs_set_display_off(dsi);
+-	if (ret < 0)
+-		dev_err(dev, "failed to set display off: %d\n", ret);
+-
+-	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+-	if (ret < 0)
+-		dev_err(dev, "failed to enter sleep mode: %d\n", ret);
+-
+-	msleep(100);
++	return 0;
+ }
+ 
+ static int jdi_panel_disable(struct drm_panel *panel)
+@@ -195,8 +135,6 @@ static int jdi_panel_unprepare(struct dr
+ 	if (!jdi->prepared)
+ 		return 0;
+ 
+-	jdi_panel_off(jdi);
+-
+ 	ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
+ 	if (ret < 0)
+ 		dev_err(dev, "regulator disable failed, %d\n", ret);
+@@ -238,44 +176,29 @@ static int jdi_panel_prepare(struct drm_
+ 	gpiod_set_value(jdi->enable_gpio, 1);
+ 	usleep_range(10, 20);
+ 
+-	ret = jdi_panel_init(jdi);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to init panel: %d\n", ret);
+-		goto poweroff;
+-	}
+-
+-	ret = jdi_panel_on(jdi);
+-	if (ret < 0) {
+-		dev_err(dev, "failed to set panel on: %d\n", ret);
+-		goto poweroff;
+-	}
+-
+ 	jdi->prepared = true;
+ 
+ 	return 0;
+-
+-poweroff:
+-	ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
+-	if (ret < 0)
+-		dev_err(dev, "regulator disable failed, %d\n", ret);
+-
+-	gpiod_set_value(jdi->enable_gpio, 0);
+-
+-	gpiod_set_value(jdi->reset_gpio, 1);
+-
+-	gpiod_set_value(jdi->dcdc_en_gpio, 0);
+-
+-	return ret;
+ }
+ 
+ static int jdi_panel_enable(struct drm_panel *panel)
+ {
+ 	struct jdi_panel *jdi = to_jdi_panel(panel);
++	struct device *dev = &jdi->dsi->dev;
++	int ret;
+ 
+ 	if (jdi->enabled)
+ 		return 0;
+ 
+-	backlight_enable(jdi->backlight);
++	// FIXME
++	for (int i=0; i<2; i++) {
++		ret = jdi_panel_init(jdi);
++		if (ret < 0) {
++			dev_err(dev, "failed jdi_panel_init: %d\n", ret);
++			return ret;
++		}
++	}
++	mipi_dsi_dcs_set_display_brightness(jdi->dsi, 200);
+ 
+ 	jdi->enabled = true;
+ 
+@@ -283,7 +206,7 @@ static int jdi_panel_enable(struct drm_p
+ }
+ 
+ static const struct drm_display_mode default_mode = {
+-		.clock = 155493,
++		.clock = 140000,
+ 		.hdisplay = 1200,
+ 		.hsync_start = 1200 + 48,
+ 		.hsync_end = 1200 + 48 + 32,
+@@ -296,7 +219,7 @@ static const struct drm_display_mode def
+ };
+ 
+ static int jdi_panel_get_modes(struct drm_panel *panel,
+-			       struct drm_connector *connector)
++						 struct drm_connector *connector)
+ {
+ 	struct drm_display_mode *mode;
+ 	struct jdi_panel *jdi = to_jdi_panel(panel);
+@@ -326,27 +249,19 @@ static int dsi_dcs_bl_get_brightness(str
+ 	int ret;
+ 	u16 brightness = bl->props.brightness;
+ 
+-	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+-
+-	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
+-	if (ret < 0)
+-		return ret;
+-
+-	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+-
++	// FIXME kernel oops when getting brightness via DCS
+ 	return brightness & 0xff;
+ }
+ 
+ static int dsi_dcs_bl_update_status(struct backlight_device *bl)
+ {
+-	struct mipi_dsi_device *dsi = bl_get_data(bl);
++	struct jdi_panel *jdi = bl_get_data(bl);
++	struct mipi_dsi_device *dsi = jdi->dsi;
+ 	int ret;
+ 
+ 	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+ 
+-	ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
+-	if (ret < 0)
+-		return ret;
++	mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
+ 
+ 	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+ 
+@@ -359,9 +274,9 @@ static const struct backlight_ops dsi_bl
+ };
+ 
+ static struct backlight_device *
+-drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
++drm_panel_create_dsi_backlight(struct jdi_panel *jdi)
+ {
+-	struct device *dev = &dsi->dev;
++	struct device *dev = &jdi->dsi->dev;
+ 	struct backlight_properties props;
+ 
+ 	memset(&props, 0, sizeof(props));
+@@ -369,8 +284,8 @@ drm_panel_create_dsi_backlight(struct mi
+ 	props.brightness = 255;
+ 	props.max_brightness = 255;
+ 
+-	return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+-					      &dsi_bl_ops, &props);
++	return devm_backlight_device_register(dev, dev_name(dev), dev, jdi,
++								&dsi_bl_ops, &props);
+ }
+ 
+ static const struct drm_panel_funcs jdi_panel_funcs = {
+@@ -420,7 +335,7 @@ static int jdi_panel_add(struct jdi_pane
+ 		return dev_err_probe(dev, PTR_ERR(jdi->dcdc_en_gpio),
+ 				     "cannot get dcdc-en-gpio %d\n", ret);
+ 
+-	jdi->backlight = drm_panel_create_dsi_backlight(jdi->dsi);
++	jdi->backlight = drm_panel_create_dsi_backlight(jdi);
+ 	if (IS_ERR(jdi->backlight))
+ 		return dev_err_probe(dev, PTR_ERR(jdi->backlight),
+ 				     "failed to register backlight %d\n", ret);
+@@ -446,8 +361,7 @@ static int jdi_panel_probe(struct mipi_d
+ 
+ 	dsi->lanes = 4;
+ 	dsi->format = MIPI_DSI_FMT_RGB888;
+-	dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+-			   MIPI_DSI_CLOCK_NON_CONTINUOUS;
++	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_HSE;
+ 
+ 	jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+ 	if (!jdi)
diff --git a/linux/patches6.7/imx8mq-mnt-reform2/0001-nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS.patch b/linux/patches6.7/imx8mq-mnt-reform2/0001-nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c25600bff93ffc36f1a360a96ed2150741bba411
--- /dev/null
+++ b/linux/patches6.7/imx8mq-mnt-reform2/0001-nwl-dsi-fixup-mode-only-for-LCDIF-input-not-DCSS.patch
@@ -0,0 +1,37 @@
+From e142b3299f5cf105f41b40df86973fa50f9a0fa1 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/5] 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 4a5f5c4f5dcc..fa7ebaba15ca 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.40.0
+
diff --git a/linux/patches6.7/imx8mq-mnt-reform2/0002-pci-imx6-add-support-for-internal-refclk-imx8mq.patch b/linux/patches6.7/imx8mq-mnt-reform2/0002-pci-imx6-add-support-for-internal-refclk-imx8mq.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9396810461fd55226dc58ecaa5ec611ad31915a7
--- /dev/null
+++ b/linux/patches6.7/imx8mq-mnt-reform2/0002-pci-imx6-add-support-for-internal-refclk-imx8mq.patch
@@ -0,0 +1,90 @@
+From a206ec2a1b5cce523c2345bae73271148eaa5f72 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/5] pci-imx6-add-support-for-internal-refclk-imx8mq
+
+---
+ drivers/pci/controller/dwc/pci-imx6.c | 44 +++++++++++++++++++++++++--
+ 1 file changed, 42 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pci-imx6.c
++++ b/drivers/pci/controller/dwc/pci-imx6.c
+@@ -73,6 +73,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;
+@@ -165,6 +166,40 @@ static unsigned int imx6_pcie_grp_offset
+ 	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, mode;
+@@ -322,6 +357,9 @@ static int pcie_phy_write(struct imx6_pc
+ 
+ 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 IMX8MM_EP:
+@@ -341,7 +379,8 @@ static void imx6_pcie_init_phy(struct im
+ 		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
+@@ -1329,7 +1368,8 @@ static int imx6_pcie_probe(struct platfo
+ 		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)
diff --git a/linux/patches6.7/imx8mq-mnt-reform2/0003-lcdif-fix-pcie-interference.patch b/linux/patches6.7/imx8mq-mnt-reform2/0003-lcdif-fix-pcie-interference.patch
new file mode 100644
index 0000000000000000000000000000000000000000..adc49fdf3e0a403b7221c4c3749f194d7cffe0de
--- /dev/null
+++ b/linux/patches6.7/imx8mq-mnt-reform2/0003-lcdif-fix-pcie-interference.patch
@@ -0,0 +1,65 @@
+From 325b2c0798ad9ed5529f225488576175aa360466 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/5] lcdif-fix-pcie-interference
+
+---
+ drivers/gpu/drm/mxsfb/mxsfb_kms.c | 34 ++++++++++++++++++++++++++++++-
+ 1 file changed, 33 insertions(+), 1 deletion(-)
+
+--- 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(str
+ 
+ 	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);
+ }
+@@ -671,12 +671,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,
diff --git a/linux/patches6.7/imx8mq-mnt-reform2/0004-mnt4002-imx-gpcv2-wake-smccc.patch.patch b/linux/patches6.7/imx8mq-mnt-reform2/0004-mnt4002-imx-gpcv2-wake-smccc.patch.patch
new file mode 100644
index 0000000000000000000000000000000000000000..83cf4e29bcb7ee1ab19644c5095547c566c45097
--- /dev/null
+++ b/linux/patches6.7/imx8mq-mnt-reform2/0004-mnt4002-imx-gpcv2-wake-smccc.patch.patch
@@ -0,0 +1,94 @@
+From 1636b47d4f5a89cc1791d5b2a29a0443713b4f93 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/5] 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 8a0e82067924..75196a0d8745 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.40.0
+
diff --git a/linux/patches6.7/imx8mq-mnt-reform2/0005-imx8mq-import-HDMI-driver-and-make-DCSS-compatible-w.patch b/linux/patches6.7/imx8mq-mnt-reform2/0005-imx8mq-import-HDMI-driver-and-make-DCSS-compatible-w.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c9423b5d99947cc046f42422347a3906712b6e89
--- /dev/null
+++ b/linux/patches6.7/imx8mq-mnt-reform2/0005-imx8mq-import-HDMI-driver-and-make-DCSS-compatible-w.patch
@@ -0,0 +1,7584 @@
+From e5d2b36379cb1d33cfda3c51e2a5180a9c5b82f8 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 20:36:19 +0200
+Subject: [PATCH 5/5] imx8mq: import HDMI driver and make DCSS compatible with
+ both HDMI and LCDIF
+
+---
+ drivers/gpu/drm/bridge/cadence/Kconfig        |   27 +
+ 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                   |    1 +
+ drivers/gpu/drm/imx/Makefile                  |    1 +
+ drivers/gpu/drm/imx/cdns/Kconfig              |    8 +
+ drivers/gpu/drm/imx/cdns/Makefile             |    2 +
+ drivers/gpu/drm/imx/cdns/cdn-mhdp-dp-phy.c    |  529 ++++++++
+ drivers/gpu/drm/imx/cdns/cdn-mhdp-hdmi-phy.c  |  777 ++++++++++++
+ drivers/gpu/drm/imx/cdns/cdn-mhdp-imx8qm.c    |  622 ++++++++++
+ drivers/gpu/drm/imx/cdns/cdn-mhdp-imxdrv.c    |  212 ++++
+ drivers/gpu/drm/imx/cdns/cdn-mhdp-ls1028a.c   |  111 ++
+ drivers/gpu/drm/imx/cdns/cdn-mhdp-phy.h       |  155 +++
+ drivers/gpu/drm/imx/cdns/cdns-mhdp-imx.h      |   75 ++
+ drivers/gpu/drm/imx/dcss/dcss-dev.c           |   14 +
+ drivers/gpu/drm/imx/dcss/dcss-dev.h           |    1 +
+ drivers/gpu/drm/imx/dcss/dcss-drv.c           |   98 +-
+ drivers/gpu/drm/imx/dcss/dcss-dtg.c           |   25 +-
+ drivers/gpu/drm/imx/dcss/dcss-kms.c           |   37 +-
+ 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 +++++++++++++
+ 26 files changed, 6417 insertions(+), 37 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/cdns/Kconfig
+ create mode 100644 drivers/gpu/drm/imx/cdns/Makefile
+ create mode 100644 drivers/gpu/drm/imx/cdns/cdn-mhdp-dp-phy.c
+ create mode 100644 drivers/gpu/drm/imx/cdns/cdn-mhdp-hdmi-phy.c
+ create mode 100644 drivers/gpu/drm/imx/cdns/cdn-mhdp-imx8qm.c
+ create mode 100644 drivers/gpu/drm/imx/cdns/cdn-mhdp-imxdrv.c
+ create mode 100644 drivers/gpu/drm/imx/cdns/cdn-mhdp-ls1028a.c
+ create mode 100644 drivers/gpu/drm/imx/cdns/cdn-mhdp-phy.h
+ create mode 100644 drivers/gpu/drm/imx/cdns/cdns-mhdp-imx.h
+ create mode 100644 include/drm/bridge/cdns-mhdp-cbs.h
+ create mode 100644 include/drm/bridge/cdns-mhdp-common.h
+
+--- a/drivers/gpu/drm/bridge/cadence/Kconfig
++++ b/drivers/gpu/drm/bridge/cadence/Kconfig
+@@ -47,3 +47,30 @@ 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
++	select DRM_DISPLAY_HDMI_HELPER
++
++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
+--- a/drivers/gpu/drm/bridge/cadence/Makefile
++++ b/drivers/gpu/drm/bridge/cadence/Makefile
+@@ -5,3 +5,12 @@ cdns-dsi-$(CONFIG_DRM_CDNS_DSI_J721E) +=
+ 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
+--- /dev/null
++++ b/drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
+@@ -0,0 +1,643 @@
++/*
++ * 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_print.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>
++
++#define DP_LINK_CAP_ENHANCED_FRAMING		BIT(0)
++
++/*
++ * 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.drm_dev = mhdp->bridge.base.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);
++}
++
++/* the following two functions were forward ported from linux 5.17
++   because apparently they were removed in 5.18 */
++int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
++{
++  u8 values[3];
++  int err;
++
++  memset(link, 0, sizeof(*link));
++
++  err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
++  if (err < 0)
++  	return err;
++
++  link->revision = values[0];
++  link->rate = drm_dp_bw_code_to_link_rate(values[1]);
++  link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
++
++  if (values[2] & DP_ENHANCED_FRAME_CAP)
++  	link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
++
++  return 0;
++}
++
++int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
++{
++  u8 value;
++  int err;
++
++  /* DP_SET_POWER register is only available on DPCD v1.1 and later */
++  if (link->revision < 0x11)
++  	return 0;
++
++  err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
++  if (err < 0)
++  	return err;
++
++  value &= ~DP_SET_POWER_MASK;
++  value |= DP_SET_POWER_D0;
++
++  err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
++  if (err < 0)
++  	return err;
++
++  /*
++   * According to the DP 1.1 specification, a "Sink Device must exit the
++   * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
++   * Control Field" (register 0x600).
++   */
++  usleep_range(1000, 2000);
++
++  return 0;
++}
++
++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 const struct drm_display_mode mnt_reform_innolux_n125hce_gn1_mode = {
++	 .clock = 162000,
++	 .hdisplay = 1920,
++	 .hsync_start = 1920 + 40,
++	 .hsync_end = 1920 + 40 + 40,
++	 .htotal = 1920 + 40 + 40 + 80,
++	 .vdisplay = 1080,
++	 .vsync_start = 1080 + 4,
++	 .vsync_end = 1080 + 4 + 4,
++	 .vtotal = 1080 + 4 + 4 + 24,
++	 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
++	 .type = DRM_MODE_TYPE_DRIVER
++};
++
++static int cdns_dp_connector_get_modes(struct drm_connector *connector)
++{
++	 /* HACK for MNT Reform: the display's EDID contains a mode that is unusable, so override it */
++	 drm_mode_probed_add(connector, drm_mode_duplicate(connector->dev, &mnt_reform_innolux_n125hce_gn1_mode));
++	 return 1;
++}
++
++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, enum drm_bridge_attach_flags flags)
++{
++	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_info *info,
++			  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, 0);
++	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");
+--- /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, 0))
++			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);
++	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");
+--- /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);
++}
+--- /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");
+--- /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);
+--- /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);
++}
+--- /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
+--- a/drivers/gpu/drm/imx/Kconfig
++++ b/drivers/gpu/drm/imx/Kconfig
+@@ -3,3 +3,4 @@
+ source "drivers/gpu/drm/imx/dcss/Kconfig"
+ source "drivers/gpu/drm/imx/ipuv3/Kconfig"
+ source "drivers/gpu/drm/imx/lcdc/Kconfig"
++source "drivers/gpu/drm/imx/cdns/Kconfig"
+--- a/drivers/gpu/drm/imx/Makefile
++++ b/drivers/gpu/drm/imx/Makefile
+@@ -3,3 +3,4 @@
+ obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
+ obj-$(CONFIG_DRM_IMX) += ipuv3/
+ obj-$(CONFIG_DRM_IMX_LCDC) += lcdc/
++obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns/
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/Kconfig
+@@ -0,0 +1,8 @@
++config DRM_IMX_CDNS_MHDP
++        tristate "NXP i.MX8M / LS1028A 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.MX8M or DP on LS1028A.
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/Makefile
+@@ -0,0 +1,2 @@
++cdns_mhdp_imx-objs := cdn-mhdp-imxdrv.o cdn-mhdp-dp-phy.o cdn-mhdp-hdmi-phy.o cdn-mhdp-imx8qm.o cdn-mhdp-ls1028a.o
++obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/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;
++}
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/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;
++}
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/cdn-mhdp-imx8qm.c
+@@ -0,0 +1,625 @@
++/*
++ * 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 <dt-bindings/firmware/imx/rsrc.h>
++#include <linux/firmware/imx/sci.h>
++#include <linux/firmware.h>
++#include <linux/pm_domain.h>
++#include <linux/clk.h>
++#include <drm/drm_print.h>
++
++#include "cdns-mhdp-imx.h"
++
++#define FW_IRAM_OFFSET		0x2000
++#define FW_IRAM_SIZE		0x10000
++#define FW_DRAM_SIZE		0x8000
++
++#define PLL_800MHZ (800000000)
++
++#define HDP_DUAL_MODE_MIN_PCLK_RATE	300000	/* KHz */
++#define HDP_SINGLE_MODE_MAX_WIDTH	1920
++
++#define CSR_PIXEL_LINK_MUX_CTL		0x00
++#define CSR_PIXEL_LINK_MUX_VCP_OFFSET		5
++#define CSR_PIXEL_LINK_MUX_HCP_OFFSET		4
++
++static bool imx8qm_video_dual_mode(struct cdns_mhdp_device *mhdp)
++{
++	struct drm_display_mode *mode = &mhdp->mode;
++	return (mode->clock > HDP_DUAL_MODE_MIN_PCLK_RATE ||
++		mode->hdisplay > HDP_SINGLE_MODE_MAX_WIDTH) ? true : false;
++}
++
++static void imx8qm_pixel_link_mux(struct imx_mhdp_device *imx_mhdp)
++{
++	struct drm_display_mode *mode = &imx_mhdp->mhdp.mode;
++	bool dual_mode;
++	u32 val;
++
++	dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp);
++
++	val = 0x4;	/* RGB */
++	if (dual_mode)
++		val |= 0x2;	/* pixel link 0 and 1 are active */
++	if (mode->flags & DRM_MODE_FLAG_PVSYNC)
++		val |= 1 << CSR_PIXEL_LINK_MUX_VCP_OFFSET;
++	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
++		val |= 1 << CSR_PIXEL_LINK_MUX_HCP_OFFSET;
++	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
++		val |= 0x2;
++
++	writel(val, imx_mhdp->mhdp.regs_sec);
++}
++
++static void imx8qm_pixel_link_valid(u32 dual_mode)
++{
++	struct imx_sc_ipc *handle;
++
++	imx_scu_get_handle(&handle);
++
++	imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST1_VLD, 1);
++	if (dual_mode)
++		imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST2_VLD, 1);
++}
++
++static void imx8qm_pixel_link_invalid(u32 dual_mode)
++{
++	struct imx_sc_ipc *handle;
++
++	imx_scu_get_handle(&handle);
++
++	imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST1_VLD, 0);
++	if (dual_mode)
++		imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_PXL_LINK_MST2_VLD, 0);
++}
++
++static void imx8qm_pixel_link_sync_enable(u32 dual_mode)
++{
++	struct imx_sc_ipc *handle;
++
++	imx_scu_get_handle(&handle);
++
++	if (dual_mode)
++		imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL, 3);
++	else
++		imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL0, 1);
++}
++
++static void imx8qm_pixel_link_sync_disable(u32 dual_mode)
++{
++	struct imx_sc_ipc *handle;
++
++	imx_scu_get_handle(&handle);
++
++	if (dual_mode)
++		imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL, 0);
++	else
++		imx_sc_misc_set_control(handle, IMX_SC_R_DC_0, IMX_SC_C_SYNC_CTRL0, 0);
++}
++
++static void imx8qm_phy_reset(u8 reset)
++{
++	struct imx_sc_ipc *handle;
++
++	imx_scu_get_handle(&handle);
++
++	/* set the pixel link mode and pixel type */
++	imx_sc_misc_set_control(handle, IMX_SC_R_HDMI, IMX_SC_C_PHY_RESET, reset);
++}
++
++static void imx8qm_clk_mux(u8 is_dp)
++{
++	struct imx_sc_ipc *handle;
++
++	imx_scu_get_handle(&handle);
++
++	if (is_dp)
++		/* Enable the 24MHz for HDP PHY */
++		imx_sc_misc_set_control(handle, IMX_SC_R_HDMI, IMX_SC_C_MODE, 1);
++	else
++		imx_sc_misc_set_control(handle, IMX_SC_R_HDMI, IMX_SC_C_MODE, 0);
++}
++
++int imx8qm_clocks_init(struct imx_mhdp_device *imx_mhdp)
++{
++	struct device *dev = imx_mhdp->mhdp.dev;
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++
++	clks->dig_pll = devm_clk_get(dev, "dig_pll");
++	if (IS_ERR(clks->dig_pll)) {
++		dev_warn(dev, "failed to get dig pll clk\n");
++		return PTR_ERR(clks->dig_pll);
++	}
++
++	clks->av_pll = devm_clk_get(dev, "av_pll");
++	if (IS_ERR(clks->av_pll)) {
++		dev_warn(dev, "failed to get av pll clk\n");
++		return PTR_ERR(clks->av_pll);
++	}
++
++	clks->clk_ipg = devm_clk_get(dev, "clk_ipg");
++	if (IS_ERR(clks->clk_ipg)) {
++		dev_warn(dev, "failed to get dp ipg clk\n");
++		return PTR_ERR(clks->clk_ipg);
++	}
++
++	clks->clk_core = devm_clk_get(dev, "clk_core");
++	if (IS_ERR(clks->clk_core)) {
++		dev_warn(dev, "failed to get hdp core clk\n");
++		return PTR_ERR(clks->clk_core);
++	}
++
++	clks->clk_pxl = devm_clk_get(dev, "clk_pxl");
++	if (IS_ERR(clks->clk_pxl)) {
++		dev_warn(dev, "failed to get pxl clk\n");
++		return PTR_ERR(clks->clk_pxl);
++	}
++
++	clks->clk_pxl_mux = devm_clk_get(dev, "clk_pxl_mux");
++	if (IS_ERR(clks->clk_pxl_mux)) {
++		dev_warn(dev, "failed to get pxl mux clk\n");
++		return PTR_ERR(clks->clk_pxl_mux);
++	}
++
++	clks->clk_pxl_link = devm_clk_get(dev, "clk_pxl_link");
++	if (IS_ERR(clks->clk_pxl_mux)) {
++		dev_warn(dev, "failed to get pxl link clk\n");
++		return PTR_ERR(clks->clk_pxl_link);
++	}
++
++	clks->lpcg_hdp = devm_clk_get(dev, "lpcg_hdp");
++	if (IS_ERR(clks->lpcg_hdp)) {
++		dev_warn(dev, "failed to get lpcg hdp clk\n");
++		return PTR_ERR(clks->lpcg_hdp);
++	}
++
++	clks->lpcg_msi = devm_clk_get(dev, "lpcg_msi");
++	if (IS_ERR(clks->lpcg_msi)) {
++		dev_warn(dev, "failed to get lpcg msi clk\n");
++		return PTR_ERR(clks->lpcg_msi);
++	}
++
++	clks->lpcg_pxl = devm_clk_get(dev, "lpcg_pxl");
++	if (IS_ERR(clks->lpcg_pxl)) {
++		dev_warn(dev, "failed to get lpcg pxl clk\n");
++		return PTR_ERR(clks->lpcg_pxl);
++	}
++
++	clks->lpcg_vif = devm_clk_get(dev, "lpcg_vif");
++	if (IS_ERR(clks->lpcg_vif)) {
++		dev_warn(dev, "failed to get lpcg vif clk\n");
++		return PTR_ERR(clks->lpcg_vif);
++	}
++
++	clks->lpcg_lis = devm_clk_get(dev, "lpcg_lis");
++	if (IS_ERR(clks->lpcg_lis)) {
++		dev_warn(dev, "failed to get lpcg lis clk\n");
++		return PTR_ERR(clks->lpcg_lis);
++	}
++
++	clks->lpcg_apb = devm_clk_get(dev, "lpcg_apb");
++	if (IS_ERR(clks->lpcg_apb)) {
++		dev_warn(dev, "failed to get lpcg apb clk\n");
++		return PTR_ERR(clks->lpcg_apb);
++	}
++
++	clks->lpcg_apb_csr = devm_clk_get(dev, "lpcg_apb_csr");
++	if (IS_ERR(clks->lpcg_apb_csr)) {
++		dev_warn(dev, "failed to get apb csr clk\n");
++		return PTR_ERR(clks->lpcg_apb_csr);
++	}
++
++	clks->lpcg_apb_ctrl = devm_clk_get(dev, "lpcg_apb_ctrl");
++	if (IS_ERR(clks->lpcg_apb_ctrl)) {
++		dev_warn(dev, "failed to get lpcg apb ctrl clk\n");
++		return PTR_ERR(clks->lpcg_apb_ctrl);
++	}
++
++	clks->clk_i2s_bypass = devm_clk_get(dev, "clk_i2s_bypass");
++	if (IS_ERR(clks->clk_i2s_bypass)) {
++		dev_err(dev, "failed to get i2s bypass clk\n");
++		return PTR_ERR(clks->clk_i2s_bypass);
++	}
++
++	clks->lpcg_i2s = devm_clk_get(dev, "lpcg_i2s");
++	if (IS_ERR(clks->lpcg_i2s)) {
++		dev_err(dev, "failed to get lpcg i2s clk\n");
++		return PTR_ERR(clks->lpcg_i2s);
++	}
++	return true;
++}
++
++static int imx8qm_pixel_clk_enable(struct imx_mhdp_device *imx_mhdp)
++{
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++	struct device *dev = imx_mhdp->mhdp.dev;
++	int ret;
++
++	ret = clk_prepare_enable(clks->av_pll);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre av pll  error\n", __func__);
++		return ret;
++	}
++
++	ret = clk_prepare_enable(clks->clk_pxl);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk pxl error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->clk_pxl_mux);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk pxl mux error\n", __func__);
++		return ret;
++	}
++
++	ret = clk_prepare_enable(clks->clk_pxl_link);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk pxl link error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_vif);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk vif error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_pxl);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre lpcg pxl error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_hdp);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre lpcg hdp error\n", __func__);
++		return ret;
++	}
++	return ret;
++}
++
++static void imx8qm_pixel_clk_disable(struct imx_mhdp_device *imx_mhdp)
++{
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++
++	clk_disable_unprepare(clks->lpcg_pxl);
++	clk_disable_unprepare(clks->lpcg_hdp);
++	clk_disable_unprepare(clks->lpcg_vif);
++	clk_disable_unprepare(clks->clk_pxl);
++	clk_disable_unprepare(clks->clk_pxl_link);
++	clk_disable_unprepare(clks->clk_pxl_mux);
++	clk_disable_unprepare(clks->av_pll);
++}
++
++static void imx8qm_pixel_clk_set_rate(struct imx_mhdp_device *imx_mhdp, u32 pclock)
++{
++	bool dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp);
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++
++	/* pixel clock for HDMI */
++	clk_set_rate(clks->av_pll, pclock);
++
++	if (dual_mode == true) {
++		clk_set_rate(clks->clk_pxl, pclock/2);
++		clk_set_rate(clks->clk_pxl_link, pclock/2);
++	} else {
++		clk_set_rate(clks->clk_pxl_link, pclock);
++		clk_set_rate(clks->clk_pxl, pclock);
++	}
++	clk_set_rate(clks->clk_pxl_mux, pclock);
++}
++
++static int imx8qm_ipg_clk_enable(struct imx_mhdp_device *imx_mhdp)
++{
++	int ret;
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++	struct device *dev = imx_mhdp->mhdp.dev;
++
++	ret = clk_prepare_enable(clks->dig_pll);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre dig pll error\n", __func__);
++		return ret;
++	}
++
++	ret = clk_prepare_enable(clks->clk_ipg);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk_ipg error\n", __func__);
++		return ret;
++	}
++
++	ret = clk_prepare_enable(clks->clk_core);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk core error\n", __func__);
++		return ret;
++	}
++
++	ret = clk_prepare_enable(clks->lpcg_apb);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk apb error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_lis);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk lis error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_msi);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk msierror\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_apb_csr);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk apb csr error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_apb_ctrl);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk apb ctrl error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->lpcg_i2s);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk i2s error\n", __func__);
++		return ret;
++	}
++	ret = clk_prepare_enable(clks->clk_i2s_bypass);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk i2s bypass error\n", __func__);
++		return ret;
++	}
++	return ret;
++}
++
++static void imx8qm_ipg_clk_set_rate(struct imx_mhdp_device *imx_mhdp)
++{
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++
++	/* ipg/core clock */
++	clk_set_rate(clks->dig_pll,  PLL_800MHZ);
++	clk_set_rate(clks->clk_core, PLL_800MHZ/4);
++	clk_set_rate(clks->clk_ipg,  PLL_800MHZ/8);
++}
++
++static void imx8qm_detach_pm_domains(struct imx_mhdp_device *imx_mhdp)
++{
++	if (imx_mhdp->pd_pll1_link && !IS_ERR(imx_mhdp->pd_pll1_link))
++		device_link_del(imx_mhdp->pd_pll1_link);
++	if (imx_mhdp->pd_pll1_dev && !IS_ERR(imx_mhdp->pd_pll1_dev))
++		dev_pm_domain_detach(imx_mhdp->pd_pll1_dev, true);
++
++	if (imx_mhdp->pd_pll0_link && !IS_ERR(imx_mhdp->pd_pll0_link))
++		device_link_del(imx_mhdp->pd_pll0_link);
++	if (imx_mhdp->pd_pll0_dev && !IS_ERR(imx_mhdp->pd_pll0_dev))
++		dev_pm_domain_detach(imx_mhdp->pd_pll0_dev, true);
++
++	if (imx_mhdp->pd_mhdp_link && !IS_ERR(imx_mhdp->pd_mhdp_link))
++		device_link_del(imx_mhdp->pd_mhdp_link);
++	if (imx_mhdp->pd_mhdp_dev && !IS_ERR(imx_mhdp->pd_mhdp_dev))
++		dev_pm_domain_detach(imx_mhdp->pd_mhdp_dev, true);
++
++	imx_mhdp->pd_mhdp_dev = NULL;
++	imx_mhdp->pd_mhdp_link = NULL;
++	imx_mhdp->pd_pll0_dev = NULL;
++	imx_mhdp->pd_pll0_link = NULL;
++	imx_mhdp->pd_pll1_dev = NULL;
++	imx_mhdp->pd_pll1_link = NULL;
++}
++
++static int imx8qm_attach_pm_domains(struct imx_mhdp_device *imx_mhdp)
++{
++	struct device *dev = imx_mhdp->mhdp.dev;
++	u32 flags = DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE;
++	int ret = 0;
++
++	imx_mhdp->pd_mhdp_dev = dev_pm_domain_attach_by_name(dev, "hdmi");
++	if (IS_ERR(imx_mhdp->pd_mhdp_dev)) {
++		ret = PTR_ERR(imx_mhdp->pd_mhdp_dev);
++		dev_err(dev, "Failed to attach dc pd dev: %d\n", ret);
++		goto fail;
++	}
++	imx_mhdp->pd_mhdp_link = device_link_add(dev, imx_mhdp->pd_mhdp_dev, flags);
++	if (IS_ERR(imx_mhdp->pd_mhdp_link)) {
++		ret = PTR_ERR(imx_mhdp->pd_mhdp_link);
++		dev_err(dev, "Failed to add device link to dc pd dev: %d\n",
++			ret);
++		goto fail;
++	}
++
++	imx_mhdp->pd_pll0_dev = dev_pm_domain_attach_by_name(dev, "pll0");
++	if (IS_ERR(imx_mhdp->pd_pll0_dev)) {
++		ret = PTR_ERR(imx_mhdp->pd_pll0_dev);
++		dev_err(dev, "Failed to attach pll0 pd dev: %d\n", ret);
++		goto fail;
++	}
++	imx_mhdp->pd_pll0_link = device_link_add(dev, imx_mhdp->pd_pll0_dev, flags);
++	if (IS_ERR(imx_mhdp->pd_pll0_link)) {
++		ret = PTR_ERR(imx_mhdp->pd_pll0_link);
++		dev_err(dev, "Failed to add device link to pll0 pd dev: %d\n",
++			ret);
++		goto fail;
++	}
++
++	imx_mhdp->pd_pll1_dev = dev_pm_domain_attach_by_name(dev, "pll1");
++	if (IS_ERR(imx_mhdp->pd_pll1_dev)) {
++		ret = PTR_ERR(imx_mhdp->pd_pll1_dev);
++		dev_err(dev, "Failed to attach pll0 pd dev: %d\n", ret);
++		goto fail;
++	}
++	imx_mhdp->pd_pll1_link = device_link_add(dev, imx_mhdp->pd_pll1_dev, flags);
++	if (IS_ERR(imx_mhdp->pd_pll1_link)) {
++		ret = PTR_ERR(imx_mhdp->pd_pll1_link);
++		dev_err(dev, "Failed to add device link to pll1 pd dev: %d\n",
++			ret);
++		goto fail;
++	}
++fail:
++	imx8qm_detach_pm_domains(imx_mhdp);
++	return ret;
++}
++
++int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp =
++				container_of(mhdp, struct imx_mhdp_device, mhdp);
++	/* Power on PM Domains */
++
++	imx8qm_attach_pm_domains(imx_mhdp);
++
++	/* clock init and  rate set */
++	imx8qm_clocks_init(imx_mhdp);
++
++	imx8qm_ipg_clk_set_rate(imx_mhdp);
++
++	/* Init pixel clock with 148.5MHz before FW init */
++	imx8qm_pixel_clk_set_rate(imx_mhdp, 148500000);
++
++	imx8qm_ipg_clk_enable(imx_mhdp);
++
++	imx8qm_clk_mux(imx_mhdp->mhdp.plat_data->is_dp);
++
++	imx8qm_pixel_clk_enable(imx_mhdp);
++
++	imx8qm_phy_reset(1);
++
++	return 0;
++}
++
++void cdns_mhdp_plat_init_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp =
++				container_of(mhdp, struct imx_mhdp_device, mhdp);
++	bool dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp);
++
++	imx8qm_pixel_link_sync_disable(dual_mode);
++	imx8qm_pixel_link_invalid(dual_mode);
++}
++
++void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp =
++				container_of(mhdp, struct imx_mhdp_device, mhdp);
++	bool dual_mode = imx8qm_video_dual_mode(&imx_mhdp->mhdp);
++
++	imx8qm_pixel_link_valid(dual_mode);
++	imx8qm_pixel_link_sync_enable(dual_mode);
++}
++
++void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp =
++				container_of(mhdp, struct imx_mhdp_device, mhdp);
++
++	/* set pixel clock before video mode setup */
++	imx8qm_pixel_clk_disable(imx_mhdp);
++
++	imx8qm_pixel_clk_set_rate(imx_mhdp, imx_mhdp->mhdp.mode.clock * 1000);
++
++	imx8qm_pixel_clk_enable(imx_mhdp);
++
++	/* Config pixel link mux */
++	imx8qm_pixel_link_mux(imx_mhdp);
++}
++
++static void cdns_mhdp_firmware_load_cont(const struct firmware *fw, void *context)
++{
++	struct imx_mhdp_device *imx_mhdp = context;
++
++	imx_mhdp->fw = fw;
++}
++
++static int cdns_mhdp_load_firmware_imx8qm(struct imx_mhdp_device *imx_mhdp)
++{
++	const u8 *iram;
++	const u8 *dram;
++	u32 rate;
++	int ret;
++
++	/* configure HDMI/DP core clock */
++	rate = clk_get_rate(imx_mhdp->clks.clk_core);
++	if (imx_mhdp->mhdp.is_ls1028a)
++		rate = rate / 4;
++
++	cdns_mhdp_set_fw_clk(&imx_mhdp->mhdp, rate);
++
++	/* skip fw loading if none is specified */
++	if (!imx_mhdp->firmware_name)
++		goto out;
++
++	if (!imx_mhdp->fw) {
++		ret = request_firmware(&imx_mhdp->fw,
++						imx_mhdp->firmware_name,
++						imx_mhdp->mhdp.dev);
++		if (ret < 0) {
++			DRM_ERROR("Failed to request MHDP firmware\n");
++			return -ENOENT;
++		} else {
++			DRM_INFO("Requested MHDP firmware\n");
++		}
++	}
++
++ if (imx_mhdp->fw) {
++		iram = imx_mhdp->fw->data + FW_IRAM_OFFSET;
++		dram = iram + FW_IRAM_SIZE;
++
++		cdns_mhdp_load_firmware(&imx_mhdp->mhdp,
++					(const u32 *) iram, FW_IRAM_SIZE,
++					(const u32 *) dram, FW_DRAM_SIZE);
++		DRM_INFO("Loaded MHDP firmware\n");
++	}
++
++out:
++	/* un-reset ucpu */
++	cdns_mhdp_bus_write(0, &imx_mhdp->mhdp, APB_CTRL);
++	DRM_INFO("Started MHDP firmware\n");
++
++	return 0;
++}
++
++int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp =
++				container_of(mhdp, struct imx_mhdp_device, mhdp);
++	int ret;
++
++	/* load firmware */
++	ret = cdns_mhdp_load_firmware_imx8qm(imx_mhdp);
++	if (ret)
++		return ret;
++
++	ret = cdns_mhdp_check_alive(&imx_mhdp->mhdp);
++	if (ret == false) {
++		DRM_ERROR("NO HDMI FW running\n");
++		return -ENXIO;
++	}
++
++	/* turn on IP activity */
++	cdns_mhdp_set_firmware_active(&imx_mhdp->mhdp, 1);
++
++	DRM_INFO("HDP FW Version - ver %d verlib %d\n",
++			cdns_mhdp_bus_read(mhdp, VER_L) + (cdns_mhdp_bus_read(mhdp, VER_H) << 8),
++			cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) + (cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) << 8));
++
++	return 0;
++}
++
++int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp =
++				container_of(mhdp, struct imx_mhdp_device, mhdp);
++
++	imx8qm_pixel_clk_disable(imx_mhdp);
++
++	return 0;
++}
++
++int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp =
++				container_of(mhdp, struct imx_mhdp_device, mhdp);
++
++	imx8qm_pixel_clk_enable(imx_mhdp);
++
++	return cdns_mhdp_firmware_init_imx8qm(mhdp);
++}
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/cdn-mhdp-imxdrv.c
+@@ -0,0 +1,222 @@
++/*
++ * 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 <drm/drm_atomic_helper.h>
++
++#include "cdns-mhdp-imx.h"
++#include "cdn-mhdp-phy.h"
++#include "../ipuv3/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 struct cdns_plat_data ls1028a_dp_drv_data = {
++	.bind = cdns_dp_bind,
++	.unbind = cdns_dp_unbind,
++	.phy_set = cdns_dp_phy_set_imx8mq,
++	.power_on = cdns_mhdp_power_on_ls1028a,
++	.firmware_init = cdns_mhdp_firmware_init_imx8qm,
++	.pclk_rate = cdns_mhdp_pclk_rate_ls1028a,
++	.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,ls1028a-dp",
++	  .data = &ls1028a_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");
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/cdn-mhdp-ls1028a.c
+@@ -0,0 +1,111 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright 2019 NXP
++ *
++ */
++#include <linux/clk.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++
++#include "cdns-mhdp-imx.h"
++
++static const struct of_device_id scfg_device_ids[] = {
++	{ .compatible = "fsl,ls1028a-scfg", },
++	{}
++};
++
++static void ls1028a_phy_reset(u8 reset)
++{
++	struct device_node *scfg_node;
++	void __iomem *scfg_base = NULL;
++
++	scfg_node = of_find_matching_node(NULL, scfg_device_ids);
++	if (scfg_node)
++		scfg_base = of_iomap(scfg_node, 0);
++
++	iowrite32(reset, scfg_base + 0x230);
++}
++
++int ls1028a_clocks_init(struct imx_mhdp_device *imx_mhdp)
++{
++	struct device *dev = imx_mhdp->mhdp.dev;
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++
++	clks->clk_core = devm_clk_get(dev, "clk_core");
++	if (IS_ERR(clks->clk_core)) {
++		dev_warn(dev, "failed to get hdp core clk\n");
++		return PTR_ERR(clks->clk_core);
++	}
++
++	clks->clk_pxl = devm_clk_get(dev, "clk_pxl");
++	if (IS_ERR(clks->clk_pxl)) {
++		dev_warn(dev, "failed to get pxl clk\n");
++		return PTR_ERR(clks->clk_pxl);
++	}
++
++	return true;
++}
++
++static int ls1028a_pixel_clk_enable(struct imx_mhdp_device *imx_mhdp)
++{
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++	struct device *dev = imx_mhdp->mhdp.dev;
++	int ret;
++
++	ret = clk_prepare_enable(clks->clk_pxl);
++	if (ret < 0) {
++		dev_err(dev, "%s, pre clk pxl error\n", __func__);
++		return ret;
++	}
++
++	return ret;
++}
++
++static void ls1028a_pixel_clk_disable(struct imx_mhdp_device *imx_mhdp)
++{
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++
++	clk_disable_unprepare(clks->clk_pxl);
++}
++
++static void ls1028a_pixel_clk_set_rate(struct imx_mhdp_device *imx_mhdp,
++				       u32 pclock)
++{
++	struct imx_hdp_clks *clks = &imx_mhdp->clks;
++
++	clk_set_rate(clks->clk_pxl, pclock);
++}
++
++int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp = container_of
++				(mhdp, struct imx_mhdp_device, mhdp);
++
++	/* clock init and  rate set */
++	ls1028a_clocks_init(imx_mhdp);
++
++	ls1028a_pixel_clk_enable(imx_mhdp);
++
++	/* Init pixel clock with 148.5MHz before FW init */
++	ls1028a_pixel_clk_set_rate(imx_mhdp, 148500000);
++
++	ls1028a_phy_reset(1);
++
++	return 0;
++}
++
++void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp)
++{
++	struct imx_mhdp_device *imx_mhdp = container_of
++				(mhdp, struct imx_mhdp_device, mhdp);
++
++  printk(KERN_ALERT "DEBUG: Passed %s %d clock: %d\n",__FUNCTION__,__LINE__,imx_mhdp->mhdp.mode.clock);
++
++	/* set pixel clock before video mode setup */
++	ls1028a_pixel_clk_disable(imx_mhdp);
++
++	ls1028a_pixel_clk_set_rate(imx_mhdp, imx_mhdp->mhdp.mode.clock * 1000);
++
++	ls1028a_pixel_clk_enable(imx_mhdp);
++}
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/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 */
+--- /dev/null
++++ b/drivers/gpu/drm/imx/cdns/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_ */
+--- a/drivers/gpu/drm/imx/dcss/dcss-dev.c
++++ b/drivers/gpu/drm/imx/dcss/dcss-dev.c
+@@ -18,6 +18,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);
+@@ -32,6 +37,11 @@ static void dcss_clocks_disable(struct d
+ 	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)
+@@ -140,6 +150,8 @@ static int dcss_clks_init(struct dcss_de
+ 		{"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++) {
+@@ -161,6 +173,8 @@ static void dcss_clks_release(struct dcs
+ 	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)
+--- a/drivers/gpu/drm/imx/dcss/dcss-dev.h
++++ b/drivers/gpu/drm/imx/dcss/dcss-dev.h
+@@ -94,6 +94,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);
+ void dcss_enable_dtg_and_ss(struct dcss_dev *dcss);
+--- 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(s
+ 	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(struc
+ 
+ 	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);
+ 		dev_err_probe(dev, err, "Failed to initialize KMS\n");
+@@ -80,14 +77,73 @@ err:
+ 	return err;
+ }
+ 
+-static int dcss_drv_platform_remove(struct platform_device *pdev)
++static void dcss_drv_deinit(struct device *dev, bool componentized)
+ {
+-	struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev);
++	struct dcss_drv *mdrv = dev_get_drvdata(dev);
+ 
+-	dcss_kms_detach(mdrv->kms);
++	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->is_componentized)
++		component_master_del(&pdev->dev, &dcss_master_ops);
++	else
++		dcss_drv_deinit(&pdev->dev, false);
+ 
+ 	return 0;
+ }
+--- 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,
+ 	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,
+ 	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 *
+ 	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 dc
+ {
+ 	return !!(dcss_readl(dtg->base_reg + DCSS_DTG_INT_STATUS) & LINE1_IRQ);
+ }
+-
+--- 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"
+@@ -68,6 +69,7 @@ static int dcss_kms_bridge_connector_ini
+ 	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,
+@@ -90,23 +92,23 @@ static int dcss_kms_bridge_connector_ini
+ 		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;
+@@ -129,14 +131,22 @@ struct dcss_kms_dev *dcss_kms_attach(str
+ 	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);
+@@ -160,9 +170,10 @@ cleanup_mode_config:
+ 	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_kms_helper_poll_fini(drm);
+@@ -170,6 +181,8 @@ void dcss_kms_detach(struct dcss_kms_dev
+ 	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;
+ }
+ 
+--- 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);
+ void dcss_kms_shutdown(struct dcss_kms_dev *kms);
+ int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm);
+ void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm);
+--- /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
+--- /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_ */
diff --git a/linux/patches6.7/ls1028a-mnt-reform2/0000-dtsi-add-hdptx.patch b/linux/patches6.7/ls1028a-mnt-reform2/0000-dtsi-add-hdptx.patch
new file mode 100644
index 0000000000000000000000000000000000000000..fce52fbfe6e6c2f735698c89acaa715a4e4d4df4
--- /dev/null
+++ b/linux/patches6.7/ls1028a-mnt-reform2/0000-dtsi-add-hdptx.patch
@@ -0,0 +1,32 @@
+--- a/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi
++++ b/arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi
+@@ -871,8 +871,28 @@
+ 			arm,malidp-arqos-value = <0xd000d000>;
+ 
+ 			port {
+-				dpi0_out: endpoint {
++					dpi0_out: endpoint {
++					remote-endpoint = <&dp1_out>;
++				};
++			};
++		};
+ 
++		hdptx0: display@f200000 {
++			compatible = "cdn,ls1028a-dp";
++			reg = <0x0 0xf200000 0x0 0xfffff>;
++			interrupts = <0 221 IRQ_TYPE_LEVEL_HIGH>;
++			clocks = <&clockgen QORIQ_CLK_HWACCEL 2>, <&clockgen QORIQ_CLK_HWACCEL 2>,
++							 <&clockgen QORIQ_CLK_HWACCEL 2>, <&clockgen QORIQ_CLK_HWACCEL 2>,
++							 <&clockgen QORIQ_CLK_HWACCEL 2>, <&dpclk>;
++			clock-names = "clk_core", "pclk", "sclk",
++										"cclk", "clk_vif", "clk_pxl";
++			lane-mapping = <0x4e>;
++			edp_num_lanes = <0x2>;
++			firmware-name = "ls1028a-mhdpfw.bin";
++
++			port {
++				dp1_out: endpoint {
++					remote-endpoint = <&dpi0_out>;
+ 				};
+ 			};
+ 		};
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0000-v9_20231124_neil_armstrong_drm_meson_add_support_for_mipi_dsi_display.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0000-v9_20231124_neil_armstrong_drm_meson_add_support_for_mipi_dsi_display.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3ad8647c594a2b588cd618959fc0d7c47b70633e
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0000-v9_20231124_neil_armstrong_drm_meson_add_support_for_mipi_dsi_display.patch
@@ -0,0 +1,1648 @@
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 1/12] dt-bindings: clk: g12a-clkc: add CTS_ENCL clock
+ ids
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:12 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-1-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+Add new CLK ids for the CTS_ENCL and CTS_ENCL_SEL clocks
+on G12A compatible SoCs.
+
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ include/dt-bindings/clock/g12a-clkc.h | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h
+index 387767f4e298..636d713f95ff 100644
+--- a/include/dt-bindings/clock/g12a-clkc.h
++++ b/include/dt-bindings/clock/g12a-clkc.h
+@@ -279,5 +279,7 @@
+ #define CLKID_MIPI_DSI_PXCLK_DIV		268
+ #define CLKID_MIPI_DSI_PXCLK_SEL		269
+ #define CLKID_MIPI_DSI_PXCLK			270
++#define CLKID_CTS_ENCL				271
++#define CLKID_CTS_ENCL_SEL			272
+ 
+ #endif /* __G12A_CLKC_H */
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 2/12] dt-bindings: soc: amlogic,meson-gx-hhi-sysctrl:
+ add example covering meson-axg-hhi-sysctrl
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:13 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-2-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+Add a third example covering the meson-axg-hhi-sysctrl variant and more
+importantly the phy subnode.
+
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ .../soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml  | 33 ++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+diff --git a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml
+index 16977e4e4357..c6bce40946d4 100644
+--- a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml
++++ b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml
+@@ -158,3 +158,36 @@ examples:
+             };
+         };
+     };
++
++  - |
++    system-controller@ff63c000 {
++        compatible = "amlogic,meson-axg-hhi-sysctrl", "simple-mfd", "syscon";
++        reg = <0xff63c000 0x400>;
++
++        clock-controller {
++            compatible = "amlogic,axg-clkc";
++            #clock-cells = <1>;
++            clocks = <&xtal>;
++            clock-names = "xtal";
++        };
++
++        power-controller {
++           compatible = "amlogic,meson-axg-pwrc";
++           #power-domain-cells = <1>;
++           amlogic,ao-sysctrl = <&sysctrl_AO>;
++
++           resets = <&reset_viu>,
++                    <&reset_venc>,
++                    <&reset_vcbus>,
++                    <&reset_vencl>,
++                    <&reset_vid_lock>;
++           reset-names = "viu", "venc", "vcbus", "vencl", "vid_lock";
++           clocks = <&clk_vpu>, <&clk_vapb>;
++           clock-names = "vpu", "vapb";
++        };
++
++        phy {
++           compatible = "amlogic,axg-mipi-pcie-analog-phy";
++           #phy-cells = <0>;
++        };
++    };
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 3/12] dt-bindings: phy:
+ amlogic,meson-axg-mipi-pcie-analog: drop text about parent syscon and drop
+ example
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:14 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-3-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+Since this bindings is referred from amlogic,meson-gx-hhi-sysctrl.yaml, drop the now
+useless description about the parent node and also drop the unnecessary example.
+
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ .../phy/amlogic,meson-axg-mipi-pcie-analog.yaml         | 17 -----------------
+ 1 file changed, 17 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml b/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml
+index 009a39808318..70def36e5688 100644
+--- a/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml
++++ b/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml
+@@ -9,16 +9,6 @@ title: Amlogic AXG shared MIPI/PCIE analog PHY
+ maintainers:
+   - Remi Pommarel <repk@triplefau.lt>
+ 
+-description: |+
+-  The Everything-Else Power Domains node should be the child of a syscon
+-  node with the required property:
+-
+-  - compatible: Should be the following:
+-                "amlogic,meson-gx-hhi-sysctrl", "simple-mfd", "syscon"
+-
+-  Refer to the bindings described in
+-  Documentation/devicetree/bindings/mfd/syscon.yaml
+-
+ properties:
+   compatible:
+     const: amlogic,axg-mipi-pcie-analog-phy
+@@ -31,10 +21,3 @@ required:
+   - "#phy-cells"
+ 
+ additionalProperties: false
+-
+-examples:
+-  - |
+-    mpphy: phy {
+-          compatible = "amlogic,axg-mipi-pcie-analog-phy";
+-          #phy-cells = <0>;
+-    };
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 4/12] dt-bindings: phy: amlogic,g12a-mipi-dphy-analog:
+ drop unneeded reg property and example
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:15 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-4-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+The amlogic,g12a-mipi-dphy-analog is a feature of the simple-mfd
+amlogic,meson-axg-hhi-sysctrl system control register zone which is an
+intermixed registers zone, thus it's very hard to define clear ranges for
+each SoC controlled features even if possible.
+
+The amlogic,g12a-mipi-dphy-analog was wrongly documented as an independent
+register range, which is not the reality, thus fix the bindings by dropping
+the reg property now it's referred from amlogic,meson-gx-hhi-sysctrl.yaml
+and documented as a subnode of amlogic,meson-axg-hhi-sysctrl.
+
+Also drop the unnecessary example, the top level bindings example should
+be enough.
+
+Fixes: 76ab79f9726c ("dt-bindings: phy: add Amlogic G12A Analog MIPI D-PHY bindings")
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ .../bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml          | 12 ------------
+ 1 file changed, 12 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
+index c8c83acfb871..81c2654b7e57 100644
+--- a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
++++ b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
+@@ -16,20 +16,8 @@ properties:
+   "#phy-cells":
+     const: 0
+ 
+-  reg:
+-    maxItems: 1
+-
+ required:
+   - compatible
+-  - reg
+   - "#phy-cells"
+ 
+ additionalProperties: false
+-
+-examples:
+-  - |
+-    phy@0 {
+-          compatible = "amlogic,g12a-mipi-dphy-analog";
+-          reg = <0x0 0xc>;
+-          #phy-cells = <0>;
+-    };
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 5/12] dt-bindings: arm: amlogic: Document the MNT Reform
+ 2 CM4 adapter with a BPI-CM4 Module
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:16 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-5-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+The MNT Reform 2 CM4 adapter can be populated with any Raspberry Pi CM4
+compatible module such as a BPI-CM4 Module, document that.
+
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ Documentation/devicetree/bindings/arm/amlogic.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/Documentation/devicetree/bindings/arm/amlogic.yaml b/Documentation/devicetree/bindings/arm/amlogic.yaml
+index caab7ceeda45..2154a4614fda 100644
+--- a/Documentation/devicetree/bindings/arm/amlogic.yaml
++++ b/Documentation/devicetree/bindings/arm/amlogic.yaml
+@@ -164,6 +164,7 @@ properties:
+         items:
+           - enum:
+               - bananapi,bpi-cm4io
++              - mntre,reform2-cm4
+           - const: bananapi,bpi-cm4
+           - const: amlogic,a311d
+           - const: amlogic,g12b
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 6/12] clk: meson: g12a: add CTS_ENCL & CTS_ENCL_SEL
+ clocks
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:17 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-6-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+Add new CTS_ENCL & CTS_ENCL_SEL clocks for the G12A compatible
+SoCs, they are used to feed the VPU LCD Pixel encoder used for
+DSI display purposes.
+
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ drivers/clk/meson/g12a.c | 40 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
+index f373a8d48b1d..cadd824336ad 100644
+--- a/drivers/clk/meson/g12a.c
++++ b/drivers/clk/meson/g12a.c
+@@ -3549,6 +3549,22 @@ static struct clk_regmap g12a_cts_encp_sel = {
+ 	},
+ };
+ 
++static struct clk_regmap g12a_cts_encl_sel = {
++	.data = &(struct clk_regmap_mux_data){
++		.offset = HHI_VIID_CLK_DIV,
++		.mask = 0xf,
++		.shift = 12,
++		.table = mux_table_cts_sel,
++	},
++	.hw.init = &(struct clk_init_data){
++		.name = "cts_encl_sel",
++		.ops = &clk_regmap_mux_ops,
++		.parent_hws = g12a_cts_parent_hws,
++		.num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
++		.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
++	},
++};
++
+ static struct clk_regmap g12a_cts_vdac_sel = {
+ 	.data = &(struct clk_regmap_mux_data){
+ 		.offset = HHI_VIID_CLK_DIV,
+@@ -3628,6 +3644,22 @@ static struct clk_regmap g12a_cts_encp = {
+ 	},
+ };
+ 
++static struct clk_regmap g12a_cts_encl = {
++	.data = &(struct clk_regmap_gate_data){
++		.offset = HHI_VID_CLK_CNTL2,
++		.bit_idx = 3,
++	},
++	.hw.init = &(struct clk_init_data) {
++		.name = "cts_encl",
++		.ops = &clk_regmap_gate_ops,
++		.parent_hws = (const struct clk_hw *[]) {
++			&g12a_cts_encl_sel.hw
++		},
++		.num_parents = 1,
++		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++	},
++};
++
+ static struct clk_regmap g12a_cts_vdac = {
+ 	.data = &(struct clk_regmap_gate_data){
+ 		.offset = HHI_VID_CLK_CNTL2,
+@@ -4407,10 +4439,12 @@ static struct clk_hw *g12a_hw_clks[] = {
+ 	[CLKID_VCLK2_DIV12]		= &g12a_vclk2_div12.hw,
+ 	[CLKID_CTS_ENCI_SEL]		= &g12a_cts_enci_sel.hw,
+ 	[CLKID_CTS_ENCP_SEL]		= &g12a_cts_encp_sel.hw,
++	[CLKID_CTS_ENCL_SEL]		= &g12a_cts_encl_sel.hw,
+ 	[CLKID_CTS_VDAC_SEL]		= &g12a_cts_vdac_sel.hw,
+ 	[CLKID_HDMI_TX_SEL]		= &g12a_hdmi_tx_sel.hw,
+ 	[CLKID_CTS_ENCI]		= &g12a_cts_enci.hw,
+ 	[CLKID_CTS_ENCP]		= &g12a_cts_encp.hw,
++	[CLKID_CTS_ENCL]		= &g12a_cts_encl.hw,
+ 	[CLKID_CTS_VDAC]		= &g12a_cts_vdac.hw,
+ 	[CLKID_HDMI_TX]			= &g12a_hdmi_tx.hw,
+ 	[CLKID_HDMI_SEL]		= &g12a_hdmi_sel.hw,
+@@ -4632,10 +4666,12 @@ static struct clk_hw *g12b_hw_clks[] = {
+ 	[CLKID_VCLK2_DIV12]		= &g12a_vclk2_div12.hw,
+ 	[CLKID_CTS_ENCI_SEL]		= &g12a_cts_enci_sel.hw,
+ 	[CLKID_CTS_ENCP_SEL]		= &g12a_cts_encp_sel.hw,
++	[CLKID_CTS_ENCL_SEL]		= &g12a_cts_encl_sel.hw,
+ 	[CLKID_CTS_VDAC_SEL]		= &g12a_cts_vdac_sel.hw,
+ 	[CLKID_HDMI_TX_SEL]		= &g12a_hdmi_tx_sel.hw,
+ 	[CLKID_CTS_ENCI]		= &g12a_cts_enci.hw,
+ 	[CLKID_CTS_ENCP]		= &g12a_cts_encp.hw,
++	[CLKID_CTS_ENCL]		= &g12a_cts_encl.hw,
+ 	[CLKID_CTS_VDAC]		= &g12a_cts_vdac.hw,
+ 	[CLKID_HDMI_TX]			= &g12a_hdmi_tx.hw,
+ 	[CLKID_HDMI_SEL]		= &g12a_hdmi_sel.hw,
+@@ -4892,10 +4928,12 @@ static struct clk_hw *sm1_hw_clks[] = {
+ 	[CLKID_VCLK2_DIV12]		= &g12a_vclk2_div12.hw,
+ 	[CLKID_CTS_ENCI_SEL]		= &g12a_cts_enci_sel.hw,
+ 	[CLKID_CTS_ENCP_SEL]		= &g12a_cts_encp_sel.hw,
++	[CLKID_CTS_ENCL_SEL]		= &g12a_cts_encl_sel.hw,
+ 	[CLKID_CTS_VDAC_SEL]		= &g12a_cts_vdac_sel.hw,
+ 	[CLKID_HDMI_TX_SEL]		= &g12a_hdmi_tx_sel.hw,
+ 	[CLKID_CTS_ENCI]		= &g12a_cts_enci.hw,
+ 	[CLKID_CTS_ENCP]		= &g12a_cts_encp.hw,
++	[CLKID_CTS_ENCL]		= &g12a_cts_encl.hw,
+ 	[CLKID_CTS_VDAC]		= &g12a_cts_vdac.hw,
+ 	[CLKID_HDMI_TX]			= &g12a_hdmi_tx.hw,
+ 	[CLKID_HDMI_SEL]		= &g12a_hdmi_sel.hw,
+@@ -5123,10 +5161,12 @@ static struct clk_regmap *const g12a_clk_regmaps[] = {
+ 	&g12a_vclk2_div12_en,
+ 	&g12a_cts_enci_sel,
+ 	&g12a_cts_encp_sel,
++	&g12a_cts_encl_sel,
+ 	&g12a_cts_vdac_sel,
+ 	&g12a_hdmi_tx_sel,
+ 	&g12a_cts_enci,
+ 	&g12a_cts_encp,
++	&g12a_cts_encl,
+ 	&g12a_cts_vdac,
+ 	&g12a_hdmi_tx,
+ 	&g12a_hdmi_sel,
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 7/12] clk: meson: add vclk driver
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:18 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-7-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+The VCLK and VCLK_DIV clocks have supplementary bits.
+
+The VCLK has a "SOFT RESET" bit to toggle after the whole
+VCLK sub-tree rate has been set, this is implemented in
+the gate enable callback.
+
+The VCLK_DIV clocks as enable and reset bits used to disable
+and reset the divider, associated with CLK_SET_RATE_GATE it ensures
+the rate is set while the divider is disabled and in reset mode.
+
+The VCLK_DIV enable bit isn't implemented as a gate since it's part
+of the divider logic and vendor does this exact sequence to ensure
+the divider is correctly set.
+
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ drivers/clk/meson/Kconfig  |   5 ++
+ drivers/clk/meson/Makefile |   1 +
+ drivers/clk/meson/vclk.c   | 141 +++++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/meson/vclk.h   |  51 ++++++++++++++++
+ 4 files changed, 198 insertions(+)
+
+diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
+index 29ffd14d267b..59a40a49f8e1 100644
+--- a/drivers/clk/meson/Kconfig
++++ b/drivers/clk/meson/Kconfig
+@@ -30,6 +30,10 @@ config COMMON_CLK_MESON_VID_PLL_DIV
+ 	tristate
+ 	select COMMON_CLK_MESON_REGMAP
+ 
++config COMMON_CLK_MESON_VCLK
++	tristate
++	select COMMON_CLK_MESON_REGMAP
++
+ config COMMON_CLK_MESON_CLKC_UTILS
+ 	tristate
+ 
+@@ -140,6 +144,7 @@ config COMMON_CLK_G12A
+ 	select COMMON_CLK_MESON_EE_CLKC
+ 	select COMMON_CLK_MESON_CPU_DYNDIV
+ 	select COMMON_CLK_MESON_VID_PLL_DIV
++	select COMMON_CLK_MESON_VCLK
+ 	select MFD_SYSCON
+ 	help
+ 	  Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2
+diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
+index 9ee4b954c896..9ba43fe7a07a 100644
+--- a/drivers/clk/meson/Makefile
++++ b/drivers/clk/meson/Makefile
+@@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
+ obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
+ obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
+ obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
++obj-$(CONFIG_COMMON_CLK_MESON_VCLK) += vclk.o
+ 
+ # Amlogic Clock controllers
+ 
+diff --git a/drivers/clk/meson/vclk.c b/drivers/clk/meson/vclk.c
+new file mode 100644
+index 000000000000..47f08a52b49f
+--- /dev/null
++++ b/drivers/clk/meson/vclk.c
+@@ -0,0 +1,141 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org>
++ */
++
++#include <linux/module.h>
++#include "vclk.h"
++
++/* The VCLK gate has a supplementary reset bit to pulse after ungating */
++
++static inline struct clk_regmap_vclk_data *
++clk_get_regmap_vclk_data(struct clk_regmap *clk)
++{
++	return (struct clk_regmap_vclk_data *)clk->data;
++}
++
++static int clk_regmap_vclk_enable(struct clk_hw *hw)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
++
++	meson_parm_write(clk->map, &vclk->enable, 1);
++
++	/* Do a reset pulse */
++	meson_parm_write(clk->map, &vclk->reset, 1);
++	meson_parm_write(clk->map, &vclk->reset, 0);
++
++	return 0;
++}
++
++static void clk_regmap_vclk_disable(struct clk_hw *hw)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
++
++	meson_parm_write(clk->map, &vclk->enable, 0);
++}
++
++static int clk_regmap_vclk_is_enabled(struct clk_hw *hw)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
++
++	return meson_parm_read(clk->map, &vclk->enable);
++}
++
++const struct clk_ops clk_regmap_vclk_ops = {
++	.enable = clk_regmap_vclk_enable,
++	.disable = clk_regmap_vclk_disable,
++	.is_enabled = clk_regmap_vclk_is_enabled,
++};
++EXPORT_SYMBOL_GPL(clk_regmap_vclk_ops);
++
++/* The VCLK Divider has supplementary reset & enable bits */
++
++static inline struct clk_regmap_vclk_div_data *
++clk_get_regmap_vclk_div_data(struct clk_regmap *clk)
++{
++	return (struct clk_regmap_vclk_div_data *)clk->data;
++}
++
++static unsigned long clk_regmap_vclk_div_recalc_rate(struct clk_hw *hw,
++						     unsigned long prate)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
++
++	return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
++				   vclk->table, vclk->flags, vclk->div.width);
++}
++
++static int clk_regmap_vclk_div_determine_rate(struct clk_hw *hw,
++					      struct clk_rate_request *req)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
++
++	return divider_determine_rate(hw, req, vclk->table, vclk->div.width,
++				      vclk->flags);
++}
++
++static int clk_regmap_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
++					unsigned long parent_rate)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
++	int ret;
++
++	ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
++			      vclk->flags);
++	if (ret < 0)
++		return ret;
++
++	meson_parm_write(clk->map, &vclk->div, ret);
++
++	return 0;
++};
++
++static int clk_regmap_vclk_div_enable(struct clk_hw *hw)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
++
++	/* Unreset the divider when ungating */
++	meson_parm_write(clk->map, &vclk->reset, 0);
++	meson_parm_write(clk->map, &vclk->enable, 1);
++
++	return 0;
++}
++
++static void clk_regmap_vclk_div_disable(struct clk_hw *hw)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
++
++	/* Reset the divider when gating */
++	meson_parm_write(clk->map, &vclk->enable, 0);
++	meson_parm_write(clk->map, &vclk->reset, 1);
++}
++
++static int clk_regmap_vclk_div_is_enabled(struct clk_hw *hw)
++{
++	struct clk_regmap *clk = to_clk_regmap(hw);
++	struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
++
++	return meson_parm_read(clk->map, &vclk->enable);
++}
++
++const struct clk_ops clk_regmap_vclk_div_ops = {
++	.recalc_rate = clk_regmap_vclk_div_recalc_rate,
++	.determine_rate = clk_regmap_vclk_div_determine_rate,
++	.set_rate = clk_regmap_vclk_div_set_rate,
++	.enable = clk_regmap_vclk_div_enable,
++	.disable = clk_regmap_vclk_div_disable,
++	.is_enabled = clk_regmap_vclk_div_is_enabled,
++};
++EXPORT_SYMBOL_GPL(clk_regmap_vclk_div_ops);
++
++MODULE_DESCRIPTION("Amlogic vclk clock driver");
++MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/clk/meson/vclk.h b/drivers/clk/meson/vclk.h
+new file mode 100644
+index 000000000000..4f25d7ad2717
+--- /dev/null
++++ b/drivers/clk/meson/vclk.h
+@@ -0,0 +1,51 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org>
++ */
++
++#ifndef __VCLK_H
++#define __VCLK_H
++
++#include "clk-regmap.h"
++#include "parm.h"
++
++/**
++ * struct clk_regmap_vclk_data - vclk regmap backed specific data
++ *
++ * @enable:	vclk enable field
++ * @reset:	vclk reset field
++ * @flags:	hardware-specific flags
++ *
++ * Flags:
++ * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
++ */
++struct clk_regmap_vclk_data {
++	struct parm enable;
++	struct parm reset;
++	u8 flags;
++};
++
++extern const struct clk_ops clk_regmap_vclk_ops;
++
++/**
++ * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
++ *
++ * @div:	divider field
++ * @enable:	vclk divider enable field
++ * @reset:	vclk divider reset field
++ * @table:	array of value/divider pairs, last entry should have div = 0
++ *
++ * Flags:
++ * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
++ */
++struct clk_regmap_vclk_div_data {
++	struct parm div;
++	struct parm enable;
++	struct parm reset;
++	const struct clk_div_table *table;
++	u8 flags;
++};
++
++extern const struct clk_ops clk_regmap_vclk_div_ops;
++
++#endif /* __VCLK_H */
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 8/12] clk: meson: g12a: make VCLK2 and ENCL clock path
+ configurable by CCF
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:19 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-8-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+In order to setup the DSI clock, let's make the unused VCLK2 clock path
+configuration via CCF.
+
+The nocache option is removed from following clocks:
+- vclk2_sel
+- vclk2_input
+- vclk2_div
+- vclk2
+- vclk_div1
+- vclk2_div2_en
+- vclk2_div4_en
+- vclk2_div6_en
+- vclk2_div12_en
+- vclk2_div2
+- vclk2_div4
+- vclk2_div6
+- vclk2_div12
+- cts_encl_sel
+
+vclk2 and vclk2_div uses the newly introduced vclk regmap driver
+to handle the enable and reset bits.
+
+In order to set a rate on cts_encl via the vclk2 clock path,
+the NO_REPARENT flag is set on cts_encl_sel & vclk2_sel in order
+to keep CCF from selection a parent.
+The parents of cts_encl_sel & vclk2_sel are expected to be defined
+in DT.
+
+The following clock scheme is to be used for DSI:
+
+xtal
+\_ gp0_pll_dco
+   \_ gp0_pll
+      |- vclk2_sel
+      |  \_ vclk2_input
+      |     \_ vclk2_div
+      |        \_ vclk2
+      |           \_ vclk2_div1
+      |              \_ cts_encl_sel
+      |                 \_ cts_encl	-> to VPU LCD Encoder
+      |- mipi_dsi_pxclk_sel
+      \_ mipi_dsi_pxclk_div
+         \_ mipi_dsi_pxclk		-> to DSI controller
+
+The mipi_dsi_pxclk_div is set as RO in order to use the same GP0
+for mipi_dsi_pxclk and vclk2_input.
+
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ drivers/clk/meson/g12a.c | 68 +++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 47 insertions(+), 21 deletions(-)
+
+diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
+index cadd824336ad..fb3d9196a1fd 100644
+--- a/drivers/clk/meson/g12a.c
++++ b/drivers/clk/meson/g12a.c
+@@ -22,6 +22,7 @@
+ #include "clk-regmap.h"
+ #include "clk-cpu-dyndiv.h"
+ #include "vid-pll-div.h"
++#include "vclk.h"
+ #include "meson-eeclk.h"
+ #include "g12a.h"
+ 
+@@ -3165,7 +3166,7 @@ static struct clk_regmap g12a_vclk2_sel = {
+ 		.ops = &clk_regmap_mux_ops,
+ 		.parent_hws = g12a_vclk_parent_hws,
+ 		.num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
+-		.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
++		.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 	},
+ };
+ 
+@@ -3193,7 +3194,7 @@ static struct clk_regmap g12a_vclk2_input = {
+ 		.ops = &clk_regmap_gate_ops,
+ 		.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_sel.hw },
+ 		.num_parents = 1,
+-		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3215,19 +3216,32 @@ static struct clk_regmap g12a_vclk_div = {
+ };
+ 
+ static struct clk_regmap g12a_vclk2_div = {
+-	.data = &(struct clk_regmap_div_data){
+-		.offset = HHI_VIID_CLK_DIV,
+-		.shift = 0,
+-		.width = 8,
++	.data = &(struct clk_regmap_vclk_div_data){
++		.div = {
++			.reg_off = HHI_VIID_CLK_DIV,
++			.shift   = 0,
++			.width   = 8,
++		},
++		.enable = {
++			.reg_off = HHI_VIID_CLK_DIV,
++			.shift   = 16,
++			.width   = 1,
++		},
++		.reset = {
++			.reg_off = HHI_VIID_CLK_DIV,
++			.shift   = 17,
++			.width   = 1,
++		},
++		.flags = CLK_DIVIDER_ROUND_CLOSEST,
+ 	},
+ 	.hw.init = &(struct clk_init_data){
+ 		.name = "vclk2_div",
+-		.ops = &clk_regmap_divider_ops,
++		.ops = &clk_regmap_vclk_div_ops,
+ 		.parent_hws = (const struct clk_hw *[]) {
+ 			&g12a_vclk2_input.hw
+ 		},
+ 		.num_parents = 1,
+-		.flags = CLK_GET_RATE_NOCACHE,
++		.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+ 	},
+ };
+ 
+@@ -3246,16 +3260,24 @@ static struct clk_regmap g12a_vclk = {
+ };
+ 
+ static struct clk_regmap g12a_vclk2 = {
+-	.data = &(struct clk_regmap_gate_data){
+-		.offset = HHI_VIID_CLK_CNTL,
+-		.bit_idx = 19,
++	.data = &(struct clk_regmap_vclk_data){
++		.enable = {
++			.reg_off = HHI_VIID_CLK_CNTL,
++			.shift   = 19,
++			.width   = 1,
++		},
++		.reset = {
++			.reg_off = HHI_VIID_CLK_CNTL,
++			.shift   = 15,
++			.width   = 1,
++		},
+ 	},
+ 	.hw.init = &(struct clk_init_data) {
+ 		.name = "vclk2",
+-		.ops = &clk_regmap_gate_ops,
++		.ops = &clk_regmap_vclk_ops,
+ 		.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_div.hw },
+ 		.num_parents = 1,
+-		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++		.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
+ 	},
+ };
+ 
+@@ -3339,7 +3361,7 @@ static struct clk_regmap g12a_vclk2_div1 = {
+ 		.ops = &clk_regmap_gate_ops,
+ 		.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
+ 		.num_parents = 1,
+-		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3353,7 +3375,7 @@ static struct clk_regmap g12a_vclk2_div2_en = {
+ 		.ops = &clk_regmap_gate_ops,
+ 		.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
+ 		.num_parents = 1,
+-		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3367,7 +3389,7 @@ static struct clk_regmap g12a_vclk2_div4_en = {
+ 		.ops = &clk_regmap_gate_ops,
+ 		.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
+ 		.num_parents = 1,
+-		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3381,7 +3403,7 @@ static struct clk_regmap g12a_vclk2_div6_en = {
+ 		.ops = &clk_regmap_gate_ops,
+ 		.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
+ 		.num_parents = 1,
+-		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3395,7 +3417,7 @@ static struct clk_regmap g12a_vclk2_div12_en = {
+ 		.ops = &clk_regmap_gate_ops,
+ 		.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
+ 		.num_parents = 1,
+-		.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3461,6 +3483,7 @@ static struct clk_fixed_factor g12a_vclk2_div2 = {
+ 			&g12a_vclk2_div2_en.hw
+ 		},
+ 		.num_parents = 1,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3474,6 +3497,7 @@ static struct clk_fixed_factor g12a_vclk2_div4 = {
+ 			&g12a_vclk2_div4_en.hw
+ 		},
+ 		.num_parents = 1,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3487,6 +3511,7 @@ static struct clk_fixed_factor g12a_vclk2_div6 = {
+ 			&g12a_vclk2_div6_en.hw
+ 		},
+ 		.num_parents = 1,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3500,6 +3525,7 @@ static struct clk_fixed_factor g12a_vclk2_div12 = {
+ 			&g12a_vclk2_div12_en.hw
+ 		},
+ 		.num_parents = 1,
++		.flags = CLK_SET_RATE_PARENT,
+ 	},
+ };
+ 
+@@ -3561,7 +3587,7 @@ static struct clk_regmap g12a_cts_encl_sel = {
+ 		.ops = &clk_regmap_mux_ops,
+ 		.parent_hws = g12a_cts_parent_hws,
+ 		.num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
+-		.flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
++		.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 	},
+ };
+ 
+@@ -3717,7 +3743,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel = {
+ 		.ops = &clk_regmap_mux_ops,
+ 		.parent_hws = g12a_mipi_dsi_pxclk_parent_hws,
+ 		.num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parent_hws),
+-		.flags = CLK_SET_RATE_NO_REPARENT,
++		.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
+ 	},
+ };
+ 
+@@ -3729,7 +3755,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_div = {
+ 	},
+ 	.hw.init = &(struct clk_init_data){
+ 		.name = "mipi_dsi_pxclk_div",
+-		.ops = &clk_regmap_divider_ops,
++		.ops = &clk_regmap_divider_ro_ops,
+ 		.parent_hws = (const struct clk_hw *[]) {
+ 			&g12a_mipi_dsi_pxclk_sel.hw
+ 		},
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 9/12] drm/meson: gate px_clk when setting rate
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:20 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-9-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+Disable the px_clk when setting the rate to recover a fully
+configured and correctly reset VCLK clock tree after the rate
+is set.
+
+Fixes: 77d9e1e6b846 ("drm/meson: add support for MIPI-DSI transceiver")
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
+index e5fe4e994f43..72abe2057ec3 100644
+--- a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
++++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
+@@ -95,6 +95,7 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
+ 		return ret;
+ 	}
+ 
++	clk_disable_unprepare(mipi_dsi->px_clk);
+ 	ret = clk_set_rate(mipi_dsi->px_clk, mipi_dsi->mode->clock * 1000);
+ 
+ 	if (ret) {
+@@ -103,6 +104,12 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
+ 		return ret;
+ 	}
+ 
++	ret = clk_prepare_enable(mipi_dsi->px_clk);
++	if (ret) {
++		dev_err(mipi_dsi->dev, "Failed to enable DSI Pixel clock (ret %d)\n", ret);
++		return ret;
++	}
++
+ 	switch (mipi_dsi->dsi_device->format) {
+ 	case MIPI_DSI_FMT_RGB888:
+ 		dpi_data_format = DPI_COLOR_24BIT;
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 10/12] arm64: meson: g12-common: add the MIPI DSI nodes
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:21 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-10-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+Add the MIPI DSI Analog & Digital PHY nodes and the DSI control
+nodes with proper port endpoint to the VPU.
+
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi | 70 +++++++++++++++++++++++
+ 1 file changed, 70 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
+index ff68b911b729..7300408262d5 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
+@@ -1663,9 +1663,28 @@ pwrc: power-controller {
+ 								       <250000000>,
+ 								       <0>; /* Do Nothing */
+ 					};
++
++					mipi_analog_dphy: phy {
++						compatible = "amlogic,g12a-mipi-dphy-analog";
++						#phy-cells = <0>;
++						status = "disabled";
++					};
+ 				};
+ 			};
+ 
++			mipi_dphy: phy@44000 {
++				compatible = "amlogic,axg-mipi-dphy";
++				reg = <0x0 0x44000 0x0 0x2000>;
++				clocks = <&clkc CLKID_MIPI_DSI_PHY>;
++				clock-names = "pclk";
++				resets = <&reset RESET_MIPI_DSI_PHY>;
++				reset-names = "phy";
++				phys = <&mipi_analog_dphy>;
++				phy-names = "analog";
++				#phy-cells = <0>;
++				status = "disabled";
++			};
++
+ 			usb3_pcie_phy: phy@46000 {
+ 				compatible = "amlogic,g12a-usb3-pcie-phy";
+ 				reg = <0x0 0x46000 0x0 0x2000>;
+@@ -2152,6 +2171,15 @@ hdmi_tx_out: endpoint {
+ 					remote-endpoint = <&hdmi_tx_in>;
+ 				};
+ 			};
++
++			/* DPI output port */
++			dpi_port: port@2 {
++				reg = <2>;
++
++				dpi_out: endpoint {
++					remote-endpoint = <&mipi_dsi_in>;
++				};
++			};
+ 		};
+ 
+ 		gic: interrupt-controller@ffc01000 {
+@@ -2189,6 +2217,48 @@ gpio_intc: interrupt-controller@f080 {
+ 				amlogic,channel-interrupts = <64 65 66 67 68 69 70 71>;
+ 			};
+ 
++			mipi_dsi: dsi@7000 {
++				compatible = "amlogic,meson-g12a-dw-mipi-dsi";
++				reg = <0x0 0x7000 0x0 0x1000>;
++				resets = <&reset RESET_MIPI_DSI_HOST>;
++				reset-names = "top";
++				clocks = <&clkc CLKID_MIPI_DSI_HOST>,
++					 <&clkc CLKID_MIPI_DSI_PXCLK>,
++					 <&clkc CLKID_CTS_ENCL>;
++				clock-names = "pclk", "bit", "px";
++				phys = <&mipi_dphy>;
++				phy-names = "dphy";
++				#address-cells = <1>;
++				#size-cells = <0>;
++				status = "disabled";
++
++				assigned-clocks = <&clkc CLKID_MIPI_DSI_PXCLK_SEL>,
++					 <&clkc CLKID_CTS_ENCL_SEL>,
++					 <&clkc CLKID_VCLK2_SEL>;
++				assigned-clock-parents = <&clkc CLKID_GP0_PLL>,
++					 <&clkc CLKID_VCLK2_DIV1>,
++					 <&clkc CLKID_GP0_PLL>;
++
++				ports {
++					#address-cells = <1>;
++					#size-cells = <0>;
++
++					/* VPU VENC Input */
++					mipi_dsi_venc_port: port@0 {
++						reg = <0>;
++
++						mipi_dsi_in: endpoint {
++							remote-endpoint = <&dpi_out>;
++						};
++					};
++
++					/* DSI Output */
++					mipi_dsi_panel_port: port@1 {
++						reg = <1>;
++					};
++				};
++			};
++
+ 			watchdog: watchdog@f0d0 {
+ 				compatible = "amlogic,meson-gxbb-wdt";
+ 				reg = <0x0 0xf0d0 0x0 0x10>;
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 11/12] DONOTMERGE: arm64: meson: khadas-vim3l: add DSI
+ panel
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:22 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-11-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+This add nodes to support the Khadas TS050 panel on the
+Khadas VIM3 & VIM3L boards.
+
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ .../boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi   |  2 +-
+ arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi | 74 ++++++++++++++++++++++
+ .../boot/dts/amlogic/meson-sm1-khadas-vim3l.dts    |  2 +-
+ 3 files changed, 76 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi
+index 16dd409051b4..81c3057143b4 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi
+@@ -98,7 +98,7 @@ &pwm_ab {
+ };
+ 
+ &pwm_AO_cd {
+-	pinctrl-0 = <&pwm_ao_d_e_pins>;
++	pinctrl-0 = <&pwm_ao_c_6_pins>, <&pwm_ao_d_e_pins>;
+ 	pinctrl-names = "default";
+ 	clocks = <&xtal>;
+ 	clock-names = "clkin1";
+diff --git a/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi b/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi
+index 514a6dd4b124..aafc37863f2e 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi
+@@ -40,6 +40,14 @@ button-function {
+ 		};
+ 	};
+ 
++	panel_backlight: backlight {
++		compatible = "pwm-backlight";
++		pwms = <&pwm_AO_cd 0 25000 0>;
++		brightness-levels = <0 255>;
++		num-interpolated-steps = <255>;
++		default-brightness-level = <200>;
++	};
++
+ 	leds {
+ 		compatible = "gpio-leds";
+ 
+@@ -358,6 +366,23 @@ rtc: rtc@51 {
+ 	};
+ };
+ 
++&i2c3 {
++	status = "okay";
++	pinctrl-0 = <&i2c3_sda_a_pins>, <&i2c3_sck_a_pins>;
++	pinctrl-names = "default";
++
++	touch-controller@38 {
++		compatible = "edt,edt-ft5206";
++		reg = <0x38>;
++		interrupt-parent = <&gpio_intc>;
++		interrupts = <66 IRQ_TYPE_EDGE_FALLING>; /* GPIOA_5 */
++		reset-gpios = <&gpio_expander 6 GPIO_ACTIVE_LOW>;
++		touchscreen-size-x = <1080>;
++		touchscreen-size-y = <1920>;
++		status = "okay";
++	};
++};
++
+ &ir {
+ 	status = "okay";
+ 	pinctrl-0 = <&remote_input_ao_pins>;
+@@ -365,6 +390,55 @@ &ir {
+ 	linux,rc-map-name = "rc-khadas";
+ };
+ 
++&mipi_dsi {
++	status = "okay";
++
++	assigned-clocks = <&clkc CLKID_GP0_PLL>,
++			  <&clkc CLKID_MIPI_DSI_PXCLK_SEL>,
++			  <&clkc CLKID_MIPI_DSI_PXCLK>,
++			  <&clkc CLKID_CTS_ENCL_SEL>,
++			  <&clkc CLKID_VCLK2_SEL>;
++	assigned-clock-parents = <0>,
++				 <&clkc CLKID_GP0_PLL>,
++				 <0>,
++				 <&clkc CLKID_VCLK2_DIV1>,
++				 <&clkc CLKID_GP0_PLL>;
++	assigned-clock-rates = <960000000>,
++			       <0>,
++			       <960000000>,
++			       <0>,
++			       <0>;
++
++	panel@0 {
++		compatible = "khadas,ts050";
++		reset-gpios = <&gpio_expander 0 GPIO_ACTIVE_LOW>;
++		enable-gpios = <&gpio_expander 1 GPIO_ACTIVE_HIGH>;
++		power-supply = <&vcc_3v3>;
++		backlight = <&panel_backlight>;
++		reg = <0>;
++
++		port {
++			mipi_in_panel: endpoint {
++				remote-endpoint = <&mipi_out_panel>;
++			};
++		};
++	};
++};
++
++&mipi_analog_dphy {
++	status = "okay";
++};
++
++&mipi_dphy {
++	status = "okay";
++};
++
++&mipi_dsi_panel_port {
++	mipi_out_panel: endpoint {
++		remote-endpoint = <&mipi_in_panel>;
++	};
++};
++
+ &pcie {
+ 	reset-gpios = <&gpio GPIOA_8 GPIO_ACTIVE_LOW>;
+ };
+diff --git a/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts b/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts
+index 9c0b544e2209..cb52a55ab70a 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts
++++ b/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts
+@@ -76,7 +76,7 @@ &cpu3 {
+ };
+ 
+ &pwm_AO_cd {
+-	pinctrl-0 = <&pwm_ao_d_e_pins>;
++	pinctrl-0 = <&pwm_ao_c_6_pins>, <&pwm_ao_d_e_pins>;
+ 	pinctrl-names = "default";
+ 	clocks = <&xtal>;
+ 	clock-names = "clkin1";
+-- 
+2.34.1
+
+From git@z Thu Jan  1 00:00:00 1970
+Subject: [PATCH v9 12/12] arm64: dts: amlogic: meson-g12b-bananapi-cm4: add
+ support for MNT Reform2 with CM4 adaper
+From: Neil Armstrong <neil.armstrong@linaro.org>
+Date: Fri, 24 Nov 2023 09:41:23 +0100
+Message-Id: <20231124-amlogic-v6-4-upstream-dsi-ccf-vim3-v9-12-95256ed139e6@linaro.org>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 7bit
+
+This adds a basic devicetree for the MNT Reform2 DIY laptop when using a
+CM4 adapter and a BPI-CM4 module.
+
+Co-developed-by: Lukas F. Hartmann <lukas@mntre.com>
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+---
+ arch/arm64/boot/dts/amlogic/Makefile               |   1 +
+ .../meson-g12b-bananapi-cm4-mnt-reform2.dts        | 384 +++++++++++++++++++++
+ 2 files changed, 385 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile
+index cc8b34bd583d..58b5b332bdb7 100644
+--- a/arch/arm64/boot/dts/amlogic/Makefile
++++ b/arch/arm64/boot/dts/amlogic/Makefile
+@@ -15,6 +15,7 @@ dtb-$(CONFIG_ARCH_MESON) += meson-g12a-x96-max.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-g12b-a311d-bananapi-m2s.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-g12b-a311d-khadas-vim3.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-g12b-bananapi-cm4-cm4io.dtb
++dtb-$(CONFIG_ARCH_MESON) += meson-g12b-bananapi-cm4-mnt-reform2.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-g12b-gsking-x.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-g12b-gtking-pro.dtb
+ dtb-$(CONFIG_ARCH_MESON) += meson-g12b-gtking.dtb
+diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4-mnt-reform2.dts b/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4-mnt-reform2.dts
+new file mode 100644
+index 000000000000..003efed529ba
+--- /dev/null
++++ b/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4-mnt-reform2.dts
+@@ -0,0 +1,384 @@
++// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
++/*
++ * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org>
++ * Copyright 2023 MNT Research GmbH
++ */
++
++/dts-v1/;
++
++#include "meson-g12b-bananapi-cm4.dtsi"
++#include <dt-bindings/input/input.h>
++#include <dt-bindings/leds/common.h>
++#include <dt-bindings/sound/meson-g12a-tohdmitx.h>
++
++/ {
++	model = "MNT Reform 2 with BPI-CM4 Module";
++	compatible = "mntre,reform2-cm4", "bananapi,bpi-cm4", "amlogic,a311d", "amlogic,g12b";
++	chassis-type = "laptop";
++
++	aliases {
++		ethernet0 = &ethmac;
++		i2c0 = &i2c1;
++		i2c1 = &i2c3;
++	};
++
++	hdmi_connector: hdmi-connector {
++		compatible = "hdmi-connector";
++		type = "a";
++
++		port {
++			hdmi_connector_in: endpoint {
++				remote-endpoint = <&hdmi_tx_tmds_out>;
++			};
++		};
++	};
++
++	leds {
++		compatible = "gpio-leds";
++
++		led-blue {
++			color = <LED_COLOR_ID_BLUE>;
++			function = LED_FUNCTION_STATUS;
++			gpios = <&gpio_ao GPIOAO_7 GPIO_ACTIVE_HIGH>;
++			linux,default-trigger = "heartbeat";
++		};
++
++		led-green {
++			color = <LED_COLOR_ID_GREEN>;
++			function = LED_FUNCTION_STATUS;
++			gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>;
++		};
++	};
++
++	sound {
++		compatible = "amlogic,axg-sound-card";
++		model = "MNT-REFORM2-BPI-CM4";
++		audio-widgets = "Headphone", "Headphone Jack",
++				"Speaker", "External Speaker",
++				"Microphone", "Mic Jack";
++		audio-aux-devs = <&tdmout_a>, <&tdmout_b>, <&tdmin_b>;
++		audio-routing =	"TDMOUT_A IN 0", "FRDDR_A OUT 0",
++				"TDMOUT_A IN 1", "FRDDR_B OUT 0",
++				"TDMOUT_A IN 2", "FRDDR_C OUT 0",
++				"TDM_A Playback", "TDMOUT_A OUT",
++				"TDMOUT_B IN 0", "FRDDR_A OUT 1",
++				"TDMOUT_B IN 1", "FRDDR_B OUT 1",
++				"TDMOUT_B IN 2", "FRDDR_C OUT 1",
++				"TDM_B Playback", "TDMOUT_B OUT",
++				"TDMIN_B IN 1", "TDM_B Capture",
++				"TDMIN_B IN 4", "TDM_B Loopback",
++				"TODDR_A IN 1", "TDMIN_B OUT",
++				"TODDR_B IN 1", "TDMIN_B OUT",
++				"TODDR_C IN 1", "TDMIN_B OUT",
++				"Headphone Jack", "HP_L",
++				"Headphone Jack", "HP_R",
++				"External Speaker", "SPK_LP",
++				"External Speaker", "SPK_LN",
++				"External Speaker", "SPK_RP",
++				"External Speaker", "SPK_RN",
++				"LINPUT1", "Mic Jack",
++				"Mic Jack", "MICB";
++
++		assigned-clocks = <&clkc CLKID_MPLL2>,
++					<&clkc CLKID_MPLL0>,
++					<&clkc CLKID_MPLL1>;
++		assigned-clock-parents = <0>, <0>, <0>;
++		assigned-clock-rates = <294912000>,
++				       <270950400>,
++				       <393216000>;
++
++		dai-link-0 {
++			sound-dai = <&frddr_a>;
++		};
++
++		dai-link-1 {
++			sound-dai = <&frddr_b>;
++		};
++
++		dai-link-2 {
++			sound-dai = <&frddr_c>;
++		};
++
++		dai-link-3 {
++			sound-dai = <&toddr_a>;
++		};
++
++		dai-link-4 {
++			sound-dai = <&toddr_b>;
++		};
++
++		dai-link-5 {
++			sound-dai = <&toddr_c>;
++		};
++
++		/* 8ch hdmi interface */
++		dai-link-6 {
++			sound-dai = <&tdmif_a>;
++			dai-format = "i2s";
++			dai-tdm-slot-tx-mask-0 = <1 1>;
++			dai-tdm-slot-tx-mask-1 = <1 1>;
++			dai-tdm-slot-tx-mask-2 = <1 1>;
++			dai-tdm-slot-tx-mask-3 = <1 1>;
++			mclk-fs = <256>;
++
++			codec {
++				sound-dai = <&tohdmitx TOHDMITX_I2S_IN_A>;
++			};
++		};
++
++		/* Analog Audio */
++		dai-link-7 {
++			sound-dai = <&tdmif_b>;
++			dai-format = "i2s";
++			dai-tdm-slot-tx-mask-0 = <1 1>;
++			mclk-fs = <256>;
++
++			codec {
++				sound-dai = <&wm8960>;
++			};
++		};
++
++		/* hdmi glue */
++		dai-link-8 {
++			sound-dai = <&tohdmitx TOHDMITX_I2S_OUT>;
++
++			codec {
++				sound-dai = <&hdmi_tx>;
++			};
++		};
++	};
++
++	reg_main_1v8: regulator-main-1v8 {
++		compatible = "regulator-fixed";
++		regulator-name = "1V8";
++		regulator-min-microvolt = <1800000>;
++		regulator-max-microvolt = <1800000>;
++		vin-supply = <&reg_main_3v3>;
++	};
++
++	reg_main_1v2: regulator-main-1v2 {
++		compatible = "regulator-fixed";
++		regulator-name = "1V2";
++		regulator-min-microvolt = <1200000>;
++		regulator-max-microvolt = <1200000>;
++		vin-supply = <&reg_main_5v>;
++	};
++
++	reg_main_3v3: regulator-main-3v3 {
++		compatible = "regulator-fixed";
++		regulator-name = "3V3";
++		regulator-min-microvolt = <3300000>;
++		regulator-max-microvolt = <3300000>;
++	};
++
++	reg_main_5v: regulator-main-5v {
++		compatible = "regulator-fixed";
++		regulator-name = "5V";
++		regulator-min-microvolt = <5000000>;
++		regulator-max-microvolt = <5000000>;
++	};
++
++	reg_main_usb: regulator-main-usb {
++		compatible = "regulator-fixed";
++		regulator-name = "USB_PWR";
++		regulator-min-microvolt = <5000000>;
++		regulator-max-microvolt = <5000000>;
++		vin-supply = <&reg_main_5v>;
++	};
++
++	backlight: backlight {
++		compatible = "pwm-backlight";
++		pwms = <&pwm_AO_ab 0 10000 0>;
++		power-supply = <&reg_main_usb>;
++		enable-gpios = <&gpio 58 GPIO_ACTIVE_HIGH>;
++		brightness-levels = <0 32 64 128 160 200 255>;
++		default-brightness-level = <6>;
++	};
++
++	panel {
++		compatible = "innolux,n125hce-gn1";
++		power-supply = <&reg_main_3v3>;
++		backlight = <&backlight>;
++		no-hpd;
++
++		port {
++			panel_in: endpoint {
++				remote-endpoint = <&edp_bridge_out>;
++			};
++		};
++	};
++
++	clock_12288: clock_12288 {
++		compatible = "fixed-clock";
++		#clock-cells = <0>;
++		clock-frequency = <12288000>;
++	};
++};
++
++&mipi_analog_dphy {
++	status = "okay";
++};
++
++&mipi_dphy {
++	status = "okay";
++};
++
++&mipi_dsi {
++	status = "okay";
++
++	assigned-clocks = <&clkc CLKID_GP0_PLL>,
++			  <&clkc CLKID_MIPI_DSI_PXCLK_SEL>,
++			  <&clkc CLKID_MIPI_DSI_PXCLK>,
++			  <&clkc CLKID_CTS_ENCL_SEL>,
++			  <&clkc CLKID_VCLK2_SEL>;
++	assigned-clock-parents = <0>,
++				 <&clkc CLKID_GP0_PLL>,
++				 <0>,
++				 <&clkc CLKID_VCLK2_DIV1>,
++				 <&clkc CLKID_GP0_PLL>;
++	assigned-clock-rates = <936000000>,
++			       <0>,
++			       <936000000>,
++			       <0>,
++			       <0>;
++};
++
++&mipi_dsi_panel_port {
++	mipi_dsi_out: endpoint {
++		remote-endpoint = <&edp_bridge_in>;
++	};
++};
++
++&cecb_AO {
++	status = "okay";
++};
++
++&ethmac {
++	status = "okay";
++};
++
++&hdmi_tx {
++	status = "okay";
++};
++
++&hdmi_tx_tmds_port {
++	hdmi_tx_tmds_out: endpoint {
++		remote-endpoint = <&hdmi_connector_in>;
++	};
++};
++
++&pwm_AO_ab {
++	pinctrl-names = "default";
++	pinctrl-0 = <&pwm_ao_a_pins>;
++	status = "okay";
++};
++
++&i2c0 {
++	status = "okay";
++};
++
++&i2c3 {
++	status = "okay";
++
++	edp_bridge: bridge@2c {
++		compatible = "ti,sn65dsi86";
++		reg = <0x2c>;
++		enable-gpios = <&gpio GPIOX_10 GPIO_ACTIVE_HIGH>; // PIN_24 / GPIO8
++		vccio-supply = <&reg_main_1v8>;
++		vpll-supply = <&reg_main_1v8>;
++		vcca-supply = <&reg_main_1v2>;
++		vcc-supply = <&reg_main_1v2>;
++
++		ports {
++			#address-cells = <1>;
++			#size-cells = <0>;
++
++			port@0 {
++				reg = <0>;
++
++				edp_bridge_in: endpoint {
++					remote-endpoint = <&mipi_dsi_out>;
++				};
++			};
++
++			port@1 {
++				reg = <1>;
++
++				edp_bridge_out: endpoint {
++					remote-endpoint = <&panel_in>;
++				};
++			};
++		};
++	};
++};
++
++&i2c2 {
++	status = "okay";
++
++	wm8960: codec@1a {
++		compatible = "wlf,wm8960";
++		reg = <0x1a>;
++		clocks = <&clock_12288>;
++		clock-names = "mclk";
++		#sound-dai-cells = <0>;
++		wlf,shared-lrclk;
++	};
++
++	rtc@68 {
++		compatible = "nxp,pcf8523";
++		reg = <0x68>;
++	};
++};
++
++&pcie {
++	status = "okay";
++};
++
++&sd_emmc_b {
++	status = "okay";
++};
++
++&tdmif_a {
++	status = "okay";
++};
++
++&tdmout_a {
++	status = "okay";
++};
++
++&tdmif_b {
++	pinctrl-0 = <&tdm_b_dout0_pins>, <&tdm_b_fs_pins>, <&tdm_b_sclk_pins>, <&tdm_b_din1_pins>;
++	pinctrl-names = "default";
++
++	assigned-clocks = <&clkc_audio AUD_CLKID_TDM_SCLK_PAD1>,
++			  <&clkc_audio AUD_CLKID_TDM_LRCLK_PAD1>;
++	assigned-clock-parents = <&clkc_audio AUD_CLKID_MST_B_SCLK>,
++				 <&clkc_audio AUD_CLKID_MST_B_LRCLK>;
++	assigned-clock-rates = <0>, <0>;
++};
++
++&tdmin_b {
++	status = "okay";
++};
++
++&toddr_a {
++	status = "okay";
++};
++
++&toddr_b {
++	status = "okay";
++};
++
++&toddr_c {
++	status = "okay";
++};
++
++&tohdmitx {
++	status = "okay";
++};
++
++&usb {
++	dr_mode = "host";
++
++	status = "okay";
++};
+-- 
+2.34.1
+
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0011-dw-mipi-dsi-phy-stop-wait-time.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0011-dw-mipi-dsi-phy-stop-wait-time.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3941afb5ed2c4dc03b7731dfaa849c878cdf28ee
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0011-dw-mipi-dsi-phy-stop-wait-time.patch
@@ -0,0 +1,32 @@
+From 573d769372d0dd1b6e7199048764cd851a511750 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 23:02:47 +0200
+Subject: [PATCH 11/21] dw-mipi-dsi-phy-stop-wait-time
+
+---
+ drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
++++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+@@ -605,8 +605,10 @@ static void dw_mipi_dsi_video_mode_confi
+ 	 */
+ 	val = ENABLE_LOW_POWER;
+ 
+-	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
++	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
+ 		val |= VID_MODE_TYPE_BURST;
++		printk(KERN_ALERT "DEBUG: dw-mipi-dsi VID_MODE_TYPE_BURST\n");
++	}
+ 	else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+ 		val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
+ 	else
+@@ -886,7 +888,7 @@ static void dw_mipi_dsi_dphy_interface_c
+ 	 * stop wait time should be the maximum between host dsi
+ 	 * and panel stop wait times
+ 	 */
+-	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
++	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x1) |
+ 		  N_LANES(dsi->lanes));
+ }
+ 
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0012-innolux-n125hce-gn1-timing-tweaks.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0012-innolux-n125hce-gn1-timing-tweaks.patch
new file mode 100644
index 0000000000000000000000000000000000000000..17e38143b5486f4fa4e4360f12452305a112cc86
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0012-innolux-n125hce-gn1-timing-tweaks.patch
@@ -0,0 +1,57 @@
+From 6a51b0b4e1bae07da42ab82c41628c5ca1997c6f Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 23:02:48 +0200
+Subject: [PATCH 12/21] innolux-n125hce-gn1-timing-tweaks
+
+---
+ drivers/gpu/drm/panel/panel-edp.c | 32 +++++++++++++++++++++++++++++++
+ 1 file changed, 32 insertions(+)
+
+--- a/drivers/gpu/drm/panel/panel-edp.c
++++ b/drivers/gpu/drm/panel/panel-edp.c
+@@ -1318,6 +1318,35 @@ static const struct panel_desc innolux_n
+ 	},
+ };
+ 
++static const struct drm_display_mode innolux_n125hce_gn1_a311d_mode = {
++	//.clock = 162000, 70Hz
++	// wrong colors are only if this mismatches dtb assigned clock
++	.clock = 156000, // 68Hz
++	//.clock = 153600, // 65Hz (921.6mhz)
++	//.clock = 138780, // 59Hz (832.68mhz)
++	//.clock = 138780, // 59Hz (832.68mhz)
++	.hdisplay = 1920,
++	.hsync_start = 1920 + 40,
++	.hsync_end = 1920 + 40 + 40,
++	.htotal = 1920 + 40 + 40 + 80,
++	.vdisplay = 1080,
++	// a311d mipi dsi driver currently calculates these wrongly,
++	// so we have to work around that here
++	.vsync_start = 1080 + 1,
++	.vsync_end = 1080 + 1 + 4,
++	.vtotal = 1080 + 1 + 4 + 27,
++};
++
++static const struct panel_desc innolux_n125hce_gn1_a311d = {
++	.modes = &innolux_n125hce_gn1_a311d_mode,
++	.num_modes = 1,
++	.bpc = 8,
++	.size = {
++		.width = 276,
++		.height = 155,
++	},
++};
++
+ static const struct drm_display_mode innolux_p120zdg_bf1_mode = {
+ 	.clock = 206016,
+ 	.hdisplay = 2160,
+@@ -1736,6 +1765,9 @@ static const struct of_device_id platfor
+ 		.compatible = "innolux,n125hce-gn1",
+ 		.data = &innolux_n125hce_gn1,
+ 	}, {
++		.compatible = "innolux,n125hce-gn1-a311d",
++		.data = &innolux_n125hce_gn1_a311d,
++	}, {
+ 		.compatible = "innolux,p120zdg-bf1",
+ 		.data = &innolux_p120zdg_bf1,
+ 	}, {
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0013-meson-viu-hold-fifo-lines.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0013-meson-viu-hold-fifo-lines.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d255d2021439ec0c4417f389b642397b23bd1204
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0013-meson-viu-hold-fifo-lines.patch
@@ -0,0 +1,25 @@
+From f45675995ec2fab90adec43426039dd6ffda94a7 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 23:02:49 +0200
+Subject: [PATCH 13/21] meson-viu-hold-fifo-lines
+
+---
+ drivers/gpu/drm/meson/meson_viu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c
+index cd399b0b7..119d00f23 100644
+--- a/drivers/gpu/drm/meson/meson_viu.c
++++ b/drivers/gpu/drm/meson/meson_viu.c
+@@ -441,7 +441,7 @@ void meson_viu_init(struct meson_drm *priv)
+ 		VIU_OSD_FIFO_LIMITS(2);      /* fifo_lim: 2*16=32 */
+ 
+ 	if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
+-		reg |= (VIU_OSD_BURST_LENGTH_32 | VIU_OSD_HOLD_FIFO_LINES(31));
++		reg |= (VIU_OSD_BURST_LENGTH_32 | VIU_OSD_HOLD_FIFO_LINES(4)); // FIXME altered
+ 	else
+ 		reg |= (VIU_OSD_BURST_LENGTH_64 | VIU_OSD_HOLD_FIFO_LINES(4));
+ 
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0014-meson-venc-sync.patch.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0014-meson-venc-sync.patch.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bfece0535b57d5c0a5b76f586e97e97dc79a7f2c
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0014-meson-venc-sync.patch.patch
@@ -0,0 +1,34 @@
+From 46a66919ef279bace11147d89c373bc4440a6d5a Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 23:02:50 +0200
+Subject: [PATCH 14/21] meson-venc-sync.patch
+
+---
+ drivers/gpu/drm/meson/meson_venc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
+index 3bf0d6e4f..0e9a9bb07 100644
+--- a/drivers/gpu/drm/meson/meson_venc.c
++++ b/drivers/gpu/drm/meson/meson_venc.c
+@@ -1711,7 +1711,7 @@ void meson_venc_mipi_dsi_mode_set(struct meson_drm *priv,
+ 	writel_relaxed(vavon_eline, priv->io_base + _REG(L_OEV1_VE_ADDR));
+ 
+ 	/* Hsync signal for TTL */
+-	if (mode->flags & DRM_MODE_FLAG_PHSYNC) {
++	if (mode->flags & DRM_MODE_FLAG_NHSYNC) {
+ 		writel_relaxed(hso_begin, priv->io_base + _REG(L_STH1_HS_ADDR));
+ 		writel_relaxed(hso_end, priv->io_base + _REG(L_STH1_HE_ADDR));
+ 	} else {
+@@ -1724,7 +1724,7 @@ void meson_venc_mipi_dsi_mode_set(struct meson_drm *priv,
+ 	/* Vsync signal for TTL */
+ 	writel_relaxed(vso_begin, priv->io_base + _REG(L_STV1_HS_ADDR));
+ 	writel_relaxed(vso_end, priv->io_base + _REG(L_STV1_HE_ADDR));
+-	if (mode->flags & DRM_MODE_FLAG_PVSYNC) {
++	if (mode->flags & DRM_MODE_FLAG_NVSYNC) {
+ 		writel_relaxed(vso_bline, priv->io_base + _REG(L_STV1_VS_ADDR));
+ 		writel_relaxed(vso_eline, priv->io_base + _REG(L_STV1_VE_ADDR));
+ 	} else {
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0015-meson-dw-mipi-dsi-sync-invert.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0015-meson-dw-mipi-dsi-sync-invert.patch
new file mode 100644
index 0000000000000000000000000000000000000000..434371b03238980588e56b99ac084f622056b9bc
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0015-meson-dw-mipi-dsi-sync-invert.patch
@@ -0,0 +1,22 @@
+From 55cc68c56db6bb4b27bb7e52d63d37172dbfb51b Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 23:02:51 +0200
+Subject: [PATCH 15/21] meson-dw-mipi-dsi-sync-invert
+
+---
+ drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
++++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
+@@ -129,7 +129,9 @@ static int dw_mipi_dsi_phy_init(void *pr
+ 		       FIELD_PREP(MIPI_DSI_TOP_IN_COLOR_MODE, venc_data_width) |
+ 		       FIELD_PREP(MIPI_DSI_TOP_COMP2_SEL, 2) |
+ 		       FIELD_PREP(MIPI_DSI_TOP_COMP1_SEL, 1) |
+-		       FIELD_PREP(MIPI_DSI_TOP_COMP0_SEL, 0),
++		       FIELD_PREP(MIPI_DSI_TOP_COMP0_SEL, 0) |
++		           (mipi_dsi->mode->flags & DRM_MODE_FLAG_NHSYNC ? 0 : MIPI_DSI_TOP_HSYNC_INVERT) |
++		           (mipi_dsi->mode->flags & DRM_MODE_FLAG_NVSYNC ? 0 : MIPI_DSI_TOP_VSYNC_INVERT),
+ 			mipi_dsi->base + MIPI_DSI_TOP_CNTL);
+ 
+ 	return phy_configure(mipi_dsi->phy, &mipi_dsi->phy_opts);
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0016-sn65dsi86-burst-mode-support.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0016-sn65dsi86-burst-mode-support.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d9c53ea0416cbc0c6b9ca7ec0648c5c66afacc83
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0016-sn65dsi86-burst-mode-support.patch
@@ -0,0 +1,22 @@
+From 6d6b45ed6cdf1ed5c2efcd05019cea4a1df0a962 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 9 Jul 2023 23:02:51 +0200
+Subject: [PATCH 16/21] sn65dsi86-burst-mode-support
+
+---
+ drivers/gpu/drm/bridge/ti-sn65dsi86.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
++++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+@@ -717,6 +717,10 @@ static int ti_sn_attach_host(struct auxi
+ 	dsi->lanes = 4;
+ 	dsi->format = MIPI_DSI_FMT_RGB888;
+ 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
++	if (of_property_read_bool(pdata->dev->of_node, "burst-mode")) {
++		dsi->mode_flags |= MIPI_DSI_MODE_VIDEO_BURST;
++		printk(KERN_ALERT "DEBUG ti_sn65dsi86: burst mode enabled\n");
++	};
+ 
+ 	/* check if continuous dsi clock is required or not */
+ 	pm_runtime_get_sync(dev);
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0017-hdmi-disable-broken-scdc.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0017-hdmi-disable-broken-scdc.patch
new file mode 100644
index 0000000000000000000000000000000000000000..359e68899dd69593b54872a811de3c54780548bb
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0017-hdmi-disable-broken-scdc.patch
@@ -0,0 +1,21 @@
+From 7d825f9ded4fde6b8238c87d63886a3f04e0d7e6 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 17 Sep 2023 17:43:09 +0200
+Subject: [PATCH 17/21] hdmi-disable-broken-scdc
+
+---
+ drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
++++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+@@ -1375,6 +1375,9 @@ static bool dw_hdmi_support_scdc(struct
+ 	if (!hdmi->ddc)
+ 		return false;
+ 
++	// disable, scdc is broken
++	return false;
++
+ 	/* Disable if SCDC is not supported, or if an HF-VSDB block is absent */
+ 	if (!display->hdmi.scdc.supported ||
+ 	    !display->hdmi.scdc.scrambling.supported)
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0018-sn65dsi86-never-turn-off.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0018-sn65dsi86-never-turn-off.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f54dd6ec07ab73b6596295f57b48d848421545df
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0018-sn65dsi86-never-turn-off.patch
@@ -0,0 +1,61 @@
+From 87c72ab5cd34dfa5fe1c91c475e5b6d4b8e1aad6 Mon Sep 17 00:00:00 2001
+From: "Lukas F. Hartmann" <lukas@mntre.com>
+Date: Sun, 17 Sep 2023 17:43:10 +0200
+Subject: [PATCH 18/21] sn65dsi86-never-turn-off
+
+---
+ drivers/gpu/drm/bridge/ti-sn65dsi86.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
++++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+@@ -347,6 +347,9 @@ static void ti_sn65dsi86_disable_comms(s
+ {
+ 	mutex_lock(&pdata->comms_mutex);
+ 
++	printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
++	return;
++
+ 	pdata->comms_enabled = false;
+ 	clk_disable_unprepare(pdata->refclk);
+ 
+@@ -387,6 +390,9 @@ static int __maybe_unused ti_sn65dsi86_s
+ 	struct ti_sn65dsi86 *pdata = dev_get_drvdata(dev);
+ 	int ret;
+ 
++	printk(KERN_ALERT "DEBUG: ti_sn65dsi86_suspend skipped.\n");
++	return 0;
++
+ 	if (pdata->refclk)
+ 		ti_sn65dsi86_disable_comms(pdata);
+ 
+@@ -820,6 +826,9 @@ static void ti_sn_bridge_atomic_disable(
+ {
+ 	struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+ 
++	printk(KERN_ALERT "DEBUG: ti_sn_bridge_atomic_disable skipped.\n");
++	return;
++
+ 	/* disable video stream */
+ 	regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, VSTREAM_ENABLE, 0);
+ }
+@@ -1185,6 +1194,9 @@ static void ti_sn_bridge_atomic_post_dis
+ {
+ 	struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+ 
++	printk(KERN_ALERT "DEBUG: ti_sn_bridge_atomic_post_disable skipped.\n");
++	return;
++
+ 	/* semi auto link training mode OFF */
+ 	regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0);
+ 	/* Num lanes to 0 as per power sequencing in data sheet */
+@@ -1871,6 +1883,9 @@ static inline void ti_sn_gpio_unregister
+ 
+ static void ti_sn65dsi86_runtime_disable(void *data)
+ {
++	printk(KERN_ALERT "DEBUG: Passed %s %d \n",__FUNCTION__,__LINE__);
++	return;
++
+ 	pm_runtime_dont_use_autosuspend(data);
+ 	pm_runtime_disable(data);
+ }
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0019-wifi-rtw88-sdio-Honor-the-host-max_req_size-in-the-R.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0019-wifi-rtw88-sdio-Honor-the-host-max_req_size-in-the-R.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a6b93f383ad29b99dd7634c9e4be8eb4e7be7e07
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0019-wifi-rtw88-sdio-Honor-the-host-max_req_size-in-the-R.patch
@@ -0,0 +1,100 @@
+From 66849f4a78fa5e96fd152d22cd51cefef5da7036 Mon Sep 17 00:00:00 2001
+From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Date: Sun, 9 Jul 2023 21:36:29 +0200
+Subject: [PATCH 19/21] wifi: rtw88: sdio: Honor the host max_req_size in the
+ RX path
+
+Lukas reports skb_over_panic errors on his Banana Pi BPI-CM4 which comes
+with an Amlogic A311D (G12B) SoC and a RTL8822CS SDIO wifi/Bluetooth
+combo card. The error he observed is identical to what has been fixed
+in commit e967229ead0e ("wifi: rtw88: sdio: Check the HISR RX_REQUEST
+bit in rtw_sdio_rx_isr()") but that commit didn't fix Lukas' problem.
+
+Lukas found that disabling or limiting RX aggregation works around the
+problem for some time (but does not fully fix it). In the following
+discussion a few key topics have been discussed which have an impact on
+this problem:
+- The Amlogic A311D (G12B) SoC has a hardware bug in the SDIO controller
+  which prevents DMA transfers. Instead all transfers need to go through
+  the controller SRAM which limits transfers to 1536 bytes
+- rtw88 chips don't split incoming (RX) packets, so if a big packet is
+  received this is forwarded to the host in it's original form
+- rtw88 chips can do RX aggregation, meaning more multiple incoming
+  packets can be pulled by the host from the card with one MMC/SDIO
+  transfer. This Depends on settings in the REG_RXDMA_AGG_PG_TH
+  register (BIT_RXDMA_AGG_PG_TH limits the number of packets that will
+  be aggregated, BIT_DMA_AGG_TO_V1 configures a timeout for aggregation
+  and BIT_EN_PRE_CALC makes the chip honor the limits more effectively)
+
+Use multiple consecutive reads in rtw_sdio_read_port() and limit the
+number of bytes which are copied by the host from the card in one
+MMC/SDIO transfer. This allows receiving a buffer that's larger than
+the hosts max_req_size (number of bytes which can be transferred in
+one MMC/SDIO transfer). As a result of this the skb_over_panic error
+is gone as the rtw88 driver is now able to receive more than 1536 bytes
+from the card (either because the incoming packet is larger than that
+or because multiple packets have been aggregated).
+
+In case of an receive errors (-EILSEQ has been observed by Lukas) we
+need to drain the remaining data from the card's buffer, otherwise the
+card will return corrupt data for the next rtw_sdio_read_port() call.
+
+Fixes: 65371a3f14e7 ("wifi: rtw88: sdio: Add HCI implementation for SDIO based chipsets")
+Reported-by: Lukas F. Hartmann <lukas@mntre.com>
+Closes: https://lore.kernel.org/linux-wireless/CAFBinCBaXtebixKbjkWKW_WXc5k=NdGNaGUjVE8NCPNxOhsb2g@mail.gmail.com/
+Suggested-by: Ping-Ke Shih <pkshih@realtek.com>
+Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+---
+ drivers/net/wireless/realtek/rtw88/sdio.c | 30 +++++++++++++++++------
+ 1 file changed, 23 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
+index 2c1fb2dab..0a711effc 100644
+--- a/drivers/net/wireless/realtek/rtw88/sdio.c
++++ b/drivers/net/wireless/realtek/rtw88/sdio.c
+@@ -500,19 +500,35 @@ static u32 rtw_sdio_get_tx_addr(struct rtw_dev *rtwdev, size_t size,
+ static int rtw_sdio_read_port(struct rtw_dev *rtwdev, u8 *buf, size_t count)
+ {
+ 	struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
++	struct mmc_host *host = rtwsdio->sdio_func->card->host;
+ 	bool bus_claim = rtw_sdio_bus_claim_needed(rtwsdio);
+ 	u32 rxaddr = rtwsdio->rx_addr++;
+-	int ret;
++	int ret, err = 0;
++	size_t bytes;
+ 
+ 	if (bus_claim)
+ 		sdio_claim_host(rtwsdio->sdio_func);
+ 
+-	ret = sdio_memcpy_fromio(rtwsdio->sdio_func, buf,
+-				 RTW_SDIO_ADDR_RX_RX0FF_GEN(rxaddr), count);
+-	if (ret)
+-		rtw_warn(rtwdev,
+-			 "Failed to read %zu byte(s) from SDIO port 0x%08x",
+-			 count, rxaddr);
++	while (count > 0) {
++		bytes = min_t(size_t, host->max_req_size, count);
++
++		err = sdio_memcpy_fromio(rtwsdio->sdio_func, buf,
++					 RTW_SDIO_ADDR_RX_RX0FF_GEN(rxaddr),
++					 bytes);
++		if (err) {
++			rtw_warn(rtwdev,
++				 "Failed to read %zu byte(s) from SDIO port 0x%08x: %d",
++				 bytes, rxaddr, err);
++			ret = err;
++			/* Don't stop here - instead drain the remaining data
++			 * from the card's buffer, else the card will return
++			 * corrupt data for the next rtw_sdio_read_port() call.
++			 */
++		}
++
++		count -= bytes;
++		buf += bytes;
++	}
+ 
+ 	if (bus_claim)
+ 		sdio_release_host(rtwsdio->sdio_func);
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0020-LOCAL-ALSA-Assign-internal-PCM-chmap-ELD-IEC958-kctl.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0020-LOCAL-ALSA-Assign-internal-PCM-chmap-ELD-IEC958-kctl.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d333be4f64a5a0b23ad940032ff0dee604d76759
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0020-LOCAL-ALSA-Assign-internal-PCM-chmap-ELD-IEC958-kctl.patch
@@ -0,0 +1,52 @@
+From 2c1b731fb890ac217152a62ea524c2e7ee10e05e Mon Sep 17 00:00:00 2001
+From: Anssi Hannula <anssi.hannula@iki.fi>
+Date: Sun, 17 Apr 2022 04:37:48 +0000
+Subject: [PATCH 20/21] LOCAL: ALSA: Assign internal PCM chmap/ELD/IEC958 kctls
+ to device 0
+
+On SoC sound devices utilizing codec2codec DAI links with a HDMI codec
+the kctls for chmap, ELD, IEC958 are currently created using the
+internal PCM device numbers. This causes userspace to not see the
+actual channel mapping.
+
+Affected devices include LibreTech LePotato and Wetek Play 2.
+
+The proper fix would be not create these kctls for internal PCMs and
+instead create them for the real userspace-visible PCMs, somehow
+forwarding the controls between the HDMI codec and the real PCM.
+
+As a workaround, simply use device=0 for all channel map controls and
+SoC HDMI codec controls for internal PCM devices.
+
+Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>
+---
+ sound/core/pcm_lib.c          | 5 ++++-
+ sound/soc/codecs/hdmi-codec.c | 3 ++-
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/sound/core/pcm_lib.c
++++ b/sound/core/pcm_lib.c
+@@ -2547,7 +2547,10 @@ int snd_pcm_add_chmap_ctls(struct snd_pc
+ 		knew.name = "Playback Channel Map";
+ 	else
+ 		knew.name = "Capture Channel Map";
+-	knew.device = pcm->device;
++	if (pcm->internal && pcm->device)
++		dev_info(pcm->card->dev, "workaround active: internal PCM chmap controls mapped to device 0\n");
++	else
++		knew.device = pcm->device;
+ 	knew.count = pcm->streams[stream].substream_count;
+ 	knew.private_value = private_value;
+ 	info->kctl = snd_ctl_new1(&knew, info);
+--- a/sound/soc/codecs/hdmi-codec.c
++++ b/sound/soc/codecs/hdmi-codec.c
+@@ -801,7 +801,8 @@ static int hdmi_codec_pcm_new(struct snd
+ 		if (!kctl)
+ 			return -ENOMEM;
+ 
+-		kctl->id.device = rtd->pcm->device;
++		if (!rtd->pcm->internal)
++			kctl->id.device = rtd->pcm->device;
+ 		ret = snd_ctl_add(rtd->card->snd_card, kctl);
+ 		if (ret < 0)
+ 			return ret;
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0021-HACK-of-partial-revert-of-fdt.c-changes.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0021-HACK-of-partial-revert-of-fdt.c-changes.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6c5e1aa78a6a0d76b9c02bc31661ffb6e8097b8a
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0021-HACK-of-partial-revert-of-fdt.c-changes.patch
@@ -0,0 +1,38 @@
+From 25b84eeff0c5013366821e82654313d591609c57 Mon Sep 17 00:00:00 2001
+From: Stefan Agner <stefan@agner.ch>
+Date: Wed, 15 Sep 2021 05:00:45 +0000
+Subject: [PATCH 21/21] HACK: of: partial revert of fdt.c changes
+
+This resolves reports similar to the below which are present in dmesg
+since Linux 5.10; which are also causing crashes in some distros:
+
+[    0.000000] OF: fdt: Reserved memory: failed to reserve memory for node 'secmon@5000000': base 0x0000000005000000, size 3 MiB
+
+Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
+---
+ drivers/of/fdt.c | 9 ---------
+ 1 file changed, 9 deletions(-)
+
+diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
+index bf502ba8d..0789f61be 100644
+--- a/drivers/of/fdt.c
++++ b/drivers/of/fdt.c
+@@ -480,15 +480,6 @@ static int __init early_init_dt_reserve_memory(phys_addr_t base,
+ 					       phys_addr_t size, bool nomap)
+ {
+ 	if (nomap) {
+-		/*
+-		 * If the memory is already reserved (by another region), we
+-		 * should not allow it to be marked nomap, but don't worry
+-		 * if the region isn't memory as it won't be mapped.
+-		 */
+-		if (memblock_overlaps_region(&memblock.memory, base, size) &&
+-		    memblock_is_region_reserved(base, size))
+-			return -EBUSY;
+-
+ 		return memblock_mark_nomap(base, size);
+ 	}
+ 	return memblock_reserve(base, size);
+-- 
+2.40.0
+
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0022-add-bt-and-eth-resets.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0022-add-bt-and-eth-resets.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5e6c642d15fd5829a536193402fdfd4409c534ea
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0022-add-bt-and-eth-resets.patch
@@ -0,0 +1,26 @@
+diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4.dtsi
+index 97e5229..d1435a3 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4.dtsi
+@@ -211,6 +211,11 @@ &ethmac {
+ 	pinctrl-names = "default";
+ 	phy-mode = "rgmii-txid";
+ 	phy-handle = <&external_phy>;
++
++	/* This is only a partial fix; ethernet only works reliably after a reload
++	of the mdio, dwmac, stmmac etc modules */
++	snps,reset-gpio = <&gpio GPIOZ_15 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>;
++	snps,reset-delays-us = <0 1000000 2000000>;
+ };
+ 
+ &frddr_a {
+@@ -369,8 +374,7 @@ &uart_A {
+ 
+ 	bluetooth {
+ 		compatible = "realtek,rtl8822cs-bt";
+-		enable-gpios  = <&gpio GPIOX_17 GPIO_ACTIVE_HIGH>;
+-		host-wake-gpios = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>;
++		enable-gpios  = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>;
+ 		device-wake-gpios = <&gpio GPIOX_18 GPIO_ACTIVE_HIGH>;
+ 	};
+ };
diff --git a/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0023-sdio-pullups.patch b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0023-sdio-pullups.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c161f51b464562c311c5cca0af1b347186c17560
--- /dev/null
+++ b/linux/patches6.7/meson-g12b-bananapi-cm4-mnt-reform2/0023-sdio-pullups.patch
@@ -0,0 +1,28 @@
+--- a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
+@@ -937,17 +937,22 @@
+ 					};
+ 
+ 					sdio_pins: sdio {
+-						mux {
++						mux-0 {
+ 							groups = "sdio_d0",
+ 								 "sdio_d1",
+ 								 "sdio_d2",
+ 								 "sdio_d3",
+-								 "sdio_clk",
+ 								 "sdio_cmd";
+ 							function = "sdio";
+-							bias-disable;
++							bias-pull-up;
+ 							drive-strength-microamp = <4000>;
+ 						};
++
++						mux-1 {
++							groups = "sdio_clk";
++							function = "sdio";
++							bias-disable;
++						};
+ 					};
+ 
+ 					sdio_clk_gate_pins: sdio_clk_gate {