1
0
mirror of https://github.com/libretro/Lakka-LibreELEC.git synced 2024-11-25 03:46:19 +00:00
Lakka-LibreELEC/projects/NXP/devices/iMX8/patches/linux/0030-drm-imx-Add-mhdp-dp-hdmi-driver-for-imx8x-platform.patch
2022-10-04 10:53:58 +00:00

2657 lines
89 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 09102ec28d08ae95d476ee241ed016d2fe9da894 Mon Sep 17 00:00:00 2001
From: Sandor Yu <Sandor.yu@nxp.com>
Date: Mon, 20 Apr 2020 22:47:36 +0800
Subject: [PATCH 30/49] drm: imx: Add mhdp dp/hdmi driver for imx8x platform
Added i.MX8MQ HDMI/DP driver.
Added i.MX8MQ HDMI/DP driver.
Added LS1028A DP driver.
Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
---
drivers/gpu/drm/imx/Kconfig | 1 +
drivers/gpu/drm/imx/Makefile | 1 +
drivers/gpu/drm/imx/mhdp/Kconfig | 11 +
drivers/gpu/drm/imx/mhdp/Makefile | 5 +
drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c | 531 ++++++++++++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c | 764 ++++++++++++++++++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h | 75 ++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c | 638 +++++++++++++++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 257 ++++++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c | 110 +++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h | 155 ++++
11 files changed, 2548 insertions(+)
create mode 100644 drivers/gpu/drm/imx/mhdp/Kconfig
create mode 100644 drivers/gpu/drm/imx/mhdp/Makefile
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index 6231048aa5aa..4af2f575f04b 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -41,3 +41,4 @@ config DRM_IMX_HDMI
Choose this if you want to use HDMI on i.MX6.
source "drivers/gpu/drm/imx/dcss/Kconfig"
+source "drivers/gpu/drm/imx/mhdp/Kconfig"
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
index b644deffe948..0b46c46b19a8 100644
--- a/drivers/gpu/drm/imx/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
+obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += mhdp/
diff --git a/drivers/gpu/drm/imx/mhdp/Kconfig b/drivers/gpu/drm/imx/mhdp/Kconfig
new file mode 100644
index 000000000000..86950badb947
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_IMX_CDNS_MHDP
+ tristate "NXP i.MX MX8 DRM HDMI/DP"
+ select DRM_CDNS_MHDP
+ select DRM_CDNS_DP
+ select DRM_CDNS_HDMI
+ select DRM_CDNS_AUDIO
+ depends on DRM_IMX
+ help
+ Choose this if you want to use HDMI on i.MX8.
diff --git a/drivers/gpu/drm/imx/mhdp/Makefile b/drivers/gpu/drm/imx/mhdp/Makefile
new file mode 100644
index 000000000000..235fa2d515e9
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o \
+ cdns-mhdp-hdmi-phy.o cdns-mhdp-imx8qm.o cdns-mhdp-ls1028a.o
+obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c
new file mode 100644
index 000000000000..a6d03c94d196
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c
@@ -0,0 +1,531 @@
+/*
+ * 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.h>
+#include "cdns-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_1, 0x3);
+ ndelay(150);
+ 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.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.num_lanes;
+ u32 link_rate = mhdp->dp.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.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.num_lanes;
+ u32 link_rate = mhdp->dp.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 PLLs 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 PLLs 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 PLLs 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 PLLs data rate and full rate clocks out of PMA. */
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ val |= (1 << 2);
+ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
+ /* Wait for PLL clock enable ACK */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ if (val & (1 << 3))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait PLL clock enable ACk failed\n");
+ return -1;
+ }
+
+ /* Configure PHY in A2 Mode */
+ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004);
+ /* Wait for Power State A2 Ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
+ if (val & (1 << 6))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait A2 Ack failed\n");
+ return -1;
+ }
+
+ /* Configure PHY in A0 mode (PHY must be in the A0 power
+ * state in order to transmit data)
+ */
+ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0101);
+
+ /* Wait for Power State A0 Ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
+ if (val & (1 << 4))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait A0 Ack failed\n");
+ return -1;
+ }
+
+ mhdp->power_up = true;
+
+ return 0;
+}
+
+int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *mhdp)
+{
+ int ret;
+
+ /* Disable phy clock if PHY in power up state */
+ dp_phy_power_down(mhdp);
+
+ dp_phy_pma_cmn_cfg_27mhz(mhdp);
+
+ dp_phy_pma_cmn_pll0_27mhz(mhdp);
+
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1);
+
+ /* PHY power up */
+ ret = dp_phy_power_up(mhdp);
+ if (ret < 0)
+ return ret;
+
+ dp_aux_cfg(mhdp);
+
+ return ret;
+}
+
+int cdns_dp_phy_set_imx8qm(struct cdns_mhdp_device *mhdp)
+{
+ int ret;
+
+ /* Disable phy clock if PHY in power up state */
+ dp_phy_power_down(mhdp);
+
+ dp_phy_pma_cmn_cfg_24mhz(mhdp);
+
+ dp_phy_pma_cmn_pll0_24mhz(mhdp);
+
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1);
+
+ /* PHY power up */
+ ret = dp_phy_power_up(mhdp);
+ if (ret < 0)
+ return ret;
+
+ dp_aux_cfg(mhdp);
+
+ return true;
+}
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c
new file mode 100644
index 000000000000..120300e6a2df
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c
@@ -0,0 +1,764 @@
+/*
+ * 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_vblank.h>
+#include <drm/drm_print.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.h>
+#include "cdns-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},
+{ 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 },
+ { 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");
+ 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");
+ 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");
+ 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;
+
+ DRM_INFO("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)) {
+ DRM_WARN("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)) {
+ DRM_WARN("VCO (%d KHz) not supported\n", vco_freq);
+ return 0;
+ }
+ DRM_INFO("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;
+
+ DRM_INFO("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)) {
+ DRM_WARN("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)) {
+ DRM_WARN("VCO (%d KHz) not supported\n", vco_freq);
+ return 0;
+ }
+ DRM_INFO("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)
+ */
+ //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;
+ }
+ 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) {
+ DRM_ERROR("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) {
+ DRM_ERROR("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) {
+ DRM_ERROR("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) {
+ DRM_ERROR("failed to set phy pclock\n");
+ return -EINVAL;
+ }
+
+ ret = hdmi_phy_power_up(mhdp);
+ if (ret < 0)
+ return ret;
+
+ hdmi_phy_set_vswing(mhdp);
+
+ return true;
+}
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx.h
new file mode 100644
index 000000000000..fc3247dada2d
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/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.h>
+#include <drm/drm_encoder_slave.h>
+
+
+struct imx_mhdp_device;
+
+struct imx_hdp_clks {
+ struct clk *av_pll;
+ struct clk *dig_pll;
+ struct clk *clk_ipg;
+ struct clk *clk_core;
+ struct clk *clk_pxl;
+ struct clk *clk_pxl_mux;
+ struct clk *clk_pxl_link;
+
+ struct clk *lpcg_hdp;
+ struct clk *lpcg_msi;
+ struct clk *lpcg_pxl;
+ struct clk *lpcg_vif;
+ struct clk *lpcg_lis;
+ struct clk *lpcg_apb;
+ struct clk *lpcg_apb_csr;
+ struct clk *lpcg_apb_ctrl;
+
+ struct clk *lpcg_i2s;
+ struct clk *clk_i2s_bypass;
+};
+
+struct imx_mhdp_device {
+ struct cdns_mhdp_device mhdp;
+ struct drm_encoder encoder;
+
+ struct mutex audio_mutex;
+ spinlock_t audio_lock;
+ bool connected;
+ bool active;
+ bool suspended;
+ struct imx_hdp_clks clks;
+ const struct firmware *fw;
+ const char *firmware_name;
+
+ int bus_type;
+
+ struct device *pd_mhdp_dev;
+ struct device *pd_pll0_dev;
+ struct device *pd_pll1_dev;
+ struct device_link *pd_mhdp_link;
+ struct device_link *pd_pll0_link;
+ struct device_link *pd_pll1_link;
+};
+
+void cdns_mhdp_plat_init_imx8qm(struct cdns_mhdp_device *mhdp);
+void cdns_mhdp_plat_deinit_imx8qm(struct cdns_mhdp_device *mhdp);
+void cdns_mhdp_pclk_rate_imx8qm(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_firmware_init_imx8qm(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_resume_imx8qm(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_suspend_imx8qm(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_power_on_imx8qm(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_power_on_ls1028a(struct cdns_mhdp_device *mhdp);
+void cdns_mhdp_pclk_rate_ls1028a(struct cdns_mhdp_device *mhdp);
+#endif /* CDNS_MHDP_IMX_H_ */
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c
new file mode 100644
index 000000000000..a3ba3da4b05d
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c
@@ -0,0 +1,638 @@
+/*
+ * 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_vblank.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);
+}
+
+int cdns_mhdp_firmware_write_section(struct imx_mhdp_device *imx_mhdp,
+ const u8 *data, int size, int addr)
+{
+ int i;
+
+ for (i = 0; i < size; i += 4) {
+ u32 val = (unsigned int)data[i] << 0 |
+ (unsigned int)data[i + 1] << 8 |
+ (unsigned int)data[i + 2] << 16 |
+ (unsigned int)data[i + 3] << 24;
+ cdns_mhdp_bus_write(val, &imx_mhdp->mhdp, addr + i);
+ }
+
+ return 0;
+}
+
+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_firmware_load(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_nowait(THIS_MODULE, FW_ACTION_NOUEVENT,
+ imx_mhdp->firmware_name,
+ imx_mhdp->mhdp.dev, GFP_KERNEL,
+ imx_mhdp,
+ cdns_mhdp_firmware_load_cont);
+ if (ret < 0) {
+ DRM_ERROR("failed to load firmware\n");
+ return -ENOENT;
+ }
+ } else {
+ iram = imx_mhdp->fw->data + FW_IRAM_OFFSET;
+ dram = iram + FW_IRAM_SIZE;
+
+ cdns_mhdp_firmware_write_section(imx_mhdp, iram, FW_IRAM_SIZE, ADDR_IMEM);
+ cdns_mhdp_firmware_write_section(imx_mhdp, dram, FW_DRAM_SIZE, ADDR_DMEM);
+ }
+
+out:
+ /* un-reset ucpu */
+ cdns_mhdp_bus_write(0, &imx_mhdp->mhdp, APB_CTRL);
+ DRM_INFO("Started 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_firmware_load(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);
+}
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
new file mode 100644
index 000000000000..3acbdf575ee2
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
@@ -0,0 +1,259 @@
+/*
+ * 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_vblank.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+
+#include <uapi/linux/media-bus-format.h>
+
+#include "cdns-mhdp-imx.h"
+#include "cdns-mhdp-phy.h"
+#include "../imx-drm.h"
+
+static void cdns_mhdp_imx_encoder_disable(struct drm_encoder *encoder)
+{
+ 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)
+{
+ 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)
+{
+ struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
+ struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder);
+ 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 imx8qm_hdmi_drv_data = {
+ .plat_name = "imx8qm-hdmi",
+ .bind = cdns_hdmi_bind,
+ .unbind = cdns_hdmi_unbind,
+ .phy_set = cdns_hdmi_phy_set_imx8qm,
+ .phy_video_valid = cdns_hdmi_phy_video_valid_imx8qm,
+ .power_on = cdns_mhdp_power_on_imx8qm,
+ .firmware_init = cdns_mhdp_firmware_init_imx8qm,
+ .resume = cdns_mhdp_resume_imx8qm,
+ .suspend = cdns_mhdp_suspend_imx8qm,
+ .pclk_rate = cdns_mhdp_pclk_rate_imx8qm,
+ .plat_init = cdns_mhdp_plat_init_imx8qm,
+ .plat_deinit = cdns_mhdp_plat_deinit_imx8qm,
+ .bus_type = BUS_TYPE_LOW4K_APB,
+ .video_format = MEDIA_BUS_FMT_RGB101010_1X30,
+};
+
+static struct cdns_plat_data imx8qm_dp_drv_data = {
+ .plat_name = "imx8qm-dp",
+ .bind = cdns_dp_bind,
+ .unbind = cdns_dp_unbind,
+ .phy_set = cdns_dp_phy_set_imx8qm,
+ .power_on = cdns_mhdp_power_on_imx8qm,
+ .firmware_init = cdns_mhdp_firmware_init_imx8qm,
+ .pclk_rate = cdns_mhdp_pclk_rate_imx8qm,
+ .plat_init = cdns_mhdp_plat_init_imx8qm,
+ .plat_deinit = cdns_mhdp_plat_deinit_imx8qm,
+ .bus_type = BUS_TYPE_LOW4K_APB,
+ .video_format = MEDIA_BUS_FMT_RGB101010_1X30,
+ .is_dp = true,
+};
+
+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,imx8mq-dp",
+ .data = &imx8mq_dp_drv_data
+ },
+ { .compatible = "cdn,imx8qm-hdmi",
+ .data = &imx8qm_hdmi_drv_data
+ },
+ { .compatible = "cdn,imx8qm-dp",
+ .data = &imx8qm_dp_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)
+ return -EPROBE_DEFER;
+
+ drm_encoder_helper_add(encoder, &cdns_mhdp_imx_encoder_helper_funcs);
+ drm_encoder_init(drm, encoder, &cdns_mhdp_imx_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+
+
+ imx_mhdp->mhdp.plat_data = plat_data;
+ imx_mhdp->mhdp.dev = dev;
+ imx_mhdp->mhdp.bus_type = plat_data->bus_type;
+ ret = plat_data->bind(pdev, encoder, &imx_mhdp->mhdp);
+ /*
+ * If cdns_mhdp_bind() fails we'll never call cdns_mhdp_unbind(),
+ * which would have called the encoder cleanup. Do it manually.
+ */
+ if (ret < 0)
+ drm_encoder_cleanup(encoder);
+
+ return ret;
+}
+
+static void cdns_mhdp_imx_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
+
+ imx_mhdp->mhdp.plat_data->unbind(dev);
+}
+
+static const struct component_ops cdns_mhdp_imx_ops = {
+ .bind = cdns_mhdp_imx_bind,
+ .unbind = cdns_mhdp_imx_unbind,
+};
+
+static int cdns_mhdp_imx_suspend(struct device *dev)
+{
+ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
+
+ cdns_mhdp_plat_call(&imx_mhdp->mhdp, suspend);
+
+ return 0;
+}
+
+static int cdns_mhdp_imx_resume(struct device *dev)
+{
+ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
+
+ cdns_mhdp_plat_call(&imx_mhdp->mhdp, resume);
+
+ return 0;
+}
+
+static int cdns_mhdp_imx_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &cdns_mhdp_imx_ops);
+}
+
+static int cdns_mhdp_imx_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &cdns_mhdp_imx_ops);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cdns_mhdp_imx_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(cdns_mhdp_imx_suspend, cdns_mhdp_imx_resume)
+};
+
+static struct platform_driver cdns_mhdp_imx_platform_driver = {
+ .probe = cdns_mhdp_imx_probe,
+ .remove = cdns_mhdp_imx_remove,
+ .driver = {
+ .name = "cdns-mhdp-imx",
+ .of_match_table = cdns_mhdp_imx_dt_ids,
+ .pm = &cdns_mhdp_imx_pm_ops,
+ },
+};
+
+module_platform_driver(cdns_mhdp_imx_platform_driver);
+
+MODULE_AUTHOR("Sandor YU <sandor.yu@nxp.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cdnhdmi-imx");
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c
new file mode 100644
index 000000000000..4cc71301f5fe
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-ls1028a.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 NXP
+ *
+ */
+#include <linux/clk.h>
+#include <drm/drm_vblank.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);
+
+ /* 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);
+}
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
new file mode 100644
index 000000000000..5682b9fbc90f
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-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.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 */
--
2.29.2