From e5fd91f1ef340da553f7a79da9540c3db711c937 Mon Sep 17 00:00:00 2001
From: André Fabian Silva Delgado <emulatorman@parabola.nu>
Date: Tue, 8 Sep 2015 01:01:14 -0300
Subject: Linux-libre 4.2-gnu

---
 drivers/gpu/drm/msm/dsi/dsi.c              |  43 +-
 drivers/gpu/drm/msm/dsi/dsi.h              |  61 ++-
 drivers/gpu/drm/msm/dsi/dsi.xml.h          | 163 +++++++-
 drivers/gpu/drm/msm/dsi/dsi_host.c         | 120 +++---
 drivers/gpu/drm/msm/dsi/dsi_manager.c      |  79 +++-
 drivers/gpu/drm/msm/dsi/dsi_phy.c          | 315 ++++++++++++--
 drivers/gpu/drm/msm/dsi/mmss_cc.xml.h      |  12 +-
 drivers/gpu/drm/msm/dsi/pll/dsi_pll.c      | 164 ++++++++
 drivers/gpu/drm/msm/dsi/pll/dsi_pll.h      |  89 ++++
 drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c | 652 +++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/dsi/sfpb.xml.h         |  12 +-
 11 files changed, 1583 insertions(+), 127 deletions(-)
 create mode 100644 drivers/gpu/drm/msm/dsi/pll/dsi_pll.c
 create mode 100644 drivers/gpu/drm/msm/dsi/pll/dsi_pll.h
 create mode 100644 drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c

(limited to 'drivers/gpu/drm/msm/dsi')

diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
index ad50b8022..1f2561e2f 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.c
+++ b/drivers/gpu/drm/msm/dsi/dsi.c
@@ -23,12 +23,47 @@ struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi)
 		msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID];
 }
 
+static int dsi_get_phy(struct msm_dsi *msm_dsi)
+{
+	struct platform_device *pdev = msm_dsi->pdev;
+	struct platform_device *phy_pdev;
+	struct device_node *phy_node;
+
+	phy_node = of_parse_phandle(pdev->dev.of_node, "qcom,dsi-phy", 0);
+	if (!phy_node) {
+		dev_err(&pdev->dev, "cannot find phy device\n");
+		return -ENXIO;
+	}
+
+	phy_pdev = of_find_device_by_node(phy_node);
+	if (phy_pdev)
+		msm_dsi->phy = platform_get_drvdata(phy_pdev);
+
+	of_node_put(phy_node);
+
+	if (!phy_pdev || !msm_dsi->phy) {
+		dev_err(&pdev->dev, "%s: phy driver is not ready\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	msm_dsi->phy_dev = get_device(&phy_pdev->dev);
+
+	return 0;
+}
+
 static void dsi_destroy(struct msm_dsi *msm_dsi)
 {
 	if (!msm_dsi)
 		return;
 
 	msm_dsi_manager_unregister(msm_dsi);
+
+	if (msm_dsi->phy_dev) {
+		put_device(msm_dsi->phy_dev);
+		msm_dsi->phy = NULL;
+		msm_dsi->phy_dev = NULL;
+	}
+
 	if (msm_dsi->host) {
 		msm_dsi_host_destroy(msm_dsi->host);
 		msm_dsi->host = NULL;
@@ -43,7 +78,6 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev)
 	int ret;
 
 	if (!pdev) {
-		dev_err(&pdev->dev, "no dsi device\n");
 		ret = -ENXIO;
 		goto fail;
 	}
@@ -63,6 +97,11 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev)
 	if (ret)
 		goto fail;
 
+	/* GET dsi PHY */
+	ret = dsi_get_phy(msm_dsi);
+	if (ret)
+		goto fail;
+
 	/* Register to dsi manager */
 	ret = msm_dsi_manager_register(msm_dsi);
 	if (ret)
@@ -142,12 +181,14 @@ static struct platform_driver dsi_driver = {
 void __init msm_dsi_register(void)
 {
 	DBG("");
+	msm_dsi_phy_driver_register();
 	platform_driver_register(&dsi_driver);
 }
 
 void __exit msm_dsi_unregister(void)
 {
 	DBG("");
+	msm_dsi_phy_driver_unregister();
 	platform_driver_unregister(&dsi_driver);
 }
 
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
index 10f54d4e3..92d697de4 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.h
@@ -14,6 +14,7 @@
 #ifndef __DSI_CONNECTOR_H__
 #define __DSI_CONNECTOR_H__
 
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 
 #include "drm_crtc.h"
@@ -38,6 +39,28 @@
 #define DSI_ENCODER_MASTER	DSI_1
 #define DSI_ENCODER_SLAVE	DSI_0
 
+enum msm_dsi_phy_type {
+	MSM_DSI_PHY_28NM_HPM,
+	MSM_DSI_PHY_28NM_LP,
+	MSM_DSI_PHY_MAX
+};
+
+#define DSI_DEV_REGULATOR_MAX	8
+
+/* Regulators for DSI devices */
+struct dsi_reg_entry {
+	char name[32];
+	int min_voltage;
+	int max_voltage;
+	int enable_load;
+	int disable_load;
+};
+
+struct dsi_reg_config {
+	int num;
+	struct dsi_reg_entry regs[DSI_DEV_REGULATOR_MAX];
+};
+
 struct msm_dsi {
 	struct drm_device *dev;
 	struct platform_device *pdev;
@@ -49,6 +72,8 @@ struct msm_dsi {
 	struct msm_dsi_phy *phy;
 	struct drm_panel *panel;
 	unsigned long panel_flags;
+
+	struct device *phy_dev;
 	bool phy_enabled;
 
 	/* the encoders we are hooked to (outside of dsi block) */
@@ -73,6 +98,29 @@ void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi);
 /* msm dsi */
 struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi);
 
+/* dsi pll */
+struct msm_dsi_pll;
+#ifdef CONFIG_DRM_MSM_DSI_PLL
+struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
+			enum msm_dsi_phy_type type, int dsi_id);
+void msm_dsi_pll_destroy(struct msm_dsi_pll *pll);
+int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll,
+	struct clk **byte_clk_provider, struct clk **pixel_clk_provider);
+#else
+static inline struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
+			 enum msm_dsi_phy_type type, int id) {
+	return ERR_PTR(-ENODEV);
+}
+static inline void msm_dsi_pll_destroy(struct msm_dsi_pll *pll)
+{
+}
+static inline int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll,
+	struct clk **byte_clk_provider, struct clk **pixel_clk_provider)
+{
+	return -ENODEV;
+}
+#endif
+
 /* dsi host */
 int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host,
 					const struct mipi_dsi_msg *msg);
@@ -94,6 +142,8 @@ struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host,
 					unsigned long *panel_flags);
 int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer);
 void msm_dsi_host_unregister(struct mipi_dsi_host *host);
+int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
+			struct msm_dsi_pll *src_pll);
 void msm_dsi_host_destroy(struct mipi_dsi_host *host);
 int msm_dsi_host_modeset_init(struct mipi_dsi_host *host,
 					struct drm_device *dev);
@@ -101,17 +151,14 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi);
 
 /* dsi phy */
 struct msm_dsi_phy;
-enum msm_dsi_phy_type {
-	MSM_DSI_PHY_UNKNOWN,
-	MSM_DSI_PHY_28NM,
-	MSM_DSI_PHY_MAX
-};
-struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
-			enum msm_dsi_phy_type type, int id);
+void msm_dsi_phy_driver_register(void);
+void msm_dsi_phy_driver_unregister(void);
 int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
 	const unsigned long bit_rate, const unsigned long esc_rate);
 int msm_dsi_phy_disable(struct msm_dsi_phy *phy);
 void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,
 					u32 *clk_pre, u32 *clk_post);
+struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy);
+
 #endif /* __DSI_CONNECTOR_H__ */
 
diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h
index 1dcfae265..9791ea04b 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.xml.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h
@@ -8,8 +8,17 @@ http://github.com/freedreno/envytools/
 git clone https://github.com/freedreno/envytools.git
 
 The rules-ng-ng source files this header was generated from are:
-- /usr2/hali/local/envytools/envytools/rnndb/dsi/dsi.xml             (  18681 bytes, from 2015-03-04 23:08:31)
-- /usr2/hali/local/envytools/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2015-01-28 21:43:22)
+- /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    676 bytes, from 2014-12-05 15:34:49)
+- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  20915 bytes, from 2015-03-24 22:05:22)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   2352 bytes, from 2015-04-12 15:02:42)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  35083 bytes, from 2015-04-12 15:04:03)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  22094 bytes, from 2015-05-12 12:45:23)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1686 bytes, from 2014-10-31 16:48:57)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  29012 bytes, from 2015-05-12 12:45:23)
+- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml             (  10416 bytes, from 2015-05-12 12:45:23)
 
 Copyright (C) 2013-2015 by the following authors:
 - Rob Clark <robdclark@gmail.com> (robclark)
@@ -394,6 +403,9 @@ static inline uint32_t DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(uint32_t val)
 #define DSI_EOT_PACKET_CTRL_TX_EOT_APPEND			0x00000001
 #define DSI_EOT_PACKET_CTRL_RX_EOT_IGNORE			0x00000010
 
+#define REG_DSI_LANE_CTRL					0x000000a8
+#define DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST			0x10000000
+
 #define REG_DSI_LANE_SWAP_CTRL					0x000000ac
 #define DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL__MASK			0x00000007
 #define DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL__SHIFT			0
@@ -835,5 +847,152 @@ static inline uint32_t DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val)
 
 #define REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG			0x00000018
 
