Merge "msm: mdss: add support to program of HSTX drivers for DSI 12nm PHY"
diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
index 7bcb2dc..b8cb903 100644
--- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt
@@ -40,6 +40,8 @@
 					timing settings for the panel.
 - qcom,mdss-dsi-panel-timings-phy-v2:	An array of length 40 char that specifies the PHY version 2
 					lane timing settings for the panel.
+- qcom,mdss-dsi-panel-timings-phy-12nm: An array of length 8 char that specifies the 12nm DSI PHY
+					lane timing settings for the panel.
 - qcom,mdss-dsi-on-command:		A byte stream formed by multiple dcs packets base on
 					qcom dsi controller protocol.
 					byte 0: dcs data type
@@ -631,6 +633,8 @@
                                 23 20 06 09 05 03 04 a0
                                 23 20 06 09 05 03 04 a0
                                 23 2e 06 08 05 03 04 a0];
+                qcom,mdss-dsi-panel-timings-phy-12nm =
+				[a9 4e 56 0b 8a 4d 0b d6];
 		qcom,mdss-dsi-on-command = [32 01 00 00 00 00 02 00 00
 					29 01 00 00 10 00 02 FF 99];
 		qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index 4ee7f4a..81d4953 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -41,6 +41,7 @@
 mdss-dsi-objs += mdss_dsi_panel.o
 mdss-dsi-objs += msm_mdss_io_8974.o
 mdss-dsi-objs += mdss_dsi_phy.o
+mdss-dsi-objs += mdss_dsi_phy_12nm.o
 mdss-dsi-objs += mdss_dsi_clk.o
 obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
 obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o
diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c
index 6bb0960..cad1387 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.c
+++ b/drivers/video/fbdev/msm/mdss_dsi.c
@@ -2989,9 +2989,9 @@
 	info.core_clks.mmss_misc_ahb_clk =
 		ctrl_pdata->shared_data->mmss_misc_ahb_clk;
 
-	info.link_clks.esc_clk = ctrl_pdata->esc_clk;
-	info.link_clks.byte_clk = ctrl_pdata->byte_clk;
-	info.link_clks.pixel_clk = ctrl_pdata->pixel_clk;
+	info.link_lp_clks.esc_clk = ctrl_pdata->esc_clk;
+	info.link_hs_clks.byte_clk = ctrl_pdata->byte_clk;
+	info.link_hs_clks.pixel_clk = ctrl_pdata->pixel_clk;
 
 	info.pre_clkoff_cb = mdss_dsi_pre_clkoff_cb;
 	info.post_clkon_cb = mdss_dsi_post_clkon_cb;
@@ -3976,13 +3976,12 @@
 	if (!data) {
 		pr_err("%s:%d, Unable to read Phy Strength ctrl settings\n",
 			__func__, __LINE__);
-		return -EINVAL;
+	} else {
+		pinfo->mipi.dsi_phy_db.strength_len = len;
+		for (i = 0; i < len; i++)
+			pinfo->mipi.dsi_phy_db.strength[i] = data[i];
 	}
 
-	pinfo->mipi.dsi_phy_db.strength_len = len;
-	for (i = 0; i < len; i++)
-		pinfo->mipi.dsi_phy_db.strength[i] = data[i];
-
 	pinfo->mipi.dsi_phy_db.reg_ldo_mode = of_property_read_bool(
 		ctrl_pdev->dev.of_node, "qcom,regulator-ldo-mode");
 
@@ -3991,13 +3990,12 @@
 	if (!data) {
 		pr_err("%s:%d, Unable to read Phy regulator settings\n",
 			__func__, __LINE__);
-		return -EINVAL;
+	} else {
+		pinfo->mipi.dsi_phy_db.regulator_len = len;
+		for (i = 0; i < len; i++)
+			pinfo->mipi.dsi_phy_db.regulator[i] = data[i];
 	}
 
-	pinfo->mipi.dsi_phy_db.regulator_len = len;
-	for (i = 0; i < len; i++)
-		pinfo->mipi.dsi_phy_db.regulator[i] = data[i];
-
 	data = of_get_property(ctrl_pdev->dev.of_node,
 		"qcom,platform-bist-ctrl", &len);
 	if ((!data) || (len != 6))
@@ -4012,13 +4010,12 @@
 	if (!data) {
 		pr_err("%s:%d, Unable to read Phy lane configure settings\n",
 			__func__, __LINE__);
-		return -EINVAL;
+	} else {
+		pinfo->mipi.dsi_phy_db.lanecfg_len = len;
+		for (i = 0; i < len; i++)
+			pinfo->mipi.dsi_phy_db.lanecfg[i] = data[i];
 	}
 
-	pinfo->mipi.dsi_phy_db.lanecfg_len = len;
-	for (i = 0; i < len; i++)
-		pinfo->mipi.dsi_phy_db.lanecfg[i] = data[i];
-
 	ctrl_pdata->timing_db_mode = of_property_read_bool(
 		ctrl_pdev->dev.of_node, "qcom,timing-db-mode");
 
diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h
index 60bc455..058c27a 100644
--- a/drivers/video/fbdev/msm/mdss_dsi.h
+++ b/drivers/video/fbdev/msm/mdss_dsi.h
@@ -337,6 +337,7 @@
 	struct mdss_panel_timing timing;
 	uint32_t phy_timing[12];
 	uint32_t phy_timing_8996[40];
+	uint32_t phy_timing_12nm[8];
 	/* DSI_CLKOUT_TIMING_CTRL */
 	char t_clk_post;
 	char t_clk_pre;
@@ -616,15 +617,19 @@
 			struct mdss_dsi_ctrl_pdata *ctrl_pdata);
 int mdss_dsi_pre_clkoff_cb(void *priv,
 			   enum mdss_dsi_clk_type clk_type,
+			   enum mdss_dsi_lclk_type l_type,
 			   enum mdss_dsi_clk_state new_state);
 int mdss_dsi_post_clkoff_cb(void *priv,
 			    enum mdss_dsi_clk_type clk_type,
+			    enum mdss_dsi_lclk_type l_type,
 			    enum mdss_dsi_clk_state curr_state);
 int mdss_dsi_post_clkon_cb(void *priv,
 			   enum mdss_dsi_clk_type clk_type,
+			   enum mdss_dsi_lclk_type l_type,
 			   enum mdss_dsi_clk_state curr_state);
 int mdss_dsi_pre_clkon_cb(void *priv,
 			  enum mdss_dsi_clk_type clk_type,
+			  enum mdss_dsi_lclk_type l_type,
 			  enum mdss_dsi_clk_state new_state);
 int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable);
 void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_clk.c b/drivers/video/fbdev/msm/mdss_dsi_clk.c
index 372c93e..779e847 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_clk.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_clk.c
@@ -27,11 +27,9 @@
 };
 
 struct dsi_link_clks {
-	struct mdss_dsi_link_clk_info clks;
+	struct mdss_dsi_link_hs_clk_info hs_clks;
+	struct mdss_dsi_link_lp_clk_info lp_clks;
 	u32 current_clk_state;
-	u32 byte_clk_rate;
-	u32 pix_clk_rate;
-	u32 esc_clk_rate;
 };
 
 struct mdss_dsi_clk_mngr {
@@ -73,28 +71,27 @@
 
 	rc = clk_prepare_enable(c_clks->clks.mdp_core_clk);
 	if (rc) {
-		pr_err("%s: failed to enable mdp_core_clock. rc=%d\n",
-							 __func__, rc);
+		pr_err("failed to enable mdp_core_clock. rc=%d\n", rc);
 		goto error;
 	}
 
 	rc = clk_prepare_enable(c_clks->clks.ahb_clk);
 	if (rc) {
-		pr_err("%s: failed to enable ahb clock. rc=%d\n", __func__, rc);
+		pr_err("failed to enable ahb clock. rc=%d\n", rc);
 		goto disable_core_clk;
 	}
 
 	rc = clk_prepare_enable(c_clks->clks.axi_clk);
 	if (rc) {
-		pr_err("%s: failed to enable ahb clock. rc=%d\n", __func__, rc);
+		pr_err("failed to enable ahb clock. rc=%d\n", rc);
 		goto disable_ahb_clk;
 	}
 
 	if (c_clks->clks.mmss_misc_ahb_clk) {
 		rc = clk_prepare_enable(c_clks->clks.mmss_misc_ahb_clk);
 		if (rc) {
-			pr_err("%s: failed to enable mmss misc ahb clk.rc=%d\n",
-				__func__, rc);
+			pr_err("failed to enable mmss misc ahb clk.rc=%d\n",
+				rc);
 			goto disable_axi_clk;
 		}
 	}
@@ -140,12 +137,15 @@
 	return rc;
 }
 
-static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_set_rate(
+	struct mdss_dsi_link_hs_clk_info *link_hs_clks)
 {
 	int rc = 0;
 	struct mdss_dsi_clk_mngr *mngr;
+	struct dsi_link_clks *l_clks;
 	struct mdss_dsi_ctrl_pdata *ctrl;
 
+	l_clks = container_of(link_hs_clks, struct dsi_link_clks, hs_clks);
 	mngr = container_of(l_clks, struct mdss_dsi_clk_mngr, link_clks);
 
 	/*
@@ -160,19 +160,13 @@
 	if (ctrl->panel_data.panel_info.cont_splash_enabled)
 		return 0;
 
-	rc = clk_set_rate(l_clks->clks.esc_clk, l_clks->esc_clk_rate);
-	if (rc) {
-		pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
-		goto error;
-	}
-
-	rc = clk_set_rate(l_clks->clks.byte_clk, l_clks->byte_clk_rate);
+	rc = clk_set_rate(link_hs_clks->byte_clk, link_hs_clks->byte_clk_rate);
 	if (rc) {
 		pr_err("clk_set_rate failed for byte_clk rc = %d\n", rc);
 		goto error;
 	}
 
-	rc = clk_set_rate(l_clks->clks.pixel_clk, l_clks->pix_clk_rate);
+	rc = clk_set_rate(link_hs_clks->pixel_clk, link_hs_clks->pix_clk_rate);
 	if (rc) {
 		pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
 		goto error;
@@ -182,141 +176,203 @@
 	return rc;
 }
 
-static int dsi_link_clk_prepare(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_prepare(
+	struct mdss_dsi_link_hs_clk_info *link_hs_clks)
 {
 	int rc = 0;
 
-	rc = clk_prepare(l_clks->clks.esc_clk);
+	rc = clk_prepare(link_hs_clks->byte_clk);
 	if (rc) {
-		pr_err("%s: Failed to prepare dsi esc clk\n", __func__);
-		goto esc_clk_err;
-	}
-
-	rc = clk_prepare(l_clks->clks.byte_clk);
-	if (rc) {
-		pr_err("%s: Failed to prepare dsi byte clk\n", __func__);
+		pr_err("Failed to prepare dsi byte clk\n");
 		goto byte_clk_err;
 	}
 
-	rc = clk_prepare(l_clks->clks.pixel_clk);
+	rc = clk_prepare(link_hs_clks->pixel_clk);
 	if (rc) {
-		pr_err("%s: Failed to prepare dsi pixel clk\n", __func__);
+		pr_err("Failed to prepare dsi pixel_clk\n");
 		goto pixel_clk_err;
 	}
 
 	return rc;
 
 pixel_clk_err:
-	clk_unprepare(l_clks->clks.byte_clk);
+	clk_unprepare(link_hs_clks->byte_clk);
 byte_clk_err:
-	clk_unprepare(l_clks->clks.esc_clk);
-esc_clk_err:
 	return rc;
 }
 
-static int dsi_link_clk_unprepare(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_unprepare(
+	struct mdss_dsi_link_hs_clk_info *link_hs_clks)
 {
 	int rc = 0;
 
-	clk_unprepare(l_clks->clks.pixel_clk);
-	clk_unprepare(l_clks->clks.byte_clk);
-	clk_unprepare(l_clks->clks.esc_clk);
+	clk_unprepare(link_hs_clks->pixel_clk);
+	clk_unprepare(link_hs_clks->byte_clk);
 
 	return rc;
 }
 
-static int dsi_link_clk_enable(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_enable(
+	struct mdss_dsi_link_hs_clk_info *link_hs_clks)
 {
 	int rc = 0;
 
-	rc = clk_enable(l_clks->clks.esc_clk);
+	rc = clk_enable(link_hs_clks->byte_clk);
 	if (rc) {
-		pr_err("%s: Failed to enable dsi esc clk\n", __func__);
-		goto esc_clk_err;
-	}
-
-	rc = clk_enable(l_clks->clks.byte_clk);
-	if (rc) {
-		pr_err("%s: Failed to enable dsi byte clk\n", __func__);
+		pr_err("Failed to enable dsi byte clk\n");
 		goto byte_clk_err;
 	}
 
-	rc = clk_enable(l_clks->clks.pixel_clk);
+	rc = clk_enable(link_hs_clks->pixel_clk);
 	if (rc) {
-		pr_err("%s: Failed to enable dsi pixel clk\n", __func__);
+		pr_err("Failed to enable dsi pixel_clk\n");
 		goto pixel_clk_err;
 	}
 
 	return rc;
 
 pixel_clk_err:
-	clk_disable(l_clks->clks.byte_clk);
+	clk_disable(link_hs_clks->byte_clk);
 byte_clk_err:
-	clk_disable(l_clks->clks.esc_clk);
-esc_clk_err:
 	return rc;
 }
 
-static int dsi_link_clk_disable(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_disable(
+	struct mdss_dsi_link_hs_clk_info *link_hs_clks)
 {
 	int rc = 0;
 
-	clk_disable(l_clks->clks.esc_clk);
-	clk_disable(l_clks->clks.pixel_clk);
-	clk_disable(l_clks->clks.byte_clk);
+	clk_disable(link_hs_clks->pixel_clk);
+	clk_disable(link_hs_clks->byte_clk);
 
 	return rc;
 }
 
 
-static int dsi_link_clk_start(struct dsi_link_clks *l_clks)
+static int dsi_link_hs_clk_start(
+	struct mdss_dsi_link_hs_clk_info *link_hs_clks,
+	enum mdss_dsi_link_clk_op_type op_type)
 {
 	int rc = 0;
+	struct dsi_link_clks *l_clks;
 	struct mdss_dsi_clk_mngr *mngr;
 
+	l_clks = container_of(link_hs_clks, struct dsi_link_clks, hs_clks);
 	mngr = container_of(l_clks, struct mdss_dsi_clk_mngr, link_clks);
 
-	rc = dsi_link_clk_set_rate(l_clks);
-	if (rc) {
-		pr_err("failed to set clk rates, rc = %d\n", rc);
-		goto error;
+	if (op_type & MDSS_DSI_LINK_CLK_SET_RATE) {
+		rc = dsi_link_hs_clk_set_rate(link_hs_clks);
+		if (rc) {
+			pr_err("failed to set HS clk rates, rc = %d\n", rc);
+			goto error;
+		}
 	}
 
-	rc = dsi_link_clk_prepare(l_clks);
-	if (rc) {
-		pr_err("failed to prepare link clks, rc = %d\n", rc);
-		goto error;
+	if (op_type & MDSS_DSI_LINK_CLK_PREPARE) {
+		rc = dsi_link_hs_clk_prepare(link_hs_clks);
+		if (rc) {
+			pr_err("failed to prepare link HS clks, rc = %d\n", rc);
+			goto error;
+		}
 	}
 
-	rc = dsi_link_clk_enable(l_clks);
-	if (rc) {
-		pr_err("failed to enable link clks, rc = %d\n", rc);
-		goto error_unprepare;
+	if (op_type & MDSS_DSI_LINK_CLK_ENABLE) {
+		rc = dsi_link_hs_clk_enable(link_hs_clks);
+		if (rc) {
+			pr_err("failed to enable link HS clks, rc = %d\n", rc);
+			goto error_unprepare;
+		}
 	}
 
-	pr_debug("%s: LINK CLOCK IS ON\n", mngr->name);
+	pr_debug("%s: LINK HS CLOCK IS ON\n", mngr->name);
 	return rc;
 error_unprepare:
-	dsi_link_clk_unprepare(l_clks);
+	dsi_link_hs_clk_unprepare(link_hs_clks);
 error:
 	return rc;
 }
 
-static int dsi_link_clk_stop(struct dsi_link_clks *l_clks)
+static int dsi_link_lp_clk_start(
+	struct mdss_dsi_link_lp_clk_info *link_lp_clks)
 {
 	int rc = 0;
 	struct mdss_dsi_clk_mngr *mngr;
+	struct dsi_link_clks *l_clks;
+	struct mdss_dsi_ctrl_pdata *ctrl;
 
+	l_clks = container_of(link_lp_clks, struct dsi_link_clks, lp_clks);
+	mngr = container_of(l_clks, struct mdss_dsi_clk_mngr, link_clks);
+	/*
+	 * In an ideal world, cont_splash_enabled should not be required inside
+	 * the clock manager. But, in the current driver cont_splash_enabled
+	 * flag is set inside mdp driver and there is no interface event
+	 * associated with this flag setting. Also, set rate for clock need not
+	 * be called for every enable call. It should be done only once when
+	 * coming out of suspend.
+	 */
+	ctrl = mngr->priv_data;
+	if (ctrl->panel_data.panel_info.cont_splash_enabled)
+		goto prepare;
+
+	rc = clk_set_rate(link_lp_clks->esc_clk, link_lp_clks->esc_clk_rate);
+	if (rc) {
+		pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
+		goto error;
+	}
+
+prepare:
+	rc = clk_prepare(link_lp_clks->esc_clk);
+	if (rc) {
+		pr_err("Failed to prepare dsi esc clk\n");
+		goto error;
+	}
+
+	rc = clk_enable(link_lp_clks->esc_clk);
+	if (rc) {
+		pr_err("Failed to enable dsi esc clk\n");
+		clk_unprepare(l_clks->lp_clks.esc_clk);
+		goto error;
+	}
+error:
+	pr_debug("%s: LINK LP CLOCK IS ON\n", mngr->name);
+	return rc;
+}
+
+static int dsi_link_hs_clk_stop(
+	struct mdss_dsi_link_hs_clk_info *link_hs_clks)
+{
+	int rc = 0;
+	struct dsi_link_clks *l_clks;
+	struct mdss_dsi_clk_mngr *mngr;
+
+	l_clks = container_of(link_hs_clks, struct dsi_link_clks, hs_clks);
 	mngr = container_of(l_clks, struct mdss_dsi_clk_mngr, link_clks);
 
-	(void)dsi_link_clk_disable(l_clks);
+	(void)dsi_link_hs_clk_disable(link_hs_clks);
 
-	(void)dsi_link_clk_unprepare(l_clks);
-	pr_debug("%s: LINK CLOCK IS OFF\n", mngr->name);
+	(void)dsi_link_hs_clk_unprepare(link_hs_clks);
+	pr_debug("%s: LINK HS CLOCK IS OFF\n", mngr->name);
 
 	return rc;
 }
 
