[DPU PATCH 2/3] drm/msm/dp: add displayPort driver support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add the needed displayPort files to enable DP driver
on msm target.

"dp_display" module is the main module that calls into
other sub-modules. "dp_drm" file represents the interface
between DRM framework and DP driver.

Signed-off-by: Chandan Uddaraju <chandanu@xxxxxxxxxxxxxx>
---
 drivers/gpu/drm/msm/Kconfig                 |    9 +
 drivers/gpu/drm/msm/Makefile                |   15 +-
 drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c |  206 ++++
 drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.h |   44 +
 drivers/gpu/drm/msm/dp/dp_aux.c             |  570 ++++++++++
 drivers/gpu/drm/msm/dp/dp_aux.h             |   44 +
 drivers/gpu/drm/msm/dp/dp_catalog.c         | 1188 ++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_catalog.h         |  144 +++
 drivers/gpu/drm/msm/dp/dp_ctrl.c            | 1475 +++++++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_ctrl.h            |   50 +
 drivers/gpu/drm/msm/dp/dp_debug.c           |  507 +++++++++
 drivers/gpu/drm/msm/dp/dp_debug.h           |   81 ++
 drivers/gpu/drm/msm/dp/dp_display.c         |  977 +++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_display.h         |   55 +
 drivers/gpu/drm/msm/dp/dp_drm.c             |  542 ++++++++++
 drivers/gpu/drm/msm/dp/dp_drm.h             |   52 +
 drivers/gpu/drm/msm/dp/dp_extcon.c          |  400 +++++++
 drivers/gpu/drm/msm/dp/dp_extcon.h          |  111 ++
 drivers/gpu/drm/msm/dp/dp_link.c            | 1549 +++++++++++++++++++++++++++
 drivers/gpu/drm/msm/dp/dp_link.h            |  184 ++++
 drivers/gpu/drm/msm/dp/dp_panel.c           |  624 +++++++++++
 drivers/gpu/drm/msm/dp/dp_panel.h           |  121 +++
 drivers/gpu/drm/msm/dp/dp_parser.c          |  679 ++++++++++++
 drivers/gpu/drm/msm/dp/dp_parser.h          |  205 ++++
 drivers/gpu/drm/msm/dp/dp_power.c           |  599 +++++++++++
 drivers/gpu/drm/msm/dp/dp_power.h           |   57 +
 drivers/gpu/drm/msm/dp/dp_reg.h             |  357 ++++++
 drivers/gpu/drm/msm/msm_drv.c               |    2 +
 drivers/gpu/drm/msm/msm_drv.h               |   22 +
 include/drm/drm_dp_helper.h                 |   19 +
 30 files changed, 10887 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_aux.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_catalog.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_ctrl.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_debug.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_debug.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_display.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_drm.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_extcon.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_extcon.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_link.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_panel.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_parser.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.c
 create mode 100644 drivers/gpu/drm/msm/dp/dp_power.h
 create mode 100644 drivers/gpu/drm/msm/dp/dp_reg.h

diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 843a9d4..c363f24 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -49,6 +49,15 @@ config DRM_MSM_HDMI_HDCP
 	help
 	  Choose this option to enable HDCP state machine
 
+config DRM_MSM_DP
+	bool "Enable DP support in MSM DRM driver"
+	depends on DRM_MSM
+	default n
+	help
+	  Compile in support for DP driver in msm drm
+	  driver. DP external display support is enabled
+	  through this config option. It can be primary or
+	  secondary display on device.
 config DRM_MSM_DSI
 	bool "Enable DSI support in MSM DRM driver"
 	depends on DRM_MSM
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 19ab521..765a8d8 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -2,6 +2,7 @@
 ccflags-y := -Idrivers/gpu/drm/msm
 ccflags-y += -Idrivers/gpu/drm/msm/disp/dpu1
 ccflags-$(CONFIG_DRM_MSM_DSI) += -Idrivers/gpu/drm/msm/dsi
+ccflags-$(CONFIG_DRM_MSM_DP) += -Idrivers/gpu/drm/msm/dp
 
 msm-y := \
 	adreno/adreno_device.o \
@@ -93,7 +94,19 @@ msm-y := \
 	msm_submitqueue.o
 
 msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o \
-			  disp/dpu1/dpu_dbg.o
+			  disp/dpu1/dpu_dbg.o \
+			  dp/dp_debug.o
+
+msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_extcon.o \
+	dp/dp_parser.o \
+	dp/dp_power.o \
+	dp/dp_catalog.o \
+	dp/dp_aux.o \
+	dp/dp_panel.o \
+	dp/dp_link.o \
+	dp/dp_ctrl.o \
+	dp/dp_display.o \
+	dp/dp_drm.o
 
 msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
 msm-$(CONFIG_COMMON_CLK) += disp/mdp4/mdp4_lvds_pll.o
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c
index 790d39f..0f53a15 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c
@@ -14,10 +14,216 @@
 #include <linux/clk.h>
 #include <linux/clk/clk-conf.h>
 #include <linux/err.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
 #include <linux/delay.h>
 
 #include "dpu_io_util.h"
 
+static struct resource *msm_dss_get_res_byname(struct platform_device *pdev,
+					       unsigned int type,
+					       const char *name)
+{
+	struct resource *res = NULL;
+
+	res = platform_get_resource_byname(pdev, type, name);
+	if (!res)
+		DEV_ERR("%s: '%s' resource not found\n", __func__, name);
+
+	return res;
+} /* msm_dss_get_res_byname */
+
+int msm_dss_ioremap_byname(struct platform_device *pdev,
+			   struct dss_io_data *io_data, const char *name)
+{
+	struct resource *res = NULL;
+
+	if (!pdev || !io_data) {
+		DEV_ERR("%pS->%s: invalid input\n",
+			__builtin_return_address(0), __func__);
+		return -EINVAL;
+	}
+
+	res = msm_dss_get_res_byname(pdev, IORESOURCE_MEM, name);
+	if (!res) {
+		DEV_ERR("%pS->%s: '%s' msm_dss_get_res_byname failed\n",
+			__builtin_return_address(0), __func__, name);
+		return -ENODEV;
+	}
+
+	io_data->len = (u32)resource_size(res);
+	io_data->base = ioremap(res->start, io_data->len);
+	if (!io_data->base) {
+		DEV_ERR("%pS->%s: '%s' ioremap failed\n",
+			__builtin_return_address(0), __func__, name);
+		return -EIO;
+	}
+
+	return 0;
+} /* msm_dss_ioremap_byname */
+
+void msm_dss_iounmap(struct dss_io_data *io_data)
+{
+	if (!io_data) {
+		DEV_ERR("%pS->%s: invalid input\n",
+			__builtin_return_address(0), __func__);
+		return;
+	}
+
+	if (io_data->base) {
+		iounmap(io_data->base);
+		io_data->base = NULL;
+	}
+	io_data->len = 0;
+} /* msm_dss_iounmap */
+
+int msm_dss_config_vreg(struct device *dev, struct dss_vreg *in_vreg,
+			int num_vreg, int config)
+{
+	int i = 0, rc = 0;
+	struct dss_vreg *curr_vreg = NULL;
+	enum dss_vreg_type type;
+
+	if (!in_vreg || !num_vreg)
+		return rc;
+
+	if (config) {
+		for (i = 0; i < num_vreg; i++) {
+			curr_vreg = &in_vreg[i];
+			curr_vreg->vreg = regulator_get(dev,
+				curr_vreg->vreg_name);
+			rc = PTR_RET(curr_vreg->vreg);
+			if (rc) {
+				DEV_ERR("%pS->%s: %s get failed. rc=%d\n",
+					 __builtin_return_address(0), __func__,
+					 curr_vreg->vreg_name, rc);
+				curr_vreg->vreg = NULL;
+				goto vreg_get_fail;
+			}
+			type = (regulator_count_voltages(curr_vreg->vreg) > 0)
+					? DSS_REG_LDO : DSS_REG_VS;
+			if (type == DSS_REG_LDO) {
+				rc = regulator_set_voltage(
+					curr_vreg->vreg,
+					curr_vreg->min_voltage,
+					curr_vreg->max_voltage);
+				if (rc < 0) {
+					DEV_ERR("%pS->%s: %s set vltg fail\n",
+						__builtin_return_address(0),
+						__func__,
+						curr_vreg->vreg_name);
+					goto vreg_set_voltage_fail;
+				}
+			}
+		}
+	} else {
+		for (i = num_vreg-1; i >= 0; i--) {
+			curr_vreg = &in_vreg[i];
+			if (curr_vreg->vreg) {
+				type = (regulator_count_voltages(
+					curr_vreg->vreg) > 0)
+					? DSS_REG_LDO : DSS_REG_VS;
+				if (type == DSS_REG_LDO) {
+					regulator_set_voltage(curr_vreg->vreg,
+						0, curr_vreg->max_voltage);
+				}
+				regulator_put(curr_vreg->vreg);
+				curr_vreg->vreg = NULL;
+			}
+		}
+	}
+	return 0;
+
+vreg_unconfig:
+if (type == DSS_REG_LDO)
+	regulator_set_load(curr_vreg->vreg, 0);
+
+vreg_set_voltage_fail:
+	regulator_put(curr_vreg->vreg);
+	curr_vreg->vreg = NULL;
+
+vreg_get_fail:
+	for (i--; i >= 0; i--) {
+		curr_vreg = &in_vreg[i];
+		type = (regulator_count_voltages(curr_vreg->vreg) > 0)
+			? DSS_REG_LDO : DSS_REG_VS;
+		goto vreg_unconfig;
+	}
+	return rc;
+} /* msm_dss_config_vreg */
+
+int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg, int enable)
+{
+	int i = 0, rc = 0;
+	bool need_sleep;
+
+	if (enable) {
+		for (i = 0; i < num_vreg; i++) {
+			rc = PTR_RET(in_vreg[i].vreg);
+			if (rc) {
+				DEV_ERR("%pS->%s: %s regulator error. rc=%d\n",
+					__builtin_return_address(0), __func__,
+					in_vreg[i].vreg_name, rc);
+				goto vreg_set_opt_mode_fail;
+			}
+			need_sleep = !regulator_is_enabled(in_vreg[i].vreg);
+			if (in_vreg[i].pre_on_sleep && need_sleep)
+				usleep_range(in_vreg[i].pre_on_sleep * 1000,
+					in_vreg[i].pre_on_sleep * 1000);
+			rc = regulator_set_load(in_vreg[i].vreg,
+				in_vreg[i].enable_load);
+			if (rc < 0) {
+				DEV_ERR("%pS->%s: %s set opt m fail\n",
+					__builtin_return_address(0), __func__,
+					in_vreg[i].vreg_name);
+				goto vreg_set_opt_mode_fail;
+			}
+			rc = regulator_enable(in_vreg[i].vreg);
+			if (in_vreg[i].post_on_sleep && need_sleep)
+				usleep_range(in_vreg[i].post_on_sleep * 1000,
+					in_vreg[i].post_on_sleep * 1000);
+			if (rc < 0) {
+				DEV_ERR("%pS->%s: %s enable failed\n",
+					__builtin_return_address(0), __func__,
+					in_vreg[i].vreg_name);
+				goto disable_vreg;
+			}
+		}
+	} else {
+		for (i = num_vreg-1; i >= 0; i--) {
+			if (in_vreg[i].pre_off_sleep)
+				usleep_range(in_vreg[i].pre_off_sleep * 1000,
+					in_vreg[i].pre_off_sleep * 1000);
+			regulator_set_load(in_vreg[i].vreg,
+				in_vreg[i].disable_load);
+			regulator_disable(in_vreg[i].vreg);
+			if (in_vreg[i].post_off_sleep)
+				usleep_range(in_vreg[i].post_off_sleep * 1000,
+					in_vreg[i].post_off_sleep * 1000);
+		}
+	}
+	return rc;
+
+disable_vreg:
+	regulator_set_load(in_vreg[i].vreg, in_vreg[i].disable_load);
+
+vreg_set_opt_mode_fail:
+	for (i--; i >= 0; i--) {
+		if (in_vreg[i].pre_off_sleep)
+			usleep_range(in_vreg[i].pre_off_sleep * 1000,
+				in_vreg[i].pre_off_sleep * 1000);
+		regulator_set_load(in_vreg[i].vreg,
+			in_vreg[i].disable_load);
+		regulator_disable(in_vreg[i].vreg);
+		if (in_vreg[i].post_off_sleep)
+			usleep_range(in_vreg[i].post_off_sleep * 1000,
+				in_vreg[i].post_off_sleep * 1000);
+	}
+
+	return rc;
+} /* msm_dss_enable_vreg */
+
+
 void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk)
 {
 	int i;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.h
index bc07381..524b394 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.h
@@ -15,6 +15,7 @@
 
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/types.h>
 
 #define DEV_DBG(fmt, args...)   pr_debug(fmt, ##args)
@@ -22,6 +23,39 @@
 #define DEV_WARN(fmt, args...)  pr_warn(fmt, ##args)
 #define DEV_ERR(fmt, args...)   pr_err(fmt, ##args)
 
+struct dss_io_data {
+	u32 len;
+	void __iomem *base;
+};
+
+/*
+void dss_reg_w(struct dss_io_data *io, u32 offset, u32 value, u32 debug);
+u32 dss_reg_r(struct dss_io_data *io, u32 offset, u32 debug);
+void dss_reg_dump(void __iomem *base, u32 len, const char *prefix, u32 debug);
+
+#define DSS_REG_W_ND(io, offset, val)  dss_reg_w(io, offset, val, false)
+#define DSS_REG_W(io, offset, val)     dss_reg_w(io, offset, val, true)
+#define DSS_REG_R_ND(io, offset)       dss_reg_r(io, offset, false)
+#define DSS_REG_R(io, offset)          dss_reg_r(io, offset, true)
+*/
+enum dss_vreg_type {
+	DSS_REG_LDO,
+	DSS_REG_VS,
+};
+
+struct dss_vreg {
+	struct regulator *vreg; /* vreg handle */
+	char vreg_name[32];
+	int min_voltage;
+	int max_voltage;
+	int enable_load;
+	int disable_load;
+	int pre_on_sleep;
+	int post_on_sleep;
+	int pre_off_sleep;
+	int post_off_sleep;
+};
+
 struct dss_gpio {
 	unsigned int gpio;
 	unsigned int value;
@@ -42,12 +76,22 @@ struct dss_clk {
 };
 
 struct dss_module_power {
+	unsigned int num_vreg;
+	struct dss_vreg *vreg_config;
 	unsigned int num_gpio;
 	struct dss_gpio *gpio_config;
 	unsigned int num_clk;
 	struct dss_clk *clk_config;
 };
 
+int msm_dss_ioremap_byname(struct platform_device *pdev,
+			   struct dss_io_data *io_data, const char *name);
+void msm_dss_iounmap(struct dss_io_data *io_data);
+
+int msm_dss_config_vreg(struct device *dev, struct dss_vreg *in_vreg,
+			int num_vreg, int config);
+int msm_dss_enable_vreg(struct dss_vreg *in_vreg, int num_vreg,	int enable);
+
 int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk);
 void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk);
 int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk);
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c
new file mode 100644
index 0000000..00b82c2
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_aux.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/delay.h>
+
+#include "dp_aux.h"
+
+#define DP_AUX_ENUM_STR(x)		#x
+
+enum {
+	DP_AUX_DATA_INDEX_WRITE = BIT(31),
+};
+
+struct dp_aux_private {
+	struct device *dev;
+	struct dp_aux dp_aux;
+	struct dp_catalog_aux *catalog;
+	struct dp_aux_cfg *cfg;
+
+	struct mutex mutex;
+	struct completion comp;
+
+	u32 aux_error_num;
+	u32 retry_cnt;
+	bool cmd_busy;
+	bool native;
+	bool read;
+	bool no_send_addr;
+	bool no_send_stop;
+	u32 offset;
+	u32 segment;
+
+	struct drm_dp_aux drm_aux;
+};
+
+static char *dp_aux_get_error(u32 aux_error)
+{
+	switch (aux_error) {
+	case DP_AUX_ERR_NONE:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
+	case DP_AUX_ERR_ADDR:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
+	case DP_AUX_ERR_TOUT:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
+	case DP_AUX_ERR_NACK:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
+	case DP_AUX_ERR_DEFER:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
+	case DP_AUX_ERR_NACK_DEFER:
+		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
+	default:
+		return "unknown";
+	}
+}
+
+static u32 dp_aux_write(struct dp_aux_private *aux,
+			struct drm_dp_aux_msg *msg)
+{
+	u32 data[4], reg, len;
+	u8 *msgdata = msg->buffer;
+	int const aux_cmd_fifo_len = 128;
+	int i = 0;
+
+	if (aux->read)
+		len = 4;
+	else
+		len = msg->size + 4;
+
+	/*
+	 * cmd fifo only has depth of 144 bytes
+	 * limit buf length to 128 bytes here
+	 */
+	if (len > aux_cmd_fifo_len) {
+		pr_err("buf len error\n");
+		return 0;
+	}
+
+	/* Pack cmd and write to HW */
+	data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
+	if (aux->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 = ((reg) << 8) & 0x0000ff00; /* index = 0, write */
+		if (i == 0)
+			reg |= DP_AUX_DATA_INDEX_WRITE;
+		aux->catalog->data = reg;
+		aux->catalog->write_data(aux->catalog);
+	}
+
+	aux->catalog->clear_trans(aux->catalog, false);
+
+	reg = 0; /* Transaction number == 1 */
+	if (!aux->native) { /* i2c */
+		reg |= BIT(8);
+
+		if (aux->no_send_addr)
+			reg |= BIT(10);
+
+		if (aux->no_send_stop)
+			reg |= BIT(11);
+	}
+
+	reg |= BIT(9);
+	aux->catalog->data = reg;
+	aux->catalog->write_trans(aux->catalog);
+
+	return len;
+}
+
+static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
+			      struct drm_dp_aux_msg *msg)
+{
+	u32 ret = 0, len = 0, timeout;
+	int const aux_timeout_ms = HZ/4;
+
+	reinit_completion(&aux->comp);
+
+	len = dp_aux_write(aux, msg);
+	if (len == 0) {
+		pr_err("DP AUX write failed\n");
+		return -EINVAL;
+	}
+
+	timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
+	if (!timeout) {
+		pr_err("aux %s timeout\n", (aux->read ? "read" : "write"));
+		return -ETIMEDOUT;
+	}
+
+	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
+		ret = len;
+	} else {
+		pr_err_ratelimited("aux err: %s\n",
+			dp_aux_get_error(aux->aux_error_num));
+
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *msg)
+{
+	u32 data;
+	u8 *dp;
+	u32 i, actual_i;
+	u32 len = msg->size;
+
+	aux->catalog->clear_trans(aux->catalog, true);
+
+	data = 0;
+	data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
+	data |= BIT(0);  /* read */
+
+	aux->catalog->data = data;
+	aux->catalog->write_data(aux->catalog);
+
+	dp = msg->buffer;
+
+	/* discard first byte */
+	data = aux->catalog->read_data(aux->catalog);
+
+	for (i = 0; i < len; i++) {
+		data = aux->catalog->read_data(aux->catalog);
+		*dp++ = (u8)((data >> 8) & 0xff);
+
+		actual_i = (data >> 16) & 0xFF;
+		if (i != actual_i)
+			pr_warn("Index mismatch: expected %d, found %d\n",
+				i, actual_i);
+	}
+}
+
+static void dp_aux_native_handler(struct dp_aux_private *aux)
+{
+	u32 isr = aux->catalog->isr;
+
+	if (isr & DP_INTR_AUX_I2C_DONE)
+		aux->aux_error_num = DP_AUX_ERR_NONE;
+	else if (isr & DP_INTR_WRONG_ADDR)
+		aux->aux_error_num = DP_AUX_ERR_ADDR;
+	else if (isr & DP_INTR_TIMEOUT)
+		aux->aux_error_num = DP_AUX_ERR_TOUT;
+	if (isr & DP_INTR_NACK_DEFER)
+		aux->aux_error_num = DP_AUX_ERR_NACK;
+
+	complete(&aux->comp);
+}
+
+static void dp_aux_i2c_handler(struct dp_aux_private *aux)
+{
+	u32 isr = aux->catalog->isr;
+
+	if (isr & DP_INTR_AUX_I2C_DONE) {
+		if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
+			aux->aux_error_num = DP_AUX_ERR_NACK;
+		else
+			aux->aux_error_num = DP_AUX_ERR_NONE;
+	} else {
+		if (isr & DP_INTR_WRONG_ADDR)
+			aux->aux_error_num = DP_AUX_ERR_ADDR;
+		else if (isr & DP_INTR_TIMEOUT)
+			aux->aux_error_num = DP_AUX_ERR_TOUT;
+		if (isr & DP_INTR_NACK_DEFER)
+			aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
+		if (isr & DP_INTR_I2C_NACK)
+			aux->aux_error_num = DP_AUX_ERR_NACK;
+		if (isr & DP_INTR_I2C_DEFER)
+			aux->aux_error_num = DP_AUX_ERR_DEFER;
+	}
+
+	complete(&aux->comp);
+}
+
+static void dp_aux_isr(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->catalog->get_irq(aux->catalog, aux->cmd_busy);
+
+	if (!aux->cmd_busy)
+		return;
+
+	if (aux->native)
+		dp_aux_native_handler(aux);
+	else
+		dp_aux_i2c_handler(aux);
+}
+
+static void dp_aux_reconfig(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->catalog->update_aux_cfg(aux->catalog,
+			aux->cfg, PHY_AUX_CFG1);
+	aux->catalog->reset(aux->catalog);
+}
+
+static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
+					     struct drm_dp_aux_msg *input_msg)
+{
+	u32 const edid_address = 0x50;
+	u32 const segment_address = 0x30;
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	u8 *data = NULL;
+
+	if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
+		(input_msg->address != segment_address)))
+		return;
+
+
+	data = input_msg->buffer;
+	if (input_msg->address == segment_address)
+		aux->segment = *data;
+	else
+		aux->offset = *data;
+}
+
+/**
+ * dp_aux_transfer_helper() - helper function for EDID read transactions
+ *
+ * @aux: DP AUX private structure
+ * @input_msg: input message from DRM upstream APIs
+ *
+ * return: void
+ *
+ * This helper function is used to fix EDID reads for non-compliant
+ * sinks that do not handle the i2c middle-of-transaction flag correctly.
+ */
+static void dp_aux_transfer_helper(struct dp_aux_private *aux,
+				   struct drm_dp_aux_msg *input_msg)
+{
+	struct drm_dp_aux_msg helper_msg;
+	u32 const message_size = 0x10;
+	u32 const segment_address = 0x30;
+	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+	if (!i2c_mot || !i2c_read || (input_msg->size == 0))
+		return;
+
+	aux->read = false;
+	aux->cmd_busy = true;
+	aux->no_send_addr = true;
+	aux->no_send_stop = true;
+
+	/*
+	 * Send the segment address for every i2c read in which the
+	 * middle-of-tranaction flag is set. This is required to support EDID
+	 * reads of more than 2 blocks as the segment address is reset to 0
+	 * since we are overriding the middle-of-transaction flag for read
+	 * transactions.
+	 */
+	memset(&helper_msg, 0, sizeof(helper_msg));
+	helper_msg.address = segment_address;
+	helper_msg.buffer = &aux->segment;
+	helper_msg.size = 1;
+	dp_aux_cmd_fifo_tx(aux, &helper_msg);
+
+	/*
+	 * Send the offset address for every i2c read in which the
+	 * middle-of-transaction flag is set. This will ensure that the sink
+	 * will update its read pointer and return the correct portion of the
+	 * EDID buffer in the subsequent i2c read trasntion triggered in the
+	 * native AUX transfer function.
+	 */
+	memset(&helper_msg, 0, sizeof(helper_msg));
+	helper_msg.address = input_msg->address;
+	helper_msg.buffer = &aux->offset;
+	helper_msg.size = 1;
+	dp_aux_cmd_fifo_tx(aux, &helper_msg);
+	aux->offset += message_size;
+
+	if (aux->offset == 0x80 || aux->offset == 0x100)
+		aux->segment = 0x0; /* reset segment at end of block */
+}
+
+/*
+ * This function does the real job to process an AUX transaction.
+ * It will call aux_reset() function to reset the AUX channel,
+ * if the waiting is timeout.
+ */
+static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
+			       struct drm_dp_aux_msg *msg)
+{
+	ssize_t ret;
+	int const aux_cmd_native_max = 16;
+	int const aux_cmd_i2c_max = 128;
+	int const retry_count = 5;
+	struct dp_aux_private *aux = container_of(drm_aux,
+		struct dp_aux_private, drm_aux);
+
+	mutex_lock(&aux->mutex);
+
+	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+
+	/* Ignore address only message */
+	if ((msg->size == 0) || (msg->buffer == NULL)) {
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+		ret = msg->size;
+		goto unlock_exit;
+	}
+
+	/* msg sanity check */
+	if ((aux->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);
+		ret = -EINVAL;
+		goto unlock_exit;
+	}
+
+	dp_aux_update_offset_and_segment(aux, msg);
+	dp_aux_transfer_helper(aux, msg);
+
+	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	aux->cmd_busy = true;
+
+	if (aux->read) {
+		aux->no_send_addr = true;
+		aux->no_send_stop = false;
+	} else {
+		aux->no_send_addr = true;
+		aux->no_send_stop = true;
+	}
+
+	ret = dp_aux_cmd_fifo_tx(aux, msg);
+	if ((ret < 0) && aux->native) {
+		aux->retry_cnt++;
+		if (!(aux->retry_cnt % retry_count))
+			aux->catalog->update_aux_cfg(aux->catalog,
+				aux->cfg, PHY_AUX_CFG1);
+		aux->catalog->reset(aux->catalog);
+		goto unlock_exit;
+	} else if (ret < 0) {
+		goto unlock_exit;
+	}
+
+	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
+		if (aux->read)
+			dp_aux_cmd_fifo_rx(aux, msg);
+
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+	} else {
+		/* Reply defer to retry */
+		msg->reply = aux->native ?
+			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
+	}
+
+	/* Return requested size for success or retry */
+	ret = msg->size;
+	aux->retry_cnt = 0;
+
+unlock_exit:
+	aux->cmd_busy = false;
+	mutex_unlock(&aux->mutex);
+	return ret;
+}
+
+static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
+{
+	int i = 0;
+
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+		aux_cfg[i].current_index = 0;
+}
+
+static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux || !aux_cfg) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	dp_aux_reset_phy_config_indices(aux_cfg);
+	aux->catalog->setup(aux->catalog, aux_cfg);
+	aux->catalog->reset(aux->catalog);
+	aux->catalog->enable(aux->catalog, true);
+	aux->retry_cnt = 0;
+}
+
+static void dp_aux_deinit(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->catalog->enable(aux->catalog, false);
+}
+
+static int dp_aux_register(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+	int ret = 0;
+
+	if (!dp_aux) {
+		pr_err("invalid input\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	aux->drm_aux.name = "dpu_dp_aux";
+	aux->drm_aux.dev = aux->dev;
+	aux->drm_aux.transfer = dp_aux_transfer;
+	ret = drm_dp_aux_register(&aux->drm_aux);
+	if (ret) {
+		pr_err("%s: failed to register drm aux: %d\n", __func__, ret);
+		goto exit;
+	}
+	dp_aux->drm_aux = &aux->drm_aux;
+exit:
+	return ret;
+}
+
+static void dp_aux_deregister(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+	drm_dp_aux_unregister(&aux->drm_aux);
+}
+
+struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
+			  struct dp_aux_cfg *aux_cfg)
+{
+	int rc = 0;
+	struct dp_aux_private *aux;
+	struct dp_aux *dp_aux;
+
+	if (!catalog || !aux_cfg) {
+		pr_err("invalid input\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
+	if (!aux) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	init_completion(&aux->comp);
+	aux->cmd_busy = false;
+	mutex_init(&aux->mutex);
+
+	aux->dev = dev;
+	aux->catalog = catalog;
+	aux->cfg = aux_cfg;
+	dp_aux = &aux->dp_aux;
+	aux->retry_cnt = 0;
+
+	dp_aux->isr     = dp_aux_isr;
+	dp_aux->init    = dp_aux_init;
+	dp_aux->deinit  = dp_aux_deinit;
+	dp_aux->drm_aux_register = dp_aux_register;
+	dp_aux->drm_aux_deregister = dp_aux_deregister;
+	dp_aux->reconfig = dp_aux_reconfig;
+
+	return dp_aux;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_aux_put(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux)
+		return;
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	mutex_destroy(&aux->mutex);
+
+	devm_kfree(aux->dev, aux);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.h b/drivers/gpu/drm/msm/dp/dp_aux.h
new file mode 100644
index 0000000..0c300ed
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_aux.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_AUX_H_
+#define _DP_AUX_H_
+
+#include "dp_catalog.h"
+#include <drm/drm_dp_helper.h>
+
+enum dp_aux_error {
+	DP_AUX_ERR_NONE	= 0,
+	DP_AUX_ERR_ADDR	= -1,
+	DP_AUX_ERR_TOUT	= -2,
+	DP_AUX_ERR_NACK	= -3,
+	DP_AUX_ERR_DEFER	= -4,
+	DP_AUX_ERR_NACK_DEFER	= -5,
+};
+
+struct dp_aux {
+	struct drm_dp_aux *drm_aux;
+	int (*drm_aux_register)(struct dp_aux *aux);
+	void (*drm_aux_deregister)(struct dp_aux *aux);
+	void (*isr)(struct dp_aux *aux);
+	void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg);
+	void (*deinit)(struct dp_aux *aux);
+	void (*reconfig)(struct dp_aux *aux);
+};
+
+struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
+			  struct dp_aux_cfg *aux_cfg);
+void dp_aux_put(struct dp_aux *aux);
+
+#endif /*__DP_AUX_H_*/
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
new file mode 100644
index 0000000..5818612
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <drm/drm_dp_helper.h>
+
+#include "dp_catalog.h"
+#include "dp_reg.h"
+
+#define DP_GET_MSB(x)	(x >> 8)
+#define DP_GET_LSB(x)	(x & 0xff)
+
+#define dp_read(offset) readl_relaxed((offset))
+#define dp_write(offset, data) writel_relaxed((data), (offset))
+
+#define dp_catalog_get_priv(x) { \
+	struct dp_catalog *dp_catalog; \
+	dp_catalog = container_of(x, struct dp_catalog, x); \
+	catalog = container_of(dp_catalog, struct dp_catalog_private, \
+				dp_catalog); \
+}
+
+#define DP_INTERRUPT_STATUS1 \
+	(DP_INTR_AUX_I2C_DONE| \
+	DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \
+	DP_INTR_NACK_DEFER | DP_INTR_WRONG_DATA_CNT | \
+	DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER | \
+	DP_INTR_PLL_UNLOCKED | DP_INTR_AUX_ERROR)
+
+#define DP_INTR_MASK1		(DP_INTERRUPT_STATUS1 << 2)
+
+#define DP_INTERRUPT_STATUS2 \
+	(DP_INTR_READY_FOR_VIDEO | DP_INTR_IDLE_PATTERN_SENT | \
+	DP_INTR_FRAME_END | DP_INTR_CRC_UPDATED)
+
+#define DP_INTR_MASK2		(DP_INTERRUPT_STATUS2 << 2)
+
+static u8 const vm_pre_emphasis[4][4] = {
+	{0x00, 0x0B, 0x12, 0xFF},       /* pe0, 0 db */
+	{0x00, 0x0A, 0x12, 0xFF},       /* pe1, 3.5 db */
+	{0x00, 0x0C, 0xFF, 0xFF},       /* pe2, 6.0 db */
+	{0xFF, 0xFF, 0xFF, 0xFF}        /* pe3, 9.5 db */
+};
+
+/* voltage swing, 0.2v and 1.0v are not support */
+static u8 const vm_voltage_swing[4][4] = {
+	{0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v  */
+	{0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */
+	{0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
+	{0xFF, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
+};
+
+/* audio related catalog functions */
+struct dp_catalog_private {
+	struct device *dev;
+	struct dp_io *io;
+
+	u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX];
+	struct dp_catalog dp_catalog;
+};
+
+/* aux related catalog functions */
+static u32 dp_catalog_aux_read_data(struct dp_catalog_aux *aux)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		goto end;
+	}
+
+	dp_catalog_get_priv(aux);
+	base = catalog->io->dp_aux.base;
+
+	return dp_read(base + DP_AUX_DATA);
+end:
+	return 0;
+}
+
+static int dp_catalog_aux_write_data(struct dp_catalog_aux *aux)
+{
+	int rc = 0;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp_catalog_get_priv(aux);
+	base = catalog->io->dp_aux.base;
+
+	dp_write(base + DP_AUX_DATA, aux->data);
+end:
+	return rc;
+}
+
+static int dp_catalog_aux_write_trans(struct dp_catalog_aux *aux)
+{
+	int rc = 0;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp_catalog_get_priv(aux);
+	base = catalog->io->dp_aux.base;
+
+	dp_write(base + DP_AUX_TRANS_CTRL, aux->data);
+end:
+	return rc;
+}
+
+static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read)
+{
+	int rc = 0;
+	u32 data = 0;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp_catalog_get_priv(aux);
+	base = catalog->io->dp_aux.base;
+
+	if (read) {
+		data = dp_read(base + DP_AUX_TRANS_CTRL);
+		data &= ~BIT(9);
+		dp_write(base + DP_AUX_TRANS_CTRL, data);
+	} else {
+		dp_write(base + DP_AUX_TRANS_CTRL, 0);
+	}
+end:
+	return rc;
+}
+
+static void dp_catalog_aux_reset(struct dp_catalog_aux *aux)
+{
+	u32 aux_ctrl;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(aux);
+	base = catalog->io->dp_aux.base;
+
+	aux_ctrl = dp_read(base + DP_AUX_CTRL);
+
+	aux_ctrl |= BIT(1);
+	dp_write(base + DP_AUX_CTRL, aux_ctrl);
+	usleep_range(1000, 1010); /* h/w recommended delay */
+
+	aux_ctrl &= ~BIT(1);
+	dp_write(base + DP_AUX_CTRL, aux_ctrl);
+	wmb();
+}
+
+static void dp_catalog_aux_enable(struct dp_catalog_aux *aux, bool enable)
+{
+	u32 aux_ctrl;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(aux);
+	base = catalog->io->dp_aux.base;
+
+	aux_ctrl = dp_read(base + DP_AUX_CTRL);
+
+	if (enable) {
+		dp_write(base + DP_TIMEOUT_COUNT, 0xffff);
+		dp_write(base + DP_AUX_LIMITS, 0xffff);
+		aux_ctrl |= BIT(0);
+	} else {
+		aux_ctrl &= ~BIT(0);
+	}
+
+	dp_write(base + DP_AUX_CTRL, aux_ctrl);
+}
+
+static void dp_catalog_aux_update_cfg(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg, enum dp_phy_aux_config_type type)
+{
+	struct dp_catalog_private *catalog;
+	u32 new_index = 0, current_index = 0;
+
+	if (!aux || !cfg || (type >= PHY_AUX_CFG_MAX)) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(aux);
+
+	current_index = cfg[type].current_index;
+	new_index = (current_index + 1) % cfg[type].cfg_cnt;
+	pr_debug("Updating %s from 0x%08x to 0x%08x\n",
+		dp_phy_aux_config_type_to_string(type),
+	cfg[type].lut[current_index], cfg[type].lut[new_index]);
+
+	dp_write(catalog->io->phy_io.base + cfg[type].offset,
+			cfg[type].lut[new_index]);
+	cfg[type].current_index = new_index;
+}
+
+static void dp_catalog_aux_setup(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg)
+{
+	struct dp_catalog_private *catalog;
+	int i = 0;
+
+	if (!aux || !cfg) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(aux);
+
+	dp_write(catalog->io->phy_io.base + DP_PHY_PD_CTL, 0x65);
+	wmb(); /* make sure PD programming happened */
+
+	/* Turn on BIAS current for PHY/PLL */
+	dp_write(catalog->io->dp_pll_io.base +
+		QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1b);
+
+	/* DP AUX CFG register programming */
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+		pr_debug("%s: offset=0x%08x, value=0x%08x\n",
+			dp_phy_aux_config_type_to_string(i),
+			cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
+		dp_write(catalog->io->phy_io.base + cfg[i].offset,
+			cfg[i].lut[cfg[i].current_index]);
+	}
+
+	dp_write(catalog->io->phy_io.base + DP_PHY_AUX_INTERRUPT_MASK, 0x1F);
+}
+
+static void dp_catalog_aux_get_irq(struct dp_catalog_aux *aux, bool cmd_busy)
+{
+	u32 ack;
+	struct dp_catalog_private *catalog;
+	void __iomem *ahb_base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(aux);
+	ahb_base = catalog->io->dp_ahb.base;
+
+	aux->isr = dp_read(ahb_base + DP_INTR_STATUS);
+	aux->isr &= ~DP_INTR_MASK1;
+	ack = aux->isr & DP_INTERRUPT_STATUS1;
+	ack <<= 1;
+	ack |= DP_INTR_MASK1;
+	dp_write(ahb_base + DP_INTR_STATUS, ack);
+}
+
+/* controller related catalog functions */
+static void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_link.base;
+
+	dp_write(base + DP_VALID_BOUNDARY, ctrl->valid_boundary);
+	dp_write(base + DP_TU, ctrl->dp_tu);
+	dp_write(base + DP_VALID_BOUNDARY_2, ctrl->valid_boundary2);
+}
+
+static void dp_catalog_ctrl_state_ctrl(struct dp_catalog_ctrl *ctrl, u32 state)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_link.base;
+
+	dp_write(base + DP_STATE_CTRL, state);
+}
+
+static void dp_catalog_ctrl_config_ctrl(struct dp_catalog_ctrl *ctrl, u32 cfg)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *link_base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	link_base = catalog->io->dp_link.base;
+
+	pr_debug("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
+
+	dp_write(link_base + DP_CONFIGURATION_CTRL, cfg);
+}
+
+static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_link.base;
+
+	dp_write(base + DP_LOGICAL2PHYSICAL_LANE_MAPPING, 0xe4);
+}
+
+static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
+						bool enable)
+{
+	u32 mainlink_ctrl;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_link.base;
+
+	if (enable) {
+		dp_write(base + DP_MAINLINK_CTRL, 0x02000000);
+		wmb(); /* make sure mainlink is turned off before reset */
+		dp_write(base + DP_MAINLINK_CTRL, 0x02000002);
+		wmb(); /* make sure mainlink entered reset */
+		dp_write(base + DP_MAINLINK_CTRL, 0x02000000);
+		wmb(); /* make sure mainlink reset done */
+		dp_write(base + DP_MAINLINK_CTRL, 0x02000001);
+		wmb(); /* make sure mainlink turned on */
+	} else {
+		mainlink_ctrl = dp_read(base + DP_MAINLINK_CTRL);
+		mainlink_ctrl &= ~BIT(0);
+		dp_write(base + DP_MAINLINK_CTRL, mainlink_ctrl);
+	}
+}
+
+static void dp_catalog_ctrl_config_misc(struct dp_catalog_ctrl *ctrl,
+					u32 cc, u32 tb)
+{
+	u32 misc_val;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_link.base;
+
+	misc_val = dp_read(base + DP_MISC1_MISC0);
+	misc_val |= cc;
+	misc_val |= (tb << 5);
+	misc_val |= BIT(0); /* Configure clock to synchronous mode */
+
+	pr_debug("misc settings = 0x%x\n", misc_val);
+	dp_write(base + DP_MISC1_MISC0, misc_val);
+}
+
+static void dp_catalog_ctrl_config_msa(struct dp_catalog_ctrl *ctrl,
+					u32 rate, u32 stream_rate_khz,
+					bool fixed_nvid)
+{
+	u32 pixel_m, pixel_n;
+	u32 mvid, nvid;
+	u64 mvid_calc;
+	u32 const nvid_fixed = 0x8000;
+	u32 const link_rate_hbr2 = 540000;
+	u32 const link_rate_hbr3 = 810000;
+	struct dp_catalog_private *catalog;
+	void __iomem *base_cc, *base_ctrl;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	if (fixed_nvid) {
+		pr_debug("use fixed NVID=0x%x\n", nvid_fixed);
+		nvid = nvid_fixed;
+
+		pr_debug("link rate=%dkbps, stream_rate_khz=%uKhz",
+			rate, stream_rate_khz);
+
+		/*
+		 * For intermediate results, use 64 bit arithmetic to avoid
+		 * loss of precision.
+		 */
+		mvid_calc = (u64) stream_rate_khz * nvid;
+		mvid_calc = div_u64(mvid_calc, rate);
+
+		/*
+		 * truncate back to 32 bits as this final divided value will
+		 * always be within the range of a 32 bit unsigned int.
+		 */
+		mvid = (u32) mvid_calc;
+	} else {
+		base_cc = catalog->io->dp_cc_io.base;
+
+		pixel_m = dp_read(base_cc + MMSS_DP_PIXEL_M);
+		pixel_n = dp_read(base_cc + MMSS_DP_PIXEL_N);
+		pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
+
+		mvid = (pixel_m & 0xFFFF) * 5;
+		nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+
+		pr_debug("rate = %d\n", rate);
+
+		if (link_rate_hbr2 == rate)
+			nvid *= 2;
+
+		if (link_rate_hbr3 == rate)
+			nvid *= 3;
+	}
+
+	base_ctrl = catalog->io->dp_link.base;
+	pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
+	dp_write(base_ctrl + DP_SOFTWARE_MVID, mvid);
+	dp_write(base_ctrl + DP_SOFTWARE_NVID, nvid);
+}
+
+static void dp_catalog_ctrl_set_pattern(struct dp_catalog_ctrl *ctrl,
+					u32 pattern)
+{
+	int bit, cnt = 10;
+	u32 data;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_link.base;
+
+	bit = 1;
+	bit <<= (pattern - 1);
+	pr_debug("hw: bit=%d train=%d\n", bit, pattern);
+	dp_write(base + DP_STATE_CTRL, bit);
+
+	bit = 8;
+	bit <<= (pattern - 1);
+
+	while (cnt--) {
+		data = dp_read(base + DP_MAINLINK_READY);
+		if (data & bit)
+			break;
+	}
+
+	if (cnt == 0)
+		pr_err("set link_train=%d failed\n", pattern);
+}
+
+static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+
+	base = catalog->io->usb3_dp_com.base;
+
+	dp_write(base + USB3_DP_COM_RESET_OVRD_CTRL, 0x0a);
+	dp_write(base + USB3_DP_COM_PHY_MODE_CTRL, 0x02);
+	dp_write(base + USB3_DP_COM_SW_RESET, 0x01);
+	/* make sure usb3 com phy software reset is done */
+	wmb();
+
+	if (!flip) /* CC1 */
+		dp_write(base + USB3_DP_COM_TYPEC_CTRL, 0x02);
+	else /* CC2 */
+		dp_write(base + USB3_DP_COM_TYPEC_CTRL, 0x03);
+
+	dp_write(base + USB3_DP_COM_SWI_CTRL, 0x00);
+	dp_write(base + USB3_DP_COM_SW_RESET, 0x00);
+	/* make sure the software reset is done */
+	wmb();
+
+	dp_write(base + USB3_DP_COM_POWER_DOWN_CTRL, 0x01);
+	dp_write(base + USB3_DP_COM_RESET_OVRD_CTRL, 0x00);
+	/* make sure phy is brought out of reset */
+	wmb();
+
+}
+
+static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel,
+	bool enable)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->dp_p0.base;
+
+	if (!enable) {
+		dp_write(base + MMSS_DP_TPG_MAIN_CONTROL, 0x0);
+		dp_write(base + MMSS_DP_BIST_ENABLE, 0x0);
+		dp_write(base + MMSS_DP_TIMING_ENGINE_EN, 0x0);
+		wmb(); /* ensure Timing generator is turned off */
+		return;
+	}
+
+	dp_write(base + MMSS_DP_INTF_CONFIG, 0x0);
+	dp_write(base + MMSS_DP_INTF_HSYNC_CTL, panel->hsync_ctl);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PERIOD_F0, panel->vsync_period *
+			panel->hsync_period);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, panel->v_sync_width *
+			panel->hsync_period);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
+	dp_write(base + MMSS_DP_INTF_DISPLAY_HCTL, panel->display_hctl);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_HCTL, 0);
+	dp_write(base + MMSS_INTF_DISPLAY_V_START_F0, panel->display_v_start);
+	dp_write(base + MMSS_DP_INTF_DISPLAY_V_END_F0, panel->display_v_end);
+	dp_write(base + MMSS_INTF_DISPLAY_V_START_F1, 0);
+	dp_write(base + MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
+	dp_write(base + MMSS_DP_INTF_POLARITY_CTL, 0);
+	wmb(); /* ensure TPG registers are programmed */
+
+	dp_write(base + MMSS_DP_TPG_MAIN_CONTROL, 0x100);
+	dp_write(base + MMSS_DP_TPG_VIDEO_CONFIG, 0x5);
+	wmb(); /* ensure TPG config is programmed */
+	dp_write(base + MMSS_DP_BIST_ENABLE, 0x1);
+	dp_write(base + MMSS_DP_TIMING_ENGINE_EN, 0x1);
+	wmb(); /* ensure Timing generator is turned on */
+	pr_debug("%s: enabled tpg\n", __func__);
+}
+
+static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl)
+{
+	u32 sw_reset;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_ahb.base;
+
+	sw_reset = dp_read(base + DP_SW_RESET);
+
+	sw_reset |= BIT(0);
+	dp_write(base + DP_SW_RESET, sw_reset);
+	usleep_range(1000, 1010); /* h/w recommended delay */
+
+	sw_reset &= ~BIT(0);
+	dp_write(base + DP_SW_RESET, sw_reset);
+}
+
+static bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog_ctrl *ctrl)
+{
+	u32 data;
+	int cnt = 10;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		goto end;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_link.base;
+
+	while (--cnt) {
+		/* DP_MAINLINK_READY */
+		data = dp_read(base + DP_MAINLINK_READY);
+		if (data & BIT(0))
+			return true;
+
+		usleep_range(1000, 1010); /* 1ms wait before next reg read */
+	}
+	pr_err("mainlink not ready\n");
+end:
+	return false;
+}
+
+static void dp_catalog_ctrl_enable_irq(struct dp_catalog_ctrl *ctrl,
+						bool enable)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_ahb.base;
+
+	if (enable) {
+		dp_write(base + DP_INTR_STATUS, DP_INTR_MASK1);
+		dp_write(base + DP_INTR_STATUS2, DP_INTR_MASK2);
+	} else {
+		dp_write(base + DP_INTR_STATUS, 0x00);
+		dp_write(base + DP_INTR_STATUS2, 0x00);
+	}
+}
+
+static void dp_catalog_ctrl_hpd_config(struct dp_catalog_ctrl *ctrl, bool en)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_aux.base;
+
+	if (en) {
+		u32 reftimer = dp_read(base + DP_DP_HPD_REFTIMER);
+
+		dp_write(base + DP_DP_HPD_INT_ACK, 0xF);
+		dp_write(base + DP_DP_HPD_INT_MASK, 0xF);
+
+		/* Enabling REFTIMER */
+		reftimer |= BIT(16);
+		dp_write(base + DP_DP_HPD_REFTIMER, 0xF);
+		/* Enable HPD */
+		dp_write(base + DP_DP_HPD_CTRL, 0x1);
+	} else {
+		/*Disable HPD */
+		dp_write(base + DP_DP_HPD_CTRL, 0x0);
+	}
+}
+
+static void dp_catalog_ctrl_get_interrupt(struct dp_catalog_ctrl *ctrl)
+{
+	u32 ack = 0;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_ahb.base;
+
+	ctrl->isr = dp_read(base + DP_INTR_STATUS2);
+	ctrl->isr &= ~DP_INTR_MASK2;
+	ack = ctrl->isr & DP_INTERRUPT_STATUS2;
+	ack <<= 1;
+	ack |= DP_INTR_MASK2;
+	dp_write(base + DP_INTR_STATUS2, ack);
+}
+
+static void dp_catalog_ctrl_phy_reset(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base = catalog->io->dp_ahb.base;
+
+	dp_write(base + DP_PHY_CTRL, 0x5); /* bit 0 & 2 */
+	usleep_range(1000, 1010); /* h/w recommended delay */
+	dp_write(base + DP_PHY_CTRL, 0x0);
+	wmb(); /* make sure PHY reset done */
+}
+
+static void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog_ctrl *ctrl,
+		bool flipped, u8 ln_cnt)
+{
+	u32 info = 0x0;
+	struct dp_catalog_private *catalog;
+	u8 orientation = BIT(!!flipped);
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+
+	info |= (ln_cnt & 0x0F);
+	info |= ((orientation & 0x0F) << 4);
+	pr_debug("Shared Info = 0x%x\n", info);
+
+	dp_write(catalog->io->phy_io.base + DP_PHY_SPARE0, info);
+}
+
+static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl,
+		u8 v_level, u8 p_level)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base0, *base1;
+	u8 value0, value1;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+	base0 = catalog->io->ln_tx0_io.base;
+	base1 = catalog->io->ln_tx1_io.base;
+
+	pr_debug("hw: v=%d p=%d\n", v_level, p_level);
+
+	value0 = vm_voltage_swing[v_level][p_level];
+	value1 = vm_pre_emphasis[v_level][p_level];
+
+	/* program default setting first */
+	dp_write(base0 + TXn_TX_DRV_LVL, 0x2A);
+	dp_write(base1 + TXn_TX_DRV_LVL, 0x2A);
+	dp_write(base0 + TXn_TX_EMP_POST1_LVL, 0x20);
+	dp_write(base1 + TXn_TX_EMP_POST1_LVL, 0x20);
+
+	/* Enable MUX to use Cursor values from these registers */
+	value0 |= BIT(5);
+	value1 |= BIT(5);
+
+	/* Configure host and panel only if both values are allowed */
+	if (value0 != 0xFF && value1 != 0xFF) {
+		dp_write(base0 + TXn_TX_DRV_LVL, value0);
+		dp_write(base1 + TXn_TX_DRV_LVL, value0);
+		dp_write(base0 + TXn_TX_EMP_POST1_LVL, value1);
+		dp_write(base1 + TXn_TX_EMP_POST1_LVL, value1);
+
+		pr_debug("hw: vx_value=0x%x px_value=0x%x\n",
+			value0, value1);
+	} else {
+		pr_err("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
+			v_level, value0, p_level, value1);
+	}
+}
+
+static void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog_ctrl *ctrl,
+			u32 pattern)
+{
+	struct dp_catalog_private *catalog;
+	u32 value = 0x0;
+	void __iomem *base = NULL;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+
+	base = catalog->io->dp_link.base;
+
+	dp_write(base + DP_STATE_CTRL, 0x0);
+
+	switch (pattern) {
+	case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
+		dp_write(base + DP_STATE_CTRL, 0x1);
+		break;
+	case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+		value &= ~(1 << 16);
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		value |= 0xFC;
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		dp_write(base + DP_MAINLINK_LEVELS, 0x2);
+		dp_write(base + DP_STATE_CTRL, 0x10);
+		break;
+	case DP_TEST_PHY_PATTERN_PRBS7:
+		dp_write(base + DP_STATE_CTRL, 0x20);
+		break;
+	case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
+		dp_write(base + DP_STATE_CTRL, 0x40);
+		/* 00111110000011111000001111100000 */
+		dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG0, 0x3E0F83E0);
+		/* 00001111100000111110000011111000 */
+		dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG1, 0x0F83E0F8);
+		/* 1111100000111110 */
+		dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG2, 0x0000F83E);
+		break;
+	case DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN:
+		value = BIT(16);
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		value |= 0xFC;
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		dp_write(base + DP_MAINLINK_LEVELS, 0x2);
+		dp_write(base + DP_STATE_CTRL, 0x10);
+		break;
+	default:
+		pr_debug("No valid test pattern requested: 0x%x\n", pattern);
+		return;
+	}
+
+	/* Make sure the test pattern is programmed in the hardware */
+	wmb();
+}
+
+static u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base = NULL;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return 0;
+	}
+
+	dp_catalog_get_priv(ctrl);
+
+	base = catalog->io->dp_link.base;
+
+	return dp_read(base + DP_MAINLINK_READY);
+}
+
+/* panel related catalog functions */
+static int dp_catalog_panel_timing_cfg(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		goto end;
+	}
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->dp_link.base;
+
+	dp_write(base + DP_TOTAL_HOR_VER, panel->total);
+	dp_write(base + DP_START_HOR_VER_FROM_SYNC, panel->sync_start);
+	dp_write(base + DP_HSYNC_VSYNC_WIDTH_POLARITY, panel->width_blanking);
+	dp_write(base + DP_ACTIVE_HOR_VER, panel->dp_active);
+end:
+	return 0;
+}
+
+static void dp_catalog_audio_init(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	static u32 sdp_map[][DP_AUDIO_SDP_HEADER_MAX] = {
+		{
+			MMSS_DP_AUDIO_STREAM_0,
+			MMSS_DP_AUDIO_STREAM_1,
+			MMSS_DP_AUDIO_STREAM_1,
+		},
+		{
+			MMSS_DP_AUDIO_TIMESTAMP_0,
+			MMSS_DP_AUDIO_TIMESTAMP_1,
+			MMSS_DP_AUDIO_TIMESTAMP_1,
+		},
+		{
+			MMSS_DP_AUDIO_INFOFRAME_0,
+			MMSS_DP_AUDIO_INFOFRAME_1,
+			MMSS_DP_AUDIO_INFOFRAME_1,
+		},
+		{
+			MMSS_DP_AUDIO_COPYMANAGEMENT_0,
+			MMSS_DP_AUDIO_COPYMANAGEMENT_1,
+			MMSS_DP_AUDIO_COPYMANAGEMENT_1,
+		},
+		{
+			MMSS_DP_AUDIO_ISRC_0,
+			MMSS_DP_AUDIO_ISRC_1,
+			MMSS_DP_AUDIO_ISRC_1,
+		},
+	};
+
+	if (!audio)
+		return;
+
+	dp_catalog_get_priv(audio);
+
+	catalog->audio_map = sdp_map;
+}
+
+static void dp_catalog_audio_config_sdp(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 sdp_cfg = 0;
+	u32 sdp_cfg2 = 0;
+
+	if (!audio)
+		return;
+
+	dp_catalog_get_priv(audio);
+	base = catalog->io->dp_link.base;
+
+	sdp_cfg = dp_read(base + MMSS_DP_SDP_CFG);
+
+	/* AUDIO_TIMESTAMP_SDP_EN */
+	sdp_cfg |= BIT(1);
+	/* AUDIO_STREAM_SDP_EN */
+	sdp_cfg |= BIT(2);
+	/* AUDIO_COPY_MANAGEMENT_SDP_EN */
+	sdp_cfg |= BIT(5);
+	/* AUDIO_ISRC_SDP_EN  */
+	sdp_cfg |= BIT(6);
+	/* AUDIO_INFOFRAME_SDP_EN  */
+	sdp_cfg |= BIT(20);
+
+	pr_debug("sdp_cfg = 0x%x\n", sdp_cfg);
+	dp_write(base + MMSS_DP_SDP_CFG, sdp_cfg);
+
+	sdp_cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
+	/* IFRM_REGSRC -> Do not use reg values */
+	sdp_cfg2 &= ~BIT(0);
+	/* AUDIO_STREAM_HB3_REGSRC-> Do not use reg values */
+	sdp_cfg2 &= ~BIT(1);
+
+	pr_debug("sdp_cfg2 = 0x%x\n", sdp_cfg2);
+	dp_write(base + MMSS_DP_SDP_CFG2, sdp_cfg2);
+}
+
+static void dp_catalog_audio_get_header(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
+	void __iomem *base;
+	enum dp_catalog_audio_sdp_type sdp;
+	enum dp_catalog_audio_header_type header;
+
+	if (!audio)
+		return;
+
+	dp_catalog_get_priv(audio);
+
+	base    = catalog->io->dp_link.base;
+	sdp_map = catalog->audio_map;
+	sdp     = audio->sdp_type;
+	header  = audio->sdp_header;
+
+	audio->data = dp_read(base + sdp_map[sdp][header]);
+}
+
+static void dp_catalog_audio_set_header(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
+	void __iomem *base;
+	enum dp_catalog_audio_sdp_type sdp;
+	enum dp_catalog_audio_header_type header;
+	u32 data;
+
+	if (!audio)
+		return;
+
+	dp_catalog_get_priv(audio);
+
+	base    = catalog->io->dp_link.base;
+	sdp_map = catalog->audio_map;
+	sdp     = audio->sdp_type;
+	header  = audio->sdp_header;
+	data    = audio->data;
+
+	dp_write(base + sdp_map[sdp][header], data);
+}
+
+static void dp_catalog_audio_config_acr(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 acr_ctrl, select;
+
+	dp_catalog_get_priv(audio);
+
+	select = audio->data;
+	base   = catalog->io->dp_link.base;
+
+	acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
+
+	pr_debug("select = 0x%x, acr_ctrl = 0x%x\n", select, acr_ctrl);
+
+	dp_write(base + MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
+}
+
+static void dp_catalog_audio_safe_to_exit_level(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 mainlink_levels, safe_to_exit_level;
+
+	dp_catalog_get_priv(audio);
+
+	base   = catalog->io->dp_link.base;
+	safe_to_exit_level = audio->data;
+
+	mainlink_levels = dp_read(base + DP_MAINLINK_LEVELS);
+	mainlink_levels &= 0xFE0;
+	mainlink_levels |= safe_to_exit_level;
+
+	pr_debug("mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
+			mainlink_levels, safe_to_exit_level);
+
+	dp_write(base + DP_MAINLINK_LEVELS, mainlink_levels);
+}
+
+static void dp_catalog_audio_enable(struct dp_catalog_audio *audio)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	bool enable;
+	u32 audio_ctrl;
+
+	dp_catalog_get_priv(audio);
+
+	base   = catalog->io->dp_link.base;
+	enable = !!audio->data;
+
+	audio_ctrl = dp_read(base + MMSS_DP_AUDIO_CFG);
+
+	if (enable)
+		audio_ctrl |= BIT(0);
+	else
+		audio_ctrl &= ~BIT(0);
+
+	pr_debug("dp_audio_cfg = 0x%x\n", audio_ctrl);
+	dp_write(base + MMSS_DP_AUDIO_CFG, audio_ctrl);
+
+	/* make sure audio engine is disabled */
+	wmb();
+}
+
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
+{
+	int rc = 0;
+	struct dp_catalog *dp_catalog;
+	struct dp_catalog_private *catalog;
+	struct dp_catalog_aux aux = {
+		.read_data     = dp_catalog_aux_read_data,
+		.write_data    = dp_catalog_aux_write_data,
+		.write_trans   = dp_catalog_aux_write_trans,
+		.clear_trans   = dp_catalog_aux_clear_trans,
+		.reset         = dp_catalog_aux_reset,
+		.update_aux_cfg = dp_catalog_aux_update_cfg,
+		.enable        = dp_catalog_aux_enable,
+		.setup         = dp_catalog_aux_setup,
+		.get_irq       = dp_catalog_aux_get_irq,
+	};
+	struct dp_catalog_ctrl ctrl = {
+		.state_ctrl     = dp_catalog_ctrl_state_ctrl,
+		.config_ctrl    = dp_catalog_ctrl_config_ctrl,
+		.lane_mapping   = dp_catalog_ctrl_lane_mapping,
+		.mainlink_ctrl  = dp_catalog_ctrl_mainlink_ctrl,
+		.config_misc    = dp_catalog_ctrl_config_misc,
+		.config_msa     = dp_catalog_ctrl_config_msa,
+		.set_pattern    = dp_catalog_ctrl_set_pattern,
+		.reset          = dp_catalog_ctrl_reset,
+		.usb_reset      = dp_catalog_ctrl_usb_reset,
+		.mainlink_ready = dp_catalog_ctrl_mainlink_ready,
+		.enable_irq     = dp_catalog_ctrl_enable_irq,
+		.hpd_config     = dp_catalog_ctrl_hpd_config,
+		.phy_reset      = dp_catalog_ctrl_phy_reset,
+		.phy_lane_cfg   = dp_catalog_ctrl_phy_lane_cfg,
+		.update_vx_px   = dp_catalog_ctrl_update_vx_px,
+		.get_interrupt  = dp_catalog_ctrl_get_interrupt,
+		.update_transfer_unit = dp_catalog_ctrl_update_transfer_unit,
+		.send_phy_pattern    = dp_catalog_ctrl_send_phy_pattern,
+		.read_phy_pattern = dp_catalog_ctrl_read_phy_pattern,
+	};
+	struct dp_catalog_audio audio = {
+		.init       = dp_catalog_audio_init,
+		.config_acr = dp_catalog_audio_config_acr,
+		.enable     = dp_catalog_audio_enable,
+		.config_sdp = dp_catalog_audio_config_sdp,
+		.set_header = dp_catalog_audio_set_header,
+		.get_header = dp_catalog_audio_get_header,
+		.safe_to_exit_level = dp_catalog_audio_safe_to_exit_level,
+	};
+	struct dp_catalog_panel panel = {
+		.timing_cfg = dp_catalog_panel_timing_cfg,
+		.tpg_config = dp_catalog_panel_tpg_cfg,
+	};
+
+	if (!io) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	catalog  = devm_kzalloc(dev, sizeof(*catalog), GFP_KERNEL);
+	if (!catalog) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	catalog->dev = dev;
+	catalog->io = io;
+
+	dp_catalog = &catalog->dp_catalog;
+
+	dp_catalog->aux   = aux;
+	dp_catalog->ctrl  = ctrl;
+	dp_catalog->audio = audio;
+	dp_catalog->panel = panel;
+
+	return dp_catalog;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_catalog_put(struct dp_catalog *dp_catalog)
+{
+	struct dp_catalog_private *catalog;
+
+	if (!dp_catalog)
+		return;
+
+	catalog = container_of(dp_catalog, struct dp_catalog_private,
+				dp_catalog);
+
+	devm_kfree(catalog->dev, catalog);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
new file mode 100644
index 0000000..58951df
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_CATALOG_H_
+#define _DP_CATALOG_H_
+
+#include "dp_parser.h"
+
+/* interrupts */
+#define DP_INTR_HPD		BIT(0)
+#define DP_INTR_AUX_I2C_DONE	BIT(3)
+#define DP_INTR_WRONG_ADDR	BIT(6)
+#define DP_INTR_TIMEOUT		BIT(9)
+#define DP_INTR_NACK_DEFER	BIT(12)
+#define DP_INTR_WRONG_DATA_CNT	BIT(15)
+#define DP_INTR_I2C_NACK	BIT(18)
+#define DP_INTR_I2C_DEFER	BIT(21)
+#define DP_INTR_PLL_UNLOCKED	BIT(24)
+#define DP_INTR_AUX_ERROR	BIT(27)
+
+#define DP_INTR_READY_FOR_VIDEO		BIT(0)
+#define DP_INTR_IDLE_PATTERN_SENT	BIT(3)
+#define DP_INTR_FRAME_END		BIT(6)
+#define DP_INTR_CRC_UPDATED		BIT(9)
+
+struct dp_catalog_aux {
+	u32 data;
+	u32 isr;
+
+	u32 (*read_data)(struct dp_catalog_aux *aux);
+	int (*write_data)(struct dp_catalog_aux *aux);
+	int (*write_trans)(struct dp_catalog_aux *aux);
+	int (*clear_trans)(struct dp_catalog_aux *aux, bool read);
+	void (*reset)(struct dp_catalog_aux *aux);
+	void (*enable)(struct dp_catalog_aux *aux, bool enable);
+	void (*update_aux_cfg)(struct dp_catalog_aux *aux,
+		struct dp_aux_cfg *cfg, enum dp_phy_aux_config_type type);
+	void (*setup)(struct dp_catalog_aux *aux,
+			struct dp_aux_cfg *aux_cfg);
+	void (*get_irq)(struct dp_catalog_aux *aux, bool cmd_busy);
+};
+
+struct dp_catalog_ctrl {
+	u32 dp_tu;
+	u32 valid_boundary;
+	u32 valid_boundary2;
+	u32 isr;
+
+	void (*state_ctrl)(struct dp_catalog_ctrl *ctrl, u32 state);
+	void (*config_ctrl)(struct dp_catalog_ctrl *ctrl, u32 config);
+	void (*lane_mapping)(struct dp_catalog_ctrl *ctrl);
+	void (*mainlink_ctrl)(struct dp_catalog_ctrl *ctrl, bool enable);
+	void (*config_misc)(struct dp_catalog_ctrl *ctrl, u32 cc, u32 tb);
+	void (*config_msa)(struct dp_catalog_ctrl *ctrl, u32 rate,
+				u32 stream_rate_khz, bool fixed_nvid);
+	void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern);
+	void (*reset)(struct dp_catalog_ctrl *ctrl);
+	void (*usb_reset)(struct dp_catalog_ctrl *ctrl, bool flip);
+	bool (*mainlink_ready)(struct dp_catalog_ctrl *ctrl);
+	void (*enable_irq)(struct dp_catalog_ctrl *ctrl, bool enable);
+	void (*hpd_config)(struct dp_catalog_ctrl *ctrl, bool enable);
+	void (*phy_reset)(struct dp_catalog_ctrl *ctrl);
+	void (*phy_lane_cfg)(struct dp_catalog_ctrl *ctrl, bool flipped,
+				u8 lane_cnt);
+	void (*update_vx_px)(struct dp_catalog_ctrl *ctrl, u8 v_level,
+				u8 p_level);
+	void (*get_interrupt)(struct dp_catalog_ctrl *ctrl);
+	void (*update_transfer_unit)(struct dp_catalog_ctrl *ctrl);
+	void (*send_phy_pattern)(struct dp_catalog_ctrl *ctrl,
+			u32 pattern);
+	u32 (*read_phy_pattern)(struct dp_catalog_ctrl *ctrl);
+};
+
+enum dp_catalog_audio_sdp_type {
+	DP_AUDIO_SDP_STREAM,
+	DP_AUDIO_SDP_TIMESTAMP,
+	DP_AUDIO_SDP_INFOFRAME,
+	DP_AUDIO_SDP_COPYMANAGEMENT,
+	DP_AUDIO_SDP_ISRC,
+	DP_AUDIO_SDP_MAX,
+};
+
+enum dp_catalog_audio_header_type {
+	DP_AUDIO_SDP_HEADER_1,
+	DP_AUDIO_SDP_HEADER_2,
+	DP_AUDIO_SDP_HEADER_3,
+	DP_AUDIO_SDP_HEADER_MAX,
+};
+
+struct dp_catalog_audio {
+	enum dp_catalog_audio_sdp_type sdp_type;
+	enum dp_catalog_audio_header_type sdp_header;
+	u32 data;
+
+	void (*init)(struct dp_catalog_audio *audio);
+	void (*enable)(struct dp_catalog_audio *audio);
+	void (*config_acr)(struct dp_catalog_audio *audio);
+	void (*config_sdp)(struct dp_catalog_audio *audio);
+	void (*set_header)(struct dp_catalog_audio *audio);
+	void (*get_header)(struct dp_catalog_audio *audio);
+	void (*safe_to_exit_level)(struct dp_catalog_audio *audio);
+};
+
+struct dp_catalog_panel {
+	u32 total;
+	u32 sync_start;
+	u32 width_blanking;
+	u32 dp_active;
+
+	/* TPG */
+	u32 hsync_period;
+	u32 vsync_period;
+	u32 display_v_start;
+	u32 display_v_end;
+	u32 v_sync_width;
+	u32 hsync_ctl;
+	u32 display_hctl;
+
+	int (*timing_cfg)(struct dp_catalog_panel *panel);
+	void (*tpg_config)(struct dp_catalog_panel *panel, bool enable);
+};
+
+struct dp_catalog {
+	struct dp_catalog_aux aux;
+	struct dp_catalog_ctrl ctrl;
+	struct dp_catalog_audio audio;
+	struct dp_catalog_panel panel;
+};
+
+struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io);
+void dp_catalog_put(struct dp_catalog *catalog);
+
+#endif /* _DP_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
new file mode 100644
index 0000000..08a52f5
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -0,0 +1,1475 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+
+#include "dp_ctrl.h"
+
+#define DP_KHZ_TO_HZ 1000
+
+#define DP_CTRL_INTR_READY_FOR_VIDEO     BIT(0)
+#define DP_CTRL_INTR_IDLE_PATTERN_SENT  BIT(3)
+
+/* dp state ctrl */
+#define ST_TRAIN_PATTERN_1		BIT(0)
+#define ST_TRAIN_PATTERN_2		BIT(1)
+#define ST_TRAIN_PATTERN_3		BIT(2)
+#define ST_TRAIN_PATTERN_4		BIT(3)
+#define ST_SYMBOL_ERR_RATE_MEASUREMENT	BIT(4)
+#define ST_PRBS7			BIT(5)
+#define ST_CUSTOM_80_BIT_PATTERN	BIT(6)
+#define ST_SEND_VIDEO			BIT(7)
+#define ST_PUSH_IDLE			BIT(8)
+
+#define MR_LINK_TRAINING1  0x8
+#define MR_LINK_SYMBOL_ERM 0x80
+#define MR_LINK_PRBS7 0x100
+#define MR_LINK_CUSTOM80 0x200
+
+struct dp_vc_tu_mapping_table {
+	u32 vic;
+	u8 lanes;
+	u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
+	u8 bpp;
+	u8 valid_boundary_link;
+	u16 delay_start_link;
+	bool boundary_moderation_en;
+	u8 valid_lower_boundary_link;
+	u8 upper_boundary_count;
+	u8 lower_boundary_count;
+	u8 tu_size_minus1;
+};
+
+struct dp_ctrl_private {
+	struct dp_ctrl dp_ctrl;
+
+	struct device *dev;
+	struct dp_aux *aux;
+	struct dp_panel *panel;
+	struct dp_link *link;
+	struct dp_power *power;
+	struct dp_parser *parser;
+	struct dp_catalog_ctrl *catalog;
+
+	struct completion idle_comp;
+	struct completion video_comp;
+
+	bool orientation;
+	atomic_t aborted;
+
+	u32 pixel_rate;
+	u32 vic;
+};
+
+enum notification_status {
+	NOTIFY_UNKNOWN,
+	NOTIFY_CONNECT,
+	NOTIFY_DISCONNECT,
+	NOTIFY_CONNECT_IRQ_HPD,
+	NOTIFY_DISCONNECT_IRQ_HPD,
+};
+
+static void dp_ctrl_idle_patterns_sent(struct dp_ctrl_private *ctrl)
+{
+	pr_debug("idle_patterns_sent\n");
+	complete(&ctrl->idle_comp);
+}
+
+static void dp_ctrl_video_ready(struct dp_ctrl_private *ctrl)
+{
+	pr_debug("dp_video_ready\n");
+	complete(&ctrl->video_comp);
+}
+
+static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		pr_err("Invalid input data\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	atomic_set(&ctrl->aborted, 1);
+}
+
+static void dp_ctrl_state_ctrl(struct dp_ctrl_private *ctrl, u32 state)
+{
+	ctrl->catalog->state_ctrl(ctrl->catalog, state);
+}
+
+static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
+{
+	int const idle_pattern_completion_timeout_ms = 3 * HZ / 100;
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		pr_err("Invalid input data\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	reinit_completion(&ctrl->idle_comp);
+	dp_ctrl_state_ctrl(ctrl, ST_PUSH_IDLE);
+
+	if (!wait_for_completion_timeout(&ctrl->idle_comp,
+			idle_pattern_completion_timeout_ms))
+		pr_warn("PUSH_IDLE pattern timedout\n");
+
+	pr_debug("mainlink off done\n");
+}
+
+static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
+{
+	u32 config = 0, tbd;
+	u8 *dpcd = ctrl->panel->dpcd;
+
+	config |= (2 << 13); /* Default-> LSCLK DIV: 1/4 LCLK  */
+	config |= (0 << 11); /* RGB */
+
+	/* Scrambler reset enable */
+	if (dpcd[DP_EDP_CONFIGURATION_CAP] & DP_ALTERNATE_SCRAMBLER_RESET_CAP)
+		config |= (1 << 10);
+
+	tbd = ctrl->link->get_test_bits_depth(ctrl->link,
+			ctrl->panel->pinfo.bpp);
+
+	if (tbd == DP_TEST_BIT_DEPTH_UNKNOWN)
+		tbd = DP_TEST_BIT_DEPTH_8;
+
+	config |= tbd << 8;
+
+	/* Num of Lanes */
+	config |= ((ctrl->link->link_params.lane_count - 1) << 4);
+
+	if (drm_dp_enhanced_frame_cap(dpcd))
+		config |= 0x40;
+
+	config |= 0x04; /* progressive video */
+
+	config |= 0x03;	/* sycn clock & static Mvid */
+
+	ctrl->catalog->config_ctrl(ctrl->catalog, config);
+}
+
+/**
+ * dp_ctrl_configure_source_params() - configures DP transmitter source params
+ * @ctrl: Display Port Driver data
+ *
+ * Configures the DP transmitter source params including details such as lane
+ * configuration, output format and sink/panel timing information.
+ */
+static void dp_ctrl_configure_source_params(struct dp_ctrl_private *ctrl)
+{
+	u32 cc, tb;
+
+	ctrl->catalog->lane_mapping(ctrl->catalog);
+	ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
+
+	dp_ctrl_config_ctrl(ctrl);
+
+	tb = ctrl->link->get_test_bits_depth(ctrl->link,
+		ctrl->panel->pinfo.bpp);
+	cc = ctrl->link->get_colorimetry_config(ctrl->link);
+	ctrl->catalog->config_misc(ctrl->catalog, cc, tb);
+	ctrl->panel->timing_cfg(ctrl->panel);
+}
+
+static void dp_ctrl_get_extra_req_bytes(u64 result_valid,
+					int valid_bdary_link,
+					u64 value1, u64 value2,
+					bool *negative, u64 *result,
+					u64 compare)
+{
+	*negative = false;
+	if (result_valid >= compare) {
+		if (valid_bdary_link
+				>= compare)
+			*result = value1 + value2;
+		else {
+			if (value1 < value2)
+				*negative = true;
+			*result = (value1 >= value2) ?
+				(value1 - value2) : (value2 - value1);
+		}
+	} else {
+		if (valid_bdary_link
+				>= compare) {
+			if (value1 >= value2)
+				*negative = true;
+			*result = (value1 >= value2) ?
+				(value1 - value2) : (value2 - value1);
+		} else {
+			*result = value1 + value2;
+			*negative = true;
+		}
+	}
+}
+
+static u64 roundup_u64(u64 x, u64 y)
+{
+	x += (y - 1);
+	return (div64_ul(x, y) * y);
+}
+
+static u64 rounddown_u64(u64 x, u64 y)
+{
+	u64 rem;
+
+	div64_u64_rem(x, y, &rem);
+	return (x - rem);
+}
+
+static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
+				       struct dp_vc_tu_mapping_table *tu_table)
+{
+	u32 multiplier = 1000000;
+	u64 pclk, lclk;
+	u8 bpp, ln_cnt;
+	int run_idx = 0;
+	u32 lwidth, h_blank;
+	u32 fifo_empty = 0;
+	u32 ratio_scale = 1001;
+	u64 temp, ratio, original_ratio;
+	u64 temp2, reminder;
+	u64 temp3, temp4, result = 0;
+
+	u64 err = multiplier;
+	u64 n_err = 0, n_n_err = 0;
+	bool n_err_neg, nn_err_neg;
+	u8 hblank_margin = 16;
+
+	u8 tu_size, tu_size_desired = 0, tu_size_minus1;
+	int valid_boundary_link;
+	u64 resulting_valid;
+	u64 total_valid;
+	u64 effective_valid;
+	u64 effective_valid_recorded;
+	int n_tus;
+	int n_tus_per_lane;
+	int paired_tus;
+	int remainder_tus;
+	int remainder_tus_upper, remainder_tus_lower;
+	int extra_bytes;
+	int filler_size;
+	int delay_start_link;
+	int boundary_moderation_en = 0;
+	int upper_bdry_cnt = 0;
+	int lower_bdry_cnt = 0;
+	int i_upper_bdry_cnt = 0;
+	int i_lower_bdry_cnt = 0;
+	int valid_lower_boundary_link = 0;
+	int even_distribution_bf = 0;
+	int even_distribution_legacy = 0;
+	int even_distribution = 0;
+	int min_hblank = 0;
+	int extra_pclk_cycles;
+	u8 extra_pclk_cycle_delay = 4;
+	int extra_pclk_cycles_in_link_clk;
+	u64 ratio_by_tu;
+	u64 average_valid2;
+	u64 extra_buffer_margin;
+	int new_valid_boundary_link;
+
+	u64 resulting_valid_tmp;
+	u64 ratio_by_tu_tmp;
+	int n_tus_tmp;
+	int extra_pclk_cycles_tmp;
+	int extra_pclk_cycles_in_lclk_tmp;
+	int extra_req_bytes_new_tmp;
+	int filler_size_tmp;
+	int lower_filler_size_tmp;
+	int delay_start_link_tmp;
+	int min_hblank_tmp = 0;
+	bool extra_req_bytes_is_neg = false;
+	struct dp_panel_info *pinfo = &ctrl->panel->pinfo;
+
+	u8 dp_brute_force = 1;
+	u64 brute_force_threshold = 10;
+	u64 diff_abs;
+
+	ln_cnt =  ctrl->link->link_params.lane_count;
+
+	bpp = pinfo->bpp;
+	lwidth = pinfo->h_active;
+	h_blank = pinfo->h_back_porch + pinfo->h_front_porch +
+				pinfo->h_sync_width;
+	pclk = pinfo->pixel_clk_khz * 1000;
+
+	boundary_moderation_en = 0;
+	upper_bdry_cnt = 0;
+	lower_bdry_cnt = 0;
+	i_upper_bdry_cnt = 0;
+	i_lower_bdry_cnt = 0;
+	valid_lower_boundary_link = 0;
+	even_distribution_bf = 0;
+	even_distribution_legacy = 0;
+	even_distribution = 0;
+	min_hblank = 0;
+
+	lclk = drm_dp_bw_code_to_link_rate(
+		ctrl->link->link_params.bw_code) * DP_KHZ_TO_HZ;
+
+	pr_debug("pclk=%lld, active_width=%d, h_blank=%d\n",
+						pclk, lwidth, h_blank);
+	pr_debug("lclk = %lld, ln_cnt = %d\n", lclk, ln_cnt);
+	ratio = div64_u64_rem(pclk * bpp * multiplier,
+				8 * ln_cnt * lclk, &reminder);
+	ratio = div64_u64((pclk * bpp * multiplier), (8 * ln_cnt * lclk));
+	original_ratio = ratio;
+
+	extra_buffer_margin = roundup_u64(div64_u64(extra_pclk_cycle_delay
+				* lclk * multiplier, pclk), multiplier);
+	extra_buffer_margin = div64_u64(extra_buffer_margin, multiplier);
+
+	/* To deal with cases where lines are not distributable */
+	if (((lwidth % ln_cnt) != 0) && ratio < multiplier) {
+		ratio = ratio * ratio_scale;
+		ratio = ratio < (1000 * multiplier)
+				? ratio : (1000 * multiplier);
+	}
+	pr_debug("ratio = %lld\n", ratio);
+
+	for (tu_size = 32; tu_size <= 64; tu_size++) {
+		temp = ratio * tu_size;
+		temp2 = ((temp / multiplier) + 1) * multiplier;
+		n_err = roundup_u64(temp, multiplier) - temp;
+
+		if (n_err < err) {
+			err = n_err;
+			tu_size_desired = tu_size;
+		}
+	}
+	pr_debug("Info: tu_size_desired = %d\n", tu_size_desired);
+
+	tu_size_minus1 = tu_size_desired - 1;
+
+	valid_boundary_link = roundup_u64(ratio * tu_size_desired, multiplier);
+	valid_boundary_link /= multiplier;
+	n_tus = rounddown((lwidth * bpp * multiplier)
+			/ (8 * valid_boundary_link), multiplier) / multiplier;
+	even_distribution_legacy = n_tus % ln_cnt == 0 ? 1 : 0;
+	pr_debug("Info: n_symbol_per_tu=%d, number_of_tus=%d\n",
+					valid_boundary_link, n_tus);
+
+	extra_bytes = roundup_u64((n_tus + 1)
+			* ((valid_boundary_link * multiplier)
+			- (original_ratio * tu_size_desired)), multiplier);
+	extra_bytes /= multiplier;
+	extra_pclk_cycles = roundup(extra_bytes * 8 * multiplier / bpp,
+			multiplier);
+	extra_pclk_cycles /= multiplier;
+	extra_pclk_cycles_in_link_clk = roundup_u64(div64_u64(extra_pclk_cycles
+				* lclk * multiplier, pclk), multiplier);
+	extra_pclk_cycles_in_link_clk /= multiplier;
+	filler_size = roundup_u64((tu_size_desired - valid_boundary_link)
+						* multiplier, multiplier);
+	filler_size /= multiplier;
+	ratio_by_tu = div64_u64(ratio * tu_size_desired, multiplier);
+
+	pr_debug("extra_pclk_cycles_in_link_clk=%d, extra_bytes=%d\n",
+				extra_pclk_cycles_in_link_clk, extra_bytes);
+	pr_debug("extra_pclk_cycles_in_link_clk=%d\n",
+				extra_pclk_cycles_in_link_clk);
+	pr_debug("filler_size=%d, extra_buffer_margin=%lld\n",
+				filler_size, extra_buffer_margin);
+
+	delay_start_link = ((extra_bytes > extra_pclk_cycles_in_link_clk)
+			? extra_bytes
+			: extra_pclk_cycles_in_link_clk)
+				+ filler_size + extra_buffer_margin;
+	resulting_valid = valid_boundary_link;
+	pr_debug("Info: delay_start_link=%d, filler_size=%d\n",
+				delay_start_link, filler_size);
+	pr_debug("valid_boundary_link=%d ratio_by_tu=%lld\n",
+				valid_boundary_link, ratio_by_tu);
+
+	diff_abs = (resulting_valid >= ratio_by_tu)
+				? (resulting_valid - ratio_by_tu)
+				: (ratio_by_tu - resulting_valid);
+
+	if (err != 0 && ((diff_abs > brute_force_threshold)
+			|| (even_distribution_legacy == 0)
+			|| (dp_brute_force == 1))) {
+		err = multiplier;
+		for (tu_size = 32; tu_size <= 64; tu_size++) {
+			for (i_upper_bdry_cnt = 1; i_upper_bdry_cnt <= 15;
+						i_upper_bdry_cnt++) {
+				for (i_lower_bdry_cnt = 1;
+					i_lower_bdry_cnt <= 15;
+					i_lower_bdry_cnt++) {
+					new_valid_boundary_link =
+						roundup_u64(ratio
+						* tu_size, multiplier);
+					average_valid2 = (i_upper_bdry_cnt
+						* new_valid_boundary_link
+						+ i_lower_bdry_cnt
+						* (new_valid_boundary_link
+							- multiplier))
+						/ (i_upper_bdry_cnt
+							+ i_lower_bdry_cnt);
+					n_tus = rounddown_u64(div64_u64(lwidth
+						* multiplier * multiplier
+						* (bpp / 8), average_valid2),
+							multiplier);
+					n_tus /= multiplier;
+					n_tus_per_lane
+						= rounddown(n_tus
+							* multiplier
+							/ ln_cnt, multiplier);
+					n_tus_per_lane /= multiplier;
+					paired_tus =
+						rounddown((n_tus_per_lane)
+							* multiplier
+							/ (i_upper_bdry_cnt
+							+ i_lower_bdry_cnt),
+							multiplier);
+					paired_tus /= multiplier;
+					remainder_tus = n_tus_per_lane
+							- paired_tus
+						* (i_upper_bdry_cnt
+							+ i_lower_bdry_cnt);
+					if ((remainder_tus
+						- i_upper_bdry_cnt) > 0) {
+						remainder_tus_upper
+							= i_upper_bdry_cnt;
+						remainder_tus_lower =
+							remainder_tus
+							- i_upper_bdry_cnt;
+					} else {
+						remainder_tus_upper
+							= remainder_tus;
+						remainder_tus_lower = 0;
+					}
+					total_valid = paired_tus
+						* (i_upper_bdry_cnt
+						* new_valid_boundary_link
+							+ i_lower_bdry_cnt
+						* (new_valid_boundary_link
+							- multiplier))
+						+ (remainder_tus_upper
+						* new_valid_boundary_link)
+						+ (remainder_tus_lower
+						* (new_valid_boundary_link
+							- multiplier));
+					n_err_neg = nn_err_neg = false;
+					effective_valid
+						= div_u64(total_valid,
+							n_tus_per_lane);
+					n_n_err = (effective_valid
+							>= (ratio * tu_size))
+						? (effective_valid
+							- (ratio * tu_size))
+						: ((ratio * tu_size)
+							- effective_valid);
+					if (effective_valid < (ratio * tu_size))
+						nn_err_neg = true;
+					n_err = (average_valid2
+						>= (ratio * tu_size))
+						? (average_valid2
+							- (ratio * tu_size))
+						: ((ratio * tu_size)
+							- average_valid2);
+					if (average_valid2 < (ratio * tu_size))
+						n_err_neg = true;
+					even_distribution =
+						n_tus % ln_cnt == 0 ? 1 : 0;
+					diff_abs =
+						resulting_valid >= ratio_by_tu
+						? (resulting_valid
+							- ratio_by_tu)
+						: (ratio_by_tu
+							- resulting_valid);
+
+					resulting_valid_tmp = div64_u64(
+						(i_upper_bdry_cnt
+						* new_valid_boundary_link
+						+ i_lower_bdry_cnt
+						* (new_valid_boundary_link
+							- multiplier)),
+						(i_upper_bdry_cnt
+							+ i_lower_bdry_cnt));
+					ratio_by_tu_tmp =
+						original_ratio * tu_size;
+					ratio_by_tu_tmp /= multiplier;
+					n_tus_tmp = rounddown_u64(
+						div64_u64(lwidth
+						* multiplier * multiplier
+						* bpp / 8,
+						resulting_valid_tmp),
+						multiplier);
+					n_tus_tmp /= multiplier;
+
+					temp3 = (resulting_valid_tmp
+						>= (original_ratio * tu_size))
+						? (resulting_valid_tmp
+						- original_ratio * tu_size)
+						: (original_ratio * tu_size)
+						- resulting_valid_tmp;
+					temp3 = (n_tus_tmp + 1) * temp3;
+					temp4 = (new_valid_boundary_link
+						>= (original_ratio * tu_size))
+						? (new_valid_boundary_link
+							- original_ratio
+							* tu_size)
+						: (original_ratio * tu_size)
+						- new_valid_boundary_link;
+					temp4 = (i_upper_bdry_cnt
+							* ln_cnt * temp4);
+
+					temp3 = roundup_u64(temp3, multiplier);
+					temp4 = roundup_u64(temp4, multiplier);
+					dp_ctrl_get_extra_req_bytes
+						(resulting_valid_tmp,
+						new_valid_boundary_link,
+						temp3, temp4,
+						&extra_req_bytes_is_neg,
+						&result,
+						(original_ratio * tu_size));
+					extra_req_bytes_new_tmp
+						= div64_ul(result, multiplier);
+					if ((extra_req_bytes_is_neg)
+						&& (extra_req_bytes_new_tmp
+							> 1))
+						extra_req_bytes_new_tmp
+						= extra_req_bytes_new_tmp - 1;
+					if (extra_req_bytes_new_tmp == 0)
+						extra_req_bytes_new_tmp = 1;
+					extra_pclk_cycles_tmp =
+						(u64)(extra_req_bytes_new_tmp
+						      * 8 * multiplier) / bpp;
+					extra_pclk_cycles_tmp /= multiplier;
+
+					if (extra_pclk_cycles_tmp <= 0)
+						extra_pclk_cycles_tmp = 1;
+					extra_pclk_cycles_in_lclk_tmp =
+						roundup_u64(div64_u64(
+							extra_pclk_cycles_tmp
+							* lclk * multiplier,
+							pclk), multiplier);
+					extra_pclk_cycles_in_lclk_tmp
+						/= multiplier;
+					filler_size_tmp = roundup_u64(
+						(tu_size * multiplier *
+						new_valid_boundary_link),
+						multiplier);
+					filler_size_tmp /= multiplier;
+					lower_filler_size_tmp =
+						filler_size_tmp + 1;
+					if (extra_req_bytes_is_neg)
+						temp3 = (extra_req_bytes_new_tmp
+						> extra_pclk_cycles_in_lclk_tmp
+						? extra_pclk_cycles_in_lclk_tmp
+						: extra_req_bytes_new_tmp);
+					else
+						temp3 = (extra_req_bytes_new_tmp
+						> extra_pclk_cycles_in_lclk_tmp
+						? extra_req_bytes_new_tmp :
+						extra_pclk_cycles_in_lclk_tmp);
+
+					temp4 = lower_filler_size_tmp
+						+ extra_buffer_margin;
+					if (extra_req_bytes_is_neg)
+						delay_start_link_tmp
+							= (temp3 >= temp4)
+							? (temp3 - temp4)
+							: (temp4 - temp3);
+					else
+						delay_start_link_tmp
+							= temp3 + temp4;
+
+					min_hblank_tmp = (int)div64_u64(
+						roundup_u64(
+						div64_u64(delay_start_link_tmp
+						* pclk * multiplier, lclk),
+						multiplier), multiplier)
+						+ hblank_margin;
+
+					if (((even_distribution == 1)
+						|| ((even_distribution_bf == 0)
+						&& (even_distribution_legacy
+								== 0)))
+						&& !n_err_neg && !nn_err_neg
+						&& n_n_err < err
+						&& (n_n_err < diff_abs
+						|| (dp_brute_force == 1))
+						&& (new_valid_boundary_link
+									- 1) > 0
+						&& (h_blank >=
+							(u32)min_hblank_tmp)) {
+						upper_bdry_cnt =
+							i_upper_bdry_cnt;
+						lower_bdry_cnt =
+							i_lower_bdry_cnt;
+						err = n_n_err;
+						boundary_moderation_en = 1;
+						tu_size_desired = tu_size;
+						valid_boundary_link =
+							new_valid_boundary_link;
+						effective_valid_recorded
+							= effective_valid;
+						delay_start_link
+							= delay_start_link_tmp;
+						filler_size = filler_size_tmp;
+						min_hblank = min_hblank_tmp;
+						n_tus = n_tus_tmp;
+						even_distribution_bf = 1;
+
+						pr_debug("upper_bdry_cnt=%d, lower_boundary_cnt=%d, err=%lld, tu_size_desired=%d, valid_boundary_link=%d, effective_valid=%lld\n",
+							upper_bdry_cnt,
+							lower_bdry_cnt, err,
+							tu_size_desired,
+							valid_boundary_link,
+							effective_valid);
+					}
+				}
+			}
+		}
+
+		if (boundary_moderation_en == 1) {
+			resulting_valid = (u64)(upper_bdry_cnt
+					*valid_boundary_link + lower_bdry_cnt
+					* (valid_boundary_link - 1))
+					/ (upper_bdry_cnt + lower_bdry_cnt);
+			ratio_by_tu = original_ratio * tu_size_desired;
+			valid_lower_boundary_link =
+				(valid_boundary_link / multiplier) - 1;
+
+			tu_size_minus1 = tu_size_desired - 1;
+			even_distribution_bf = 1;
+			valid_boundary_link /= multiplier;
+			pr_debug("Info: Boundary_moderation enabled\n");
+		}
+	}
+
+	min_hblank = ((int) roundup_u64(div64_u64(delay_start_link * pclk
+			* multiplier, lclk), multiplier))
+			/ multiplier + hblank_margin;
+	if (h_blank < (u32)min_hblank) {
+		pr_debug(" WARNING: run_idx=%d Programmed h_blank %d is smaller than the min_hblank %d supported.\n",
+					run_idx, h_blank, min_hblank);
+	}
+
+	if (fifo_empty)	{
+		tu_size_minus1 = 31;
+		valid_boundary_link = 32;
+		delay_start_link = 0;
+		boundary_moderation_en = 0;
+	}
+
+	pr_debug("tu_size_minus1=%d valid_boundary_link=%d delay_start_link=%d boundary_moderation_en=%d\n upper_boundary_cnt=%d lower_boundary_cnt=%d valid_lower_boundary_link=%d min_hblank=%d\n",
+		tu_size_minus1, valid_boundary_link, delay_start_link,
+		boundary_moderation_en, upper_bdry_cnt, lower_bdry_cnt,
+		valid_lower_boundary_link, min_hblank);
+
+	tu_table->valid_boundary_link = valid_boundary_link;
+	tu_table->delay_start_link = delay_start_link;
+	tu_table->boundary_moderation_en = boundary_moderation_en;
+	tu_table->valid_lower_boundary_link = valid_lower_boundary_link;
+	tu_table->upper_boundary_count = upper_bdry_cnt;
+	tu_table->lower_boundary_count = lower_bdry_cnt;
+	tu_table->tu_size_minus1 = tu_size_minus1;
+}
+
+static void dp_ctrl_setup_tr_unit(struct dp_ctrl_private *ctrl)
+{
+	u32 dp_tu = 0x0;
+	u32 valid_boundary = 0x0;
+	u32 valid_boundary2 = 0x0;
+	struct dp_vc_tu_mapping_table tu_calc_table;
+
+	dp_ctrl_calc_tu_parameters(ctrl, &tu_calc_table);
+
+	dp_tu |= tu_calc_table.tu_size_minus1;
+	valid_boundary |= tu_calc_table.valid_boundary_link;
+	valid_boundary |= (tu_calc_table.delay_start_link << 16);
+
+	valid_boundary2 |= (tu_calc_table.valid_lower_boundary_link << 1);
+	valid_boundary2 |= (tu_calc_table.upper_boundary_count << 16);
+	valid_boundary2 |= (tu_calc_table.lower_boundary_count << 20);
+
+	if (tu_calc_table.boundary_moderation_en)
+		valid_boundary2 |= BIT(0);
+
+	pr_debug("dp_tu=0x%x, valid_boundary=0x%x, valid_boundary2=0x%x\n",
+			dp_tu, valid_boundary, valid_boundary2);
+
+	ctrl->catalog->dp_tu = dp_tu;
+	ctrl->catalog->valid_boundary = valid_boundary;
+	ctrl->catalog->valid_boundary2 = valid_boundary2;
+
+	ctrl->catalog->update_transfer_unit(ctrl->catalog);
+}
+
+static int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	ret = wait_for_completion_timeout(&ctrl->video_comp, HZ / 2);
+	if (ret <= 0) {
+		pr_err("Link Train timedout\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int dp_ctrl_update_sink_vx_px(struct dp_ctrl_private *ctrl,
+				     u32 voltage_level, u32 pre_emphasis_level)
+{
+	int i;
+	u8 buf[4];
+	u32 max_level_reached = 0;
+
+	if (voltage_level == DP_LINK_VOLTAGE_MAX) {
+		pr_debug("max. voltage swing level reached %d\n",
+				voltage_level);
+		max_level_reached |= BIT(2);
+	}
+
+	if (pre_emphasis_level == DP_LINK_PRE_EMPHASIS_MAX) {
+		pr_debug("max. pre-emphasis level reached %d\n",
+				pre_emphasis_level);
+		max_level_reached  |= BIT(5);
+	}
+
+	pre_emphasis_level <<= 3;
+
+	for (i = 0; i < 4; i++)
+		buf[i] = voltage_level | pre_emphasis_level | max_level_reached;
+
+	pr_debug("sink: p|v=0x%x\n", voltage_level | pre_emphasis_level);
+	return drm_dp_dpcd_write(ctrl->aux->drm_aux, 0x103, buf, 4);
+}
+
+static void dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
+{
+	struct dp_link *link = ctrl->link;
+
+	ctrl->catalog->update_vx_px(ctrl->catalog,
+		link->phy_params.v_level, link->phy_params.p_level);
+
+	dp_ctrl_update_sink_vx_px(ctrl, link->phy_params.v_level,
+		link->phy_params.p_level);
+}
+
+static void dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
+		u8 pattern)
+{
+	u8 buf[4];
+
+	pr_debug("sink: pattern=%x\n", pattern);
+
+	buf[0] = pattern;
+	drm_dp_dpcd_write(ctrl->aux->drm_aux, DP_TRAINING_PATTERN_SET, buf, 1);
+}
+
+static int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
+				    u8 *link_status)
+{
+	int ret = 0, len;
+	u32 const offset = DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS;
+	u32 link_status_read_max_retries = 100;
+
+	while (--link_status_read_max_retries) {
+		len = drm_dp_dpcd_read_link_status(ctrl->aux->drm_aux,
+			link_status);
+		if (len != DP_LINK_STATUS_SIZE) {
+			pr_err("DP link status read failed, err: %d\n", len);
+			ret = len;
+			break;
+		}
+
+		if (!(link_status[offset] & DP_LINK_STATUS_UPDATED))
+			break;
+	}
+
+	return ret;
+}
+
+static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)
+{
+	int tries, old_v_level, ret = 0;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	int const maximum_retries = 5;
+
+	dp_ctrl_state_ctrl(ctrl, 0);
+	/* Make sure to clear the current pattern before starting a new one */
+	wmb();
+
+	ctrl->catalog->set_pattern(ctrl->catalog, 0x01);
+	dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
+		DP_LINK_SCRAMBLING_DISABLE); /* train_1 */
+	dp_ctrl_update_vx_px(ctrl);
+
+	tries = 0;
+	old_v_level = ctrl->link->phy_params.v_level;
+	while (1) {
+		drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
+
+		ret = dp_ctrl_read_link_status(ctrl, link_status);
+		if (ret)
+			break;
+
+		if (drm_dp_clock_recovery_ok(link_status,
+			ctrl->link->link_params.lane_count)) {
+			break;
+		}
+
+		if (ctrl->link->phy_params.v_level == DP_LINK_VOLTAGE_MAX) {
+			pr_err_ratelimited("max v_level reached\n");
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (old_v_level == ctrl->link->phy_params.v_level) {
+			tries++;
+			if (tries >= maximum_retries) {
+				pr_err("max tries reached\n");
+				ret = -ETIMEDOUT;
+				break;
+			}
+		} else {
+			tries = 0;
+			old_v_level = ctrl->link->phy_params.v_level;
+		}
+
+		pr_debug("clock recovery not done, adjusting vx px\n");
+
+		ctrl->link->adjust_levels(ctrl->link, link_status);
+		dp_ctrl_update_vx_px(ctrl);
+	}
+
+	return ret;
+}
+
+static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	if (!ctrl)
+		return -EINVAL;
+
+	switch (ctrl->link->link_params.bw_code) {
+	case DP_LINK_BW_8_1:
+		ctrl->link->link_params.bw_code = DP_LINK_BW_5_4;
+		break;
+	case DP_LINK_BW_5_4:
+		ctrl->link->link_params.bw_code = DP_LINK_BW_2_7;
+		break;
+	case DP_LINK_BW_2_7:
+	case DP_LINK_BW_1_62:
+	default:
+		ctrl->link->link_params.bw_code = DP_LINK_BW_1_62;
+		break;
+	};
+
+	pr_debug("new bw code=0x%x\n", ctrl->link->link_params.bw_code);
+
+	return ret;
+}
+
+static void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
+{
+	dp_ctrl_train_pattern_set(ctrl, 0);
+	drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
+}
+
+static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
+{
+	int tries = 0, ret = 0;
+	char pattern;
+	int const maximum_retries = 5;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+
+	dp_ctrl_state_ctrl(ctrl, 0);
+	/* Make sure to clear the current pattern before starting a new one */
+	wmb();
+
+	if (drm_dp_tps3_supported(ctrl->panel->dpcd))
+		pattern = DP_TRAINING_PATTERN_3;
+	else
+		pattern = DP_TRAINING_PATTERN_2;
+
+	dp_ctrl_update_vx_px(ctrl);
+	ctrl->catalog->set_pattern(ctrl->catalog, pattern);
+	dp_ctrl_train_pattern_set(ctrl, pattern | DP_RECOVERED_CLOCK_OUT_EN);
+
+	do  {
+		drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
+
+		ret = dp_ctrl_read_link_status(ctrl, link_status);
+		if (ret)
+			break;
+
+		if (drm_dp_channel_eq_ok(link_status,
+			ctrl->link->link_params.lane_count))
+			break;
+
+		if (tries > maximum_retries) {
+			ret = -ETIMEDOUT;
+			break;
+		}
+		tries++;
+
+		ctrl->link->adjust_levels(ctrl->link, link_status);
+		dp_ctrl_update_vx_px(ctrl);
+	} while (1);
+
+	return ret;
+}
+
+static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+	u8 encoding = 0x1;
+	struct drm_dp_link link_info = {0};
+
+	ctrl->link->phy_params.p_level = 0;
+	ctrl->link->phy_params.v_level = 0;
+
+	dp_ctrl_config_ctrl(ctrl);
+
+	link_info.num_lanes = ctrl->link->link_params.lane_count;
+	link_info.rate = drm_dp_bw_code_to_link_rate(
+		ctrl->link->link_params.bw_code);
+	link_info.capabilities = ctrl->panel->link_info.capabilities;
+
+	drm_dp_link_configure(ctrl->aux->drm_aux, &link_info);
+	drm_dp_dpcd_write(ctrl->aux->drm_aux, DP_MAIN_LINK_CHANNEL_CODING_SET,
+				&encoding, 1);
+
+	ret = dp_ctrl_link_train_1(ctrl);
+	if (ret) {
+		pr_err("link training #1 failed\n");
+		goto end;
+	}
+
+	/* print success info as this is a result of user initiated action */
+	pr_info("link training #1 successful\n");
+
+	ret = dp_ctrl_link_training_2(ctrl);
+	if (ret) {
+		pr_err("link training #2 failed\n");
+		goto end;
+	}
+
+	/* print success info as this is a result of user initiated action */
+	pr_info("link training #2 successful\n");
+
+end:
+	dp_ctrl_state_ctrl(ctrl, 0);
+	/* Make sure to clear the current pattern before starting a new one */
+	wmb();
+
+	dp_ctrl_clear_training_pattern(ctrl);
+	return ret;
+}
+
+static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool train)
+{
+	bool mainlink_ready = false;
+	int ret = 0;
+
+	ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
+
+	ret = ctrl->link->psm_config(ctrl->link,
+		&ctrl->panel->link_info, false);
+	if (ret)
+		goto end;
+
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
+		goto end;
+
+	if (!train)
+		goto send_video;
+
+	/*
+	 * As part of previous calls, DP controller state might have
+	 * transitioned to PUSH_IDLE. In order to start transmitting a link
+	 * training pattern, we have to first to a DP software reset.
+	 */
+	ctrl->catalog->reset(ctrl->catalog);
+
+	ret = dp_ctrl_link_train(ctrl);
+	if (ret)
+		goto end;
+
+send_video:
+	/*
+	 * Set up transfer unit values and set controller state to send
+	 * video.
+	 */
+	dp_ctrl_setup_tr_unit(ctrl);
+	ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO);
+
+	dp_ctrl_wait4video_ready(ctrl);
+	mainlink_ready = ctrl->catalog->mainlink_ready(ctrl->catalog);
+	pr_debug("mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
+end:
+	return ret;
+}
+
+static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
+				   char *name, u32 rate)
+{
+	u32 num = ctrl->parser->mp[DP_CTRL_PM].num_clk;
+	struct dss_clk *cfg = ctrl->parser->mp[DP_CTRL_PM].clk_config;
+
+	while (num && strcmp(cfg->clk_name, name)) {
+		num--;
+		cfg++;
+	}
+
+	pr_debug("setting rate=%d on clk=%s\n", rate, name);
+
+	if (num)
+		cfg->rate = rate;
+	else
+		pr_err("%s clock could not be set with rate %d\n", name, rate);
+}
+
+static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	ctrl->power->set_pixel_clk_parent(ctrl->power);
+
+	dp_ctrl_set_clock_rate(ctrl, "ctrl_link_clk",
+		drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code));
+
+	dp_ctrl_set_clock_rate(ctrl, "ctrl_pixel_clk", ctrl->pixel_rate);
+
+	ret = ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, true);
+	if (ret) {
+		pr_err("Unabled to start link clocks\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int dp_ctrl_disable_mainlink_clocks(struct dp_ctrl_private *ctrl)
+{
+	return ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, false);
+}
+
+static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip)
+{
+	struct dp_ctrl_private *ctrl;
+	struct dp_catalog_ctrl *catalog;
+
+	if (!dp_ctrl) {
+		pr_err("Invalid input data\n");
+		return -EINVAL;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->orientation = flip;
+	catalog = ctrl->catalog;
+
+	catalog->usb_reset(ctrl->catalog, flip);
+	catalog->phy_reset(ctrl->catalog);
+	catalog->enable_irq(ctrl->catalog, true);
+
+	return 0;
+}
+
+/**
+ * dp_ctrl_host_deinit() - Uninitialize DP controller
+ * @ctrl: Display Port Driver data
+ *
+ * Perform required steps to uninitialize DP controller
+ * and its resources.
+ */
+static void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		pr_err("Invalid input data\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->catalog->enable_irq(ctrl->catalog, false);
+
+	pr_debug("Host deinitialized successfully\n");
+}
+
+static bool dp_ctrl_use_fixed_nvid(struct dp_ctrl_private *ctrl)
+{
+	u8 *dpcd = ctrl->panel->dpcd;
+
+	/*
+	 * For better interop experience, used a fixed NVID=0x8000
+	 * whenever connected to a VGA dongle downstream.
+	 */
+	if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT) {
+		u8 type = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+			DP_DWN_STRM_PORT_TYPE_MASK;
+		if (type == DP_DWN_STRM_PORT_TYPE_ANALOG)
+			return true;
+	}
+
+	return false;
+}
+
+static int dp_ctrl_link_maintenance(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
+	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
+
+	ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+
+	do {
+		if (ret == -EAGAIN) {
+			/* try with lower link rate */
+			dp_ctrl_link_rate_down_shift(ctrl);
+
+			ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+		}
+
+		ctrl->catalog->phy_lane_cfg(ctrl->catalog,
+			ctrl->orientation, ctrl->link->link_params.lane_count);
+
+		/*
+		 * Disable and re-enable the mainlink clock since the
+		 * link clock might have been adjusted as part of the
+		 * link maintenance.
+		 */
+		dp_ctrl_disable_mainlink_clocks(ctrl);
+
+		ret = dp_ctrl_enable_mainlink_clocks(ctrl);
+		if (ret)
+			continue;
+
+		dp_ctrl_configure_source_params(ctrl);
+
+		ctrl->catalog->config_msa(ctrl->catalog,
+			drm_dp_bw_code_to_link_rate(
+			ctrl->link->link_params.bw_code),
+			ctrl->pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));
+
+		reinit_completion(&ctrl->idle_comp);
+
+		ret = dp_ctrl_setup_main_link(ctrl, true);
+	} while (ret == -EAGAIN);
+
+	return ret;
+}
+
+static void dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl)
+{
+	int ret = 0;
+
+	if (!ctrl->link->phy_params.phy_test_pattern_sel) {
+		pr_debug("no test pattern selected by sink\n");
+		return;
+	}
+
+	pr_debug("start\n");
+
+	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
+	/*
+	 * The global reset will need DP link ralated clocks to be
+	 * running. Add the global reset just before disabling the
+	 * link clocks and core clocks.
+	 */
+	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
+	ctrl->dp_ctrl.off(&ctrl->dp_ctrl);
+
+	ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl);
+	if (ret)
+		pr_err("failed to enable DP controller\n");
+
+	pr_debug("end\n");
+}
+
+static void dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
+{
+	bool success = false;
+	u32 pattern_sent = 0x0;
+	u32 pattern_requested = ctrl->link->phy_params.phy_test_pattern_sel;
+
+	pr_debug("request: %s\n",
+			dp_link_get_phy_test_pattern(pattern_requested));
+
+	ctrl->catalog->update_vx_px(ctrl->catalog,
+			ctrl->link->phy_params.v_level,
+			ctrl->link->phy_params.p_level);
+	ctrl->catalog->send_phy_pattern(ctrl->catalog, pattern_requested);
+	ctrl->link->send_test_response(ctrl->link);
+
+	pattern_sent = ctrl->catalog->read_phy_pattern(ctrl->catalog);
+
+	switch (pattern_sent) {
+	case MR_LINK_TRAINING1:
+		if (pattern_requested ==
+				DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING)
+			success = true;
+		break;
+	case MR_LINK_SYMBOL_ERM:
+		if ((pattern_requested ==
+				DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT)
+			|| (pattern_requested ==
+				DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN))
+			success = true;
+		break;
+	case MR_LINK_PRBS7:
+		if (pattern_requested == DP_TEST_PHY_PATTERN_PRBS7)
+			success = true;
+		break;
+	case MR_LINK_CUSTOM80:
+		if (pattern_requested ==
+				DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN)
+			success = true;
+		break;
+	default:
+		success = false;
+		return;
+	}
+
+	pr_debug("%s: %s\n", success ? "success" : "failed",
+			dp_link_get_phy_test_pattern(pattern_requested));
+}
+
+static void dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+	u32 sink_request = 0x0;
+
+	if (!dp_ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+	sink_request = ctrl->link->sink_request;
+
+	if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+		pr_info("PHY_TEST_PATTERN request\n");
+		dp_ctrl_process_phy_test_request(ctrl);
+	}
+
+	if (sink_request & DP_LINK_STATUS_UPDATED)
+		dp_ctrl_link_maintenance(ctrl);
+
+	if (sink_request & DP_TEST_LINK_TRAINING) {
+		ctrl->link->send_test_response(ctrl->link);
+		dp_ctrl_link_maintenance(ctrl);
+	}
+}
+
+static void dp_ctrl_reset(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		pr_err("invalid params\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+	ctrl->catalog->reset(ctrl->catalog);
+}
+
+static int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
+{
+	int rc = 0;
+	struct dp_ctrl_private *ctrl;
+	u32 rate = 0;
+	u32 link_train_max_retries = 100;
+	u32 const phy_cts_pixel_clk_khz = 148500;
+
+	if (!dp_ctrl) {
+		rc = -EINVAL;
+		goto end;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	atomic_set(&ctrl->aborted, 0);
+	rate = ctrl->panel->link_info.rate;
+
+	ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
+	ctrl->catalog->hpd_config(ctrl->catalog, true);
+
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+		pr_debug("using phy test link parameters\n");
+		if (!ctrl->panel->pinfo.pixel_clk_khz)
+			ctrl->pixel_rate = phy_cts_pixel_clk_khz;
+	} else {
+		ctrl->link->link_params.bw_code =
+			drm_dp_link_rate_to_bw_code(rate);
+		ctrl->link->link_params.lane_count =
+			ctrl->panel->link_info.num_lanes;
+		ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+	}
+
+	pr_debug("bw_code=%d, lane_count=%d, pixel_rate=%d\n",
+		ctrl->link->link_params.bw_code,
+		ctrl->link->link_params.lane_count, ctrl->pixel_rate);
+
+	ctrl->catalog->phy_lane_cfg(ctrl->catalog,
+			ctrl->orientation, ctrl->link->link_params.lane_count);
+
+	rc = dp_ctrl_enable_mainlink_clocks(ctrl);
+	if (rc)
+		goto end;
+
+	reinit_completion(&ctrl->idle_comp);
+
+	dp_ctrl_configure_source_params(ctrl);
+
+	while (--link_train_max_retries && !atomic_read(&ctrl->aborted)) {
+		ctrl->catalog->config_msa(ctrl->catalog,
+			drm_dp_bw_code_to_link_rate(
+			ctrl->link->link_params.bw_code),
+			ctrl->pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));
+
+		rc = dp_ctrl_setup_main_link(ctrl, true);
+		if (!rc)
+			break;
+
+		/* try with lower link rate */
+		dp_ctrl_link_rate_down_shift(ctrl);
+
+		ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+
+		dp_ctrl_disable_mainlink_clocks(ctrl);
+		/* hw recommended delay before re-enabling clocks */
+		msleep(20);
+
+		dp_ctrl_enable_mainlink_clocks(ctrl);
+	}
+
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
+		dp_ctrl_send_phy_test_pattern(ctrl);
+
+	pr_debug("End-\n");
+
+end:
+	return rc;
+}
+
+static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+	ctrl->catalog->reset(ctrl->catalog);
+
+	/* Make sure DP is disabled before clk disable */
+	wmb();
+
+	dp_ctrl_disable_mainlink_clocks(ctrl);
+
+	pr_debug("DP off done\n");
+}
+
+static void dp_ctrl_isr(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	ctrl->catalog->get_interrupt(ctrl->catalog);
+
+	if (ctrl->catalog->isr & DP_CTRL_INTR_READY_FOR_VIDEO)
+		dp_ctrl_video_ready(ctrl);
+
+	if (ctrl->catalog->isr & DP_CTRL_INTR_IDLE_PATTERN_SENT)
+		dp_ctrl_idle_patterns_sent(ctrl);
+}
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
+{
+	int rc = 0;
+	struct dp_ctrl_private *ctrl;
+	struct dp_ctrl *dp_ctrl;
+
+	if (!in->dev || !in->panel || !in->aux ||
+	    !in->link || !in->catalog) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	ctrl = devm_kzalloc(in->dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	init_completion(&ctrl->idle_comp);
+	init_completion(&ctrl->video_comp);
+
+	/* in parameters */
+	ctrl->parser   = in->parser;
+	ctrl->panel    = in->panel;
+	ctrl->power    = in->power;
+	ctrl->aux      = in->aux;
+	ctrl->link     = in->link;
+	ctrl->catalog  = in->catalog;
+	ctrl->dev      = in->dev;
+
+	dp_ctrl = &ctrl->dp_ctrl;
+
+	/* out parameters */
+	dp_ctrl->init      = dp_ctrl_host_init;
+	dp_ctrl->deinit    = dp_ctrl_host_deinit;
+	dp_ctrl->on        = dp_ctrl_on;
+	dp_ctrl->off       = dp_ctrl_off;
+	dp_ctrl->push_idle = dp_ctrl_push_idle;
+	dp_ctrl->abort     = dp_ctrl_abort;
+	dp_ctrl->isr       = dp_ctrl_isr;
+	dp_ctrl->reset	   = dp_ctrl_reset;
+	dp_ctrl->handle_sink_request = dp_ctrl_handle_sink_request;
+
+	return dp_ctrl;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl)
+		return;
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+
+	devm_kfree(ctrl->dev, ctrl);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
new file mode 100644
index 0000000..6ab221a
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_CTRL_H_
+#define _DP_CTRL_H_
+
+#include "dp_aux.h"
+#include "dp_panel.h"
+#include "dp_link.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+
+struct dp_ctrl {
+	int (*init)(struct dp_ctrl *dp_ctrl, bool flip);
+	void (*deinit)(struct dp_ctrl *dp_ctrl);
+	int (*on)(struct dp_ctrl *dp_ctrl);
+	void (*off)(struct dp_ctrl *dp_ctrl);
+	void (*reset)(struct dp_ctrl *dp_ctrl);
+	void (*push_idle)(struct dp_ctrl *dp_ctrl);
+	void (*abort)(struct dp_ctrl *dp_ctrl);
+	void (*isr)(struct dp_ctrl *dp_ctrl);
+	void (*handle_sink_request)(struct dp_ctrl *dp_ctrl);
+};
+
+struct dp_ctrl_in {
+	struct device *dev;
+	struct dp_panel *panel;
+	struct dp_aux *aux;
+	struct dp_link *link;
+	struct dp_parser *parser;
+	struct dp_power *power;
+	struct dp_catalog_ctrl *catalog;
+};
+
+struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in);
+void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
+
+#endif /* _DP_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
new file mode 100644
index 0000000..5c0a8ce
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/debugfs.h>
+#include <drm/drm_connector.h>
+
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+#include "dp_aux.h"
+#include "dp_ctrl.h"
+#include "dp_debug.h"
+#include "dp_display.h"
+
+#define DEBUG_NAME "drm_dp"
+
+struct dp_debug_private {
+	struct dentry *root;
+
+	struct dp_usbpd *usbpd;
+	struct dp_link *link;
+	struct dp_panel *panel;
+	struct drm_connector **connector;
+	struct device *dev;
+
+	struct dp_debug dp_debug;
+};
+
+static ssize_t dp_debug_write_hpd(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	int hpd;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto end;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &hpd) != 0)
+		goto end;
+
+	if (debug->usbpd && debug->usbpd->connect)
+		debug->usbpd->connect(debug->usbpd, hpd);
+
+end:
+	return -len;
+}
+
+static ssize_t dp_debug_write_edid_modes(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_32];
+	size_t len = 0;
+	int hdisplay = 0, vdisplay = 0, vrefresh = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		goto end;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_32 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto clear;
+
+	buf[len] = '\0';
+
+	if (sscanf(buf, "%d %d %d", &hdisplay, &vdisplay, &vrefresh) != 3)
+		goto clear;
+
+	if (!hdisplay || !vdisplay || !vrefresh)
+		goto clear;
+
+	debug->dp_debug.debug_en = true;
+	debug->dp_debug.hdisplay = hdisplay;
+	debug->dp_debug.vdisplay = vdisplay;
+	debug->dp_debug.vrefresh = vrefresh;
+	goto end;
+clear:
+	pr_debug("clearing debug modes\n");
+	debug->dp_debug.debug_en = false;
+end:
+	return len;
+}
+
+static ssize_t dp_debug_read_connected(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	if (!debug->usbpd)
+		return -ENODEV;
+
+	len += snprintf(buf, SZ_8, "%d\n", debug->usbpd->hpd_high);
+
+	if (copy_to_user(user_buff, buf, len))
+		return -EFAULT;
+
+	*ppos += len;
+	return len;
+}
+
+static ssize_t dp_debug_read_edid_modes(struct file *file,
+		char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0;
+	int rc = 0;
+	struct drm_connector *connector;
+	struct drm_display_mode *mode;
+
+	if (!debug) {
+		pr_err("invalid data\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	connector = *debug->connector;
+
+	if (!connector) {
+		pr_err("connector is NULL\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (*ppos)
+		goto error;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	list_for_each_entry(mode, &connector->modes, head) {
+		len += snprintf(buf + len, SZ_4K - len,
+		"%s %d %d %d %d %d %d %d %d %d 0x%x\n",
+		mode->name, mode->vrefresh, mode->hdisplay,
+		mode->hsync_start, mode->hsync_end, mode->htotal,
+		mode->vdisplay, mode->vsync_start, mode->vsync_end,
+		mode->vtotal, mode->flags);
+	}
+
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		rc = -EFAULT;
+		goto error;
+	}
+
+	*ppos += len;
+	kfree(buf);
+
+	return len;
+error:
+	return rc;
+}
+
+static int dp_debug_check_buffer_overflow(int rc, int *max_size, int *len)
+{
+	if (rc >= *max_size) {
+		pr_err("buffer overflow\n");
+		return -EINVAL;
+	}
+	*len += rc;
+	*max_size = SZ_4K - *len;
+
+	return 0;
+}
+
+static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff,
+		size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0, rc = 0;
+	u64 lclk = 0;
+	u32 max_size = SZ_4K;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	rc = snprintf(buf + len, max_size, "\tname = %s\n", DEBUG_NAME);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\tdp_panel\n\t\tmax_pclk_khz = %d\n",
+		debug->panel->max_pclk_khz);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\tdrm_dp_link\n\t\trate = %u\n",
+		debug->panel->link_info.rate);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tnum_lanes = %u\n",
+		debug->panel->link_info.num_lanes);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tcapabilities = %lu\n",
+		debug->panel->link_info.capabilities);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\tdp_panel_info:\n\t\tactive = %dx%d\n",
+		debug->panel->pinfo.h_active,
+		debug->panel->pinfo.v_active);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tback_porch = %dx%d\n",
+		debug->panel->pinfo.h_back_porch,
+		debug->panel->pinfo.v_back_porch);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tfront_porch = %dx%d\n",
+		debug->panel->pinfo.h_front_porch,
+		debug->panel->pinfo.v_front_porch);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tsync_width = %dx%d\n",
+		debug->panel->pinfo.h_sync_width,
+		debug->panel->pinfo.v_sync_width);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tactive_low = %dx%d\n",
+		debug->panel->pinfo.h_active_low,
+		debug->panel->pinfo.v_active_low);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\th_skew = %d\n",
+		debug->panel->pinfo.h_skew);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\trefresh rate = %d\n",
+		debug->panel->pinfo.refresh_rate);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tpixel clock khz = %d\n",
+		debug->panel->pinfo.pixel_clk_khz);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tbpp = %d\n",
+		debug->panel->pinfo.bpp);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	/* Link Information */
+	rc = snprintf(buf + len, max_size,
+		"\tdp_link:\n\t\ttest_requested = %d\n",
+		debug->link->sink_request);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tlane_count = %d\n", debug->link->link_params.lane_count);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tbw_code = %d\n", debug->link->link_params.bw_code);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	lclk = drm_dp_bw_code_to_link_rate(
+			debug->link->link_params.bw_code) * 1000;
+	rc = snprintf(buf + len, max_size,
+		"\t\tlclk = %lld\n", lclk);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tv_level = %d\n", debug->link->phy_params.v_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	rc = snprintf(buf + len, max_size,
+		"\t\tp_level = %d\n", debug->link->phy_params.p_level);
+	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
+		goto error;
+
+	if (copy_to_user(user_buff, buf, len))
+		goto error;
+
+	*ppos += len;
+
+	kfree(buf);
+	return len;
+error:
+	kfree(buf);
+	return -EINVAL;
+}
+
+static const struct file_operations dp_debug_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_info,
+};
+
+static const struct file_operations edid_modes_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_edid_modes,
+	.write = dp_debug_write_edid_modes,
+};
+
+static const struct file_operations hpd_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_hpd,
+};
+
+static const struct file_operations connected_fops = {
+	.open = simple_open,
+	.read = dp_debug_read_connected,
+};
+
+static int dp_debug_init(struct dp_debug *dp_debug)
+{
+	int rc = 0;
+	struct dp_debug_private *debug = container_of(dp_debug,
+		struct dp_debug_private, dp_debug);
+	struct dentry *dir, *file, *edid_modes;
+	struct dentry *hpd, *connected;
+
+	dir = debugfs_create_dir(DEBUG_NAME, NULL);
+	if (IS_ERR_OR_NULL(dir)) {
+		rc = PTR_ERR(dir);
+		pr_err("[%s] debugfs create dir failed, rc = %d\n",
+		       DEBUG_NAME, rc);
+		goto error;
+	}
+
+	file = debugfs_create_file("dp_debug", 0444, dir,
+				debug, &dp_debug_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		pr_err("[%s] debugfs create file failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	edid_modes = debugfs_create_file("edid_modes", 0644, dir,
+					debug, &edid_modes_fops);
+	if (IS_ERR_OR_NULL(edid_modes)) {
+		rc = PTR_ERR(edid_modes);
+		pr_err("[%s] debugfs create edid_modes failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	hpd = debugfs_create_file("hpd", 0644, dir,
+					debug, &hpd_fops);
+	if (IS_ERR_OR_NULL(hpd)) {
+		rc = PTR_ERR(hpd);
+		pr_err("[%s] debugfs hpd failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	connected = debugfs_create_file("connected", 0444, dir,
+					debug, &connected_fops);
+	if (IS_ERR_OR_NULL(connected)) {
+		rc = PTR_ERR(connected);
+		pr_err("[%s] debugfs connected failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	debug->root = dir;
+	return rc;
+error_remove_dir:
+	debugfs_remove(dir);
+error:
+	return rc;
+}
+
+struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
+			struct dp_usbpd *usbpd, struct dp_link *link,
+			struct drm_connector **connector)
+{
+	int rc = 0;
+	struct dp_debug_private *debug;
+	struct dp_debug *dp_debug;
+
+	if (!dev || !panel || !usbpd || !link) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	debug = devm_kzalloc(dev, sizeof(*debug), GFP_KERNEL);
+	if (!debug) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	debug->dp_debug.debug_en = false;
+	debug->usbpd = usbpd;
+	debug->link = link;
+	debug->panel = panel;
+	debug->dev = dev;
+	debug->connector = connector;
+
+	dp_debug = &debug->dp_debug;
+	dp_debug->vdisplay = 0;
+	dp_debug->hdisplay = 0;
+	dp_debug->vrefresh = 0;
+
+	rc = dp_debug_init(dp_debug);
+	if (rc) {
+		devm_kfree(dev, debug);
+		goto error;
+	}
+
+	return dp_debug;
+error:
+	return ERR_PTR(rc);
+}
+
+static int dp_debug_deinit(struct dp_debug *dp_debug)
+{
+	struct dp_debug_private *debug;
+
+	if (!dp_debug)
+		return -EINVAL;
+
+	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
+
+	debugfs_remove_recursive(debug->root);
+
+	return 0;
+}
+
+void dp_debug_put(struct dp_debug *dp_debug)
+{
+	struct dp_debug_private *debug;
+
+	if (!dp_debug)
+		return;
+
+	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
+
+	dp_debug_deinit(dp_debug);
+
+	devm_kfree(debug->dev, debug);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.h b/drivers/gpu/drm/msm/dp/dp_debug.h
new file mode 100644
index 0000000..a6480e1
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_debug.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_DEBUG_H_
+#define _DP_DEBUG_H_
+
+#include "dp_panel.h"
+#include "dp_link.h"
+#include "dp_extcon.h"
+
+/**
+ * struct dp_debug
+ * @debug_en: specifies whether debug mode enabled
+ * @vdisplay: used to filter out vdisplay value
+ * @hdisplay: used to filter out hdisplay value
+ * @vrefresh: used to filter out vrefresh value
+ * @tpg_state: specifies whether tpg feature is enabled
+ */
+struct dp_debug {
+	bool debug_en;
+	int aspect_ratio;
+	int vdisplay;
+	int hdisplay;
+	int vrefresh;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+
+/**
+ * dp_debug_get() - configure and get the DisplayPlot debug module data
+ *
+ * @dev: device instance of the caller
+ * @panel: instance of panel module
+ * @usbpd: instance of usbpd module
+ * @link: instance of link module
+ * @connector: double pointer to display connector
+ * return: pointer to allocated debug module data
+ *
+ * This function sets up the debug module and provides a way
+ * for debugfs input to be communicated with existing modules
+ */
+struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
+			struct dp_usbpd *usbpd, struct dp_link *link,
+			struct drm_connector **connector);
+/**
+ * dp_debug_put()
+ *
+ * Cleans up dp_debug instance
+ *
+ * @dp_debug: instance of dp_debug
+ */
+void dp_debug_put(struct dp_debug *dp_debug);
+
+#else
+
+static inline
+struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
+			struct dp_usbpd *usbpd, struct dp_link *link,
+			struct drm_connector **connector)
+{
+	return ERR_PTR(-EINVAL);
+}
+
+static inline void dp_debug_put(struct dp_debug *dp_debug)
+{
+}
+
+#endif /* defined(CONFIG_DEBUG_FS) */
+
+#endif /* _DP_DEBUG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
new file mode 100644
index 0000000..8c98399
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/component.h>
+#include <linux/of_irq.h>
+
+#include "msm_drv.h"
+#include "dp_extcon.h"
+#include "dp_parser.h"
+#include "dp_power.h"
+#include "dp_catalog.h"
+#include "dp_aux.h"
+#include "dp_link.h"
+#include "dp_panel.h"
+#include "dp_ctrl.h"
+#include "dp_display.h"
+#include "dp_drm.h"
+#include "dp_debug.h"
+
+static struct msm_dp *g_dp_display;
+#define HPD_STRING_SIZE 30
+
+struct dp_display_private {
+	char *name;
+	int irq;
+
+	/* state variables */
+	bool core_initialized;
+	bool power_on;
+	bool hpd_irq_on;
+	bool audio_supported;
+
+	struct platform_device *pdev;
+	struct dentry *root;
+	struct completion notification_comp;
+
+	struct dp_usbpd   *usbpd;
+	struct dp_parser  *parser;
+	struct dp_power   *power;
+	struct dp_catalog *catalog;
+	struct dp_aux     *aux;
+	struct dp_link    *link;
+	struct dp_panel   *panel;
+	struct dp_ctrl    *ctrl;
+	struct dp_debug   *debug;
+
+	struct dp_usbpd_cb usbpd_cb;
+	struct dp_display_mode mode;
+	struct msm_dp dp_display;
+
+};
+
+static const struct of_device_id dp_dt_match[] = {
+	{.compatible = "qcom,dp-display"},
+	{}
+};
+
+static irqreturn_t dp_display_irq(int irq, void *dev_id)
+{
+	struct dp_display_private *dp = dev_id;
+
+	if (!dp) {
+		pr_err("invalid data\n");
+		return IRQ_NONE;
+	}
+
+	/* DP controller isr */
+	dp->ctrl->isr(dp->ctrl);
+
+	/* DP aux isr */
+	dp->aux->isr(dp->aux);
+
+	return IRQ_HANDLED;
+}
+
+static int dp_display_bind(struct device *dev, struct device *master,
+			   void *data)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+	struct drm_device *drm;
+	struct msm_drm_private *priv;
+	struct platform_device *pdev = to_platform_device(dev);
+
+	if (!dev || !pdev || !master) {
+		pr_err("invalid param(s), dev %pK, pdev %pK, master %pK\n",
+				dev, pdev, master);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	drm = dev_get_drvdata(master);
+	dp = platform_get_drvdata(pdev);
+	if (!drm || !dp) {
+		pr_err("invalid param(s), drm %pK, dp %pK\n",
+				drm, dp);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp->dp_display.drm_dev = drm;
+	priv = drm->dev_private;
+	priv->dp = &(dp->dp_display);
+
+	rc = dp->parser->parse(dp->parser);
+	if (rc) {
+		pr_err("device tree parsing failed\n");
+		goto end;
+	}
+
+	rc = dp->aux->drm_aux_register(dp->aux);
+	if (rc) {
+		pr_err("DRM DP AUX register failed\n");
+		goto end;
+	}
+
+	rc = dp->power->power_client_init(dp->power);
+	if (rc) {
+		pr_err("Power client create failed\n");
+		goto end;
+	}
+
+end:
+	return rc;
+}
+
+static void dp_display_unbind(struct device *dev, struct device *master,
+			      void *data)
+{
+	struct dp_display_private *dp;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct msm_drm_private *priv = drm->dev_private;
+
+	if (!dev || !pdev) {
+		pr_err("invalid param(s)\n");
+		return;
+	}
+
+	dp = platform_get_drvdata(pdev);
+	if (!dp) {
+		pr_err("Invalid params\n");
+		return;
+	}
+
+	(void)dp->power->power_client_deinit(dp->power);
+	(void)dp->aux->drm_aux_deregister(dp->aux);
+	priv->dp = NULL;
+}
+
+static const struct component_ops dp_display_comp_ops = {
+	.bind = dp_display_bind,
+	.unbind = dp_display_unbind,
+};
+
+static bool dp_display_is_ds_bridge(struct dp_panel *panel)
+{
+	return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+		DP_DWN_STRM_PORT_PRESENT);
+}
+
+static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
+{
+	return dp_display_is_ds_bridge(dp->panel) &&
+		(dp->link->sink_count.count == 0);
+}
+
+static void dp_display_send_hpd_event(struct msm_dp *dp_display)
+{
+	struct dp_display_private *dp;
+	struct drm_connector *connector;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	if (!dp) {
+		pr_err("invalid params\n");
+		return;
+	}
+
+	connector = dp->dp_display.connector;
+	drm_helper_hpd_irq_event(connector->dev);
+}
+
+static int dp_display_send_hpd_notification(struct dp_display_private *dp,
+					    bool hpd)
+{
+	if ((hpd && dp->dp_display.is_connected) ||
+			(!hpd && !dp->dp_display.is_connected)) {
+		pr_info("HPD already %s\n", (hpd ? "on" : "off"));
+		return 0;
+	}
+
+	/* reset video pattern flag on disconnect */
+	if (!hpd)
+		dp->panel->video_test = false;
+
+	dp->dp_display.is_connected = hpd;
+	reinit_completion(&dp->notification_comp);
+	dp_display_send_hpd_event(&dp->dp_display);
+
+	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) {
+		pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int dp_display_process_hpd_high(struct dp_display_private *dp)
+{
+	int rc = 0;
+	struct edid *edid;
+
+	dp->aux->init(dp->aux, dp->parser->aux_cfg);
+
+	if (dp->link->psm_enabled)
+		goto notify;
+
+	rc = dp->panel->read_sink_caps(dp->panel, dp->dp_display.connector);
+	if (rc)
+		goto notify;
+
+	dp->link->process_request(dp->link);
+
+	if (dp_display_is_sink_count_zero(dp)) {
+		pr_debug("no downstream devices connected\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	edid = dp->panel->edid;
+
+	dp->audio_supported = drm_detect_monitor_audio(edid);
+
+	dp->panel->handle_sink_request(dp->panel);
+
+	dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;
+notify:
+	dp_display_send_hpd_notification(dp, true);
+
+end:
+	return rc;
+}
+
+static void dp_display_host_init(struct dp_display_private *dp)
+{
+	bool flip = false;
+
+	if (dp->core_initialized) {
+		pr_debug("DP core already initialized\n");
+		return;
+	}
+
+	if (dp->usbpd->orientation == ORIENTATION_CC2)
+		flip = true;
+
+	dp->power->init(dp->power, flip);
+	dp->ctrl->init(dp->ctrl, flip);
+	enable_irq(dp->irq);
+	dp->core_initialized = true;
+}
+
+static void dp_display_host_deinit(struct dp_display_private *dp)
+{
+	if (!dp->core_initialized) {
+		pr_debug("DP core already off\n");
+		return;
+	}
+
+	dp->ctrl->deinit(dp->ctrl);
+	dp->power->deinit(dp->power);
+	disable_irq(dp->irq);
+	dp->core_initialized = false;
+}
+
+static void dp_display_process_hpd_low(struct dp_display_private *dp)
+{
+	/* cancel any pending request */
+	dp->ctrl->abort(dp->ctrl);
+
+	dp_display_send_hpd_notification(dp, false);
+
+	dp->aux->deinit(dp->aux);
+}
+
+static int dp_display_usbpd_configure_cb(struct device *dev)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dev) {
+		pr_err("invalid dev\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp = dev_get_drvdata(dev);
+	if (!dp) {
+		pr_err("no driver data found\n");
+		rc = -ENODEV;
+		goto end;
+	}
+
+	dp_display_host_init(dp);
+
+	if (dp->usbpd->hpd_high)
+		dp_display_process_hpd_high(dp);
+end:
+	return rc;
+}
+
+static void dp_display_clean(struct dp_display_private *dp)
+{
+	dp->ctrl->push_idle(dp->ctrl);
+	dp->ctrl->off(dp->ctrl);
+}
+
+static int dp_display_usbpd_disconnect_cb(struct device *dev)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dev) {
+		pr_err("invalid dev\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp = dev_get_drvdata(dev);
+	if (!dp) {
+		pr_err("no driver data found\n");
+		rc = -ENODEV;
+		goto end;
+	}
+
+	/* cancel any pending request */
+	dp->ctrl->abort(dp->ctrl);
+
+	rc = dp_display_send_hpd_notification(dp, false);
+
+	/* if cable is disconnected, reset psm_enabled flag */
+	if (!dp->usbpd->alt_mode_cfg_done)
+		dp->link->psm_enabled = false;
+
+	if ((rc < 0) && dp->power_on)
+		dp_display_clean(dp);
+
+	dp_display_host_deinit(dp);
+end:
+	return rc;
+}
+
+static void dp_display_handle_video_request(struct dp_display_private *dp)
+{
+	if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) {
+		/* force disconnect followed by connect */
+		dp->usbpd->connect(dp->usbpd, false);
+		dp->panel->video_test = true;
+		dp->usbpd->connect(dp->usbpd, true);
+		dp->link->send_test_response(dp->link);
+	}
+}
+
+static int dp_display_handle_hpd_irq(struct dp_display_private *dp)
+{
+	if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) {
+		dp_display_send_hpd_notification(dp, false);
+
+		if (dp_display_is_sink_count_zero(dp)) {
+			pr_debug("sink count is zero, nothing to do\n");
+			return 0;
+		}
+
+		return dp_display_process_hpd_high(dp);
+	}
+
+	dp->ctrl->handle_sink_request(dp->ctrl);
+
+	dp_display_handle_video_request(dp);
+
+	return 0;
+}
+
+static int dp_display_usbpd_attention_cb(struct device *dev)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dev) {
+		pr_err("invalid dev\n");
+		return -EINVAL;
+	}
+
+	dp = dev_get_drvdata(dev);
+	if (!dp) {
+		pr_err("no driver data found\n");
+		return -ENODEV;
+	}
+
+	if (dp->usbpd->hpd_irq) {
+		dp->hpd_irq_on = true;
+
+		rc = dp->link->process_request(dp->link);
+		/* check for any test request issued by sink */
+		if (!rc)
+			dp_display_handle_hpd_irq(dp);
+
+		dp->hpd_irq_on = false;
+		goto end;
+	}
+
+	if (!dp->usbpd->hpd_high) {
+		dp_display_process_hpd_low(dp);
+		goto end;
+	}
+
+	if (dp->usbpd->alt_mode_cfg_done)
+		dp_display_process_hpd_high(dp);
+end:
+	return rc;
+}
+
+static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
+{
+	dp_ctrl_put(dp->ctrl);
+	dp_link_put(dp->link);
+	dp_panel_put(dp->panel);
+	dp_aux_put(dp->aux);
+	dp_power_put(dp->power);
+	dp_catalog_put(dp->catalog);
+	dp_parser_put(dp->parser);
+	dp_debug_put(dp->debug);
+}
+
+static int dp_init_sub_modules(struct dp_display_private *dp)
+{
+	int rc = 0;
+	struct device *dev = &dp->pdev->dev;
+	struct dp_usbpd_cb *cb = &dp->usbpd_cb;
+	struct dp_ctrl_in ctrl_in = {
+		.dev = dev,
+	};
+	struct dp_panel_in panel_in = {
+		.dev = dev,
+	};
+
+	/* Callback APIs used for cable status change event */
+	cb->configure  = dp_display_usbpd_configure_cb;
+	cb->disconnect = dp_display_usbpd_disconnect_cb;
+	cb->attention  = dp_display_usbpd_attention_cb;
+
+	dp->parser = dp_parser_get(dp->pdev);
+	if (IS_ERR(dp->parser)) {
+		rc = PTR_ERR(dp->parser);
+		pr_err("failed to initialize parser, rc = %d\n", rc);
+		dp->parser = NULL;
+		goto error_parser;
+	}
+
+	dp->catalog = dp_catalog_get(dev, &dp->parser->io);
+	if (IS_ERR(dp->catalog)) {
+		rc = PTR_ERR(dp->catalog);
+		pr_err("failed to initialize catalog, rc = %d\n", rc);
+		dp->catalog = NULL;
+		goto error_catalog;
+	}
+
+	dp->power = dp_power_get(dp->parser);
+	if (IS_ERR(dp->power)) {
+		rc = PTR_ERR(dp->power);
+		pr_err("failed to initialize power, rc = %d\n", rc);
+		dp->power = NULL;
+		goto error_power;
+	}
+
+	dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser->aux_cfg);
+	if (IS_ERR(dp->aux)) {
+		rc = PTR_ERR(dp->aux);
+		pr_err("failed to initialize aux, rc = %d\n", rc);
+		dp->aux = NULL;
+		goto error_aux;
+	}
+
+	dp->link = dp_link_get(dev, dp->aux);
+	if (IS_ERR(dp->link)) {
+		rc = PTR_ERR(dp->link);
+		pr_err("failed to initialize link, rc = %d\n", rc);
+		dp->link = NULL;
+		goto error_link;
+	}
+
+	panel_in.aux = dp->aux;
+	panel_in.catalog = &dp->catalog->panel;
+	panel_in.link = dp->link;
+
+	dp->panel = dp_panel_get(&panel_in);
+	if (IS_ERR(dp->panel)) {
+		rc = PTR_ERR(dp->panel);
+		pr_err("failed to initialize panel, rc = %d\n", rc);
+		dp->panel = NULL;
+		goto error_panel;
+	}
+
+	ctrl_in.link = dp->link;
+	ctrl_in.panel = dp->panel;
+	ctrl_in.aux = dp->aux;
+	ctrl_in.power = dp->power;
+	ctrl_in.catalog = &dp->catalog->ctrl;
+	ctrl_in.parser = dp->parser;
+
+	dp->ctrl = dp_ctrl_get(&ctrl_in);
+	if (IS_ERR(dp->ctrl)) {
+		rc = PTR_ERR(dp->ctrl);
+		pr_err("failed to initialize ctrl, rc = %d\n", rc);
+		dp->ctrl = NULL;
+		goto error_ctrl;
+	}
+
+	dp->debug = dp_debug_get(dev, dp->panel, dp->usbpd,
+				dp->link, &dp->dp_display.connector);
+	if (IS_ERR(dp->debug)) {
+		rc = PTR_ERR(dp->debug);
+		pr_err("failed to initialize debug, rc = %d\n", rc);
+		dp->debug = NULL;
+		goto error_debug;
+	}
+
+	return rc;
+error_debug:
+	dp_ctrl_put(dp->ctrl);
+error_ctrl:
+	dp_panel_put(dp->panel);
+error_panel:
+	dp_link_put(dp->link);
+error_link:
+	dp_aux_put(dp->aux);
+error_aux:
+	dp_power_put(dp->power);
+error_power:
+	dp_catalog_put(dp->catalog);
+error_catalog:
+	dp_parser_put(dp->parser);
+error_parser:
+	return rc;
+}
+
+static int dp_display_set_mode(struct msm_dp *dp_display,
+			       struct dp_display_mode *mode)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	dp->panel->pinfo = mode->timing;
+	dp->panel->init_info(dp->panel);
+error:
+	return rc;
+}
+
+static int dp_display_prepare(struct msm_dp *dp)
+{
+	return 0;
+}
+
+static int dp_display_enable(struct msm_dp *dp_display)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	if (dp->power_on) {
+		pr_debug("Link already setup, return\n");
+		return 0;
+	}
+
+	dp->aux->init(dp->aux, dp->parser->aux_cfg);
+
+	rc = dp->ctrl->on(dp->ctrl);
+	if (!rc)
+		dp->power_on = true;
+error:
+	return rc;
+}
+
+static int dp_display_post_enable(struct msm_dp *dp_display)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	complete_all(&dp->notification_comp);
+
+end:
+	return rc;
+}
+
+static int dp_display_pre_disable(struct msm_dp *dp_display)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	if (dp->usbpd->alt_mode_cfg_done && (dp->usbpd->hpd_high ||
+		dp->usbpd->forced_disconnect))
+		dp->link->psm_config(dp->link, &dp->panel->link_info, true);
+
+	dp->ctrl->push_idle(dp->ctrl);
+error:
+	return rc;
+}
+
+static int dp_display_disable(struct msm_dp *dp_display)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	if (!dp->power_on || !dp->core_initialized)
+		goto error;
+
+	dp->ctrl->off(dp->ctrl);
+
+	dp->power_on = false;
+
+	complete_all(&dp->notification_comp);
+error:
+	return rc;
+}
+
+static int dp_request_irq(struct msm_dp *dp_display)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	dp->irq = irq_of_parse_and_map(dp->pdev->dev.of_node, 0);
+	if (dp->irq < 0) {
+		rc = dp->irq;
+		pr_err("failed to get irq: %d\n", rc);
+		return rc;
+	}
+
+	rc = devm_request_irq(&dp->pdev->dev, dp->irq, dp_display_irq,
+		IRQF_TRIGGER_HIGH, "dp_display_isr", dp);
+	if (rc < 0) {
+		pr_err("failed to request IRQ%u: %d\n",
+				dp->irq, rc);
+		return rc;
+	}
+	disable_irq(dp->irq);
+
+	return 0;
+}
+
+static struct dp_debug *dp_get_debug(struct msm_dp *dp_display)
+{
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	return dp->debug;
+}
+
+static int dp_display_unprepare(struct msm_dp *dp)
+{
+	return 0;
+}
+
+static int dp_display_validate_mode(struct msm_dp *dp, u32 mode_pclk_khz)
+{
+	const u32 num_components = 3, default_bpp = 24;
+	struct dp_display_private *dp_display;
+	struct drm_dp_link *link_info;
+	u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
+
+	if (!dp || !mode_pclk_khz || !dp->connector) {
+		pr_err("invalid params\n");
+		return -EINVAL;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+	link_info = &dp_display->panel->link_info;
+
+	mode_bpp = dp->connector->display_info.bpc * num_components;
+	if (!mode_bpp)
+		mode_bpp = default_bpp;
+
+	mode_bpp = dp_display->panel->get_mode_bpp(dp_display->panel,
+			mode_bpp, mode_pclk_khz);
+
+	mode_rate_khz = mode_pclk_khz * mode_bpp;
+	supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
+
+	if (mode_rate_khz > supported_rate_khz)
+		return MODE_BAD;
+
+	return MODE_OK;
+}
+
+static int dp_display_get_modes(struct msm_dp *dp,
+				struct dp_display_mode *dp_mode)
+{
+	struct dp_display_private *dp_display;
+	int ret = 0;
+
+	if (!dp) {
+		pr_err("invalid params\n");
+		return 0;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+
+	ret = dp_display->panel->get_modes(dp_display->panel,
+		dp->connector, dp_mode);
+	if (dp_mode->timing.pixel_clk_khz)
+		dp->max_pclk_khz = dp_mode->timing.pixel_clk_khz;
+	return ret;
+}
+
+static bool dp_display_check_video_test(struct msm_dp *dp)
+{
+	struct dp_display_private *dp_display;
+
+	if (!dp) {
+		pr_err("invalid params\n");
+		return false;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+
+	if (dp_display->panel->video_test)
+		return true;
+
+	return false;
+}
+
+static int dp_display_get_test_bpp(struct msm_dp *dp)
+{
+	struct dp_display_private *dp_display;
+
+	if (!dp) {
+		pr_err("invalid params\n");
+		return 0;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+
+	return dp_link_bit_depth_to_bpp(
+		dp_display->link->test_video.test_bit_depth);
+}
+
+static int dp_display_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct dp_display_private *dp;
+
+	if (!pdev || !pdev->dev.of_node) {
+		pr_err("pdev not found\n");
+		return -ENODEV;
+	}
+
+	dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+
+	init_completion(&dp->notification_comp);
+
+	dp->pdev = pdev;
+	dp->name = "drm_dp";
+
+	rc = dp_init_sub_modules(dp);
+	if (rc) {
+		pr_err("init sub module failed\n");
+		devm_kfree(&pdev->dev, dp);
+		return -EPROBE_DEFER;
+	}
+
+	platform_set_drvdata(pdev, dp);
+
+	g_dp_display = &dp->dp_display;
+
+	g_dp_display->enable        = dp_display_enable;
+	g_dp_display->post_enable   = dp_display_post_enable;
+	g_dp_display->pre_disable   = dp_display_pre_disable;
+	g_dp_display->disable       = dp_display_disable;
+	g_dp_display->set_mode      = dp_display_set_mode;
+	g_dp_display->validate_mode = dp_display_validate_mode;
+	g_dp_display->get_modes     = dp_display_get_modes;
+	g_dp_display->prepare       = dp_display_prepare;
+	g_dp_display->unprepare     = dp_display_unprepare;
+	g_dp_display->request_irq   = dp_request_irq;
+	g_dp_display->get_debug     = dp_get_debug;
+	g_dp_display->send_hpd_event    = dp_display_send_hpd_event;
+	g_dp_display->is_video_test = dp_display_check_video_test;
+	g_dp_display->get_test_bpp = dp_display_get_test_bpp;
+
+	rc = component_add(&pdev->dev, &dp_display_comp_ops);
+	if (rc) {
+		pr_err("component add failed, rc=%d\n", rc);
+		dp_display_deinit_sub_modules(dp);
+		devm_kfree(&pdev->dev, dp);
+	}
+
+	return rc;
+}
+
+int dp_display_get_displays(void **displays, int count)
+{
+	if (!displays) {
+		pr_err("invalid data\n");
+		return -EINVAL;
+	}
+
+	if (count != 1) {
+		pr_err("invalid number of displays\n");
+		return -EINVAL;
+	}
+
+	displays[0] = g_dp_display;
+	return count;
+}
+
+int dp_display_get_num_of_displays(void)
+{
+	return 1;
+}
+
+static int dp_display_remove(struct platform_device *pdev)
+{
+	struct dp_display_private *dp;
+
+	if (!pdev)
+		return -EINVAL;
+
+	dp = platform_get_drvdata(pdev);
+
+	dp_display_deinit_sub_modules(dp);
+
+	platform_set_drvdata(pdev, NULL);
+	devm_kfree(&pdev->dev, dp);
+
+	return 0;
+}
+
+static struct platform_driver dp_display_driver = {
+	.probe  = dp_display_probe,
+	.remove = dp_display_remove,
+	.driver = {
+		.name = "msm-dp-display",
+		.of_match_table = dp_dt_match,
+	},
+};
+
+int __init msm_dp_register(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&dp_display_driver);
+	if (ret) {
+		pr_err("driver register failed");
+		return ret;
+	}
+
+	return ret;
+}
+
+void __exit msm_dp_unregister(void)
+{
+	platform_driver_unregister(&dp_display_driver);
+}
+
+int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
+			struct drm_encoder *encoder)
+{
+	struct msm_drm_private *priv;
+	int ret;
+
+	if (WARN_ON(!encoder) || WARN_ON(!dp_display) || WARN_ON(!dev))
+		return -EINVAL;
+
+	priv = dev->dev_private;
+	dp_display->drm_dev = dev;
+
+	ret = dp_drm_bridge_init(dp_display, encoder);
+	if (ret) {
+		dev_err(dev->dev, "failed to create dp bridge: %d\n", ret);
+		dp_display->bridge = NULL;
+		return ret;
+	}
+	dp_display->encoder = encoder;
+
+	dp_display->connector = dp_drm_connector_init(dp_display);
+	if (IS_ERR(dp_display->connector)) {
+		ret = PTR_ERR(dp_display->connector);
+		dev_err(dev->dev,
+			"failed to create dp connector: %d\n", ret);
+		dp_display->connector = NULL;
+		goto conn_fail;
+	}
+
+	priv->bridges[priv->num_bridges++] = dp_display->bridge;
+	priv->connectors[priv->num_connectors++] = dp_display->connector;
+	return 0;
+
+conn_fail:
+	if (dp_display->bridge) {
+		dp_drm_bridge_deinit(dp_display);
+		dp_display->bridge = NULL;
+	}
+	return ret;
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
new file mode 100644
index 0000000..ca5eae5
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_DISPLAY_H_
+#define _DP_DISPLAY_H_
+
+#include <drm/drmP.h>
+
+#include "dp_panel.h"
+
+struct msm_dp {
+	struct drm_device *drm_dev;
+	struct dp_bridge *dp_bridge;
+	struct drm_connector *connector;
+	struct drm_bridge *bridge;
+
+	/* the encoder we are hooked to (outside of dsi block) */
+	struct drm_encoder *encoder;
+	bool is_connected;
+	u32 max_pclk_khz;
+
+	int (*enable)(struct msm_dp *dp_display);
+	int (*post_enable)(struct msm_dp *dp_display);
+
+	int (*pre_disable)(struct msm_dp *dp_display);
+	int (*disable)(struct msm_dp *dp_display);
+
+	int (*set_mode)(struct msm_dp *dp_display,
+			struct dp_display_mode *mode);
+	int (*validate_mode)(struct msm_dp *dp_display, u32 mode_pclk_khz);
+	int (*get_modes)(struct msm_dp *dp_display,
+		struct dp_display_mode *dp_mode);
+	int (*prepare)(struct msm_dp *dp_display);
+	int (*unprepare)(struct msm_dp *dp_display);
+	int (*request_irq)(struct msm_dp *dp_display);
+	struct dp_debug *(*get_debug)(struct msm_dp *dp_display);
+	void (*send_hpd_event)(struct msm_dp *dp_display);
+	bool (*is_video_test)(struct msm_dp *dp_display);
+	int (*get_test_bpp)(struct msm_dp *dp_display);
+};
+
+int dp_display_get_num_of_displays(void);
+int dp_display_get_displays(void **displays, int count);
+#endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
new file mode 100644
index 0000000..64d6449
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp]: %s: " fmt, __func__
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_crtc.h>
+
+#include "msm_drv.h"
+#include "msm_kms.h"
+//#include "dpu_connector.h"
+#include "dp_drm.h"
+#include "dp_debug.h"
+
+struct dp_connector {
+	struct drm_connector base;
+	struct msm_dp *dp_display;
+};
+#define to_dp_connector(x) container_of(x, struct dp_connector, base)
+#define to_dp_bridge(x)     container_of((x), struct dp_bridge, base)
+
+static void convert_to_dp_mode(const struct drm_display_mode *drm_mode,
+			struct dp_display_mode *dp_mode, struct msm_dp *dp)
+{
+	const u32 num_components = 3;
+
+	memset(dp_mode, 0, sizeof(*dp_mode));
+
+	dp_mode->timing.h_active = drm_mode->hdisplay;
+	dp_mode->timing.h_back_porch = drm_mode->htotal - drm_mode->hsync_end;
+	dp_mode->timing.h_sync_width = drm_mode->htotal -
+			(drm_mode->hsync_start + dp_mode->timing.h_back_porch);
+	dp_mode->timing.h_front_porch = drm_mode->hsync_start -
+					 drm_mode->hdisplay;
+	dp_mode->timing.h_skew = drm_mode->hskew;
+
+	dp_mode->timing.v_active = drm_mode->vdisplay;
+	dp_mode->timing.v_back_porch = drm_mode->vtotal - drm_mode->vsync_end;
+	dp_mode->timing.v_sync_width = drm_mode->vtotal -
+		(drm_mode->vsync_start + dp_mode->timing.v_back_porch);
+
+	dp_mode->timing.v_front_porch = drm_mode->vsync_start -
+					 drm_mode->vdisplay;
+
+	if (dp->is_video_test(dp))
+		dp_mode->timing.bpp = dp->get_test_bpp(dp);
+	else
+		dp_mode->timing.bpp = dp->connector->display_info.bpc *
+		num_components;
+
+	if (!dp_mode->timing.bpp)
+		dp_mode->timing.bpp = 24;
+
+	dp_mode->timing.refresh_rate = drm_mode->vrefresh;
+
+	dp_mode->timing.pixel_clk_khz = drm_mode->clock;
+
+	dp_mode->timing.v_active_low =
+		!!(drm_mode->flags & DRM_MODE_FLAG_NVSYNC);
+
+	dp_mode->timing.h_active_low =
+		!!(drm_mode->flags & DRM_MODE_FLAG_NHSYNC);
+}
+
+static void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
+				struct drm_display_mode *drm_mode)
+{
+	u32 flags = 0;
+
+	memset(drm_mode, 0, sizeof(*drm_mode));
+
+	drm_mode->hdisplay = dp_mode->timing.h_active;
+	drm_mode->hsync_start = drm_mode->hdisplay +
+				dp_mode->timing.h_front_porch;
+	drm_mode->hsync_end = drm_mode->hsync_start +
+			      dp_mode->timing.h_sync_width;
+	drm_mode->htotal = drm_mode->hsync_end + dp_mode->timing.h_back_porch;
+	drm_mode->hskew = dp_mode->timing.h_skew;
+
+	drm_mode->vdisplay = dp_mode->timing.v_active;
+	drm_mode->vsync_start = drm_mode->vdisplay +
+				dp_mode->timing.v_front_porch;
+	drm_mode->vsync_end = drm_mode->vsync_start +
+			      dp_mode->timing.v_sync_width;
+	drm_mode->vtotal = drm_mode->vsync_end + dp_mode->timing.v_back_porch;
+
+	drm_mode->vrefresh = dp_mode->timing.refresh_rate;
+	drm_mode->clock = dp_mode->timing.pixel_clk_khz;
+
+	if (dp_mode->timing.h_active_low)
+		flags |= DRM_MODE_FLAG_NHSYNC;
+	else
+		flags |= DRM_MODE_FLAG_PHSYNC;
+
+	if (dp_mode->timing.v_active_low)
+		flags |= DRM_MODE_FLAG_NVSYNC;
+	else
+		flags |= DRM_MODE_FLAG_PVSYNC;
+
+	drm_mode->flags = flags;
+
+	drm_mode->type = 0x48;
+	drm_mode_set_name(drm_mode);
+}
+
+static void dp_bridge_pre_enable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct msm_dp *dp;
+
+	if (!drm_bridge) {
+		pr_err("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	dp = bridge->display;
+
+	/* By this point mode should have been validated through mode_fixup */
+	rc = dp->set_mode(dp, &bridge->dp_mode);
+	if (rc) {
+		pr_err("[%d] failed to perform a mode set, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+
+	rc = dp->prepare(dp);
+	if (rc) {
+		pr_err("[%d] DP display prepare failed, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+
+	rc = dp->enable(dp);
+	if (rc) {
+		pr_err("[%d] DP display enable failed, rc=%d\n",
+		       bridge->id, rc);
+		dp->unprepare(dp);
+	}
+}
+
+static void dp_bridge_enable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct msm_dp *dp;
+
+	if (!drm_bridge) {
+		pr_err("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	dp = bridge->display;
+
+	rc = dp->post_enable(dp);
+	if (rc)
+		pr_err("[%d] DP display post enable failed, rc=%d\n",
+		       bridge->id, rc);
+}
+
+static void dp_bridge_disable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct msm_dp *dp;
+
+	if (!drm_bridge) {
+		pr_err("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	dp = bridge->display;
+
+	rc = dp->pre_disable(dp);
+	if (rc) {
+		pr_err("[%d] DP display pre disable failed, rc=%d\n",
+		       bridge->id, rc);
+	}
+}
+
+static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
+{
+	int rc = 0;
+	struct dp_bridge *bridge;
+	struct msm_dp *dp;
+
+	if (!drm_bridge) {
+		pr_err("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	dp = bridge->display;
+
+	rc = dp->disable(dp);
+	if (rc) {
+		pr_err("[%d] DP display disable failed, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+
+	rc = dp->unprepare(dp);
+	if (rc) {
+		pr_err("[%d] DP display unprepare failed, rc=%d\n",
+		       bridge->id, rc);
+		return;
+	}
+}
+
+static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct dp_bridge *bridge;
+	struct msm_dp *dp;
+
+	if (!drm_bridge || !mode || !adjusted_mode) {
+		pr_err("Invalid params\n");
+		return;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	dp = bridge->display;
+
+	memset(&bridge->dp_mode, 0x0, sizeof(struct dp_display_mode));
+	convert_to_dp_mode(adjusted_mode, &bridge->dp_mode, dp);
+}
+
+static bool dp_bridge_mode_fixup(struct drm_bridge *drm_bridge,
+				  const struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	bool ret = true;
+	struct dp_display_mode dp_mode;
+	struct dp_bridge *bridge;
+	struct msm_dp *dp;
+
+	if (!drm_bridge || !mode || !adjusted_mode) {
+		pr_err("Invalid params\n");
+		ret = false;
+		goto end;
+	}
+
+	bridge = to_dp_bridge(drm_bridge);
+	dp = bridge->display;
+
+	convert_to_dp_mode(mode, &dp_mode, dp);
+	convert_to_drm_mode(&dp_mode, adjusted_mode);
+end:
+	return ret;
+}
+
+static const struct drm_bridge_funcs dp_bridge_ops = {
+	.mode_fixup   = dp_bridge_mode_fixup,
+	.pre_enable   = dp_bridge_pre_enable,
+	.enable       = dp_bridge_enable,
+	.disable      = dp_bridge_disable,
+	.post_disable = dp_bridge_post_disable,
+	.mode_set     = dp_bridge_mode_set,
+};
+
+int dp_connector_post_init(struct drm_connector *connector, void *display)
+{
+	struct msm_dp *dp_display = display;
+
+	if (!dp_display)
+		return -EINVAL;
+
+	dp_display->connector = connector;
+	return 0;
+}
+
+/**
+ * dp_connector_detect - callback to determine if connector is connected
+ * @connector: Pointer to drm connector structure
+ * @force: Force detect setting from drm framework
+ * Returns: Connector 'is connected' status
+ */
+static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
+		bool force)
+{
+	enum drm_connector_status status = connector_status_unknown;
+	static enum drm_connector_status current_status = connector_status_unknown;
+	struct msm_dp *dp;
+	struct dp_connector *dp_conn;
+	struct msm_drm_private *priv = conn->dev->dev_private;
+	struct msm_kms *kms = priv->kms;
+
+	dp_conn = to_dp_connector(conn);
+	dp = dp_conn->dp_display;
+
+	pr_err("is_connected = %s\n",
+		(dp->is_connected) ? "true" : "false");
+
+	status = (dp->is_connected) ? connector_status_connected :
+					connector_status_disconnected;
+
+	if (dp->is_connected && dp->bridge->encoder
+				&& (current_status != status)
+				&& kms->funcs->set_encoder_mode) {
+		kms->funcs->set_encoder_mode(kms,
+				dp->bridge->encoder, false);
+		pr_err("call set_encoder_mode\n");
+	}
+	current_status = status;
+	return status;
+}
+
+static void dp_connector_destroy(struct drm_connector *connector)
+{
+	struct dp_connector *dp_conn = to_dp_connector(connector);
+
+	DBG("");
+
+	drm_connector_cleanup(connector);
+
+	kfree(dp_conn);
+}
+
+void dp_connector_send_hpd_event(void *display)
+{
+	struct msm_dp *dp;
+
+	if (!display) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp = display;
+
+	if (dp->send_hpd_event)
+		dp->send_hpd_event(dp);
+}
+
+/**
+ * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
+ * @connector: Pointer to drm connector structure
+ * Returns: Number of modes added
+ */
+static int dp_connector_get_modes(struct drm_connector *connector)
+{
+	int rc = 0;
+	struct msm_dp *dp;
+	struct dp_display_mode *dp_mode = NULL;
+	struct drm_display_mode *m, drm_mode;
+	struct dp_connector *dp_conn;
+
+	if (!connector)
+		return 0;
+
+	dp_conn = to_dp_connector(connector);
+	dp = dp_conn->dp_display;
+
+	dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL);
+	if (!dp_mode)
+		return 0;
+
+	/* pluggable case assumes EDID is read when HPD */
+	if (dp->is_connected) {
+		rc = dp->get_modes(dp, dp_mode);
+		if (!rc)
+			pr_err("failed to get DP sink modes, rc=%d\n", rc);
+
+		if (dp_mode->timing.pixel_clk_khz) { /* valid DP mode */
+			memset(&drm_mode, 0x0, sizeof(drm_mode));
+			convert_to_drm_mode(dp_mode, &drm_mode);
+			m = drm_mode_duplicate(connector->dev, &drm_mode);
+			if (!m) {
+				pr_err("failed to add mode %ux%u\n",
+				       drm_mode.hdisplay,
+				       drm_mode.vdisplay);
+				kfree(dp_mode);
+				return 0;
+			}
+			m->width_mm = connector->display_info.width_mm;
+			m->height_mm = connector->display_info.height_mm;
+			drm_mode_probed_add(connector, m);
+		}
+	} else {
+		pr_err("No sink connected\n");
+	}
+	kfree(dp_mode);
+
+	return rc;
+}
+
+int dp_drm_bridge_init(void *data, struct drm_encoder *encoder)
+{
+	int rc = 0;
+	struct dp_bridge *dp_bridge;
+	struct drm_device *dev;
+	struct msm_dp *dp_display = data;
+	struct msm_drm_private *priv = NULL;
+
+	dp_bridge = kzalloc(sizeof(*dp_bridge), GFP_KERNEL);
+	if (!dp_bridge) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	dev = dp_display->drm_dev;
+	dp_bridge->display = dp_display;
+	dp_bridge->base.funcs = &dp_bridge_ops;
+	dp_bridge->base.encoder = encoder;
+
+	priv = dev->dev_private;
+
+	rc = drm_bridge_attach(encoder, &dp_bridge->base, NULL);
+	if (rc) {
+		pr_err("failed to attach bridge, rc=%d\n", rc);
+		goto error_free_bridge;
+	}
+
+	rc = dp_display->request_irq(dp_display);
+	if (rc) {
+		pr_err("request_irq failed, rc=%d\n", rc);
+		goto error_free_bridge;
+	}
+
+	encoder->bridge = &dp_bridge->base;
+	dp_display->dp_bridge = dp_bridge;
+	dp_display->bridge = &dp_bridge->base;
+
+	return 0;
+error_free_bridge:
+	kfree(dp_bridge);
+error:
+	return rc;
+}
+
+void dp_drm_bridge_deinit(void *data)
+{
+	struct msm_dp *display = data;
+	struct dp_bridge *bridge = display->dp_bridge;
+
+	if (bridge && bridge->base.encoder)
+		bridge->base.encoder->bridge = NULL;
+
+	kfree(bridge);
+}
+
+/**
+ * dp_connector_mode_valid - callback to determine if specified mode is valid
+ * @connector: Pointer to drm connector structure
+ * @mode: Pointer to drm mode structure
+ * Returns: Validity status for specified mode
+ */
+static enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	struct msm_dp *dp_disp;
+	struct dp_debug *debug;
+	struct dp_connector *dp_conn;
+
+	if (!mode || !connector) {
+		pr_err("invalid params\n");
+		return MODE_ERROR;
+	}
+
+	dp_conn = to_dp_connector(connector);
+	dp_disp = dp_conn->dp_display;
+	debug = dp_disp->get_debug(dp_disp);
+
+	mode->vrefresh = drm_mode_vrefresh(mode);
+
+	if (mode->clock > dp_disp->max_pclk_khz)
+		return MODE_BAD;
+
+	if (debug->debug_en && (mode->hdisplay != debug->hdisplay ||
+			mode->vdisplay != debug->vdisplay ||
+			mode->vrefresh != debug->vrefresh ||
+			mode->picture_aspect_ratio != debug->aspect_ratio))
+		return MODE_BAD;
+
+	return dp_disp->validate_mode(dp_disp, mode->clock);
+}
+
+static const struct drm_connector_funcs dp_connector_funcs = {
+	.detect = dp_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = dp_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 dp_connector_helper_funcs = {
+	.get_modes = dp_connector_get_modes,
+	.mode_valid = dp_connector_mode_valid,
+};
+
+/* connector initialization */
+struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
+{
+	struct drm_connector *connector = NULL;
+	struct dp_connector *dp_connector;
+	int ret;
+
+	dp_connector = kzalloc(sizeof(*dp_connector), GFP_KERNEL);
+	if (!dp_connector)
+		return ERR_PTR(-ENOMEM);
+
+	dp_connector->dp_display = dp_display;
+
+	connector = &dp_connector->base;
+
+	ret = drm_connector_init(dp_display->drm_dev, connector, &dp_connector_funcs,
+			DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret)
+		return ERR_PTR(ret);
+
+	drm_connector_helper_add(connector, &dp_connector_helper_funcs);
+
+	/*
+	 * Enable HPD to let hpd event is handled
+	 * when cable is attached to the host.
+	 */
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+	/* Display driver doesn't support interlace now. */
+	connector->interlace_allowed = false;
+	connector->doublescan_allowed = false;
+
+	drm_connector_attach_encoder(connector, dp_display->encoder);
+
+	return connector;
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
new file mode 100644
index 0000000..efbc1be
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_DRM_H_
+#define _DP_DRM_H_
+
+#include <linux/types.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "msm_drv.h"
+#include "dp_display.h"
+
+struct dp_bridge {
+	struct drm_bridge base;
+	u32 id;
+
+	struct msm_dp *display;
+	struct dp_display_mode dp_mode;
+};
+
+/**
+ * dp_connector_post_init - callback to perform additional initialization steps
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * Returns: Zero on success
+ */
+int dp_connector_post_init(struct drm_connector *connector, void *display);
+
+void dp_connector_send_hpd_event(void *display);
+
+int dp_drm_bridge_init(void *display,
+	struct drm_encoder *encoder);
+
+void dp_drm_bridge_deinit(void *display);
+
+struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display);
+
+#endif /* _DP_DRM_H_ */
+
diff --git a/drivers/gpu/drm/msm/dp/dp_extcon.c b/drivers/gpu/drm/msm/dp/dp_extcon.c
new file mode 100644
index 0000000..635c13b
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_extcon.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/extcon.h>
+
+#include "dp_extcon.h"
+
+/* DP specific VDM commands */
+#define DP_USBPD_VDM_STATUS	0x10
+#define DP_USBPD_VDM_CONFIGURE	0x11
+
+/* USBPD-TypeC specific Macros */
+#define VDM_VERSION		0x0
+#define USB_C_DP_SID		0xFF01
+
+enum dp_usbpd_pin_assignment {
+	DP_USBPD_PIN_A,
+	DP_USBPD_PIN_B,
+	DP_USBPD_PIN_C,
+	DP_USBPD_PIN_D,
+	DP_USBPD_PIN_E,
+	DP_USBPD_PIN_F,
+	DP_USBPD_PIN_MAX,
+};
+
+struct dp_usbpd_capabilities {
+	enum dp_usbpd_port port;
+	bool receptacle_state;
+	u8 ulink_pin_config;
+	u8 dlink_pin_config;
+};
+
+struct dp_extcon_private {
+	u32 vdo;
+	struct device *dev;
+	struct notifier_block extcon_nb;
+	struct extcon_dev *extcon;
+	struct workqueue_struct *extcon_wq;
+	struct work_struct event_work;
+	struct usbpd *pd;
+	struct dp_usbpd_cb *dp_cb;
+	struct dp_usbpd_capabilities cap;
+	struct dp_usbpd dp_usbpd;
+};
+
+static const char *dp_usbpd_pin_name(u8 pin)
+{
+	switch (pin) {
+	case DP_USBPD_PIN_A: return "DP_USBPD_PIN_ASSIGNMENT_A";
+	case DP_USBPD_PIN_B: return "DP_USBPD_PIN_ASSIGNMENT_B";
+	case DP_USBPD_PIN_C: return "DP_USBPD_PIN_ASSIGNMENT_C";
+	case DP_USBPD_PIN_D: return "DP_USBPD_PIN_ASSIGNMENT_D";
+	case DP_USBPD_PIN_E: return "DP_USBPD_PIN_ASSIGNMENT_E";
+	case DP_USBPD_PIN_F: return "DP_USBPD_PIN_ASSIGNMENT_F";
+	default: return "UNKNOWN";
+	}
+}
+
+static const char *dp_usbpd_port_name(enum dp_usbpd_port port)
+{
+	switch (port) {
+	case DP_USBPD_PORT_NONE: return "DP_USBPD_PORT_NONE";
+	case DP_USBPD_PORT_UFP_D: return "DP_USBPD_PORT_UFP_D";
+	case DP_USBPD_PORT_DFP_D: return "DP_USBPD_PORT_DFP_D";
+	case DP_USBPD_PORT_D_UFP_D: return "DP_USBPD_PORT_D_UFP_D";
+	default: return "DP_USBPD_PORT_NONE";
+	}
+}
+
+static void dp_usbpd_init_port(enum dp_usbpd_port *port, u32 in_port)
+{
+	switch (in_port) {
+	case 0:
+		*port = DP_USBPD_PORT_NONE;
+		break;
+	case 1:
+		*port = DP_USBPD_PORT_UFP_D;
+		break;
+	case 2:
+		*port = DP_USBPD_PORT_DFP_D;
+		break;
+	case 3:
+		*port = DP_USBPD_PORT_D_UFP_D;
+		break;
+	default:
+		*port = DP_USBPD_PORT_NONE;
+	}
+	pr_debug("port:%s\n", dp_usbpd_port_name(*port));
+}
+
+void dp_usbpd_get_capabilities(struct dp_extcon_private *pd)
+{
+	struct dp_usbpd_capabilities *cap = &pd->cap;
+	u32 buf = pd->vdo;
+	int port = buf & 0x3;
+
+	cap->receptacle_state = (buf & BIT(6)) ? true : false;
+	cap->dlink_pin_config = (buf >> 8) & 0xff;
+	cap->ulink_pin_config = (buf >> 16) & 0xff;
+
+	dp_usbpd_init_port(&cap->port, port);
+}
+
+void dp_usbpd_get_status(struct dp_extcon_private *pd)
+{
+	struct dp_usbpd *status = &pd->dp_usbpd;
+	u32 buf = pd->vdo;
+	int port = buf & 0x3;
+
+	status->low_pow_st     = (buf & BIT(2)) ? true : false;
+	status->adaptor_dp_en  = (buf & BIT(3)) ? true : false;
+	status->multi_func     = (buf & BIT(4)) ? true : false;
+	status->usb_config_req = (buf & BIT(5)) ? true : false;
+	status->exit_dp_mode   = (buf & BIT(6)) ? true : false;
+	status->hpd_high       = (buf & BIT(7)) ? true : false;
+	status->hpd_irq        = (buf & BIT(8)) ? true : false;
+
+	pr_debug("low_pow_st = %d, adaptor_dp_en = %d, multi_func = %d\n",
+			status->low_pow_st, status->adaptor_dp_en,
+			status->multi_func);
+	pr_debug("usb_config_req = %d, exit_dp_mode = %d, hpd_high =%d\n",
+			status->usb_config_req,
+			status->exit_dp_mode, status->hpd_high);
+	pr_debug("hpd_irq = %d\n", status->hpd_irq);
+
+	dp_usbpd_init_port(&status->port, port);
+}
+
+u32 dp_usbpd_gen_config_pkt(struct dp_extcon_private *pd)
+{
+	u8 pin_cfg, pin;
+	u32 config = 0;
+	const u32 ufp_d_config = 0x2, dp_ver = 0x1;
+
+	if (pd->cap.receptacle_state)
+		pin_cfg = pd->cap.ulink_pin_config;
+	else
+		pin_cfg = pd->cap.dlink_pin_config;
+
+	for (pin = DP_USBPD_PIN_A; pin < DP_USBPD_PIN_MAX; pin++) {
+		if (pin_cfg & BIT(pin)) {
+			if (pd->dp_usbpd.multi_func) {
+				if (pin == DP_USBPD_PIN_D)
+					break;
+			} else {
+				break;
+			}
+		}
+	}
+
+	if (pin == DP_USBPD_PIN_MAX)
+		pin = DP_USBPD_PIN_C;
+
+	pr_debug("pin assignment: %s\n", dp_usbpd_pin_name(pin));
+
+	config |= BIT(pin) << 8;
+
+	config |= (dp_ver << 2);
+	config |= ufp_d_config;
+
+	pr_debug("config = 0x%x\n", config);
+	return config;
+}
+
+static int dp_extcon_connect(struct dp_usbpd *dp_usbpd, bool hpd)
+{
+	int rc = 0;
+	struct dp_extcon_private *extcon;
+
+	if (!dp_usbpd) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	extcon = container_of(dp_usbpd, struct dp_extcon_private, dp_usbpd);
+
+	dp_usbpd->hpd_high = hpd;
+	dp_usbpd->forced_disconnect = !hpd;
+
+	if (hpd)
+		extcon->dp_cb->configure(extcon->dev);
+	else
+		extcon->dp_cb->disconnect(extcon->dev);
+
+error:
+	return rc;
+}
+
+static int dp_extcon_get_lanes(struct dp_extcon_private *extcon_priv)
+{
+	union extcon_property_value property;
+	u8 lanes;
+
+	if (!extcon_priv || !extcon_priv->extcon) {
+		pr_err("Invalid input arguments\n");
+		return 0;
+	}
+
+	extcon_get_property(extcon_priv->extcon,
+					EXTCON_DISP_DP,
+					EXTCON_PROP_USB_SS,
+					&property);
+	lanes = ((property.intval) ? 2 : 4);
+
+	return lanes;
+}
+
+
+static void dp_extcon_event_work(struct work_struct *work)
+{
+	struct dp_extcon_private *extcon_priv;
+	int dp_state, ret;
+	int lanes;
+	union extcon_property_value property;
+
+	extcon_priv = container_of(work,
+			struct dp_extcon_private, event_work);
+
+	if (!extcon_priv || !extcon_priv->extcon) {
+		pr_err("Invalid extcon device handler\n");
+		return;
+	}
+
+	dp_state = extcon_get_state(extcon_priv->extcon,
+						EXTCON_DISP_DP);
+
+	if (dp_state > 0) {
+		ret = extcon_get_property(extcon_priv->extcon,
+					EXTCON_DISP_DP,
+					EXTCON_PROP_USB_TYPEC_POLARITY,
+					&property);
+		if (ret) {
+			pr_err("Get Polarity property failed\n");
+			return;
+		}
+		extcon_priv->dp_usbpd.orientation =
+			((property.intval) ?
+				ORIENTATION_CC2 : ORIENTATION_CC1);
+
+		lanes = dp_extcon_get_lanes(extcon_priv);
+		if (!lanes)
+			return;
+		extcon_priv->dp_usbpd.multi_func =
+					((lanes == 2) ? false : true);
+
+		if (extcon_priv->dp_cb && extcon_priv->dp_cb->configure)
+			dp_extcon_connect(&extcon_priv->dp_usbpd, true);
+	} else {
+		if (extcon_priv->dp_cb && extcon_priv->dp_cb->disconnect)
+			dp_extcon_connect(&extcon_priv->dp_usbpd, false);
+	}
+}
+
+static int dp_extcon_create_workqueue(struct dp_extcon_private *extcon_priv)
+{
+	extcon_priv->extcon_wq = create_singlethread_workqueue("drm_dp_extcon");
+	if (IS_ERR_OR_NULL(extcon_priv->extcon_wq)) {
+		pr_err("Error creating extcon wq\n");
+		return -EPERM;
+	}
+
+	INIT_WORK(&extcon_priv->event_work, dp_extcon_event_work);
+
+	return 0;
+}
+
+static int dp_extcon_event_notify(struct notifier_block *nb,
+				  unsigned long event, void *priv)
+{
+	struct dp_extcon_private *extcon_priv;
+
+	extcon_priv = container_of(nb, struct dp_extcon_private,
+						extcon_nb);
+
+	queue_work(extcon_priv->extcon_wq, &extcon_priv->event_work);
+	return NOTIFY_DONE;
+}
+
+int dp_extcon_register(struct dp_usbpd *dp_usbpd)
+{
+	struct dp_extcon_private *extcon_priv;
+	int ret = 0;
+
+	if (!dp_usbpd)
+		return -EINVAL;
+
+	pr_err("Start++++");
+	extcon_priv = container_of(dp_usbpd, struct dp_extcon_private, dp_usbpd);
+
+	extcon_priv->extcon_nb.notifier_call = dp_extcon_event_notify;
+	ret = devm_extcon_register_notifier(extcon_priv->dev, extcon_priv->extcon,
+					    EXTCON_DISP_DP,
+					    &extcon_priv->extcon_nb);
+	if (ret) {
+		dev_err(extcon_priv->dev,
+			"register EXTCON_DISP_DP notifier err\n");
+		ret = -EINVAL;
+	}
+
+	ret = dp_extcon_create_workqueue(extcon_priv);
+	if (ret) {
+		pr_err("Failed to create workqueue\n");
+		dp_extcon_unregister(dp_usbpd);
+	}
+
+	pr_err("End----");
+	return ret;
+}
+
+void dp_extcon_unregister(struct dp_usbpd *dp_usbpd)
+{
+	struct dp_extcon_private *extcon_priv;
+
+	if (!dp_usbpd) {
+		pr_err("Invalid input\n");
+		return;
+	}
+
+	pr_err("Start++++");
+	extcon_priv = container_of(dp_usbpd, struct dp_extcon_private, dp_usbpd);
+
+	devm_extcon_unregister_notifier(extcon_priv->dev, extcon_priv->extcon,
+					    EXTCON_DISP_DP,
+					    &extcon_priv->extcon_nb);
+
+	if (extcon_priv->extcon_wq)
+		destroy_workqueue(extcon_priv->extcon_wq);
+
+	pr_err("End----");
+	return;
+}
+
+struct dp_usbpd *dp_extcon_get(struct device *dev, struct dp_usbpd_cb *cb)
+{
+	int rc = 0;
+	struct dp_extcon_private *dp_extcon;
+	struct dp_usbpd *dp_usbpd;
+
+	if (!cb) {
+		pr_err("invalid cb data\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	pr_err("%s: Start", __func__);
+	dp_extcon = devm_kzalloc(dev, sizeof(*dp_extcon), GFP_KERNEL);
+	if (!dp_extcon) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	dp_extcon->extcon = extcon_get_edev_by_phandle(dev, 0);
+	if (!dp_extcon->extcon) {
+		pr_err("invalid extcon data\n");
+		rc = -EINVAL;
+		devm_kfree(dev, dp_extcon);
+		goto error;
+        }
+
+	dp_extcon->dev = dev;
+	dp_extcon->dp_cb = cb;
+
+	dp_extcon->dp_usbpd.connect = dp_extcon_connect;
+	dp_usbpd = &dp_extcon->dp_usbpd;
+
+	pr_err("%s: end", __func__);
+	return dp_usbpd;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_extcon_put(struct dp_usbpd *dp_usbpd)
+{
+	struct dp_extcon_private *extcon;
+
+	if (!dp_usbpd)
+		return;
+
+	extcon = container_of(dp_usbpd, struct dp_extcon_private, dp_usbpd);
+
+	devm_kfree(extcon->dev, extcon);
+
+	pr_err("%s: end", __func__);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_extcon.h b/drivers/gpu/drm/msm/dp/dp_extcon.h
new file mode 100644
index 0000000..1f83a18
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_extcon.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_EXTCON_H_
+#define _DP_EXTCON_H_
+
+//#include <linux/usb/usbpd.h>
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+/**
+ * enum dp_usbpd_port - usb/dp port type
+ * @DP_USBPD_PORT_NONE: port not configured
+ * @DP_USBPD_PORT_UFP_D: Upstream Facing Port - DisplayPort
+ * @DP_USBPD_PORT_DFP_D: Downstream Facing Port - DisplayPort
+ * @DP_USBPD_PORT_D_UFP_D: Both UFP & DFP - DisplayPort
+ */
+
+enum dp_usbpd_port {
+	DP_USBPD_PORT_NONE,
+	DP_USBPD_PORT_UFP_D,
+	DP_USBPD_PORT_DFP_D,
+	DP_USBPD_PORT_D_UFP_D,
+};
+
+enum plug_orientation {
+	ORIENTATION_NONE,
+	ORIENTATION_CC1,
+	ORIENTATION_CC2,
+};
+
+/**
+ * struct dp_usbpd - DisplayPort status
+ *
+ * @port: port configured
+ * orientation: plug orientation configuration
+ * @low_pow_st: low power state
+ * @adaptor_dp_en: adaptor functionality enabled
+ * @multi_func: multi-function preferred
+ * @usb_config_req: request to switch to usb
+ * @exit_dp_mode: request exit from displayport mode
+ * @hpd_high: Hot Plug Detect signal is high.
+ * @hpd_irq: Change in the status since last message
+ * @alt_mode_cfg_done: bool to specify alt mode status
+ * @debug_en: bool to specify debug mode
+ * @connect: simulate disconnect or connect for debug mode
+ */
+struct dp_usbpd {
+	enum dp_usbpd_port port;
+	enum plug_orientation orientation;
+	bool low_pow_st;
+	bool adaptor_dp_en;
+	bool multi_func;
+	bool usb_config_req;
+	bool exit_dp_mode;
+	bool hpd_high;
+	bool hpd_irq;
+	bool alt_mode_cfg_done;
+	bool debug_en;
+	bool forced_disconnect;
+
+	int (*connect)(struct dp_usbpd *dp_usbpd, bool hpd);
+};
+
+/**
+ * struct dp_usbpd_cb - callback functions provided by the client
+ *
+ * @configure: called by usbpd module when PD communication has
+ * been completed and the usb peripheral has been configured on
+ * dp mode.
+ * @disconnect: notify the cable disconnect issued by usb.
+ * @attention: notify any attention message issued by usb.
+ */
+struct dp_usbpd_cb {
+	int (*configure)(struct device *dev);
+	int (*disconnect)(struct device *dev);
+	int (*attention)(struct device *dev);
+};
+
+/**
+ * dp_extcon_get() - setup usbpd module
+ *
+ * @dev: device instance of the caller
+ * @cb: struct containing callback function pointers.
+ *
+ * This function allows the client to initialize the usbpd
+ * module. The module will communicate with usb driver and
+ * handles the power delivery (PD) communication with the
+ * sink/usb device. This module will notify the client using
+ * the callback functions about the connection and status.
+ */
+struct dp_usbpd *dp_extcon_get(struct device *dev, struct dp_usbpd_cb *cb);
+
+void dp_extcon_put(struct dp_usbpd *pd);
+
+int dp_extcon_register(struct dp_usbpd *dp_usbpd);
+void dp_extcon_unregister(struct dp_usbpd *dp_usbpd);
+
+#endif /* _DP_EXTCON_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
new file mode 100644
index 0000000..5ab1efc
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -0,0 +1,1549 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include "dp_link.h"
+#include "dp_panel.h"
+
+enum dynamic_range {
+	DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
+	DP_DYNAMIC_RANGE_RGB_CEA = 0x01,
+	DP_DYNAMIC_RANGE_UNKNOWN = 0xFFFFFFFF,
+};
+
+enum audio_sample_rate {
+	AUDIO_SAMPLE_RATE_32_KHZ	= 0x00,
+	AUDIO_SAMPLE_RATE_44_1_KHZ	= 0x01,
+	AUDIO_SAMPLE_RATE_48_KHZ	= 0x02,
+	AUDIO_SAMPLE_RATE_88_2_KHZ	= 0x03,
+	AUDIO_SAMPLE_RATE_96_KHZ	= 0x04,
+	AUDIO_SAMPLE_RATE_176_4_KHZ	= 0x05,
+	AUDIO_SAMPLE_RATE_192_KHZ	= 0x06,
+};
+
+enum audio_pattern_type {
+	AUDIO_TEST_PATTERN_OPERATOR_DEFINED	= 0x00,
+	AUDIO_TEST_PATTERN_SAWTOOTH		= 0x01,
+};
+
+struct dp_link_request {
+	u32 test_requested;
+	u32 test_link_rate;
+	u32 test_lane_count;
+};
+
+struct dp_link_private {
+	u32 prev_sink_count;
+	struct device *dev;
+	struct dp_aux *aux;
+	struct dp_link dp_link;
+
+	struct dp_link_request request;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+};
+
+static char *dp_link_get_audio_test_pattern(u32 pattern)
+{
+	switch (pattern) {
+	case AUDIO_TEST_PATTERN_OPERATOR_DEFINED:
+		return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_OPERATOR_DEFINED);
+	case AUDIO_TEST_PATTERN_SAWTOOTH:
+		return DP_LINK_ENUM_STR(AUDIO_TEST_PATTERN_SAWTOOTH);
+	default:
+		return "unknown";
+	}
+}
+
+static char *dp_link_get_audio_sample_rate(u32 rate)
+{
+	switch (rate) {
+	case AUDIO_SAMPLE_RATE_32_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_32_KHZ);
+	case AUDIO_SAMPLE_RATE_44_1_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_44_1_KHZ);
+	case AUDIO_SAMPLE_RATE_48_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_48_KHZ);
+	case AUDIO_SAMPLE_RATE_88_2_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_88_2_KHZ);
+	case AUDIO_SAMPLE_RATE_96_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_96_KHZ);
+	case AUDIO_SAMPLE_RATE_176_4_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_176_4_KHZ);
+	case AUDIO_SAMPLE_RATE_192_KHZ:
+		return DP_LINK_ENUM_STR(AUDIO_SAMPLE_RATE_192_KHZ);
+	default:
+		return "unknown";
+	}
+}
+
+static int dp_link_get_period(struct dp_link_private *link, int const addr)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	u32 const param_len = 0x1;
+	u32 const max_audio_period = 0xA;
+
+	/* TEST_AUDIO_PERIOD_CH_XX */
+	if (drm_dp_dpcd_read(link->aux->drm_aux, addr, &bp,
+		param_len) < param_len) {
+		pr_err("failed to read test_audio_period (0x%x)\n", addr);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	data = bp;
+
+	/* Period - Bits 3:0 */
+	data = data & 0xF;
+	if ((int)data > max_audio_period) {
+		pr_err("invalid test_audio_period_ch_1 = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	ret = data;
+exit:
+	return ret;
+}
+
+static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
+{
+	int ret = 0;
+	struct dp_link_test_audio *req = &link->dp_link.test_audio;
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH1);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_1 = ret;
+	pr_debug("test_audio_period_ch_1 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH2);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_2 = ret;
+	pr_debug("test_audio_period_ch_2 = 0x%x\n", ret);
+
+	/* TEST_AUDIO_PERIOD_CH_3 (Byte 0x275) */
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH3);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_3 = ret;
+	pr_debug("test_audio_period_ch_3 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH4);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_4 = ret;
+	pr_debug("test_audio_period_ch_4 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH5);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_5 = ret;
+	pr_debug("test_audio_period_ch_5 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH6);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_6 = ret;
+	pr_debug("test_audio_period_ch_6 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH7);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_7 = ret;
+	pr_debug("test_audio_period_ch_7 = 0x%x\n", ret);
+
+	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH8);
+	if (ret == -EINVAL)
+		goto exit;
+
+	req->test_audio_period_ch_8 = ret;
+	pr_debug("test_audio_period_ch_8 = 0x%x\n", ret);
+exit:
+	return ret;
+}
+
+static int dp_link_parse_audio_pattern_type(struct dp_link_private *link)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	int rlen;
+	int const param_len = 0x1;
+	int const max_audio_pattern_type = 0x1;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux,
+		DP_TEST_AUDIO_PATTERN_TYPE, &bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read link audio mode data\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	/* Audio Pattern Type - Bits 7:0 */
+	if ((int)data > max_audio_pattern_type) {
+		pr_err("invalid audio pattern type = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_audio.test_audio_pattern_type = data;
+	pr_debug("audio pattern type = %s\n",
+			dp_link_get_audio_test_pattern(data));
+exit:
+	return ret;
+}
+
+static int dp_link_parse_audio_mode(struct dp_link_private *link)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	int rlen;
+	int const param_len = 0x1;
+	int const max_audio_sampling_rate = 0x6;
+	int const max_audio_channel_count = 0x8;
+	int sampling_rate = 0x0;
+	int channel_count = 0x0;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_AUDIO_MODE,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read link audio mode data\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	/* Sampling Rate - Bits 3:0 */
+	sampling_rate = data & 0xF;
+	if (sampling_rate > max_audio_sampling_rate) {
+		pr_err("sampling rate (0x%x) greater than max (0x%x)\n",
+				sampling_rate, max_audio_sampling_rate);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/* Channel Count - Bits 7:4 */
+	channel_count = ((data & 0xF0) >> 4) + 1;
+	if (channel_count > max_audio_channel_count) {
+		pr_err("channel_count (0x%x) greater than max (0x%x)\n",
+				channel_count, max_audio_channel_count);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_audio.test_audio_sampling_rate = sampling_rate;
+	link->dp_link.test_audio.test_audio_channel_count = channel_count;
+	pr_debug("sampling_rate = %s, channel_count = 0x%x\n",
+		dp_link_get_audio_sample_rate(sampling_rate), channel_count);
+exit:
+	return ret;
+}
+
+/**
+ * dp_parse_audio_pattern_params() - parses audio pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the audio link pattern parameters.
+ */
+static int dp_link_parse_audio_pattern_params(struct dp_link_private *link)
+{
+	int ret = 0;
+
+	ret = dp_link_parse_audio_mode(link);
+	if (ret)
+		goto exit;
+
+	ret = dp_link_parse_audio_pattern_type(link);
+	if (ret)
+		goto exit;
+
+	ret = dp_link_parse_audio_channel_period(link);
+
+exit:
+	return ret;
+}
+
+/**
+ * dp_link_is_video_pattern_valid() - validates the video pattern
+ * @pattern: video pattern requested by the sink
+ *
+ * Returns true if the requested video pattern is supported.
+ */
+static bool dp_link_is_video_pattern_valid(u32 pattern)
+{
+	switch (pattern) {
+	case DP_NO_TEST_PATTERN:
+	case DP_COLOR_RAMP:
+	case DP_BLACK_AND_WHITE_VERTICAL_LINES:
+	case DP_COLOR_SQUARE:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static char *dp_link_video_pattern_to_string(u32 test_video_pattern)
+{
+	switch (test_video_pattern) {
+	case DP_NO_TEST_PATTERN:
+		return DP_LINK_ENUM_STR(DP_NO_TEST_PATTERN);
+	case DP_COLOR_RAMP:
+		return DP_LINK_ENUM_STR(DP_COLOR_RAMP);
+	case DP_BLACK_AND_WHITE_VERTICAL_LINES:
+		return DP_LINK_ENUM_STR(DP_BLACK_AND_WHITE_VERTICAL_LINES);
+	case DP_COLOR_SQUARE:
+		return DP_LINK_ENUM_STR(DP_COLOR_SQUARE);
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * dp_link_is_dynamic_range_valid() - validates the dynamic range
+ * @bit_depth: the dynamic range value to be checked
+ *
+ * Returns true if the dynamic range value is supported.
+ */
+static bool dp_link_is_dynamic_range_valid(u32 dr)
+{
+	switch (dr) {
+	case DP_DYNAMIC_RANGE_RGB_VESA:
+	case DP_DYNAMIC_RANGE_RGB_CEA:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static char *dp_link_dynamic_range_to_string(u32 dr)
+{
+	switch (dr) {
+	case DP_DYNAMIC_RANGE_RGB_VESA:
+		return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_VESA);
+	case DP_DYNAMIC_RANGE_RGB_CEA:
+		return DP_LINK_ENUM_STR(DP_DYNAMIC_RANGE_RGB_CEA);
+	case DP_DYNAMIC_RANGE_UNKNOWN:
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * dp_link_is_bit_depth_valid() - validates the bit depth requested
+ * @bit_depth: bit depth requested by the sink
+ *
+ * Returns true if the requested bit depth is supported.
+ */
+static bool dp_link_is_bit_depth_valid(u32 tbd)
+{
+	/* DP_TEST_VIDEO_PATTERN_NONE is treated as invalid */
+	switch (tbd) {
+	case DP_TEST_BIT_DEPTH_6:
+	case DP_TEST_BIT_DEPTH_8:
+	case DP_TEST_BIT_DEPTH_10:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static char *dp_link_bit_depth_to_string(u32 tbd)
+{
+	switch (tbd) {
+	case DP_TEST_BIT_DEPTH_6:
+		return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_6);
+	case DP_TEST_BIT_DEPTH_8:
+		return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_8);
+	case DP_TEST_BIT_DEPTH_10:
+		return DP_LINK_ENUM_STR(DP_TEST_BIT_DEPTH_10);
+	case DP_TEST_BIT_DEPTH_UNKNOWN:
+	default:
+		return "unknown";
+	}
+}
+
+static int dp_link_parse_timing_params1(struct dp_link_private *link,
+					int const addr, int const len, u32 *val)
+{
+	u8 bp[2];
+	int rlen;
+
+	if (len < 2)
+		return -EINVAL;
+
+	/* Read the requested video link pattern (Byte 0x221). */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, addr, bp, len);
+	if (rlen < len) {
+		pr_err("failed to read 0x%x\n", addr);
+		return -EINVAL;
+	}
+
+	*val = bp[1] | (bp[0] << 8);
+
+	return 0;
+}
+
+static int dp_link_parse_timing_params2(struct dp_link_private *link,
+					int const addr, int const len,
+					u32 *val1, u32 *val2)
+{
+	u8 bp[2];
+	int rlen;
+
+	if (len < 2)
+		return -EINVAL;
+
+	/* Read the requested video link pattern (Byte 0x221). */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, addr, bp, len);
+	if (rlen < len) {
+		pr_err("failed to read 0x%x\n", addr);
+		return -EINVAL;
+	}
+
+	*val1 = (bp[0] & BIT(7)) >> 7;
+	*val2 = bp[1] | ((bp[0] & 0x7F) << 8);
+
+	return 0;
+}
+
+static int dp_link_parse_timing_params3(struct dp_link_private *link,
+					int const addr, u32 *val)
+{
+	u8 bp;
+	u32 len = 1;
+	int rlen;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, addr, &bp, len);
+	if (rlen < 1) {
+		pr_err("failed to read 0x%x\n", addr);
+		return -EINVAL;
+	}
+	*val = bp;
+
+	return 0;
+}
+
+/**
+ * dp_parse_video_pattern_params() - parses video pattern parameters from DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the video link pattern and the link
+ * bit depth requested by the sink and, and if the values parsed are valid.
+ */
+static int dp_link_parse_video_pattern_params(struct dp_link_private *link)
+{
+	int ret = 0;
+	int rlen;
+	u8 bp;
+	u8 data;
+	u32 dyn_range;
+	int const param_len = 0x1;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_PATTERN,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read link video pattern\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	if (!dp_link_is_video_pattern_valid(data)) {
+		pr_err("invalid link video pattern = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_video.test_video_pattern = data;
+	pr_debug("link video pattern = 0x%x (%s)\n",
+		link->dp_link.test_video.test_video_pattern,
+		dp_link_video_pattern_to_string(
+			link->dp_link.test_video.test_video_pattern));
+
+	/* Read the requested color bit depth and dynamic range (Byte 0x232) */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_MISC0,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read link bit depth\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	/* Dynamic Range */
+	dyn_range = (data & DP_TEST_DYNAMIC_RANGE_CEA) >> 3;
+	if (!dp_link_is_dynamic_range_valid(dyn_range)) {
+		pr_err("invalid link dynamic range = 0x%x", dyn_range);
+		ret = -EINVAL;
+		goto exit;
+	}
+	link->dp_link.test_video.test_dyn_range = dyn_range;
+	pr_debug("link dynamic range = 0x%x (%s)\n",
+		link->dp_link.test_video.test_dyn_range,
+		dp_link_dynamic_range_to_string(
+			link->dp_link.test_video.test_dyn_range));
+
+	/* Color bit depth */
+	data &= DP_TEST_BIT_DEPTH_MASK;
+	if (!dp_link_is_bit_depth_valid(data)) {
+		pr_err("invalid link bit depth = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->dp_link.test_video.test_bit_depth = data;
+	pr_debug("link bit depth = 0x%x (%s)\n",
+		link->dp_link.test_video.test_bit_depth,
+		dp_link_bit_depth_to_string(
+		link->dp_link.test_video.test_bit_depth));
+
+	/* resolution timing params */
+	ret = dp_link_parse_timing_params1(link, DP_TEST_H_TOTAL_HI, 2,
+			&link->dp_link.test_video.test_h_total);
+	if (ret) {
+		pr_err("failed to parse test_h_total (DP_TEST_H_TOTAL_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_H_TOTAL = %d\n", link->dp_link.test_video.test_h_total);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_V_TOTAL_HI, 2,
+			&link->dp_link.test_video.test_v_total);
+	if (ret) {
+		pr_err("failed to parse test_v_total (DP_TEST_V_TOTAL_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_V_TOTAL = %d\n", link->dp_link.test_video.test_v_total);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_H_START_HI, 2,
+			&link->dp_link.test_video.test_h_start);
+	if (ret) {
+		pr_err("failed to parse test_h_start (DP_TEST_H_START_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_H_START = %d\n", link->dp_link.test_video.test_h_start);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_V_START_HI, 2,
+			&link->dp_link.test_video.test_v_start);
+	if (ret) {
+		pr_err("failed to parse test_v_start (DP_TEST_V_START_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_V_START = %d\n", link->dp_link.test_video.test_v_start);
+
+	ret = dp_link_parse_timing_params2(link, DP_TEST_HSYNC_HI, 2,
+			&link->dp_link.test_video.test_hsync_pol,
+			&link->dp_link.test_video.test_hsync_width);
+	if (ret) {
+		pr_err("failed to parse (DP_TEST_HSYNC_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_HSYNC_POL = %d\n",
+		link->dp_link.test_video.test_hsync_pol);
+	pr_debug("TEST_HSYNC_WIDTH = %d\n",
+		link->dp_link.test_video.test_hsync_width);
+
+	ret = dp_link_parse_timing_params2(link, DP_TEST_VSYNC_HI, 2,
+			&link->dp_link.test_video.test_vsync_pol,
+			&link->dp_link.test_video.test_vsync_width);
+	if (ret) {
+		pr_err("failed to parse (DP_TEST_VSYNC_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_VSYNC_POL = %d\n",
+		link->dp_link.test_video.test_vsync_pol);
+	pr_debug("TEST_VSYNC_WIDTH = %d\n",
+		link->dp_link.test_video.test_vsync_width);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_H_WIDTH_HI, 2,
+			&link->dp_link.test_video.test_h_width);
+	if (ret) {
+		pr_err("failed to parse test_h_width (DP_TEST_H_WIDTH_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_H_WIDTH = %d\n", link->dp_link.test_video.test_h_width);
+
+	ret = dp_link_parse_timing_params1(link, DP_TEST_V_HEIGHT_HI, 2,
+			&link->dp_link.test_video.test_v_height);
+	if (ret) {
+		pr_err("failed to parse test_v_height (DP_TEST_V_HEIGHT_HI)\n");
+		goto exit;
+	}
+	pr_debug("TEST_V_HEIGHT = %d\n",
+		link->dp_link.test_video.test_v_height);
+
+	ret = dp_link_parse_timing_params3(link, DP_TEST_MISC1,
+		&link->dp_link.test_video.test_rr_d);
+	link->dp_link.test_video.test_rr_d &= DP_TEST_REFRESH_DENOMINATOR;
+	if (ret) {
+		pr_err("failed to parse test_rr_d (DP_TEST_MISC1)\n");
+		goto exit;
+	}
+	pr_debug("TEST_REFRESH_DENOMINATOR = %d\n",
+		link->dp_link.test_video.test_rr_d);
+
+	ret = dp_link_parse_timing_params3(link, DP_TEST_REFRESH_RATE_NUMERATOR,
+		&link->dp_link.test_video.test_rr_n);
+	if (ret) {
+		pr_err("failed to parse test_rr_n (DP_TEST_REFRESH_RATE_NUMERATOR)\n");
+		goto exit;
+	}
+	pr_debug("TEST_REFRESH_NUMERATOR = %d\n",
+		link->dp_link.test_video.test_rr_n);
+exit:
+	return ret;
+}
+
+/**
+ * dp_link_parse_link_training_params() - parses link training parameters from
+ * DPCD
+ * @link: Display Port Driver data
+ *
+ * Returns 0 if it successfully parses the link rate (Byte 0x219) and lane
+ * count (Byte 0x220), and if these values parse are valid.
+ */
+static int dp_link_parse_link_training_params(struct dp_link_private *link)
+{
+	u8 bp;
+	u8 data;
+	int ret = 0;
+	int rlen;
+	int const param_len = 0x1;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_LINK_RATE,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read link rate\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+
+	if (!is_link_rate_valid(data)) {
+		pr_err("invalid link rate = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->request.test_link_rate = data;
+	pr_debug("link rate = 0x%x\n", link->request.test_link_rate);
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_LANE_COUNT,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read lane count\n");
+		ret = -EINVAL;
+		goto exit;
+	}
+	data = bp;
+	data &= 0x1F;
+
+	if (!is_lane_count_valid(data)) {
+		pr_err("invalid lane count = 0x%x\n", data);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	link->request.test_lane_count = data;
+	pr_debug("lane count = 0x%x\n", link->request.test_lane_count);
+exit:
+	return ret;
+}
+
+static bool dp_link_is_phy_test_pattern_supported(u32 phy_test_pattern_sel)
+{
+	switch (phy_test_pattern_sel) {
+	case DP_TEST_PHY_PATTERN_NONE:
+	case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
+	case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+	case DP_TEST_PHY_PATTERN_PRBS7:
+	case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
+	case DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/**
+ * dp_parse_phy_test_params() - parses the phy link parameters
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD (Byte 0x248) for the DP PHY link pattern that is being
+ * requested.
+ */
+static int dp_link_parse_phy_test_params(struct dp_link_private *link)
+{
+	u8 bp;
+	u8 data;
+	int rlen;
+	int const param_len = 0x1;
+	int ret = 0;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_PHY_PATTERN,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read phy link pattern\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	link->dp_link.phy_params.phy_test_pattern_sel = data;
+
+	pr_debug("phy_test_pattern_sel = %s\n",
+			dp_link_get_phy_test_pattern(data));
+
+	if (!dp_link_is_phy_test_pattern_supported(data))
+		ret = -EINVAL;
+end:
+	return ret;
+}
+
+static char *dp_link_get_test_name(u32 test_requested)
+{
+	switch (test_requested) {
+	case DP_TEST_LINK_TRAINING:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_TRAINING);
+	case DP_TEST_LINK_VIDEO_PATTERN:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_VIDEO_PATTERN);
+	case DP_TEST_LINK_EDID_READ:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_EDID_READ);
+	case DP_TEST_LINK_PHY_TEST_PATTERN:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_PHY_TEST_PATTERN);
+	case DP_TEST_LINK_AUDIO_PATTERN:
+		return DP_LINK_ENUM_STR(DP_TEST_LINK_AUDIO_PATTERN);
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * dp_link_is_video_audio_test_requested() - checks for audio/video link request
+ * @link: link requested by the sink
+ *
+ * Returns true if the requested link is a permitted audio/video link.
+ */
+static bool dp_link_is_video_audio_test_requested(u32 link)
+{
+	return (link == DP_TEST_LINK_VIDEO_PATTERN) ||
+		(link == (DP_TEST_LINK_AUDIO_PATTERN |
+		DP_TEST_LINK_VIDEO_PATTERN)) ||
+		(link == DP_TEST_LINK_AUDIO_PATTERN) ||
+		(link == (DP_TEST_LINK_AUDIO_PATTERN |
+		DP_TEST_LINK_AUDIO_DISABLED_VIDEO));
+}
+
+/**
+ * dp_link_supported() - checks if link requested by sink is supported
+ * @test_requested: link requested by the sink
+ *
+ * Returns true if the requested link is supported.
+ */
+static bool dp_link_is_test_supported(u32 test_requested)
+{
+	return (test_requested == DP_TEST_LINK_TRAINING) ||
+		(test_requested == DP_TEST_LINK_EDID_READ) ||
+		(test_requested == DP_TEST_LINK_PHY_TEST_PATTERN) ||
+		dp_link_is_video_audio_test_requested(test_requested);
+}
+
+static bool dp_link_is_test_edid_read(struct dp_link_private *link)
+{
+	return (link->request.test_requested == DP_TEST_LINK_EDID_READ);
+}
+
+/**
+ * dp_sink_parse_test_request() - parses link request parameters from sink
+ * @link: Display Port Driver data
+ *
+ * Parses the DPCD to check if an automated link is requested (Byte 0x201),
+ * and what type of link automation is being requested (Byte 0x218).
+ */
+static int dp_link_parse_request(struct dp_link_private *link)
+{
+	int ret = 0;
+	u8 bp;
+	u8 data;
+	int rlen;
+	u32 const param_len = 0x1;
+
+	/**
+	 * Read the device service IRQ vector (Byte 0x201) to determine
+	 * whether an automated link has been requested by the sink.
+	 */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux,
+		DP_DEVICE_SERVICE_IRQ_VECTOR, &bp, param_len);
+	if (rlen < param_len) {
+		pr_err("aux read failed\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	pr_debug("device service irq vector = 0x%x\n", data);
+
+	if (!(data & DP_AUTOMATED_TEST_REQUEST)) {
+		pr_debug("no test requested\n");
+		return 0;
+	}
+
+	/**
+	 * Read the link request byte (Byte 0x218) to determine what type
+	 * of automated link has been requested by the sink.
+	 */
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_REQUEST,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("aux read failed\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	if (!dp_link_is_test_supported(data)) {
+		pr_debug("link 0x%x not supported\n", data);
+		goto end;
+	}
+
+	pr_debug("%s (0x%x) requested\n", dp_link_get_test_name(data), data);
+	link->request.test_requested = data;
+
+	if (link->request.test_requested == DP_TEST_LINK_PHY_TEST_PATTERN) {
+		ret = dp_link_parse_phy_test_params(link);
+		if (ret)
+			goto end;
+		ret = dp_link_parse_link_training_params(link);
+	}
+
+	if (link->request.test_requested == DP_TEST_LINK_TRAINING)
+		ret = dp_link_parse_link_training_params(link);
+
+	if (dp_link_is_video_audio_test_requested(
+			link->request.test_requested)) {
+		ret = dp_link_parse_video_pattern_params(link);
+		if (ret)
+			goto end;
+
+		ret = dp_link_parse_audio_pattern_params(link);
+	}
+end:
+	/**
+	 * Send a DP_TEST_ACK if all link parameters are valid, otherwise send
+	 * a DP_TEST_NAK.
+	 */
+	if (ret) {
+		link->dp_link.test_response = DP_TEST_NAK;
+	} else {
+		if (!dp_link_is_test_edid_read(link))
+			link->dp_link.test_response = DP_TEST_ACK;
+		else
+			link->dp_link.test_response =
+				DP_TEST_EDID_CHECKSUM_WRITE;
+	}
+
+	return ret;
+}
+
+/**
+ * dp_link_parse_sink_count() - parses the sink count
+ *
+ * Parses the DPCD to check if there is an update to the sink count
+ * (Byte 0x200), and whether all the sink devices connected have Content
+ * Protection enabled.
+ */
+static int dp_link_parse_sink_count(struct dp_link *dp_link)
+{
+	int rlen;
+	int const param_len = 0x1;
+	struct dp_link_private *link = container_of(dp_link,
+			struct dp_link_private, dp_link);
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_SINK_COUNT,
+			&link->dp_link.sink_count.count, param_len);
+	if (rlen < param_len) {
+		pr_err("failed to read sink count\n");
+		return -EINVAL;
+	}
+
+	link->dp_link.sink_count.cp_ready =
+		link->dp_link.sink_count.count & DP_SINK_CP_READY;
+	/* BIT 7, BIT 5:0 */
+	link->dp_link.sink_count.count =
+		DP_GET_SINK_COUNT(link->dp_link.sink_count.count);
+
+	pr_debug("sink_count = 0x%x, cp_ready = 0x%x\n",
+		link->dp_link.sink_count.count,
+		link->dp_link.sink_count.cp_ready);
+	return 0;
+}
+
+static void dp_link_parse_sink_status_field(struct dp_link_private *link)
+{
+	int len = 0;
+
+	link->prev_sink_count = link->dp_link.sink_count.count;
+	dp_link_parse_sink_count(&link->dp_link);
+
+	len = drm_dp_dpcd_read_link_status(link->aux->drm_aux,
+		link->link_status);
+	if (len < DP_LINK_STATUS_SIZE)
+		pr_err("DP link status read failed\n");
+	dp_link_parse_request(link);
+}
+
+static bool dp_link_is_link_training_requested(struct dp_link_private *link)
+{
+	return (link->request.test_requested == DP_TEST_LINK_TRAINING);
+}
+
+/**
+ * dp_link_process_link_training_request() - processes new training requests
+ * @link: Display Port link data
+ *
+ * This function will handle new link training requests that are initiated by
+ * the sink. In particular, it will update the requested lane count and link
+ * link rate, and then trigger the link retraining procedure.
+ *
+ * The function will return 0 if a link training request has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_training_request(struct dp_link_private *link)
+{
+	if (!dp_link_is_link_training_requested(link))
+		return -EINVAL;
+
+	pr_debug("%s link rate = 0x%x, lane count = 0x%x\n",
+			dp_link_get_test_name(DP_TEST_LINK_TRAINING),
+			link->request.test_link_rate,
+			link->request.test_lane_count);
+
+	link->dp_link.link_params.lane_count = link->request.test_lane_count;
+	link->dp_link.link_params.bw_code = link->request.test_link_rate;
+
+	return 0;
+}
+
+static void dp_link_send_test_response(struct dp_link *dp_link)
+{
+	struct dp_link_private *link = NULL;
+	u32 const response_len = 0x1;
+
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	drm_dp_dpcd_write(link->aux->drm_aux, DP_TEST_RESPONSE,
+			&dp_link->test_response, response_len);
+}
+
+static int dp_link_psm_config(struct dp_link *dp_link,
+			      struct drm_dp_link *link_info, bool enable)
+{
+	struct dp_link_private *link = NULL;
+	int ret = 0;
+
+	if (!dp_link) {
+		pr_err("invalid params\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	if (enable)
+		ret = drm_dp_link_power_down(link->aux->drm_aux, link_info);
+	else
+		ret = drm_dp_link_power_up(link->aux->drm_aux, link_info);
+
+	if (ret)
+		pr_err("Failed to %s low power mode\n",
+			(enable ? "enter" : "exit"));
+	else
+		dp_link->psm_enabled = enable;
+
+	return ret;
+}
+
+static void dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum)
+{
+	struct dp_link_private *link = NULL;
+	u32 const response_len = 0x1;
+
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	drm_dp_dpcd_write(link->aux->drm_aux, DP_TEST_EDID_CHECKSUM,
+			&checksum, response_len);
+}
+
+static int dp_link_parse_vx_px(struct dp_link_private *link)
+{
+	u8 bp;
+	u8 data;
+	int const param_len = 0x1;
+	int ret = 0;
+	u32 v0, p0, v1, p1, v2, p2, v3, p3;
+	int rlen;
+
+	pr_debug("\n");
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_ADJUST_REQUEST_LANE0_1,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed reading lanes 0/1\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	pr_debug("lanes 0/1 (Byte 0x206): 0x%x\n", data);
+
+	v0 = data & 0x3;
+	data = data >> 2;
+	p0 = data & 0x3;
+	data = data >> 2;
+
+	v1 = data & 0x3;
+	data = data >> 2;
+	p1 = data & 0x3;
+	data = data >> 2;
+
+	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_ADJUST_REQUEST_LANE2_3,
+			&bp, param_len);
+	if (rlen < param_len) {
+		pr_err("failed reading lanes 2/3\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	data = bp;
+
+	pr_debug("lanes 2/3 (Byte 0x207): 0x%x\n", data);
+
+	v2 = data & 0x3;
+	data = data >> 2;
+	p2 = data & 0x3;
+	data = data >> 2;
+
+	v3 = data & 0x3;
+	data = data >> 2;
+	p3 = data & 0x3;
+	data = data >> 2;
+
+	pr_debug("vx: 0=%d, 1=%d, 2=%d, 3=%d\n", v0, v1, v2, v3);
+	pr_debug("px: 0=%d, 1=%d, 2=%d, 3=%d\n", p0, p1, p2, p3);
+
+	/**
+	 * Update the voltage and pre-emphasis levels as per DPCD request
+	 * vector.
+	 */
+	pr_debug("Current: v_level = 0x%x, p_level = 0x%x\n",
+			link->dp_link.phy_params.v_level,
+			link->dp_link.phy_params.p_level);
+	pr_debug("Requested: v_level = 0x%x, p_level = 0x%x\n", v0, p0);
+	link->dp_link.phy_params.v_level = v0;
+	link->dp_link.phy_params.p_level = p0;
+
+	pr_debug("Success\n");
+end:
+	return ret;
+}
+
+/**
+ * dp_link_process_phy_test_pattern_request() - process new phy link requests
+ * @link: Display Port Driver data
+ *
+ * This function will handle new phy link pattern requests that are initiated
+ * by the sink. The function will return 0 if a phy link pattern has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_phy_test_pattern_request(
+		struct dp_link_private *link)
+{
+	u32 test_link_rate = 0, test_lane_count = 0;
+
+	if (!(link->request.test_requested & DP_TEST_LINK_PHY_TEST_PATTERN)) {
+		pr_debug("no phy test\n");
+		return -EINVAL;
+	}
+
+	test_link_rate = link->request.test_link_rate;
+	test_lane_count = link->request.test_lane_count;
+
+	if (!is_link_rate_valid(test_link_rate) ||
+		!is_lane_count_valid(test_lane_count)) {
+		pr_err("Invalid params: link rate = 0x%x, lane count = 0x%x\n",
+				test_link_rate, test_lane_count);
+		return -EINVAL;
+	}
+
+	pr_debug("start\n");
+
+	pr_info("Current: bw_code = 0x%x, lane count = 0x%x\n",
+			link->dp_link.link_params.bw_code,
+			link->dp_link.link_params.lane_count);
+
+	pr_info("Requested: bw_code = 0x%x, lane count = 0x%x\n",
+			test_link_rate, test_lane_count);
+
+	link->dp_link.link_params.lane_count = link->request.test_lane_count;
+	link->dp_link.link_params.bw_code = link->request.test_link_rate;
+
+	dp_link_parse_vx_px(link);
+
+	pr_debug("end\n");
+
+	return 0;
+}
+
+static u8 get_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+	return link_status[r - DP_LANE0_1_STATUS];
+}
+
+/**
+ * dp_link_process_link_status_update() - processes link status updates
+ * @link: Display Port link module data
+ *
+ * This function will check for changes in the link status, e.g. clock
+ * recovery done on all lanes, and trigger link training if there is a
+ * failure/error on the link.
+ *
+ * The function will return 0 if the a link status update has been processed,
+ * otherwise it will return -EINVAL.
+ */
+static int dp_link_process_link_status_update(struct dp_link_private *link)
+{
+	if (!(get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
+		DP_LINK_STATUS_UPDATED) || /* link status updated */
+		(drm_dp_clock_recovery_ok(link->link_status,
+			link->dp_link.link_params.lane_count) &&
+	     drm_dp_channel_eq_ok(link->link_status,
+			link->dp_link.link_params.lane_count)))
+		return -EINVAL;
+
+	pr_debug("channel_eq_done = %d, clock_recovery_done = %d\n",
+			drm_dp_clock_recovery_ok(link->link_status,
+			link->dp_link.link_params.lane_count),
+			drm_dp_clock_recovery_ok(link->link_status,
+			link->dp_link.link_params.lane_count));
+
+	return 0;
+}
+
+static bool dp_link_is_ds_port_status_changed(struct dp_link_private *link)
+{
+	if (get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
+		DP_DOWNSTREAM_PORT_STATUS_CHANGED) /* port status changed */
+		return true;
+
+	if (link->prev_sink_count != link->dp_link.sink_count.count)
+		return true;
+
+	return false;
+}
+
+/**
+ * dp_link_process_downstream_port_status_change() - process port status changes
+ * @link: Display Port Driver data
+ *
+ * This function will handle downstream port updates that are initiated by
+ * the sink. If the downstream port status has changed, the EDID is read via
+ * AUX.
+ *
+ * The function will return 0 if a downstream port update has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int dp_link_process_ds_port_status_change(struct dp_link_private *link)
+{
+	if (!dp_link_is_ds_port_status_changed(link))
+		return -EINVAL;
+
+	/* reset prev_sink_count */
+	link->prev_sink_count = link->dp_link.sink_count.count;
+
+	return 0;
+}
+
+static bool dp_link_is_video_pattern_requested(struct dp_link_private *link)
+{
+	return (link->request.test_requested & DP_TEST_LINK_VIDEO_PATTERN)
+		&& !(link->request.test_requested &
+		DP_TEST_LINK_AUDIO_DISABLED_VIDEO);
+}
+
+static bool dp_link_is_audio_pattern_requested(struct dp_link_private *link)
+{
+	return (link->request.test_requested & DP_TEST_LINK_AUDIO_PATTERN);
+}
+
+/**
+ * dp_link_process_video_pattern_request() - process new video pattern request
+ * @link: Display Port link module's data
+ *
+ * This function will handle a new video pattern request that are initiated by
+ * the sink. This is acheieved by first sending a disconnect notification to
+ * the sink followed by a subsequent connect notification to the user modules,
+ * where it is expected that the user modules would draw the required link
+ * pattern.
+ */
+static int dp_link_process_video_pattern_request(struct dp_link_private *link)
+{
+	if (!dp_link_is_video_pattern_requested(link))
+		goto end;
+
+	pr_debug("%s: bit depth=%d(%d bpp) pattern=%s\n",
+		dp_link_get_test_name(DP_TEST_LINK_VIDEO_PATTERN),
+		link->dp_link.test_video.test_bit_depth,
+		dp_link_bit_depth_to_bpp(
+		link->dp_link.test_video.test_bit_depth),
+		dp_link_video_pattern_to_string(
+			link->dp_link.test_video.test_video_pattern));
+
+	return 0;
+end:
+	return -EINVAL;
+}
+
+/**
+ * dp_link_process_audio_pattern_request() - process new audio pattern request
+ * @link: Display Port link module data
+ *
+ * This function will handle a new audio pattern request that is initiated by
+ * the sink. This is acheieved by sending the necessary secondary data packets
+ * to the sink. It is expected that any simulatenous requests for video
+ * patterns will be handled before the audio pattern is sent to the sink.
+ */
+static int dp_link_process_audio_pattern_request(struct dp_link_private *link)
+{
+	if (!dp_link_is_audio_pattern_requested(link))
+		return -EINVAL;
+
+	pr_debug("sampling_rate=%s, channel_count=%d, pattern_type=%s\n",
+		dp_link_get_audio_sample_rate(
+			link->dp_link.test_audio.test_audio_sampling_rate),
+		link->dp_link.test_audio.test_audio_channel_count,
+		dp_link_get_audio_test_pattern(
+			link->dp_link.test_audio.test_audio_pattern_type));
+
+	pr_debug("audio_period: ch1=0x%x, ch2=0x%x, ch3=0x%x, ch4=0x%x\n",
+		link->dp_link.test_audio.test_audio_period_ch_1,
+		link->dp_link.test_audio.test_audio_period_ch_2,
+		link->dp_link.test_audio.test_audio_period_ch_3,
+		link->dp_link.test_audio.test_audio_period_ch_4);
+
+	pr_debug("audio_period: ch5=0x%x, ch6=0x%x, ch7=0x%x, ch8=0x%x\n",
+		link->dp_link.test_audio.test_audio_period_ch_5,
+		link->dp_link.test_audio.test_audio_period_ch_6,
+		link->dp_link.test_audio.test_audio_period_ch_7,
+		link->dp_link.test_audio.test_audio_period_ch_8);
+
+	return 0;
+}
+
+static void dp_link_reset_data(struct dp_link_private *link)
+{
+	link->request = (const struct dp_link_request){ 0 };
+	link->dp_link.test_video = (const struct dp_link_test_video){ 0 };
+	link->dp_link.test_video.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
+	link->dp_link.test_audio = (const struct dp_link_test_audio){ 0 };
+	link->dp_link.phy_params.phy_test_pattern_sel = 0;
+	link->dp_link.sink_request = 0;
+	link->dp_link.test_response = 0;
+}
+
+/**
+ * dp_link_process_request() - handle HPD IRQ transition to HIGH
+ * @link: pointer to link module data
+ *
+ * This function will handle the HPD IRQ state transitions from LOW to HIGH
+ * (including cases when there are back to back HPD IRQ HIGH) indicating
+ * the start of a new link training request or sink status update.
+ */
+static int dp_link_process_request(struct dp_link *dp_link)
+{
+	int ret = 0;
+	struct dp_link_private *link;
+
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	pr_debug("start\n");
+
+	dp_link_reset_data(link);
+
+	dp_link_parse_sink_status_field(link);
+
+	if (dp_link_is_test_edid_read(link)) {
+		dp_link->sink_request |= DP_TEST_LINK_EDID_READ;
+		goto exit;
+	}
+
+	ret = dp_link_process_ds_port_status_change(link);
+	if (!ret) {
+		dp_link->sink_request |= DS_PORT_STATUS_CHANGED;
+		goto exit;
+	}
+
+	ret = dp_link_process_link_training_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
+		goto exit;
+	}
+
+	ret = dp_link_process_phy_test_pattern_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
+		goto exit;
+	}
+
+	ret = dp_link_process_link_status_update(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_LINK_STATUS_UPDATED;
+		goto exit;
+	}
+
+	ret = dp_link_process_video_pattern_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_VIDEO_PATTERN;
+		goto exit;
+	}
+
+	ret = dp_link_process_audio_pattern_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_AUDIO_PATTERN;
+		goto exit;
+	}
+
+	pr_debug("done\n");
+exit:
+	return ret;
+}
+
+static int dp_link_get_colorimetry_config(struct dp_link *dp_link)
+{
+	u32 cc;
+	enum dynamic_range dr;
+	struct dp_link_private *link;
+
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	/* unless a video pattern CTS test is ongoing, use CEA_VESA */
+	if (dp_link_is_video_pattern_requested(link))
+		dr = link->dp_link.test_video.test_dyn_range;
+	else
+		dr = DP_DYNAMIC_RANGE_RGB_VESA;
+
+	/* Only RGB_VESA nd RGB_CEA supported for now */
+	switch (dr) {
+	case DP_DYNAMIC_RANGE_RGB_CEA:
+		cc = BIT(3);
+		break;
+	case DP_DYNAMIC_RANGE_RGB_VESA:
+	default:
+		cc = 0;
+	}
+
+	return cc;
+}
+
+static int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status)
+{
+	int i;
+	int max = 0;
+	u8 data;
+	struct dp_link_private *link;
+
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	/* use the max level across lanes */
+	for (i = 0; i < dp_link->link_params.lane_count; i++) {
+		data = drm_dp_get_adjust_request_voltage(link_status, i);
+		pr_debug("lane=%d req_voltage_swing=%d\n", i, data);
+		if (max < data)
+			max = data;
+	}
+
+	dp_link->phy_params.v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+
+	/* use the max level across lanes */
+	max = 0;
+	for (i = 0; i < dp_link->link_params.lane_count; i++) {
+		data = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+		pr_debug("lane=%d req_pre_emphasis=%d\n", i, data);
+		if (max < data)
+			max = data;
+	}
+
+	dp_link->phy_params.p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+	/**
+	 * Adjust the voltage swing and pre-emphasis level combination to within
+	 * the allowable range.
+	 */
+	if (dp_link->phy_params.v_level > DP_LINK_VOLTAGE_MAX) {
+		pr_debug("Requested vSwingLevel=%d, change to %d\n",
+			dp_link->phy_params.v_level, DP_LINK_VOLTAGE_MAX);
+		dp_link->phy_params.v_level = DP_LINK_VOLTAGE_MAX;
+	}
+
+	if (dp_link->phy_params.p_level > DP_LINK_PRE_EMPHASIS_MAX) {
+		pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
+			dp_link->phy_params.p_level, DP_LINK_PRE_EMPHASIS_MAX);
+		dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_MAX;
+	}
+
+	if ((dp_link->phy_params.p_level > DP_LINK_PRE_EMPHASIS_LEVEL_1)
+		&& (dp_link->phy_params.v_level == DP_LINK_VOLTAGE_LEVEL_2)) {
+		pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
+			dp_link->phy_params.p_level,
+			DP_LINK_PRE_EMPHASIS_LEVEL_1);
+		dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
+	}
+
+	pr_debug("adjusted: v_level=%d, p_level=%d\n",
+		dp_link->phy_params.v_level, dp_link->phy_params.p_level);
+
+	return 0;
+}
+
+static int dp_link_send_psm_request(struct dp_link *dp_link, bool req)
+{
+	struct dp_link_private *link;
+
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	return 0;
+}
+
+static u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp)
+{
+	u32 tbd;
+
+	/*
+	 * Few simplistic rules and assumptions made here:
+	 *    1. Test bit depth is bit depth per color component
+	 *    2. Assume 3 color components
+	 */
+	switch (bpp) {
+	case 18:
+		tbd = DP_TEST_BIT_DEPTH_6;
+		break;
+	case 24:
+		tbd = DP_TEST_BIT_DEPTH_8;
+		break;
+	case 30:
+		tbd = DP_TEST_BIT_DEPTH_10;
+		break;
+	default:
+		tbd = DP_TEST_BIT_DEPTH_UNKNOWN;
+		break;
+	}
+
+	if (tbd != DP_TEST_BIT_DEPTH_UNKNOWN)
+		tbd = (tbd >> DP_TEST_BIT_DEPTH_SHIFT);
+
+	return tbd;
+}
+
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux)
+{
+	int rc = 0;
+	struct dp_link_private *link;
+	struct dp_link *dp_link;
+
+	if (!dev || !aux) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
+	if (!link) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	link->dev   = dev;
+	link->aux   = aux;
+
+	dp_link = &link->dp_link;
+
+	dp_link->process_request        = dp_link_process_request;
+	dp_link->get_test_bits_depth    = dp_link_get_test_bits_depth;
+	dp_link->get_colorimetry_config = dp_link_get_colorimetry_config;
+	dp_link->adjust_levels          = dp_link_adjust_levels;
+	dp_link->send_psm_request       = dp_link_send_psm_request;
+	dp_link->send_test_response     = dp_link_send_test_response;
+	dp_link->psm_config             = dp_link_psm_config;
+	dp_link->send_edid_checksum     = dp_link_send_edid_checksum;
+
+	return dp_link;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_link_put(struct dp_link *dp_link)
+{
+	struct dp_link_private *link;
+
+	if (!dp_link)
+		return;
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	devm_kfree(link->dev, link);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
new file mode 100644
index 0000000..6d6ef43
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_LINK_H_
+#define _DP_LINK_H_
+
+#include "dp_aux.h"
+
+#define DS_PORT_STATUS_CHANGED 0x200
+#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF
+#define DP_LINK_ENUM_STR(x)		#x
+
+enum dp_link_voltage_level {
+	DP_LINK_VOLTAGE_LEVEL_0	= 0,
+	DP_LINK_VOLTAGE_LEVEL_1	= 1,
+	DP_LINK_VOLTAGE_LEVEL_2	= 2,
+	DP_LINK_VOLTAGE_MAX	= DP_LINK_VOLTAGE_LEVEL_2,
+};
+
+enum dp_link_preemaphasis_level {
+	DP_LINK_PRE_EMPHASIS_LEVEL_0	= 0,
+	DP_LINK_PRE_EMPHASIS_LEVEL_1	= 1,
+	DP_LINK_PRE_EMPHASIS_LEVEL_2	= 2,
+	DP_LINK_PRE_EMPHASIS_MAX	= DP_LINK_PRE_EMPHASIS_LEVEL_2,
+};
+
+struct dp_link_sink_count {
+	u32 count;
+	bool cp_ready;
+};
+
+struct dp_link_test_video {
+	u32 test_video_pattern;
+	u32 test_bit_depth;
+	u32 test_dyn_range;
+	u32 test_h_total;
+	u32 test_v_total;
+	u32 test_h_start;
+	u32 test_v_start;
+	u32 test_hsync_pol;
+	u32 test_hsync_width;
+	u32 test_vsync_pol;
+	u32 test_vsync_width;
+	u32 test_h_width;
+	u32 test_v_height;
+	u32 test_rr_d;
+	u32 test_rr_n;
+};
+
+struct dp_link_test_audio {
+	u32 test_audio_sampling_rate;
+	u32 test_audio_channel_count;
+	u32 test_audio_pattern_type;
+	u32 test_audio_period_ch_1;
+	u32 test_audio_period_ch_2;
+	u32 test_audio_period_ch_3;
+	u32 test_audio_period_ch_4;
+	u32 test_audio_period_ch_5;
+	u32 test_audio_period_ch_6;
+	u32 test_audio_period_ch_7;
+	u32 test_audio_period_ch_8;
+};
+
+struct dp_link_phy_params {
+	u32 phy_test_pattern_sel;
+	u8 v_level;
+	u8 p_level;
+};
+
+struct dp_link_params {
+	u32 lane_count;
+	u32 bw_code;
+};
+
+struct dp_link {
+	u32 sink_request;
+	u32 test_response;
+	bool psm_enabled;
+
+	struct dp_link_sink_count sink_count;
+	struct dp_link_test_video test_video;
+	struct dp_link_test_audio test_audio;
+	struct dp_link_phy_params phy_params;
+	struct dp_link_params link_params;
+
+	u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
+	int (*process_request)(struct dp_link *dp_link);
+	int (*get_colorimetry_config)(struct dp_link *dp_link);
+	int (*adjust_levels)(struct dp_link *dp_link, u8 *link_status);
+	int (*send_psm_request)(struct dp_link *dp_link, bool req);
+	void (*send_test_response)(struct dp_link *dp_link);
+	int (*psm_config)(struct dp_link *dp_link,
+		struct drm_dp_link *link_info, bool enable);
+	void (*send_edid_checksum)(struct dp_link *dp_link, u8 checksum);
+};
+
+static inline char *dp_link_get_phy_test_pattern(u32 phy_test_pattern_sel)
+{
+	switch (phy_test_pattern_sel) {
+	case DP_TEST_PHY_PATTERN_NONE:
+		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_NONE);
+	case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
+		return DP_LINK_ENUM_STR(
+			DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING);
+	case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+		return DP_LINK_ENUM_STR(
+			DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
+	case DP_TEST_PHY_PATTERN_PRBS7:
+		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_PRBS7);
+	case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
+		return DP_LINK_ENUM_STR(
+			DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN);
+	case DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN:
+		return DP_LINK_ENUM_STR(
+			DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN);
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
+ * @tbd: test bit depth
+ *
+ * Returns the bits per pixel (bpp) to be used corresponding to the
+ * git bit depth value. This function assumes that bit depth has
+ * already been validated.
+ */
+static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
+{
+	u32 bpp;
+
+	/*
+	 * Few simplistic rules and assumptions made here:
+	 *    1. Bit depth is per color component
+	 *    2. If bit depth is unknown return 0
+	 *    3. Assume 3 color components
+	 */
+	switch (tbd) {
+	case DP_TEST_BIT_DEPTH_6:
+		bpp = 18;
+		break;
+	case DP_TEST_BIT_DEPTH_8:
+		bpp = 24;
+		break;
+	case DP_TEST_BIT_DEPTH_10:
+		bpp = 30;
+		break;
+	case DP_TEST_BIT_DEPTH_UNKNOWN:
+	default:
+		bpp = 0;
+	}
+
+	return bpp;
+}
+
+/**
+ * dp_link_get() - get the functionalities of dp test module
+ *
+ *
+ * return: a pointer to dp_link struct
+ */
+struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux);
+
+/**
+ * dp_link_put() - releases the dp test module's resources
+ *
+ * @dp_link: an instance of dp_link module
+ *
+ */
+void dp_link_put(struct dp_link *dp_link);
+
+#endif /* _DP_LINK_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
new file mode 100644
index 0000000..e497b44
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include "dp_panel.h"
+
+#include <drm/drm_connector.h>
+#include <drm/drm_edid.h>
+
+#define DP_PANEL_DEFAULT_BPP 24
+#define DP_MAX_DS_PORT_COUNT 1
+
+enum {
+	DP_LINK_RATE_MULTIPLIER = 27000000,
+};
+
+struct dp_panel_private {
+	struct device *dev;
+	struct dp_panel dp_panel;
+	struct dp_aux *aux;
+	struct dp_link *link;
+	struct dp_catalog_panel *catalog;
+	bool panel_on;
+	bool aux_cfg_update_done;
+};
+
+static const struct dp_panel_info fail_safe = {
+	.h_active = 640,
+	.v_active = 480,
+	.h_back_porch = 48,
+	.h_front_porch = 16,
+	.h_sync_width = 96,
+	.h_active_low = 0,
+	.v_back_porch = 33,
+	.v_front_porch = 10,
+	.v_sync_width = 2,
+	.v_active_low = 0,
+	.h_skew = 0,
+	.refresh_rate = 60,
+	.pixel_clk_khz = 25200,
+	.bpp = 24,
+};
+
+static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
+{
+	int rlen, rc = 0;
+	struct dp_panel_private *panel;
+	struct drm_dp_link *link_info;
+	u8 *dpcd, major = 0, minor = 0;
+	u32 dfp_count = 0;
+	unsigned long caps = DP_LINK_CAP_ENHANCED_FRAMING;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dpcd = dp_panel->dpcd;
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	link_info = &dp_panel->link_info;
+
+	rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_DPCD_REV,
+		dpcd, (DP_RECEIVER_CAP_SIZE + 1));
+	if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) {
+		pr_err("dpcd read failed, rlen=%d\n", rlen);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	link_info->revision = dp_panel->dpcd[DP_DPCD_REV];
+
+	major = (link_info->revision >> 4) & 0x0f;
+	minor = link_info->revision & 0x0f;
+	pr_debug("version: %d.%d\n", major, minor);
+
+	link_info->rate =
+		drm_dp_bw_code_to_link_rate(dp_panel->dpcd[DP_MAX_LINK_RATE]);
+	pr_debug("link_rate=%d\n", link_info->rate);
+
+	link_info->num_lanes = dp_panel->dpcd[DP_MAX_LANE_COUNT] &
+				DP_MAX_LANE_COUNT_MASK;
+
+	pr_debug("lane_count=%d\n", link_info->num_lanes);
+
+	if (drm_dp_enhanced_frame_cap(dpcd))
+		link_info->capabilities |= caps;
+
+	dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] &
+						DP_DOWN_STREAM_PORT_COUNT;
+
+	if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT)
+		&& (dpcd[DP_DPCD_REV] > 0x10)) {
+		rlen = drm_dp_dpcd_read(panel->aux->drm_aux,
+			DP_DOWNSTREAM_PORT_0, dp_panel->ds_ports,
+			DP_MAX_DOWNSTREAM_PORTS);
+		if (rlen < DP_MAX_DOWNSTREAM_PORTS) {
+			pr_err("ds port status failed, rlen=%d\n", rlen);
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+
+	if (dfp_count > DP_MAX_DS_PORT_COUNT)
+		pr_debug("DS port count %d greater that max (%d) supported\n",
+			dfp_count, DP_MAX_DS_PORT_COUNT);
+
+end:
+	return rc;
+}
+
+static int dp_panel_set_default_link_params(struct dp_panel *dp_panel)
+{
+	struct drm_dp_link *link_info;
+	const int default_bw_code = 162000;
+	const int default_num_lanes = 1;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+	link_info = &dp_panel->link_info;
+	link_info->rate = default_bw_code;
+	link_info->num_lanes = default_num_lanes;
+	pr_debug("link_rate=%d num_lanes=%d\n",
+		link_info->rate, link_info->num_lanes);
+	return 0;
+}
+
+static int dp_panel_read_edid(struct dp_panel *dp_panel,
+	struct drm_connector *connector)
+{
+	int retry_cnt = 0;
+	const int max_retry = 10;
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	do {
+		kfree(dp_panel->edid);
+		dp_panel->edid = drm_get_edid(connector,
+					      &panel->aux->drm_aux->ddc);
+		if (!dp_panel->edid) {
+			pr_err("EDID read failed\n");
+			retry_cnt++;
+			panel->aux->reconfig(panel->aux);
+			panel->aux_cfg_update_done = true;
+		} else {
+			return 0;
+		}
+	} while (retry_cnt < max_retry);
+
+	return -EINVAL;
+}
+
+static int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
+	struct drm_connector *connector)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+
+	if (!dp_panel || !connector) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	rc = dp_panel_read_dpcd(dp_panel);
+	if (rc || !is_link_rate_valid(drm_dp_link_rate_to_bw_code(
+		dp_panel->link_info.rate)) || !is_lane_count_valid(
+		dp_panel->link_info.num_lanes) ||
+		((drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate)) >
+		dp_panel->max_bw_code)) {
+		if ((rc == -ETIMEDOUT) || (rc == -ENODEV)) {
+			pr_err("DPCD read failed, return early\n");
+			return rc;
+		}
+		pr_err("panel dpcd read failed/incorrect, set default params\n");
+		dp_panel_set_default_link_params(dp_panel);
+	}
+
+	rc = dp_panel_read_edid(dp_panel, connector);
+	if (rc) {
+		pr_err("panel edid read failed, set failsafe mode\n");
+		return rc;
+	}
+
+	if (panel->aux_cfg_update_done) {
+		pr_debug("read DPCD with updated AUX config\n");
+		dp_panel_read_dpcd(dp_panel);
+		panel->aux_cfg_update_done = false;
+	}
+
+	return 0;
+}
+
+static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
+		u32 mode_edid_bpp, u32 mode_pclk_khz)
+{
+	struct drm_dp_link *link_info;
+	const u32 max_supported_bpp = 30, min_supported_bpp = 18;
+	u32 bpp = 0, data_rate_khz = 0;
+
+	bpp = min_t(u32, mode_edid_bpp, max_supported_bpp);
+
+	link_info = &dp_panel->link_info;
+	data_rate_khz = link_info->num_lanes * link_info->rate * 8;
+
+	while (bpp > min_supported_bpp) {
+		if (mode_pclk_khz * bpp <= data_rate_khz)
+			break;
+		bpp -= 6;
+	}
+
+	return bpp;
+}
+
+static u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel,
+		u32 mode_edid_bpp, u32 mode_pclk_khz)
+{
+	struct dp_panel_private *panel;
+	u32 bpp = mode_edid_bpp;
+
+	if (!dp_panel || !mode_edid_bpp || !mode_pclk_khz) {
+		pr_err("invalid input\n");
+		return 0;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (dp_panel->video_test)
+		bpp = dp_link_bit_depth_to_bpp(
+				panel->link->test_video.test_bit_depth);
+	else
+		bpp = dp_panel_get_supported_bpp(dp_panel, mode_edid_bpp,
+				mode_pclk_khz);
+
+	return bpp;
+}
+
+static void dp_panel_set_test_mode(struct dp_panel_private *panel,
+		struct dp_display_mode *mode)
+{
+	struct dp_panel_info *pinfo = NULL;
+	struct dp_link_test_video *test_info = NULL;
+
+	if (!panel) {
+		pr_err("invalid params\n");
+		return;
+	}
+
+	pinfo = &mode->timing;
+	test_info = &panel->link->test_video;
+
+	pinfo->h_active = test_info->test_h_width;
+	pinfo->h_sync_width = test_info->test_hsync_width;
+	pinfo->h_back_porch = test_info->test_h_start -
+		test_info->test_hsync_width;
+	pinfo->h_front_porch = test_info->test_h_total -
+		(test_info->test_h_start + test_info->test_h_width);
+
+	pinfo->v_active = test_info->test_v_height;
+	pinfo->v_sync_width = test_info->test_vsync_width;
+	pinfo->v_back_porch = test_info->test_v_start -
+		test_info->test_vsync_width;
+	pinfo->v_front_porch = test_info->test_v_total -
+		(test_info->test_v_start + test_info->test_v_height);
+
+	pinfo->bpp = dp_link_bit_depth_to_bpp(test_info->test_bit_depth);
+	pinfo->h_active_low = test_info->test_hsync_pol;
+	pinfo->v_active_low = test_info->test_vsync_pol;
+
+	pinfo->refresh_rate = test_info->test_rr_n;
+	pinfo->pixel_clk_khz = test_info->test_h_total *
+		test_info->test_v_total * pinfo->refresh_rate;
+
+	if (test_info->test_rr_d == 0)
+		pinfo->pixel_clk_khz /= 1000;
+	else
+		pinfo->pixel_clk_khz /= 1001;
+
+	if (test_info->test_h_width == 640)
+		pinfo->pixel_clk_khz = 25170;
+}
+
+static int dp_panel_update_modes(struct drm_connector *connector,
+	struct edid *edid)
+{
+	int rc = 0;
+
+	pr_debug("%s +", __func__);
+	if (edid) {
+		drm_connector_update_edid_property(connector,
+			edid);
+		rc = drm_add_edid_modes(connector, edid);
+		pr_debug("%s -", __func__);
+		return rc;
+	}
+
+	drm_connector_update_edid_property(connector, NULL);
+	pr_debug("%s null edid -", __func__);
+	return rc;
+}
+
+static int dp_panel_get_modes(struct dp_panel *dp_panel,
+	struct drm_connector *connector, struct dp_display_mode *mode)
+{
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (dp_panel->video_test) {
+		dp_panel_set_test_mode(panel, mode);
+		return 1;
+	} else if (dp_panel->edid) {
+	  return dp_panel_update_modes(connector, dp_panel->edid);
+	}
+
+	/* fail-safe mode */
+	memcpy(&mode->timing, &fail_safe,
+		sizeof(fail_safe));
+	return 1;
+}
+
+static u8 dp_panel_get_edid_checksum(struct edid *edid)
+{
+	struct edid *last_block = NULL;
+	u8 *raw_edid = NULL;
+
+	if (!edid) {
+		pr_err("invalid edid input\n");
+		return 0;
+	}
+
+	raw_edid = (u8 *)edid;
+	raw_edid += (edid->extensions * EDID_LENGTH);
+	last_block = (struct edid *)raw_edid;
+
+	if (last_block)
+		return last_block->checksum;
+
+	pr_err("Invalid block, no checksum\n");
+	return 0;
+}
+
+static void dp_panel_handle_sink_request(struct dp_panel *dp_panel)
+{
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
+		u8 checksum = dp_panel_get_edid_checksum(dp_panel->edid);
+
+		panel->link->send_edid_checksum(panel->link, checksum);
+		panel->link->send_test_response(panel->link);
+	}
+}
+
+static void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable)
+{
+	u32 hsync_start_x, hsync_end_x;
+	struct dp_catalog_panel *catalog;
+	struct dp_panel_private *panel;
+	struct dp_panel_info *pinfo;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	catalog = panel->catalog;
+	pinfo = &panel->dp_panel.pinfo;
+
+	if (!panel->panel_on) {
+		pr_debug("DP panel not enabled, handle TPG on next panel on\n");
+		return;
+	}
+
+	if (!enable) {
+		panel->catalog->tpg_config(catalog, false);
+		return;
+	}
+
+	/* TPG config */
+	catalog->hsync_period = pinfo->h_sync_width + pinfo->h_back_porch +
+			pinfo->h_active + pinfo->h_front_porch;
+	catalog->vsync_period = pinfo->v_sync_width + pinfo->v_back_porch +
+			pinfo->v_active + pinfo->v_front_porch;
+
+	catalog->display_v_start = ((pinfo->v_sync_width +
+			pinfo->v_back_porch) * catalog->hsync_period);
+	catalog->display_v_end = ((catalog->vsync_period -
+			pinfo->v_front_porch) * catalog->hsync_period) - 1;
+
+	catalog->display_v_start += pinfo->h_sync_width + pinfo->h_back_porch;
+	catalog->display_v_end -= pinfo->h_front_porch;
+
+	hsync_start_x = pinfo->h_back_porch + pinfo->h_sync_width;
+	hsync_end_x = catalog->hsync_period - pinfo->h_front_porch - 1;
+
+	catalog->v_sync_width = pinfo->v_sync_width;
+
+	catalog->hsync_ctl = (catalog->hsync_period << 16) |
+			pinfo->h_sync_width;
+	catalog->display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+	pr_err("%s: calling catalog tpg_config\n", __func__);
+	panel->catalog->tpg_config(catalog, true);
+}
+
+static int dp_panel_timing_cfg(struct dp_panel *dp_panel)
+{
+	int rc = 0;
+	u32 data, total_ver, total_hor;
+	struct dp_catalog_panel *catalog;
+	struct dp_panel_private *panel;
+	struct dp_panel_info *pinfo;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	catalog = panel->catalog;
+	pinfo = &panel->dp_panel.pinfo;
+
+	pr_debug("width=%d hporch= %d %d %d\n",
+		pinfo->h_active, pinfo->h_back_porch,
+		pinfo->h_front_porch, pinfo->h_sync_width);
+
+	pr_debug("height=%d vporch= %d %d %d\n",
+		pinfo->v_active, pinfo->v_back_porch,
+		pinfo->v_front_porch, pinfo->v_sync_width);
+
+	total_hor = pinfo->h_active + pinfo->h_back_porch +
+		pinfo->h_front_porch + pinfo->h_sync_width;
+
+	total_ver = pinfo->v_active + pinfo->v_back_porch +
+			pinfo->v_front_porch + pinfo->v_sync_width;
+
+	data = total_ver;
+	data <<= 16;
+	data |= total_hor;
+
+	catalog->total = data;
+
+	data = (pinfo->v_back_porch + pinfo->v_sync_width);
+	data <<= 16;
+	data |= (pinfo->h_back_porch + pinfo->h_sync_width);
+
+	catalog->sync_start = data;
+
+	data = pinfo->v_sync_width;
+	data <<= 16;
+	data |= (pinfo->v_active_low << 31);
+	data |= pinfo->h_sync_width;
+	data |= (pinfo->h_active_low << 15);
+
+	catalog->width_blanking = data;
+
+	data = pinfo->v_active;
+	data <<= 16;
+	data |= pinfo->h_active;
+
+	catalog->dp_active = data;
+
+	panel->catalog->timing_cfg(catalog);
+	panel->panel_on = true;
+end:
+	return rc;
+}
+
+static int dp_panel_init_panel_info(struct dp_panel *dp_panel)
+{
+	int rc = 0;
+	struct dp_panel_info *pinfo;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	pinfo = &dp_panel->pinfo;
+
+	/*
+	 * print resolution info as this is a result
+	 * of user initiated action of cable connection
+	 */
+	pr_info("SET NEW RESOLUTION:\n");
+	pr_info("%dx%d@%dfps\n", pinfo->h_active,
+		pinfo->v_active, pinfo->refresh_rate);
+	pr_info("h_porches(back|front|width) = (%d|%d|%d)\n",
+			pinfo->h_back_porch,
+			pinfo->h_front_porch,
+			pinfo->h_sync_width);
+	pr_info("v_porches(back|front|width) = (%d|%d|%d)\n",
+			pinfo->v_back_porch,
+			pinfo->v_front_porch,
+			pinfo->v_sync_width);
+	pr_info("pixel clock (KHz)=(%d)\n", pinfo->pixel_clk_khz);
+	pr_info("bpp = %d\n", pinfo->bpp);
+	pr_info("active low (h|v)=(%d|%d)\n", pinfo->h_active_low,
+		pinfo->v_active_low);
+
+	pinfo->bpp = max_t(u32, 18, min_t(u32, pinfo->bpp, 30));
+	pr_info("updated bpp = %d\n", pinfo->bpp);
+end:
+	return rc;
+}
+
+static u32 dp_panel_get_min_req_link_rate(struct dp_panel *dp_panel)
+{
+	const u32 encoding_factx10 = 8;
+	u32 min_link_rate_khz = 0, lane_cnt;
+	struct dp_panel_info *pinfo;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		goto end;
+	}
+
+	lane_cnt = dp_panel->link_info.num_lanes;
+	pinfo = &dp_panel->pinfo;
+
+	/* num_lanes * lane_count * 8 >= pclk * bpp * 10 */
+	min_link_rate_khz = pinfo->pixel_clk_khz /
+				(lane_cnt * encoding_factx10);
+	min_link_rate_khz *= pinfo->bpp;
+
+	pr_debug("min lclk req=%d khz for pclk=%d khz, lanes=%d, bpp=%d\n",
+		min_link_rate_khz, pinfo->pixel_clk_khz, lane_cnt,
+		pinfo->bpp);
+end:
+	return min_link_rate_khz;
+}
+
+struct dp_panel *dp_panel_get(struct dp_panel_in *in)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+	struct dp_panel *dp_panel;
+
+	if (!in->dev || !in->catalog || !in->aux || !in->link) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	panel = devm_kzalloc(in->dev, sizeof(*panel), GFP_KERNEL);
+	if (!panel) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	panel->dev = in->dev;
+	panel->aux = in->aux;
+	panel->catalog = in->catalog;
+	panel->link = in->link;
+
+	dp_panel = &panel->dp_panel;
+	dp_panel->max_bw_code = DP_LINK_BW_8_1;
+	panel->aux_cfg_update_done = false;
+
+	dp_panel->init_info = dp_panel_init_panel_info;
+	dp_panel->timing_cfg = dp_panel_timing_cfg;
+	dp_panel->read_sink_caps = dp_panel_read_sink_caps;
+	dp_panel->get_min_req_link_rate = dp_panel_get_min_req_link_rate;
+	dp_panel->get_mode_bpp = dp_panel_get_mode_bpp;
+	dp_panel->get_modes = dp_panel_get_modes;
+	dp_panel->handle_sink_request = dp_panel_handle_sink_request;
+	dp_panel->tpg_config = dp_panel_tpg_config;
+
+	return dp_panel;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_panel_put(struct dp_panel *dp_panel)
+{
+	struct dp_panel_private *panel;
+
+	if (!dp_panel)
+		return;
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	kfree(dp_panel->edid);
+	dp_panel->edid = NULL;
+	devm_kfree(panel->dev, panel);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
new file mode 100644
index 0000000..bf802df
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_PANEL_H_
+#define _DP_PANEL_H_
+
+#include <drm/msm_drm.h>
+
+#include "dp_aux.h"
+#include "dp_link.h"
+#include "dp_extcon.h"
+
+struct edid;
+
+enum dp_lane_count {
+	DP_LANE_COUNT_1	= 1,
+	DP_LANE_COUNT_2	= 2,
+	DP_LANE_COUNT_4	= 4,
+};
+
+#define DP_MAX_DOWNSTREAM_PORTS 0x10
+
+struct dp_panel_info {
+	u32 h_active;
+	u32 v_active;
+	u32 h_back_porch;
+	u32 h_front_porch;
+	u32 h_sync_width;
+	u32 h_active_low;
+	u32 v_back_porch;
+	u32 v_front_porch;
+	u32 v_sync_width;
+	u32 v_active_low;
+	u32 h_skew;
+	u32 refresh_rate;
+	u32 pixel_clk_khz;
+	u32 bpp;
+};
+
+struct dp_display_mode {
+	struct dp_panel_info timing;
+	u32 capabilities;
+};
+
+struct dp_panel_in {
+	struct device *dev;
+	struct dp_aux *aux;
+	struct dp_link *link;
+	struct dp_catalog_panel *catalog;
+};
+
+struct dp_panel {
+	/* dpcd raw data */
+	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
+	u8 ds_ports[DP_MAX_DOWNSTREAM_PORTS];
+
+	struct drm_dp_link link_info;
+	struct edid *edid;
+	struct drm_connector *connector;
+	struct dp_panel_info pinfo;
+	bool video_test;
+
+	u32 vic;
+	u32 max_pclk_khz;
+
+	u32 max_bw_code;
+	int (*init_info)(struct dp_panel *dp_panel);
+	int (*deinit)(struct dp_panel *dp_panel);
+	int (*timing_cfg)(struct dp_panel *dp_panel);
+	int (*read_sink_caps)(struct dp_panel *dp_panel,
+		struct drm_connector *connector);
+	u32 (*get_min_req_link_rate)(struct dp_panel *dp_panel);
+	u32 (*get_mode_bpp)(struct dp_panel *dp_panel, u32 mode_max_bpp,
+			u32 mode_pclk_khz);
+	int (*get_modes)(struct dp_panel *dp_panel,
+		struct drm_connector *connector, struct dp_display_mode *mode);
+	void (*handle_sink_request)(struct dp_panel *dp_panel);
+	void (*tpg_config)(struct dp_panel *dp_panel, bool enable);
+};
+
+/**
+ * is_link_rate_valid() - validates the link rate
+ * @lane_rate: link rate requested by the sink
+ *
+ * Returns true if the requested link rate is supported.
+ */
+static inline bool is_link_rate_valid(u32 bw_code)
+{
+	return ((bw_code == DP_LINK_BW_1_62) ||
+		(bw_code == DP_LINK_BW_2_7) ||
+		(bw_code == DP_LINK_BW_5_4) ||
+		(bw_code == DP_LINK_BW_8_1));
+}
+
+/**
+ * dp_link_is_lane_count_valid() - validates the lane count
+ * @lane_count: lane count requested by the sink
+ *
+ * Returns true if the requested lane count is supported.
+ */
+static inline bool is_lane_count_valid(u32 lane_count)
+{
+	return (lane_count == DP_LANE_COUNT_1) ||
+		(lane_count == DP_LANE_COUNT_2) ||
+		(lane_count == DP_LANE_COUNT_4);
+}
+
+struct dp_panel *dp_panel_get(struct dp_panel_in *in);
+void dp_panel_put(struct dp_panel *dp_panel);
+#endif /* _DP_PANEL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
new file mode 100644
index 0000000..a366dc5
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/of_gpio.h>
+
+#include "dp_parser.h"
+
+static void dp_parser_unmap_io_resources(struct dp_parser *parser)
+{
+	struct dp_io *io = &parser->io;
+
+	msm_dss_iounmap(&io->dp_ahb);
+	msm_dss_iounmap(&io->dp_aux);
+	msm_dss_iounmap(&io->dp_link);
+	msm_dss_iounmap(&io->dp_p0);
+	msm_dss_iounmap(&io->phy_io);
+	msm_dss_iounmap(&io->ln_tx0_io);
+	msm_dss_iounmap(&io->ln_tx0_io);
+	msm_dss_iounmap(&io->dp_pll_io);
+	msm_dss_iounmap(&io->dp_cc_io);
+	msm_dss_iounmap(&io->usb3_dp_com);
+	msm_dss_iounmap(&io->qfprom_io);
+}
+
+static int dp_parser_ctrl_res(struct dp_parser *parser)
+{
+	int rc = 0;
+	u32 index;
+	struct platform_device *pdev = parser->pdev;
+	struct device_node *of_node = parser->pdev->dev.of_node;
+	struct dp_io *io = &parser->io;
+
+	rc = of_property_read_u32(of_node, "cell-index", &index);
+	if (rc) {
+		pr_err("cell-index not specified, rc=%d\n", rc);
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_ahb, "dp_ahb");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_aux, "dp_aux");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_link, "dp_link");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_p0, "dp_p0");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->phy_io, "dp_phy");
+	if (rc) {
+		pr_err("unable to remap dp PHY resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->ln_tx0_io, "dp_ln_tx0");
+	if (rc) {
+		pr_err("unable to remap dp TX0 resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->ln_tx1_io, "dp_ln_tx1");
+	if (rc) {
+		pr_err("unable to remap dp TX1 resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_pll_io, "dp_pll");
+	if (rc) {
+		pr_err("unable to remap DP PLL resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->usb3_dp_com, "usb3_dp_com");
+	if (rc) {
+		pr_err("unable to remap USB3 DP com resources\n");
+		goto err;
+	}
+
+	if (msm_dss_ioremap_byname(pdev, &io->dp_cc_io, "dp_mmss_cc")) {
+		pr_err("unable to remap dp MMSS_CC resources\n");
+		goto err;
+	}
+
+	if (msm_dss_ioremap_byname(pdev, &io->qfprom_io, "qfprom_physical"))
+		pr_warn("unable to remap dp qfprom resources\n");
+
+	return 0;
+err:
+	dp_parser_unmap_io_resources(parser);
+	return rc;
+}
+
+static const char *dp_get_phy_aux_config_property(u32 cfg_type)
+{
+	switch (cfg_type) {
+	case PHY_AUX_CFG0:
+		return "qcom,aux-cfg0-settings";
+	case PHY_AUX_CFG1:
+		return "qcom,aux-cfg1-settings";
+	case PHY_AUX_CFG2:
+		return "qcom,aux-cfg2-settings";
+	case PHY_AUX_CFG3:
+		return "qcom,aux-cfg3-settings";
+	case PHY_AUX_CFG4:
+		return "qcom,aux-cfg4-settings";
+	case PHY_AUX_CFG5:
+		return "qcom,aux-cfg5-settings";
+	case PHY_AUX_CFG6:
+		return "qcom,aux-cfg6-settings";
+	case PHY_AUX_CFG7:
+		return "qcom,aux-cfg7-settings";
+	case PHY_AUX_CFG8:
+		return "qcom,aux-cfg8-settings";
+	case PHY_AUX_CFG9:
+		return "qcom,aux-cfg9-settings";
+	default:
+		return "unknown";
+	}
+}
+
+static void dp_parser_phy_aux_cfg_reset(struct dp_parser *parser)
+{
+	int i = 0;
+
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++)
+		parser->aux_cfg[i] = (const struct dp_aux_cfg){ 0 };
+}
+
+static int dp_parser_aux(struct dp_parser *parser)
+{
+	struct device_node *of_node = parser->pdev->dev.of_node;
+	int len = 0, i = 0, j = 0, config_count = 0;
+	const char *data;
+	int const minimum_config_count = 1;
+
+	for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
+		const char *property = dp_get_phy_aux_config_property(i);
+
+		data = of_get_property(of_node, property, &len);
+		if (!data) {
+			pr_err("Unable to read %s\n", property);
+			goto error;
+		}
+
+		config_count = len - 1;
+		if ((config_count < minimum_config_count) ||
+			(config_count > DP_AUX_CFG_MAX_VALUE_CNT)) {
+			pr_err("Invalid config count (%d) configs for %s\n",
+					config_count, property);
+			goto error;
+		}
+
+		parser->aux_cfg[i].offset = data[0];
+		parser->aux_cfg[i].cfg_cnt = config_count;
+		pr_debug("%s offset=0x%x, cfg_cnt=%d\n",
+				property,
+				parser->aux_cfg[i].offset,
+				parser->aux_cfg[i].cfg_cnt);
+		for (j = 1; j < len; j++) {
+			parser->aux_cfg[i].lut[j - 1] = data[j];
+			pr_debug("%s lut[%d]=0x%x\n",
+					property,
+					i,
+					parser->aux_cfg[i].lut[j - 1]);
+		}
+	}
+		return 0;
+
+error:
+	dp_parser_phy_aux_cfg_reset(parser);
+	return -EINVAL;
+}
+
+static int dp_parser_misc(struct dp_parser *parser)
+{
+	int rc = 0;
+	struct device_node *of_node = parser->pdev->dev.of_node;
+
+	rc = of_property_read_u32(of_node,
+		"qcom,max-pclk-frequency-khz", &parser->max_pclk_khz);
+	if (rc)
+		parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
+
+	return 0;
+}
+
+static int dp_parser_pinctrl(struct dp_parser *parser)
+{
+	int rc = 0;
+	struct dp_pinctrl *pinctrl = &parser->pinctrl;
+
+	pinctrl->pin = devm_pinctrl_get(&parser->pdev->dev);
+
+	if (IS_ERR_OR_NULL(pinctrl->pin)) {
+		rc = PTR_ERR(pinctrl->pin);
+		pr_err("failed to get pinctrl, rc=%d\n", rc);
+		goto error;
+	}
+
+	pinctrl->state_active = pinctrl_lookup_state(pinctrl->pin,
+					"mdss_dp_active");
+	if (IS_ERR_OR_NULL(pinctrl->state_active)) {
+		rc = PTR_ERR(pinctrl->state_active);
+		pr_err("failed to get pinctrl active state, rc=%d\n", rc);
+		goto error;
+	}
+
+	pinctrl->state_suspend = pinctrl_lookup_state(pinctrl->pin,
+					"mdss_dp_sleep");
+	if (IS_ERR_OR_NULL(pinctrl->state_suspend)) {
+		rc = PTR_ERR(pinctrl->state_suspend);
+		pr_err("failed to get pinctrl suspend state, rc=%d\n", rc);
+		goto error;
+	}
+error:
+	return rc;
+}
+
+static int dp_parser_gpio(struct dp_parser *parser)
+{
+	int i = 0;
+	struct device *dev = &parser->pdev->dev;
+	struct device_node *of_node = dev->of_node;
+	struct dss_module_power *mp = &parser->mp[DP_CORE_PM];
+	static const char * const dp_gpios[] = {
+		"qcom,aux-en-gpio",
+		"qcom,aux-sel-gpio",
+		"qcom,usbplug-cc-gpio",
+	};
+
+	mp->gpio_config = devm_kzalloc(dev,
+		sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
+	if (!mp->gpio_config)
+		return -ENOMEM;
+
+	mp->num_gpio = ARRAY_SIZE(dp_gpios);
+
+	for (i = 0; i < ARRAY_SIZE(dp_gpios); i++) {
+		mp->gpio_config[i].gpio = of_get_named_gpio(of_node,
+			dp_gpios[i], 0);
+
+		if (!gpio_is_valid(mp->gpio_config[i].gpio)) {
+			pr_err("%s gpio not specified\n", dp_gpios[i]);
+			return -EINVAL;
+		}
+
+		strlcpy(mp->gpio_config[i].gpio_name, dp_gpios[i],
+			sizeof(mp->gpio_config[i].gpio_name));
+
+		mp->gpio_config[i].value = 0;
+	}
+
+	return 0;
+}
+
+static const char *dp_parser_supply_node_name(enum dp_pm_type module)
+{
+	switch (module) {
+	case DP_CORE_PM:	return "qcom,core-supply-entries";
+	case DP_CTRL_PM:	return "qcom,ctrl-supply-entries";
+	case DP_PHY_PM:		return "qcom,phy-supply-entries";
+	default:		return "???";
+	}
+}
+
+static int dp_parser_get_vreg(struct dp_parser *parser,
+		enum dp_pm_type module)
+{
+	int i = 0, rc = 0;
+	u32 tmp = 0;
+	const char *pm_supply_name = NULL;
+	struct device_node *supply_node = NULL;
+	struct device_node *of_node = parser->pdev->dev.of_node;
+	struct device_node *supply_root_node = NULL;
+	struct dss_module_power *mp = &parser->mp[module];
+
+	mp->num_vreg = 0;
+	pm_supply_name = dp_parser_supply_node_name(module);
+	supply_root_node = of_get_child_by_name(of_node, pm_supply_name);
+	if (!supply_root_node) {
+		pr_err("no supply entry present: %s\n", pm_supply_name);
+		goto novreg;
+	}
+
+	mp->num_vreg = of_get_available_child_count(supply_root_node);
+
+	if (mp->num_vreg == 0) {
+		pr_debug("no vreg\n");
+		goto novreg;
+	} else {
+		pr_debug("vreg found. count=%d\n", mp->num_vreg);
+	}
+
+	mp->vreg_config = devm_kzalloc(&parser->pdev->dev,
+		sizeof(struct dss_vreg) * mp->num_vreg, GFP_KERNEL);
+	if (!mp->vreg_config) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	for_each_child_of_node(supply_root_node, supply_node) {
+		const char *st = NULL;
+		/* vreg-name */
+		rc = of_property_read_string(supply_node,
+			"qcom,supply-name", &st);
+		if (rc) {
+			pr_err("error reading name. rc=%d\n",
+				 rc);
+			goto error;
+		}
+		snprintf(mp->vreg_config[i].vreg_name,
+			ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st);
+		/* vreg-min-voltage */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-min-voltage", &tmp);
+		if (rc) {
+			pr_err("error reading min volt. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].min_voltage = tmp;
+
+		/* vreg-max-voltage */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-max-voltage", &tmp);
+		if (rc) {
+			pr_err("error reading max volt. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].max_voltage = tmp;
+
+		/* enable-load */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-enable-load", &tmp);
+		if (rc) {
+			pr_err("error reading enable load. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].enable_load = tmp;
+
+		/* disable-load */
+		rc = of_property_read_u32(supply_node,
+			"qcom,supply-disable-load", &tmp);
+		if (rc) {
+			pr_err("error reading disable load. rc=%d\n",
+				rc);
+			goto error;
+		}
+		mp->vreg_config[i].disable_load = tmp;
+
+		pr_debug("%s min=%d, max=%d, enable=%d, disable=%d\n",
+			mp->vreg_config[i].vreg_name,
+			mp->vreg_config[i].min_voltage,
+			mp->vreg_config[i].max_voltage,
+			mp->vreg_config[i].enable_load,
+			mp->vreg_config[i].disable_load
+			);
+		++i;
+	}
+
+	return rc;
+
+error:
+	if (mp->vreg_config) {
+		devm_kfree(&parser->pdev->dev, mp->vreg_config);
+		mp->vreg_config = NULL;
+	}
+novreg:
+	mp->num_vreg = 0;
+
+	return rc;
+}
+
+static void dp_parser_put_vreg_data(struct device *dev,
+	struct dss_module_power *mp)
+{
+	if (!mp) {
+		DEV_ERR("invalid input\n");
+		return;
+	}
+
+	if (mp->vreg_config) {
+		devm_kfree(dev, mp->vreg_config);
+		mp->vreg_config = NULL;
+	}
+	mp->num_vreg = 0;
+}
+
+static int dp_parser_regulator(struct dp_parser *parser)
+{
+	int i, rc = 0;
+	struct platform_device *pdev = parser->pdev;
+
+	/* Parse the regulator information */
+	for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+		rc = dp_parser_get_vreg(parser, i);
+		if (rc) {
+			pr_err("get_dt_vreg_data failed for %s. rc=%d\n",
+				dp_parser_pm_name(i), rc);
+			i--;
+			for (; i >= DP_CORE_PM; i--)
+				dp_parser_put_vreg_data(&pdev->dev,
+					&parser->mp[i]);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static bool dp_parser_check_prefix(const char *clk_prefix, const char *clk_name)
+{
+	return !!strnstr(clk_name, clk_prefix, strlen(clk_name));
+}
+
+static void dp_parser_put_clk_data(struct device *dev,
+	struct dss_module_power *mp)
+{
+	if (!mp) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return;
+	}
+
+	if (mp->clk_config) {
+		devm_kfree(dev, mp->clk_config);
+		mp->clk_config = NULL;
+	}
+
+	mp->num_clk = 0;
+}
+
+static void dp_parser_put_gpio_data(struct device *dev,
+	struct dss_module_power *mp)
+{
+	if (!mp) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return;
+	}
+
+	if (mp->gpio_config) {
+		devm_kfree(dev, mp->gpio_config);
+		mp->gpio_config = NULL;
+	}
+
+	mp->num_gpio = 0;
+}
+
+static int dp_parser_init_clk_data(struct dp_parser *parser)
+{
+	int num_clk = 0, i = 0, rc = 0;
+	int core_clk_count = 0, ctrl_clk_count = 0;
+	const char *core_clk = "core";
+	const char *ctrl_clk = "ctrl";
+	const char *clk_name;
+	struct device *dev = &parser->pdev->dev;
+	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
+	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
+
+	num_clk = of_property_count_strings(dev->of_node, "clock-names");
+	if (num_clk <= 0) {
+		pr_err("no clocks are defined\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	for (i = 0; i < num_clk; i++) {
+		of_property_read_string_index(dev->of_node,
+				"clock-names", i, &clk_name);
+
+		if (dp_parser_check_prefix(core_clk, clk_name))
+			core_clk_count++;
+
+		if (dp_parser_check_prefix(ctrl_clk, clk_name))
+			ctrl_clk_count++;
+	}
+
+	/* Initialize the CORE power module */
+	if (core_clk_count <= 0) {
+		pr_err("no core clocks are defined\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	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) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Initialize the CTRL power module */
+	if (ctrl_clk_count <= 0) {
+		pr_err("no ctrl clocks are defined\n");
+		rc = -EINVAL;
+		goto ctrl_clock_error;
+	}
+
+	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;
+		rc = -EINVAL;
+		goto ctrl_clock_error;
+	}
+
+	return rc;
+
+ctrl_clock_error:
+	dp_parser_put_clk_data(dev, core_power);
+exit:
+	return rc;
+}
+
+static int dp_parser_clock(struct dp_parser *parser)
+{
+	int rc = 0, i = 0;
+	int num_clk = 0;
+	int core_clk_index = 0, ctrl_clk_index = 0;
+	int core_clk_count = 0, ctrl_clk_count = 0;
+	const char *clk_name;
+	const char *core_clk = "core";
+	const char *ctrl_clk = "ctrl";
+	struct device *dev = &parser->pdev->dev;
+	struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
+	struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
+
+	core_power = &parser->mp[DP_CORE_PM];
+	ctrl_power = &parser->mp[DP_CTRL_PM];
+
+	rc =  dp_parser_init_clk_data(parser);
+	if (rc) {
+		pr_err("failed to initialize power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	core_clk_count = core_power->num_clk;
+	ctrl_clk_count = ctrl_power->num_clk;
+
+	num_clk = core_clk_count + ctrl_clk_count;
+
+	for (i = 0; i < num_clk; i++) {
+		of_property_read_string_index(dev->of_node, "clock-names",
+				i, &clk_name);
+
+		if (dp_parser_check_prefix(core_clk, clk_name) &&
+				core_clk_index < core_clk_count) {
+			struct dss_clk *clk =
+				&core_power->clk_config[core_clk_index];
+			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+			clk->type = DSS_CLK_AHB;
+			core_clk_index++;
+		} else if (dp_parser_check_prefix(ctrl_clk, clk_name) &&
+			   ctrl_clk_index < ctrl_clk_count) {
+			struct dss_clk *clk =
+				&ctrl_power->clk_config[ctrl_clk_index];
+			strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+			ctrl_clk_index++;
+
+			if (!strcmp(clk_name, "ctrl_link_clk") ||
+			    !strcmp(clk_name, "ctrl_pixel_clk"))
+				clk->type = DSS_CLK_PCLK;
+			else
+				clk->type = DSS_CLK_AHB;
+		}
+	}
+
+	pr_debug("clock parsing successful\n");
+
+exit:
+	return rc;
+}
+
+static int dp_parser_parse(struct dp_parser *parser)
+{
+	int rc = 0;
+
+	if (!parser) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto err;
+	}
+
+	rc = dp_parser_ctrl_res(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_aux(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_misc(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_clock(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_regulator(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_gpio(parser);
+	if (rc)
+		goto err;
+
+	rc = dp_parser_pinctrl(parser);
+err:
+	return rc;
+}
+
+struct dp_parser *dp_parser_get(struct platform_device *pdev)
+{
+	struct dp_parser *parser;
+
+	parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
+	if (!parser)
+		return ERR_PTR(-ENOMEM);
+
+	parser->parse = dp_parser_parse;
+	parser->pdev = pdev;
+
+	return parser;
+}
+
+void dp_parser_put(struct dp_parser *parser)
+{
+	int i = 0;
+	struct dss_module_power *power = NULL;
+
+	if (!parser) {
+		pr_err("invalid parser module\n");
+		return;
+	}
+
+	power = parser->mp;
+
+	for (i = 0; i < DP_MAX_PM; i++) {
+		dp_parser_put_clk_data(&parser->pdev->dev, &power[i]);
+		dp_parser_put_vreg_data(&parser->pdev->dev, &power[i]);
+		dp_parser_put_gpio_data(&parser->pdev->dev, &power[i]);
+	}
+
+	devm_kfree(&parser->pdev->dev, parser);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
new file mode 100644
index 0000000..b39ea02
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_parser.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_PARSER_H_
+#define _DP_PARSER_H_
+
+#include "dpu_io_util.h"
+
+#define DP_LABEL "MDSS DP DISPLAY"
+#define AUX_CFG_LEN	10
+#define DP_MAX_PIXEL_CLK_KHZ	675000
+
+enum dp_pm_type {
+	DP_CORE_PM,
+	DP_CTRL_PM,
+	DP_PHY_PM,
+	DP_MAX_PM
+};
+
+static inline const char *dp_parser_pm_name(enum dp_pm_type module)
+{
+	switch (module) {
+	case DP_CORE_PM:	return "DP_CORE_PM";
+	case DP_CTRL_PM:	return "DP_CTRL_PM";
+	case DP_PHY_PM:		return "DP_PHY_PM";
+	default:		return "???";
+	}
+}
+
+/**
+ * struct dp_display_data  - display related device tree data.
+ *
+ * @ctrl_node: referece to controller device
+ * @phy_node:  reference to phy device
+ * @is_active: is the controller currently active
+ * @name: name of the display
+ * @display_type: type of the display
+ */
+struct dp_display_data {
+	struct device_node *ctrl_node;
+	struct device_node *phy_node;
+	bool is_active;
+	const char *name;
+	const char *display_type;
+};
+
+/**
+ * struct dp_ctrl_resource - controller's IO related data
+ *
+ * @dp_ahb: controller's ahb mapped memory address
+ * @dp_aux: controller's aux mapped memory address
+ * @dp_link: controller's link mapped memory address
+ * @dp_p0: controller's p0 mapped memory address
+ * @phy_io: phy's mapped memory address
+ * @ln_tx0_io: USB-DP lane TX0's mapped memory address
+ * @ln_tx1_io: USB-DP lane TX1's mapped memory address
+ * @dp_cc_io: DP cc's mapped memory address
+ * @qfprom_io: qfprom's mapped memory address
+ * @dp_pll_io: DP PLL mapped memory address
+ * @usb3_dp_com: USB3 DP PHY combo mapped memory address
+ */
+struct dp_io {
+	struct dss_io_data ctrl_io;
+	struct dss_io_data dp_ahb;
+	struct dss_io_data dp_aux;
+	struct dss_io_data dp_link;
+	struct dss_io_data dp_p0;
+	struct dss_io_data phy_io;
+	struct dss_io_data ln_tx0_io;
+	struct dss_io_data ln_tx1_io;
+	struct dss_io_data dp_cc_io;
+	struct dss_io_data qfprom_io;
+	struct dss_io_data dp_pll_io;
+	struct dss_io_data usb3_dp_com;
+};
+
+/**
+ * struct dp_pinctrl - DP's pin control
+ *
+ * @pin: pin-controller's instance
+ * @state_active: active state pin control
+ * @state_hpd_active: hpd active state pin control
+ * @state_suspend: suspend state pin control
+ */
+struct dp_pinctrl {
+	struct pinctrl *pin;
+	struct pinctrl_state *state_active;
+	struct pinctrl_state *state_hpd_active;
+	struct pinctrl_state *state_suspend;
+};
+
+#define DP_ENUM_STR(x)	#x
+#define DP_AUX_CFG_MAX_VALUE_CNT 3
+/**
+ * struct dp_aux_cfg - DP's AUX configuration settings
+ *
+ * @cfg_cnt: count of the configurable settings for the AUX register
+ * @current_index: current index of the AUX config lut
+ * @offset: register offset of the AUX config register
+ * @lut: look up table for the AUX config values for this register
+ */
+struct dp_aux_cfg {
+	u32 cfg_cnt;
+	u32 current_index;
+	u32 offset;
+	u32 lut[DP_AUX_CFG_MAX_VALUE_CNT];
+};
+
+/* PHY AUX config registers */
+enum dp_phy_aux_config_type {
+	PHY_AUX_CFG0,
+	PHY_AUX_CFG1,
+	PHY_AUX_CFG2,
+	PHY_AUX_CFG3,
+	PHY_AUX_CFG4,
+	PHY_AUX_CFG5,
+	PHY_AUX_CFG6,
+	PHY_AUX_CFG7,
+	PHY_AUX_CFG8,
+	PHY_AUX_CFG9,
+	PHY_AUX_CFG_MAX,
+};
+
+static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
+{
+	switch (cfg_type) {
+	case PHY_AUX_CFG0:
+		return DP_ENUM_STR(PHY_AUX_CFG0);
+	case PHY_AUX_CFG1:
+		return DP_ENUM_STR(PHY_AUX_CFG1);
+	case PHY_AUX_CFG2:
+		return DP_ENUM_STR(PHY_AUX_CFG2);
+	case PHY_AUX_CFG3:
+		return DP_ENUM_STR(PHY_AUX_CFG3);
+	case PHY_AUX_CFG4:
+		return DP_ENUM_STR(PHY_AUX_CFG4);
+	case PHY_AUX_CFG5:
+		return DP_ENUM_STR(PHY_AUX_CFG5);
+	case PHY_AUX_CFG6:
+		return DP_ENUM_STR(PHY_AUX_CFG6);
+	case PHY_AUX_CFG7:
+		return DP_ENUM_STR(PHY_AUX_CFG7);
+	case PHY_AUX_CFG8:
+		return DP_ENUM_STR(PHY_AUX_CFG8);
+	case PHY_AUX_CFG9:
+		return DP_ENUM_STR(PHY_AUX_CFG9);
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * struct dp_parser - DP parser's data exposed to clients
+ *
+ * @pdev: platform data of the client
+ * @mp: gpio, regulator and clock related data
+ * @pinctrl: pin-control related data
+ * @disp_data: controller's display related data
+ * @parse: function to be called by client to parse device tree.
+ */
+struct dp_parser {
+	struct platform_device *pdev;
+	struct dss_module_power mp[DP_MAX_PM];
+	struct dp_pinctrl pinctrl;
+	struct dp_io io;
+	struct dp_display_data disp_data;
+
+	u8 l_map[4];
+	struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
+	u32 max_pclk_khz;
+
+	int (*parse)(struct dp_parser *parser);
+};
+
+/**
+ * dp_parser_get() - get the DP's device tree parser module
+ *
+ * @pdev: platform data of the client
+ * return: pointer to dp_parser structure.
+ *
+ * This function provides client capability to parse the
+ * device tree and populate the data structures. The data
+ * related to clock, regulators, pin-control and other
+ * can be parsed using this module.
+ */
+struct dp_parser *dp_parser_get(struct platform_device *pdev);
+
+/**
+ * dp_parser_put() - cleans the dp_parser module
+ *
+ * @parser: pointer to the parser's data.
+ */
+void dp_parser_put(struct dp_parser *parser);
+#endif
diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
new file mode 100644
index 0000000..1d58480
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_power.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
+
+#include <linux/clk.h>
+#include "dp_power.h"
+
+#define DP_CLIENT_NAME_SIZE	20
+
+struct dp_power_private {
+	struct dp_parser *parser;
+	struct platform_device *pdev;
+	struct clk *pixel_clk_rcg;
+	struct clk *pixel_parent;
+
+	struct dp_power dp_power;
+
+	bool core_clks_on;
+	bool link_clks_on;
+};
+
+static int dp_power_regulator_init(struct dp_power_private *power)
+{
+	int rc = 0, i = 0, j = 0;
+	struct platform_device *pdev;
+	struct dp_parser *parser;
+
+	parser = power->parser;
+	pdev = power->pdev;
+
+	for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) {
+		rc = msm_dss_config_vreg(&pdev->dev,
+			parser->mp[i].vreg_config,
+			parser->mp[i].num_vreg, 1);
+		if (rc) {
+			pr_err("failed to init vregs for %s\n",
+				dp_parser_pm_name(i));
+			for (j = i - 1; j >= DP_CORE_PM; j--) {
+				msm_dss_config_vreg(&pdev->dev,
+				parser->mp[j].vreg_config,
+				parser->mp[j].num_vreg, 0);
+			}
+
+			goto error;
+		}
+	}
+error:
+	return rc;
+}
+
+static void dp_power_regulator_deinit(struct dp_power_private *power)
+{
+	int rc = 0, i = 0;
+	struct platform_device *pdev;
+	struct dp_parser *parser;
+
+	parser = power->parser;
+	pdev = power->pdev;
+
+	for (i = DP_CORE_PM; (i < DP_MAX_PM); i++) {
+		rc = msm_dss_config_vreg(&pdev->dev,
+			parser->mp[i].vreg_config,
+			parser->mp[i].num_vreg, 0);
+		if (rc)
+			pr_err("failed to deinit vregs for %s\n",
+				dp_parser_pm_name(i));
+	}
+}
+
+static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
+{
+	int rc = 0, i = 0, j = 0;
+	struct dp_parser *parser;
+
+	parser = power->parser;
+
+	for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
+		rc = msm_dss_enable_vreg(
+			parser->mp[i].vreg_config,
+			parser->mp[i].num_vreg, enable);
+		if (rc) {
+			pr_err("failed to '%s' vregs for %s\n",
+					enable ? "enable" : "disable",
+					dp_parser_pm_name(i));
+			if (enable) {
+				for (j = i-1; j >= DP_CORE_PM; j--) {
+					msm_dss_enable_vreg(
+					parser->mp[j].vreg_config,
+					parser->mp[j].num_vreg, 0);
+				}
+			}
+			goto error;
+		}
+	}
+error:
+	return rc;
+}
+
+static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
+{
+	int rc = -EFAULT;
+	struct pinctrl_state *pin_state;
+	struct dp_parser *parser;
+
+	parser = power->parser;
+
+	if (IS_ERR_OR_NULL(parser->pinctrl.pin))
+		return PTR_ERR(parser->pinctrl.pin);
+
+	pin_state = active ? parser->pinctrl.state_active
+				: parser->pinctrl.state_suspend;
+	if (!IS_ERR_OR_NULL(pin_state)) {
+		rc = pinctrl_select_state(parser->pinctrl.pin,
+				pin_state);
+		if (rc)
+			pr_err("can not set %s pins\n",
+			       active ? "dp_active"
+			       : "dp_sleep");
+	} else {
+		pr_err("invalid '%s' pinstate\n",
+		       active ? "dp_active"
+		       : "dp_sleep");
+	}
+
+	return rc;
+}
+
+static int dp_power_clk_init(struct dp_power_private *power, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power *core, *ctrl;
+	struct device *dev;
+
+	core = &power->parser->mp[DP_CORE_PM];
+	ctrl = &power->parser->mp[DP_CTRL_PM];
+
+	dev = &power->pdev->dev;
+
+	if (!core || !ctrl) {
+		pr_err("invalid power_data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (enable) {
+		rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk);
+		if (rc) {
+			pr_err("failed to get %s clk. err=%d\n",
+				dp_parser_pm_name(DP_CORE_PM), rc);
+			goto exit;
+		}
+
+		rc = msm_dss_get_clk(dev, ctrl->clk_config, ctrl->num_clk);
+		if (rc) {
+			pr_err("failed to get %s clk. err=%d\n",
+				dp_parser_pm_name(DP_CTRL_PM), rc);
+			goto ctrl_get_error;
+		}
+
+		power->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg");
+		if (IS_ERR(power->pixel_clk_rcg)) {
+			pr_debug("Unable to get DP pixel clk RCG\n");
+			power->pixel_clk_rcg = NULL;
+		}
+
+		power->pixel_parent = devm_clk_get(dev, "pixel_parent");
+		if (IS_ERR(power->pixel_parent)) {
+			pr_debug("Unable to get DP pixel RCG parent\n");
+			power->pixel_parent = NULL;
+		}
+	} else {
+		if (power->pixel_parent)
+			devm_clk_put(dev, power->pixel_parent);
+
+		if (power->pixel_clk_rcg)
+			devm_clk_put(dev, power->pixel_clk_rcg);
+
+		msm_dss_put_clk(ctrl->clk_config, ctrl->num_clk);
+		msm_dss_put_clk(core->clk_config, core->num_clk);
+	}
+
+	return rc;
+
+ctrl_get_error:
+	msm_dss_put_clk(core->clk_config, core->num_clk);
+exit:
+	return rc;
+}
+
+static int dp_power_clk_set_rate(struct dp_power_private *power,
+		enum dp_pm_type module, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power *mp;
+
+	if (!power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mp = &power->parser->mp[module];
+
+	if (enable) {
+		rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
+		if (rc) {
+			pr_err("failed to set clks rate.\n");
+			goto exit;
+		}
+
+		rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 1);
+		if (rc) {
+			pr_err("failed to enable clks\n");
+			goto exit;
+		}
+	} else {
+		rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 0);
+		if (rc) {
+			pr_err("failed to disable clks\n");
+				goto exit;
+		}
+	}
+exit:
+	return rc;
+}
+
+static int dp_power_clk_enable(struct dp_power *dp_power,
+		enum dp_pm_type pm_type, bool enable)
+{
+	int rc = 0;
+	struct dss_module_power *mp;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	mp = &power->parser->mp[pm_type];
+
+	if ((pm_type != DP_CORE_PM) && (pm_type != DP_CTRL_PM)) {
+		pr_err("unsupported power module: %s\n",
+				dp_parser_pm_name(pm_type));
+		return -EINVAL;
+	}
+
+	if (enable) {
+		if ((pm_type == DP_CORE_PM)
+			&& (power->core_clks_on)) {
+			pr_debug("core clks already enabled\n");
+			return 0;
+		}
+
+		if ((pm_type == DP_CTRL_PM)
+			&& (power->link_clks_on)) {
+			pr_debug("links clks already enabled\n");
+			return 0;
+		}
+
+		if ((pm_type == DP_CTRL_PM) && (!power->core_clks_on)) {
+			pr_debug("Need to enable core clks before link clks\n");
+
+			rc = dp_power_clk_set_rate(power, DP_CORE_PM, enable);
+			if (rc) {
+				pr_err("failed to enable clks: %s. err=%d\n",
+					dp_parser_pm_name(DP_CORE_PM), rc);
+				goto error;
+			} else {
+				power->core_clks_on = true;
+			}
+		}
+	}
+
+	rc = dp_power_clk_set_rate(power, pm_type, enable);
+	if (rc) {
+		pr_err("failed to '%s' clks for: %s. err=%d\n",
+			enable ? "enable" : "disable",
+			dp_parser_pm_name(pm_type), rc);
+			goto error;
+	}
+
+	if (pm_type == DP_CORE_PM)
+		power->core_clks_on = enable;
+	else
+		power->link_clks_on = enable;
+
+	pr_debug("%s clocks for %s\n",
+			enable ? "enable" : "disable",
+			dp_parser_pm_name(pm_type));
+	pr_debug("link_clks:%s core_clks:%s\n",
+		power->link_clks_on ? "on" : "off",
+		power->core_clks_on ? "on" : "off");
+error:
+	return rc;
+}
+
+static int dp_power_request_gpios(struct dp_power_private *power)
+{
+	int rc = 0, i;
+	struct device *dev;
+	struct dss_module_power *mp;
+	static const char * const gpio_names[] = {
+		"aux_enable", "aux_sel", "usbplug_cc",
+	};
+
+	if (!power) {
+		pr_err("invalid power data\n");
+		return -EINVAL;
+	}
+
+	dev = &power->pdev->dev;
+	mp = &power->parser->mp[DP_CORE_PM];
+
+	for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
+		unsigned int gpio = mp->gpio_config[i].gpio;
+
+		if (gpio_is_valid(gpio)) {
+			rc = devm_gpio_request(dev, gpio, gpio_names[i]);
+			if (rc) {
+				pr_err("request %s gpio failed, rc=%d\n",
+					       gpio_names[i], rc);
+				goto error;
+			}
+		}
+	}
+	return 0;
+error:
+	for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
+		unsigned int gpio = mp->gpio_config[i].gpio;
+
+		if (gpio_is_valid(gpio))
+			gpio_free(gpio);
+	}
+	return rc;
+}
+
+static bool dp_power_find_gpio(const char *gpio1, const char *gpio2)
+{
+	return !!strnstr(gpio1, gpio2, strlen(gpio1));
+}
+
+static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
+{
+	int i;
+	struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
+	struct dss_gpio *config = mp->gpio_config;
+
+	for (i = 0; i < mp->num_gpio; i++) {
+		if (dp_power_find_gpio(config->gpio_name, "aux-sel"))
+			config->value = flip;
+
+		if (gpio_is_valid(config->gpio)) {
+			pr_debug("gpio %s, value %d\n", config->gpio_name,
+				config->value);
+
+			if (dp_power_find_gpio(config->gpio_name, "aux-en") ||
+			    dp_power_find_gpio(config->gpio_name, "aux-sel"))
+				gpio_direction_output(config->gpio,
+					config->value);
+			else
+				gpio_set_value(config->gpio, config->value);
+
+		}
+		config++;
+	}
+}
+
+static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
+					bool enable)
+{
+	int rc = 0, i;
+	struct dss_module_power *mp;
+	struct dss_gpio *config;
+
+	mp = &power->parser->mp[DP_CORE_PM];
+	config = mp->gpio_config;
+
+	if (enable) {
+		rc = dp_power_request_gpios(power);
+		if (rc) {
+			pr_err("gpio request failed\n");
+			return rc;
+		}
+
+		dp_power_set_gpio(power, flip);
+	} else {
+		for (i = 0; i < mp->num_gpio; i++) {
+			gpio_set_value(config[i].gpio, 0);
+			gpio_free(config[i].gpio);
+		}
+	}
+
+	return 0;
+}
+
+static int dp_power_client_init(struct dp_power *dp_power)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		return -EINVAL;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	pm_runtime_enable(&power->pdev->dev);
+
+	rc = dp_power_regulator_init(power);
+	if (rc) {
+		pr_err("failed to init regulators\n");
+		goto error_power;
+	}
+
+	rc = dp_power_clk_init(power, true);
+	if (rc) {
+		pr_err("failed to init clocks\n");
+		goto error_clk;
+	}
+	return 0;
+
+error_clk:
+	dp_power_regulator_deinit(power);
+error_power:
+	pm_runtime_disable(&power->pdev->dev);
+	return rc;
+}
+
+static void dp_power_client_deinit(struct dp_power *dp_power)
+{
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		return;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	dp_power_clk_init(power, false);
+	dp_power_regulator_deinit(power);
+	pm_runtime_disable(&power->pdev->dev);
+
+}
+
+static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	if (power->pixel_clk_rcg && power->pixel_parent)
+		clk_set_parent(power->pixel_clk_rcg, power->pixel_parent);
+exit:
+	return rc;
+}
+
+static int dp_power_init(struct dp_power *dp_power, bool flip)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	pm_runtime_get_sync(&power->pdev->dev);
+	rc = dp_power_regulator_ctrl(power, true);
+	if (rc) {
+		pr_err("failed to enable regulators\n");
+		goto exit;
+	}
+
+	rc = dp_power_pinctrl_set(power, true);
+	if (rc) {
+		pr_err("failed to set pinctrl state\n");
+		goto err_pinctrl;
+	}
+
+	rc = dp_power_config_gpios(power, flip, true);
+	if (rc) {
+		pr_err("failed to enable gpios\n");
+		goto err_gpio;
+	}
+
+	rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
+	if (rc) {
+		pr_err("failed to enable DP core clocks\n");
+		goto err_clk;
+	}
+
+	return 0;
+
+err_clk:
+	dp_power_config_gpios(power, flip, false);
+err_gpio:
+	dp_power_pinctrl_set(power, false);
+err_pinctrl:
+	dp_power_regulator_ctrl(power, false);
+exit:
+	pm_runtime_put_sync(&power->pdev->dev);
+	return rc;
+}
+
+static int dp_power_deinit(struct dp_power *dp_power)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+
+	if (!dp_power) {
+		pr_err("invalid power data\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	dp_power_clk_enable(dp_power, DP_CORE_PM, false);
+	dp_power_config_gpios(power, false, false);
+	dp_power_pinctrl_set(power, false);
+	dp_power_regulator_ctrl(power, false);
+	pm_runtime_put_sync(&power->pdev->dev);
+exit:
+	return rc;
+}
+
+struct dp_power *dp_power_get(struct dp_parser *parser)
+{
+	int rc = 0;
+	struct dp_power_private *power;
+	struct dp_power *dp_power;
+
+	if (!parser) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL);
+	if (!power) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	power->parser = parser;
+	power->pdev = parser->pdev;
+
+	dp_power = &power->dp_power;
+
+	dp_power->init = dp_power_init;
+	dp_power->deinit = dp_power_deinit;
+	dp_power->clk_enable = dp_power_clk_enable;
+	dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent;
+	dp_power->power_client_init = dp_power_client_init;
+	dp_power->power_client_deinit = dp_power_client_deinit;
+
+	return dp_power;
+error:
+	return ERR_PTR(rc);
+}
+
+void dp_power_put(struct dp_power *dp_power)
+{
+	struct dp_power_private *power = NULL;
+
+	if (!dp_power)
+		return;
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
+
+	devm_kfree(&power->pdev->dev, power);
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_power.h b/drivers/gpu/drm/msm/dp/dp_power.h
new file mode 100644
index 0000000..d1adaaf
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_power.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_POWER_H_
+#define _DP_POWER_H_
+
+#include "dp_parser.h"
+#include "dpu_power_handle.h"
+
+/**
+ * sruct dp_power - DisplayPort's power related data
+ *
+ * @init: initializes the regulators/core clocks/GPIOs/pinctrl
+ * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
+ * @clk_enable: enable/disable the DP clocks
+ * @set_pixel_clk_parent: set the parent of DP pixel clock
+ */
+struct dp_power {
+	int (*init)(struct dp_power *power, bool flip);
+	int (*deinit)(struct dp_power *power);
+	int (*clk_enable)(struct dp_power *power, enum dp_pm_type pm_type,
+				bool enable);
+	int (*set_pixel_clk_parent)(struct dp_power *power);
+	int (*power_client_init)(struct dp_power *power);
+	void (*power_client_deinit)(struct dp_power *power);
+};
+
+/**
+ * dp_power_get() - configure and get the DisplayPort power module data
+ *
+ * @parser: instance of parser module
+ * return: pointer to allocated power module data
+ *
+ * This API will configure the DisplayPort's power module and provides
+ * methods to be called by the client to configure the power related
+ * modueles.
+ */
+struct dp_power *dp_power_get(struct dp_parser *parser);
+
+/**
+ * dp_power_put() - release the power related resources
+ *
+ * @power: pointer to the power module's data
+ */
+void dp_power_put(struct dp_power *power);
+#endif /* _DP_POWER_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
new file mode 100644
index 0000000..77b5c0e
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DP_REG_H_
+#define _DP_REG_H_
+
+/* DP_TX Registers */
+#define DP_HW_VERSION				(0x00000000)
+#define DP_SW_RESET				(0x00000010)
+#define DP_PHY_CTRL				(0x00000014)
+#define DP_CLK_CTRL				(0x00000018)
+#define DP_CLK_ACTIVE				(0x0000001C)
+#define DP_INTR_STATUS				(0x00000020)
+#define DP_INTR_STATUS2				(0x00000024)
+#define DP_INTR_STATUS3				(0x00000028)
+
+#define DP_DP_HPD_CTRL				(0x00000000)
+#define DP_DP_HPD_INT_STATUS			(0x00000004)
+#define DP_DP_HPD_INT_ACK			(0x00000008)
+#define DP_DP_HPD_INT_MASK			(0x0000000C)
+#define DP_DP_HPD_REFTIMER			(0x00000018)
+#define DP_DP_HPD_EVENT_TIME_0			(0x0000001C)
+#define DP_DP_HPD_EVENT_TIME_1			(0x00000020)
+#define DP_AUX_CTRL				(0x00000030)
+#define DP_AUX_DATA				(0x00000034)
+#define DP_AUX_TRANS_CTRL			(0x00000038)
+#define DP_TIMEOUT_COUNT			(0x0000003C)
+#define DP_AUX_LIMITS				(0x00000040)
+#define DP_AUX_STATUS				(0x00000044)
+
+#define DP_DPCD_CP_IRQ				(0x201)
+#define DP_DPCD_RXSTATUS			(0x69493)
+
+#define DP_INTERRUPT_TRANS_NUM			(0x000000A0)
+
+#define DP_MAINLINK_CTRL			(0x00000000)
+#define DP_STATE_CTRL				(0x00000004)
+#define DP_CONFIGURATION_CTRL			(0x00000008)
+#define DP_SOFTWARE_MVID			(0x00000010)
+#define DP_SOFTWARE_NVID			(0x00000018)
+#define DP_TOTAL_HOR_VER			(0x0000001C)
+#define DP_START_HOR_VER_FROM_SYNC		(0x00000020)
+#define DP_HSYNC_VSYNC_WIDTH_POLARITY		(0x00000024)
+#define DP_ACTIVE_HOR_VER			(0x00000028)
+#define DP_MISC1_MISC0				(0x0000002C)
+#define DP_VALID_BOUNDARY			(0x00000030)
+#define DP_VALID_BOUNDARY_2			(0x00000034)
+#define DP_LOGICAL2PHYSICAL_LANE_MAPPING	(0x00000038)
+
+#define DP_MAINLINK_READY			(0x00000040)
+#define DP_MAINLINK_LEVELS			(0x00000044)
+#define DP_TU					(0x0000004C)
+
+#define DP_HBR2_COMPLIANCE_SCRAMBLER_RESET	(0x00000054)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG0	(0x000000C0)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG1	(0x000000C4)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG2	(0x000000C8)
+
+#define MMSS_DP_MISC1_MISC0			(0x0000002C)
+#define MMSS_DP_AUDIO_TIMING_GEN		(0x00000080)
+#define MMSS_DP_AUDIO_TIMING_RBR_32		(0x00000084)
+#define MMSS_DP_AUDIO_TIMING_HBR_32		(0x00000088)
+#define MMSS_DP_AUDIO_TIMING_RBR_44		(0x0000008C)
+#define MMSS_DP_AUDIO_TIMING_HBR_44		(0x00000090)
+#define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
+#define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
+
+#define MMSS_DP_PSR_CRC_RG			(0x00000154)
+#define MMSS_DP_PSR_CRC_B			(0x00000158)
+
+#define DP_COMPRESSION_MODE_CTRL		(0x00000180)
+
+#define MMSS_DP_AUDIO_CFG			(0x00000200)
+#define MMSS_DP_AUDIO_STATUS			(0x00000204)
+#define MMSS_DP_AUDIO_PKT_CTRL			(0x00000208)
+#define MMSS_DP_AUDIO_PKT_CTRL2			(0x0000020C)
+#define MMSS_DP_AUDIO_ACR_CTRL			(0x00000210)
+#define MMSS_DP_AUDIO_CTRL_RESET		(0x00000214)
+
+#define MMSS_DP_SDP_CFG				(0x00000228)
+#define MMSS_DP_SDP_CFG2			(0x0000022C)
+#define MMSS_DP_AUDIO_TIMESTAMP_0		(0x00000230)
+#define MMSS_DP_AUDIO_TIMESTAMP_1		(0x00000234)
+
+#define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
+#define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
+
+#define MMSS_DP_EXTENSION_0			(0x00000250)
+#define MMSS_DP_EXTENSION_1			(0x00000254)
+#define MMSS_DP_EXTENSION_2			(0x00000258)
+#define MMSS_DP_EXTENSION_3			(0x0000025C)
+#define MMSS_DP_EXTENSION_4			(0x00000260)
+#define MMSS_DP_EXTENSION_5			(0x00000264)
+#define MMSS_DP_EXTENSION_6			(0x00000268)
+#define MMSS_DP_EXTENSION_7			(0x0000026C)
+#define MMSS_DP_EXTENSION_8			(0x00000270)
+#define MMSS_DP_EXTENSION_9			(0x00000274)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_0		(0x00000278)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_1		(0x0000027C)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_2		(0x00000280)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_3		(0x00000284)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_4		(0x00000288)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_5		(0x0000028C)
+#define MMSS_DP_AUDIO_ISRC_0			(0x00000290)
+#define MMSS_DP_AUDIO_ISRC_1			(0x00000294)
+#define MMSS_DP_AUDIO_ISRC_2			(0x00000298)
+#define MMSS_DP_AUDIO_ISRC_3			(0x0000029C)
+#define MMSS_DP_AUDIO_ISRC_4			(0x000002A0)
+#define MMSS_DP_AUDIO_ISRC_5			(0x000002A4)
+#define MMSS_DP_AUDIO_INFOFRAME_0		(0x000002A8)
+#define MMSS_DP_AUDIO_INFOFRAME_1		(0x000002AC)
+#define MMSS_DP_AUDIO_INFOFRAME_2		(0x000002B0)
+
+#define MMSS_DP_GENERIC0_0			(0x00000300)
+#define MMSS_DP_GENERIC0_1			(0x00000304)
+#define MMSS_DP_GENERIC0_2			(0x00000308)
+#define MMSS_DP_GENERIC0_3			(0x0000030C)
+#define MMSS_DP_GENERIC0_4			(0x00000310)
+#define MMSS_DP_GENERIC0_5			(0x00000314)
+#define MMSS_DP_GENERIC0_6			(0x00000318)
+#define MMSS_DP_GENERIC0_7			(0x0000031C)
+#define MMSS_DP_GENERIC0_8			(0x00000320)
+#define MMSS_DP_GENERIC0_9			(0x00000324)
+#define MMSS_DP_GENERIC1_0			(0x00000328)
+#define MMSS_DP_GENERIC1_1			(0x0000032C)
+#define MMSS_DP_GENERIC1_2			(0x00000330)
+#define MMSS_DP_GENERIC1_3			(0x00000334)
+#define MMSS_DP_GENERIC1_4			(0x00000338)
+#define MMSS_DP_GENERIC1_5			(0x0000033C)
+#define MMSS_DP_GENERIC1_6			(0x00000340)
+#define MMSS_DP_GENERIC1_7			(0x00000344)
+#define MMSS_DP_GENERIC1_8			(0x00000348)
+#define MMSS_DP_GENERIC1_9			(0x0000034C)
+
+#define MMSS_DP_VSCEXT_0			(0x000002D0)
+#define MMSS_DP_VSCEXT_1			(0x000002D4)
+#define MMSS_DP_VSCEXT_2			(0x000002D8)
+#define MMSS_DP_VSCEXT_3			(0x000002DC)
+#define MMSS_DP_VSCEXT_4			(0x000002E0)
+#define MMSS_DP_VSCEXT_5			(0x000002E4)
+#define MMSS_DP_VSCEXT_6			(0x000002E8)
+#define MMSS_DP_VSCEXT_7			(0x000002EC)
+#define MMSS_DP_VSCEXT_8			(0x000002F0)
+#define MMSS_DP_VSCEXT_9			(0x000002F4)
+
+#define MMSS_DP_BIST_ENABLE			(0x00000000)
+#define MMSS_DP_TIMING_ENGINE_EN		(0x00000010)
+#define MMSS_DP_INTF_CONFIG			(0x00000014)
+#define MMSS_DP_INTF_HSYNC_CTL			(0x00000018)
+#define MMSS_DP_INTF_VSYNC_PERIOD_F0		(0x0000001C)
+#define MMSS_DP_INTF_VSYNC_PERIOD_F1		(0x00000020)
+#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0	(0x00000024)
+#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1	(0x00000028)
+#define MMSS_INTF_DISPLAY_V_START_F0		(0x0000002C)
+#define MMSS_INTF_DISPLAY_V_START_F1		(0x00000030)
+#define MMSS_DP_INTF_DISPLAY_V_END_F0		(0x00000034)
+#define MMSS_DP_INTF_DISPLAY_V_END_F1		(0x00000038)
+#define MMSS_DP_INTF_ACTIVE_V_START_F0		(0x0000003C)
+#define MMSS_DP_INTF_ACTIVE_V_START_F1		(0x00000040)
+#define MMSS_DP_INTF_ACTIVE_V_END_F0		(0x00000044)
+#define MMSS_DP_INTF_ACTIVE_V_END_F1		(0x00000048)
+#define MMSS_DP_INTF_DISPLAY_HCTL		(0x0000004C)
+#define MMSS_DP_INTF_ACTIVE_HCTL		(0x00000050)
+#define MMSS_DP_INTF_POLARITY_CTL		(0x00000058)
+#define MMSS_DP_TPG_MAIN_CONTROL		(0x00000060)
+#define MMSS_DP_TPG_VIDEO_CONFIG		(0x00000064)
+#define MMSS_DP_ASYNC_FIFO_CONFIG		(0x00000088)
+
+/*DP PHY Register offsets */
+#define DP_PHY_REVISION_ID0                     (0x00000000)
+#define DP_PHY_REVISION_ID1                     (0x00000004)
+#define DP_PHY_REVISION_ID2                     (0x00000008)
+#define DP_PHY_REVISION_ID3                     (0x0000000C)
+
+#define DP_PHY_CFG                              (0x00000010)
+#define DP_PHY_PD_CTL                           (0x00000018)
+#define DP_PHY_MODE                             (0x0000001C)
+
+#define DP_PHY_AUX_CFG0                         (0x00000020)
+#define DP_PHY_AUX_CFG1                         (0x00000024)
+#define DP_PHY_AUX_CFG2                         (0x00000028)
+#define DP_PHY_AUX_CFG3                         (0x0000002C)
+#define DP_PHY_AUX_CFG4                         (0x00000030)
+#define DP_PHY_AUX_CFG5                         (0x00000034)
+#define DP_PHY_AUX_CFG6                         (0x00000038)
+#define DP_PHY_AUX_CFG7                         (0x0000003C)
+#define DP_PHY_AUX_CFG8                         (0x00000040)
+#define DP_PHY_AUX_CFG9                         (0x00000044)
+#define DP_PHY_AUX_INTERRUPT_MASK               (0x00000048)
+#define DP_PHY_AUX_INTERRUPT_CLEAR              (0x0000004C)
+#define DP_PHY_AUX_BIST_CFG			(0x00000050)
+
+#define DP_PHY_VCO_DIV				0x0064
+#define DP_PHY_TX0_TX1_LANE_CTL			0x006C
+#define DP_PHY_TX2_TX3_LANE_CTL			0x0088
+
+#define DP_PHY_SPARE0				(0x00AC)
+#define DP_PHY_STATUS				(0x00C0)
+
+/* Tx registers */
+#define TXn_BIST_MODE_LANENO			0x0000
+#define TXn_CLKBUF_ENABLE			0x0008
+#define TXn_TX_EMP_POST1_LVL			0x000C
+
+#define TXn_TX_DRV_LVL				0x001C
+
+#define TXn_RESET_TSYNC_EN			0x0024
+#define TXn_PRE_STALL_LDO_BOOST_EN		0x0028
+#define TXn_TX_BAND				0x002C
+#define TXn_SLEW_CNTL				0x0030
+#define TXn_INTERFACE_SELECT			0x0034
+
+#define TXn_RES_CODE_LANE_TX			0x003C
+#define TXn_RES_CODE_LANE_RX			0x0040
+#define TXn_RES_CODE_LANE_OFFSET_TX		0x0044
+#define TXn_RES_CODE_LANE_OFFSET_RX		0x0048
+
+#define TXn_DEBUG_BUS_SEL			0x0058
+#define TXn_TRANSCEIVER_BIAS_EN			0x005C
+#define TXn_HIGHZ_DRVR_EN			0x0060
+#define TXn_TX_POL_INV				0x0064
+#define TXn_PARRATE_REC_DETECT_IDLE_EN		0x0068
+
+#define TXn_LANE_MODE_1				0x008C
+
+#define TXn_TRAN_DRVR_EMP_EN			0x00C0
+#define TXn_TX_INTERFACE_MODE			0x00C4
+
+#define TXn_VMODE_CTRL1				0x00F0
+
+/* PLL register offset */
+#define QSERDES_COM_ATB_SEL1			0x0000
+#define QSERDES_COM_ATB_SEL2			0x0004
+#define QSERDES_COM_FREQ_UPDATE			0x0008
+#define QSERDES_COM_BG_TIMER			0x000C
+#define QSERDES_COM_SSC_EN_CENTER		0x0010
+#define QSERDES_COM_SSC_ADJ_PER1		0x0014
+#define QSERDES_COM_SSC_ADJ_PER2		0x0018
+#define QSERDES_COM_SSC_PER1			0x001C
+#define QSERDES_COM_SSC_PER2			0x0020
+#define QSERDES_COM_SSC_STEP_SIZE1		0x0024
+#define QSERDES_COM_SSC_STEP_SIZE2		0x0028
+#define QSERDES_COM_POST_DIV			0x002C
+#define QSERDES_COM_POST_DIV_MUX		0x0030
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		0x0034
+#define QSERDES_COM_CLK_ENABLE1			0x0038
+#define QSERDES_COM_SYS_CLK_CTRL		0x003C
+#define QSERDES_COM_SYSCLK_BUF_ENABLE		0x0040
+#define QSERDES_COM_PLL_EN			0x0044
+#define QSERDES_COM_PLL_IVCO			0x0048
+#define QSERDES_COM_CMN_IETRIM			0x004C
+#define QSERDES_COM_CMN_IPTRIM			0x0050
+
+#define QSERDES_COM_CP_CTRL_MODE0		0x0060
+#define QSERDES_COM_CP_CTRL_MODE1		0x0064
+#define QSERDES_COM_PLL_RCTRL_MODE0		0x0068
+#define QSERDES_COM_PLL_RCTRL_MODE1		0x006C
+#define QSERDES_COM_PLL_CCTRL_MODE0		0x0070
+#define QSERDES_COM_PLL_CCTRL_MODE1		0x0074
+#define QSERDES_COM_PLL_CNTRL			0x0078
+#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM		0x007C
+#define QSERDES_COM_SYSCLK_EN_SEL		0x0080
+#define QSERDES_COM_CML_SYSCLK_SEL		0x0084
+#define QSERDES_COM_RESETSM_CNTRL		0x0088
+#define QSERDES_COM_RESETSM_CNTRL2		0x008C
+#define QSERDES_COM_LOCK_CMP_EN			0x0090
+#define QSERDES_COM_LOCK_CMP_CFG		0x0094
+#define QSERDES_COM_LOCK_CMP1_MODE0		0x0098
+#define QSERDES_COM_LOCK_CMP2_MODE0		0x009C
+#define QSERDES_COM_LOCK_CMP3_MODE0		0x00A0
+
+#define QSERDES_COM_DEC_START_MODE0		0x00B0
+#define QSERDES_COM_DEC_START_MODE1		0x00B4
+#define QSERDES_COM_DIV_FRAC_START1_MODE0	0x00B8
+#define QSERDES_COM_DIV_FRAC_START2_MODE0	0x00BC
+#define QSERDES_COM_DIV_FRAC_START3_MODE0	0x00C0
+#define QSERDES_COM_DIV_FRAC_START1_MODE1	0x00C4
+#define QSERDES_COM_DIV_FRAC_START2_MODE1	0x00C8
+#define QSERDES_COM_DIV_FRAC_START3_MODE1	0x00CC
+#define QSERDES_COM_INTEGLOOP_INITVAL		0x00D0
+#define QSERDES_COM_INTEGLOOP_EN		0x00D4
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0	0x00D8
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0	0x00DC
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1	0x00E0
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1	0x00E4
+#define QSERDES_COM_VCOCAL_DEADMAN_CTRL		0x00E8
+#define QSERDES_COM_VCO_TUNE_CTRL		0x00EC
+#define QSERDES_COM_VCO_TUNE_MAP		0x00F0
+
+#define QSERDES_COM_CMN_STATUS			0x0124
+#define QSERDES_COM_RESET_SM_STATUS		0x0128
+
+#define QSERDES_COM_CLK_SEL			0x0138
+#define QSERDES_COM_HSCLK_SEL			0x013C
+
+#define QSERDES_COM_CORECLK_DIV_MODE0		0x0148
+
+#define QSERDES_COM_SW_RESET			0x0150
+#define QSERDES_COM_CORE_CLK_EN			0x0154
+#define QSERDES_COM_C_READY_STATUS		0x0158
+#define QSERDES_COM_CMN_CONFIG			0x015C
+
+#define QSERDES_COM_SVS_MODE_CLK_SEL		0x0164
+
+/* DP MMSS_CC registers */
+#define MMSS_DP_LINK_CMD_RCGR			(0x0138)
+#define MMSS_DP_LINK_CFG_RCGR			(0x013C)
+#define MMSS_DP_PIXEL_M				(0x0174)
+#define MMSS_DP_PIXEL_N				(0x0178)
+
+/* DP HDCP 1.3 registers */
+#define DP_HDCP_CTRL                                   (0x0A0)
+#define DP_HDCP_STATUS                                 (0x0A4)
+#define DP_HDCP_SW_UPPER_AKSV                          (0x098)
+#define DP_HDCP_SW_LOWER_AKSV                          (0x09C)
+#define DP_HDCP_ENTROPY_CTRL0                          (0x350)
+#define DP_HDCP_ENTROPY_CTRL1                          (0x35C)
+#define DP_HDCP_SHA_STATUS                             (0x0C8)
+#define DP_HDCP_RCVPORT_DATA2_0                        (0x0B0)
+#define DP_HDCP_RCVPORT_DATA3                          (0x0A4)
+#define DP_HDCP_RCVPORT_DATA4                          (0x0A8)
+#define DP_HDCP_RCVPORT_DATA5                          (0x0C0)
+#define DP_HDCP_RCVPORT_DATA6                          (0x0C4)
+
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_CTRL           (0x024)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_DATA           (0x028)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA0      (0x004)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA1      (0x008)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA7      (0x00C)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA8      (0x010)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9      (0x014)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10     (0x018)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11     (0x01C)
+#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12     (0x020)
+
+/* USB3 DP COM registers */
+#define USB3_DP_COM_RESET_OVRD_CTRL (0x1C)
+#define USB3_DP_COM_PHY_MODE_CTRL   (0x00)
+#define USB3_DP_COM_SW_RESET        (0x04)
+#define USB3_DP_COM_TYPEC_CTRL      (0x10)
+#define USB3_DP_COM_SWI_CTRL        (0x0c)
+#define USB3_DP_COM_POWER_DOWN_CTRL (0x08)
+
+
+
+#endif /* _DP_REG_H_ */
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index f5cd7f6..be801be 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -1367,6 +1367,7 @@ static int __init msm_drm_register(void)
 	msm_dsi_register();
 	msm_edp_register();
 	msm_hdmi_register();
+	msm_dp_register();
 	adreno_register();
 	return platform_driver_register(&msm_platform_driver);
 }
@@ -1375,6 +1376,7 @@ static void __exit msm_drm_unregister(void)
 {
 	DBG("fini");
 	platform_driver_unregister(&msm_platform_driver);
+	msm_dp_unregister();
 	msm_hdmi_unregister();
 	adreno_unregister();
 	msm_edp_unregister();
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 9d11f32..fb1e6f4 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -176,6 +176,7 @@ struct msm_drm_private {
 	/* DSI is shared by mdp4 and mdp5 */
 	struct msm_dsi *dsi[2];
 
+	struct msm_dp *dp;
 	/* when we have more than one 'msm_gpu' these need to be an array: */
 	struct msm_gpu *gpu;
 	struct msm_file_private *lastctx;
@@ -364,6 +365,27 @@ static inline int msm_dsi_modeset_init(struct msm_dsi *msm_dsi,
 }
 #endif
 
+#ifdef CONFIG_DRM_MSM_DP
+int __init msm_dp_register(void);
+void __exit msm_dp_unregister(void);
+int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
+			 struct drm_encoder *encoder);
+#else
+static inline int __init msm_dp_register(void)
+{
+	return -EINVAL;
+}
+static inline void __exit msm_dp_unregister(void)
+{
+}
+static inline int msm_dp_modeset_init(struct msm_dp *dp_display,
+				       struct drm_device *dev,
+				       struct drm_encoder *encoder)
+{
+	return -EINVAL;
+}
+#endif
+
 void __init msm_mdp_register(void);
 void __exit msm_mdp_unregister(void);
 void __init msm_dpu_register(void);
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 2a3843f..b0816f5 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -551,6 +551,8 @@
 # define DP_TEST_LINK_EDID_READ		    (1 << 2)
 # define DP_TEST_LINK_PHY_TEST_PATTERN	    (1 << 3) /* DPCD >= 1.1 */
 # define DP_TEST_LINK_FAUX_PATTERN	    (1 << 4) /* DPCD >= 1.2 */
+# define DP_TEST_LINK_AUDIO_PATTERN     (1 << 5)
+# define DP_TEST_LINK_AUDIO_DISABLED_VIDEO (1 << 6)
 
 #define DP_TEST_LINK_RATE		    0x219
 # define DP_LINK_RATE_162		    (0x6)
@@ -639,6 +641,13 @@
 #define	DP_TEST_80BIT_CUSTOM_PATTERN_71_64  0x258
 #define	DP_TEST_80BIT_CUSTOM_PATTERN_79_72  0x259
 
+#define DP_TEST_PHY_PATTERN_NONE			0x0
+#define DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING	0x1
+#define DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT 0x2
+#define DP_TEST_PHY_PATTERN_PRBS7			0x3
+#define DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN	0x4
+#define DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN	0x5
+
 #define DP_TEST_RESPONSE		    0x260
 # define DP_TEST_ACK			    (1 << 0)
 # define DP_TEST_NAK			    (1 << 1)
@@ -648,6 +657,16 @@
 
 #define DP_TEST_SINK			    0x270
 # define DP_TEST_SINK_START		    (1 << 0)
+#define DP_TEST_AUDIO_MODE		    0x271
+#define DP_TEST_AUDIO_PATTERN_TYPE	    0x272
+#define DP_TEST_AUDIO_PERIOD_CH1	    0x273
+#define DP_TEST_AUDIO_PERIOD_CH2	    0x274
+#define DP_TEST_AUDIO_PERIOD_CH3	    0x275
+#define DP_TEST_AUDIO_PERIOD_CH4	    0x276
+#define DP_TEST_AUDIO_PERIOD_CH5	    0x277
+#define DP_TEST_AUDIO_PERIOD_CH6	    0x278
+#define DP_TEST_AUDIO_PERIOD_CH7	    0x279
+#define DP_TEST_AUDIO_PERIOD_CH8	    0x27A
 
 #define DP_FEC_STATUS			    0x280    /* 1.4 */
 # define DP_FEC_DECODE_EN_DETECTED	    (1 << 0)
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

_______________________________________________
dri-devel mailing list
dri-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/dri-devel





[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux