[Freedreno] [PATCH v1 2/3] drm/msm/edp: add support for next gen edp

Sankeerth Billakanti sbillaka at codeaurora.org
Wed May 5 08:16:18 UTC 2021


The eDP controller and phy hardware for the next generation
snapdragon platform has a different desgin compared to their
earlier generations. So, there is a need to implement new
sequences in driver software for the new hardware.

This change will implement the required seuqences to enable
eDP interface on the next generation snapdragon platforms.

Signed-off-by: Sankeerth Billakanti <sbillaka at codeaurora.org>
---
 drivers/gpu/drm/msm/Makefile                      |    6 +
 drivers/gpu/drm/msm/edp/edp_common.c              |    4 +
 drivers/gpu/drm/msm/edp/edp_common.h              |    6 +
 drivers/gpu/drm/msm/edp/v510/edp_v510.c           |  220 +++
 drivers/gpu/drm/msm/edp/v510/edp_v510.h           |  151 ++
 drivers/gpu/drm/msm/edp/v510/edp_v510_aux.c       |  268 ++++
 drivers/gpu/drm/msm/edp/v510/edp_v510_bridge.c    |  111 ++
 drivers/gpu/drm/msm/edp/v510/edp_v510_connector.c |  117 ++
 drivers/gpu/drm/msm/edp/v510/edp_v510_ctrl.c      | 1583 +++++++++++++++++++++
 drivers/gpu/drm/msm/edp/v510/edp_v510_phy.c       |  641 +++++++++
 drivers/gpu/drm/msm/edp/v510/edp_v510_reg.h       |  339 +++++
 11 files changed, 3446 insertions(+)
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510.c
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510.h
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_aux.c
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_bridge.c
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_connector.c
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_ctrl.c
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_phy.c
 create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_reg.h

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 0a4ff4ea..f405fd6 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -32,6 +32,12 @@ msm-y := \
 	edp/v200/edp_v200_connector.o \
 	edp/v200/edp_v200_ctrl.o \
 	edp/v200/edp_v200_phy.o \
+	edp/v510/edp_v510.o \
+	edp/v510/edp_v510_aux.o \
+	edp/v510/edp_v510_bridge.o \
+	edp/v510/edp_v510_connector.o \
+	edp/v510/edp_v510_ctrl.o \
+	edp/v510/edp_v510_phy.o \
 	disp/mdp_format.o \
 	disp/mdp_kms.o \
 	disp/mdp4/mdp4_crtc.o \
diff --git a/drivers/gpu/drm/msm/edp/edp_common.c b/drivers/gpu/drm/msm/edp/edp_common.c
index 8330050..32336a6 100644
--- a/drivers/gpu/drm/msm/edp/edp_common.c
+++ b/drivers/gpu/drm/msm/edp/edp_common.c
@@ -8,11 +8,13 @@
 void __init msm_edp_register(void)
 {
 	msm_edp_v200_register();
+	msm_edp_v510_register();
 }
 
 void __exit msm_edp_unregister(void)
 {
 	msm_edp_v200_unregister();
+	msm_edp_v510_unregister();
 }
 
 /* Second part of initialization, the drm/kms level modeset_init */
@@ -29,6 +31,8 @@ int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev,
 
 	if (edp->version == MSM_EDP_VERSION_200)
 		ret = msm_edp_v200_modeset_init(edp, dev, encoder);
+	else if (edp->version == MSM_EDP_VERSION_510)
+		ret = msm_edp_v510_modeset_init(edp, dev, encoder);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/msm/edp/edp_common.h b/drivers/gpu/drm/msm/edp/edp_common.h
index 30a74d9..768fce0 100644
--- a/drivers/gpu/drm/msm/edp/edp_common.h
+++ b/drivers/gpu/drm/msm/edp/edp_common.h
@@ -19,6 +19,7 @@
 #include "dpu_io_util.h"
 
 #define MSM_EDP_VERSION_200 200