+static int dsi_link_lp_clk_stop(
+	struct mdss_dsi_link_lp_clk_info *link_lp_clks)
+{
+	struct dsi_link_clks *l_clks;
+	struct mdss_dsi_clk_mngr *mngr;
+
+	l_clks = container_of(link_lp_clks, struct dsi_link_clks, lp_clks);
+	mngr = container_of(l_clks, struct mdss_dsi_clk_mngr, link_clks);
+
+	clk_disable(l_clks->lp_clks.esc_clk);
+	clk_unprepare(l_clks->lp_clks.esc_clk);
+
+	pr_debug("%s: LINK LP CLOCK IS OFF\n", mngr->name);
+	return 0;
+}
+
+
 static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state,
 				struct dsi_link_clks *l_clks, u32 l_state)
 {
@@ -347,8 +403,8 @@
 	if (c_clks && (c_state == MDSS_DSI_CLK_ON)) {
 		if (c_clks->current_clk_state == MDSS_DSI_CLK_OFF) {
 			rc = mngr->pre_clkon_cb(mngr->priv_data,
-						MDSS_DSI_CORE_CLK,
-						MDSS_DSI_CLK_ON);
+				MDSS_DSI_CORE_CLK, MDSS_DSI_LINK_NONE,
+				MDSS_DSI_CLK_ON);
 			if (rc) {
 				pr_err("failed to turn on MDP FS rc= %d\n", rc);
 				goto error;
@@ -362,8 +418,8 @@
 
 		if (mngr->post_clkon_cb) {
 			rc = mngr->post_clkon_cb(mngr->priv_data,
-						 MDSS_DSI_CORE_CLK,
-						 MDSS_DSI_CLK_ON);
+				 MDSS_DSI_CORE_CLK, MDSS_DSI_LINK_NONE,
+				 MDSS_DSI_CLK_ON);
 			if (rc)
 				pr_err("post clk on cb failed, rc = %d\n", rc);
 		}
@@ -375,21 +431,50 @@
 		if (l_state == MDSS_DSI_CLK_ON) {
 			if (mngr->pre_clkon_cb) {
 				rc = mngr->pre_clkon_cb(mngr->priv_data,
-					MDSS_DSI_LINK_CLK, l_state);
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_LP_CLK,
+					l_state);
 				if (rc)
-					pr_err("pre link clk on cb failed\n");
+					pr_err("pre link LP clk on cb failed\n");
 			}
-			rc = dsi_link_clk_start(l_clks);
+			rc = dsi_link_lp_clk_start(&l_clks->lp_clks);
 			if (rc) {
-				pr_err("failed to start link clk rc= %d\n", rc);
+				pr_err("failed to start LP link clk clk\n");
 				goto error;
 			}
 			if (mngr->post_clkon_cb) {
 				rc = mngr->post_clkon_cb(mngr->priv_data,
-							MDSS_DSI_LINK_CLK,
-							l_state);
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_LP_CLK,
+					l_state);
 				if (rc)
-					pr_err("post link clk on cb failed\n");
+					pr_err("post LP clk on cb failed\n");
+			}
+
+			if (mngr->pre_clkon_cb) {
+				rc = mngr->pre_clkon_cb(mngr->priv_data,
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_HS_CLK,
+					l_state);
+				if (rc)
+					pr_err("pre HS clk on cb failed\n");
+			}
+			rc = dsi_link_hs_clk_start(&l_clks->hs_clks,
+				(MDSS_DSI_LINK_CLK_SET_RATE |
+				MDSS_DSI_LINK_CLK_PREPARE));
+			if (rc) {
+				pr_err("failed to prepare HS clk rc= %d\n", rc);
+				goto error;
+			}
+			if (mngr->post_clkon_cb) {
+				rc = mngr->post_clkon_cb(mngr->priv_data,
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_HS_CLK,
+					l_state);
+				if (rc)
+					pr_err("post HS clk on cb failed\n");
+			}
+			rc = dsi_link_hs_clk_start(&l_clks->hs_clks,
+				MDSS_DSI_LINK_CLK_ENABLE);
+			if (rc) {
+				pr_err("failed to enable HS clk rc= %d\n", rc);
+				goto error;
 			}
 		} else {
 			/*
@@ -418,9 +503,16 @@
 					goto error;
 				}
 
-				rc = dsi_link_clk_start(l_clks);
+				rc = dsi_link_lp_clk_start(&l_clks->lp_clks);
 				if (rc) {
-					pr_err("Link clks did not start\n");
+					pr_err("LP Link clks did not start\n");
+					goto error;
+				}
+
+				rc = dsi_link_hs_clk_start(&l_clks->hs_clks,
+						MDSS_DSI_LINK_CLK_START);
+				if (rc) {
+					pr_err("HS Link clks did not start\n");
 					goto error;
 				}
 				l_c_on = true;
@@ -429,24 +521,50 @@
 
 			if (mngr->pre_clkoff_cb) {
 				rc = mngr->pre_clkoff_cb(mngr->priv_data,
-					MDSS_DSI_LINK_CLK, l_state);
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_HS_CLK,
+					l_state);
 				if (rc)
-					pr_err("pre link clk off cb failed\n");
+					pr_err("pre HS clk off cb failed\n");
 			}
 
-			rc = dsi_link_clk_stop(l_clks);
+			rc = dsi_link_hs_clk_stop(&l_clks->hs_clks);
 			if (rc) {
-				pr_err("failed to stop link clk, rc = %d\n",
+				pr_err("failed to stop HS clk, rc = %d\n",
 				       rc);
 				goto error;
 			}
 
 			if (mngr->post_clkoff_cb) {
 				rc = mngr->post_clkoff_cb(mngr->priv_data,
-					MDSS_DSI_LINK_CLK, l_state);
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_HS_CLK,
+					l_state);
 				if (rc)
-					pr_err("post link clk off cb failed\n");
+					pr_err("post HS clk off cb failed\n");
 			}
+
+			if (mngr->pre_clkoff_cb) {
+				rc = mngr->pre_clkoff_cb(mngr->priv_data,
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_LP_CLK,
+					l_state);
+				if (rc)
+					pr_err("pre LP clk off cb failed\n");
+			}
+
+			rc = dsi_link_lp_clk_stop(&l_clks->lp_clks);
+			if (rc) {
+				pr_err("failed to stop LP link clk, rc = %d\n",
+				       rc);
+				goto error;
+			}
+
+			if (mngr->post_clkoff_cb) {
+				rc = mngr->post_clkoff_cb(mngr->priv_data,
+					MDSS_DSI_LINK_CLK, MDSS_DSI_LINK_LP_CLK,
+					l_state);
+				if (rc)
+					pr_err("post LP clk off cb failed\n");
+			}
+
 			/*
 			 * This check is to save unnecessary clock state
 			 * change when going from EARLY_GATE to OFF. In the
@@ -502,8 +620,8 @@
 
 		if (mngr->pre_clkoff_cb) {
 			rc = mngr->pre_clkoff_cb(mngr->priv_data,
-						 MDSS_DSI_CORE_CLK,
-						 c_state);
+				 MDSS_DSI_CORE_CLK, MDSS_DSI_LINK_NONE,
+				 c_state);
 			if (rc)
 				pr_err("pre core clk off cb failed\n");
 		}
@@ -517,7 +635,7 @@
 		if (c_state == MDSS_DSI_CLK_OFF) {
 			if (mngr->post_clkoff_cb) {
 				rc = mngr->post_clkoff_cb(mngr->priv_data,
-						MDSS_DSI_CORE_CLK,
+					MDSS_DSI_CORE_CLK, MDSS_DSI_LINK_NONE,
 						MDSS_DSI_CLK_OFF);
 				if (rc)
 					pr_err("post clkoff cb fail, rc = %d\n",
@@ -610,27 +728,30 @@
 	MDSS_XLOG(clk, rate, flags);
 	switch (clk) {
 	case MDSS_DSI_LINK_ESC_CLK:
-		mngr->link_clks.esc_clk_rate = rate;
+		mngr->link_clks.lp_clks.esc_clk_rate = rate;
 		if (!flags) {
-			rc = clk_set_rate(mngr->link_clks.clks.esc_clk, rate);
+			rc = clk_set_rate(mngr->link_clks.lp_clks.esc_clk,
+				rate);
 			if (rc)
 				pr_err("set rate failed for esc clk rc=%d\n",
 				       rc);
 		}
 		break;
 	case MDSS_DSI_LINK_BYTE_CLK:
-		mngr->link_clks.byte_clk_rate = rate;
+		mngr->link_clks.hs_clks.byte_clk_rate = rate;
 		if (!flags) {
-			rc = clk_set_rate(mngr->link_clks.clks.byte_clk, rate);
+			rc = clk_set_rate(mngr->link_clks.hs_clks.byte_clk,
+				rate);
 			if (rc)
 				pr_err("set rate failed for byte clk rc=%d\n",
 				       rc);
 		}
 		break;
 	case MDSS_DSI_LINK_PIX_CLK:
-		mngr->link_clks.pix_clk_rate = rate;
+		mngr->link_clks.hs_clks.pix_clk_rate = rate;
 		if (!flags) {
-			rc = clk_set_rate(mngr->link_clks.clks.pixel_clk, rate);
+			rc = clk_set_rate(mngr->link_clks.hs_clks.pixel_clk,
+				rate);
 			if (rc)
 				pr_err("failed to set rate for pix clk rc=%d\n",
 				       rc);
@@ -888,8 +1009,10 @@
 	mutex_init(&mngr->clk_mutex);
 	memcpy(&mngr->core_clks.clks, &info->core_clks, sizeof(struct
 						 mdss_dsi_core_clk_info));
-	memcpy(&mngr->link_clks.clks, &info->link_clks, sizeof(struct
-						 mdss_dsi_link_clk_info));
+	memcpy(&mngr->link_clks.hs_clks, &info->link_hs_clks, sizeof(struct
+						 mdss_dsi_link_hs_clk_info));
+	memcpy(&mngr->link_clks.lp_clks, &info->link_lp_clks, sizeof(struct
+						 mdss_dsi_link_lp_clk_info));
 
 	INIT_LIST_HEAD(&mngr->client_list);
 	mngr->pre_clkon_cb = info->pre_clkon_cb;
@@ -981,15 +1104,26 @@
 	if ((clk & MDSS_DSI_LINK_CLK) &&
 	    (mngr->link_clks.current_clk_state == MDSS_DSI_CLK_ON)) {
 
-		rc = dsi_link_clk_stop(&mngr->link_clks);
+		rc = dsi_link_hs_clk_stop(&mngr->link_clks.hs_clks);
 		if (rc) {
-			pr_err("failed to stop link clks\n");
+			pr_err("failed to stop HS link clks\n");
 			goto error;
 		}
 
-		rc = dsi_link_clk_start(&mngr->link_clks);
+		rc = dsi_link_lp_clk_stop(&mngr->link_clks.lp_clks);
+		if (rc) {
+			pr_err("failed to stop LP link clks\n");
+			goto error;
+		}
+
+		rc = dsi_link_lp_clk_start(&mngr->link_clks.lp_clks);
 		if (rc)
-			pr_err("failed to start link clks\n");
+			pr_err("failed to start LP link clks\n");
+
+		rc = dsi_link_hs_clk_start(&mngr->link_clks.hs_clks,
+				MDSS_DSI_LINK_CLK_START);
+		if (rc)
+			pr_err("failed to start HS link clks\n");
 
 	} else if (clk & MDSS_DSI_LINK_CLK) {
 		pr_err("cannot reset, link clock is off\n");
diff --git a/drivers/video/fbdev/msm/mdss_dsi_clk.h b/drivers/video/fbdev/msm/mdss_dsi_clk.h
index 837f2f6..5f000f9 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_clk.h
+++ b/drivers/video/fbdev/msm/mdss_dsi_clk.h
@@ -39,6 +39,13 @@
 	MDSS_DSI_LINK_CLK_MAX,
 };
 
+enum mdss_dsi_link_clk_op_type {
+	MDSS_DSI_LINK_CLK_SET_RATE = BIT(0),
+	MDSS_DSI_LINK_CLK_PREPARE = BIT(1),
+	MDSS_DSI_LINK_CLK_ENABLE = BIT(2),
+	MDSS_DSI_LINK_CLK_START = BIT(0) | BIT(1) | BIT(2),
+};
+
 enum mdss_dsi_clk_type {
 	MDSS_DSI_CORE_CLK = BIT(0),
 	MDSS_DSI_LINK_CLK = BIT(1),
@@ -46,53 +53,67 @@
 	MDSS_DSI_CLKS_MAX = BIT(2),
 };
 
+enum mdss_dsi_lclk_type {
+	MDSS_DSI_LINK_NONE = 0,
+	MDSS_DSI_LINK_LP_CLK = BIT(0),
+	MDSS_DSI_LINK_HS_CLK = BIT(1),
+};
+
 /**
  * typedef *pre_clockoff_cb() - Callback before clock is turned off
  * @priv: private data pointer.
  * @clk_type: clock which is being turned off.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
  * @new_state: next state for the clock.
  *
  * @return: error code.
  */
 typedef int (*pre_clockoff_cb)(void *priv,
-			       enum mdss_dsi_clk_type clk_type,
-			       enum mdss_dsi_clk_state new_state);
+				enum mdss_dsi_clk_type clk_type,
+				enum mdss_dsi_lclk_type l_type,
+				enum mdss_dsi_clk_state new_state);
 
 /**
  * typedef *post_clockoff_cb() - Callback after clock is turned off
  * @priv: private data pointer.
  * @clk_type: clock which was turned off.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
  * @curr_state: current state for the clock.
  *
  * @return: error code.
  */
 typedef int (*post_clockoff_cb)(void *priv,
 				enum mdss_dsi_clk_type clk_type,
+				enum mdss_dsi_lclk_type l_type,
 				enum mdss_dsi_clk_state curr_state);
 
 /**
  * typedef *post_clockon_cb() - Callback after clock is turned on
  * @priv: private data pointer.
  * @clk_type: clock which was turned on.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
  * @curr_state: current state for the clock.
  *
  * @return: error code.
  */
 typedef int (*post_clockon_cb)(void *priv,
-			       enum mdss_dsi_clk_type clk_type,
-			       enum mdss_dsi_clk_state curr_state);
+				enum mdss_dsi_clk_type clk_type,
+				enum mdss_dsi_lclk_type l_type,
+				enum mdss_dsi_clk_state curr_state);
 
 /**
  * typedef *pre_clockon_cb() - Callback before clock is turned on
  * @priv: private data pointer.
  * @clk_type: clock which is being turned on.
+ * @l_type: specifies if the clock is HS or LP type. Valid only for link clocks.
  * @new_state: next state for the clock.
  *
  * @return: error code.
  */
 typedef int (*pre_clockon_cb)(void *priv,
-			      enum mdss_dsi_clk_type clk_type,
-			      enum mdss_dsi_clk_state new_state);
+				enum mdss_dsi_clk_type clk_type,
+				enum mdss_dsi_lclk_type l_type,
+				enum mdss_dsi_clk_state new_state);
 
 struct mdss_dsi_core_clk_info {
 	struct clk *mdp_core_clk;
@@ -101,10 +122,16 @@
 	struct clk *mmss_misc_ahb_clk;
 };
 