+#define REG_DSI_28nm_PHY_PLL_REFCLK_CFG				0x00000000
+#define DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR			0x00000001
+
+#define REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG			0x00000004
+
+#define REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG			0x00000008
+
+#define REG_DSI_28nm_PHY_PLL_VCOLPF_CFG				0x0000000c
+
+#define REG_DSI_28nm_PHY_PLL_VREG_CFG				0x00000010
+#define DSI_28nm_PHY_PLL_VREG_CFG_POSTDIV1_BYPASS_B		0x00000002
+
+#define REG_DSI_28nm_PHY_PLL_PWRGEN_CFG				0x00000014
+
+#define REG_DSI_28nm_PHY_PLL_DMUX_CFG				0x00000018
+
+#define REG_DSI_28nm_PHY_PLL_AMUX_CFG				0x0000001c
+
+#define REG_DSI_28nm_PHY_PLL_GLB_CFG				0x00000020
+#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B			0x00000001
+#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B		0x00000002
+#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B		0x00000004
+#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE			0x00000008
+
+#define REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG			0x00000024
+
+#define REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG			0x00000028
+
+#define REG_DSI_28nm_PHY_PLL_LPFR_CFG				0x0000002c
+
+#define REG_DSI_28nm_PHY_PLL_LPFC1_CFG				0x00000030
+
+#define REG_DSI_28nm_PHY_PLL_LPFC2_CFG				0x00000034
+
+#define REG_DSI_28nm_PHY_PLL_SDM_CFG0				0x00000038
+#define DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__MASK			0x0000003f
+#define DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__SHIFT		0
+static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(uint32_t val)
+{
+	return ((val) << DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__MASK;
+}
+#define DSI_28nm_PHY_PLL_SDM_CFG0_BYP				0x00000040
+
+#define REG_DSI_28nm_PHY_PLL_SDM_CFG1				0x0000003c
+#define DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK		0x0000003f
+#define DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__SHIFT		0
+static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(uint32_t val)
+{
+	return ((val) << DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK;
+}
+#define DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__MASK		0x00000040
+#define DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__SHIFT		6
+static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN(uint32_t val)
+{
+	return ((val) << DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__MASK;
+}
+
+#define REG_DSI_28nm_PHY_PLL_SDM_CFG2				0x00000040
+#define DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__MASK		0x000000ff
+#define DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__SHIFT		0
+static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(uint32_t val)
+{
+	return ((val) << DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__MASK;
+}
+
+#define REG_DSI_28nm_PHY_PLL_SDM_CFG3				0x00000044
+#define DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__MASK		0x000000ff
+#define DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__SHIFT		0
+static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(uint32_t val)
+{
+	return ((val) << DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__MASK;
+}
+
+#define REG_DSI_28nm_PHY_PLL_SDM_CFG4				0x00000048
+
+#define REG_DSI_28nm_PHY_PLL_SSC_CFG0				0x0000004c
+
+#define REG_DSI_28nm_PHY_PLL_SSC_CFG1				0x00000050
+
+#define REG_DSI_28nm_PHY_PLL_SSC_CFG2				0x00000054
+
+#define REG_DSI_28nm_PHY_PLL_SSC_CFG3				0x00000058
+
+#define REG_DSI_28nm_PHY_PLL_LKDET_CFG0				0x0000005c
+
+#define REG_DSI_28nm_PHY_PLL_LKDET_CFG1				0x00000060
+
+#define REG_DSI_28nm_PHY_PLL_LKDET_CFG2				0x00000064
+
+#define REG_DSI_28nm_PHY_PLL_TEST_CFG				0x00000068
+#define DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET			0x00000001
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG0				0x0000006c
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG1				0x00000070
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG2				0x00000074
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG3				0x00000078
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG4				0x0000007c
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG5				0x00000080
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG6				0x00000084
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG7				0x00000088
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG8				0x0000008c
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG9				0x00000090
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG10				0x00000094
+
+#define REG_DSI_28nm_PHY_PLL_CAL_CFG11				0x00000098
+
+#define REG_DSI_28nm_PHY_PLL_EFUSE_CFG				0x0000009c
+
+#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS_SEL			0x000000a0
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_42				0x000000a4
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_43				0x000000a8
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_44				0x000000ac
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_45				0x000000b0
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_46				0x000000b4
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_47				0x000000b8
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_48				0x000000bc
+
+#define REG_DSI_28nm_PHY_PLL_STATUS				0x000000c0
+#define DSI_28nm_PHY_PLL_STATUS_PLL_RDY				0x00000001
+
+#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS0				0x000000c4
+
+#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS1				0x000000c8
+
+#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS2				0x000000cc
+
+#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS3				0x000000d0
+
+#define REG_DSI_28nm_PHY_PLL_CTRL_54				0x000000d4
+
 
 #endif /* DSI_XML */
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index 649d20d29..de0400923 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
@@ -36,35 +37,19 @@
 
 #define DSI_6G_REG_SHIFT	4
 
-#define DSI_REGULATOR_MAX	8
-struct dsi_reg_entry {
-	char name[32];
-	int min_voltage;
-	int max_voltage;
-	int enable_load;
-	int disable_load;
-};
-
-struct dsi_reg_config {
-	int num;
-	struct dsi_reg_entry regs[DSI_REGULATOR_MAX];
-};
-
 struct dsi_config {
 	u32 major;
 	u32 minor;
 	u32 io_offset;
-	enum msm_dsi_phy_type phy_type;
 	struct dsi_reg_config reg_cfg;
 };
 
 static const struct dsi_config dsi_cfgs[] = {
-	{MSM_DSI_VER_MAJOR_V2, 0, 0, MSM_DSI_PHY_UNKNOWN},
+	{MSM_DSI_VER_MAJOR_V2, 0, 0, {0,} },
 	{ /* 8974 v1 */
 		.major = MSM_DSI_VER_MAJOR_6G,
 		.minor = MSM_DSI_6G_VER_MINOR_V1_0,
 		.io_offset = DSI_6G_REG_SHIFT,
-		.phy_type = MSM_DSI_PHY_28NM,
 		.reg_cfg = {
 			.num = 4,
 			.regs = {
@@ -79,7 +64,6 @@ static const struct dsi_config dsi_cfgs[] = {
 		.major = MSM_DSI_VER_MAJOR_6G,
 		.minor = MSM_DSI_6G_VER_MINOR_V1_1,
 		.io_offset = DSI_6G_REG_SHIFT,
-		.phy_type = MSM_DSI_PHY_28NM,
 		.reg_cfg = {
 			.num = 4,
 			.regs = {
@@ -94,7 +78,6 @@ static const struct dsi_config dsi_cfgs[] = {
 		.major = MSM_DSI_VER_MAJOR_6G,
 		.minor = MSM_DSI_6G_VER_MINOR_V1_1_1,
 		.io_offset = DSI_6G_REG_SHIFT,
-		.phy_type = MSM_DSI_PHY_28NM,
 		.reg_cfg = {
 			.num = 4,
 			.regs = {
@@ -109,7 +92,6 @@ static const struct dsi_config dsi_cfgs[] = {
 		.major = MSM_DSI_VER_MAJOR_6G,
 		.minor = MSM_DSI_6G_VER_MINOR_V1_2,
 		.io_offset = DSI_6G_REG_SHIFT,
-		.phy_type = MSM_DSI_PHY_28NM,
 		.reg_cfg = {
 			.num = 4,
 			.regs = {
@@ -124,7 +106,6 @@ static const struct dsi_config dsi_cfgs[] = {
 		.major = MSM_DSI_VER_MAJOR_6G,
 		.minor = MSM_DSI_6G_VER_MINOR_V1_3_1,
 		.io_offset = DSI_6G_REG_SHIFT,
-		.phy_type = MSM_DSI_PHY_28NM,
 		.reg_cfg = {
 			.num = 4,
 			.regs = {
@@ -197,7 +178,7 @@ struct msm_dsi_host {
 	int id;
 
 	void __iomem *ctrl_base;
-	struct regulator_bulk_data supplies[DSI_REGULATOR_MAX];
+	struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX];
 	struct clk *mdp_core_clk;
 	struct clk *ahb_clk;
 	struct clk *axi_clk;
@@ -205,6 +186,9 @@ struct msm_dsi_host {
 	struct clk *byte_clk;
 	struct clk *esc_clk;
 	struct clk *pixel_clk;
+	struct clk *byte_clk_src;
+	struct clk *pixel_clk_src;
+
 	u32 byte_clk_rate;
 
 	struct gpio_desc *disp_en_gpio;
@@ -273,7 +257,7 @@ static const struct dsi_config *dsi_get_config(struct msm_dsi_host *msm_host)
 	u32 major = 0, minor = 0;
 
 	gdsc_reg = regulator_get(&msm_host->pdev->dev, "gdsc");
-	if (IS_ERR_OR_NULL(gdsc_reg)) {
+	if (IS_ERR(gdsc_reg)) {
 		pr_err("%s: cannot get gdsc\n", __func__);
 		goto fail;
 	}
@@ -463,6 +447,22 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host)
 		goto exit;
 	}
 
+	msm_host->byte_clk_src = devm_clk_get(dev, "byte_clk_src");
+	if (IS_ERR(msm_host->byte_clk_src)) {
+		ret = PTR_ERR(msm_host->byte_clk_src);
+		pr_err("%s: can't find byte_clk_src. ret=%d\n", __func__, ret);
+		msm_host->byte_clk_src = NULL;
+		goto exit;
+	}
+
+	msm_host->pixel_clk_src = devm_clk_get(dev, "pixel_clk_src");
+	if (IS_ERR(msm_host->pixel_clk_src)) {
+		ret = PTR_ERR(msm_host->pixel_clk_src);
+		pr_err("%s: can't find pixel_clk_src. ret=%d\n", __func__, ret);
+		msm_host->pixel_clk_src = NULL;
+		goto exit;
+	}
+
 exit:
 	return ret;
 }
@@ -787,6 +787,11 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable,
 		dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL,
 			DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(LANE_SWAP_0123));
 	}
+
+	if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
+		dsi_write(msm_host, REG_DSI_LANE_CTRL,
+			DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST);
+
 	data |= DSI_CTRL_ENABLE;
 
 	dsi_write(msm_host, REG_DSI_CTRL, data);
@@ -1345,36 +1350,19 @@ static irqreturn_t dsi_host_irq(int irq, void *ptr)
 static int dsi_host_init_panel_gpios(struct msm_dsi_host *msm_host,
 			struct device *panel_device)
 {
-	int ret;
-
-	msm_host->disp_en_gpio = devm_gpiod_get(panel_device,
-						"disp-enable");
+	msm_host->disp_en_gpio = devm_gpiod_get_optional(panel_device,
+							 "disp-enable",
+							 GPIOD_OUT_LOW);
 	if (IS_ERR(msm_host->disp_en_gpio)) {
 		DBG("cannot get disp-enable-gpios %ld",
 				PTR_ERR(msm_host->disp_en_gpio));
-		msm_host->disp_en_gpio = NULL;
-	}
-	if (msm_host->disp_en_gpio) {
-		ret = gpiod_direction_output(msm_host->disp_en_gpio, 0);
-		if (ret) {
-			pr_err("cannot set dir to disp-en-gpios %d\n", ret);
-			return ret;
-		}
+		return PTR_ERR(msm_host->disp_en_gpio);
 	}
 
-	msm_host->te_gpio = devm_gpiod_get(panel_device, "disp-te");
+	msm_host->te_gpio = devm_gpiod_get(panel_device, "disp-te", GPIOD_IN);
 	if (IS_ERR(msm_host->te_gpio)) {
 		DBG("cannot get disp-te-gpios %ld", PTR_ERR(msm_host->te_gpio));
-		msm_host->te_gpio = NULL;
-	}
-
-	if (msm_host->te_gpio) {
-		ret = gpiod_direction_input(msm_host->te_gpio);
-		if (ret) {
-			pr_err("%s: cannot set dir to disp-te-gpios, %d\n",
-				__func__, ret);
-			return ret;
-		}
+		return PTR_ERR(msm_host->te_gpio);
 	}
 
 	return 0;
@@ -1508,13 +1496,6 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi)
 	msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0);
 	INIT_WORK(&msm_host->err_work, dsi_err_worker);
 
-	msm_dsi->phy = msm_dsi_phy_init(pdev, msm_host->cfg->phy_type,
-					msm_host->id);
-	if (!msm_dsi->phy) {
-		ret = -EINVAL;
-		pr_err("%s: phy init failed\n", __func__);
-		goto fail;
-	}
 	msm_dsi->host = &msm_host->base;
 	msm_dsi->id = msm_host->id;
 
@@ -1824,6 +1805,39 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 iova, u32 len)
 	wmb();
 }
 
+int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
+	struct msm_dsi_pll *src_pll)
+{
+	struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+	struct clk *byte_clk_provider, *pixel_clk_provider;
+	int ret;
+
+	ret = msm_dsi_pll_get_clk_provider(src_pll,
+				&byte_clk_provider, &pixel_clk_provider);
+	if (ret) {
+		pr_info("%s: can't get provider from pll, don't set parent\n",
+			__func__);
+		return 0;
+	}
+
+	ret = clk_set_parent(msm_host->byte_clk_src, byte_clk_provider);
+	if (ret) {
+		pr_err("%s: can't set parent to byte_clk_src. ret=%d\n",
+			__func__, ret);
+		goto exit;
+	}
+
+	ret = clk_set_parent(msm_host->pixel_clk_src, pixel_clk_provider);
+	if (ret) {
+		pr_err("%s: can't set parent to pixel_clk_src. ret=%d\n",
+			__func__, ret);
+		goto exit;
+	}
+
+exit:
+	return ret;
+}
+
 int msm_dsi_host_enable(struct mipi_dsi_host *host)
 {
 	struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
index 0a40f3c64..87ac6612b 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
@@ -60,6 +60,53 @@ static int dsi_mgr_parse_dual_panel(struct device_node *np, int id)
 	return 0;
 }
 
+static int dsi_mgr_host_register(int id)
+{
+	struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+	struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id);
+	struct msm_dsi *clk_master_dsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
+	struct msm_dsi_pll *src_pll;
+	int ret;
+
+	if (!IS_DUAL_PANEL()) {
+		ret = msm_dsi_host_register(msm_dsi->host, true);
+		if (ret)
+			return ret;
+
+		src_pll = msm_dsi_phy_get_pll(msm_dsi->phy);
+		ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll);
+	} else if (!other_dsi) {
+		ret = 0;
+	} else {
+		struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ?
+					msm_dsi : other_dsi;
+		struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ?
+					other_dsi : msm_dsi;
+		/* Register slave host first, so that slave DSI device
+		 * has a chance to probe, and do not block the master
+		 * DSI device's probe.
+		 * Also, do not check defer for the slave host,
+		 * because only master DSI device adds the panel to global
+		 * panel list. The panel's device is the master DSI device.
+		 */
+		ret = msm_dsi_host_register(sdsi->host, false);
+		if (ret)
+			return ret;
+		ret = msm_dsi_host_register(mdsi->host, true);
+		if (ret)
+			return ret;
+
+		/* PLL0 is to drive both 2 DSI link clocks in Dual DSI mode. */
+		src_pll = msm_dsi_phy_get_pll(clk_master_dsi->phy);
+		ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll);
+		if (ret)
+			return ret;
+		ret = msm_dsi_host_set_src_pll(other_dsi->host, src_pll);
+	}
+
+	return ret;
+}
+
 struct dsi_connector {
 	struct drm_connector base;
 	int id;
@@ -652,7 +699,6 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi)
 {
 	struct msm_dsi_manager *msm_dsim = &msm_dsim_glb;
 	int id = msm_dsi->id;
-	struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id);
 	int ret;
 
 	if (id > DSI_MAX) {
@@ -670,31 +716,20 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi)
 	ret = dsi_mgr_parse_dual_panel(msm_dsi->pdev->dev.of_node, id);
 	if (ret) {
 		pr_err("%s: failed to parse dual panel info\n", __func__);
-		return ret;
+		goto fail;
 	}
 
-	if (!IS_DUAL_PANEL()) {
-		ret = msm_dsi_host_register(msm_dsi->host, true);
-	} else if (!other_dsi) {
-		return 0;
-	} else {
-		struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ?
-					msm_dsi : other_dsi;
-		struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ?
-					other_dsi : msm_dsi;
-		/* Register slave host first, so that slave DSI device
-		 * has a chance to probe, and do not block the master
-		 * DSI device's probe.
-		 * Also, do not check defer for the slave host,
-		 * because only master DSI device adds the panel to global
-		 * panel list. The panel's device is the master DSI device.
-		 */
-		ret = msm_dsi_host_register(sdsi->host, false);
-		if (ret)
-			return ret;
-		ret = msm_dsi_host_register(mdsi->host, true);
+	ret = dsi_mgr_host_register(id);
+	if (ret) {
+		pr_err("%s: failed to register mipi dsi host for DSI %d\n",
+			__func__, id);
+		goto fail;
 	}
 
+	return 0;
+
+fail:
+	msm_dsim->dsi[id] = NULL;
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c
index f0cea8927..2d3b33ce1 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_phy.c
@@ -11,12 +11,27 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
 #include "dsi.h"
 #include "dsi.xml.h"
 
 #define dsi_phy_read(offset) msm_readl((offset))
 #define dsi_phy_write(offset, data) msm_writel((data), (offset))
 
+struct dsi_phy_ops {
+	int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
+		const unsigned long bit_rate, const unsigned long esc_rate);
+	int (*disable)(struct msm_dsi_phy *phy);
+};
+
+struct dsi_phy_cfg {
+	enum msm_dsi_phy_type type;
+	struct dsi_reg_config reg_cfg;
+	struct dsi_phy_ops ops;
+};
+
 struct dsi_dphy_timing {
 	u32 clk_pre;
 	u32 clk_post;
@@ -34,15 +49,106 @@ struct dsi_dphy_timing {
 };
 
 struct msm_dsi_phy {
+	struct platform_device *pdev;
 	void __iomem *base;
 	void __iomem *reg_base;
 	int id;
+
+	struct clk *ahb_clk;
+	struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX];
+
 	struct dsi_dphy_timing timing;
-	int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel,
-		const unsigned long bit_rate, const unsigned long esc_rate);
-	int (*disable)(struct msm_dsi_phy *phy);
+	const struct dsi_phy_cfg *cfg;
+
+	struct msm_dsi_pll *pll;
 };
 
+static int dsi_phy_regulator_init(struct msm_dsi_phy *phy)
+{
+	struct regulator_bulk_data *s = phy->supplies;
+	const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+	struct device *dev = &phy->pdev->dev;
+	int num = phy->cfg->reg_cfg.num;
+	int i, ret;
+
+	for (i = 0; i < num; i++)
+		s[i].supply = regs[i].name;
+
+	ret = devm_regulator_bulk_get(&phy->pdev->dev, num, s);
+	if (ret < 0) {
+		dev_err(dev, "%s: failed to init regulator, ret=%d\n",
+						__func__, ret);
+		return ret;
+	}
+
+	for (i = 0; i < num; i++) {
+		if ((regs[i].min_voltage >= 0) && (regs[i].max_voltage >= 0)) {
+			ret = regulator_set_voltage(s[i].consumer,
+				regs[i].min_voltage, regs[i].max_voltage);
+			if (ret < 0) {
+				dev_err(dev,
+					"regulator %d set voltage failed, %d\n",
+					i, ret);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void dsi_phy_regulator_disable(struct msm_dsi_phy *phy)
+{
+	struct regulator_bulk_data *s = phy->supplies;
+	const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+	int num = phy->cfg->reg_cfg.num;
+	int i;
+
+	DBG("");
+	for (i = num - 1; i >= 0; i--)
+		if (regs[i].disable_load >= 0)
+			regulator_set_load(s[i].consumer,
+						regs[i].disable_load);
+
+	regulator_bulk_disable(num, s);
+}
+
+static int dsi_phy_regulator_enable(struct msm_dsi_phy *phy)
+{
+	struct regulator_bulk_data *s = phy->supplies;
+	const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs;
+	struct device *dev = &phy->pdev->dev;
+	int num = phy->cfg->reg_cfg.num;
+	int ret, i;
+
+	DBG("");
+	for (i = 0; i < num; i++) {
+		if (regs[i].enable_load >= 0) {
+			ret = regulator_set_load(s[i].consumer,
+							regs[i].enable_load);
+			if (ret < 0) {
+				dev_err(dev,
+					"regulator %d set op mode failed, %d\n",
+					i, ret);
+				goto fail;
+			}
+		}
+	}
+
+	ret = regulator_bulk_enable(num, s);
+	if (ret < 0) {
+		dev_err(dev, "regulator enable failed, %d\n", ret);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	for (i--; i >= 0; i--)
+		regulator_set_load(s[i].consumer, regs[i].disable_load);
+	return ret;
+}
+
 #define S_DIV_ROUND_UP(n, d)	\
 	(((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d)))
 
@@ -284,59 +390,200 @@ static int dsi_28nm_phy_disable(struct msm_dsi_phy *phy)
 	return 0;
 }
 
-#define dsi_phy_func_init(name)	\
-	do {	\
-		phy->enable = dsi_##name##_phy_enable;	\
-		phy->disable = dsi_##name##_phy_disable;	\
-	} while (0)
+static int dsi_phy_enable_resource(struct msm_dsi_phy *phy)
+{
+	int ret;
+
+	pm_runtime_get_sync(&phy->pdev->dev);
 
-struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev,
-			enum msm_dsi_phy_type type, int id)
+	ret = clk_prepare_enable(phy->ahb_clk);
+	if (ret) {
+		pr_err("%s: can't enable ahb clk, %d\n", __func__, ret);
+		pm_runtime_put_sync(&phy->pdev->dev);
+	}
+
+	return ret;
+}
+
+static void dsi_phy_disable_resource(struct msm_dsi_phy *phy)
+{
+	clk_disable_unprepare(phy->ahb_clk);
+	pm_runtime_put_sync(&phy->pdev->dev);
+}
+
+static const struct dsi_phy_cfg dsi_phy_cfgs[MSM_DSI_PHY_MAX] = {
+	[MSM_DSI_PHY_28NM_HPM] = {
+		.type = MSM_DSI_PHY_28NM_HPM,
+		.reg_cfg = {
+			.num = 1,
+			.regs = {
+				{"vddio", 1800000, 1800000, 100000, 100},
+			},
+		},
+		.ops = {
+			.enable = dsi_28nm_phy_enable,
+			.disable = dsi_28nm_phy_disable,
+		}
+	},
+	[MSM_DSI_PHY_28NM_LP] = {
+		.type = MSM_DSI_PHY_28NM_LP,
+		.reg_cfg = {
+			.num = 1,
+			.regs = {
+				{"vddio", 1800000, 1800000, 100000, 100},
+			},
+		},
+		.ops = {
+			.enable = dsi_28nm_phy_enable,
+			.disable = dsi_28nm_phy_disable,
+		}
+	},
+};
+
+static const struct of_device_id dsi_phy_dt_match[] = {
+	{ .compatible = "qcom,dsi-phy-28nm-hpm",
+	  .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_HPM],},
+	{ .compatible = "qcom,dsi-phy-28nm-lp",
+	  .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_LP],},
+	{}
+};
+
+static int dsi_phy_driver_probe(struct platform_device *pdev)
 {
 	struct msm_dsi_phy *phy;
+	const struct of_device_id *match;
+	int ret;
 
 	phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
 	if (!phy)
-		return NULL;
+		return -ENOMEM;
+
+	match = of_match_node(dsi_phy_dt_match, pdev->dev.of_node);
+	if (!match)
+		return -ENODEV;
+
+	phy->cfg = match->data;
+	phy->pdev = pdev;
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+				"qcom,dsi-phy-index", &phy->id);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"%s: PHY index not specified, ret=%d\n",
+			__func__, ret);
+		goto fail;
+	}
 
 	phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY");
-	if (IS_ERR_OR_NULL(phy->base)) {
-		pr_err("%s: failed to map phy base\n", __func__);
-		return NULL;
+	if (IS_ERR(phy->base)) {
+		dev_err(&pdev->dev, "%s: failed to map phy base\n", __func__);
+		ret = -ENOMEM;
+		goto fail;
 	}
 	phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG");
-	if (IS_ERR_OR_NULL(phy->reg_base)) {
-		pr_err("%s: failed to map phy regulator base\n", __func__);
-		return NULL;
+	if (IS_ERR(phy->reg_base)) {
+		dev_err(&pdev->dev,
+			"%s: failed to map phy regulator base\n", __func__);
+		ret = -ENOMEM;
+		goto fail;
 	}
 
-	switch (type) {
-	case MSM_DSI_PHY_28NM:
-		dsi_phy_func_init(28nm);
-		break;
-	default:
-		pr_err("%s: unsupported type, %d\n", __func__, type);
-		return NULL;
+	ret = dsi_phy_regulator_init(phy);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: failed to init regulator\n", __func__);
+		goto fail;
+	}
+
+	phy->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(phy->ahb_clk)) {
+		pr_err("%s: Unable to get ahb clk\n", __func__);
+		ret = PTR_ERR(phy->ahb_clk);
+		goto fail;
 	}
 
-	phy->id = id;
+	/* PLL init will call into clk_register which requires
+	 * register access, so we need to enable power and ahb clock.
+	 */
+	ret = dsi_phy_enable_resource(phy);
+	if (ret)
+		goto fail;
+
+	phy->pll = msm_dsi_pll_init(pdev, phy->cfg->type, phy->id);
+	if (!phy->pll)
+		dev_info(&pdev->dev,
+			"%s: pll init failed, need separate pll clk driver\n",
+			__func__);
+
+	dsi_phy_disable_resource(phy);
+
+	platform_set_drvdata(pdev, phy);
+
+	return 0;
 
-	return phy;
+fail:
+	return ret;
+}
+
+static int dsi_phy_driver_remove(struct platform_device *pdev)
+{
+	struct msm_dsi_phy *phy = platform_get_drvdata(pdev);
+
+	if (phy && phy->pll) {
+		msm_dsi_pll_destroy(phy->pll);
+		phy->pll = NULL;
+	}
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver dsi_phy_platform_driver = {
+	.probe      = dsi_phy_driver_probe,
+	.remove     = dsi_phy_driver_remove,
+	.driver     = {
+		.name   = "msm_dsi_phy",
+		.of_match_table = dsi_phy_dt_match,
+	},
+};
+
+void __init msm_dsi_phy_driver_register(void)
+{
+	platform_driver_register(&dsi_phy_platform_driver);
+}
+
+void __exit msm_dsi_phy_driver_unregister(void)
+{
+	platform_driver_unregister(&dsi_phy_platform_driver);
 }
 
 int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel,
 	const unsigned long bit_rate, const unsigned long esc_rate)
 {
-	if (!phy || !phy->enable)
+	int ret;
+
+	if (!phy || !phy->cfg->ops.enable)
 		return -EINVAL;
-	return phy->enable(phy, is_dual_panel, bit_rate, esc_rate);
+
+	ret = dsi_phy_regulator_enable(phy);
+	if (ret) {
+		dev_err(&phy->pdev->dev, "%s: regulator enable failed, %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	return phy->cfg->ops.enable(phy, is_dual_panel, bit_rate, esc_rate);
 }
 
 int msm_dsi_phy_disable(struct msm_dsi_phy *phy)
 {
-	if (!phy || !phy->disable)
+	if (!phy || !phy->cfg->ops.disable)
 		return -EINVAL;
-	return phy->disable(phy);
+
+	phy->cfg->ops.disable(phy);
+	dsi_phy_regulator_disable(phy);
+
+	return 0;
 }
 
 void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,
@@ -350,3 +597,11 @@ void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy,
 		*clk_post = phy->timing.clk_post;
 }
 
+struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy)
+{
+	if (!phy)
+		return NULL;
+
+	return phy->pll;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h
index 695f99d4b..728152f3e 100644
--- a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h
+++ b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h
@@ -10,15 +10,15 @@ git clone https://github.com/freedreno/envytools.git
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    676 bytes, from 2014-12-05 15:34:49)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  20908 bytes, from 2014-12-08 16:13:00)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   2357 bytes, from 2014-12-08 16:13:00)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  27208 bytes, from 2015-01-13 23:56:11)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  20915 bytes, from 2015-03-24 22:05:22)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   2352 bytes, from 2015-04-12 15:02:42)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  35083 bytes, from 2015-04-12 15:04:03)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  22094 bytes, from 2015-05-12 12:45:23)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1686 bytes, from 2014-10-31 16:48:57)
 - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  26848 bytes, from 2015-01-13 23:55:57)
-- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml             (   8253 bytes, from 2014-12-08 16:13:00)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  29012 bytes, from 2015-05-12 12:45:23)
+- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml             (  10416 bytes, from 2015-05-12 12:45:23)
 
 Copyright (C) 2013-2014 by the following authors:
 - Rob Clark <robdclark@gmail.com> (robclark)
diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c
new file mode 100644
index 000000000..509376fdd
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2012-2015, 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 "dsi_pll.h"
+
+static int dsi_pll_enable(struct msm_dsi_pll *pll)
+{
+	int i, ret = 0;
+
+	/*
+	 * Certain PLLs do not allow VCO rate update when it is on.
+	 * Keep track of their status to turn on/off after set rate success.
+	 */
+	if (unlikely(pll->pll_on))
+		return 0;
+
+	/* Try all enable sequences until one succeeds */
+	for (i = 0; i < pll->en_seq_cnt; i++) {
+		ret = pll->enable_seqs[i](pll);
+		DBG("DSI PLL %s after sequence #%d",
+			ret ? "unlocked" : "locked", i + 1);
+		if (!ret)
+			break;
+	}
+
+	if (ret) {
+		DRM_ERROR("DSI PLL failed to lock\n");
+		return ret;
+	}
+
+	pll->pll_on = true;
+
+	return 0;
+}
+
+static void dsi_pll_disable(struct msm_dsi_pll *pll)
+{
+	if (unlikely(!pll->pll_on))
+		return;
+
+	pll->disable_seq(pll);
+
+	pll->pll_on = false;
+}
+
+/*
+ * DSI PLL Helper functions
+ */
+long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw,
+		unsigned long rate, unsigned long *parent_rate)
+{
+	struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+
+	if      (rate < pll->min_rate)
+		return  pll->min_rate;
+	else if (rate > pll->max_rate)
+		return  pll->max_rate;
+	else
+		return rate;
+}
+
+int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw)
+{
+	struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+	int ret;
+
+	/*
+	 * Certain PLLs need to update the same VCO rate and registers
+	 * after resume in suspend/resume scenario.
+	 */
+	if (pll->restore_state) {
+		ret = pll->restore_state(pll);
+		if (ret)
+			goto error;
+	}
+
+	ret = dsi_pll_enable(pll);
+
+error:
+	return ret;
+}
+
+void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw)
+{
+	struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+
+	if (pll->save_state)
+		pll->save_state(pll);
+
+	dsi_pll_disable(pll);
+}
+
+void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev,
+					struct clk **clks, u32 num_clks)
+{
+	of_clk_del_provider(pdev->dev.of_node);
+
+	if (!num_clks || !clks)
+		return;
+
+	do {
+		clk_unregister(clks[--num_clks]);
+		clks[num_clks] = NULL;
+	} while (num_clks);
+}
+
+/*
+ * DSI PLL API
+ */
+int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll,
+	struct clk **byte_clk_provider, struct clk **pixel_clk_provider)
+{
+	if (pll->get_provider)
+		return pll->get_provider(pll,
+					byte_clk_provider,
+					pixel_clk_provider);
+
+	return -EINVAL;
+}
+
+void msm_dsi_pll_destroy(struct msm_dsi_pll *pll)
+{
+	if (pll->destroy)
+		pll->destroy(pll);
+}
+
+struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
+			enum msm_dsi_phy_type type, int id)
+{
+	struct device *dev = &pdev->dev;
+	struct msm_dsi_pll *pll;
+
+	switch (type) {
+	case MSM_DSI_PHY_28NM_HPM:
+	case MSM_DSI_PHY_28NM_LP:
+		pll = msm_dsi_pll_28nm_init(pdev, type, id);
+		break;
+	default:
+		pll = ERR_PTR(-ENXIO);
+		break;
+	}
+
+	if (IS_ERR(pll)) {
+		dev_err(dev, "%s: failed to init DSI PLL\n", __func__);
+		return NULL;
+	}
+
+	pll->type = type;
+
+	DBG("DSI:%d PLL registered", id);
+
+	return pll;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h
new file mode 100644
index 000000000..5a3bb241c
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2012-2015, 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 __DSI_PLL_H__
+#define __DSI_PLL_H__
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#include "dsi.h"
+
+#define NUM_DSI_CLOCKS_MAX	6
+#define MAX_DSI_PLL_EN_SEQS	10
+
+struct msm_dsi_pll {
+	enum msm_dsi_phy_type type;
+
+	struct clk_hw	clk_hw;
+	bool		pll_on;
+
+	unsigned long	min_rate;
+	unsigned long	max_rate;
+	u32		en_seq_cnt;
+
+	int (*enable_seqs[MAX_DSI_PLL_EN_SEQS])(struct msm_dsi_pll *pll);
+	void (*disable_seq)(struct msm_dsi_pll *pll);
+	int (*get_provider)(struct msm_dsi_pll *pll,
+			struct clk **byte_clk_provider,
+			struct clk **pixel_clk_provider);
+	void (*destroy)(struct msm_dsi_pll *pll);
+	void (*save_state)(struct msm_dsi_pll *pll);
+	int (*restore_state)(struct msm_dsi_pll *pll);
+};
+
+#define hw_clk_to_pll(x) container_of(x, struct msm_dsi_pll, clk_hw)
+
+static inline void pll_write(void __iomem *reg, u32 data)
+{
+	msm_writel(data, reg);
+}
+
+static inline u32 pll_read(const void __iomem *reg)
+{
+	return msm_readl(reg);
+}
+
+static inline void pll_write_udelay(void __iomem *reg, u32 data, u32 delay_us)
+{
+	pll_write(reg, data);
+	udelay(delay_us);
+}
+
+static inline void pll_write_ndelay(void __iomem *reg, u32 data, u32 delay_ns)
+{
+	pll_write((reg), data);
+	ndelay(delay_ns);
+}
+
+/*
+ * DSI PLL Helper functions
+ */
+
+/* clock callbacks */
+long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw,
+		unsigned long rate, unsigned long *parent_rate);
+int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw);
+void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw);
+/* misc */
+void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev,
+					struct clk **clks, u32 num_clks);
+
+/*
+ * Initialization for Each PLL Type
+ */
+struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev,
+					enum msm_dsi_phy_type type, int id);
+
+#endif /* __DSI_PLL_H__ */
+
diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c
new file mode 100644
index 000000000..eb8ac3097
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright (c) 2012-2015, 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/clk.h>
+#include <linux/clk-provider.h>
+
+#include "dsi_pll.h"
+#include "dsi.xml.h"
+
+/*
+ * DSI PLL 28nm - clock diagram (eg: DSI0):
+ *
+ *         dsi0analog_postdiv_clk
+ *                             |         dsi0indirect_path_div2_clk
+ *                             |          |
+ *                   +------+  |  +----+  |  |\   dsi0byte_mux
+ *  dsi0vco_clk --o--| DIV1 |--o--| /2 |--o--| \   |
+ *                |  +------+     +----+     | m|  |  +----+
+ *                |                          | u|--o--| /4 |-- dsi0pllbyte
+ *                |                          | x|     +----+
+ *                o--------------------------| /
+ *                |                          |/
+ *                |          +------+
+ *                o----------| DIV3 |------------------------- dsi0pll
+ *                           +------+
+ */
+
+#define POLL_MAX_READS			10
+#define POLL_TIMEOUT_US		50
+
+#define NUM_PROVIDED_CLKS		2
+
+#define VCO_REF_CLK_RATE		19200000
+#define VCO_MIN_RATE			350000000
+#define VCO_MAX_RATE			750000000
+
+#define DSI_BYTE_PLL_CLK		0
+#define DSI_PIXEL_PLL_CLK		1
+
+#define LPFR_LUT_SIZE			10
+struct lpfr_cfg {
+	unsigned long vco_rate;
+	u32 resistance;
+};
+
+/* Loop filter resistance: */
+static const struct lpfr_cfg lpfr_lut[LPFR_LUT_SIZE] = {
+	{ 479500000,  8 },
+	{ 480000000, 11 },
+	{ 575500000,  8 },
+	{ 576000000, 12 },
+	{ 610500000,  8 },
+	{ 659500000,  9 },
+	{ 671500000, 10 },
+	{ 672000000, 14 },
+	{ 708500000, 10 },
+	{ 750000000, 11 },
+};
+
+struct pll_28nm_cached_state {
+	unsigned long vco_rate;
+	u8 postdiv3;
+	u8 postdiv1;
+	u8 byte_mux;
+};
+
+struct dsi_pll_28nm {
+	struct msm_dsi_pll base;
+
+	int id;
+	struct platform_device *pdev;
+	void __iomem *mmio;
+
+	int vco_delay;
+
+	/* private clocks: */
+	struct clk *clks[NUM_DSI_CLOCKS_MAX];
+	u32 num_clks;
+
+	/* clock-provider: */
+	struct clk *provided_clks[NUM_PROVIDED_CLKS];
+	struct clk_onecell_data clk_data;
+
+	struct pll_28nm_cached_state cached_state;
+};
+
+#define to_pll_28nm(x)	container_of(x, struct dsi_pll_28nm, base)
+
+static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm,
+				u32 nb_tries, u32 timeout_us)
+{
+	bool pll_locked = false;
+	u32 val;
+
+	while (nb_tries--) {
+		val = pll_read(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_STATUS);
+		pll_locked = !!(val & DSI_28nm_PHY_PLL_STATUS_PLL_RDY);
+
+		if (pll_locked)
+			break;
+
+		udelay(timeout_us);
+	}
+	DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* ");
+
+	return pll_locked;
+}
+
+static void pll_28nm_software_reset(struct dsi_pll_28nm *pll_28nm)
+{
+	void __iomem *base = pll_28nm->mmio;
+
+	/*
+	 * Add HW recommended delays after toggling the software
+	 * reset bit off and back on.
+	 */
+	pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG,
+			DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET, 1);
+	pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG, 0x00, 1);
+}
+
+/*
+ * Clock Callbacks
+ */
+static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long parent_rate)
+{
+	struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+	struct device *dev = &pll_28nm->pdev->dev;
+	void __iomem *base = pll_28nm->mmio;
+	unsigned long div_fbx1000, gen_vco_clk;
+	u32 refclk_cfg, frac_n_mode, frac_n_value;
+	u32 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3;
+	u32 cal_cfg10, cal_cfg11;
+	u32 rem;
+	int i;
+
+	VERB("rate=%lu, parent's=%lu", rate, parent_rate);
+
+	/* Force postdiv2 to be div-4 */
+	pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG, 3);
+
+	/* Configure the Loop filter resistance */
+	for (i = 0; i < LPFR_LUT_SIZE; i++)
+		if (rate <= lpfr_lut[i].vco_rate)
+			break;
+	if (i == LPFR_LUT_SIZE) {
+		dev_err(dev, "unable to get loop filter resistance. vco=%lu\n",
+				rate);
+		return -EINVAL;
+	}
+	pll_write(base + REG_DSI_28nm_PHY_PLL_LPFR_CFG, lpfr_lut[i].resistance);
+
+	/* Loop filter capacitance values : c1 and c2 */
+	pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC1_CFG, 0x70);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC2_CFG, 0x15);
+
+	rem = rate % VCO_REF_CLK_RATE;
+	if (rem) {
+		refclk_cfg = DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR;
+		frac_n_mode = 1;
+		div_fbx1000 = rate / (VCO_REF_CLK_RATE / 500);
+		gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 500);
+	} else {
+		refclk_cfg = 0x0;
+		frac_n_mode = 0;
+		div_fbx1000 = rate / (VCO_REF_CLK_RATE / 1000);
+		gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 1000);
+	}
+
+	DBG("refclk_cfg = %d", refclk_cfg);
+
+	rem = div_fbx1000 % 1000;
+	frac_n_value = (rem << 16) / 1000;
+
+	DBG("div_fb = %lu", div_fbx1000);
+	DBG("frac_n_value = %d", frac_n_value);
+
+	DBG("Generated VCO Clock: %lu", gen_vco_clk);
+	rem = 0;
+	sdm_cfg1 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1);
+	sdm_cfg1 &= ~DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK;
+	if (frac_n_mode) {
+		sdm_cfg0 = 0x0;
+		sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(0);
+		sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(
+				(u32)(((div_fbx1000 / 1000) & 0x3f) - 1));
+		sdm_cfg3 = frac_n_value >> 8;
+		sdm_cfg2 = frac_n_value & 0xff;
+	} else {
+		sdm_cfg0 = DSI_28nm_PHY_PLL_SDM_CFG0_BYP;
+		sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(
+				(u32)(((div_fbx1000 / 1000) & 0x3f) - 1));
+		sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(0);
+		sdm_cfg2 = 0;
+		sdm_cfg3 = 0;
+	}
+
+	DBG("sdm_cfg0=%d", sdm_cfg0);
+	DBG("sdm_cfg1=%d", sdm_cfg1);
+	DBG("sdm_cfg2=%d", sdm_cfg2);
+	DBG("sdm_cfg3=%d", sdm_cfg3);
+
+	cal_cfg11 = (u32)(gen_vco_clk / (256 * 1000000));
+	cal_cfg10 = (u32)((gen_vco_clk % (256 * 1000000)) / 1000000);
+	DBG("cal_cfg10=%d, cal_cfg11=%d", cal_cfg10, cal_cfg11);
+
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG, 0x02);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG3,    0x2b);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG4,    0x06);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2,  0x0d);
+
+	pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1, sdm_cfg1);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2,
+		DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(sdm_cfg2));
+	pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3,
+		DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(sdm_cfg3));
+	pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG4, 0x00);
+
+	/* Add hardware recommended delay for correct PLL configuration */
+	if (pll_28nm->vco_delay)
+		udelay(pll_28nm->vco_delay);
+
+	pll_write(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG, refclk_cfg);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG, 0x00);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_VCOLPF_CFG, 0x31);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0,   sdm_cfg0);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG0,   0x12);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG6,   0x30);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG7,   0x00);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG8,   0x60);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG9,   0x00);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG10,  cal_cfg10 & 0xff);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG11,  cal_cfg11 & 0xff);
+	pll_write(base + REG_DSI_28nm_PHY_PLL_EFUSE_CFG,  0x20);
+
+	return 0;
+}
+
+static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw)
+{
+	struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+
+	return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS,
+					POLL_TIMEOUT_US);
+}
+
+static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+	void __iomem *base = pll_28nm->mmio;
+	u32 sdm0, doubler, sdm_byp_div;
+	u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3;
+	u32 ref_clk = VCO_REF_CLK_RATE;
+	unsigned long vco_rate;
+
+	VERB("parent_rate=%lu", parent_rate);
+
+	/* Check to see if the ref clk doubler is enabled */
+	doubler = pll_read(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG) &
+			DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR;
+	ref_clk += (doubler * VCO_REF_CLK_RATE);
+
+	/* see if it is integer mode or sdm mode */
+	sdm0 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0);
+	if (sdm0 & DSI_28nm_PHY_PLL_SDM_CFG0_BYP) {
+		/* integer mode */
+		sdm_byp_div = FIELD(
+				pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0),
+				DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV) + 1;
+		vco_rate = ref_clk * sdm_byp_div;
+	} else {
+		/* sdm mode */
+		sdm_dc_off = FIELD(
+				pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1),
+				DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET);
+		DBG("sdm_dc_off = %d", sdm_dc_off);
+		sdm2 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2),
+				DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0);
+		sdm3 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3),
+				DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8);
+		sdm_freq_seed = (sdm3 << 8) | sdm2;
+		DBG("sdm_freq_seed = %d", sdm_freq_seed);
+
+		vco_rate = (ref_clk * (sdm_dc_off + 1)) +
+			mult_frac(ref_clk, sdm_freq_seed, BIT(16));
+		DBG("vco rate = %lu", vco_rate);
+	}
+
+	DBG("returning vco rate = %lu", vco_rate);
+
+	return vco_rate;
+}
+
+static const struct clk_ops clk_ops_dsi_pll_28nm_vco = {
+	.round_rate = msm_dsi_pll_helper_clk_round_rate,
+	.set_rate = dsi_pll_28nm_clk_set_rate,
+	.recalc_rate = dsi_pll_28nm_clk_recalc_rate,
+	.prepare = msm_dsi_pll_helper_clk_prepare,
+	.unprepare = msm_dsi_pll_helper_clk_unprepare,
+	.is_enabled = dsi_pll_28nm_clk_is_enabled,
+};
+
+/*
+ * PLL Callbacks
+ */
+static int dsi_pll_28nm_enable_seq_hpm(struct msm_dsi_pll *pll)
+{
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+	struct device *dev = &pll_28nm->pdev->dev;
+	void __iomem *base = pll_28nm->mmio;
+	u32 max_reads = 5, timeout_us = 100;
+	bool locked;
+	u32 val;
+	int i;
+
+	DBG("id=%d", pll_28nm->id);
+
+	pll_28nm_software_reset(pll_28nm);
+
+	/*
+	 * PLL power up sequence.
+	 * Add necessary delays recommended by hardware.
+	 */
+	val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
+	pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1);
+
+	val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
+	pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
+
+	val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
+	pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
+
+	val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
+	pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600);
+
+	for (i = 0; i < 2; i++) {
+		/* DSI Uniphy lock detect setting */
+		pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2,
+				0x0c, 100);
+		pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d);
+
+		/* poll for PLL ready status */
+		locked = pll_28nm_poll_for_ready(pll_28nm,
+						max_reads, timeout_us);
+		if (locked)
+			break;
+
+		pll_28nm_software_reset(pll_28nm);
+
+		/*
+		 * PLL power up sequence.
+		 * Add necessary delays recommended by hardware.
+		 */
+		val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
+		pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1);
+
+		val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
+		pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
+
+		val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
+		pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 250);
+
+		val &= ~DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
+		pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200);
+
+		val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B;
+		pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
+
+		val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
+		pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600);
+	}
+
+	if (unlikely(!locked))
+		dev_err(dev, "DSI PLL lock failed\n");
+	else
+		DBG("DSI PLL Lock success");
+
+	return locked ? 0 : -EINVAL;
+}
+
+static int dsi_pll_28nm_enable_seq_lp(struct msm_dsi_pll *pll)
+{
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+	struct device *dev = &pll_28nm->pdev->dev;
+	void __iomem *base = pll_28nm->mmio;
+	bool locked;
+	u32 max_reads = 10, timeout_us = 50;
+	u32 val;
+
+	DBG("id=%d", pll_28nm->id);
+
+	pll_28nm_software_reset(pll_28nm);
+
+	/*
+	 * PLL power up sequence.
+	 * Add necessary delays recommended by hardware.
+	 */
+	pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_CAL_CFG1, 0x34, 500);
+
+	val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B;
+	pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
+
+	val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B;
+	pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
+
+	val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B |
+		DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE;
+	pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500);
+
+	/* DSI PLL toggle lock detect setting */
+	pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x04, 500);
+	pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x05, 512);
+
+	locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us);
+
+	if (unlikely(!locked))
+		dev_err(dev, "DSI PLL lock failed\n");
+	else
+		DBG("DSI PLL lock success");
+
+	return locked ? 0 : -EINVAL;
+}
+
+static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll)
+{
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+
+	DBG("id=%d", pll_28nm->id);
+	pll_write(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_GLB_CFG, 0x00);
+}
+
+static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll)
+{
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+	struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
+	void __iomem *base = pll_28nm->mmio;
+
+	cached_state->postdiv3 =
+			pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG);
+	cached_state->postdiv1 =
+			pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG);
+	cached_state->byte_mux = pll_read(base + REG_DSI_28nm_PHY_PLL_VREG_CFG);
+	cached_state->vco_rate = __clk_get_rate(pll->clk_hw.clk);
+}
+
+static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll)
+{
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+	struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state;
+	void __iomem *base = pll_28nm->mmio;
+	int ret;
+
+	if ((cached_state->vco_rate != 0) &&
+		(cached_state->vco_rate == __clk_get_rate(pll->clk_hw.clk))) {
+		ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
+						cached_state->vco_rate, 0);
+		if (ret) {
+			dev_err(&pll_28nm->pdev->dev,
+				"restore vco rate failed. ret=%d\n", ret);
+			return ret;
+		}
+
+		pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
+				cached_state->postdiv3);
+		pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
+				cached_state->postdiv1);
+		pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG,
+				cached_state->byte_mux);
+
+		cached_state->vco_rate = 0;
+	}
+
+	return 0;
+}
+
+static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll,
+				struct clk **byte_clk_provider,
+				struct clk **pixel_clk_provider)
+{
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+
+	if (byte_clk_provider)
+		*byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK];
+	if (pixel_clk_provider)
+		*pixel_clk_provider =
+				pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK];
+
+	return 0;
+}
+
+static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll)
+{
+	struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll);
+	int i;
+
+	msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev,
+					pll_28nm->clks, pll_28nm->num_clks);
+
+	for (i = 0; i < NUM_PROVIDED_CLKS; i++)
+		pll_28nm->provided_clks[i] = NULL;
+
+	pll_28nm->num_clks = 0;
+	pll_28nm->clk_data.clks = NULL;
+	pll_28nm->clk_data.clk_num = 0;
+}
+
+static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm)
+{
+	char clk_name[32], parent1[32], parent2[32], vco_name[32];
+	struct clk_init_data vco_init = {
+		.parent_names = (const char *[]){ "xo" },
+		.num_parents = 1,
+		.name = vco_name,
+		.ops = &clk_ops_dsi_pll_28nm_vco,
+	};
+	struct device *dev = &pll_28nm->pdev->dev;
+	struct clk **clks = pll_28nm->clks;
+	struct clk **provided_clks = pll_28nm->provided_clks;
+	int num = 0;
+	int ret;
+
+	DBG("%d", pll_28nm->id);
+
+	snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id);
+	pll_28nm->base.clk_hw.init = &vco_init;
+	clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw);
+
+	snprintf(clk_name, 32, "dsi%danalog_postdiv_clk", pll_28nm->id);
+	snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
+	clks[num++] = clk_register_divider(dev, clk_name,
+			parent1, CLK_SET_RATE_PARENT,
+			pll_28nm->mmio +
+			REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
+			0, 4, 0, NULL);
+
+	snprintf(clk_name, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id);
+	snprintf(parent1, 32, "dsi%danalog_postdiv_clk", pll_28nm->id);
+	clks[num++] = clk_register_fixed_factor(dev, clk_name,
+			parent1, CLK_SET_RATE_PARENT,
+			1, 2);
+
+	snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id);
+	snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
+	clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] =
+			clk_register_divider(dev, clk_name,
+				parent1, 0, pll_28nm->mmio +
+				REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
+				0, 8, 0, NULL);
+
+	snprintf(clk_name, 32, "dsi%dbyte_mux", pll_28nm->id);
+	snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id);
+	snprintf(parent2, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id);
+	clks[num++] = clk_register_mux(dev, clk_name,
+			(const char *[]){
+				parent1, parent2
+			}, 2, CLK_SET_RATE_PARENT, pll_28nm->mmio +
+			REG_DSI_28nm_PHY_PLL_VREG_CFG, 1, 1, 0, NULL);
+
+	snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id);
+	snprintf(parent1, 32, "dsi%dbyte_mux", pll_28nm->id);
+	clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] =
+			clk_register_fixed_factor(dev, clk_name,
+				parent1, CLK_SET_RATE_PARENT, 1, 4);
+
+	pll_28nm->num_clks = num;
+
+	pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS;
+	pll_28nm->clk_data.clks = provided_clks;
+
+	ret = of_clk_add_provider(dev->of_node,
+			of_clk_src_onecell_get, &pll_28nm->clk_data);
+	if (ret) {
+		dev_err(dev, "failed to register clk provider: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev,
+					enum msm_dsi_phy_type type, int id)
+{
+	struct dsi_pll_28nm *pll_28nm;
+	struct msm_dsi_pll *pll;
+	int ret;
+
+	if (!pdev)
+		return ERR_PTR(-ENODEV);
+
+	pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL);
+	if (!pll_28nm)
+		return ERR_PTR(-ENOMEM);
+
+	pll_28nm->pdev = pdev;
+	pll_28nm->id = id;
+
+	pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL");
+	if (IS_ERR_OR_NULL(pll_28nm->mmio)) {
+		dev_err(&pdev->dev, "%s: failed to map pll base\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pll = &pll_28nm->base;
+	pll->min_rate = VCO_MIN_RATE;
+	pll->max_rate = VCO_MAX_RATE;
+	pll->get_provider = dsi_pll_28nm_get_provider;
+	pll->destroy = dsi_pll_28nm_destroy;
+	pll->disable_seq = dsi_pll_28nm_disable_seq;
+	pll->save_state = dsi_pll_28nm_save_state;
+	pll->restore_state = dsi_pll_28nm_restore_state;
+
+	if (type == MSM_DSI_PHY_28NM_HPM) {
+		pll_28nm->vco_delay = 1;
+
+		pll->en_seq_cnt = 3;
+		pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_hpm;
+		pll->enable_seqs[1] = dsi_pll_28nm_enable_seq_hpm;
+		pll->enable_seqs[2] = dsi_pll_28nm_enable_seq_hpm;
+	} else if (type == MSM_DSI_PHY_28NM_LP) {
+		pll_28nm->vco_delay = 1000;
+
+		pll->en_seq_cnt = 1;
+		pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_lp;
+	} else {
+		dev_err(&pdev->dev, "phy type (%d) is not 28nm\n", type);
+		return ERR_PTR(-EINVAL);
+	}
+
+	ret = pll_28nm_register(pll_28nm);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register PLL: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	return pll;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi/sfpb.xml.h b/drivers/gpu/drm/msm/dsi/sfpb.xml.h
index 50ff9851d..26f268e2d 100644
--- a/drivers/gpu/drm/msm/dsi/sfpb.xml.h
+++ b/drivers/gpu/drm/msm/dsi/sfpb.xml.h
@@ -10,15 +10,15 @@ git clone https://github.com/freedreno/envytools.git
 The rules-ng-ng source files this header was generated from are:
 - /home/robclark/src/freedreno/envytools/rnndb/msm.xml                 (    676 bytes, from 2014-12-05 15:34:49)
 - /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml (   1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  20908 bytes, from 2014-12-08 16:13:00)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   2357 bytes, from 2014-12-08 16:13:00)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  27208 bytes, from 2015-01-13 23:56:11)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  11712 bytes, from 2013-08-17 17:13:43)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml            (  20915 bytes, from 2015-03-24 22:05:22)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml      (   2352 bytes, from 2015-04-12 15:02:42)
+- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml            (  35083 bytes, from 2015-04-12 15:04:03)
+- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml             (  22094 bytes, from 2015-05-12 12:45:23)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml            (    344 bytes, from 2013-08-11 19:26:32)
 - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml         (   1686 bytes, from 2014-10-31 16:48:57)
 - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml         (    600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  26848 bytes, from 2015-01-13 23:55:57)
-- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml             (   8253 bytes, from 2014-12-08 16:13:00)
+- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml           (  29012 bytes, from 2015-05-12 12:45:23)
+- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml             (  10416 bytes, from 2015-05-12 12:45:23)
 
 Copyright (C) 2013 by the following authors:
 - Rob Clark <robdclark@gmail.com> (robclark)
-- 
cgit v1.2.3-54-g00ecf