+#define MSM_EDP_VERSION_510 510
 
 struct msm_edp {
 	struct drm_device *dev;
@@ -38,4 +39,9 @@ void __exit msm_edp_v200_unregister(void);
 int msm_edp_v200_modeset_init(struct msm_edp *edp, struct drm_device *dev,
 				struct drm_encoder *encoder);
 
+void __init msm_edp_v510_register(void);
+void __exit msm_edp_v510_unregister(void);
+int msm_edp_v510_modeset_init(struct msm_edp *edp, struct drm_device *dev,
+				struct drm_encoder *encoder);
+
 #endif /* __EDP_COMMON_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510.c b/drivers/gpu/drm/msm/edp/v510/edp_v510.c
new file mode 100644
index 0000000..8650411
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/of_irq.h>
+#include "edp_v510.h"
+
+static irqreturn_t edp_irq(int irq, void *dev_id)
+{
+	struct msm_edp_v510 *edp = dev_id;
+
+	/* Process eDP irq */
+	return msm_edp_v510_ctrl_irq(edp->ctrl);
+}
+
+static void edp_destroy(struct platform_device *pdev)
+{
+	struct msm_edp_v510 *edp = platform_get_drvdata(pdev);
+
+	if (!edp)
+		return;
+
+	if (edp->ctrl) {
+		msm_edp_v510_ctrl_destroy(edp->ctrl);
+		edp->ctrl = NULL;
+	}
+
+	platform_set_drvdata(pdev, NULL);
+}
+
+/* construct eDP at bind/probe time, grab all the resources. */
+static struct msm_edp *edp_init(struct platform_device *pdev)
+{
+	struct msm_edp_v510 *edp = NULL;
+	int ret;
+
+	if (!pdev) {
+		DRM_ERROR("no eDP device\n");
+		ret = -ENXIO;
+		goto fail;
+	}
+
+	edp = devm_kzalloc(&pdev->dev, sizeof(*edp), GFP_KERNEL);
+	if (!edp) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	DRM_INFO("eDP v510 probed\n");
+	edp->base.version = MSM_EDP_VERSION_510;
+
+	edp->base.pdev = pdev;
+	platform_set_drvdata(pdev, edp);
+
+	ret = msm_edp_v510_ctrl_init(edp);
+	if (ret)
+		goto fail;
+
+	return &edp->base;
+
+fail:
+	if (edp)
+		edp_destroy(pdev);
+
+	return ERR_PTR(ret);
+}
+
+static int edp_bind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct msm_drm_private *priv = drm->dev_private;
+	struct msm_edp *edp;
+
+	edp = edp_init(to_platform_device(dev));
+	if (IS_ERR(edp))
+		return PTR_ERR(edp);
+	priv->edp = edp;
+
+	return 0;
+}
+
+static void edp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct msm_drm_private *priv = drm->dev_private;
+
+	if (priv->edp) {
+		edp_destroy(to_platform_device(dev));
+		priv->edp = NULL;
+	}
+}
+
+static const struct component_ops edp_ops = {
+		.bind   = edp_bind,
+		.unbind = edp_unbind,
+};
+
+static int edp_dev_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &edp_ops);
+}
+
+static int edp_dev_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &edp_ops);
+	return 0;
+}
+
+static const struct of_device_id dt_match[] = {
+	{ .compatible = "qcom,mdss-edp-v510" },
+	{}
+};
+
+static struct platform_driver edp_driver = {
+	.probe = edp_dev_probe,
+	.remove = edp_dev_remove,
+	.driver = {
+		.name = "msm_edp_v510",
+		.of_match_table = dt_match,
+	},
+};
+
+void __init msm_edp_v510_register(void)
+{
+	platform_driver_register(&edp_driver);
+}
+
+void __exit msm_edp_v510_unregister(void)
+{
+	platform_driver_unregister(&edp_driver);
+}
+
+static void edp_display_set_encoder_mode(struct msm_edp_v510 *edp)
+{
+	struct msm_drm_private *priv = edp->base.dev->dev_private;
+	struct msm_kms *kms = priv->kms;
+
+	if (!edp->encoder_mode_set && edp->base.encoder &&
+				kms->funcs->set_encoder_mode) {
+		kms->funcs->set_encoder_mode(kms,
+				edp->base.encoder, true);
+
+		edp->encoder_mode_set = true;
+	}
+}
+
+/* Second part of initialization, the drm/kms level modeset_init */
+int msm_edp_v510_modeset_init(struct msm_edp *edp, struct drm_device *dev,
+				struct drm_encoder *encoder)
+{
+	struct platform_device *pdev = edp->pdev;
+	struct msm_drm_private *priv = dev->dev_private;
+	struct msm_edp_v510 *edp_ptr = NULL;
+	int ret;
+
+	if (WARN_ON(!encoder) || WARN_ON(!edp) || WARN_ON(!dev))
+		return -EINVAL;
+
+	edp->encoder = encoder;
+	edp->dev = dev;
+
+	edp_ptr = container_of(edp, struct msm_edp_v510, base);
+	if (IS_ERR(edp_ptr)) {
+		ret = PTR_ERR(edp_ptr);
+		DRM_DEV_ERROR(dev->dev, "failed to retrieve edp_v510 ptr: %d\n", ret);
+		goto fail;
+	}
+
+	edp_ptr->base.bridge = msm_edp_v510_bridge_init(edp_ptr);
+	if (IS_ERR(edp_ptr->base.bridge)) {
+		ret = PTR_ERR(edp_ptr->base.bridge);
+		DRM_DEV_ERROR(dev->dev, "failed to create eDP bridge: %d\n", ret);
+		edp_ptr->base.bridge = NULL;
+		goto fail;
+	}
+
+	edp_ptr->base.connector = msm_edp_v510_connector_init(edp_ptr);
+	if (IS_ERR(edp_ptr->base.connector)) {
+		ret = PTR_ERR(edp_ptr->base.connector);
+		DRM_DEV_ERROR(dev->dev, "failed to create eDP connector: %d\n", ret);
+		edp_ptr->base.connector = NULL;
+		goto fail;
+	}
+
+	edp_ptr->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+	if (edp_ptr->irq < 0) {
+		ret = edp_ptr->irq;
+		DRM_DEV_ERROR(dev->dev, "failed to get IRQ: %d\n", ret);
+		goto fail;
+	}
+
+	ret = devm_request_irq(&pdev->dev, edp_ptr->irq,
+			edp_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+			"edp_isr", edp_ptr);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev->dev, "failed to request IRQ%u: %d\n",
+				edp_ptr->irq, ret);
+		goto fail;
+	}
+
+	edp_display_set_encoder_mode(edp_ptr);
+
+	priv->bridges[priv->num_bridges++]       = edp->bridge;
+	priv->connectors[priv->num_connectors++] = edp->connector;
+
+	return 0;
+
+fail:
+	/* bridge/connector are normally destroyed by drm */
+	if (edp->bridge) {
+		edp_v510_bridge_destroy(edp->bridge);
+		edp->bridge = NULL;
+	}
+	if (edp->connector) {
+		edp->connector->funcs->destroy(edp->connector);
+		edp->connector = NULL;
+	}
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510.h b/drivers/gpu/drm/msm/edp/v510/edp_v510.h
new file mode 100644
index 0000000..9a01987
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __EDP_V510_CONNECTOR_H__
+#define __EDP_V510_CONNECTOR_H__
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+
+#include "msm_drv.h"
+#include "msm_kms.h"
+#include "dpu_io_util.h"
+#include "../edp_common.h"
+
+#define MSM_EDP_CONTROLLER_AHB_OFFSET	0x0000
+#define MSM_EDP_CONTROLLER_AHB_SIZE	0x0200
+#define MSM_EDP_CONTROLLER_AUX_OFFSET	0x0200
+#define MSM_EDP_CONTROLLER_AUX_SIZE	0x0200
+#define MSM_EDP_CONTROLLER_LINK_OFFSET	0x0400
+#define MSM_EDP_CONTROLLER_LINK_SIZE	0x0C00
+#define MSM_EDP_CONTROLLER_P0_OFFSET	0x1000
+#define MSM_EDP_CONTROLLER_P0_SIZE	0x0400
+
+static inline u32 edp_read_aux(void *base, u32 offset)
+{
+	offset += MSM_EDP_CONTROLLER_AUX_OFFSET;
+	return readl_relaxed(base + offset);
+}
+
+static inline void edp_write_aux(void *base, u32 offset, u32 data)
+{
+	offset += MSM_EDP_CONTROLLER_AUX_OFFSET;
+	/*
+	 * To make sure aux reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, base + offset);
+}
+
+static inline u32 edp_read_ahb(void *base, u32 offset)
+{
+	offset += MSM_EDP_CONTROLLER_AHB_OFFSET;
+	return readl_relaxed(base + offset);
+}
+
+static inline void edp_write_ahb(void *base, u32 offset, u32 data)
+{
+	offset += MSM_EDP_CONTROLLER_AHB_OFFSET;
+	/*
+	 * To make sure ahb reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, base + offset);
+}
+
+static inline void edp_write_p0(void *base, u32 offset, u32 data)
+{
+	offset += MSM_EDP_CONTROLLER_P0_OFFSET;
+	/*
+	 * To make sure pclk reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, base + offset);
+}
+
+static inline u32 edp_read_p0(void *base, u32 offset)
+{
+	offset += MSM_EDP_CONTROLLER_P0_OFFSET;
+	return readl_relaxed(base + offset);
+}
+
+static inline u32 edp_read_link(void *base, u32 offset)
+{
+	offset += MSM_EDP_CONTROLLER_LINK_OFFSET;
+	return readl_relaxed(base + offset);
+}
+
+static inline void edp_write_link(void *base, u32 offset, u32 data)
+{
+	offset += MSM_EDP_CONTROLLER_LINK_OFFSET;
+	/*
+	 * To make sure link reg writes happens before any other operation,
+	 * this function uses writel() instread of writel_relaxed()
+	 */
+	writel(data, base + offset);
+}
+
+struct edp_ctrl;
+struct edp_aux;
+struct edp_phy;
+struct edp_phy_opts {
+	unsigned long link_rate;
+	int lanes;
+	int voltage[4];
+	int pre[4];
+};
+
+
+struct msm_edp_v510 {
+	struct msm_edp base;
+	struct edp_ctrl *ctrl;
+
+	int irq;
+	bool encoder_mode_set;
+};
+
+/* eDP bridge */
+struct drm_bridge *msm_edp_v510_bridge_init(struct msm_edp_v510 *edp);
+void edp_v510_bridge_destroy(struct drm_bridge *bridge);
+
+/* eDP connector */
+struct drm_connector *msm_edp_v510_connector_init(struct msm_edp_v510 *edp);
+
+/* AUX */
+void *msm_edp_v510_aux_init(struct device *dev, void __iomem *regbase,
+			struct drm_dp_aux **drm_aux);
+void msm_edp_v510_aux_destroy(struct device *dev, struct edp_aux *aux);
+irqreturn_t msm_edp_v510_aux_irq(struct edp_aux *aux, u32 isr);
+void msm_edp_v510_aux_ctrl(struct edp_aux *aux, int enable);
+
+/* Phy */
+int msm_edp_v510_phy_enable(struct edp_phy *edp_phy);
+void msm_edp_v510_phy_vm_pe_init(struct edp_phy *edp_phy,
+					struct edp_phy_opts *opts);
+void *msm_edp_v510_phy_init(struct device *dev, void __iomem *regbase,
+			struct edp_phy_opts *opts);
+int msm_edp_v510_phy_power_on(struct edp_phy *edp_phy);
+void msm_edp_v510_phy_config(struct edp_phy *edp_phy, u8 v_level, u8 p_level);
+
+/* Ctrl */
+irqreturn_t msm_edp_v510_ctrl_irq(struct edp_ctrl *ctrl);
+void msm_edp_v510_ctrl_power(struct edp_ctrl *ctrl, bool on);
+int msm_edp_v510_ctrl_init(struct msm_edp_v510 *edp);
+void msm_edp_v510_ctrl_destroy(struct edp_ctrl *ctrl);
+bool msm_edp_v510_ctrl_panel_connected(struct edp_ctrl *ctrl);
+int msm_edp_v510_ctrl_get_panel_info(struct edp_ctrl *ctrl,
+	struct drm_connector *connector, struct edid **edid);
+int msm_edp_v510_ctrl_mode_set(struct edp_ctrl *ctrl,
+				const struct drm_display_mode *mode,
+				const struct drm_display_info *info);
+/* @pixel_rate is in kHz */
+bool msm_edp_v510_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl, u32 pixel_rate);
+
+#endif /* __EDP_V510_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510_aux.c b/drivers/gpu/drm/msm/edp/v510/edp_v510_aux.c
new file mode 100644
index 0000000..817e0e6
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510_aux.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include "edp_v510.h"
+#include "edp_v510_reg.h"
+
+#define AUX_CMD_FIFO_LEN	144
+#define AUX_CMD_NATIVE_MAX	16
+#define AUX_CMD_I2C_MAX		128
+
+#define EDP_INTR_AUX_I2C_ERR	\
+	(EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
+	EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
+	EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER)
+
+#define EDP_INTR_TRANS_STATUS	\
+	(EDP_INTR_AUX_I2C_DONE | EDP_INTR_AUX_I2C_ERR)
+
+struct edp_aux {
+	void __iomem *base;
+	bool msg_err;
+
+	struct completion msg_comp;
+
+	/* To prevent the message transaction routine from reentry. */
+	struct mutex msg_mutex;
+
+	struct drm_dp_aux drm_aux;
+};
+#define to_edp_aux(x) container_of(x, struct edp_aux, drm_aux)
+
+static int edp_msg_fifo_tx(struct edp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+	u32 data[4];
+	u32 reg, len;
+	bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+	bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	u8 *msgdata = msg->buffer;
+	int i;
+
+	if (read)
+		len = 4;
+	else
+		len = msg->size + 4;
+
+	/*
+	 * cmd fifo only has depth of 144 bytes
+	 */
+	if (len > AUX_CMD_FIFO_LEN)
+		return -EINVAL;
+
+	/* Pack cmd and write to HW */
+	data[0] = (msg->address >> 16) & 0xf;	/* addr[19:16] */
+	if (read)
+		data[0] |=  BIT(4);		/* R/W */
+
+	data[1] = (msg->address >> 8) & 0xff;	/* addr[15:8] */
+	data[2] = msg->address & 0xff;		/* addr[7:0] */
+	data[3] = (msg->size - 1) & 0xff;	/* len[7:0] */
+
+	for (i = 0; i < len; i++) {
+		reg = (i < 4) ? data[i] : msgdata[i - 4];
+		reg = EDP_AUX_DATA_DATA(reg); /* index = 0, write */
+		if (i == 0)
+			reg |= EDP_AUX_DATA_INDEX_WRITE;
+		edp_write_aux(aux->base, REG_EDP_AUX_DATA, reg);
+	}
+
+	reg = 0; /* Transaction number is always 1 */
+	if (!native) /* i2c */
+		reg |= EDP_AUX_TRANS_CTRL_I2C;
+
+	reg |= EDP_AUX_TRANS_CTRL_GO;
+	edp_write_aux(aux->base, REG_EDP_AUX_TRANS_CTRL, reg);
+
+	return 0;
+}
+
+static int edp_msg_fifo_rx(struct edp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+	u32 data;
+	u8 *dp;
+	int i;
+	u32 len = msg->size;
+
+	edp_write_aux(aux->base, REG_EDP_AUX_DATA,
+		EDP_AUX_DATA_INDEX_WRITE | EDP_AUX_DATA_READ); /* index = 0 */
+
+	dp = msg->buffer;
+
+	/* discard first byte */
+	data = edp_read_aux(aux->base, REG_EDP_AUX_DATA);
+	for (i = 0; i < len; i++) {
+		data = edp_read_aux(aux->base, REG_EDP_AUX_DATA);
+		dp[i] = (u8)((data >> 8) & 0xff);
+	}
+
+	return 0;
+}
+
+/*
+ * This function does the real job to process an AUX transaction.
+ * It will call msm_edp_aux_ctrl() function to reset the AUX channel,
+ * if the waiting is timeout.
+ * The caller who triggers the transaction should avoid the
+ * msm_edp_aux_ctrl() running concurrently in other threads, i.e.
+ * start transaction only when AUX channel is fully enabled.
+ */
+static ssize_t edp_aux_transfer(struct drm_dp_aux *drm_aux,
+		struct drm_dp_aux_msg *msg)
+{
+	struct edp_aux *aux = to_edp_aux(drm_aux);
+	ssize_t ret;
+	unsigned long time_left;
+	bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+	bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+	/* 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;
+	}
+
+	/* msg sanity check */
+	if ((native && (msg->size > AUX_CMD_NATIVE_MAX)) ||
+		(msg->size > AUX_CMD_I2C_MAX)) {
+		pr_err("%s: invalid msg: size(%zu), request(%x)\n",
+			__func__, msg->size, msg->request);
+		return -EINVAL;
+	}
+
+	mutex_lock(&aux->msg_mutex);
+
+	aux->msg_err = false;
+	reinit_completion(&aux->msg_comp);
+
+	ret = edp_msg_fifo_tx(aux, msg);
+	if (ret < 0)
+		goto unlock_exit;
+
+	DRM_DEBUG_DP("wait_for_completion");
+	time_left = wait_for_completion_timeout(&aux->msg_comp,
+						msecs_to_jiffies(300));
+	if (!time_left) {
+		/*
+		 * Clear GO and reset AUX channel
+		 * to cancel the current transaction.
+		 */
+		edp_write_aux(aux->base, REG_EDP_AUX_TRANS_CTRL, 0);
+		msm_edp_v510_aux_ctrl(aux, 1);
+		pr_err("%s: aux timeout,\n", __func__);
+		ret = -ETIMEDOUT;
+		goto unlock_exit;
+	}
+	DRM_DEBUG_DP("completion");
+
+	if (!aux->msg_err) {
+		if (read) {
+			ret = edp_msg_fifo_rx(aux, msg);
+			if (ret < 0)
+				goto unlock_exit;
+		}
+
+		msg->reply = native ?
+			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+	} else {
+		/* Reply defer to retry */
+		msg->reply = native ?
+			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
+		/*
+		 * The sleep time in caller is not long enough to make sure
+		 * our H/W completes transactions. Add more defer time here.
+		 */
+		msleep(100);
+	}
+
+	/* Return requested size for success or retry */
+	ret = msg->size;
+
+unlock_exit:
+	mutex_unlock(&aux->msg_mutex);
+	return ret;
+}
+
+void *msm_edp_v510_aux_init(struct device *dev, void __iomem *regbase,
+	struct drm_dp_aux **drm_aux)
+{
+	struct edp_aux *aux = NULL;
+	int ret;
+
+	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
+	if (!aux)
+		return NULL;
+
+	aux->base = regbase;
+	mutex_init(&aux->msg_mutex);
+	init_completion(&aux->msg_comp);
+
+	aux->drm_aux.name = "msm_edp_aux";
+	aux->drm_aux.dev = dev;
+	aux->drm_aux.transfer = edp_aux_transfer;
+	ret = drm_dp_aux_register(&aux->drm_aux);
+	if (ret) {
+		pr_err("%s: failed to register drm aux: %d\n", __func__, ret);
+		mutex_destroy(&aux->msg_mutex);
+	}
+
+	if (drm_aux && aux)
+		*drm_aux = &aux->drm_aux;
+
+	return aux;
+}
+
+void msm_edp_v510_aux_destroy(struct device *dev, struct edp_aux *aux)
+{
+	if (aux) {
+		drm_dp_aux_unregister(&aux->drm_aux);
+		mutex_destroy(&aux->msg_mutex);
+	}
+}
+
+irqreturn_t msm_edp_v510_aux_irq(struct edp_aux *aux, u32 isr)
+{
+	if (isr & EDP_INTR_TRANS_STATUS) {
+		DRM_DEBUG_DP("isr=%x", isr);
+		edp_write_aux(aux->base, REG_EDP_AUX_TRANS_CTRL, 0);
+
+		if (isr & EDP_INTR_AUX_I2C_ERR)
+			aux->msg_err = true;
+		else
+			aux->msg_err = false;
+
+		complete(&aux->msg_comp);
+	}
+
+	return IRQ_HANDLED;
+}
+
+void msm_edp_v510_aux_ctrl(struct edp_aux *aux, int enable)
+{
+	u32 data;
+
+	DRM_INFO("enable=%d", enable);
+	data = edp_read_aux(aux->base, REG_EDP_AUX_CTRL);
+
+	if (enable) {
+		data |= EDP_AUX_CTRL_RESET;
+		edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+		/* Make sure full reset */
+		wmb();
+		usleep_range(500, 1000);
+
+		data &= ~EDP_AUX_CTRL_RESET;
+		edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+
+		edp_write_aux(aux->base, REG_EDP_TIMEOUT_COUNT, 0xffff);
+		edp_write_aux(aux->base, REG_EDP_AUX_LIMITS, 0xffff);
+
+		data |= EDP_AUX_CTRL_ENABLE;
+		edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+	} else {
+		data &= ~EDP_AUX_CTRL_ENABLE;
+		edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+	}
+}
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510_bridge.c b/drivers/gpu/drm/msm/edp/v510/edp_v510_bridge.c
new file mode 100644
index 0000000..12a85dd
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510_bridge.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include "edp_v510.h"
+
+struct edp_bridge {
+	struct drm_bridge base;
+	struct msm_edp_v510 *edp;
+};
+#define to_edp_bridge(x) container_of(x, struct edp_bridge, base)
+
+void edp_v510_bridge_destroy(struct drm_bridge *bridge)
+{
+}
+
+static void edp_bridge_pre_enable(struct drm_bridge *bridge)
+{
+	struct edp_bridge *edp_bridge = to_edp_bridge(bridge);
+	struct msm_edp_v510 *edp = edp_bridge->edp;
+
+	DBG("");
+	msm_edp_v510_ctrl_power(edp->ctrl, true);
+}
+
+static void edp_bridge_enable(struct drm_bridge *bridge)
+{
+	DBG("");
+}
+
+static void edp_bridge_disable(struct drm_bridge *bridge)
+{
+	DBG("");
+}
+
+static void edp_bridge_post_disable(struct drm_bridge *bridge)
+{
+	struct edp_bridge *edp_bridge = to_edp_bridge(bridge);
+	struct msm_edp_v510 *edp = edp_bridge->edp;
+
+	DBG("");
+	msm_edp_v510_ctrl_power(edp->ctrl, false);
+}
+
+static void edp_bridge_mode_set(struct drm_bridge *bridge,
+		const struct drm_display_mode *mode,
+		const struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = bridge->dev;
+	struct drm_connector *connector;
+	struct edp_bridge *edp_bridge = to_edp_bridge(bridge);
+	struct msm_edp_v510 *edp = edp_bridge->edp;
+
+	DRM_INFO("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		struct drm_encoder *encoder = connector->encoder;
+		struct drm_bridge *first_bridge;
+
+		if (!connector->encoder)
+			continue;
+
+		first_bridge = drm_bridge_chain_get_first_bridge(encoder);
+		if (bridge == first_bridge) {
+			msm_edp_v510_ctrl_mode_set(edp->ctrl,
+				adjusted_mode, &connector->display_info);
+			break;
+		}
+	}
+}
+
+static const struct drm_bridge_funcs edp_bridge_funcs = {
+	.pre_enable = edp_bridge_pre_enable,
+	.enable = edp_bridge_enable,
+	.disable = edp_bridge_disable,
+	.post_disable = edp_bridge_post_disable,
+	.mode_set = edp_bridge_mode_set,
+};
+
+/* initialize bridge */
+struct drm_bridge *msm_edp_v510_bridge_init(struct msm_edp_v510 *edp)
+{
+	struct drm_bridge *bridge = NULL;
+	struct edp_bridge *edp_bridge;
+	int ret;
+
+	edp_bridge = devm_kzalloc(edp->base.dev->dev,
+			sizeof(*edp_bridge), GFP_KERNEL);
+	if (!edp_bridge) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	edp_bridge->edp = edp;
+
+	bridge = &edp_bridge->base;
+	bridge->funcs = &edp_bridge_funcs;
+
+	ret = drm_bridge_attach(edp->base.encoder, bridge, NULL, 0);
+	if (ret)
+		goto fail;
+
+	return bridge;
+
+fail:
+	if (bridge)
+		edp_v510_bridge_destroy(bridge);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510_connector.c b/drivers/gpu/drm/msm/edp/v510/edp_v510_connector.c
new file mode 100644
index 0000000..6ba2071
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510_connector.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include "drm/drm_edid.h"
+#include "msm_kms.h"
+#include "edp_v510.h"
+
+struct edp_connector {
+	struct drm_connector base;
+	struct msm_edp_v510 *edp;
+};
+#define to_edp_connector(x) container_of(x, struct edp_connector, base)
+
+static enum drm_connector_status edp_connector_detect(
+		struct drm_connector *connector, bool force)
+{
+	struct edp_connector *edp_connector = to_edp_connector(connector);
+	struct msm_edp_v510 *edp = edp_connector->edp;
+
+	DBG("");
+	return msm_edp_v510_ctrl_panel_connected(edp->ctrl) ?
+		connector_status_connected : connector_status_disconnected;
+}
+
+static void edp_connector_destroy(struct drm_connector *connector)
+{
+	struct edp_connector *edp_connector = to_edp_connector(connector);
+
+	DBG("");
+
+	drm_connector_cleanup(connector);
+
+	kfree(edp_connector);
+}
+
+static int edp_connector_get_modes(struct drm_connector *connector)
+{
+	struct edp_connector *edp_connector = to_edp_connector(connector);
+	struct msm_edp_v510 *edp = edp_connector->edp;
+
+	struct edid *drm_edid = NULL;
+	int ret = 0;
+
+	DBG("");
+	ret = msm_edp_v510_ctrl_get_panel_info(edp->ctrl, connector, &drm_edid);
+	if (ret)
+		return ret;
+
+	drm_connector_update_edid_property(connector, drm_edid);
+	if (drm_edid)
+		ret = drm_add_edid_modes(connector, drm_edid);
+
+	return ret;
+}
+
+static int edp_connector_mode_valid(struct drm_connector *connector,
+				 struct drm_display_mode *mode)
+{
+	struct edp_connector *edp_connector = to_edp_connector(connector);
+	struct msm_edp_v510 *edp = edp_connector->edp;
+
+	if (!msm_edp_v510_ctrl_pixel_clock_valid(edp->ctrl, mode->clock))
+		return MODE_CLOCK_RANGE;
+
+	return MODE_OK;
+}
+
+static const struct drm_connector_funcs edp_connector_funcs = {
+	.detect = edp_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = edp_connector_destroy,
+	.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 edp_connector_helper_funcs = {
+	.get_modes = edp_connector_get_modes,
+	.mode_valid = edp_connector_mode_valid,
+};
+
+/* initialize connector */
+struct drm_connector *msm_edp_v510_connector_init(struct msm_edp_v510 *edp)
+{
+	struct drm_connector *connector = NULL;
+	struct edp_connector *edp_connector;
+	int ret;
+
+	edp_connector = devm_kzalloc(edp->base.dev->dev, sizeof(*edp_connector),
+				GFP_KERNEL);
+	if (!edp_connector)
+		return ERR_PTR(-ENOMEM);
+
+	edp_connector->edp = edp;
+
+	connector = &edp_connector->base;
+
+	ret = drm_connector_init(edp->base.dev, connector, &edp_connector_funcs,
+			DRM_MODE_CONNECTOR_eDP);
+	if (ret)
+		return ERR_PTR(ret);
+
+	drm_connector_helper_add(connector, &edp_connector_helper_funcs);
+
+	/* We don't support HPD, so only poll status until connected. */
+	connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+
+	/* Display driver doesn't support interlace now. */
+	connector->interlace_allowed = false;
+	connector->doublescan_allowed = false;
+
+	drm_connector_attach_encoder(connector, edp->base.encoder);
+
+	return connector;
+}
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510_ctrl.c b/drivers/gpu/drm/msm/edp/v510/edp_v510_ctrl.c
new file mode 100644
index 0000000..8bc4609
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510_ctrl.c
@@ -0,0 +1,1583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/rational.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <linux/delay.h>
+
+#include "edp_v510.h"
+#include "edp_v510_reg.h"
+
+#define VDDA_UA_ON_LOAD		21800	/* uA units */
+#define VDDA_UA_OFF_LOAD	4	/* uA units */
+#define LVL_UA_ON_LOAD		36000	/* uA units */
+#define LVL_UA_OFF_LOAD		32	/* uA units */
+
+#define DPCD_LINK_VOLTAGE_MAX		4
+#define DPCD_LINK_PRE_EMPHASIS_MAX	4
+
+#define EDP_LINK_BW_MAX		DP_LINK_BW_5_4
+
+/* Link training return value */
+#define EDP_TRAIN_FAIL		-1
+#define EDP_TRAIN_SUCCESS	0
+#define EDP_TRAIN_RECONFIG	1
+
+#define EDP_CLK_MASK_AHB		BIT(0)
+#define EDP_CLK_MASK_AUX		BIT(1)
+#define EDP_CLK_MASK_LINK		BIT(2)
+#define EDP_CLK_MASK_PIXEL		BIT(3)
+#define EDP_CLK_MASK_MDP_CORE		BIT(4)
+#define EDP_CLK_MASK_LINK_CHAN	(EDP_CLK_MASK_LINK | EDP_CLK_MASK_PIXEL)
+#define EDP_CLK_MASK_AUX_CHAN	\
+	(EDP_CLK_MASK_AHB | EDP_CLK_MASK_AUX | EDP_CLK_MASK_MDP_CORE)
+#define EDP_CLK_MASK_ALL	(EDP_CLK_MASK_AUX_CHAN | EDP_CLK_MASK_LINK_CHAN)
+
+#define EDP_BACKLIGHT_MAX	255
+
+#define EDP_INTERRUPT_STATUS_ACK_SHIFT	1
+#define EDP_INTERRUPT_STATUS_MASK_SHIFT	2
+
+#define EDP_INTERRUPT_STATUS1 \
+	(EDP_INTR_AUX_I2C_DONE| \
+	EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
+	EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
+	EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \
+	EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR)
+
+#define EDP_INTERRUPT_STATUS1_ACK \
+	(EDP_INTERRUPT_STATUS1 << EDP_INTERRUPT_STATUS_ACK_SHIFT)
+#define EDP_INTERRUPT_STATUS1_MASK \
+	(EDP_INTERRUPT_STATUS1 << EDP_INTERRUPT_STATUS_MASK_SHIFT)
+
+#define EDP_INTERRUPT_STATUS2 \
+	(EDP_INTR_READY_FOR_VIDEO | EDP_INTR_IDLE_PATTERN_SENT | \
+	EDP_INTR_FRAME_END | EDP_INTR_CRC_UPDATED | EDP_INTR_SST_FIFO_UNDERFLOW)
+
+#define EDP_INTERRUPT_STATUS2_ACK \
+	(EDP_INTERRUPT_STATUS2 << EDP_INTERRUPT_STATUS_ACK_SHIFT)
+#define EDP_INTERRUPT_STATUS2_MASK \
+	(EDP_INTERRUPT_STATUS2 << EDP_INTERRUPT_STATUS_MASK_SHIFT)
+
+enum edp_pm_type {
+	EDP_CORE_PM,
+	EDP_CTRL_PM,
+	EDP_STREAM_PM,
+	EDP_PHY_PM,
+	EDP_MAX_PM
+};
+
+struct edp_ctrl {
+	struct platform_device *pdev;
+
+	void __iomem *base;
+	void __iomem *phy_base;
+
+	/* regulators */
+	struct regulator *vdda_vreg;	/* 1.8 V */
+	struct regulator *lvl_vreg;
+
+	/* clocks */
+	struct dss_module_power mp[EDP_MAX_PM];
+	bool core_clks_on;
+	bool link_clks_on;
+	bool stream_clks_on;
+
+	/* gpios */
+	struct gpio_desc *panel_en_gpio;
+	struct gpio_desc *panel_hpd_gpio;
+	struct gpio_desc *panel_bklt1_gpio;
+	struct gpio_desc *panel_bklt2_gpio;
+	struct gpio_desc *panel_pwm_gpio;
+
+	/* completion and mutex */
+	struct completion idle_comp;
+	struct mutex dev_mutex; /* To protect device power status */
+
+	/* work queue */
+	struct work_struct on_work;
+	struct work_struct off_work;
+	struct workqueue_struct *workqueue;
+
+	/* Interrupt register lock */
+	spinlock_t irq_lock;
+
+	bool edp_connected;
+	bool power_on;
+	bool core_initialized;
+
+	/* edid raw data */
+	struct edid *edid;
+
+	struct drm_dp_aux *drm_aux;
+
+	/* dpcd raw data */
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+
+	/* Link status */
+	u8 link_rate;
+	u8 lane_cnt;
+	u8 v_level;
+	u8 p_level;
+	struct edp_phy_opts edp_opts;
+
+	/* Timing status */
+	u8 interlaced;
+	u32 pixel_rate; /* in kHz */
+	u32 color_depth;
+	struct drm_display_mode drm_mode;
+
+	struct edp_aux *aux;
+	struct edp_phy *phy;
+};
+
+struct edp_ctrl_tu {
+	u32 rate;
+	u32 edp_tu;
+	u32 valid_boundary;
+	u32 valid_boundary2;
+};
+
+#define MAX_TU_TABLE 1
+static const struct edp_ctrl_tu tu[MAX_TU_TABLE] = {
+	{285550, 0x20, 0x13001B, 0x920035}, /* 1920x1080 at 120Hz CVT RB1 */
+};
+
+static inline bool edp_check_prefix(const char *clk_prefix,
+						const char *clk_name)
+{
+	return !strncmp(clk_prefix, clk_name, strlen(clk_prefix));
+}
+
+static int edp_init_clk_data(struct edp_ctrl *ctrl)
+{
+	int num_clk, i, rc;
+	int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
+	const char *clk_name;
+	struct device *dev = &ctrl->pdev->dev;
+	struct dss_module_power *core_power = &ctrl->mp[EDP_CORE_PM];
+	struct dss_module_power *ctrl_power = &ctrl->mp[EDP_CTRL_PM];
+	struct dss_module_power *stream_power = &ctrl->mp[EDP_STREAM_PM];
+
+	num_clk = of_property_count_strings(dev->of_node, "clock-names");
+	if (num_clk <= 0) {
+		DRM_ERROR("no clocks are defined\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_clk; i++) {
+		rc = of_property_read_string_index(dev->of_node,
+				"clock-names", i, &clk_name);
+		if (rc < 0)
+			return rc;
+
+		if (edp_check_prefix("core", clk_name))
+			core_clk_count++;
+
+		if (edp_check_prefix("ctrl", clk_name))
+			ctrl_clk_count++;
+
+		if (edp_check_prefix("stream", clk_name))
+			stream_clk_count++;
+	}
+
+	/* Initialize the CORE power module */
+	if (core_clk_count == 0) {
+		DRM_ERROR("no core clocks are defined\n");
+		return -EINVAL;
+	}
+
+	core_power->num_clk = core_clk_count;
+	core_power->clk_config = devm_kzalloc(dev,
+			sizeof(struct dss_clk) * core_power->num_clk,
+			GFP_KERNEL);
+	if (!core_power->clk_config)
+		return -EINVAL;
+
+	/* Initialize the CTRL power module */
+	if (ctrl_clk_count == 0) {
+		DRM_ERROR("no ctrl clocks are defined\n");
+		return -EINVAL;
+	}
+
+	ctrl_power->num_clk = ctrl_clk_count;
+	ctrl_power->clk_config = devm_kzalloc(dev,
+			sizeof(struct dss_clk) * ctrl_power->num_clk,
+			GFP_KERNEL);
+	if (!ctrl_power->clk_config) {
+		ctrl_power->num_clk = 0;
+		return -EINVAL;
+	}
+
+	/* Initialize the STREAM power module */
+	if (stream_clk_count == 0) {
+		DRM_ERROR("no stream (pixel) clocks are defined\n");
+		return -EINVAL;
+	}
+
+	stream_power->num_clk = stream_clk_count;
+	stream_power->clk_config = devm_kzalloc(dev,
+			sizeof(struct dss_clk) * stream_power->num_clk,
+			GFP_KERNEL);
+	if (!stream_power->clk_config) {
+		stream_power->num_clk = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int edp_clk_init(struct edp_ctrl *ctrl)
+{
+	int rc = 0, i = 0;
+	int num_clk = 0;
+	int core_clk_index = 0, ctrl_clk_index = 0, stream_clk_index = 0;
+	int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
+	const char *clk_name;
+	struct device *dev = &ctrl->pdev->dev;
+	struct dss_module_power *core_power = &ctrl->mp[EDP_CORE_PM];
+	struct dss_module_power *ctrl_power = &ctrl->mp[EDP_CTRL_PM];
+	struct dss_module_power *stream_power = &ctrl->mp[EDP_STREAM_PM];
+
+	rc =  edp_init_clk_data(ctrl);
+	if (rc) {
+		DRM_ERROR("failed to initialize power data %d\n", rc);
+		return -EINVAL;
+	}
+
+	core_clk_count = core_power->num_clk;
+	ctrl_clk_count = ctrl_power->num_clk;
+	stream_clk_count = stream_power->num_clk;
+
+	num_clk = core_clk_count + ctrl_clk_count + stream_clk_count;
+
+	for (i = 0; i < num_clk; i++) {
+		rc = of_property_read_string_index(dev->of_node, "clock-names",
+				i, &clk_name);
+		if (rc) {
+			DRM_ERROR("error reading clock-names %d\n", rc);
+			return rc;
+		}
+		if (edp_check_prefix("core", clk_name) &&
+				core_clk_index < core_clk_count) {
+			struct dss_clk *clk =
+				&core_power->clk_config[core_clk_index];
+			strscpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+			clk->type = DSS_CLK_AHB;
+			core_clk_index++;
+		} else if (edp_check_prefix("stream", clk_name) &&
+				stream_clk_index < stream_clk_count) {
+			struct dss_clk *clk =
+				&stream_power->clk_config[stream_clk_index];
+			strscpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+			clk->type = DSS_CLK_PCLK;
+			stream_clk_index++;
+		} else if (edp_check_prefix("ctrl", clk_name) &&
+			   ctrl_clk_index < ctrl_clk_count) {
+			struct dss_clk *clk =
+				&ctrl_power->clk_config[ctrl_clk_index];
+			strscpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+			ctrl_clk_index++;
+			if (edp_check_prefix("ctrl_link", clk_name) ||
+			    edp_check_prefix("stream_pixel", clk_name))
+				clk->type = DSS_CLK_PCLK;
+			else
+				clk->type = DSS_CLK_AHB;
+		}
+	}
+
+	DRM_DEBUG_DP("clock parsing successful\n");
+
+	rc = msm_dss_get_clk(dev, core_power->clk_config, core_power->num_clk);
+	if (rc) {
+		DRM_ERROR("failed to get core clk. err=%d\n", rc);
+		return rc;
+	}
+
+	rc = msm_dss_get_clk(dev, ctrl_power->clk_config, ctrl_power->num_clk);
+	if (rc) {
+		DRM_ERROR("failed to get ctrl clk. err=%d\n", rc);
+		msm_dss_put_clk(core_power->clk_config, core_power->num_clk);
+		return -ENODEV;
+	}
+
+	rc = msm_dss_get_clk(dev, stream_power->clk_config, stream_power->num_clk);
+	if (rc) {
+		DRM_ERROR("failed to get strem clk. err=%d\n", rc);
+		msm_dss_put_clk(core_power->clk_config, core_power->num_clk);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void edp_clk_deinit(struct edp_ctrl *ctrl)
+{
+	struct dss_module_power *core_power, *ctrl_power, *stream_power;
+
+	core_power = &ctrl->mp[EDP_CORE_PM];
+	ctrl_power = &ctrl->mp[EDP_CTRL_PM];
+	stream_power = &ctrl->mp[EDP_STREAM_PM];
+
+	if (!core_power || !ctrl_power || !stream_power)
+		DRM_ERROR("invalid power_data\n");
+
+	msm_dss_put_clk(ctrl_power->clk_config, ctrl_power->num_clk);
+	msm_dss_put_clk(core_power->clk_config, core_power->num_clk);
+	msm_dss_put_clk(stream_power->clk_config, stream_power->num_clk);
+}
+
+static int edp_clk_set_rate(struct edp_ctrl *ctrl,
+		enum edp_pm_type module, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power *mp = &ctrl->mp[module];
+
+	if (enable) {
+		rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
+		if (rc) {
+			DRM_ERROR("failed to set clks rate.\n");
+			return rc;
+		}
+	}
+
+	rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
+	if (rc) {
+		DRM_ERROR("failed to %d clks, err: %d\n", enable, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+int edp_clk_enable(struct edp_ctrl *ctrl,
+		enum edp_pm_type pm_type, bool enable)
+{
+	int rc = 0;
+
+	if (pm_type != EDP_CORE_PM && pm_type != EDP_CTRL_PM &&
+			pm_type != EDP_STREAM_PM) {
+		DRM_ERROR("unsupported power module\n");
+		return -EINVAL;
+	}
+
+	if (enable) {
+		if (pm_type == EDP_CORE_PM && ctrl->core_clks_on) {
+			DRM_DEBUG_DP("core clks already enabled\n");
+			return 0;
+		}
+
+		if (pm_type == EDP_CTRL_PM && ctrl->link_clks_on) {
+			DRM_DEBUG_DP("links clks already enabled\n");
+			return 0;
+		}
+
+		if (pm_type == EDP_STREAM_PM && ctrl->stream_clks_on) {
+			DRM_DEBUG_DP("pixel clks already enabled\n");
+			return 0;
+		}
+
+		if ((pm_type == EDP_CTRL_PM) && (!ctrl->core_clks_on)) {
+			DRM_DEBUG_DP("Enable core clks before link clks\n");
+
+			rc = edp_clk_set_rate(ctrl, EDP_CORE_PM, enable);
+			if (rc) {
+				DRM_ERROR("fail to enable clks: core. err=%d\n",
+					rc);
+				return rc;
+			}
+			ctrl->core_clks_on = true;
+		}
+	}
+
+	rc = edp_clk_set_rate(ctrl, pm_type, enable);
+	if (rc) {
+		DRM_ERROR("failed to '%s' clks. err=%d\n",
+			enable ? "enable" : "disable", rc);
+			return rc;
+	}
+
+	if (pm_type == EDP_CORE_PM)
+		ctrl->core_clks_on = enable;
+	else if (pm_type == EDP_STREAM_PM)
+		ctrl->stream_clks_on = enable;
+	else
+		ctrl->link_clks_on = enable;
+
+	DRM_DEBUG_DP("stream_clks:%s link_clks:%s core_clks:%s\n",
+		ctrl->stream_clks_on ? "on" : "off",
+		ctrl->link_clks_on ? "on" : "off",
+		ctrl->core_clks_on ? "on" : "off");
+
+	return 0;
+}
+
+static void edp_ctrl_set_clock_rate(struct edp_ctrl *ctrl,
+			enum edp_pm_type module, char *name, unsigned long rate)
+{
+	u32 num = ctrl->mp[module].num_clk;
+	struct dss_clk *cfg = ctrl->mp[module].clk_config;
+
+	while (num && strcmp(cfg->clk_name, name)) {
+		num--;
+		cfg++;
+	}
+
+	DRM_DEBUG_DP("setting rate=%lu on clk=%s\n", rate, name);
+
+	if (num)
+		cfg->rate = rate;
+	else
+		DRM_ERROR("%s clock doesn't exit to set rate %lu\n",
+				name, rate);
+}
+
+static int edp_regulator_init(struct edp_ctrl *ctrl)
+{
+	struct device *dev = &ctrl->pdev->dev;
+	int ret;
+
+	ctrl->vdda_vreg = devm_regulator_get(dev, "vdda");
+	ret = PTR_ERR_OR_ZERO(ctrl->vdda_vreg);
+	if (ret) {
+		DRM_ERROR("%s: Could not get vdda reg, ret = %d\n", __func__,
+				ret);
+		ctrl->vdda_vreg = NULL;
+		return ret;
+	}
+	ctrl->lvl_vreg = devm_regulator_get(dev, "lvl-vdd");
+	ret = PTR_ERR_OR_ZERO(ctrl->lvl_vreg);
+	if (ret) {
+		DRM_ERROR("%s: Could not get lvl-vdd reg, ret = %d\n", __func__,
+				ret);
+		ctrl->lvl_vreg = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+static int edp_regulator_enable(struct edp_ctrl *ctrl)
+{
+	int ret;
+
+	ret = regulator_set_load(ctrl->vdda_vreg, VDDA_UA_ON_LOAD);
+	if (ret < 0) {
+		DRM_ERROR("%s: vdda_vreg set regulator mode failed.\n", __func__);
+		goto vdda_set_fail;
+	}
+
+	ret = regulator_enable(ctrl->vdda_vreg);
+	if (ret) {
+		DRM_ERROR("%s: Failed to enable vdda_vreg regulator.\n", __func__);
+		goto vdda_enable_fail;
+	}
+
+	ret = regulator_set_load(ctrl->lvl_vreg, LVL_UA_ON_LOAD);
+	if (ret < 0) {
+		DRM_ERROR("%s: vdda_vreg set regulator mode failed.\n", __func__);
+		goto vdda_set_fail;
+	}
+
+	ret = regulator_enable(ctrl->lvl_vreg);
+	if (ret) {
+		DRM_ERROR("Failed to enable lvl-vdd reg regulator, %d", ret);
+		goto lvl_enable_fail;
+	}
+
+	return 0;
+
+lvl_enable_fail:
+	regulator_disable(ctrl->vdda_vreg);
+vdda_enable_fail:
+	regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
+vdda_set_fail:
+	return ret;
+}
+
+static void edp_regulator_disable(struct edp_ctrl *ctrl)
+{
+	regulator_disable(ctrl->lvl_vreg);
+	regulator_set_load(ctrl->lvl_vreg, LVL_UA_OFF_LOAD);
+	regulator_disable(ctrl->vdda_vreg);
+	regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
+}
+
+static int edp_gpio_config(struct edp_ctrl *ctrl)
+{
+	struct device *dev = &ctrl->pdev->dev;
+	int ret;
+
+	ctrl->panel_hpd_gpio = devm_gpiod_get(dev, "panel-hpd", GPIOD_IN);
+	if (IS_ERR(ctrl->panel_hpd_gpio)) {
+		ret = PTR_ERR(ctrl->panel_hpd_gpio);
+		ctrl->panel_hpd_gpio = NULL;
+		DRM_ERROR("%s: cannot get panel-hpd-gpios, %d\n", __func__, ret);
+		return ret;
+	}
+
+	ctrl->panel_en_gpio = devm_gpiod_get(dev, "panel-en", GPIOD_OUT_HIGH);
+	if (IS_ERR(ctrl->panel_en_gpio)) {
+		ret = PTR_ERR(ctrl->panel_en_gpio);
+		ctrl->panel_en_gpio = NULL;
+		DRM_ERROR("%s: cannot get panel-en-gpios, %d\n", __func__, ret);
+		return ret;
+	}
+
+	ctrl->panel_bklt1_gpio = devm_gpiod_get(dev, "panel-bklt1",
+			GPIOD_OUT_HIGH);
+	if (IS_ERR(ctrl->panel_bklt1_gpio)) {
+		ret = PTR_ERR(ctrl->panel_bklt1_gpio);
+		ctrl->panel_bklt1_gpio = NULL;
+		DRM_ERROR("%s: cannot get panel-bklt1-gpios, %d\n", __func__, ret);
+		return ret;
+	}
+
+	ctrl->panel_bklt2_gpio = devm_gpiod_get(dev, "panel-bklt2",
+			GPIOD_OUT_HIGH);
+	if (IS_ERR(ctrl->panel_bklt2_gpio)) {
+		ret = PTR_ERR(ctrl->panel_bklt2_gpio);
+		ctrl->panel_bklt2_gpio = NULL;
+		DRM_ERROR("%s: cannot get panel-bklt2-gpios, %d\n", __func__, ret);
+		return ret;
+	}
+
+	ctrl->panel_pwm_gpio = devm_gpiod_get(dev, "panel-pwm", GPIOD_OUT_HIGH);
+	if (IS_ERR(ctrl->panel_pwm_gpio)) {
+		ret = PTR_ERR(ctrl->panel_pwm_gpio);
+		ctrl->panel_pwm_gpio = NULL;
+		DRM_ERROR("%s: cannot get panel-pwm-gpios, %d\n", __func__, ret);
+		return ret;
+	}
+
+	DRM_INFO("gpio on");
+
+	return 0;
+}
+
+static void edp_ctrl_irq_enable(struct edp_ctrl *ctrl, int enable)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctrl->irq_lock, flags);
+	if (enable) {
+		edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS,
+				EDP_INTERRUPT_STATUS1_MASK);
+		edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS2,
+				EDP_INTERRUPT_STATUS2_MASK);
+	} else {
+		edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS,
+				EDP_INTERRUPT_STATUS1_ACK);
+		edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS2,
+				EDP_INTERRUPT_STATUS2_ACK);
+	}
+	spin_unlock_irqrestore(&ctrl->irq_lock, flags);
+}
+
+static void edp_fill_link_cfg(struct edp_ctrl *ctrl)
+{
+	u32 prate;
+	u32 bpp;
+	u8 max_lane = drm_dp_max_lane_count(ctrl->dpcd);
+
+	prate = ctrl->pixel_rate;
+	bpp = ctrl->color_depth * 3;
+
+	/*
+	 * By default, use the maximum link rate and minimum lane count,
+	 * so that we can do rate down shift during link training.
+	 */
+	ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
+	ctrl->lane_cnt = max_lane;
+	DRM_INFO("rate=%d lane=%d", ctrl->link_rate, ctrl->lane_cnt);
+}
+
+static void edp_config_ctrl(struct edp_ctrl *ctrl)
+{
+	u32 config = 0, depth = 0;
+	u8 *dpcd = ctrl->dpcd;
+
+	/* Default-> LSCLK DIV: 1/4 LCLK  */
+	config |= (2 << EDP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT);
+
+	/* Scrambler reset enable */
+	if (dpcd[DP_EDP_CONFIGURATION_CAP] & DP_ALTERNATE_SCRAMBLER_RESET_CAP)
+		config |= EDP_CONFIGURATION_CTRL_ASSR;
+
+	if (ctrl->color_depth == 8)
+		depth = EDP_8BIT;
+	else if (ctrl->color_depth == 10)
+		depth = EDP_10BIT;
+	else if (ctrl->color_depth == 12)
+		depth = EDP_12BIT;
+	else if (ctrl->color_depth == 16)
+		depth = EDP_16BIT;
+	config |= depth << EDP_CONFIGURATION_CTRL_BPC_SHIFT;
+
+	/* Num of Lanes */
+	config |= ((ctrl->lane_cnt - 1)
+			<< EDP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT);
+
+	if (drm_dp_enhanced_frame_cap(dpcd))
+		config |= EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
+
+	config |= EDP_CONFIGURATION_CTRL_P_INTERLACED; /* progressive video */
+
+	/* sync clock & static Mvid */
+	config |= EDP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN;
+	config |= EDP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK;
+
+	edp_write_link(ctrl->base, REG_EDP_CONFIGURATION_CTRL, config);
+}
+
+static void edp_state_ctrl(struct edp_ctrl *ctrl, u32 state)
+{
+	edp_write_link(ctrl->base, REG_EDP_STATE_CTRL, state);
+	/* Make sure H/W status is set */
+	wmb();
+}
+
+static int edp_lane_set_write(struct edp_ctrl *ctrl,
+	u8 voltage_level, u8 pre_emphasis_level)
+{
+	int i;
+	u8 buf[4];
+
+	if (voltage_level >= DPCD_LINK_VOLTAGE_MAX)
+		voltage_level |= 0x04;
+
+	if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX)
+		pre_emphasis_level |= 0x04;
+
+	pre_emphasis_level <<= 3;
+
+	for (i = 0; i < 4; i++)
+		buf[i] = voltage_level | pre_emphasis_level;
+
+	DRM_INFO("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level);
+	if (drm_dp_dpcd_write(ctrl->drm_aux, 0x103, buf, 4) < 4) {
+		DRM_ERROR("%s: Set sw/pe to panel failed\n", __func__);
+		return -ENOLINK;
+	}
+
+	return 0;
+}
+
+static int edp_train_pattern_set_write(struct edp_ctrl *ctrl, u8 pattern)
+{
+	u8 p = pattern;
+
+	DRM_DEBUG_DP("pattern=%x", p);
+	if (drm_dp_dpcd_write(ctrl->drm_aux,
+				DP_TRAINING_PATTERN_SET, &p, 1) < 1) {
+		DRM_ERROR("%s: Set training pattern to panel failed\n", __func__);
+		return -ENOLINK;
+	}
+
+	return 0;
+}
+
+static void edp_sink_train_set_adjust(struct edp_ctrl *ctrl,
+	const u8 *link_status)
+{
+	int i;
+	u8 max = 0;
+	u8 data;
+
+	/* use the max level across lanes */
+	for (i = 0; i < ctrl->lane_cnt; i++) {
+		data = drm_dp_get_adjust_request_voltage(link_status, i);
+		DRM_DEBUG_DP("lane=%d req_voltage_swing=0x%x", i, data);
+		if (max < data)
+			max = data;
+	}
+
+	ctrl->v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+
+	/* use the max level across lanes */
+	max = 0;
+	for (i = 0; i < ctrl->lane_cnt; i++) {
+		data = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+		DRM_DEBUG_DP("lane=%d req_pre_emphasis=0x%x", i, data);
+		if (max < data)
+			max = data;
+	}
+
+	ctrl->p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+	DRM_DEBUG_DP("v_level=%d, p_level=%d", ctrl->v_level, ctrl->p_level);
+}
+
+static void edp_host_train_set(struct edp_ctrl *ctrl, u32 train)
+{
+	int cnt = 10;
+	u32 data;
+	u32 shift = train - 1;
+
+	DRM_DEBUG_DP("train=%d", train);
+
+	edp_state_ctrl(ctrl, EDP_STATE_CTRL_LINK_TRAINING_PATTERN1 << shift);
+	while (--cnt) {
+		data = edp_read_link(ctrl->base, REG_EDP_MAINLINK_READY);
+		if (data & (EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY << shift))
+			break;
+	}
+
+	if (cnt == 0)
+		DRM_DEBUG_DP("%s: set link_train=%d failed\n", __func__, train);
+}
+
+static int edp_voltage_pre_emphasis_set(struct edp_ctrl *ctrl)
+{
+	DRM_DEBUG_DP("v=%d p=%d", ctrl->v_level, ctrl->p_level);
+
+	msm_edp_v510_phy_config(ctrl->phy, ctrl->v_level, ctrl->p_level);
+	return edp_lane_set_write(ctrl, ctrl->v_level, ctrl->p_level);
+}
+
+static int edp_start_link_train_1(struct edp_ctrl *ctrl)
+{
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	u8 old_v_level;
+	int tries;
+	int ret;
+	int rlen;
+
+	edp_host_train_set(ctrl, DP_TRAINING_PATTERN_1);
+	ret = edp_voltage_pre_emphasis_set(ctrl);
+	if (ret)
+		return ret;
+
+	ret = edp_train_pattern_set_write(ctrl,
+			DP_TRAINING_PATTERN_1 | DP_RECOVERED_CLOCK_OUT_EN);
+	if (ret)
+		return ret;
+
+	tries = 0;
+	old_v_level = ctrl->v_level;
+	while (1) {
+		drm_dp_link_train_clock_recovery_delay(ctrl->dpcd);
+
+		rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
+		if (rlen < DP_LINK_STATUS_SIZE) {
+			DRM_ERROR("%s: read link status failed\n", __func__);
+			return -ENOLINK;
+		}
+		if (drm_dp_clock_recovery_ok(link_status, ctrl->lane_cnt)) {
+			ret = 0;
+			break;
+		}
+
+		if (ctrl->v_level == DPCD_LINK_VOLTAGE_MAX) {
+			ret = -1;
+			break;
+		}
+
+		if (old_v_level == ctrl->v_level) {
+			tries++;
+			if (tries >= 5) {
+				ret = -1;
+				break;
+			}
+		} else {
+			tries = 0;
+			old_v_level = ctrl->v_level;
+		}
+
+		edp_sink_train_set_adjust(ctrl, link_status);
+		ret = edp_voltage_pre_emphasis_set(ctrl);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int edp_start_link_train_2(struct edp_ctrl *ctrl)
+{
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	int tries = 0;
+	int ret;
+	int rlen;
+
+	edp_host_train_set(ctrl, DP_TRAINING_PATTERN_2);
+	ret = edp_voltage_pre_emphasis_set(ctrl);
+	if (ret)
+		return ret;
+
+	ret = edp_train_pattern_set_write(ctrl,
+			DP_TRAINING_PATTERN_2 | DP_RECOVERED_CLOCK_OUT_EN);
+	if (ret)
+		return ret;
+
+	while (1) {
+		drm_dp_link_train_channel_eq_delay(ctrl->dpcd);
+
+		rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
+		if (rlen < DP_LINK_STATUS_SIZE) {
+			DRM_ERROR("%s: read link status failed\n", __func__);
+			return -ENOLINK;
+		}
+		if (drm_dp_channel_eq_ok(link_status, ctrl->lane_cnt)) {
+			ret = 0;
+			break;
+		}
+
+		tries++;
+		if (tries > 10) {
+			ret = -1;
+			break;
+		}
+
+		edp_sink_train_set_adjust(ctrl, link_status);
+		ret = edp_voltage_pre_emphasis_set(ctrl);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int edp_link_rate_down_shift(struct edp_ctrl *ctrl)
+{
+	u32 prate, lrate, bpp;
+	u8 rate, lane, max_lane;
+	int changed = 0;
+
+	rate = ctrl->link_rate;
+	lane = ctrl->lane_cnt;
+	max_lane = drm_dp_max_lane_count(ctrl->dpcd);
+
+	bpp = ctrl->color_depth * 3;
+	prate = ctrl->pixel_rate;
+	prate *= bpp;
+	prate /= 8; /* in kByte */
+
+	if (rate > DP_LINK_BW_1_62 && rate <= EDP_LINK_BW_MAX) {
+		rate -= 4;	/* reduce rate */
+		changed++;
+	}
+
+	if (changed) {
+		if (lane >= 1 && lane < max_lane)
+			lane <<= 1;	/* increase lane */
+
+		lrate = 270000; /* in kHz */
+		lrate *= rate;
+		lrate /= 10; /* kByte, 10 bits --> 8 bits */
+		lrate *= lane;
+
+		DRM_DEBUG_DP("new lrate=%u prate=%u(kHz) rate=%d lane=%d p=%u b=%d",
+			lrate, prate, rate, lane,
+			ctrl->pixel_rate,
+			bpp);
+
+		if (lrate > prate) {
+			ctrl->link_rate = rate;
+			ctrl->lane_cnt = lane;
+			DRM_DEBUG_DP("new rate=%d %d", rate, lane);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int edp_clear_training_pattern(struct edp_ctrl *ctrl)
+{
+	int ret;
+
+	ret = edp_train_pattern_set_write(ctrl, 0);
+
+	drm_dp_link_train_channel_eq_delay(ctrl->dpcd);
+
+	return ret;
+}
+
+static int edp_do_link_train(struct edp_ctrl *ctrl)
+{
+	u8 values[2], edp_config = 0;
+	int ret;
+
+	/*
+	 * Set the current link rate and lane cnt to panel. They may have been
+	 * adjusted and the values are different from them in DPCD CAP
+	 */
+	values[0] = ctrl->lane_cnt;
+	values[1] = ctrl->link_rate;
+
+	if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
+		values[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+	if (drm_dp_dpcd_write(ctrl->drm_aux, DP_LINK_BW_SET, &values[1], 1) < 0)
+		return EDP_TRAIN_FAIL;
+
+	drm_dp_dpcd_write(ctrl->drm_aux, DP_LANE_COUNT_SET, &values[0], 1);
+	ctrl->v_level = 0; /* start from default level */
+	ctrl->p_level = 0;
+
+	values[0] = DP_SPREAD_AMP_0_5;
+	values[1] = 1;
+	drm_dp_dpcd_write(ctrl->drm_aux, DP_DOWNSPREAD_CTRL, &values[0], 1);
+	drm_dp_dpcd_write(ctrl->drm_aux, DP_MAIN_LINK_CHANNEL_CODING_SET, &values[1], 1);
+
+	edp_state_ctrl(ctrl, 0);
+	if (edp_clear_training_pattern(ctrl))
+		return EDP_TRAIN_FAIL;
+
+	ret = edp_start_link_train_1(ctrl);
+	if (ret < 0) {
+		if (edp_link_rate_down_shift(ctrl) == 0) {
+			DRM_ERROR("link reconfig");
+			ret = EDP_TRAIN_RECONFIG;
+			goto clear;
+		} else {
+			DRM_ERROR("%s: Training 1 failed", __func__);
+			ret = EDP_TRAIN_FAIL;
+			goto clear;
+		}
+	}
+	DRM_INFO("Training 1 completed successfully");
+
+	edp_state_ctrl(ctrl, 0);
+	if (edp_clear_training_pattern(ctrl))
+		return EDP_TRAIN_FAIL;
+
+	ret = edp_start_link_train_2(ctrl);
+	if (ret < 0) {
+		if (edp_link_rate_down_shift(ctrl) == 0) {
+			DRM_ERROR("link reconfig");
+			ret = EDP_TRAIN_RECONFIG;
+			goto clear;
+		} else {
+			DRM_ERROR("%s: Training 2 failed", __func__);
+			ret = EDP_TRAIN_FAIL;
+			goto clear;
+		}
+	}
+	DRM_INFO("Training 2 completed successfully");
+
+	edp_config = DP_ALTERNATE_SCRAMBLER_RESET_ENABLE;
+	drm_dp_dpcd_write(ctrl->drm_aux, DP_EDP_CONFIGURATION_SET,
+			&edp_config, 1);
+
+	edp_state_ctrl(ctrl, EDP_STATE_CTRL_SEND_VIDEO);
+clear:
+	edp_clear_training_pattern(ctrl);
+
+	return ret;
+}
+
+static void edp_ctrl_config_misc(struct edp_ctrl *ctrl)
+{
+	u32 misc_val;
+	enum edp_color_depth depth = EDP_8BIT;
+
+	misc_val = edp_read_link(ctrl->base, REG_EDP_MISC1_MISC0);
+
+	if (ctrl->color_depth == 8)
+		depth = EDP_8BIT;
+	else if (ctrl->color_depth == 10)
+		depth = EDP_10BIT;
+	else if (ctrl->color_depth == 12)
+		depth = EDP_12BIT;
+	else if (ctrl->color_depth == 16)
+		depth = EDP_16BIT;
+
+	/* clear bpp bits */
+	misc_val &= ~(0x07 << EDP_MISC0_TEST_BITS_DEPTH_SHIFT);
+	misc_val |= depth << EDP_MISC0_TEST_BITS_DEPTH_SHIFT;
+
+	/* Configure clock to synchronous mode */
+	misc_val |= EDP_MISC0_SYNCHRONOUS_CLK;
+
+	DRM_DEBUG_DP("misc settings = 0x%x\n", misc_val);
+	edp_write_link(ctrl->base, REG_EDP_MISC1_MISC0, misc_val);
+}
+
+static void edp_ctrl_config_msa(struct edp_ctrl *ctrl)
+{
+	u32 pixel_m, pixel_n;
+	u32 mvid, nvid, pixel_div = 0, dispcc_input_rate;
+	unsigned long den, num;
+	u8 rate = ctrl->link_rate;
+	u32 stream_rate_khz = ctrl->pixel_rate;
+
+	if (rate == DP_LINK_BW_8_1)
+		pixel_div = 6;
+	else if (rate == DP_LINK_BW_1_62 || rate == DP_LINK_BW_2_7)
+		pixel_div = 2;
+	else if (rate == DP_LINK_BW_5_4)
+		pixel_div = 4;
+	else
+		DRM_ERROR("Invalid pixel mux divider\n");
+
+	dispcc_input_rate = (drm_dp_bw_code_to_link_rate(rate) * 10) / pixel_div;
+
+	rational_best_approximation(dispcc_input_rate, stream_rate_khz,
+			(unsigned long)(1 << 16) - 1,
+			(unsigned long)(1 << 16) - 1, &den, &num);
+
+	den = ~(den - num);
+	den = den & 0xFFFF;
+	pixel_m = num;
+	pixel_n = den;
+
+	mvid = (pixel_m & 0xFFFF) * 5;
+	nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+
+	if (rate == DP_LINK_BW_5_4)
+		nvid *= 2;
+
+	if (rate == DP_LINK_BW_8_1)
+		nvid *= 3;
+
+	DRM_DEBUG_DP("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
+	edp_write_link(ctrl->base, REG_EDP_SOFTWARE_MVID, mvid);
+	edp_write_link(ctrl->base, REG_EDP_SOFTWARE_NVID, nvid);
+	edp_write_p0(ctrl->base, REG_EDP_DSC_DTO, 0x0);
+}
+
+static void edp_ctrl_config_TU(struct edp_ctrl *ctrl)
+{
+	int i;
+
+	for (i = 0; i < MAX_TU_TABLE; i++) {
+		if (tu[i].rate == ctrl->pixel_rate)
+			break;
+	}
+
+	edp_write_link(ctrl->base, REG_EDP_VALID_BOUNDARY,
+			tu[i].valid_boundary);
+
+	edp_write_link(ctrl->base, REG_EDP_TU, tu[i].edp_tu);
+
+	edp_write_link(ctrl->base, REG_EDP_VALID_BOUNDARY_2,
+			tu[i].valid_boundary2);
+}
+
+static void edp_ctrl_timing_cfg(struct edp_ctrl *ctrl)
+{
+	struct drm_display_mode *mode = &ctrl->drm_mode;
+	u32 hstart_from_sync, vstart_from_sync;
+	u32 data;
+
+	/* Configure eDP timing to HW */
+	edp_write_link(ctrl->base, REG_EDP_TOTAL_HOR_VER,
+		EDP_TOTAL_HOR_VER_HORIZ(mode->htotal) |
+		EDP_TOTAL_HOR_VER_VERT(mode->vtotal));
+
+	vstart_from_sync = mode->vtotal - mode->vsync_start;
+	hstart_from_sync = mode->htotal - mode->hsync_start;
+	edp_write_link(ctrl->base, REG_EDP_START_HOR_VER_FROM_SYNC,
+		EDP_START_HOR_VER_FROM_SYNC_HORIZ(hstart_from_sync) |
+		EDP_START_HOR_VER_FROM_SYNC_VERT(vstart_from_sync));
+
+	data = EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT(
+			mode->vsync_end - mode->vsync_start);
+	data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ(
+			mode->hsync_end - mode->hsync_start);
+	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+		data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NVSYNC;
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NHSYNC;
+	edp_write_link(ctrl->base, REG_EDP_HSYNC_VSYNC_WIDTH_POLARITY, data);
+
+	edp_write_link(ctrl->base, REG_EDP_ACTIVE_HOR_VER,
+		EDP_ACTIVE_HOR_VER_HORIZ(mode->hdisplay) |
+		EDP_ACTIVE_HOR_VER_VERT(mode->vdisplay));
+
+}
+
+static void edp_mainlink_ctrl(struct edp_ctrl *ctrl, int enable)
+{
+	u32 data = 0;
+
+	edp_write_link(ctrl->base, REG_EDP_MAINLINK_CTRL, EDP_MAINLINK_CTRL_RESET);
+	/* Make sure fully reset */
+	wmb();
+	usleep_range(500, 1000);
+
+	if (enable) {
+		data = (EDP_MAINLINK_CTRL_ENABLE |
+				EDP_MAINLINK_FB_BOUNDARY_SEL);
+	}
+
+	edp_write_link(ctrl->base, REG_EDP_MAINLINK_CTRL, data);
+}
+
+static void edp_ctrl_phy_enable(struct edp_ctrl *ctrl, int enable)
+{
+	if (enable) {
+		edp_write_ahb(ctrl->base, REG_EDP_PHY_CTRL,
+			EDP_PHY_CTRL_SW_RESET | EDP_PHY_CTRL_SW_RESET_PLL);
+		usleep_range(1000, 1100);
+		edp_write_ahb(ctrl->base, REG_EDP_PHY_CTRL, 0);
+
+		msm_edp_v510_phy_enable(ctrl->phy);
+	}
+}
+
+static void edp_ctrl_phy_aux_enable(struct edp_ctrl *ctrl, int enable)
+{
+	if (ctrl->core_initialized == enable)
+		return;
+
+	if (enable) {
+		pm_runtime_get_sync(&ctrl->pdev->dev);
+		edp_regulator_enable(ctrl);
+		edp_clk_enable(ctrl, EDP_CORE_PM, 1);
+		edp_ctrl_phy_enable(ctrl, 1);
+		msm_edp_v510_aux_ctrl(ctrl->aux, 1);
+		ctrl->core_initialized =  true;
+	} else {
+		msm_edp_v510_aux_ctrl(ctrl->aux, 0);
+		edp_clk_enable(ctrl, EDP_CORE_PM, 0);
+		edp_regulator_disable(ctrl);
+		pm_runtime_put_sync(&ctrl->pdev->dev);
+		ctrl->core_initialized =  false;
+	}
+}
+
+static void edp_ctrl_link_enable(struct edp_ctrl *ctrl, int enable)
+{
+	unsigned long link_rate;
+
+	link_rate = drm_dp_max_link_rate(ctrl->dpcd);
+	ctrl->edp_opts.link_rate = link_rate;
+	ctrl->edp_opts.lanes = drm_dp_max_lane_count(ctrl->dpcd);
+
+	if (enable) {
+		msm_edp_v510_phy_vm_pe_init(ctrl->phy, &ctrl->edp_opts);
+		msm_edp_v510_phy_power_on(ctrl->phy);
+
+		/* Enable link channel clocks */
+		edp_ctrl_set_clock_rate(ctrl, EDP_CTRL_PM, "ctrl_link",
+				link_rate * 1000);
+		edp_clk_enable(ctrl, EDP_CTRL_PM, 1);
+
+		edp_ctrl_set_clock_rate(ctrl, EDP_STREAM_PM, "stream_pixel",
+				ctrl->pixel_rate * 1000);
+		edp_clk_enable(ctrl, EDP_STREAM_PM, 1);
+
+		edp_mainlink_ctrl(ctrl, 1);
+		edp_config_ctrl(ctrl);
+		edp_ctrl_config_misc(ctrl);
+		edp_ctrl_timing_cfg(ctrl);
+		edp_ctrl_config_msa(ctrl);
+		edp_ctrl_config_TU(ctrl);
+
+	} else {
+		edp_mainlink_ctrl(ctrl, 0);
+		edp_clk_enable(ctrl, EDP_STREAM_PM, 0);
+		edp_clk_enable(ctrl, EDP_CTRL_PM, 0);
+	}
+}
+
+static int edp_ctrl_training(struct edp_ctrl *ctrl)
+{
+	int ret;
+
+	/* Do link training only when power is on */
+	if (!ctrl->power_on)
+		return -EINVAL;
+
+train_start:
+	ret = edp_do_link_train(ctrl);
+	if (ret == EDP_TRAIN_RECONFIG) {
+		/* Re-configure main link */
+		edp_ctrl_irq_enable(ctrl, 0);
+		edp_ctrl_link_enable(ctrl, 0);
+
+		/* Make sure link is fully disabled */
+		wmb();
+		usleep_range(500, 1000);
+
+		edp_ctrl_phy_enable(ctrl, 1);
+		edp_ctrl_irq_enable(ctrl, 1);
+		edp_ctrl_link_enable(ctrl, 1);
+		goto train_start;
+	}
+
+	return ret;
+}
+
+static void edp_ctrl_on_worker(struct work_struct *work)
+{
+	struct edp_ctrl *ctrl = container_of(
+				work, struct edp_ctrl, on_work);
+	u8 value;
+	int ret;
+
+	mutex_lock(&ctrl->dev_mutex);
+
+	if (ctrl->power_on) {
+		DRM_INFO("already on");
+		goto unlock_ret;
+	}
+
+	edp_ctrl_phy_aux_enable(ctrl, 1);
+	edp_ctrl_irq_enable(ctrl, 1);
+	edp_ctrl_link_enable(ctrl, 1);
+
+
+	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
+	if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
+		ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
+		if (ret < 0)
+			goto fail;
+
+		value &= ~DP_SET_POWER_MASK;
+		value |= DP_SET_POWER_D0;
+
+		ret = drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
+		if (ret < 0)
+			goto fail;
+
+		/*
+		 * 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);
+	}
+
+	ctrl->power_on = true;
+
+	/* Start link training */
+	ret = edp_ctrl_training(ctrl);
+	if (ret != EDP_TRAIN_SUCCESS)
+		goto fail;
+
+	DRM_INFO("DONE");
+	goto unlock_ret;
+
+fail:
+	edp_ctrl_irq_enable(ctrl, 0);
+	edp_ctrl_link_enable(ctrl, 0);
+	edp_ctrl_phy_aux_enable(ctrl, 0);
+	ctrl->power_on = false;
+unlock_ret:
+	mutex_unlock(&ctrl->dev_mutex);
+}
+
+static void edp_ctrl_off_worker(struct work_struct *work)
+{
+	struct edp_ctrl *ctrl = container_of(
+				work, struct edp_ctrl, off_work);
+	unsigned long time_left;
+
+	mutex_lock(&ctrl->dev_mutex);
+
+	if (!ctrl->power_on) {
+		DRM_INFO("already off");
+		goto unlock_ret;
+	}
+
+	reinit_completion(&ctrl->idle_comp);
+
+	edp_state_ctrl(ctrl, EDP_STATE_CTRL_PUSH_IDLE);
+
+	time_left = wait_for_completion_timeout(&ctrl->idle_comp,
+						msecs_to_jiffies(500));
+	if (!time_left)
+		DRM_ERROR("%s: idle pattern timedout\n", __func__);
+
+	edp_state_ctrl(ctrl, 0);
+
+	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
+	if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
+		u8 value;
+		int ret;
+
+		ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
+		if (ret > 0) {
+			value &= ~DP_SET_POWER_MASK;
+			value |= DP_SET_POWER_D3;
+
+			drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
+		}
+	}
+
+	edp_ctrl_irq_enable(ctrl, 0);
+
+	edp_ctrl_link_enable(ctrl, 0);
+
+	edp_ctrl_phy_aux_enable(ctrl, 0);
+
+	ctrl->power_on = false;
+
+unlock_ret:
+	mutex_unlock(&ctrl->dev_mutex);
+}
+
+irqreturn_t msm_edp_v510_ctrl_irq(struct edp_ctrl *ctrl)
+{
+	u32 isr1, isr2, mask1, mask2;
+	u32 ack;
+
+	spin_lock(&ctrl->irq_lock);
+	isr1 = edp_read_ahb(ctrl->base, REG_EDP_INTR_STATUS);
+	isr2 = edp_read_ahb(ctrl->base, REG_EDP_INTR_STATUS2);
+
+	mask1 = isr1 & EDP_INTERRUPT_STATUS1_MASK;
+	mask2 = isr2 & EDP_INTERRUPT_STATUS2_MASK;
+
+	isr1 &= ~mask1;	/* remove masks bit */
+	isr2 &= ~mask2;
+
+	DRM_DEBUG_DP("isr=%x mask=%x isr2=%x mask2=%x",
+			isr1, mask1, isr2, mask2);
+
+	ack = isr1 & EDP_INTERRUPT_STATUS1;
+	ack <<= 1;	/* ack bits */
+	ack |= mask1;
+	edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS, ack);
+
+	ack = isr2 & EDP_INTERRUPT_STATUS2;
+	ack <<= 1;	/* ack bits */
+	ack |= mask2;
+	edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS2, ack);
+	spin_unlock(&ctrl->irq_lock);
+
+	if (isr2 & EDP_INTR_READY_FOR_VIDEO)
+		DRM_INFO("edp_video_ready");
+
+	if (isr2 & EDP_INTR_IDLE_PATTERN_SENT) {
+		DRM_INFO("idle_patterns_sent");
+		complete(&ctrl->idle_comp);
+	}
+
+	msm_edp_v510_aux_irq(ctrl->aux, isr1);
+
+	return IRQ_HANDLED;
+}
+
+void msm_edp_v510_ctrl_power(struct edp_ctrl *ctrl, bool on)
+{
+	if (on)
+		queue_work(ctrl->workqueue, &ctrl->on_work);
+	else
+		queue_work(ctrl->workqueue, &ctrl->off_work);
+}
+
+int msm_edp_v510_ctrl_init(struct msm_edp_v510 *edp)
+{
+	struct edp_ctrl *ctrl = NULL;
+	struct device *dev = &edp->base.pdev->dev;
+	int ret;
+
+	if (!edp) {
+		DRM_ERROR("%s: edp is NULL!\n", __func__);
+		return -EINVAL;
+	}
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	edp->ctrl = ctrl;
+	ctrl->pdev = edp->base.pdev;
+
+	ctrl->base = msm_ioremap(ctrl->pdev, "edp_ctrl", "eDP_CTRL");
+	if (IS_ERR(ctrl->base))
+		return PTR_ERR(ctrl->base);
+
+	ctrl->phy_base = msm_ioremap(ctrl->pdev, "edp_phy", "eDP_PHY");
+	if (IS_ERR(ctrl->phy_base))
+		return PTR_ERR(ctrl->phy_base);
+
+	/* Get regulator, clock, gpio, pwm */
+	ret = edp_regulator_init(ctrl);
+	if (ret) {
+		DRM_ERROR("%s:regulator init fail\n", __func__);
+		return ret;
+	}
+	ret = edp_clk_init(ctrl);
+	if (ret) {
+		DRM_ERROR("%s:clk init fail\n", __func__);
+		return ret;
+	}
+	ret = edp_gpio_config(ctrl);
+	if (ret) {
+		DRM_ERROR("%s:failed to configure GPIOs: %d", __func__, ret);
+		return ret;
+	}
+
+	/* Init aux and phy */
+	ctrl->aux = msm_edp_v510_aux_init(dev, ctrl->base, &ctrl->drm_aux);
+	if (!ctrl->aux || !ctrl->drm_aux) {
+		DRM_ERROR("%s:failed to init aux\n", __func__);
+		return -ENOMEM;
+	}
+
+	ctrl->phy = msm_edp_v510_phy_init(dev, ctrl->phy_base, &ctrl->edp_opts);
+	if (!ctrl->phy) {
+		DRM_ERROR("%s:failed to init phy\n", __func__);
+		ret = -ENOMEM;
+		goto err_destory_aux;
+	}
+
+	pm_runtime_enable(dev);
+	spin_lock_init(&ctrl->irq_lock);
+	mutex_init(&ctrl->dev_mutex);
+	init_completion(&ctrl->idle_comp);
+
+	/* setup workqueue */
+	ctrl->workqueue = alloc_ordered_workqueue("edp_drm_work", 0);
+	INIT_WORK(&ctrl->on_work, edp_ctrl_on_worker);
+	INIT_WORK(&ctrl->off_work, edp_ctrl_off_worker);
+
+	return 0;
+
+err_destory_aux:
+	msm_edp_v510_aux_destroy(dev, ctrl->aux);
+	ctrl->aux = NULL;
+	return ret;
+}
+
+void msm_edp_v510_ctrl_destroy(struct edp_ctrl *ctrl)
+{
+	if (!ctrl)
+		return;
+
+	if (ctrl->workqueue) {
+		flush_workqueue(ctrl->workqueue);
+		destroy_workqueue(ctrl->workqueue);
+		ctrl->workqueue = NULL;
+	}
+
+	if (ctrl->aux) {
+		msm_edp_v510_aux_destroy(&ctrl->pdev->dev, ctrl->aux);
+		ctrl->aux = NULL;
+	}
+
+	edp_clk_deinit(ctrl);
+
+	kfree(ctrl->edid);
+	ctrl->edid = NULL;
+
+	mutex_destroy(&ctrl->dev_mutex);
+}
+
+bool msm_edp_v510_ctrl_panel_connected(struct edp_ctrl *ctrl)
+{
+	mutex_lock(&ctrl->dev_mutex);
+	if (ctrl->edp_connected) {
+		mutex_unlock(&ctrl->dev_mutex);
+		return true;
+	}
+
+	if (!ctrl->power_on) {
+		edp_ctrl_phy_aux_enable(ctrl, 1);
+		edp_ctrl_irq_enable(ctrl, 1);
+	}
+
+	if (drm_dp_dpcd_read(ctrl->drm_aux, DP_DPCD_REV, ctrl->dpcd,
+				DP_RECEIVER_CAP_SIZE) < DP_RECEIVER_CAP_SIZE) {
+		DRM_ERROR("%s: AUX channel is NOT ready\n", __func__);
+		memset(ctrl->dpcd, 0, DP_RECEIVER_CAP_SIZE);
+
+		if (!ctrl->power_on) {
+			edp_ctrl_irq_enable(ctrl, 0);
+			edp_ctrl_phy_aux_enable(ctrl, 0);
+		}
+
+	} else {
+		ctrl->edp_connected = true;
+	}
+
+
+	DRM_INFO("connect status=%d", ctrl->edp_connected);
+
+	mutex_unlock(&ctrl->dev_mutex);
+
+	return ctrl->edp_connected;
+}
+
+int msm_edp_v510_ctrl_get_panel_info(struct edp_ctrl *ctrl,
+		struct drm_connector *connector, struct edid **edid)
+{
+	int ret = 0;
+
+	mutex_lock(&ctrl->dev_mutex);
+
+	if (ctrl->edid) {
+		if (edid) {
+			DRM_DEBUG_DP("Just return edid buffer");
+			*edid = ctrl->edid;
+		}
+		goto unlock_ret;
+	}
+
+	if (!ctrl->power_on && !ctrl->edp_connected) {
+		edp_ctrl_phy_aux_enable(ctrl, 1);
+		edp_ctrl_irq_enable(ctrl, 1);
+	}
+
+	/* Initialize link rate as panel max link rate */
+	ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
+
+
+	ctrl->edid = drm_get_edid(connector, &ctrl->drm_aux->ddc);
+	if (!ctrl->edid) {
+		DRM_ERROR("%s: edid read fail\n", __func__);
+		if (!ctrl->power_on) {
+			edp_ctrl_irq_enable(ctrl, 0);
+			edp_ctrl_phy_aux_enable(ctrl, 0);
+		}
+		goto unlock_ret;
+	}
+
+	if (edid)
+		*edid = ctrl->edid;
+
+unlock_ret:
+	mutex_unlock(&ctrl->dev_mutex);
+	return ret;
+}
+
+int msm_edp_v510_ctrl_mode_set(struct edp_ctrl *ctrl,
+				const struct drm_display_mode *mode,
+				const struct drm_display_info *info)
+{
+	/*
+	 * Need to keep color depth, pixel rate and
+	 * interlaced information in ctrl context
+	 */
+	ctrl->color_depth = info->bpc;
+	ctrl->pixel_rate = mode->clock;
+
+	memcpy(&ctrl->drm_mode, mode, sizeof(*mode));
+
+	ctrl->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+	/* Fill initial link config based on passed in timing */
+	edp_fill_link_cfg(ctrl);
+
+	return 0;
+}
+
+
+bool msm_edp_v510_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl, u32 pixel_rate)
+{
+	u32 link_clock = 0;
+	unsigned long link_bw = 0, stream_bw = 0;
+
+	link_clock = drm_dp_bw_code_to_link_rate(ctrl->link_rate);
+	link_bw = link_clock * ctrl->lane_cnt;
+	stream_bw = pixel_rate * ctrl->color_depth * 3 / 8;
+
+	if (stream_bw > link_bw) {
+		DRM_ERROR("pixel clock %d(kHz) not supported", pixel_rate);
+		return false;
+	}
+
+	return true;
+}
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510_phy.c b/drivers/gpu/drm/msm/edp/v510/edp_v510_phy.c
new file mode 100644
index 0000000..3c575f4
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510_phy.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include "edp_v510.h"
+#include "edp_v510_reg.h"
+
+#define MSM_EDP_PLL_OFFSET 0x0000
+#define MSM_EDP_TX0_OFFSET 0x0200
+#define MSM_EDP_TX1_OFFSET 0x0600
+#define MSM_EDP_PHY_OFFSET 0x0a00
+
+struct edp_phy_clks {
+	struct edp_phy *edp_phy;
+	struct clk_hw edp_link_hw;
+	struct clk_hw edp_pixel_hw;
+};
+
+struct edp_phy {
+	void __iomem *base;
+	struct edp_phy_opts *edp_opts;
+	struct edp_phy_clks *edp_clks;
+};
+
+static inline u32 edp_pll_read(struct edp_phy *phy, u32 offset)
+{
+	offset += MSM_EDP_PLL_OFFSET;
+	return readl_relaxed(phy->base + offset);
+}
+
+static inline u32 edp_tx0_read(struct edp_phy *phy, u32 offset)
+{
+	offset += MSM_EDP_TX0_OFFSET;
+	return readl_relaxed(phy->base + offset);
+}
+
+static inline u32 edp_tx1_read(struct edp_phy *phy, u32 offset)
+{
+	offset += MSM_EDP_TX1_OFFSET;
+	return readl_relaxed(phy->base + offset);
+}
+
+static inline u32 edp_phy_read(struct edp_phy *phy, u32 offset)
+{
+	offset += MSM_EDP_PHY_OFFSET;
+	return readl_relaxed(phy->base + offset);
+}
+
+static inline void edp_pll_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+	offset += MSM_EDP_PLL_OFFSET;
+	writel(data, phy->base + offset);
+}
+
+static inline void edp_tx0_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+	offset += MSM_EDP_TX0_OFFSET;
+	writel(data, phy->base + offset);
+}
+
+static inline void edp_tx1_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+	offset += MSM_EDP_TX1_OFFSET;
+	writel(data, phy->base + offset);
+}
+
+static inline void edp_phy_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+	offset += MSM_EDP_PHY_OFFSET;
+	writel(data, phy->base + offset);
+}
+
+static int edp_pixel_clk_determine_rate(struct clk_hw *hw,
+						struct clk_rate_request *req)
+{
+	switch (req->rate) {
+	case 1620000000UL / 2:
+	case 2160000000UL / 2:
+	case 2430000000UL / 2:
+	case 2700000000UL / 2:
+	case 5940000000UL / 6:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static unsigned long
+edp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct edp_phy_clks *edp_clks;
+	struct edp_phy *edp_phy;
+	struct edp_phy_opts *opts;
+
+	edp_clks = container_of(hw, struct edp_phy_clks, edp_pixel_hw);
+	edp_phy = edp_clks->edp_phy;
+	opts = edp_phy->edp_opts;
+
+	switch (opts->link_rate) {
+	case 162000:
+		return 1620000000UL / 2;
+	break;
+	case 216000:
+		return 2160000000UL / 2;
+	break;
+	case 243000:
+		return 2430000000UL / 2;
+	break;
+	case 270000:
+		return 2700000000UL / 2;
+	break;
+	case 324000:
+		return 3240000000UL / 4;
+	break;
+	case 432000:
+		return 4320000000UL / 4;
+	break;
+	case 540000:
+		return 5400000000UL / 4;
+	break;
+	case 594000:
+		return 5940000000UL / 6;
+	case 810000:
+		return 8100000000UL / 6;
+	default:
+		return 0;
+	}
+}
+
+static const struct clk_ops edp_pixel_clk_ops = {
+	.determine_rate = edp_pixel_clk_determine_rate,
+	.recalc_rate = edp_pixel_clk_recalc_rate,
+};
+
+static int edp_link_clk_determine_rate(struct clk_hw *hw,
+						struct clk_rate_request *req)
+{
+	switch (req->rate) {
+	case 162000000:
+	case 216000000:
+	case 243000000:
+	case 270000000:
+	case 324000000:
+	case 432000000:
+	case 540000000:
+	case 594000000:
+	case 810000000:
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static unsigned long
+edp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct edp_phy_clks *edp_clks;
+	struct edp_phy *edp_phy;
+	struct edp_phy_opts *opts;
+
+	edp_clks = container_of(hw, struct edp_phy_clks, edp_link_hw);
+	edp_phy = edp_clks->edp_phy;
+	opts = edp_phy->edp_opts;
+
+	switch (opts->link_rate) {
+	case 162000:
+	case 216000:
+	case 243000:
+	case 270000:
+	case 324000:
+	case 432000:
+	case 540000:
+	case 594000:
+	case 810000:
+		return opts->link_rate * 1000;
+	default:
+		return 0;
+	}
+}
+
+static const struct clk_ops edp_link_clk_ops = {
+	.determine_rate = edp_link_clk_determine_rate,
+	.recalc_rate = edp_link_clk_recalc_rate,
+};
+
+static struct clk_hw *
+edp_clks_hw_get(struct of_phandle_args *clkspec, void *data)
+{
+	struct edp_phy_clks *edp_clks = data;
+	unsigned int idx = clkspec->args[0];
+
+	if (idx >= 2) {
+		pr_err("%s: invalid index %u\n", __func__, idx);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (idx == 0)
+		return &edp_clks->edp_link_hw;
+
+	return &edp_clks->edp_pixel_hw;
+}
+
+static void edp_clk_release_provider(void *res)
+{
+	of_clk_del_provider(res);
+}
+
+static int edp_phy_clks_register(struct device *dev, struct edp_phy *edp_phy)
+{
+	struct clk_init_data init = { };
+	struct edp_phy_clks *edp_clks;
+	int ret;
+
+	edp_clks = devm_kzalloc(dev, sizeof(*edp_clks), GFP_KERNEL);
+	if (!edp_clks)
+		return -ENOMEM;
+
+	edp_clks->edp_phy = edp_phy;
+	edp_phy->edp_clks = edp_clks;
+
+	init.ops = &edp_link_clk_ops;
+	init.name = "edp_phy_pll_link_clk";
+	edp_clks->edp_link_hw.init = &init;
+	ret = devm_clk_hw_register(dev, &edp_clks->edp_link_hw);
+	if (ret)
+		return ret;
+
+	init.ops = &edp_pixel_clk_ops;
+	init.name = "edp_phy_pll_vco_div_clk";
+	edp_clks->edp_pixel_hw.init = &init;
+	ret = devm_clk_hw_register(dev, &edp_clks->edp_pixel_hw);
+	if (ret)
+		return ret;
+
+	ret = of_clk_add_hw_provider(dev->of_node, edp_clks_hw_get, edp_clks);
+	if (ret)
+		return ret;
+
+	/*
+	 * Roll a devm action because the clock provider is the child node, but
+	 * the child node is not actually a device.
+	 */
+	ret = devm_add_action(dev, edp_clk_release_provider, dev->of_node);
+	if (ret)
+		edp_clk_release_provider(dev->of_node);
+
+	return ret;
+}
+
+static void edp_phy_ssc_en(struct edp_phy *edp_phy, bool en)
+{
+	if (en) {
+		edp_pll_write(edp_phy, 0x10, 0x01);
+		edp_pll_write(edp_phy, 0x14, 0x00);
+		edp_pll_write(edp_phy, 0x1c, 0x36);
+		edp_pll_write(edp_phy, 0x20, 0x01);
+		edp_pll_write(edp_phy, 0x24, 0x5c);
+		edp_pll_write(edp_phy, 0x28, 0x08);
+	} else {
+		edp_pll_write(edp_phy, 0x10, 0x00);
+	}
+}
+
+int msm_edp_v510_phy_enable(struct edp_phy *edp_phy)
+{
+	u32 status;
+
+	edp_phy_write(edp_phy, EDP_PHY_PD_CTL, 0x7D);
+	edp_pll_write(edp_phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG1, 0x13);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG2, 0x24);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG3, 0x00);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG4, 0x0a);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG5, 0x26);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG6, 0x0a);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG7, 0x03);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG8, 0xB7);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_CFG9, 0x03);
+	edp_phy_write(edp_phy, EDP_PHY_AUX_INTERRUPT_MASK, 0x1f);
+
+	edp_phy_write(edp_phy, EDP_PHY_MODE, 0xFC);
+
+	if (readl_poll_timeout_atomic((edp_phy->base +
+				MSM_EDP_PLL_OFFSET + QSERDES_COM_CMN_STATUS),
+				status, ((status & BIT(7)) > 0), 5, 100))
+		DRM_ERROR("%s: refgen not ready. Status=0x%x\n", __func__, status);
+
+	edp_tx0_write(edp_phy, TXn_LDO_CONFIG, 0x01);
+	edp_tx1_write(edp_phy, TXn_LDO_CONFIG, 0x01);
+	edp_tx0_write(edp_phy, TXn_LANE_MODE_1, 0x00);
+	edp_tx1_write(edp_phy, TXn_LANE_MODE_1, 0x00);
+
+	return 0;
+}
+
+static const u8 edp_hbr2_pre_emphasis[4][4] = {
+	{0x08, 0x11, 0x17, 0x1B},	/* pe0, 0 db */
+	{0x00, 0x0C, 0x13, 0xFF},	/* pe1, 3.5 db */
+	{0x05, 0x10, 0xFF, 0xFF},	/* pe2, 6.0 db */
+	{0x00, 0xFF, 0xFF, 0xFF}	/* pe3, 9.5 db */
+};
+
+static const u8 edp_hbr2_voltage_swing[4][4] = {
+	{0x0A, 0x11, 0x17, 0x1F}, /* sw0, 0.4v  */
+	{0x0C, 0x14, 0x1D, 0xFF}, /* sw1, 0.6 v */
+	{0x15, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
+	{0x17, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
+};
+
+void msm_edp_v510_phy_vm_pe_init(struct edp_phy *edp_phy, struct edp_phy_opts *opts)
+{
+
+	edp_phy->edp_opts = opts;
+
+	edp_tx0_write(edp_phy, TXn_TX_DRV_LVL, edp_hbr2_voltage_swing[0][0]);
+	edp_tx0_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+			edp_hbr2_pre_emphasis[0][0]);
+	edp_tx1_write(edp_phy, TXn_TX_DRV_LVL, edp_hbr2_voltage_swing[0][0]);
+	edp_tx1_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+			edp_hbr2_pre_emphasis[0][0]);
+
+	edp_tx0_write(edp_phy, TXn_HIGHZ_DRVR_EN, 4);
+	edp_tx0_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 3);
+	edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 7);
+	edp_tx1_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 0);
+	edp_phy_write(edp_phy, EDP_PHY_CFG_1, 3);
+
+}
+
+void msm_edp_v510_phy_config(struct edp_phy *edp_phy, u8 v_level, u8 p_level)
+{
+	edp_tx0_write(edp_phy, TXn_TX_DRV_LVL,
+			edp_hbr2_voltage_swing[v_level][p_level]);
+	edp_tx0_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+			edp_hbr2_pre_emphasis[v_level][p_level]);
+
+	edp_tx1_write(edp_phy, TXn_TX_DRV_LVL,
+			edp_hbr2_voltage_swing[v_level][p_level]);
+	edp_tx1_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+			edp_hbr2_pre_emphasis[v_level][p_level]);
+}
+
+static void edp_pll_vco_init(struct edp_phy *edp_phy)
+{
+	edp_phy_ssc_en(edp_phy, true);
+	edp_pll_write(edp_phy, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
+	edp_pll_write(edp_phy, QSERDES_COM_SYSCLK_EN_SEL, 0x0b);
+	edp_pll_write(edp_phy, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+	edp_pll_write(edp_phy, QSERDES_COM_CLK_ENABLE1, 0x0c);
+	edp_pll_write(edp_phy, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
+	edp_pll_write(edp_phy, QSERDES_COM_CLK_SEL, 0x30);
+	edp_pll_write(edp_phy, QSERDES_COM_PLL_IVCO, 0x07);
+	edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP_EN, 0x04);
+	edp_pll_write(edp_phy, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
+	edp_pll_write(edp_phy, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+	edp_pll_write(edp_phy, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+	edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00);
+	edp_pll_write(edp_phy, QSERDES_COM_CMN_CONFIG, 0x02);
+	edp_pll_write(edp_phy, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3f);
+	edp_pll_write(edp_phy, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
+	edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+	edp_pll_write(edp_phy, QSERDES_COM_BG_TIMER, 0x0a);
+	edp_pll_write(edp_phy, QSERDES_COM_CORECLK_DIV_MODE0, 0x14);
+	edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+	edp_pll_write(edp_phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
+	edp_pll_write(edp_phy, QSERDES_COM_CORE_CLK_EN, 0x0f);
+
+	switch (edp_phy->edp_opts->link_rate) {
+	case 162000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x05);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x69);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x6f);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x08);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+		break;
+	case 216000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x04);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x70);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x08);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x3f);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x0b);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x34);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+		break;
+	case 243000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x04);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x7e);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x09);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0xa7);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x0c);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x5c);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x02);
+		break;
+	case 270000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x03);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x69);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x0f);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x0e);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+		break;
+	case 324000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x03);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x7e);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x09);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0xdf);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x10);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x5c);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x02);
+		break;
+	case 432000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x01);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x70);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x08);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x7f);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x16);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x34);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+		break;
+	case 540000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x01);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x8c);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x0a);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x1f);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x1c);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x84);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x01);
+		break;
+	case 594000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x01);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x9a);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x0b);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0xef);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x1e);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xac);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x00);
+		break;
+	case 810000:
+		edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x00);
+		edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x69);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80);
+		edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x2f);
+		edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x2a);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
+		edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+		break;
+	default:
+		DRM_ERROR("%s: Invalid link rate. rate=%lu\n", __func__,
+					edp_phy->edp_opts->link_rate);
+		break;
+	}
+}
+
+static void edp_lanes_init(struct edp_phy *edp_phy)
+{
+	edp_tx0_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 0x03);
+	edp_tx0_write(edp_phy, TXn_CLKBUF_ENABLE, 0x0f);
+	edp_tx0_write(edp_phy, TXn_RESET_TSYNC_EN, 0x03);
+	edp_tx0_write(edp_phy, TXn_TRAN_DRVR_EMP_EN, 0x01);
+	edp_tx0_write(edp_phy, TXn_TX_BAND, 0x4);
+
+	edp_tx1_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 0x03);
+	edp_tx1_write(edp_phy, TXn_CLKBUF_ENABLE, 0x0f);
+	edp_tx1_write(edp_phy, TXn_RESET_TSYNC_EN, 0x03);
+	edp_tx1_write(edp_phy, TXn_TRAN_DRVR_EMP_EN, 0x01);
+	edp_tx1_write(edp_phy, TXn_TX_BAND, 0x4);
+}
+
+static void edp_lanes_configure(struct edp_phy *edp_phy)
+{
+	edp_tx0_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x1f);
+	edp_tx0_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x04);
+	edp_tx0_write(edp_phy, TXn_TX_POL_INV, 0x00);
+
+	edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x1f);
+	edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x04);
+	edp_tx1_write(edp_phy, TXn_TX_POL_INV, 0x00);
+
+	edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x04);
+	edp_tx1_write(edp_phy, TXn_TX_POL_INV, 0x00);
+
+	edp_tx0_write(edp_phy, TXn_TX_DRV_LVL_OFFSET, 0x10);
+	edp_tx1_write(edp_phy, TXn_TX_DRV_LVL_OFFSET, 0x10);
+
+	edp_tx0_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
+	edp_tx0_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
+
+	edp_tx1_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
+	edp_tx1_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
+
+	edp_tx0_write(edp_phy, TXn_TX_EMP_POST1_LVL, 0x00);
+	edp_tx0_write(edp_phy, TXn_TX_DRV_LVL, 0x18);
+	edp_tx1_write(edp_phy, TXn_TX_EMP_POST1_LVL, 0x00);
+	edp_tx1_write(edp_phy, TXn_TX_DRV_LVL, 0x18);
+}
+
+static int edp_pll_vco_configure(struct edp_phy *edp_phy)
+{
+	struct edp_phy_clks *edp_clks = edp_phy->edp_clks;
+	u32 phy_vco_div = 0, status;
+	unsigned long pixel_freq = 0;
+
+	switch (edp_phy->edp_opts->link_rate) {
+	case 162000:
+		phy_vco_div = 2;
+		pixel_freq = 1620000000UL / 2;
+	break;
+	case 216000:
+		phy_vco_div = 1;
+		pixel_freq = 2160000000UL / 2;
+	break;
+	case 243000:
+		phy_vco_div = 1;
+		pixel_freq = 2430000000UL / 2;
+	break;
+	case 270000:
+		phy_vco_div = 1;
+		pixel_freq = 2700000000UL / 2;
+	break;
+	case 324000:
+		phy_vco_div = 2;
+		pixel_freq = 3240000000UL / 4;
+	break;
+	case 432000:
+		phy_vco_div = 2;
+		pixel_freq = 4320000000UL / 4;
+	break;
+	case 540000:
+		phy_vco_div = 2;
+		pixel_freq = 5400000000UL / 4;
+	break;
+	case 594000:
+		phy_vco_div = 0;
+		pixel_freq = 5940000000UL / 6;
+	break;
+	case 810000:
+		phy_vco_div = 0;
+		pixel_freq = 8100000000UL / 6;
+	break;
+	default:
+		DRM_ERROR("%s: Invalid link rate. rate=%lu\n", __func__,
+					edp_phy->edp_opts->link_rate);
+	break;
+	}
+
+	edp_phy_write(edp_phy, EDP_PHY_VCO_DIV, phy_vco_div);
+
+	clk_set_rate(edp_clks->edp_link_hw.clk,
+			edp_phy->edp_opts->link_rate * 1000);
+	clk_set_rate(edp_clks->edp_pixel_hw.clk, pixel_freq);
+
+	edp_phy_write(edp_phy, EDP_PHY_CFG, 0x01);
+	edp_phy_write(edp_phy, EDP_PHY_CFG, 0x05);
+	edp_phy_write(edp_phy, EDP_PHY_CFG, 0x01);
+	edp_phy_write(edp_phy, EDP_PHY_CFG, 0x09);
+
+	edp_pll_write(edp_phy, QSERDES_COM_RESETSM_CNTRL, 0x20);
+
+	if (readl_poll_timeout_atomic((edp_phy->base +
+			MSM_EDP_PLL_OFFSET + QSERDES_COM_C_READY_STATUS),
+			status, ((status & BIT(0)) > 0), 500, 10000)) {
+		DRM_ERROR("%s: PLL not locked. Status=0x%x\n", __func__, status);
+		return -ETIMEDOUT;
+	}
+
+	edp_phy_write(edp_phy, EDP_PHY_CFG, 0x19);
+	edp_lanes_configure(edp_phy);
+	edp_phy_write(edp_phy, EDP_PHY_CFG_1, 0x03);
+
+	if (readl_poll_timeout_atomic((edp_phy->base +
+				MSM_EDP_PHY_OFFSET + EDP_PHY_STATUS),
+				status, ((status & BIT(1)) > 0), 500, 10000)) {
+		DRM_ERROR("%s: PHY not ready. Status=0x%x\n", __func__, status);
+		return -ETIMEDOUT;
+	}
+
+	edp_phy_write(edp_phy, EDP_PHY_CFG, 0x18);
+	udelay(2000);
+	edp_phy_write(edp_phy, EDP_PHY_CFG, 0x19);
+
+	return readl_poll_timeout_atomic((edp_phy->base +
+				MSM_EDP_PLL_OFFSET + QSERDES_COM_C_READY_STATUS),
+				status, ((status & BIT(0)) > 0), 500, 10000);
+
+}
+
+int msm_edp_v510_phy_power_on(struct edp_phy *edp_phy)
+{
+	int ret = 0;
+
+	edp_pll_vco_init(edp_phy);
+
+	edp_phy_write(edp_phy, EDP_PHY_TX0_TX1_LANE_CTL, 0x05);
+	edp_phy_write(edp_phy, EDP_PHY_TX2_TX3_LANE_CTL, 0x05);
+
+	edp_lanes_init(edp_phy);
+
+	ret = edp_pll_vco_configure(edp_phy);
+
+	return ret;
+}
+
+void *msm_edp_v510_phy_init(struct device *dev, void __iomem *regbase,
+		struct edp_phy_opts *opts)
+{
+	struct edp_phy *phy = NULL;
+
+	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return NULL;
+
+	phy->base = regbase;
+	phy->edp_opts = opts;
+	edp_phy_clks_register(dev, phy);
+
+	return phy;
+}
diff --git a/drivers/gpu/drm/msm/edp/v510/edp_v510_reg.h b/drivers/gpu/drm/msm/edp/v510/edp_v510_reg.h
new file mode 100644
index 0000000..10b4428
--- /dev/null
+++ b/drivers/gpu/drm/msm/edp/v510/edp_v510_reg.h
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef EDP_V510_REG
+#define EDP_V510_REG
+
+enum edp_color_depth {
+	EDP_6BIT = 0,
+	EDP_8BIT = 1,
+	EDP_10BIT = 2,
+	EDP_12BIT = 3,
+	EDP_16BIT = 4,
+};
+
+enum edp_component_format {
+	EDP_RGB = 0,
+	EDP_YUV422 = 1,
+	EDP_YUV444 = 2,
+};
+
+/* DP_TX Registers */
+#define REG_EDP_HW_VERSION			(0x00000000)
+
+#define REG_EDP_SW_RESET			(0x00000010)
+#define DP_SW_RESET				(0x00000001)
+
+#define REG_EDP_PHY_CTRL			(0x00000014)
+#define EDP_PHY_CTRL_SW_RESET_PLL		(0x00000001)
+#define EDP_PHY_CTRL_SW_RESET			(0x00000004)
+
+#define REG_EDP_CLK_CTRL			(0x00000018)
+#define REG_EDP_CLK_ACTIVE			(0x0000001C)
+
+#define REG_EDP_INTR_STATUS			(0x00000020)
+#define EDP_INTR_AUX_I2C_DONE			BIT(3)
+#define EDP_INTR_WRONG_ADDR			BIT(6)
+#define EDP_INTR_TIMEOUT			BIT(9)
+#define EDP_INTR_NACK_DEFER			BIT(12)
+#define EDP_INTR_WRONG_DATA_CNT			BIT(15)
+#define EDP_INTR_I2C_NACK			BIT(18)
+#define EDP_INTR_I2C_DEFER			BIT(21)
+#define EDP_INTR_PLL_UNLOCKED			BIT(24)
+#define EDP_INTR_AUX_ERROR			BIT(27)
+
+#define REG_EDP_INTR_STATUS2			(0x00000024)
+#define EDP_INTR_READY_FOR_VIDEO		BIT(0)
+#define EDP_INTR_IDLE_PATTERN_SENT		BIT(3)
+#define EDP_INTR_FRAME_END			BIT(6)
+#define EDP_INTR_CRC_UPDATED			BIT(9)
+#define EDP_INTR_SST_FIFO_UNDERFLOW		BIT(28)
+
+
+#define REG_EDP_DP_HPD_CTRL			(0x00000000)
+#define EDP_DP_HPD_CTRL_HPD_EN			(0x00000001)
+
+#define REG_EDP_DP_HPD_INT_STATUS		(0x00000004)
+
+#define REG_EDP_DP_HPD_INT_ACK			(0x00000008)
+#define EDP_DP_HPD_PLUG_INT_ACK			(0x00000001)
+#define EDP_DP_IRQ_HPD_INT_ACK			(0x00000002)
+#define EDP_DP_HPD_REPLUG_INT_ACK		(0x00000004)
+#define EDP_DP_HPD_UNPLUG_INT_ACK		(0x00000008)
+#define EDP_DP_HPD_STATE_STATUS_BITS_MASK	(0x0000000F)
+#define EDP_DP_HPD_STATE_STATUS_BITS_SHIFT	(0x1C)
+
+#define REG_EDP_DP_HPD_INT_MASK			(0x0000000C)
+#define EDP_DP_HPD_PLUG_INT_MASK		(0x00000001)
+#define EDP_DP_IRQ_HPD_INT_MASK			(0x00000002)
+#define EDP_DP_HPD_REPLUG_INT_MASK		(0x00000004)
+#define EDP_DP_HPD_UNPLUG_INT_MASK		(0x00000008)
+#define EDP_DP_HPD_INT_MASK			(EDP_DP_HPD_PLUG_INT_MASK | \
+						EDP_DP_IRQ_HPD_INT_MASK | \
+						EDP_DP_HPD_REPLUG_INT_MASK | \
+						EDP_DP_HPD_UNPLUG_INT_MASK)
+#define EDP_DP_HPD_STATE_STATUS_CONNECTED	(0x40000000)
+#define EDP_DP_HPD_STATE_STATUS_PENDING		(0x20000000)
+#define EDP_DP_HPD_STATE_STATUS_DISCONNECTED	(0x00000000)
+#define EDP_DP_HPD_STATE_STATUS_MASK		(0xE0000000)
+
+#define REG_EDP_DP_HPD_REFTIMER			(0x00000018)
+#define EDP_DP_HPD_REFTIMER_ENABLE		(1 << 16)
+
+#define REG_EDP_DP_HPD_EVENT_TIME_0		(0x0000001C)
+#define REG_EDP_DP_HPD_EVENT_TIME_1		(0x00000020)
+#define EDP_DP_HPD_EVENT_TIME_0_VAL		(0x3E800FA)
+#define EDP_DP_HPD_EVENT_TIME_1_VAL		(0x1F407D0)
+
+#define REG_EDP_AUX_CTRL			(0x00000030)
+#define EDP_AUX_CTRL_ENABLE			(0x00000001)
+#define EDP_AUX_CTRL_RESET			(0x00000002)
+
+#define REG_EDP_AUX_DATA			(0x00000034)
+#define EDP_AUX_DATA_READ			(0x00000001)
+#define EDP_AUX_DATA_DATA__MASK			(0x0000ff00)
+#define EDP_AUX_DATA_DATA__SHIFT		(8)
+static inline uint32_t EDP_AUX_DATA_DATA(uint32_t val)
+{
+	return ((val) << EDP_AUX_DATA_DATA__SHIFT) & EDP_AUX_DATA_DATA__MASK;
+}
+#define EDP_AUX_DATA_INDEX__MASK		(0x00ff0000)
+#define EDP_AUX_DATA_INDEX__SHIFT		(16)
+static inline uint32_t EDP_AUX_DATA_INDEX(uint32_t val)
+{
+	return ((val) << EDP_AUX_DATA_INDEX__SHIFT) & EDP_AUX_DATA_INDEX__MASK;
+}
+#define EDP_AUX_DATA_INDEX_WRITE		(0x80000000)
+
+#define REG_EDP_AUX_TRANS_CTRL			(0x00000038)
+#define EDP_AUX_TRANS_CTRL_I2C			(0x00000100)
+#define EDP_AUX_TRANS_CTRL_GO			(0x00000200)
+#define EDP_AUX_TRANS_CTRL_NO_SEND_ADDR		(0x00000400)
+#define EDP_AUX_TRANS_CTRL_NO_SEND_STOP		(0x00000800)
+
+#define REG_EDP_TIMEOUT_COUNT			(0x0000003C)
+#define REG_EDP_AUX_LIMITS			(0x00000040)
+#define REG_EDP_AUX_STATUS			(0x00000044)
+
+#define EDP_INTERRUPT_TRANS_NUM			(0x000000A0)
+
+#define REG_EDP_MAINLINK_CTRL			(0x00000000)
+#define EDP_MAINLINK_CTRL_ENABLE		(0x00000001)
+#define EDP_MAINLINK_CTRL_RESET			(0x00000002)
+#define EDP_MAINLINK_CTRL_SW_BYPASS_SCRAMBLER	(0x00000010)
+#define EDP_MAINLINK_FB_BOUNDARY_SEL		(0x02000000)
+
+#define REG_EDP_STATE_CTRL			(0x00000004)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN1	(0x00000001)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN2	(0x00000002)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN3	(0x00000004)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN4	(0x00000008)
+#define EDP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE	(0x00000010)
+#define EDP_STATE_CTRL_LINK_PRBS7		(0x00000020)
+#define EDP_STATE_CTRL_LINK_TEST_CUSTOM_PATTERN	(0x00000040)
+#define EDP_STATE_CTRL_SEND_VIDEO		(0x00000080)
+#define EDP_STATE_CTRL_PUSH_IDLE		(0x00000100)
+
+#define REG_EDP_CONFIGURATION_CTRL			(0x00000008)
+#define EDP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK		(0x00000001)
+#define EDP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN	(0x00000002)
+#define EDP_CONFIGURATION_CTRL_P_INTERLACED		(0x00000004)
+#define EDP_CONFIGURATION_CTRL_INTERLACED_BTF		(0x00000008)
+#define EDP_CONFIGURATION_CTRL_NUM_OF_LANES		(0x00000010)
+#define EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING		(0x00000040)
+#define EDP_CONFIGURATION_CTRL_SEND_VSC			(0x00000080)
+#define EDP_CONFIGURATION_CTRL_BPC			(0x00000100)
+#define EDP_CONFIGURATION_CTRL_ASSR			(0x00000400)
+#define EDP_CONFIGURATION_CTRL_RGB_YUV			(0x00000800)
+#define EDP_CONFIGURATION_CTRL_LSCLK_DIV		(0x00002000)
+#define EDP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT	(0x04)
+#define EDP_CONFIGURATION_CTRL_BPC_SHIFT		(0x08)
+#define EDP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT		(0x0D)
+
+#define REG_EDP_SOFTWARE_MVID			(0x00000010)
+#define REG_EDP_SOFTWARE_NVID			(0x00000018)
+
+#define REG_EDP_TOTAL_HOR_VER			(0x0000001C)
+#define EDP_TOTAL_HOR_VER_HORIZ__MASK		(0x0000FFFF)
+#define EDP_TOTAL_HOR_VER_HORIZ__SHIFT		(0)
+static inline uint32_t EDP_TOTAL_HOR_VER_HORIZ(uint32_t val)
+{
+	return ((val) << EDP_TOTAL_HOR_VER_HORIZ__SHIFT) & EDP_TOTAL_HOR_VER_HORIZ__MASK;
+}
+#define EDP_TOTAL_HOR_VER_VERT__MASK		(0xffff0000)
+#define EDP_TOTAL_HOR_VER_VERT__SHIFT		(16)
+static inline uint32_t EDP_TOTAL_HOR_VER_VERT(uint32_t val)
+{
+	return ((val) << EDP_TOTAL_HOR_VER_VERT__SHIFT) & EDP_TOTAL_HOR_VER_VERT__MASK;
+}
+
+#define REG_EDP_START_HOR_VER_FROM_SYNC			(0x00000020)
+#define EDP_START_HOR_VER_FROM_SYNC_HORIZ__MASK		(0x0000ffff)
+#define EDP_START_HOR_VER_FROM_SYNC_HORIZ__SHIFT	(0)
+static inline uint32_t EDP_START_HOR_VER_FROM_SYNC_HORIZ(uint32_t val)
+{
+	return ((val) << EDP_START_HOR_VER_FROM_SYNC_HORIZ__SHIFT) &
+		EDP_START_HOR_VER_FROM_SYNC_HORIZ__MASK;
+}
+#define EDP_START_HOR_VER_FROM_SYNC_VERT__MASK		(0xffff0000)
+#define EDP_START_HOR_VER_FROM_SYNC_VERT__SHIFT		(16)
+static inline uint32_t EDP_START_HOR_VER_FROM_SYNC_VERT(uint32_t val)
+{
+	return ((val) << EDP_START_HOR_VER_FROM_SYNC_VERT__SHIFT) &
+		EDP_START_HOR_VER_FROM_SYNC_VERT__MASK;
+}
+
+#define REG_EDP_HSYNC_VSYNC_WIDTH_POLARITY		(0x00000024)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__MASK	(0x00007fff)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__SHIFT	(0)
+static inline uint32_t EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ(uint32_t val)
+{
+	return ((val) << EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__SHIFT) &
+		EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__MASK;
+}
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_NHSYNC		(0x00008000)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__MASK	(0x7fff0000)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__SHIFT	(16)
+static inline uint32_t EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT(uint32_t val)
+{
+	return ((val) << EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__SHIFT) &
+		EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__MASK;
+}
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_NVSYNC		(0x80000000)
+
+#define REG_EDP_ACTIVE_HOR_VER				(0x00000028)
+#define EDP_ACTIVE_HOR_VER_HORIZ__MASK			(0x0000ffff)
+#define EDP_ACTIVE_HOR_VER_HORIZ__SHIFT			(0)
+static inline uint32_t EDP_ACTIVE_HOR_VER_HORIZ(uint32_t val)
+{
+	return ((val) << EDP_ACTIVE_HOR_VER_HORIZ__SHIFT) & EDP_ACTIVE_HOR_VER_HORIZ__MASK;
+}
+#define EDP_ACTIVE_HOR_VER_VERT__MASK			(0xffff0000)
+#define EDP_ACTIVE_HOR_VER_VERT__SHIFT			(16)
+static inline uint32_t EDP_ACTIVE_HOR_VER_VERT(uint32_t val)
+{
+	return ((val) << EDP_ACTIVE_HOR_VER_VERT__SHIFT) & EDP_ACTIVE_HOR_VER_VERT__MASK;
+}
+
+
+#define REG_EDP_MISC1_MISC0				(0x0000002C)
+#define EDP_MISC0_SYNCHRONOUS_CLK			(0x00000001)
+#define EDP_MISC0_COLORIMETRY_CFG_SHIFT			(0x00000001)
+#define EDP_MISC0_TEST_BITS_DEPTH_SHIFT			(0x00000005)
+
+#define REG_EDP_VALID_BOUNDARY				(0x00000030)
+#define REG_EDP_VALID_BOUNDARY_2			(0x00000034)
+
+#define REG_EDP_LOGICAL2PHYSICAL_LANE_MAPPING		(0x00000038)
+#define LANE0_MAPPING_SHIFT				(0x00000000)
+#define LANE1_MAPPING_SHIFT				(0x00000002)
+#define LANE2_MAPPING_SHIFT				(0x00000004)
+#define LANE3_MAPPING_SHIFT				(0x00000006)
+
+#define REG_EDP_MAINLINK_READY				(0x00000040)
+#define EDP_MAINLINK_READY_FOR_VIDEO			(0x00000001)
+#define EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY	(0x00000008)
+
+#define REG_EDP_MAINLINK_LEVELS				(0x00000044)
+#define EDP_MAINLINK_SAFE_TO_EXIT_LEVEL_2		(0x00000002)
+
+#define REG_EDP_TU					(0x0000004C)
+
+/* PCLK registers */
+#define REG_EDP_DSC_DTO					(0x0000007C)
+
+/* PHY registers */
+#define EDP_PHY_CFG					(0x00000010)
+#define EDP_PHY_CFG_1					(0x00000014)
+#define EDP_PHY_PD_CTL					(0x0000001C)
+#define EDP_PHY_MODE					(0x00000020)
+#define EDP_PHY_AUX_CFG0				(0x00000024)
+#define EDP_PHY_AUX_CFG1				(0x00000028)
+#define EDP_PHY_AUX_CFG2				(0x0000002C)
+#define EDP_PHY_AUX_CFG3				(0x00000030)
+#define EDP_PHY_AUX_CFG4				(0x00000034)
+#define EDP_PHY_AUX_CFG5				(0x00000038)
+#define EDP_PHY_AUX_CFG6				(0x0000003C)
+#define EDP_PHY_AUX_CFG7				(0x00000040)
+#define EDP_PHY_AUX_CFG8				(0x00000044)
+#define EDP_PHY_AUX_CFG9				(0x00000048)
+#define EDP_PHY_AUX_INTERRUPT_MASK			(0x00000058)
+#define EDP_PHY_VCO_DIV					(0x00000074)
+#define EDP_PHY_TX0_TX1_LANE_CTL			(0x0000007C)
+#define EDP_PHY_TX2_TX3_LANE_CTL			(0x000000A0)
+#define EDP_PHY_SPARE0					(0x000000CC)
+#define EDP_PHY_STATUS					(0x000000E0)
+
+
+/* Tx registers */
+#define TXn_CLKBUF_ENABLE				(0x00000000)
+#define TXn_TX_EMP_POST1_LVL				(0x00000004)
+#define TXn_TX_DRV_LVL					(0x00000014)
+#define TXn_TX_DRV_LVL_OFFSET				(0x00000018)
+#define TXn_RESET_TSYNC_EN				(0x0000001C)
+#define TXn_TX_BAND					(0x00000028)
+#define TXn_RES_CODE_LANE_OFFSET_TX0			(0x00000044)
+#define TXn_RES_CODE_LANE_OFFSET_TX1			(0x00000048)
+#define TXn_TRANSCEIVER_BIAS_EN				(0x00000054)
+#define TXn_HIGHZ_DRVR_EN				(0x00000058)
+#define TXn_TX_POL_INV					(0x0000005C)
+#define TXn_LANE_MODE_1					(0x00000064)
+#define TXn_TRAN_DRVR_EMP_EN				(0x00000078)
+#define TXn_LDO_CONFIG					(0x00000084)
+
+
+/* PLL registers */
+#define QSERDES_COM_BG_TIMER				(0x0000000C)
+#define QSERDES_COM_SSC_EN_CENTER			(0x00000010)
+#define QSERDES_COM_SSC_ADJ_PER1			(0x00000014)
+#define QSERDES_COM_SSC_ADJ_PER2			(0x00000018)
+#define QSERDES_COM_SSC_PER1				(0x0000001C)
+#define QSERDES_COM_SSC_PER2				(0x00000020)
+#define QSERDES_COM_SSC_STEP_SIZE1_MODE0		(0x00000024)
+#define QSERDES_COM_SSC_STEP_SIZE2_MODE0		(0x00000028)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN			(0x00000044)
+#define QSERDES_COM_CLK_ENABLE1				(0x00000048)
+#define QSERDES_COM_SYS_CLK_CTRL			(0x0000004C)
+#define QSERDES_COM_SYSCLK_BUF_ENABLE			(0x00000050)
+#define QSERDES_COM_PLL_IVCO				(0x00000058)
+#define QSERDES_COM_CP_CTRL_MODE0			(0x00000074)
+#define QSERDES_COM_PLL_RCTRL_MODE0			(0x0000007C)
+#define QSERDES_COM_PLL_CCTRL_MODE0			(0x00000084)
+#define QSERDES_COM_SYSCLK_EN_SEL			(0x00000094)
+#define QSERDES_COM_RESETSM_CNTRL			(0x0000009C)
+#define QSERDES_COM_LOCK_CMP_EN				(0x000000A4)
+#define QSERDES_COM_LOCK_CMP1_MODE0			(0x000000AC)
+#define QSERDES_COM_LOCK_CMP2_MODE0			(0x000000B0)
+#define QSERDES_COM_DEC_START_MODE0			(0x000000BC)
+#define QSERDES_COM_DIV_FRAC_START1_MODE0		(0x000000CC)
+#define QSERDES_COM_DIV_FRAC_START2_MODE0		(0x000000D0)
+#define QSERDES_COM_DIV_FRAC_START3_MODE0		(0x000000D4)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0		(0x000000EC)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0		(0x000000F0)
+#define QSERDES_COM_VCO_TUNE_CTRL			(0x00000108)
+#define QSERDES_COM_VCO_TUNE_MAP			(0x0000010C)
+#define QSERDES_COM_VCO_TUNE1_MODE0			(0x00000110)
+#define QSERDES_COM_VCO_TUNE2_MODE0			(0x00000114)
+#define QSERDES_COM_CMN_STATUS				(0x00000140)
+#define QSERDES_COM_CLK_SEL				(0x00000154)
+#define QSERDES_COM_HSCLK_SEL				(0x00000158)
+#define QSERDES_COM_CORECLK_DIV_MODE0			(0x00000168)
+#define QSERDES_COM_CORE_CLK_EN				(0x00000174)
+#define QSERDES_COM_C_READY_STATUS			(0x00000178)
+#define QSERDES_COM_CMN_CONFIG				(0x0000017C)
+#define QSERDES_COM_SVS_MODE_CLK_SEL			(0x00000184)
+
+
+#define DP_PHY_PLL_POLL_SLEEP_US			(500)
+#define DP_PHY_PLL_POLL_TIMEOUT_US			(10000)
+
+
+#define EDP_VCO_RATE_8100MHZDIV1000			(8100000UL)
+#define EDP_VCO_RATE_8640MHZDIV1000			(8640000UL)
+#define EDP_VCO_RATE_9720MHZDIV1000			(9720000UL)
+#define EDP_VCO_RATE_10800MHZDIV1000			(10800000UL)
+#define EDP_VCO_RATE_11880MHZDIV1000			(11880000UL)
+
+#endif /* EDP_V510_REG */
-- 
The Qualcomm Innovatin Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project



More information about the Freedreno mailing list