-struct mdss_dsi_link_clk_info {
-	struct clk *esc_clk;
+struct mdss_dsi_link_hs_clk_info {
 	struct clk *byte_clk;
 	struct clk *pixel_clk;
+	u32 byte_clk_rate;
+	u32 pix_clk_rate;
+};
+
+struct mdss_dsi_link_lp_clk_info {
+	struct clk *esc_clk;
+	u32 esc_clk_rate;
 };
 
 struct dsi_panel_clk_ctrl {
@@ -126,7 +153,8 @@
 struct mdss_dsi_clk_info {
 	char name[DSI_CLK_NAME_LEN];
 	struct mdss_dsi_core_clk_info core_clks;
-	struct mdss_dsi_link_clk_info link_clks;
+	struct mdss_dsi_link_hs_clk_info link_hs_clks;
+	struct mdss_dsi_link_lp_clk_info link_lp_clks;
 	pre_clockoff_cb pre_clkoff_cb;
 	post_clockoff_cb post_clkoff_cb;
 	post_clockon_cb post_clkon_cb;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c
index 988c7a9..d76be70 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_host.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_host.c
@@ -303,6 +303,20 @@
 		 */
 		reg_val = MIPI_INP(ctrl->phy_io.base + 0x20c);
 		reg_val = reg_val >> 4;
+		if (!reg_val) {
+			/*
+			 * DSI_0_PHY_DSIPHY_REVISION_ID3 for 12nm PHY
+			 * reset value = 0x20
+			 * 7:4 Major
+			 * 3:0 Minor
+			 */
+			reg_val = MIPI_INP(ctrl->phy_io.base + 0x3dc);
+			reg_val = reg_val >> 4;
+			if (reg_val == 0x2) {
+				ctrl->shared_data->phy_rev = DSI_PHY_REV_12NM;
+				return;
+			}
+		}
 	}
 
 	if (reg_val == DSI_PHY_REV_20)
@@ -413,6 +427,9 @@
 	/* DSI_LAN_SWAP_CTRL */
 	MIPI_OUTP((ctrl_pdata->ctrl_base) + 0x00b0, ctrl_pdata->dlane_swap);
 
+	if (ctrl_pdata->shared_data->phy_rev == DSI_PHY_REV_12NM)
+		goto next;
+
 	/* clock out ctrl */
 	data = pinfo->t_clk_post & 0x3f;	/* 6 bits */
 	data <<= 8;
@@ -420,6 +437,7 @@
 	/* DSI_CLKOUT_TIMING_CTRL */
 	MIPI_OUTP((ctrl_pdata->ctrl_base) + 0xc4, data);
 
+next:
 	data = 0;
 	if (pinfo->rx_eot_ignore)
 		data |= BIT(4);
diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 1cbaa44..2ef1695 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -2309,6 +2309,9 @@
 	for (i = 0; i < ARRAY_SIZE(pt->phy_timing_8996); i++)
 		pinfo->mipi.dsi_phy_db.timing_8996[i] = pt->phy_timing_8996[i];
 
+	for (i = 0; i < ARRAY_SIZE(pt->phy_timing_12nm); i++)
+		pinfo->mipi.dsi_phy_db.timing_12nm[i] = pt->phy_timing_12nm[i];
+
 	ctrl->on_cmds = pt->on_cmds;
 	ctrl->post_panel_on_cmds = pt->post_panel_on_cmds;
 
@@ -2421,6 +2424,18 @@
 			pt->phy_timing_8996[i] = data[i];
 		phy_timings_present = true;
 	}
+
+	data = of_get_property(np,
+		"qcom,mdss-dsi-panel-timings-phy-12nm", &len);
+	if ((!data) || (len != 8)) {
+		pr_debug("%s:%d, Unable to read 12nm Phy lane timing settings",
+		       __func__, __LINE__);
+	} else {
+		for (i = 0; i < len; i++)
+			pt->phy_timing_12nm[i] = data[i];
+		phy_timings_present = true;
+	}
+
 	if (!phy_timings_present) {
 		pr_err("%s: phy timing settings not present\n", __func__);
 		return -EINVAL;
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy.h b/drivers/video/fbdev/msm/mdss_dsi_phy.h
index aea42e8..108e26e 100644
--- a/drivers/video/fbdev/msm/mdss_dsi_phy.h
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy.h
@@ -16,11 +16,13 @@
 #include <linux/types.h>
 
 #include "mdss_panel.h"
+#include "mdss_dsi.h"
 
 enum phy_rev {
 	DSI_PHY_REV_UNKNOWN = 0x00,
 	DSI_PHY_REV_10 = 0x01,	/* REV 1.0 - 20nm, 28nm */
 	DSI_PHY_REV_20 = 0x02,	/* REV 2.0 - 14nm */
+	DSI_PHY_REV_12NM = 0x03, /* 12nm PHY */
 	DSI_PHY_REV_MAX,
 };
 
@@ -36,4 +38,53 @@
 int mdss_dsi_phy_calc_timing_param(struct mdss_panel_info *pinfo, u32 phy_rev,
 		u32 frate_hz);
 
+/*
+ * mdss_dsi_12nm_phy_regulator_enable() - enable lane reg for DSI 12nm PHY
+ *
+ * @ctrl: pointer to DSI controller structure
+ */
+int mdss_dsi_12nm_phy_regulator_enable(struct mdss_dsi_ctrl_pdata *ctrl);
+
+/*
+ * mdss_dsi_12nm_phy_regulator_disable() - disable lane reg for DSI 12nm PHY
+ *
+ * @ctrl: pointer to DSI controller structure
+ */
+int mdss_dsi_12nm_phy_regulator_disable(struct mdss_dsi_ctrl_pdata *ctrl);
+
+/*
+ * mdss_dsi_12nm_phy_config() - initialization sequence for DSI 12nm PHY
+ *
+ * @ctrl: pointer to DSI controller structure
+ *
+ * This function performs a sequence of register writes to initialize DSI
+ * 12nm phy. This function assumes that the DSI bus clocks are turned on.
+ * This function should only be called prior to enabling the DSI link clocks.
+ */
+int mdss_dsi_12nm_phy_config(struct mdss_dsi_ctrl_pdata *ctrl);
+
+/*
+ * mdss_dsi_12nm_phy_shutdown() - shutdown sequence for DSI 12nm PHY
+ *
+ * @ctrl: pointer to DSI controller structure
+ *
+ * Perform a sequence of register writes to completely shut down DSI 12nm PHY.
+ * This function assumes that the DSI bus clocks are turned on.
+ */
+int mdss_dsi_12nm_phy_shutdown(struct mdss_dsi_ctrl_pdata *ctrl);
+
+/*
+ * mdss_dsi_12nm_phy_hstx_drv_ctrl() - enable/disable HSTX drivers
+ *
+ * @ctrl: pointer to DSI controller structure
+ * @enable: boolean to specify enable/disable the HSTX drivers
+ *
+ * Perform a sequence of register writes to enable/disable HSTX drivers.
+ * This function assumes that the DSI bus clocks are turned on.
+ */
+
+void mdss_dsi_12nm_phy_hstx_drv_ctrl(
+	struct mdss_dsi_ctrl_pdata *ctrl, bool enable);
+
+
 #endif /* MDSS_DSI_PHY_H */
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
new file mode 100644
index 0000000..7b9a536
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy_12nm.c
@@ -0,0 +1,124 @@
+/* Copyright (c) 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.
+ *
+ */
+
+#include <linux/iopoll.h>
+#include "mdss_dsi_phy.h"
+
+#define T_TA_GO_TIM_COUNT                    0x014
+#define T_TA_SURE_TIM_COUNT                  0x018
+#define HSTX_DRIV_INDATA_CTRL_CLKLANE        0x0c0
+#define HSTX_DATAREV_CTRL_CLKLANE            0x0d4
+#define HSTX_DRIV_INDATA_CTRL_LANE0          0x100
+#define HSTX_READY_DLY_DATA_REV_CTRL_LANE0   0x114
+#define HSTX_DRIV_INDATA_CTRL_LANE1          0x140
+#define HSTX_READY_DLY_DATA_REV_CTRL_LANE1   0x154
+#define HSTX_CLKLANE_REQSTATE_TIM_CTRL       0x180
+#define HSTX_CLKLANE_HS0STATE_TIM_CTRL       0x188
+#define HSTX_CLKLANE_TRALSTATE_TIM_CTRL      0x18c
+#define HSTX_CLKLANE_CLKPOSTSTATE_TIM_CTRL   0x194
+#define HSTX_DATALANE_REQSTATE_TIM_CTRL      0x1c0
+#define HSTX_DATALANE_HS0STATE_TIM_CTRL      0x1c8
+#define HSTX_DATALANE_TRAILSTATE_TIM_CTRL    0x1cc
+#define HSTX_DATALANE_EXITSTATE_TIM_CTRL     0x1d0
+#define HSTX_DRIV_INDATA_CTRL_LANE2          0x200
+#define HSTX_READY_DLY_DATA_REV_CTRL_LANE2   0x214
+#define HSTX_READY_DLY_DATA_REV_CTRL_LANE3   0x254
+#define HSTX_DRIV_INDATA_CTRL_LANE3          0x240
+#define CTRL0                                0x3e8
+#define SYS_CTRL                             0x3f0
+#define REQ_DLY                              0x3fc
+
+#define DSI_PHY_W32(b, off, val) MIPI_OUTP((b) + (off), (val))
+#define DSI_PHY_R32(b, off) MIPI_INP((b) + (off))
+
+int mdss_dsi_12nm_phy_regulator_enable(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+	/* Nothing to be done for 12nm PHY */
+	return 0;
+}
+
+int mdss_dsi_12nm_phy_regulator_disable(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+	/* Nothing to be done for 12nm PHY */
+	return 0;
+}
+
+int mdss_dsi_12nm_phy_config(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+	struct mdss_dsi_phy_ctrl *pd =
+		&(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
+
+	/* CTRL0: CFG_CLK_EN */
+	DSI_PHY_W32(ctrl->phy_io.base, CTRL0, BIT(0));
+
+	/* DSI PHY clock lane timings */
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_CLKLANE_HS0STATE_TIM_CTRL,
+		(pd->timing_12nm[0] | BIT(7)));
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_CLKLANE_TRALSTATE_TIM_CTRL,
+		(pd->timing_12nm[1] | BIT(6)));
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_CLKLANE_CLKPOSTSTATE_TIM_CTRL,
+		(pd->timing_12nm[2] | BIT(6)));
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_CLKLANE_REQSTATE_TIM_CTRL,
+		pd->timing_12nm[3]);
+
+	/* DSI PHY data lane timings */
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DATALANE_HS0STATE_TIM_CTRL,
+		(pd->timing_12nm[4] | BIT(7)));
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DATALANE_TRAILSTATE_TIM_CTRL,
+		(pd->timing_12nm[5] | BIT(6)));
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DATALANE_REQSTATE_TIM_CTRL,
+		pd->timing_12nm[6]);
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DATALANE_EXITSTATE_TIM_CTRL,
+		(pd->timing_12nm[7] | BIT(6) | BIT(7)));
+
+	DSI_PHY_W32(ctrl->phy_io.base, T_TA_GO_TIM_COUNT, 0x03);
+	DSI_PHY_W32(ctrl->phy_io.base, T_TA_SURE_TIM_COUNT, 0x01);
+	DSI_PHY_W32(ctrl->phy_io.base, REQ_DLY, 0x85);
+
+	/* DSI lane control registers */
+	DSI_PHY_W32(ctrl->phy_io.base,
+	HSTX_READY_DLY_DATA_REV_CTRL_LANE0, 0x00);
+	DSI_PHY_W32(ctrl->phy_io.base,
+		HSTX_READY_DLY_DATA_REV_CTRL_LANE1, 0x00);
+	DSI_PHY_W32(ctrl->phy_io.base,
+		HSTX_READY_DLY_DATA_REV_CTRL_LANE2, 0x00);
+	DSI_PHY_W32(ctrl->phy_io.base,
+		HSTX_READY_DLY_DATA_REV_CTRL_LANE3, 0x00);
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DATAREV_CTRL_CLKLANE, 0x00);
+	wmb(); /* make sure DSI PHY registers are programmed */
+
+	return 0;
+}
+
+int mdss_dsi_12nm_phy_shutdown(struct mdss_dsi_ctrl_pdata *ctrl)
+{
+	DSI_PHY_W32(ctrl->phy_io.base, SYS_CTRL, BIT(0) | BIT(3));
+	wmb(); /* make sure DSI PHY is disabled */
+	return 0;
+}
+
+void mdss_dsi_12nm_phy_hstx_drv_ctrl(
+	struct mdss_dsi_ctrl_pdata *ctrl, bool enable)
+{
+	u32 data = 0;
+
+	if (enable)
+		data = BIT(2) | BIT(3);
+
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DRIV_INDATA_CTRL_CLKLANE, data);
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DRIV_INDATA_CTRL_LANE0, data);
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DRIV_INDATA_CTRL_LANE1, data);
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DRIV_INDATA_CTRL_LANE2, data);
+	DSI_PHY_W32(ctrl->phy_io.base, HSTX_DRIV_INDATA_CTRL_LANE3, data);
+	wmb(); /* make sure DSI PHY registers are programmed */
+}
diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h
index 53db752..a3f9349 100644
--- a/drivers/video/fbdev/msm/mdss_panel.h
+++ b/drivers/video/fbdev/msm/mdss_panel.h
@@ -308,6 +308,7 @@
 	bool reg_ldo_mode;
 
 	char timing_8996[40];/* 8996, 8 * 5 */
+	char timing_12nm[14]; /* 12nm PHY */
 	char regulator_len;
 	char strength_len;
 	char lanecfg_len;
diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
index ec1ee60..f0e46f7 100644
--- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c
+++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c
@@ -519,7 +519,8 @@
 	 * is only done from the clock master. This will ensure that the PLL is
 	 * off when PHY reset is called.
 	 */
-	if (mdss_dsi_is_ctrl_clk_slave(ctrl))
+	if (mdss_dsi_is_ctrl_clk_slave(ctrl) ||
+		(ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM))
 		return;
 
 	mdss_dsi_phy_sw_reset_sub(ctrl);
@@ -561,6 +562,9 @@
 	if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20)
 		return;
 
+	if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM)
+		return;
+
 	MIPI_OUTP(ctrl->phy_regulator_io.base + 0x018, 0x000);
 }
 
@@ -575,6 +579,8 @@
 		MIPI_OUTP(ctrl->phy_io.base + DSIPHY_PLL_CLKBUFLR_EN, 0);
 		MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_GLBL_TEST_CTRL, 0);
 		MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0);
+	} else if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) {
+		mdss_dsi_12nm_phy_shutdown(ctrl);
 	} else {
 		MIPI_OUTP(ctrl->phy_io.base + MDSS_DSI_DSIPHY_CTRL_0, 0x000);
 	}
@@ -596,7 +602,8 @@
 		return;
 	}
 
-	if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20)
+	if ((ctrl->shared_data->phy_rev == DSI_PHY_REV_20) ||
+		(ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM))
 		return;
 
 	pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db);
@@ -1144,6 +1151,8 @@
 	if (enable) {
 		if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20) {
 			mdss_dsi_8996_phy_regulator_enable(ctrl);
+		} else if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) {
+			mdss_dsi_12nm_phy_regulator_enable(ctrl);
 		} else {
 			switch (ctrl->shared_data->hw_rev) {
 			case MDSS_DSI_HW_REV_103:
@@ -1194,6 +1203,8 @@
 
 		if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20) {
 			mdss_dsi_8996_phy_config(ctrl);
+		} else if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) {
+			mdss_dsi_12nm_phy_config(ctrl);
 		} else {
 			switch (ctrl->shared_data->hw_rev) {
 			case MDSS_DSI_HW_REV_103:
@@ -1269,6 +1280,13 @@
 	}
 }
 
+static void mdss_dsi_phy_hstx_drv_ctrl(
+	struct mdss_dsi_ctrl_pdata *ctrl, bool enable)
+{
+	if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM)
+		mdss_dsi_12nm_phy_hstx_drv_ctrl(ctrl, enable);
+}
+
 void mdss_dsi_core_clk_deinit(struct device *dev, struct dsi_shared_data *sdata)
 {
 	if (sdata->mmss_misc_ahb_clk)
@@ -1688,7 +1706,7 @@
 }
 
 static bool mdss_dsi_is_ulps_req_valid(struct mdss_dsi_ctrl_pdata *ctrl,
-		int enable)
+		int enable, bool reconfig)
 {
 	struct mdss_dsi_ctrl_pdata *octrl = NULL;
 	struct mdss_panel_data *pdata = &ctrl->panel_data;
@@ -1719,11 +1737,11 @@
 	 * However, this should be allowed in following usecases:
 	 *   1. If ULPS during suspend feature is enabled, where we
 	 *      configure the lanes in ULPS after turning off the panel.
-	 *   2. When coming out of idle PC with clamps enabled, where we
-	 *      transition the controller HW state back to ULPS prior to
+	 *   2. When coming out of idle PC with ULPS enabled, where we need to
+	 *      reconfigure the controller HW state again to ULPS prior to
 	 *      disabling ULPS.
 	 */
-	if (enable && !ctrl->mmss_clamp &&
+	if (enable && !reconfig &&
 		!(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT) &&
 		!pdata->panel_info.ulps_suspend_enabled) {
 		pr_debug("%s: panel not yet initialized\n", __func__);
@@ -1752,13 +1770,14 @@
  * mdss_dsi_ulps_config() - Program DSI lanes to enter/exit ULPS mode
  * @ctrl: pointer to DSI controller structure
  * @enable: 1 to enter ULPS, 0 to exit ULPS
+ * @reconfig: boolean to specify if DSI controller is reconfigured to enter ULPS
  *
  * This function executes the necessary programming sequence to enter/exit
  * DSI Ultra-Low Power State (ULPS). This function assumes that the link and
  * core clocks are already on.
  */
 static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
-	int enable)
+	int enable, bool reconfig)
 {
 	int ret = 0;
 	struct mdss_panel_data *pdata = NULL;
@@ -1780,7 +1799,7 @@
 	pinfo = &pdata->panel_info;
 	mipi = &pinfo->mipi;
 
-	if (!mdss_dsi_is_ulps_req_valid(ctrl, enable)) {
+	if (!mdss_dsi_is_ulps_req_valid(ctrl, enable, reconfig)) {
 		pr_debug("%s: skiping ULPS config for ctrl%d, enable=%d\n",
 			__func__, ctrl->ndx, enable);
 		return 0;
@@ -1801,9 +1820,9 @@
 	if (mipi->data_lane3)
 		active_lanes |= BIT(3);
 
-	pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x,clamps=%s\n",
+	pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x,reconfig=%s\n",
 		__func__, (enable ? "on" : "off"), ctrl->ndx,
-		active_lanes, ctrl->mmss_clamp ? "enabled" : "disabled");
+		active_lanes, reconfig ? "true" : "false");
 
 	if (enable && !ctrl->ulps) {
 		/*
@@ -1816,7 +1835,7 @@
 		 * power collapse and just restoring the controller state to
 		 * ULPS with the clamps still in place.
 		 */
-		if (!ctrl->mmss_clamp) {
+		if (!reconfig) {
 			ret = mdss_dsi_wait_for_lane_idle(ctrl);
 			if (ret) {
 				pr_warn("%s: lanes not idle, skip ulps\n",
@@ -2165,6 +2184,7 @@
 
 int mdss_dsi_pre_clkoff_cb(void *priv,
 			   enum mdss_dsi_clk_type clk,
+			   enum mdss_dsi_lclk_type l_type,
 			   enum mdss_dsi_clk_state new_state)
 {
 	int rc = 0;
@@ -2173,7 +2193,14 @@
 
 	pdata = &ctrl->panel_data;
 
-	if ((clk & MDSS_DSI_LINK_CLK) && (new_state == MDSS_DSI_CLK_OFF)) {
+	if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK) &&
+		(new_state == MDSS_DSI_CLK_OFF)) {
+		/* Disable HS TX driver in DSI PHY if applicable */
+		mdss_dsi_phy_hstx_drv_ctrl(ctrl, false);
+	}
+
+	if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_LP_CLK) &&
+		(new_state == MDSS_DSI_CLK_OFF)) {
 		/*
 		 * If ULPS feature is enabled, enter ULPS first.
 		 * However, when blanking the panel, we should enter ULPS
@@ -2181,9 +2208,9 @@
 		 */
 		if (!(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT)) {
 			if (pdata->panel_info.ulps_suspend_enabled)
-				mdss_dsi_ulps_config(ctrl, 1);
+				mdss_dsi_ulps_config(ctrl, 1, false);
 		} else if (mdss_dsi_ulps_feature_enabled(pdata)) {
-			rc = mdss_dsi_ulps_config(ctrl, 1);
+			rc = mdss_dsi_ulps_config(ctrl, 1, false);
 		}
 		if (rc) {
 			pr_err("%s: failed enable ulps, rc = %d\n",
@@ -2208,7 +2235,7 @@
 			 * Make sure that controller is not in ULPS state when
 			 * the DSI link is not active.
 			 */
-			rc = mdss_dsi_ulps_config(ctrl, 0);
+			rc = mdss_dsi_ulps_config(ctrl, 0, false);
 			if (rc)
 				pr_err("%s: failed to disable ulps. rc=%d\n",
 					__func__, rc);
@@ -2220,6 +2247,7 @@
 
 int mdss_dsi_post_clkon_cb(void *priv,
 			   enum mdss_dsi_clk_type clk,
+			   enum mdss_dsi_lclk_type l_type,
 			   enum mdss_dsi_clk_state curr_state)
 {
 	int rc = 0;
@@ -2238,6 +2266,21 @@
 		if (mmss_clamp)
 			mdss_dsi_ctrl_setup(ctrl);
 
+		rc = mdss_dsi_clamp_ctrl(ctrl, 0);
+		if (rc) {
+			pr_err("%s: Failed to disable dsi clamps. rc=%d\n",
+				__func__, rc);
+			goto error;
+		}
+
+		/*
+		 * Phy setup is needed if coming out of idle
+		 * power collapse with clamps enabled.
+		 */
+		if (ctrl->phy_power_off || mmss_clamp)
+			mdss_dsi_phy_power_on(ctrl, mmss_clamp);
+	}
+	if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK)) {
 		if (ctrl->ulps) {
 			/*
 			 * ULPS Entry Request. This is needed if the lanes were
@@ -2254,37 +2297,22 @@
 			 * ULPS.
 			 */
 			ctrl->ulps = false;
-			rc = mdss_dsi_ulps_config(ctrl, 1);
+			rc = mdss_dsi_ulps_config(ctrl, 1, true);
 			if (rc) {
 				pr_err("%s: Failed to enter ULPS. rc=%d\n",
 					__func__, rc);
 				goto error;
 			}
-		}
 
-		rc = mdss_dsi_clamp_ctrl(ctrl, 0);
-		if (rc) {
-			pr_err("%s: Failed to disable dsi clamps. rc=%d\n",
-				__func__, rc);
-			goto error;
-		}
-
-		/*
-		 * Phy setup is needed if coming out of idle
-		 * power collapse with clamps enabled.
-		 */
-		if (ctrl->phy_power_off || mmss_clamp)
-			mdss_dsi_phy_power_on(ctrl, mmss_clamp);
-	}
-	if (clk & MDSS_DSI_LINK_CLK) {
-		if (ctrl->ulps) {
-			rc = mdss_dsi_ulps_config(ctrl, 0);
+			rc = mdss_dsi_ulps_config(ctrl, 0, false);
 			if (rc) {
 				pr_err("%s: failed to disable ulps, rc= %d\n",
 				       __func__, rc);
 				goto error;
 			}
 		}
+		/* Enable HS TX driver in DSI PHY if applicable */
+		mdss_dsi_phy_hstx_drv_ctrl(ctrl, true);
 	}
 error:
 	return rc;
@@ -2292,6 +2320,7 @@
 
 int mdss_dsi_post_clkoff_cb(void *priv,
 			    enum mdss_dsi_clk_type clk_type,
+			    enum mdss_dsi_lclk_type l_type,
 			    enum mdss_dsi_clk_state curr_state)
 {
 	int rc = 0;
@@ -2342,6 +2371,7 @@
 
 int mdss_dsi_pre_clkon_cb(void *priv,
 			  enum mdss_dsi_clk_type clk_type,
+			  enum mdss_dsi_lclk_type l_type,
 			  enum mdss_dsi_clk_state new_state)
 {
 	int rc = 0;