drm: msm: dsi-staging: add support for DSI clock manager

Add support for DSI clock manager driver. This includes
refactor of the DSI power module, ULPS, PHY clamping,
DSI controller/PHY re-initialisation, and adds support
for enable/disable of DSI clocks during static screen.
The DSI clock manager maintains three state of the DSI
clocks namely ON/OFF/ECG.

CRs-Fixed: 2008002
Change-Id: I15bcf8531be6596c4a9dc52690c4971de46f387d
Signed-off-by: Padmanabhan Komanduru <pkomandu@codeaurora.org>
Signed-off-by: Shashank Babu Chinta Venkata <sbchin@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 28c01c6..5c29e67 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -95,7 +95,7 @@
 msm_drm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o
 endif
 msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
-				dsi-staging/dsi_clk_pwr.o \
+				dsi-staging/dsi_pwr.o \
 				dsi-staging/dsi_phy.o \
 				dsi-staging/dsi_phy_hw_v4_0.o \
 				dsi-staging/dsi_ctrl_hw_1_4.o \
@@ -104,6 +104,7 @@
 				dsi-staging/dsi_drm.o \
 				dsi-staging/dsi_display.o \
 				dsi-staging/dsi_panel.o \
+				dsi-staging/dsi_clk_manager.o \
 				dsi-staging/dsi_display_test.o
 
 msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index 06027a9..07503f5 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -37,10 +37,10 @@
 	ctrl->ops.kickoff_fifo_command   = dsi_ctrl_hw_14_kickoff_fifo_command;
 	ctrl->ops.reset_cmd_fifo         = dsi_ctrl_hw_14_reset_cmd_fifo;
 	ctrl->ops.trigger_command_dma    = dsi_ctrl_hw_14_trigger_command_dma;
-	ctrl->ops.ulps_request           = dsi_ctrl_hw_14_ulps_request;
-	ctrl->ops.ulps_exit              = dsi_ctrl_hw_14_ulps_exit;
-	ctrl->ops.clear_ulps_request     = dsi_ctrl_hw_14_clear_ulps_request;
-	ctrl->ops.get_lanes_in_ulps      = dsi_ctrl_hw_14_get_lanes_in_ulps;
+	ctrl->ops.ulps_ops.ulps_request           = dsi_ctrl_hw_14_ulps_request;
+	ctrl->ops.ulps_ops.ulps_exit     = dsi_ctrl_hw_14_ulps_exit;
+	ctrl->ops.ulps_ops.wait_for_lane_idle = dsi_ctrl_hw_wait_for_lane_idle;
+	ctrl->ops.ulps_ops.get_lanes_in_ulps = dsi_ctrl_hw_14_get_lanes_in_ulps;
 	ctrl->ops.clamp_enable           = dsi_ctrl_hw_14_clamp_enable;
 	ctrl->ops.clamp_disable          = dsi_ctrl_hw_14_clamp_disable;
 	ctrl->ops.get_interrupt_status   = dsi_ctrl_hw_14_get_interrupt_status;
@@ -125,6 +125,8 @@
 	phy->ops.disable = dsi_phy_hw_v4_0_disable;
 	phy->ops.calculate_timing_params =
 		dsi_phy_hw_v4_0_calculate_timing_params;
+	phy->ops.phy_idle_on = dsi_phy_hw_v4_0_idle_on;
+	phy->ops.phy_idle_off = dsi_phy_hw_v4_0_idle_off;
 }
 
 /**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index 98bd9b0..03bf905 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -57,6 +57,8 @@
 					    struct dsi_host_common_cfg *cfg,
 					   struct dsi_phy_per_lane_cfgs
 					   *timing);
+void dsi_phy_hw_v4_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+void dsi_phy_hw_v4_0_idle_off(struct dsi_phy_hw *phy);
 
 /* Definitions for 1.4 controller hardware driver */
 void dsi_ctrl_hw_14_host_setup(struct dsi_ctrl_hw *ctrl,
@@ -94,10 +96,9 @@
 			     u32 flags);
 void dsi_ctrl_hw_14_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl);
 void dsi_ctrl_hw_14_trigger_command_dma(struct dsi_ctrl_hw *ctrl);
-
+int dsi_ctrl_hw_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
 void dsi_ctrl_hw_14_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes);
 void dsi_ctrl_hw_14_ulps_exit(struct dsi_ctrl_hw *ctrl, u32 lanes);
-void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes);
 u32 dsi_ctrl_hw_14_get_lanes_in_ulps(struct dsi_ctrl_hw *ctrl);
 
 void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
new file mode 100644
index 0000000..1ffd9ac
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2016-2017, 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_CLK_H_
+#define _DSI_CLK_H_
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+
+#define MAX_STRING_LEN 32
+#define MAX_DSI_CTRL 2
+
+enum dsi_clk_state {
+	DSI_CLK_OFF,
+	DSI_CLK_ON,
+	DSI_CLK_EARLY_GATE,
+};
+
+enum clk_req_client {
+	DSI_CLK_REQ_MDP_CLIENT = 0,
+	DSI_CLK_REQ_DSI_CLIENT,
+};
+
+enum dsi_link_clk_type {
+	DSI_LINK_ESC_CLK,
+	DSI_LINK_BYTE_CLK,
+	DSI_LINK_PIX_CLK,
+	DSI_LINK_CLK_MAX,
+};
+
+enum dsi_clk_type {
+	DSI_CORE_CLK = BIT(0),
+	DSI_LINK_CLK = BIT(1),
+	DSI_ALL_CLKS = (BIT(0) | BIT(1)),
+	DSI_CLKS_MAX = BIT(2),
+};
+
+struct dsi_clk_ctrl_info {
+	enum dsi_clk_type clk_type;
+	enum dsi_clk_state clk_state;
+	enum clk_req_client client;
+};
+
+struct clk_ctrl_cb {
+	void *priv;
+	int (*dsi_clk_cb)(void *priv, struct dsi_clk_ctrl_info clk_ctrl_info);
+};
+
+/**
+ * struct dsi_core_clk_info - Core clock information for DSI hardware
+ * @mdp_core_clk:        Handle to MDP core clock.
+ * @iface_clk:           Handle to MDP interface clock.
+ * @core_mmss_clk:       Handle to MMSS core clock.
+ * @bus_clk:             Handle to bus clock.
+ */
+struct dsi_core_clk_info {
+	struct clk *mdp_core_clk;
+	struct clk *iface_clk;
+	struct clk *core_mmss_clk;
+	struct clk *bus_clk;
+};
+
+/**
+ * struct dsi_link_clk_info - Link clock information for DSI hardware.
+ * @byte_clk:        Handle to DSI byte clock.
+ * @pixel_clk:       Handle to DSI pixel clock.
+ * @esc_clk:         Handle to DSI escape clock.
+ */
+struct dsi_link_clk_info {
+	struct clk *byte_clk;
+	struct clk *pixel_clk;
+	struct clk *esc_clk;
+};
+
+/**
+ * struct link_clk_freq - Clock frequency information for Link clocks
+ * @byte_clk_rate:   Frequency of DSI byte clock in KHz.
+ * @pixel_clk_rate:  Frequency of DSI pixel clock in KHz.
+ * @esc_clk_rate:    Frequency of DSI escape clock in KHz.
+ */
+struct link_clk_freq {
+	u32 byte_clk_rate;
+	u32 pix_clk_rate;
+	u32 esc_clk_rate;
+};
+
+/**
+ * typedef *pre_clockoff_cb() - Callback before clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned off.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*pre_clockoff_cb)(void *priv,
+			       enum dsi_clk_type clk_type,
+			       enum 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.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*post_clockoff_cb)(void *priv,
+				enum dsi_clk_type clk_type,
+				enum 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.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*post_clockon_cb)(void *priv,
+			       enum dsi_clk_type clk_type,
+			       enum 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.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*pre_clockon_cb)(void *priv,
+			      enum dsi_clk_type clk_type,
+			      enum dsi_clk_state new_state);
+
+
+struct dsi_clk_info {
+	char name[MAX_STRING_LEN];
+	struct dsi_core_clk_info c_clks[MAX_DSI_CTRL];
+	struct dsi_link_clk_info l_clks[MAX_DSI_CTRL];
+	u32 bus_handle[MAX_DSI_CTRL];
+	pre_clockoff_cb pre_clkoff_cb;
+	post_clockoff_cb post_clkoff_cb;
+	post_clockon_cb post_clkon_cb;
+	pre_clockon_cb pre_clkon_cb;
+	void *priv_data;
+	u32 master_ndx;
+	u32 dsi_ctrl_count;
+};
+
+/**
+ * struct dsi_clk_link_set - Pair of clock handles to describe link clocks
+ * @byte_clk:     Handle to DSi byte clock.
+ * @pixel_clk:    Handle to DSI pixel clock.
+ */
+struct dsi_clk_link_set {
+	struct clk *byte_clk;
+	struct clk *pixel_clk;
+};
+
+/**
+ * dsi_display_clk_mgr_register() - Register DSI clock manager
+ * @info:     Structure containing DSI clock information
+ */
+void *dsi_display_clk_mngr_register(struct dsi_clk_info *info);
+
+/**
+ * dsi_display_clk_mngr_deregister() - Deregister DSI clock manager
+ * @clk_mngr:  DSI clock manager pointer
+ */
+int dsi_display_clk_mngr_deregister(void *clk_mngr);
+
+/**
+ * dsi_register_clk_handle() - Register clock handle with DSI clock manager
+ * @clk_mngr:  DSI clock manager pointer
+ * @client:     DSI clock client pointer.
+ */
+void *dsi_register_clk_handle(void *clk_mngr, char *client);
+
+/**
+ * dsi_deregister_clk_handle() - Deregister clock handle from DSI clock manager
+ * @client:     DSI clock client pointer.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_deregister_clk_handle(void *client);
+
+/**
+ * dsi_display_clk_ctrl() - set frequencies for link clks
+ * @handle:     Handle of desired DSI clock client.
+ * @clk_type:   Clock which is being controlled.
+ * @clk_state:  Desired state of clock
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_display_clk_ctrl(void *handle,
+	enum dsi_clk_type clk_type, enum dsi_clk_state clk_state);
+
+/**
+ * dsi_clk_set_link_frequencies() - set frequencies for link clks
+ * @client:     DSI clock client pointer.
+ * @freq:       Structure containing link clock frequencies.
+ * @index:      Index of the DSI controller.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq,
+					u32 index);
+
+
+/**
+ * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
+ * @client:       DSI clock client pointer.
+ * @pixel_clk: Pixel clock rate in Hz.
+ * @index:      Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index);
+
+
+/**
+ * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
+ * @client:       DSI clock client pointer.
+ * @byte_clk: Pixel clock rate in Hz.
+ * @index:      Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index);
+
+/**
+ * dsi_clk_update_parent() - update parent clocks for specified clock
+ * @parent:       link clock pair which are set as parent.
+ * @child:        link clock pair whose parent has to be set.
+ */
+int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
+			  struct dsi_clk_link_set *child);
+#endif /* _DSI_CLK_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
new file mode 100644
index 0000000..4614f08
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (c) 2016-2017, 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/of.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/msm-bus.h>
+#include "dsi_clk.h"
+
+struct dsi_core_clks {
+	struct dsi_core_clk_info clks;
+	u32 bus_handle;
+};
+
+struct dsi_link_clks {
+	struct dsi_link_clk_info clks;
+	struct link_clk_freq freq;
+};
+
+struct dsi_clk_mngr {
+	char name[MAX_STRING_LEN];
+	struct mutex clk_mutex;
+	struct list_head client_list;
+
+	u32 dsi_ctrl_count;
+	u32 master_ndx;
+	struct dsi_core_clks core_clks[MAX_DSI_CTRL];
+	struct dsi_link_clks link_clks[MAX_DSI_CTRL];
+	u32 core_clk_state;
+	u32 link_clk_state;
+
+	pre_clockoff_cb pre_clkoff_cb;
+	post_clockoff_cb post_clkoff_cb;
+	post_clockon_cb post_clkon_cb;
+	pre_clockon_cb pre_clkon_cb;
+
+	void *priv_data;
+};
+
+struct dsi_clk_client_info {
+	char name[MAX_STRING_LEN];
+	u32 core_refcount;
+	u32 link_refcount;
+	u32 core_clk_state;
+	u32 link_clk_state;
+	struct list_head list;
+	struct dsi_clk_mngr *mngr;
+};
+
+/**
+ * dsi_clk_set_link_frequencies() - set frequencies for link clks
+ * @clks:         Link clock information
+ * @pixel_clk:    pixel clock frequency in KHz.
+ * @byte_clk:     Byte clock frequency in KHz.
+ * @esc_clk:      Escape clock frequency in KHz.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq,
+				u32 index)
+{
+	int rc = 0;
+	struct dsi_clk_client_info *c = client;
+	struct dsi_clk_mngr *mngr;
+
+	if (!client) {
+		pr_err("invalid params\n");
+		return -EINVAL;
+	}
+
+	mngr = c->mngr;
+	memcpy(&mngr->link_clks[index].freq, &freq,
+		sizeof(struct link_clk_freq));
+	return rc;
+}
+
+/**
+ * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
+ * @clks:      DSI link clock information.
+ * @pixel_clk: Pixel clock rate in KHz.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index)
+{
+	int rc = 0;
+	struct dsi_clk_client_info *c = client;
+	struct dsi_clk_mngr *mngr;
+
+	mngr = c->mngr;
+	rc = clk_set_rate(mngr->link_clks[index].clks.pixel_clk, pixel_clk);
+	if (rc)
+		pr_err("failed to set clk rate for pixel clk, rc=%d\n", rc);
+	else
+		mngr->link_clks[index].freq.pix_clk_rate = pixel_clk;
+
+	return rc;
+}
+
+/**
+ * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
+ * @client:       DSI clock client pointer.
+ * @byte_clk: Pixel clock rate in Hz.
+ * @index:      Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index)
+{
+	int rc = 0;
+	struct dsi_clk_client_info *c = client;
+	struct dsi_clk_mngr *mngr;
+
+	mngr = c->mngr;
+	rc = clk_set_rate(mngr->link_clks[index].clks.byte_clk, byte_clk);
+	if (rc)
+		pr_err("failed to set clk rate for byte clk, rc=%d\n", rc);
+	else
+		mngr->link_clks[index].freq.byte_clk_rate = byte_clk;
+
+	return rc;
+
+}
+
+/**
+ * dsi_clk_update_parent() - update parent clocks for specified clock
+ * @parent:       link clock pair which are set as parent.
+ * @child:        link clock pair whose parent has to be set.
+ */
+int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
+			  struct dsi_clk_link_set *child)
+{
+	int rc = 0;
+
+	rc = clk_set_parent(child->byte_clk, parent->byte_clk);
+	if (rc) {
+		pr_err("failed to set byte clk parent\n");
+		goto error;
+	}
+
+	rc = clk_set_parent(child->pixel_clk, parent->pixel_clk);
+	if (rc) {
+		pr_err("failed to set pixel clk parent\n");
+		goto error;
+	}
+error:
+	return rc;
+}
+
+int dsi_core_clk_start(struct dsi_core_clks *c_clks)
+{
+	int rc = 0;
+
+	rc = clk_prepare_enable(c_clks->clks.mdp_core_clk);
+	if (rc) {
+		pr_err("failed to enable mdp_core_clk, rc=%d\n", rc);
+		goto error;
+	}
+
+	rc = clk_prepare_enable(c_clks->clks.iface_clk);
+	if (rc) {
+		pr_err("failed to enable iface_clk, rc=%d\n", rc);
+		goto error_disable_core_clk;
+	}
+
+	rc = clk_prepare_enable(c_clks->clks.bus_clk);
+	if (rc) {
+		pr_err("failed to enable bus_clk, rc=%d\n", rc);
+		goto error_disable_iface_clk;
+	}
+
+	rc = clk_prepare_enable(c_clks->clks.core_mmss_clk);
+	if (rc) {
+		pr_err("failed to enable core_mmss_clk, rc=%d\n", rc);
+		goto error_disable_bus_clk;
+	}
+
+	rc = msm_bus_scale_client_update_request(c_clks->bus_handle, 1);
+	if (rc) {
+		pr_err("bus scale client enable failed, rc=%d\n", rc);
+		goto error_disable_mmss_clk;
+	}
+
+	return rc;
+
+error_disable_mmss_clk:
+	clk_disable_unprepare(c_clks->clks.core_mmss_clk);
+error_disable_bus_clk:
+	clk_disable_unprepare(c_clks->clks.bus_clk);
+error_disable_iface_clk:
+	clk_disable_unprepare(c_clks->clks.iface_clk);
+error_disable_core_clk:
+	clk_disable_unprepare(c_clks->clks.mdp_core_clk);
+error:
+	return rc;
+}
+
+int dsi_core_clk_stop(struct dsi_core_clks *c_clks)
+{
+	if (msm_bus_scale_client_update_request(c_clks->bus_handle, 0))
+		pr_err("bus scale client disable failed\n");
+	clk_disable_unprepare(c_clks->clks.core_mmss_clk);
+	clk_disable_unprepare(c_clks->clks.bus_clk);
+	clk_disable_unprepare(c_clks->clks.iface_clk);
+	clk_disable_unprepare(c_clks->clks.mdp_core_clk);
+
+	return 0;
+}
+
+static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks)
+{
+	int rc = 0;
+
+	rc = clk_set_rate(l_clks->clks.esc_clk, l_clks->freq.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->freq.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->freq.pix_clk_rate);
+	if (rc) {
+		pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
+		goto error;
+	}
+error:
+	return rc;
+}
+
+static int dsi_link_clk_prepare(struct dsi_link_clks *l_clks)
+{
+	int rc = 0;
+
+	rc = clk_prepare(l_clks->clks.esc_clk);
+	if (rc) {
+		pr_err("Failed to prepare dsi esc clk, rc=%d\n", rc);
+		goto esc_clk_err;
+	}
+
+	rc = clk_prepare(l_clks->clks.byte_clk);
+	if (rc) {
+		pr_err("Failed to prepare dsi byte clk, rc=%d\n", rc);
+		goto byte_clk_err;
+	}
+
+	rc = clk_prepare(l_clks->clks.pixel_clk);
+	if (rc) {
+		pr_err("Failed to prepare dsi pixel clk, rc=%d\n", rc);
+		goto pixel_clk_err;
+	}
+
+	return rc;
+
+pixel_clk_err:
+	clk_unprepare(l_clks->clks.byte_clk);
+byte_clk_err:
+	clk_unprepare(l_clks->clks.esc_clk);
+esc_clk_err:
+	return rc;
+}
+
+static void dsi_link_clk_unprepare(struct dsi_link_clks *l_clks)
+{
+	clk_unprepare(l_clks->clks.pixel_clk);
+	clk_unprepare(l_clks->clks.byte_clk);
+	clk_unprepare(l_clks->clks.esc_clk);
+}
+
+static int dsi_link_clk_enable(struct dsi_link_clks *l_clks)
+{
+	int rc = 0;
+
+	rc = clk_enable(l_clks->clks.esc_clk);
+	if (rc) {
+		pr_err("Failed to enable dsi esc clk, rc=%d\n", rc);
+		goto esc_clk_err;
+	}
+
+	rc = clk_enable(l_clks->clks.byte_clk);
+	if (rc) {
+		pr_err("Failed to enable dsi byte clk, rc=%d\n", rc);
+		goto byte_clk_err;
+	}
+
+	rc = clk_enable(l_clks->clks.pixel_clk);
+	if (rc) {
+		pr_err("Failed to enable dsi pixel clk, rc=%d\n", rc);
+		goto pixel_clk_err;
+	}
+
+	return rc;
+
+pixel_clk_err:
+	clk_disable(l_clks->clks.byte_clk);
+byte_clk_err:
+	clk_disable(l_clks->clks.esc_clk);
+esc_clk_err:
+	return rc;
+}
+
+static void dsi_link_clk_disable(struct dsi_link_clks *l_clks)
+{
+	clk_disable(l_clks->clks.esc_clk);
+	clk_disable(l_clks->clks.pixel_clk);
+	clk_disable(l_clks->clks.byte_clk);
+}
+
+/**
+ * dsi_link_clk_start() - enable dsi link clocks
+ */
+int dsi_link_clk_start(struct dsi_link_clks *clks)
+{
+	int rc = 0;
+
+	rc = dsi_link_clk_set_rate(clks);
+	if (rc) {
+		pr_err("failed to set clk rates, rc = %d\n", rc);
+		goto error;
+	}
+
+	rc = dsi_link_clk_prepare(clks);
+	if (rc) {
+		pr_err("failed to prepare link clks, rc = %d\n", rc);
+		goto error;
+	}
+
+	rc = dsi_link_clk_enable(clks);
+	if (rc) {
+		pr_err("failed to enable link clks, rc = %d\n", rc);
+		goto error_unprepare;
+	}
+
+	pr_debug("Link clocks are enabled\n");
+	return rc;
+error_unprepare:
+	dsi_link_clk_unprepare(clks);
+error:
+	return rc;
+}
+
+/**
+ * dsi_link_clk_stop() - Stop DSI link clocks.
+ */
+int dsi_link_clk_stop(struct dsi_link_clks *clks)
+{
+	dsi_link_clk_disable(clks);
+	dsi_link_clk_unprepare(clks);
+
+	pr_debug("Link clocks disabled\n");
+
+	return 0;
+}
+
+static int dsi_display_core_clk_enable(struct dsi_core_clks *clks,
+	u32 ctrl_count, u32 master_ndx)
+{
+	int rc = 0;
+	int i;
+	struct dsi_core_clks *clk, *m_clks;
+
+	/*
+	 * In case of split DSI usecases, the clock for master controller should
+	 * be enabled before the other controller. Master controller in the
+	 * clock context refers to the controller that sources the clock.
+	 */
+
+	m_clks = &clks[master_ndx];
+
+	rc = dsi_core_clk_start(m_clks);
+	if (rc) {
+		pr_err("failed to turn on master clocks, rc=%d\n", rc);
+		goto error;
+	}
+
+	/* Turn on rest of the core clocks */
+	for (i = 0; i < ctrl_count; i++) {
+		clk = &clks[i];
+		if (!clk || (clk == m_clks))
+			continue;
+
+		rc = dsi_core_clk_start(clk);
+		if (rc) {
+			pr_err("failed to turn on clocks, rc=%d\n", rc);
+			goto error_disable_master;
+		}
+	}
+	return rc;
+error_disable_master:
+	(void)dsi_core_clk_stop(m_clks);
+error:
+	return rc;
+}
+
+static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,
+	u32 ctrl_count, u32 master_ndx)
+{
+	int rc = 0;
+	int i;
+	struct dsi_link_clks *clk, *m_clks;
+
+	/*
+	 * In case of split DSI usecases, the clock for master controller should
+	 * be enabled before the other controller. Master controller in the
+	 * clock context refers to the controller that sources the clock.
+	 */
+
+	m_clks = &clks[master_ndx];
+
+	rc = dsi_link_clk_start(m_clks);
+	if (rc) {
+		pr_err("failed to turn on master clocks, rc=%d\n", rc);
+		goto error;
+	}
+
+	/* Turn on rest of the core clocks */
+	for (i = 0; i < ctrl_count; i++) {
+		clk = &clks[i];
+		if (!clk || (clk == m_clks))
+			continue;
+
+		rc = dsi_link_clk_start(clk);
+		if (rc) {
+			pr_err("failed to turn on clocks, rc=%d\n", rc);
+			goto error_disable_master;
+		}
+	}
+	return rc;
+error_disable_master:
+	(void)dsi_link_clk_stop(m_clks);
+error:
+	return rc;
+}
+
+static int dsi_display_core_clk_disable(struct dsi_core_clks *clks,
+	u32 ctrl_count, u32 master_ndx)
+{
+	int rc = 0;
+	int i;
+	struct dsi_core_clks *clk, *m_clks;
+
+	/*
+	 * In case of split DSI usecases, clock for slave DSI controllers should
+	 * be disabled first before disabling clock for master controller. Slave
+	 * controllers in the clock context refer to controller which source
+	 * clock from another controller.
+	 */
+
+	m_clks = &clks[master_ndx];
+
+	/* Turn off non-master core clocks */
+	for (i = 0; i < ctrl_count; i++) {
+		clk = &clks[i];
+		if (!clk || (clk == m_clks))
+			continue;
+
+		rc = dsi_core_clk_stop(clk);
+		if (rc)
+			pr_err("failed to turn off clocks, rc=%d\n", rc);
+	}
+
+	rc = dsi_core_clk_stop(m_clks);
+	if (rc)
+		pr_err("failed to turn off master clocks, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int dsi_display_link_clk_disable(struct dsi_link_clks *clks,
+	u32 ctrl_count, u32 master_ndx)
+{
+	int rc = 0;
+	int i;
+	struct dsi_link_clks *clk, *m_clks;
+
+	/*
+	 * In case of split DSI usecases, clock for slave DSI controllers should
+	 * be disabled first before disabling clock for master controller. Slave
+	 * controllers in the clock context refer to controller which source
+	 * clock from another controller.
+	 */
+
+	m_clks = &clks[master_ndx];
+
+	/* Turn off non-master link clocks */
+	for (i = 0; i < ctrl_count; i++) {
+		clk = &clks[i];
+		if (!clk || (clk == m_clks))
+			continue;
+
+		rc = dsi_link_clk_stop(clk);
+		if (rc)
+			pr_err("failed to turn off clocks, rc=%d\n", rc);
+	}
+
+	rc = dsi_link_clk_stop(m_clks);
+	if (rc)
+		pr_err("failed to turn off master clocks, rc=%d\n", rc);
+
+	return rc;
+}
+
+static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state,
+				struct dsi_link_clks *l_clks, u32 l_state)
+{
+	int rc = 0;
+	struct dsi_clk_mngr *mngr;
+	bool l_c_on = false;
+
+	if (c_clks) {
+		mngr =
+		container_of(c_clks, struct dsi_clk_mngr, core_clks[0]);
+	} else if (l_clks) {
+		mngr =
+		container_of(l_clks, struct dsi_clk_mngr, link_clks[0]);
+	} else {
+		mngr = NULL;
+	}
+
+	if (!mngr)
+		return -EINVAL;
+
+	pr_debug("c_state = %d, l_state = %d\n",
+		 c_clks ? c_state : -1, l_clks ? l_state : -1);
+	/*
+	 * Below is the sequence to toggle DSI clocks:
+	 *	1. For ON sequence, Core clocks before link clocks
+	 *	2. For OFF sequence, Link clocks before core clocks.
+	 */
+	if (c_clks && (c_state == DSI_CLK_ON)) {
+		if (mngr->core_clk_state == DSI_CLK_OFF) {
+			rc = mngr->pre_clkon_cb(mngr->priv_data,
+						DSI_CORE_CLK,
+						DSI_CLK_ON);
+			if (rc) {
+				pr_err("failed to turn on MDP FS rc= %d\n", rc);
+				goto error;
+			}
+		}
+		rc = dsi_display_core_clk_enable(c_clks, mngr->dsi_ctrl_count,
+				mngr->master_ndx);
+		if (rc) {
+			pr_err("failed to turn on core clks rc = %d\n", rc);
+			goto error;
+		}
+
+		if (mngr->post_clkon_cb) {
+			rc = mngr->post_clkon_cb(mngr->priv_data,
+						 DSI_CORE_CLK,
+						 DSI_CLK_ON);
+			if (rc)
+				pr_err("post clk on cb failed, rc = %d\n", rc);
+		}
+		mngr->core_clk_state = DSI_CLK_ON;
+	}
+
+	if (l_clks) {
+		if (l_state == DSI_CLK_ON) {
+			if (mngr->pre_clkon_cb) {
+				rc = mngr->pre_clkon_cb(mngr->priv_data,
+					DSI_LINK_CLK, l_state);
+				if (rc)
+					pr_err("pre link clk on cb failed\n");
+			}
+			rc = dsi_display_link_clk_enable(l_clks,
+					mngr->dsi_ctrl_count, mngr->master_ndx);
+			if (rc) {
+				pr_err("failed to start link clk rc= %d\n", rc);
+				goto error;
+			}
+			if (mngr->post_clkon_cb) {
+				rc = mngr->post_clkon_cb(mngr->priv_data,
+							DSI_LINK_CLK,
+							l_state);
+				if (rc)
+					pr_err("post link clk on cb failed\n");
+			}
+		} else {
+			/*
+			 * Two conditions that need to be checked for Link
+			 * clocks:
+			 * 1. Link clocks need core clocks to be on when
+			 *    transitioning from EARLY_GATE to OFF state.
+			 * 2. ULPS mode might have to be enabled in case of OFF
+			 *    state. For ULPS, Link clocks should be turned ON
+			 *    first before they are turned off again.
+			 *
+			 * If Link is going from EARLY_GATE to OFF state AND
+			 * Core clock is already in EARLY_GATE or OFF state,
+			 * turn on Core clocks and link clocks.
+			 *
+			 * ULPS state is managed as part of the pre_clkoff_cb.
+			 */
+			if ((l_state == DSI_CLK_OFF) &&
+			    (mngr->link_clk_state ==
+			    DSI_CLK_EARLY_GATE) &&
+			    (mngr->core_clk_state !=
+			    DSI_CLK_ON)) {
+				rc = dsi_display_core_clk_enable(
+					mngr->core_clks, mngr->dsi_ctrl_count,
+					mngr->master_ndx);
+				if (rc) {
+					pr_err("core clks did not start\n");
+					goto error;
+				}
+
+				rc = dsi_display_link_clk_enable(l_clks,
+					mngr->dsi_ctrl_count, mngr->master_ndx);
+				if (rc) {
+					pr_err("Link clks did not start\n");
+					goto error;
+				}
+				l_c_on = true;
+				pr_debug("ECG: core and Link_on\n");
+			}
+
+			if (mngr->pre_clkoff_cb) {
+				rc = mngr->pre_clkoff_cb(mngr->priv_data,
+					DSI_LINK_CLK, l_state);
+				if (rc)
+					pr_err("pre link clk off cb failed\n");
+			}
+
+			rc = dsi_display_link_clk_disable(l_clks,
+				mngr->dsi_ctrl_count, mngr->master_ndx);
+			if (rc) {
+				pr_err("failed to stop link clk, rc = %d\n",
+				       rc);
+				goto error;
+			}
+
+			if (mngr->post_clkoff_cb) {
+				rc = mngr->post_clkoff_cb(mngr->priv_data,
+					DSI_LINK_CLK, l_state);
+				if (rc)
+					pr_err("post link clk off cb failed\n");
+			}
+			/*
+			 * This check is to save unnecessary clock state
+			 * change when going from EARLY_GATE to OFF. In the
+			 * case where the request happens for both Core and Link
+			 * clocks in the same call, core clocks need to be
+			 * turned on first before OFF state can be entered.
+			 *
+			 * Core clocks are turned on here for Link clocks to go
+			 * to OFF state. If core clock request is also present,
+			 * then core clocks can be turned off Core clocks are
+			 * transitioned to OFF state.
+			 */
+			if (l_c_on && (!(c_clks && (c_state == DSI_CLK_OFF)
+					 && (mngr->core_clk_state ==
+					     DSI_CLK_EARLY_GATE)))) {
+				rc = dsi_display_core_clk_disable(
+					mngr->core_clks, mngr->dsi_ctrl_count,
+					mngr->master_ndx);
+				if (rc) {
+					pr_err("core clks did not stop\n");
+					goto error;
+				}
+
+				l_c_on = false;
+				pr_debug("ECG: core off\n");
+			} else
+				pr_debug("ECG: core off skip\n");
+		}
+
+		mngr->link_clk_state = l_state;
+	}
+
+	if (c_clks && (c_state != DSI_CLK_ON)) {
+		/*
+		 * When going to OFF state from EARLY GATE state, Core clocks
+		 * should be turned on first so that the IOs can be clamped.
+		 * l_c_on flag is set, then the core clocks were turned before
+		 * to the Link clocks go to OFF state. So Core clocks are
+		 * already ON and this step can be skipped.
+		 *
+		 * IOs are clamped in pre_clkoff_cb callback.
+		 */
+		if ((c_state == DSI_CLK_OFF) &&
+		    (mngr->core_clk_state ==
+		    DSI_CLK_EARLY_GATE) && !l_c_on) {
+			rc = dsi_display_core_clk_enable(mngr->core_clks,
+				mngr->dsi_ctrl_count, mngr->master_ndx);
+			if (rc) {
+				pr_err("core clks did not start\n");
+				goto error;
+			}
+			pr_debug("ECG: core on\n");
+		} else
+			pr_debug("ECG: core on skip\n");
+
+		if (mngr->pre_clkoff_cb) {
+			rc = mngr->pre_clkoff_cb(mngr->priv_data,
+						 DSI_CORE_CLK,
+						 c_state);
+			if (rc)
+				pr_err("pre core clk off cb failed\n");
+		}
+
+		rc = dsi_display_core_clk_disable(c_clks, mngr->dsi_ctrl_count,
+			mngr->master_ndx);
+		if (rc) {
+			pr_err("failed to turn off core clks rc = %d\n", rc);
+			goto error;
+		}
+
+		if (c_state == DSI_CLK_OFF) {
+			if (mngr->post_clkoff_cb) {
+				rc = mngr->post_clkoff_cb(mngr->priv_data,
+						DSI_CORE_CLK,
+						DSI_CLK_OFF);
+				if (rc)
+					pr_err("post clkoff cb fail, rc = %d\n",
+					       rc);
+			}
+		}
+		mngr->core_clk_state = c_state;
+	}
+
+error:
+	return rc;
+}
+
+static int dsi_recheck_clk_state(struct dsi_clk_mngr *mngr)
+{
+	int rc = 0;
+	struct list_head *pos = NULL;
+	struct dsi_clk_client_info *c;
+	u32 new_core_clk_state = DSI_CLK_OFF;
+	u32 new_link_clk_state = DSI_CLK_OFF;
+	u32 old_c_clk_state = DSI_CLK_OFF;
+	u32 old_l_clk_state = DSI_CLK_OFF;
+	struct dsi_core_clks *c_clks = NULL;
+	struct dsi_link_clks *l_clks = NULL;
+
+	/*
+	 * Conditions to maintain DSI manager clock state based on
+	 *		clock states of various clients:
+	 *	1. If any client has clock in ON state, DSI manager clock state
+	 *		should be ON.
+	 *	2. If any client is in ECG state with rest of them turned OFF,
+	 *	   go to Early gate state.
+	 *	3. If all clients have clocks as OFF, then go to OFF state.
+	 */
+	list_for_each(pos, &mngr->client_list) {
+		c = list_entry(pos, struct dsi_clk_client_info, list);
+		if (c->core_clk_state == DSI_CLK_ON) {
+			new_core_clk_state = DSI_CLK_ON;
+			break;
+		} else if (c->core_clk_state == DSI_CLK_EARLY_GATE) {
+			new_core_clk_state = DSI_CLK_EARLY_GATE;
+		}
+	}
+
+	list_for_each(pos, &mngr->client_list) {
+		c = list_entry(pos, struct dsi_clk_client_info, list);
+		if (c->link_clk_state == DSI_CLK_ON) {
+			new_link_clk_state = DSI_CLK_ON;
+			break;
+		} else if (c->link_clk_state == DSI_CLK_EARLY_GATE) {
+			new_link_clk_state = DSI_CLK_EARLY_GATE;
+		}
+	}
+
+	if (new_core_clk_state != mngr->core_clk_state)
+		c_clks = mngr->core_clks;
+
+	if (new_link_clk_state != mngr->link_clk_state)
+		l_clks = mngr->link_clks;
+
+	old_c_clk_state = mngr->core_clk_state;
+	old_l_clk_state = mngr->link_clk_state;
+
+	pr_debug("c_clk_state (%d -> %d)\n",
+		old_c_clk_state, new_core_clk_state);
+	pr_debug("l_clk_state (%d -> %d)\n",
+		old_l_clk_state, new_link_clk_state);
+
+	if (c_clks || l_clks) {
+		rc = dsi_update_clk_state(c_clks, new_core_clk_state,
+					  l_clks, new_link_clk_state);
+		if (rc) {
+			pr_err("failed to update clock state, rc = %d\n", rc);
+			goto error;
+		}
+	}
+
+error:
+	return rc;
+}
+
+int dsi_clk_req_state(void *client, enum dsi_clk_type clk,
+	enum dsi_clk_state state)
+{
+	int rc = 0;
+	struct dsi_clk_client_info *c = client;
+	struct dsi_clk_mngr *mngr;
+	bool changed = false;
+
+	if (!client || !clk || clk > (DSI_CORE_CLK | DSI_LINK_CLK) ||
+	    state > DSI_CLK_EARLY_GATE) {
+		pr_err("Invalid params, client = %pK, clk = 0x%x, state = %d\n",
+		       client, clk, state);
+		return -EINVAL;
+	}
+
+	mngr = c->mngr;
+	mutex_lock(&mngr->clk_mutex);
+
+	pr_debug("[%s]%s: CLK=%d, new_state=%d, core=%d, linkl=%d\n",
+	       mngr->name, c->name, clk, state, c->core_clk_state,
+	       c->link_clk_state);
+
+	/*
+	 * Clock refcount handling as below:
+	 *	i. Increment refcount whenever ON is called.
+	 *	ii. Decrement refcount when transitioning from ON state to
+	 *		either OFF or EARLY_GATE.
+	 *	iii. Do not decrement refcount when changing from
+	 *		EARLY_GATE to OFF.
+	 */
+	if (state == DSI_CLK_ON) {
+		if (clk & DSI_CORE_CLK) {
+			c->core_refcount++;
+			if (c->core_clk_state != DSI_CLK_ON) {
+				c->core_clk_state = DSI_CLK_ON;
+				changed = true;
+			}
+		}
+		if (clk & DSI_LINK_CLK) {
+			c->link_refcount++;
+			if (c->link_clk_state != DSI_CLK_ON) {
+				c->link_clk_state = DSI_CLK_ON;
+				changed = true;
+			}
+		}
+	} else if ((state == DSI_CLK_EARLY_GATE) ||
+		   (state == DSI_CLK_OFF)) {
+		if (clk & DSI_CORE_CLK) {
+			if (c->core_refcount == 0) {
+				if ((c->core_clk_state ==
+				    DSI_CLK_EARLY_GATE) &&
+				    (state == DSI_CLK_OFF)) {
+					changed = true;
+					c->core_clk_state = DSI_CLK_OFF;
+				} else {
+					pr_warn("Core refcount is zero for %s",
+						c->name);
+				}
+			} else {
+				c->core_refcount--;
+				if (c->core_refcount == 0) {
+					c->core_clk_state = state;
+					changed = true;
+				}
+			}
+		}
+		if (clk & DSI_LINK_CLK) {
+			if (c->link_refcount == 0) {
+				if ((c->link_clk_state ==
+				    DSI_CLK_EARLY_GATE) &&
+				    (state == DSI_CLK_OFF)) {
+					changed = true;
+					c->link_clk_state = DSI_CLK_OFF;
+				} else {
+					pr_warn("Link refcount is zero for %s",
+						c->name);
+				}
+			} else {
+				c->link_refcount--;
+				if (c->link_refcount == 0) {
+					c->link_clk_state = state;
+					changed = true;
+				}
+			}
+		}
+	}
+	pr_debug("[%s]%s: change=%d, Core (ref=%d, state=%d), Link (ref=%d, state=%d)\n",
+		 mngr->name, c->name, changed, c->core_refcount,
+		 c->core_clk_state, c->link_refcount, c->link_clk_state);
+
+	if (changed) {
+		rc = dsi_recheck_clk_state(mngr);
+		if (rc)
+			pr_err("Failed to adjust clock state rc = %d\n", rc);
+	}
+
+	mutex_unlock(&mngr->clk_mutex);
+	return rc;
+}
+
+DEFINE_MUTEX(dsi_mngr_clk_mutex);
+
+int dsi_display_clk_ctrl(void *handle,
+	enum dsi_clk_type clk_type, enum dsi_clk_state clk_state)
+{
+	int rc = 0;
+
+	if (!handle) {
+		pr_err("%s: Invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&dsi_mngr_clk_mutex);
+	rc = dsi_clk_req_state(handle, clk_type, clk_state);
+	if (rc)
+		pr_err("%s: failed set clk state, rc = %d\n", __func__, rc);
+	mutex_unlock(&dsi_mngr_clk_mutex);
+
+	return rc;
+}
+
+void *dsi_register_clk_handle(void *clk_mngr, char *client)
+{
+	void *handle = NULL;
+	struct dsi_clk_mngr *mngr = clk_mngr;
+	struct dsi_clk_client_info *c;
+
+	if (!mngr) {
+		pr_err("bad params\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	mutex_lock(&mngr->clk_mutex);
+
+	c = kzalloc(sizeof(*c), GFP_KERNEL);
+	if (!c) {
+		handle = ERR_PTR(-ENOMEM);
+		goto error;
+	}
+
+	strlcpy(c->name, client, MAX_STRING_LEN);
+	c->mngr = mngr;
+
+	list_add(&c->list, &mngr->client_list);
+
+	pr_debug("[%s]: Added new client (%s)\n", mngr->name, c->name);
+	handle = c;
+error:
+	mutex_unlock(&mngr->clk_mutex);
+	return handle;
+}
+
+int dsi_deregister_clk_handle(void *client)
+{
+	int rc = 0;
+	struct dsi_clk_client_info *c = client;
+	struct dsi_clk_mngr *mngr;
+	struct list_head *pos = NULL;
+	struct list_head *tmp = NULL;
+	struct dsi_clk_client_info *node = NULL;
+
+	if (!client) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	mngr = c->mngr;
+	pr_debug("%s: ENTER\n", mngr->name);
+	mutex_lock(&mngr->clk_mutex);
+	c->core_clk_state = DSI_CLK_OFF;
+	c->link_clk_state = DSI_CLK_OFF;
+
+	rc = dsi_recheck_clk_state(mngr);
+	if (rc) {
+		pr_err("clock state recheck failed rc = %d\n", rc);
+		goto error;
+	}
+
+	list_for_each_safe(pos, tmp, &mngr->client_list) {
+		node = list_entry(pos, struct dsi_clk_client_info,
+			  list);
+		if (node == c) {
+			list_del(&node->list);
+			pr_debug("Removed device (%s)\n", node->name);
+			kfree(node);
+			break;
+		}
+	}
+
+error:
+	mutex_unlock(&mngr->clk_mutex);
+	pr_debug("%s: EXIT, rc = %d\n", mngr->name, rc);
+	return rc;
+}
+
+void *dsi_display_clk_mngr_register(struct dsi_clk_info *info)
+{
+	struct dsi_clk_mngr *mngr;
+	int i = 0;
+
+	if (!info) {
+		pr_err("Invalid params\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	mngr = kzalloc(sizeof(*mngr), GFP_KERNEL);
+	if (!mngr) {
+		mngr = ERR_PTR(-ENOMEM);
+		goto error;
+	}
+
+	mutex_init(&mngr->clk_mutex);
+	mngr->dsi_ctrl_count = info->dsi_ctrl_count;
+	mngr->master_ndx = info->master_ndx;
+
+	if (mngr->dsi_ctrl_count > MAX_DSI_CTRL) {
+		kfree(mngr);
+		return ERR_PTR(-EINVAL);
+	}
+
+	for (i = 0; i < mngr->dsi_ctrl_count; i++) {
+		memcpy(&mngr->core_clks[i].clks, &info->c_clks[i],
+			sizeof(struct dsi_core_clk_info));
+		memcpy(&mngr->link_clks[i].clks, &info->l_clks[i],
+			sizeof(struct dsi_link_clk_info));
+		mngr->core_clks[i].bus_handle = info->bus_handle[i];
+	}
+
+	INIT_LIST_HEAD(&mngr->client_list);
+	mngr->pre_clkon_cb = info->pre_clkon_cb;
+	mngr->post_clkon_cb = info->post_clkon_cb;
+	mngr->pre_clkoff_cb = info->pre_clkoff_cb;
+	mngr->post_clkoff_cb = info->post_clkoff_cb;
+	mngr->priv_data = info->priv_data;
+	memcpy(mngr->name, info->name, MAX_STRING_LEN);
+
+error:
+	pr_debug("EXIT, rc = %ld\n", PTR_ERR(mngr));
+	return mngr;
+}
+
+int dsi_display_clk_mngr_deregister(void *clk_mngr)
+{
+	int rc = 0;
+	struct dsi_clk_mngr *mngr = clk_mngr;
+	struct list_head *position = NULL;
+	struct list_head *tmp = NULL;
+	struct dsi_clk_client_info *node = NULL;
+
+	if (!mngr) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	pr_debug("%s: ENTER\n", mngr->name);
+	mutex_lock(&mngr->clk_mutex);
+
+	list_for_each_safe(position, tmp, &mngr->client_list) {
+		node = list_entry(position, struct dsi_clk_client_info,
+			  list);
+		list_del(&node->list);
+		pr_debug("Removed device (%s)\n", node->name);
+		kfree(node);
+	}
+
+	rc = dsi_recheck_clk_state(mngr);
+	if (rc)
+		pr_err("failed to disable all clocks\n");
+
+	mutex_unlock(&mngr->clk_mutex);
+	pr_debug("%s: EXIT, rc = %d\n", mngr->name, rc);
+	kfree(mngr);
+	return rc;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c
deleted file mode 100644
index 7def847..0000000
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * Copyright (c) 2016, 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/of.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#include "dsi_clk_pwr.h"
-
-#define INC_REFCOUNT(s, start_func) \
-	({ \
-		int rc = 0; \
-		if ((s)->refcount == 0) { \
-			rc = start_func(s); \
-			if (rc) \
-				pr_err("failed to enable, rc = %d\n", rc); \
-		} \
-		(s)->refcount++; \
-		rc; \
-	})
-
-#define DEC_REFCOUNT(s, stop_func) \
-	({ \
-		int rc = 0; \
-		if ((s)->refcount == 0) { \
-			pr_err("unbalanced refcount\n"); \
-		} else { \
-			(s)->refcount--; \
-			if ((s)->refcount == 0) { \
-				rc = stop_func(s); \
-				if (rc) \
-					pr_err("disable failed, rc=%d\n", rc); \
-			} \
-		} \
-		rc; \
-	})
-
-static int dsi_core_clk_start(struct dsi_core_clk_info *clks)
-{
-	int rc = 0;
-
-	rc = clk_prepare_enable(clks->mdp_core_clk);
-	if (rc) {
-		pr_err("failed to enable mdp_core_clk, rc=%d\n", rc);
-		goto error;
-	}
-
-	rc = clk_prepare_enable(clks->iface_clk);
-	if (rc) {
-		pr_err("failed to enable iface_clk, rc=%d\n", rc);
-		goto error_disable_core_clk;
-	}
-
-	rc = clk_prepare_enable(clks->bus_clk);
-	if (rc) {
-		pr_err("failed to enable bus_clk, rc=%d\n", rc);
-		goto error_disable_iface_clk;
-	}
-
-	rc = clk_prepare_enable(clks->core_mmss_clk);
-	if (rc) {
-		pr_err("failed to enable core_mmss_clk, rc=%d\n", rc);
-		goto error_disable_bus_clk;
-	}
-
-	return rc;
-
-error_disable_bus_clk:
-	clk_disable_unprepare(clks->bus_clk);
-error_disable_iface_clk:
-	clk_disable_unprepare(clks->iface_clk);
-error_disable_core_clk:
-	clk_disable_unprepare(clks->mdp_core_clk);
-error:
-	return rc;
-}
-
-static int dsi_core_clk_stop(struct dsi_core_clk_info *clks)
-{
-	clk_disable_unprepare(clks->core_mmss_clk);
-	clk_disable_unprepare(clks->bus_clk);
-	clk_disable_unprepare(clks->iface_clk);
-	clk_disable_unprepare(clks->mdp_core_clk);
-
-	return 0;
-}
-
-static int dsi_link_clk_set_rate(struct dsi_link_clk_info *l_clks)
-{
-	int rc = 0;
-
-	rc = clk_set_rate(l_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->byte_clk, l_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->pixel_clk, l_clks->pixel_clk_rate);
-	if (rc) {
-		pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
-		goto error;
-	}
-error:
-	return rc;
-}
-
-static int dsi_link_clk_prepare(struct dsi_link_clk_info *l_clks)
-{
-	int rc = 0;
-
-	rc = clk_prepare(l_clks->esc_clk);
-	if (rc) {
-		pr_err("Failed to prepare dsi esc clk, rc=%d\n", rc);
-		goto esc_clk_err;
-	}
-
-	rc = clk_prepare(l_clks->byte_clk);
-	if (rc) {
-		pr_err("Failed to prepare dsi byte clk, rc=%d\n", rc);
-		goto byte_clk_err;
-	}
-
-	rc = clk_prepare(l_clks->pixel_clk);
-	if (rc) {
-		pr_err("Failed to prepare dsi pixel clk, rc=%d\n", rc);
-		goto pixel_clk_err;
-	}
-
-	return rc;
-
-pixel_clk_err:
-	clk_unprepare(l_clks->byte_clk);
-byte_clk_err:
-	clk_unprepare(l_clks->esc_clk);
-esc_clk_err:
-	return rc;
-}
-
-static void dsi_link_clk_unprepare(struct dsi_link_clk_info *l_clks)
-{
-	clk_unprepare(l_clks->pixel_clk);
-	clk_unprepare(l_clks->byte_clk);
-	clk_unprepare(l_clks->esc_clk);
-}
-
-static int dsi_link_clk_enable(struct dsi_link_clk_info *l_clks)
-{
-	int rc = 0;
-
-	rc = clk_enable(l_clks->esc_clk);
-	if (rc) {
-		pr_err("Failed to enable dsi esc clk, rc=%d\n", rc);
-		goto esc_clk_err;
-	}
-
-	rc = clk_enable(l_clks->byte_clk);
-	if (rc) {
-		pr_err("Failed to enable dsi byte clk, rc=%d\n", rc);
-		goto byte_clk_err;
-	}
-
-	rc = clk_enable(l_clks->pixel_clk);
-	if (rc) {
-		pr_err("Failed to enable dsi pixel clk, rc=%d\n", rc);
-		goto pixel_clk_err;
-	}
-
-	return rc;
-
-pixel_clk_err:
-	clk_disable(l_clks->byte_clk);
-byte_clk_err:
-	clk_disable(l_clks->esc_clk);
-esc_clk_err:
-	return rc;
-}
-
-static void dsi_link_clk_disable(struct dsi_link_clk_info *l_clks)
-{
-	clk_disable(l_clks->esc_clk);
-	clk_disable(l_clks->pixel_clk);
-	clk_disable(l_clks->byte_clk);
-}
-
-/**
- * dsi_link_clk_start() - enable dsi link clocks
- */
-static int dsi_link_clk_start(struct dsi_link_clk_info *clks)
-{
-	int rc = 0;
-
-	if (clks->set_new_rate) {
-		rc = dsi_link_clk_set_rate(clks);
-		if (rc) {
-			pr_err("failed to set clk rates, rc = %d\n", rc);
-			goto error;
-		} else {
-			clks->set_new_rate = false;
-		}
-	}
-
-	rc = dsi_link_clk_prepare(clks);
-	if (rc) {
-		pr_err("failed to prepare link clks, rc = %d\n", rc);
-		goto error;
-	}
-
-	rc = dsi_link_clk_enable(clks);
-	if (rc) {
-		pr_err("failed to enable link clks, rc = %d\n", rc);
-		goto error_unprepare;
-	}
-
-	pr_debug("Link clocks are enabled\n");
-	return rc;
-error_unprepare:
-	dsi_link_clk_unprepare(clks);
-error:
-	return rc;
-}
-
-/**
- * dsi_link_clk_stop() - Stop DSI link clocks.
- */
-static int dsi_link_clk_stop(struct dsi_link_clk_info *clks)
-{
-	dsi_link_clk_disable(clks);
-	dsi_link_clk_unprepare(clks);
-
-	pr_debug("Link clocks disabled\n");
-
-	return 0;
-}
-
-/*
- * dsi_pwr_parse_supply_node() - parse power supply node from root device node
- */
-static int dsi_pwr_parse_supply_node(struct device_node *root,
-				     struct dsi_regulator_info *regs)
-{
-	int rc = 0;
-	int i = 0;
-	u32 tmp = 0;
-	struct device_node *node = NULL;
-
-	for_each_child_of_node(root, node) {
-		const char *st = NULL;
-
-		rc = of_property_read_string(node, "qcom,supply-name", &st);
-		if (rc) {
-			pr_err("failed to read name, rc = %d\n", rc);
-			goto error;
-		}
-
-		snprintf(regs->vregs[i].vreg_name,
-			 ARRAY_SIZE(regs->vregs[i].vreg_name),
-			 "%s", st);
-
-		rc = of_property_read_u32(node, "qcom,supply-min-voltage",
-					  &tmp);
-		if (rc) {
-			pr_err("failed to read min voltage, rc = %d\n", rc);
-			goto error;
-		}
-		regs->vregs[i].min_voltage = tmp;
-
-		rc = of_property_read_u32(node, "qcom,supply-max-voltage",
-					  &tmp);
-		if (rc) {
-			pr_err("failed to read max voltage, rc = %d\n", rc);
-			goto error;
-		}
-		regs->vregs[i].max_voltage = tmp;
-
-		rc = of_property_read_u32(node, "qcom,supply-enable-load",
-					  &tmp);
-		if (rc) {
-			pr_err("failed to read enable load, rc = %d\n", rc);
-			goto error;
-		}
-		regs->vregs[i].enable_load = tmp;
-
-		rc = of_property_read_u32(node, "qcom,supply-disable-load",
-					  &tmp);
-		if (rc) {
-			pr_err("failed to read disable load, rc = %d\n", rc);
-			goto error;
-		}
-		regs->vregs[i].disable_load = tmp;
-
-		/* Optional values */
-		rc = of_property_read_u32(node, "qcom,supply-pre-on-sleep",
-					  &tmp);
-		if (rc) {
-			pr_debug("pre-on-sleep not specified\n");
-			rc = 0;
-		} else {
-			regs->vregs[i].pre_on_sleep = tmp;
-		}
-
-		rc = of_property_read_u32(node, "qcom,supply-pre-off-sleep",
-					  &tmp);
-		if (rc) {
-			pr_debug("pre-off-sleep not specified\n");
-			rc = 0;
-		} else {
-			regs->vregs[i].pre_off_sleep = tmp;
-		}
-
-		rc = of_property_read_u32(node, "qcom,supply-post-on-sleep",
-					  &tmp);
-		if (rc) {
-			pr_debug("post-on-sleep not specified\n");
-			rc = 0;
-		} else {
-			regs->vregs[i].post_on_sleep = tmp;
-		}
-
-		rc = of_property_read_u32(node, "qcom,supply-post-off-sleep",
-					  &tmp);
-		if (rc) {
-			pr_debug("post-off-sleep not specified\n");
-			rc = 0;
-		} else {
-			regs->vregs[i].post_off_sleep = tmp;
-		}
-
-		++i;
-		pr_debug("[%s] minv=%d maxv=%d, en_load=%d, dis_load=%d\n",
-			 regs->vregs[i].vreg_name,
-			 regs->vregs[i].min_voltage,
-			 regs->vregs[i].max_voltage,
-			 regs->vregs[i].enable_load,
-			 regs->vregs[i].disable_load);
-	}
-
-error:
-	return rc;
-}
-
-/**
- * dsi_pwr_enable_vregs() - enable/disable regulators
- */
-static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable)
-{
-	int rc = 0, i = 0;
-	struct dsi_vreg *vreg;
-	int num_of_v = 0;
-
-	if (enable) {
-		for (i = 0; i < regs->count; i++) {
-			vreg = &regs->vregs[i];
-			if (vreg->pre_on_sleep)
-				msleep(vreg->pre_on_sleep);
-
-			rc = regulator_set_load(vreg->vreg,
-						vreg->enable_load);
-			if (rc < 0) {
-				pr_err("Setting optimum mode failed for %s\n",
-				       vreg->vreg_name);
-				goto error;
-			}
-			num_of_v = regulator_count_voltages(vreg->vreg);
-			if (num_of_v > 0) {
-				rc = regulator_set_voltage(vreg->vreg,
-							   vreg->min_voltage,
-							   vreg->max_voltage);
-				if (rc) {
-					pr_err("Set voltage(%s) fail, rc=%d\n",
-						 vreg->vreg_name, rc);
-					goto error_disable_opt_mode;
-				}
-			}
-
-			rc = regulator_enable(vreg->vreg);
-			if (rc) {
-				pr_err("enable failed for %s, rc=%d\n",
-				       vreg->vreg_name, rc);
-				goto error_disable_voltage;
-			}
-
-			if (vreg->post_on_sleep)
-				msleep(vreg->post_on_sleep);
-		}
-	} else {
-		for (i = (regs->count - 1); i >= 0; i--) {
-			if (regs->vregs[i].pre_off_sleep)
-				msleep(regs->vregs[i].pre_off_sleep);
-
-			(void)regulator_set_load(regs->vregs[i].vreg,
-						regs->vregs[i].disable_load);
-			(void)regulator_disable(regs->vregs[i].vreg);
-
-			if (regs->vregs[i].post_off_sleep)
-				msleep(regs->vregs[i].post_off_sleep);
-		}
-	}
-
-	return 0;
-error_disable_opt_mode:
-	(void)regulator_set_load(regs->vregs[i].vreg,
-				 regs->vregs[i].disable_load);
-
-error_disable_voltage:
-	if (num_of_v > 0)
-		(void)regulator_set_voltage(regs->vregs[i].vreg,
-					    0, regs->vregs[i].max_voltage);
-error:
-	for (i--; i >= 0; i--) {
-		if (regs->vregs[i].pre_off_sleep)
-			msleep(regs->vregs[i].pre_off_sleep);
-
-		(void)regulator_set_load(regs->vregs[i].vreg,
-					 regs->vregs[i].disable_load);
-
-		num_of_v = regulator_count_voltages(regs->vregs[i].vreg);
-		if (num_of_v > 0)
-			(void)regulator_set_voltage(regs->vregs[i].vreg,
-					    0, regs->vregs[i].max_voltage);
-
-		(void)regulator_disable(regs->vregs[i].vreg);
-
-		if (regs->vregs[i].post_off_sleep)
-			msleep(regs->vregs[i].post_off_sleep);
-	}
-
-	return rc;
-}
-
-/**
-* dsi_clk_pwr_of_get_vreg_data - Parse regulator supply information
-* @of_node:        Device of node to parse for supply information.
-* @regs:           Pointer where regulator information will be copied to.
-* @supply_name:    Name of the supply node.
-*
-* return: error code in case of failure or 0 for success.
-*/
-int dsi_clk_pwr_of_get_vreg_data(struct device_node *of_node,
-				 struct dsi_regulator_info *regs,
-				 char *supply_name)
-{
-	int rc = 0;
-	struct device_node *supply_root_node = NULL;
-
-	if (!of_node || !regs) {
-		pr_err("Bad params\n");
-		return -EINVAL;
-	}
-
-	regs->count = 0;
-	supply_root_node = of_get_child_by_name(of_node, supply_name);
-	if (!supply_root_node) {
-		supply_root_node = of_parse_phandle(of_node, supply_name, 0);
-		if (!supply_root_node) {
-			pr_err("No supply entry present for %s\n", supply_name);
-			return -EINVAL;
-		}
-	}
-
-	regs->count = of_get_available_child_count(supply_root_node);
-	if (regs->count == 0) {
-		pr_err("No vregs defined for %s\n", supply_name);
-		return -EINVAL;
-	}
-
-	regs->vregs = kcalloc(regs->count, sizeof(*regs->vregs), GFP_KERNEL);
-	if (!regs->vregs) {
-		regs->count = 0;
-		return -ENOMEM;
-	}
-
-	rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
-	if (rc) {
-		pr_err("failed to parse supply node for %s, rc = %d\n",
-			supply_name, rc);
-
-		kfree(regs->vregs);
-		regs->vregs = NULL;
-		regs->count = 0;
-	}
-
-	return rc;
-}
-
-/**
- * dsi_clk_pwr_get_dt_vreg_data - parse regulator supply information
- * @dev:            Device whose of_node needs to be parsed.
- * @regs:           Pointer where regulator information will be copied to.
- * @supply_name:    Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_get_dt_vreg_data(struct device *dev,
-				 struct dsi_regulator_info *regs,
-				 char *supply_name)
-{
-	int rc = 0;
-	struct device_node *of_node = NULL;
-	struct device_node *supply_node = NULL;
-	struct device_node *supply_root_node = NULL;
-
-	if (!dev || !regs) {
-		pr_err("Bad params\n");
-		return -EINVAL;
-	}
-
-	of_node = dev->of_node;
-	regs->count = 0;
-	supply_root_node = of_get_child_by_name(of_node, supply_name);
-	if (!supply_root_node) {
-		supply_root_node = of_parse_phandle(of_node, supply_name, 0);
-		if (!supply_root_node) {
-			pr_err("No supply entry present for %s\n", supply_name);
-			return -EINVAL;
-		}
-	}
-
-	for_each_child_of_node(supply_root_node, supply_node)
-		regs->count++;
-
-	if (regs->count == 0) {
-		pr_err("No vregs defined for %s\n", supply_name);
-		return -EINVAL;
-	}
-
-	regs->vregs = devm_kcalloc(dev, regs->count, sizeof(*regs->vregs),
-				   GFP_KERNEL);
-	if (!regs->vregs) {
-		regs->count = 0;
-		return -ENOMEM;
-	}
-
-	rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
-	if (rc) {
-		pr_err("failed to parse supply node for %s, rc = %d\n",
-		       supply_name, rc);
-		devm_kfree(dev, regs->vregs);
-		regs->vregs = NULL;
-		regs->count = 0;
-	}
-
-	return rc;
-}
-
-/**
- * dsi_pwr_enable_regulator() - enable a set of regulators
- * @regs:       Pointer to set of regulators to enable or disable.
- * @enable:     Enable/Disable regulators.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable)
-{
-	int rc = 0;
-
-	if (enable) {
-		if (regs->refcount == 0) {
-			rc = dsi_pwr_enable_vregs(regs, true);
-			if (rc)
-				pr_err("failed to enable regulators\n");
-		}
-		regs->refcount++;
-	} else {
-		if (regs->refcount == 0) {
-			pr_err("Unbalanced regulator off\n");
-		} else {
-			regs->refcount--;
-			if (regs->refcount == 0) {
-				rc = dsi_pwr_enable_vregs(regs, false);
-				if (rc)
-					pr_err("failed to disable vregs\n");
-			}
-		}
-	}
-
-	return rc;
-}
-
-/**
- * dsi_clk_enable_core_clks() - enable DSI core clocks
- * @clks:      DSI core clock information.
- * @enable:    enable/disable DSI core clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_core_clks(struct dsi_core_clk_info *clks, bool enable)
-{
-	int rc = 0;
-
-	if (enable)
-		rc = INC_REFCOUNT(clks, dsi_core_clk_start);
-	else
-		rc = DEC_REFCOUNT(clks, dsi_core_clk_stop);
-
-	return rc;
-}
-
-/**
- * dsi_clk_enable_link_clks() - enable DSI link clocks
- * @clks:      DSI link clock information.
- * @enable:    enable/disable DSI link clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_link_clks(struct dsi_link_clk_info *clks, bool enable)
-{
-	int rc = 0;
-
-	if (enable)
-		rc = INC_REFCOUNT(clks, dsi_link_clk_start);
-	else
-		rc = DEC_REFCOUNT(clks, dsi_link_clk_stop);
-
-	return rc;
-}
-
-/**
- * dsi_clk_set_link_frequencies() - set frequencies for link clks
- * @clks:         Link clock information
- * @pixel_clk:    pixel clock frequency in KHz.
- * @byte_clk:     Byte clock frequency in KHz.
- * @esc_clk:      Escape clock frequency in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_link_frequencies(struct dsi_link_clk_info *clks,
-				 u64 pixel_clk,
-				 u64 byte_clk,
-				 u64 esc_clk)
-{
-	int rc = 0;
-
-	clks->pixel_clk_rate = pixel_clk;
-	clks->byte_clk_rate = byte_clk;
-	clks->esc_clk_rate = esc_clk;
-	clks->set_new_rate = true;
-
-	return rc;
-}
-
-/**
- * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
- * @clks:      DSI link clock information.
- * @pixel_clk: Pixel clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_pixel_clk_rate(struct dsi_link_clk_info *clks, u64 pixel_clk)
-{
-	int rc = 0;
-
-	rc = clk_set_rate(clks->pixel_clk, pixel_clk);
-	if (rc)
-		pr_err("failed to set clk rate for pixel clk, rc=%d\n", rc);
-	else
-		clks->pixel_clk_rate = pixel_clk;
-
-	return rc;
-}
-
-/**
- * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
- * @clks:      DSI link clock information.
- * @byte_clk: Byte clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_byte_clk_rate(struct dsi_link_clk_info *clks, u64 byte_clk)
-{
-	int rc = 0;
-
-	rc = clk_set_rate(clks->byte_clk, byte_clk);
-	if (rc)
-		pr_err("failed to set clk rate for byte clk, rc=%d\n", rc);
-	else
-		clks->byte_clk_rate = byte_clk;
-
-	return rc;
-}
-
-/**
- * dsi_clk_update_parent() - update parent clocks for specified clock
- * @parent:       link clock pair which are set as parent.
- * @child:        link clock pair whose parent has to be set.
- */
-int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
-			  struct dsi_clk_link_set *child)
-{
-	int rc = 0;
-
-	rc = clk_set_parent(child->byte_clk, parent->byte_clk);
-	if (rc) {
-		pr_err("failed to set byte clk parent\n");
-		goto error;
-	}
-
-	rc = clk_set_parent(child->pixel_clk, parent->pixel_clk);
-	if (rc) {
-		pr_err("failed to set pixel clk parent\n");
-		goto error;
-	}
-error:
-	return rc;
-}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h
deleted file mode 100644
index 223ca4e..0000000
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (c) 2016, 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_CLK_PWR_H_
-#define _DSI_CLK_PWR_H_
-
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/regulator/consumer.h>
-#include <linux/clk.h>
-
-/**
- * struct dsi_vreg - regulator information for DSI regulators
- * @vreg:            Handle to the regulator.
- * @vreg_name:       Regulator name.
- * @min_voltage:     Minimum voltage in uV.
- * @max_voltage:     Maximum voltage in uV.
- * @enable_load:     Load, in uA, when enabled.
- * @disable_load:    Load, in uA, when disabled.
- * @pre_on_sleep:    Sleep, in ms, before enabling the regulator.
- * @post_on_sleep:   Sleep, in ms, after enabling the regulator.
- * @pre_off_sleep:   Sleep, in ms, before disabling the regulator.
- * @post_off_sleep:  Sleep, in ms, after disabling the regulator.
- */
-struct dsi_vreg {
-	struct regulator *vreg;
-	char vreg_name[32];
-	u32 min_voltage;
-	u32 max_voltage;
-	u32 enable_load;
-	u32 disable_load;
-	u32 pre_on_sleep;
-	u32 post_on_sleep;
-	u32 pre_off_sleep;
-	u32 post_off_sleep;
-};
-
-/**
- * struct dsi_regulator_info - set of vregs that are turned on/off together.
- * @vregs:       Array of dsi_vreg structures.
- * @count:       Number of vregs.
- * @refcount:    Reference counting for enabling.
- */
-struct dsi_regulator_info {
-	struct dsi_vreg *vregs;
-	u32 count;
-	u32 refcount;
-};
-
-/**
- * struct dsi_core_clk_info - Core clock information for DSI hardware
- * @mdp_core_clk:        Handle to MDP core clock.
- * @iface_clk:           Handle to MDP interface clock.
- * @core_mmss_clk:       Handle to MMSS core clock.
- * @bus_clk:             Handle to bus clock.
- * @refcount:            Reference count for core clocks.
- * @clk_state:           Current clock state.
- */
-struct dsi_core_clk_info {
-	struct clk *mdp_core_clk;
-	struct clk *iface_clk;
-	struct clk *core_mmss_clk;
-	struct clk *bus_clk;
-
-	u32 refcount;
-	u32 clk_state;
-};
-
-/**
- * struct dsi_link_clk_info - Link clock information for DSI hardware.
- * @byte_clk:        Handle to DSI byte clock.
- * @byte_clk_rate:   Frequency of DSI byte clock in KHz.
- * @pixel_clk:       Handle to DSI pixel clock.
- * @pixel_clk_rate:  Frequency of DSI pixel clock in KHz.
- * @esc_clk:         Handle to DSI escape clock.
- * @esc_clk_rate:    Frequency of DSI escape clock in KHz.
- * @refcount:        Reference count for link clocks.
- * @clk_state:       Current clock state.
- * @set_new_rate:    private flag used by clock utility.
- */
-struct dsi_link_clk_info {
-	struct clk *byte_clk;
-	u64 byte_clk_rate;
-
-	struct clk *pixel_clk;
-	u64 pixel_clk_rate;
-
-	struct clk *esc_clk;
-	u64 esc_clk_rate;
-
-	u32 refcount;
-	u32 clk_state;
-	bool set_new_rate;
-};
-
-/**
- * struct dsi_clk_link_set - Pair of clock handles to describe link clocks
- * @byte_clk:     Handle to DSi byte clock.
- * @pixel_clk:    Handle to DSI pixel clock.
- */
-struct dsi_clk_link_set {
-	struct clk *byte_clk;
-	struct clk *pixel_clk;
-};
-
-/**
- * dsi_clk_pwr_of_get_vreg_data - parse regulator supply information
- * @of_node:        Device of node to parse for supply information.
- * @regs:           Pointer where regulator information will be copied to.
- * @supply_name:    Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_of_get_vreg_data(struct device_node *of_node,
-				 struct dsi_regulator_info *regs,
-				 char *supply_name);
-
-/**
- * dsi_clk_pwr_get_dt_vreg_data - parse regulator supply information
- * @dev:            Device whose of_node needs to be parsed.
- * @regs:           Pointer where regulator information will be copied to.
- * @supply_name:    Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_get_dt_vreg_data(struct device *dev,
-				 struct dsi_regulator_info *regs,
-				 char *supply_name);
-
-/**
- * dsi_pwr_enable_regulator() - enable a set of regulators
- * @regs:       Pointer to set of regulators to enable or disable.
- * @enable:     Enable/Disable regulators.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable);
-
-/**
- * dsi_clk_enable_core_clks() - enable DSI core clocks
- * @clks:      DSI core clock information.
- * @enable:    enable/disable DSI core clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_core_clks(struct dsi_core_clk_info *clks, bool enable);
-
-/**
- * dsi_clk_enable_link_clks() - enable DSI link clocks
- * @clks:      DSI link clock information.
- * @enable:    enable/disable DSI link clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_link_clks(struct dsi_link_clk_info *clks, bool enable);
-
-/**
- * dsi_clk_set_link_frequencies() - set frequencies for link clks
- * @clks:         Link clock information
- * @pixel_clk:    pixel clock frequency in KHz.
- * @byte_clk:     Byte clock frequency in KHz.
- * @esc_clk:      Escape clock frequency in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_link_frequencies(struct dsi_link_clk_info *clks,
-				 u64 pixel_clk,
-				 u64 byte_clk,
-				 u64 esc_clk);
-
-/**
- * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
- * @clks:      DSI link clock information.
- * @pixel_clk: Pixel clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_pixel_clk_rate(struct dsi_link_clk_info *clks, u64 pixel_clk);
-
-/**
- * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
- * @clks:      DSI link clock information.
- * @byte_clk: Byte clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_byte_clk_rate(struct dsi_link_clk_info *clks, u64 byte_clk);
-
-/**
- * dsi_clk_update_parent() - update parent clocks for specified clock
- * @parent:       link clock pair which are set as parent.
- * @child:        link clock pair whose parent has to be set.
- */
-int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
-			  struct dsi_clk_link_set *child);
-#endif /* _DSI_CLK_PWR_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index b8520aa..3e0e9f6 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, 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
@@ -27,7 +27,8 @@
 #include "msm_gpu.h"
 #include "dsi_ctrl.h"
 #include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
 #include "dsi_catalog.h"
 
 #define DSI_CTRL_DEFAULT_LABEL "MDSS DSI CTRL"
@@ -44,9 +45,6 @@
 	DSI_CTRL_OP_VID_ENGINE,
 	DSI_CTRL_OP_HOST_ENGINE,
 	DSI_CTRL_OP_CMD_TX,
-	DSI_CTRL_OP_ULPS_TOGGLE,
-	DSI_CTRL_OP_CLAMP_TOGGLE,
-	DSI_CTRL_OP_SET_CLK_SOURCE,
 	DSI_CTRL_OP_HOST_INIT,
 	DSI_CTRL_OP_TPG,
 	DSI_CTRL_OP_PHY_SW_RESET,
@@ -99,16 +97,7 @@
 	/* Dump current state */
 	len += snprintf((buf + len), (SZ_4K - len), "Current State:\n");
 	len += snprintf((buf + len), (SZ_4K - len),
-			"\tPOWER_STATUS = %s\n\tCORE_CLOCK = %s\n",
-			TO_ON_OFF(dsi_ctrl->current_state.pwr_enabled),
-			TO_ON_OFF(dsi_ctrl->current_state.core_clk_enabled));
-	len += snprintf((buf + len), (SZ_4K - len),
-			"\tLINK_CLOCK = %s\n\tULPS_STATUS = %s\n",
-			TO_ON_OFF(dsi_ctrl->current_state.link_clk_enabled),
-			TO_ON_OFF(dsi_ctrl->current_state.ulps_enabled));
-	len += snprintf((buf + len), (SZ_4K - len),
-			"\tCLAMP_STATUS = %s\n\tCTRL_ENGINE = %s\n",
-			TO_ON_OFF(dsi_ctrl->current_state.clamp_enabled),
+			"\tCTRL_ENGINE = %s\n",
 			TO_ON_OFF(dsi_ctrl->current_state.controller_state));
 	len += snprintf((buf + len), (SZ_4K - len),
 			"\tVIDEO_ENGINE = %s\n\tCOMMAND_ENGINE = %s\n",
@@ -118,10 +107,10 @@
 	/* Dump clock information */
 	len += snprintf((buf + len), (SZ_4K - len), "\nClock Info:\n");
 	len += snprintf((buf + len), (SZ_4K - len),
-			"\tBYTE_CLK = %llu, PIXEL_CLK = %llu, ESC_CLK = %llu\n",
-			dsi_ctrl->clk_info.link_clks.byte_clk_rate,
-			dsi_ctrl->clk_info.link_clks.pixel_clk_rate,
-			dsi_ctrl->clk_info.link_clks.esc_clk_rate);
+			"\tBYTE_CLK = %u, PIXEL_CLK = %u, ESC_CLK = %u\n",
+			dsi_ctrl->clk_freq.byte_clk_rate,
+			dsi_ctrl->clk_freq.pix_clk_rate,
+			dsi_ctrl->clk_freq.esc_clk_rate);
 
 	/* TODO: make sure that this does not exceed 4K */
 	if (copy_to_user(buff, buf, len)) {
@@ -142,6 +131,8 @@
 	struct dsi_ctrl *dsi_ctrl = file->private_data;
 	char *buf;
 	u32 len = 0;
+	struct dsi_clk_ctrl_info clk_info;
+	int rc = 0;
 
 	if (!dsi_ctrl)
 		return -ENODEV;
@@ -153,15 +144,28 @@
 	if (!buf)
 		return -ENOMEM;
 
-	if (dsi_ctrl->current_state.core_clk_enabled) {
-		len = dsi_ctrl->hw.ops.reg_dump_to_buffer(&dsi_ctrl->hw,
-							  buf,
-							  SZ_4K);
-	} else {
-		len = snprintf((buf + len), (SZ_4K - len),
-			       "Core clocks are not turned on, cannot read\n");
+	clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+	clk_info.clk_type = DSI_CORE_CLK;
+	clk_info.clk_state = DSI_CLK_ON;
+
+	rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+	if (rc) {
+		pr_err("failed to enable DSI core clocks\n");
+		kfree(buf);
+		return rc;
 	}
 
+	len = dsi_ctrl->hw.ops.reg_dump_to_buffer(&dsi_ctrl->hw,
+		  buf, SZ_4K);
+
+	clk_info.clk_state = DSI_CLK_OFF;
+	rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+	if (rc) {
+		pr_err("failed to disable DSI core clocks\n");
+		return rc;
+	}
+
+
 	/* TODO: make sure that this does not exceed 4K */
 	if (copy_to_user(buff, buf, len)) {
 		kfree(buf);
@@ -247,7 +251,7 @@
 			pr_debug("[%d] No change in state, pwr_state=%d\n",
 			       dsi_ctrl->index, op_state);
 			rc = -EINVAL;
-		} else if (state->power_state == DSI_CTRL_POWER_LINK_CLK_ON) {
+		} else if (state->power_state == DSI_CTRL_POWER_VREG_ON) {
 			if ((state->cmd_engine_state == DSI_CTRL_ENGINE_ON) ||
 			    (state->vid_engine_state == DSI_CTRL_ENGINE_ON) ||
 			    (state->controller_state == DSI_CTRL_ENGINE_ON)) {
@@ -266,7 +270,7 @@
 			pr_debug("[%d] No change in state, cmd_state=%d\n",
 			       dsi_ctrl->index, op_state);
 			rc = -EINVAL;
-		} else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+		} else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
 			   (state->controller_state != DSI_CTRL_ENGINE_ON)) {
 			pr_debug("[%d]State error: op=%d: %d, %d\n",
 			       dsi_ctrl->index,
@@ -281,7 +285,7 @@
 			pr_debug("[%d] No change in state, cmd_state=%d\n",
 			       dsi_ctrl->index, op_state);
 			rc = -EINVAL;
-		} else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+		} else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
 			   (state->controller_state != DSI_CTRL_ENGINE_ON)) {
 			pr_debug("[%d]State error: op=%d: %d, %d\n",
 			       dsi_ctrl->index,
@@ -296,7 +300,7 @@
 			pr_debug("[%d] No change in state, ctrl_state=%d\n",
 			       dsi_ctrl->index, op_state);
 			rc = -EINVAL;
-		} else if (state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) {
+		} else if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
 			pr_debug("[%d]State error (link is off): op=%d:, %d\n",
 			       dsi_ctrl->index,
 			       op_state,
@@ -314,7 +318,7 @@
 		}
 		break;
 	case DSI_CTRL_OP_CMD_TX:
-		if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+		if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
 		    (state->host_initialized != true) ||
 		    (state->cmd_engine_state != DSI_CTRL_ENGINE_ON)) {
 			pr_debug("[%d]State error: op=%d: %d, %d, %d\n",
@@ -331,57 +335,18 @@
 			pr_debug("[%d] No change in state, host_init=%d\n",
 			       dsi_ctrl->index, op_state);
 			rc = -EINVAL;
-		} else if (state->power_state != DSI_CTRL_POWER_CORE_CLK_ON) {
+		} else if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
 			pr_debug("[%d]State error: op=%d: %d\n",
 			       dsi_ctrl->index, op, state->power_state);
 			rc = -EINVAL;
 		}
 		break;
-	case DSI_CTRL_OP_ULPS_TOGGLE:
-		if (state->ulps_enabled == op_state) {
-			pr_debug("[%d] No change in state, ulps_enabled=%d\n",
-			       dsi_ctrl->index, op_state);
-			rc = -EINVAL;
-		} else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
-			   (state->controller_state != DSI_CTRL_ENGINE_ON)) {
-			pr_debug("[%d]State error: op=%d: %d, %d\n",
-			       dsi_ctrl->index,
-			       op,
-			       state->power_state,
-			       state->controller_state);
-			rc = -EINVAL;
-		}
-		break;
-	case DSI_CTRL_OP_CLAMP_TOGGLE:
-		if (state->clamp_enabled == op_state) {
-			pr_debug("[%d] No change in state, clamp_enabled=%d\n",
-			       dsi_ctrl->index, op_state);
-			rc = -EINVAL;
-		} else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
-			   (state->controller_state != DSI_CTRL_ENGINE_ON)) {
-			pr_debug("[%d]State error: op=%d: %d, %d\n",
-			       dsi_ctrl->index,
-			       op,
-			       state->power_state,
-			       state->controller_state);
-			rc = -EINVAL;
-		}
-		break;
-	case DSI_CTRL_OP_SET_CLK_SOURCE:
-		if (state->power_state == DSI_CTRL_POWER_LINK_CLK_ON) {
-			pr_debug("[%d] State error: op=%d: %d\n",
-			       dsi_ctrl->index,
-			       op,
-			       state->power_state);
-			rc = -EINVAL;
-		}
-		break;
 	case DSI_CTRL_OP_TPG:
 		if (state->tpg_enabled == op_state) {
 			pr_debug("[%d] No change in state, tpg_enabled=%d\n",
 			       dsi_ctrl->index, op_state);
 			rc = -EINVAL;
-		} else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+		} else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
 			   (state->controller_state != DSI_CTRL_ENGINE_ON)) {
 			pr_debug("[%d]State error: op=%d: %d, %d\n",
 			       dsi_ctrl->index,
@@ -392,7 +357,7 @@
 		}
 		break;
 	case DSI_CTRL_OP_PHY_SW_RESET:
-		if (state->power_state != DSI_CTRL_POWER_CORE_CLK_ON) {
+		if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
 			pr_debug("[%d]State error: op=%d: %d\n",
 			       dsi_ctrl->index, op, state->power_state);
 			rc = -EINVAL;
@@ -422,23 +387,6 @@
 	switch (op) {
 	case DSI_CTRL_OP_POWER_STATE_CHANGE:
 		state->power_state = op_state;
-		if (op_state == DSI_CTRL_POWER_OFF) {
-			state->pwr_enabled = false;
-			state->core_clk_enabled = false;
-			state->link_clk_enabled = false;
-		} else if (op_state == DSI_CTRL_POWER_VREG_ON) {
-			state->pwr_enabled = true;
-			state->core_clk_enabled = false;
-			state->link_clk_enabled = false;
-		} else if (op_state == DSI_CTRL_POWER_CORE_CLK_ON) {
-			state->pwr_enabled = true;
-			state->core_clk_enabled = true;
-			state->link_clk_enabled = false;
-		} else if (op_state == DSI_CTRL_POWER_LINK_CLK_ON) {
-			state->pwr_enabled = true;
-			state->core_clk_enabled = true;
-			state->link_clk_enabled = true;
-		}
 		break;
 	case DSI_CTRL_OP_CMD_ENGINE:
 		state->cmd_engine_state = op_state;
@@ -449,15 +397,6 @@
 	case DSI_CTRL_OP_HOST_ENGINE:
 		state->controller_state = op_state;
 		break;
-	case DSI_CTRL_OP_ULPS_TOGGLE:
-		state->ulps_enabled = (op_state == 1) ? true : false;
-		break;
-	case DSI_CTRL_OP_CLAMP_TOGGLE:
-		state->clamp_enabled = (op_state == 1) ? true : false;
-		break;
-	case DSI_CTRL_OP_SET_CLK_SOURCE:
-		state->clk_source_set = (op_state == 1) ? true : false;
-		break;
 	case DSI_CTRL_OP_HOST_INIT:
 		state->host_initialized = (op_state == 1) ? true : false;
 		break;
@@ -657,7 +596,7 @@
 	struct dsi_regulator_info *regs;
 	struct regulator *vreg = NULL;
 
-	rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+	rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
 					  &ctrl->pwr_info.digital,
 					  "qcom,core-supply-entries");
 	if (rc) {
@@ -665,7 +604,7 @@
 		goto error;
 	}
 
-	rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+	rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
 					  &ctrl->pwr_info.host_pwr,
 					  "qcom,ctrl-supply-entries");
 	if (rc) {
@@ -775,7 +714,7 @@
 }
 
 static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl,
-				      struct dsi_host_config *config)
+	struct dsi_host_config *config, void *clk_handle)
 {
 	int rc = 0;
 	u32 num_of_lanes = 0;
@@ -809,10 +748,12 @@
 	pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n",
 		  byte_clk_rate, pclk_rate);
 
-	rc = dsi_clk_set_link_frequencies(&dsi_ctrl->clk_info.link_clks,
-					  pclk_rate,
-					  byte_clk_rate,
-					  config->esc_clk_rate_hz);
+	dsi_ctrl->clk_freq.byte_clk_rate = byte_clk_rate;
+	dsi_ctrl->clk_freq.pix_clk_rate = pclk_rate;
+	dsi_ctrl->clk_freq.esc_clk_rate = config->esc_clk_rate_hz;
+
+	rc = dsi_clk_set_link_frequencies(clk_handle, dsi_ctrl->clk_freq,
+					dsi_ctrl->index);
 	if (rc)
 		pr_err("Failed to update link frequencies\n");
 
@@ -824,11 +765,13 @@
 	int rc = 0;
 
 	if (enable) {
-		rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.host_pwr,
-					      true);
-		if (rc) {
-			pr_err("failed to enable host power regs, rc=%d\n", rc);
-			goto error;
+		if (!dsi_ctrl->current_state.host_initialized) {
+			rc = dsi_pwr_enable_regulator(
+				&dsi_ctrl->pwr_info.host_pwr, true);
+			if (rc) {
+				pr_err("failed to enable host power regs\n");
+				goto error;
+			}
 		}
 
 		rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.digital,
@@ -849,50 +792,19 @@
 			goto error;
 		}
 
-		rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.host_pwr,
-					      false);
-		if (rc) {
-			pr_err("failed to disable host power regs, rc=%d\n",
-			       rc);
-			goto error;
+		if (!dsi_ctrl->current_state.host_initialized) {
+			rc = dsi_pwr_enable_regulator(
+				&dsi_ctrl->pwr_info.host_pwr, false);
+			if (rc) {
+				pr_err("failed to disable host power regs\n");
+				goto error;
+			}
 		}
 	}
 error:
 	return rc;
 }
 
-static int dsi_ctrl_vote_for_bandwidth(struct dsi_ctrl *dsi_ctrl, bool on)
-{
-	int rc = 0;
-	bool changed = false;
-	struct dsi_ctrl_bus_scale_info *axi_bus = &dsi_ctrl->axi_bus_info;
-
-	if (on) {
-		if (axi_bus->refcount == 0)
-			changed = true;
-
-		axi_bus->refcount++;
-	} else {
-		if (axi_bus->refcount != 0) {
-			axi_bus->refcount--;
-
-			if (axi_bus->refcount == 0)
-				changed = true;
-		} else {
-			pr_err("bus bw votes are not balanced\n");
-		}
-	}
-
-	if (changed) {
-		rc = msm_bus_scale_client_update_request(axi_bus->bus_handle,
-							 on ? 1 : 0);
-		if (rc)
-			pr_err("bus scale client update failed, rc=%d\n", rc);
-	}
-
-	return rc;
-}
-
 static int dsi_ctrl_copy_and_pad_cmd(struct dsi_ctrl *dsi_ctrl,
 				     const struct mipi_dsi_packet *packet,
 				     u8 **buffer,
@@ -1089,16 +1001,22 @@
 static int dsi_enable_ulps(struct dsi_ctrl *dsi_ctrl)
 {
 	int rc = 0;
-	u32 lanes;
+	u32 lanes = 0;
 	u32 ulps_lanes;
 
 	if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
 		lanes = dsi_ctrl->host_config.common_config.data_lanes;
 
-	lanes |= DSI_CLOCK_LANE;
-	dsi_ctrl->hw.ops.ulps_request(&dsi_ctrl->hw, lanes);
+	rc = dsi_ctrl->hw.ops.ulps_ops.wait_for_lane_idle(&dsi_ctrl->hw, lanes);
+	if (rc) {
+		pr_err("lanes not entering idle, skip ULPS\n");
+		return rc;
+	}
 
-	ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+	lanes |= DSI_CLOCK_LANE;
+	dsi_ctrl->hw.ops.ulps_ops.ulps_request(&dsi_ctrl->hw, lanes);
+
+	ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
 
 	if ((lanes & ulps_lanes) != lanes) {
 		pr_err("Failed to enter ULPS, request=0x%x, actual=0x%x\n",
@@ -1118,21 +1036,16 @@
 		lanes = dsi_ctrl->host_config.common_config.data_lanes;
 
 	lanes |= DSI_CLOCK_LANE;
-	ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+	ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
 
 	if ((lanes & ulps_lanes) != lanes)
 		pr_err("Mismatch between lanes in ULPS\n");
 
 	lanes &= ulps_lanes;
 
-	dsi_ctrl->hw.ops.ulps_exit(&dsi_ctrl->hw, lanes);
+	dsi_ctrl->hw.ops.ulps_ops.ulps_exit(&dsi_ctrl->hw, lanes);
 
-	/* 1 ms delay is recommended by specification */
-	udelay(1000);
-
-	dsi_ctrl->hw.ops.clear_ulps_request(&dsi_ctrl->hw, lanes);
-
-	ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+	ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
 	if (ulps_lanes & lanes) {
 		pr_err("Lanes (0x%x) stuck in ULPS\n", ulps_lanes);
 		rc = -EIO;
@@ -1148,15 +1061,9 @@
 	struct dsi_ctrl_state_info *state = &dsi_ctrl->current_state;
 
 	if (!splash_enabled) {
-		state->power_state = DSI_CTRL_POWER_OFF;
+		state->power_state = DSI_CTRL_POWER_VREG_OFF;
 		state->cmd_engine_state = DSI_CTRL_ENGINE_OFF;
 		state->vid_engine_state = DSI_CTRL_ENGINE_OFF;
-		state->pwr_enabled = false;
-		state->core_clk_enabled = false;
-		state->link_clk_enabled = false;
-		state->ulps_enabled = false;
-		state->clamp_enabled = false;
-		state->clk_source_set = false;
 	}
 
 	return rc;
@@ -1218,9 +1125,9 @@
 	return rc;
 }
 
-static int dsi_enable_io_clamp(struct dsi_ctrl *dsi_ctrl, bool enable)
+static int dsi_enable_io_clamp(struct dsi_ctrl *dsi_ctrl,
+		bool enable, bool ulps_enabled)
 {
-	bool en_ulps = dsi_ctrl->current_state.ulps_enabled;
 	u32 lanes = 0;
 
 	if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
@@ -1229,9 +1136,11 @@
 	lanes |= DSI_CLOCK_LANE;
 
 	if (enable)
-		dsi_ctrl->hw.ops.clamp_enable(&dsi_ctrl->hw, lanes, en_ulps);
+		dsi_ctrl->hw.ops.clamp_enable(&dsi_ctrl->hw,
+			lanes, ulps_enabled);
 	else
-		dsi_ctrl->hw.ops.clamp_disable(&dsi_ctrl->hw, lanes, en_ulps);
+		dsi_ctrl->hw.ops.clamp_disable(&dsi_ctrl->hw,
+			lanes, ulps_enabled);
 
 	return 0;
 }
@@ -1508,6 +1417,19 @@
 	return rc;
 }
 
+int dsi_ctrl_clk_cb_register(struct dsi_ctrl *dsi_ctrl,
+	struct clk_ctrl_cb *clk_cb)
+{
+	if (!dsi_ctrl || !clk_cb) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	dsi_ctrl->clk_cb.priv = clk_cb->priv;
+	dsi_ctrl->clk_cb.dsi_clk_cb = clk_cb->dsi_clk_cb;
+	return 0;
+}
+
 /**
  * dsi_ctrl_phy_sw_reset() - perform a PHY software reset
  * @dsi_ctrl:         DSI controller handle.
@@ -1588,6 +1510,63 @@
 }
 
 /**
+ * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
+ * @dsi_ctrl:        DSI controller handle.
+ *
+ * Initializes DSI controller hardware with host configuration provided by
+ * dsi_ctrl_update_host_config(). Initialization can be performed only during
+ * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
+ * performed.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+
+	dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
+					&dsi_ctrl->host_config.lane_map);
+
+	dsi_ctrl->hw.ops.host_setup(&dsi_ctrl->hw,
+				    &dsi_ctrl->host_config.common_config);
+
+	if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE) {
+		dsi_ctrl->hw.ops.cmd_engine_setup(&dsi_ctrl->hw,
+					&dsi_ctrl->host_config.common_config,
+					&dsi_ctrl->host_config.u.cmd_engine);
+
+		dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
+				dsi_ctrl->host_config.video_timing.h_active,
+				dsi_ctrl->host_config.video_timing.h_active * 3,
+				dsi_ctrl->host_config.video_timing.v_active,
+				0x0);
+		dsi_ctrl->hw.ops.cmd_engine_en(&dsi_ctrl->hw, true);
+	} else {
+		dsi_ctrl->hw.ops.video_engine_setup(&dsi_ctrl->hw,
+					&dsi_ctrl->host_config.common_config,
+					&dsi_ctrl->host_config.u.video_engine);
+		dsi_ctrl->hw.ops.set_video_timing(&dsi_ctrl->hw,
+					  &dsi_ctrl->host_config.video_timing);
+		dsi_ctrl->hw.ops.video_engine_en(&dsi_ctrl->hw, true);
+	}
+
+	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
+	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0x0);
+	dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true);
+
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+	return rc;
+}
+
+
+/**
  * dsi_ctrl_host_init() - Initialize DSI host hardware.
  * @dsi_ctrl:        DSI controller handle.
  *
@@ -1702,7 +1681,7 @@
  */
 int dsi_ctrl_update_host_config(struct dsi_ctrl *ctrl,
 				struct dsi_host_config *config,
-				int flags)
+				int flags, void *clk_handle)
 {
 	int rc = 0;
 
@@ -1720,7 +1699,7 @@
 	}
 
 	if (!(flags & DSI_MODE_FLAG_SEAMLESS)) {
-		rc = dsi_ctrl_update_link_freqs(ctrl, config);
+		rc = dsi_ctrl_update_link_freqs(ctrl, config, clk_handle);
 		if (rc) {
 			pr_err("[%s] failed to update link frequencies, rc=%d\n",
 			       ctrl->name, rc);
@@ -1795,12 +1774,6 @@
 		goto error;
 	}
 
-	rc = dsi_ctrl_vote_for_bandwidth(dsi_ctrl, true);
-	if (rc) {
-		pr_err("bandwidth request failed, rc=%d\n", rc);
-		goto error;
-	}
-
 	if (flags & DSI_CTRL_CMD_READ) {
 		rc = dsi_message_rx(dsi_ctrl, msg, flags);
 		if (rc)
@@ -1813,7 +1786,6 @@
 
 	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CMD_TX, 0x0);
 
-	(void)dsi_ctrl_vote_for_bandwidth(dsi_ctrl, false);
 error:
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
 	return rc;
@@ -1880,10 +1852,6 @@
 			     enum dsi_power_state state)
 {
 	int rc = 0;
-	bool core_clk_enable = false;
-	bool link_clk_enable = false;
-	bool reg_enable = false;
-	struct dsi_ctrl_state_info *drv_state;
 
 	if (!dsi_ctrl || (state >= DSI_CTRL_POWER_MAX)) {
 		pr_err("Invalid Params\n");
@@ -1900,57 +1868,14 @@
 		goto error;
 	}
 
-	if (state == DSI_CTRL_POWER_LINK_CLK_ON)
-		reg_enable = core_clk_enable = link_clk_enable = true;
-	else if (state == DSI_CTRL_POWER_CORE_CLK_ON)
-		reg_enable = core_clk_enable = true;
-	else if (state == DSI_CTRL_POWER_VREG_ON)
-		reg_enable = true;
-
-	drv_state = &dsi_ctrl->current_state;
-
-	if ((reg_enable) && (reg_enable != drv_state->pwr_enabled)) {
+	if (state == DSI_CTRL_POWER_VREG_ON) {
 		rc = dsi_ctrl_enable_supplies(dsi_ctrl, true);
 		if (rc) {
 			pr_err("[%d]failed to enable voltage supplies, rc=%d\n",
 			       dsi_ctrl->index, rc);
 			goto error;
 		}
-	}
-
-	if ((core_clk_enable) &&
-	    (core_clk_enable != drv_state->core_clk_enabled)) {
-		rc = dsi_clk_enable_core_clks(&dsi_ctrl->clk_info.core_clks,
-					      true);
-		if (rc) {
-			pr_err("[%d] failed to enable core clocks, rc=%d\n",
-			       dsi_ctrl->index, rc);
-			goto error;
-		}
-	}
-
-	if (link_clk_enable != drv_state->link_clk_enabled) {
-		rc = dsi_clk_enable_link_clks(&dsi_ctrl->clk_info.link_clks,
-					      link_clk_enable);
-		if (rc) {
-			pr_err("[%d] failed to enable link clocks, rc=%d\n",
-			       dsi_ctrl->index, rc);
-			goto error;
-		}
-	}
-
-	if ((!core_clk_enable) &&
-	    (core_clk_enable != drv_state->core_clk_enabled)) {
-		rc = dsi_clk_enable_core_clks(&dsi_ctrl->clk_info.core_clks,
-					      false);
-		if (rc) {
-			pr_err("[%d] failed to disable core clocks, rc=%d\n",
-			       dsi_ctrl->index, rc);
-			goto error;
-		}
-	}
-
-	if ((!reg_enable) && (reg_enable != drv_state->pwr_enabled)) {
+	} else if (state == DSI_CTRL_POWER_VREG_OFF) {
 		rc = dsi_ctrl_enable_supplies(dsi_ctrl, false);
 		if (rc) {
 			pr_err("[%d]failed to disable vreg supplies, rc=%d\n",
@@ -2147,14 +2072,14 @@
 }
 
 /**
- * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
- * @dsi_ctrl:         DSI controller handle.
- * @enable:           enable/disable ULPS.
- *
- * ULPS can be enabled/disabled after DSI host engine is turned on.
- *
- * Return: error code.
- */
+  * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
+  * @dsi_ctrl:		DSI controller handle.
+  * @enable:		enable/disable ULPS.
+  *
+  * ULPS can be enabled/disabled after DSI host engine is turned on.
+  *
+  * Return: error code.
+  */
 int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable)
 {
 	int rc = 0;
@@ -2164,15 +2089,14 @@
 		return -EINVAL;
 	}
 
-	mutex_lock(&dsi_ctrl->ctrl_lock);
-
-	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_ULPS_TOGGLE, enable);
-	if (rc) {
-		pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
-		       dsi_ctrl->index, rc);
-		goto error;
+	if (!dsi_ctrl->hw.ops.ulps_ops.ulps_request ||
+			!dsi_ctrl->hw.ops.ulps_ops.ulps_exit) {
+		pr_debug("DSI controller ULPS ops not present\n");
+		return 0;
 	}
 
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+
 	if (enable)
 		rc = dsi_enable_ulps(dsi_ctrl);
 	else
@@ -2180,12 +2104,11 @@
 
 	if (rc) {
 		pr_err("[DSI_%d] Ulps state change(%d) failed, rc=%d\n",
-		       dsi_ctrl->index, enable, rc);
+			dsi_ctrl->index, enable, rc);
 		goto error;
 	}
-
 	pr_debug("[DSI_%d] ULPS state = %d\n", dsi_ctrl->index, enable);
-	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_ULPS_TOGGLE, enable);
+
 error:
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
 	return rc;
@@ -2200,7 +2123,8 @@
  *
  * Return: error code.
  */
-int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_ctrl, bool enable)
+int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_ctrl,
+		bool enable, bool ulps_enabled)
 {
 	int rc = 0;
 
@@ -2211,21 +2135,13 @@
 
 	mutex_lock(&dsi_ctrl->ctrl_lock);
 
-	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_CLAMP_TOGGLE, enable);
-	if (rc) {
-		pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
-		       dsi_ctrl->index, rc);
-		goto error;
-	}
-
-	rc = dsi_enable_io_clamp(dsi_ctrl, enable);
+	rc = dsi_enable_io_clamp(dsi_ctrl, enable, ulps_enabled);
 	if (rc) {
 		pr_err("[DSI_%d] Failed to enable IO clamp\n", dsi_ctrl->index);
 		goto error;
 	}
 
 	pr_debug("[DSI_%d] Clamp state = %d\n", dsi_ctrl->index, enable);
-	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CLAMP_TOGGLE, enable);
 error:
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
 	return rc;
@@ -2244,7 +2160,6 @@
 			      struct dsi_clk_link_set *source_clks)
 {
 	int rc = 0;
-	u32 op_state = 0;
 
 	if (!dsi_ctrl || !source_clks) {
 		pr_err("Invalid params\n");
@@ -2253,17 +2168,6 @@
 
 	mutex_lock(&dsi_ctrl->ctrl_lock);
 
-	if (source_clks->pixel_clk && source_clks->byte_clk)
-		op_state = 1;
-
-	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_SET_CLK_SOURCE,
-				 op_state);
-	if (rc) {
-		pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
-		       dsi_ctrl->index, rc);
-		goto error;
-	}
-
 	rc = dsi_clk_update_parent(source_clks, &dsi_ctrl->clk_info.rcg_clks);
 	if (rc) {
 		pr_err("[DSI_%d]Failed to update link clk parent, rc=%d\n",
@@ -2278,8 +2182,6 @@
 
 	pr_debug("[DSI_%d] Source clocks are updated\n", dsi_ctrl->index);
 
-	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_SET_CLK_SOURCE, op_state);
-
 error:
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
 	return rc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 993a35c..5b3e25c 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -19,7 +19,8 @@
 
 #include "dsi_defs.h"
 #include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
 #include "drm_mipi_dsi.h"
 
 /*
@@ -41,18 +42,14 @@
 
 /**
  * enum dsi_power_state - defines power states for dsi controller.
- * @DSI_CTRL_POWER_OFF:         DSI controller is powered down.
+ * @DSI_CTRL_POWER_VREG_OFF:    Digital and analog supplies for DSI controller
+				turned off
  * @DSI_CTRL_POWER_VREG_ON:     Digital and analog supplies for DSI controller
- *				are powered on.
- * @DSI_CTRL_POWER_CORE_CLK_ON: DSI core clocks for register access are enabled.
- * @DSI_CTRL_POWER_LINK_CLK_ON: DSI link clocks for link transfer are enabled.
  * @DSI_CTRL_POWER_MAX:         Maximum value.
  */
 enum dsi_power_state {
-	DSI_CTRL_POWER_OFF = 0,
+	DSI_CTRL_POWER_VREG_OFF = 0,
 	DSI_CTRL_POWER_VREG_ON,
-	DSI_CTRL_POWER_CORE_CLK_ON,
-	DSI_CTRL_POWER_LINK_CLK_ON,
 	DSI_CTRL_POWER_MAX,
 };
 
@@ -112,38 +109,26 @@
  * struct dsi_ctrl_bus_scale_info - Bus scale info for msm-bus bandwidth voting
  * @bus_scale_table:        Bus scale voting usecases.
  * @bus_handle:             Handle used for voting bandwidth.
- * @refcount:               reference count.
  */
 struct dsi_ctrl_bus_scale_info {
 	struct msm_bus_scale_pdata *bus_scale_table;
 	u32 bus_handle;
-	u32 refcount;
 };
 
 /**
  * struct dsi_ctrl_state_info - current driver state information
- * @power_state:        Controller power state.
+ * @power_state:        Status of power states on DSI controller.
  * @cmd_engine_state:   Status of DSI command engine.
  * @vid_engine_state:   Status of DSI video engine.
  * @controller_state:   Status of DSI Controller engine.
- * @pwr_enabled:        Set to true, if voltage supplies are enabled.
- * @core_clk_enabled:   Set to true, if core clocks are enabled.
- * @lin_clk_enabled:    Set to true, if link clocks are enabled.
- * @ulps_enabled:       Set to true, if lanes are in ULPS state.
- * @clamp_enabled:      Set to true, if PHY output is clamped.
- * @clk_source_set:     Set to true, if parent is set for DSI link clocks.
+ * @host_initialized:	Boolean to indicate status of DSi host Initialization
+ * @tpg_enabled:        Boolean to indicate whether tpg is enabled.
  */
 struct dsi_ctrl_state_info {
 	enum dsi_power_state power_state;
 	enum dsi_engine_state cmd_engine_state;
 	enum dsi_engine_state vid_engine_state;
 	enum dsi_engine_state controller_state;
-	bool pwr_enabled;
-	bool core_clk_enabled;
-	bool link_clk_enabled;
-	bool ulps_enabled;
-	bool clamp_enabled;
-	bool clk_source_set;
 	bool host_initialized;
 	bool tpg_enabled;
 };
@@ -189,9 +174,11 @@
  * @drm_dev:             Pointer to DRM device.
  * @version:             DSI controller version.
  * @hw:                  DSI controller hardware object.
- * @current_state;       Current driver and hardware state.
+ * @current_state:       Current driver and hardware state.
+ * @clk_cb:		 Callback for DSI clock control.
  * @int_info:            Interrupt information.
  * @clk_info:            Clock information.
+ * @clk_freq:            DSi Link clock frequency information.
  * @pwr_info:            Power information.
  * @axi_bus_info:        AXI bus information.
  * @host_config:         Current host configuration.
@@ -212,10 +199,12 @@
 
 	/* Current state */
 	struct dsi_ctrl_state_info current_state;
+	struct clk_ctrl_cb clk_cb;
 
 	struct dsi_ctrl_interrupts int_info;
 	/* Clock and power states */
 	struct dsi_ctrl_clk_info clk_info;
+	struct link_clk_freq clk_freq;
 	struct dsi_ctrl_power_info pwr_info;
 	struct dsi_ctrl_bus_scale_info axi_bus_info;
 
@@ -290,6 +279,7 @@
  * @dsi_ctrl:          DSI controller handle.
  * @config:            DSI host configuration.
  * @flags:             dsi_mode_flags modifying the behavior
+ * @clk_handle:        Clock handle for DSI clocks
  *
  * Updates driver with new Host configuration to use for host initialization.
  * This function call will only update the software context. The stored
@@ -299,7 +289,7 @@
  */
 int dsi_ctrl_update_host_config(struct dsi_ctrl *dsi_ctrl,
 				struct dsi_host_config *config,
-				int flags);
+				int flags, void *clk_handle);
 
 /**
  * dsi_ctrl_async_timing_update() - update only controller timing
@@ -353,6 +343,30 @@
 int dsi_ctrl_host_deinit(struct dsi_ctrl *dsi_ctrl);
 
 /**
+  * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
+  * @dsi_ctrl:		DSI controller handle.
+  * @enable:		enable/disable ULPS.
+  *
+  * ULPS can be enabled/disabled after DSI host engine is turned on.
+  *
+  * Return: error code.
+  */
+int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
+
+/**
+ * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
+ * @dsi_ctrl:        DSI controller handle.
+ *
+ * Initializes DSI controller hardware with host configuration provided by
+ * dsi_ctrl_update_host_config(). Initialization can be performed only during
+ * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
+ * performed.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl);
+
+/**
  * dsi_ctrl_set_tpg_state() - enable/disable test pattern on the controller
  * @dsi_ctrl:          DSI controller handle.
  * @on:                enable/disable test pattern.
@@ -362,6 +376,7 @@
  *
  * Return: error code.
  */
+
 int dsi_ctrl_set_tpg_state(struct dsi_ctrl *dsi_ctrl, bool on);
 
 /**
@@ -454,15 +469,29 @@
 int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
 
 /**
+ * dsi_ctrl_clk_cb_register() - Register DSI controller clk control callback
+ * @dsi_ctrl:         DSI controller handle.
+ * @clk__cb:      Structure containing callback for clock control.
+ *
+ * Register call for DSI clock control
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_clk_cb_register(struct dsi_ctrl *dsi_ctrl,
+	struct clk_ctrl_cb *clk_cb);
+
+/**
  * dsi_ctrl_set_clamp_state() - set clamp state for DSI phy
  * @dsi_ctrl:             DSI controller handle.
  * @enable:               enable/disable clamping.
+ * @ulps_enabled:         ulps state.
  *
  * Clamps can be enabled/disabled while DSI contoller is still turned on.
  *
  * Return: error code.
  */
-int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_Ctrl, bool enable);
+int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_Ctrl,
+		bool enable, bool ulps_enabled);
 
 /**
  * dsi_ctrl_set_clock_source() - set clock source fpr dsi link clocks
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index b81cdaf..a25c780 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -215,6 +215,50 @@
 
 struct dsi_ctrl_hw;
 
+struct ulps_config_ops {
+	/**
+	 * wait_for_lane_idle() - wait for DSI lanes to go to idle state
+	 * @ctrl:          Pointer to the controller host hardware.
+	 * @lanes:         ORed list of lanes (enum dsi_data_lanes) which need
+	 *                 to be checked to be in idle state.
+	 */
+	int (*wait_for_lane_idle)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+	/**
+	 * ulps_request() - request ulps entry for specified lanes
+	 * @ctrl:          Pointer to the controller host hardware.
+	 * @lanes:         ORed list of lanes (enum dsi_data_lanes) which need
+	 *                 to enter ULPS.
+	 *
+	 * Caller should check if lanes are in ULPS mode by calling
+	 * get_lanes_in_ulps() operation.
+	 */
+	void (*ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+	/**
+	 * ulps_exit() - exit ULPS on specified lanes
+	 * @ctrl:          Pointer to the controller host hardware.
+	 * @lanes:         ORed list of lanes (enum dsi_data_lanes) which need
+	 *                 to exit ULPS.
+	 *
+	 * Caller should check if lanes are in active mode by calling
+	 * get_lanes_in_ulps() operation.
+	 */
+	void (*ulps_exit)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+	/**
+	 * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
+	 * @ctrl:          Pointer to the controller host hardware.
+	 *
+	 * Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
+	 * state. If 0 is returned, all the lanes are active.
+	 *
+	 * Return: List of lanes in ULPS state.
+	 */
+	u32 (*get_lanes_in_ulps)(struct dsi_ctrl_hw *ctrl);
+
+};
+
 /**
  * struct dsi_ctrl_hw_ops - operations supported by dsi host hardware
  */
@@ -383,53 +427,13 @@
 				 u8 *rd_buf,
 				 u32 total_read_len);
 
-	/**
-	 * ulps_request() - request ulps entry for specified lanes
-	 * @ctrl:          Pointer to the controller host hardware.
-	 * @lanes:         ORed list of lanes (enum dsi_data_lanes) which need
-	 *                 to enter ULPS.
-	 *
-	 * Caller should check if lanes are in ULPS mode by calling
-	 * get_lanes_in_ulps() operation.
-	 */
-	void (*ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
-	/**
-	 * ulps_exit() - exit ULPS on specified lanes
-	 * @ctrl:          Pointer to the controller host hardware.
-	 * @lanes:         ORed list of lanes (enum dsi_data_lanes) which need
-	 *                 to exit ULPS.
-	 *
-	 * Caller should check if lanes are in active mode by calling
-	 * get_lanes_in_ulps() operation.
-	 */
-	void (*ulps_exit)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
-	/**
-	 * clear_ulps_request() - clear ulps request once all lanes are active
-	 * @ctrl:          Pointer to controller host hardware.
-	 * @lanes:         ORed list of lanes (enum dsi_data_lanes).
-	 *
-	 * ULPS request should be cleared after the lanes have exited ULPS.
-	 */
-	void (*clear_ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
-	/**
-	 * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
-	 * @ctrl:          Pointer to the controller host hardware.
-	 *
-	 * Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
-	 * state. If 0 is returned, all the lanes are active.
-	 *
-	 * Return: List of lanes in ULPS state.
-	 */
-	u32 (*get_lanes_in_ulps)(struct dsi_ctrl_hw *ctrl);
+	struct ulps_config_ops ulps_ops;
 
 	/**
 	 * clamp_enable() - enable DSI clamps to keep PHY driving a stable link
-	 * @ctrl:          Pointer to the controller host hardware.
-	 * @lanes:         ORed list of lanes which need to be clamped.
-	 * @enable_ulps:   TODO:??
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @lanes:        ORed list of lanes which need to have clamps released.
+	 * @enable_ulps: TODO:??
 	 */
 	void (*clamp_enable)(struct dsi_ctrl_hw *ctrl,
 			     u32 lanes,
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
index ca04eed..e367db3 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -14,6 +14,7 @@
 
 #define pr_fmt(fmt) "dsi-hw:" fmt
 #include <linux/delay.h>
+#include <linux/iopoll.h>
 
 #include "dsi_ctrl_hw.h"
 #include "dsi_ctrl_reg_1_4.h"
@@ -591,6 +592,70 @@
 	pr_debug("[DSI_%d] Read %d bytes\n", ctrl->index, j);
 	return j;
 }
+
+
+/**
+ * dsi_ctrl_hw_wait_for_lane_idle()
+ * This function waits for all the active DSI lanes to be idle by polling all
+ * the FIFO_EMPTY bits and polling he lane status to ensure that all the lanes
+ * are in stop state. This function assumes that the bus clocks required to
+ * access the registers are already turned on.
+ *
+ * @ctrl:      Pointer to the controller host hardware.
+ * @lanes:     ORed list of lanes (enum dsi_data_lanes) which need
+ *             to be stopped.
+ *
+ * return: Error code.
+ */
+int dsi_ctrl_hw_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes)
+{
+	int rc = 0, val = 0;
+	u32 stop_state_mask = 0, fifo_empty_mask = 0;
+	u32 const sleep_us = 10;
+	u32 const timeout_us = 100;
+
+	if (lanes & DSI_DATA_LANE_0) {
+		stop_state_mask |= BIT(0);
+		fifo_empty_mask |= (BIT(12) | BIT(16));
+	}
+	if (lanes & DSI_DATA_LANE_1) {
+		stop_state_mask |= BIT(1);
+			fifo_empty_mask |= BIT(20);
+	}
+	if (lanes & DSI_DATA_LANE_2) {
+		stop_state_mask |= BIT(2);
+		fifo_empty_mask |= BIT(24);
+	}
+	if (lanes & DSI_DATA_LANE_3) {
+		stop_state_mask |= BIT(3);
+		fifo_empty_mask |= BIT(28);
+	}
+
+	pr_debug("%s: polling for fifo empty, mask=0x%08x\n", __func__,
+		fifo_empty_mask);
+	rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
+			(val & fifo_empty_mask), sleep_us, timeout_us);
+	if (rc) {
+		pr_err("%s: fifo not empty, FIFO_STATUS=0x%08x\n",
+				__func__, val);
+		goto error;
+	}
+
+	pr_debug("%s: polling for lanes to be in stop state, mask=0x%08x\n",
+		__func__, stop_state_mask);
+	rc = readl_poll_timeout(ctrl->base + DSI_LANE_STATUS, val,
+			(val & stop_state_mask), sleep_us, timeout_us);
+	if (rc) {
+		pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
+			__func__, val);
+		goto error;
+	}
+
+error:
+	return rc;
+
+}
+
 /**
  * ulps_request() - request ulps entry for specified lanes
  * @ctrl:          Pointer to the controller host hardware.
@@ -615,12 +680,28 @@
 	if (lanes & DSI_DATA_LANE_3)
 		reg |= BIT(3);
 
+	/*
+	 * ULPS entry request. Wait for short time to make sure
+	 * that the lanes enter ULPS. Recommended as per HPG.
+	 */
 	DSI_W32(ctrl, DSI_LANE_CTRL, reg);
+	usleep_range(100, 110);
 
 	pr_debug("[DSI_%d] ULPS requested for lanes 0x%x\n", ctrl->index,
 		 lanes);
 }
 
+static void dsi_ctrl_hw_dln0_phy_err(struct dsi_ctrl_hw *ctrl)
+{
+	u32 status = 0;
+
+	status = DSI_R32(ctrl, DSI_DLN0_PHY_ERR);
+	if (status & 0x011111) {
+		DSI_W32(ctrl, DSI_DLN0_PHY_ERR, status);
+		pr_err("%s: phy_err_status = %x\n", __func__, status);
+	}
+}
+
 /**
  * ulps_exit() - exit ULPS on specified lanes
  * @ctrl:          Pointer to the controller host hardware.
@@ -634,9 +715,15 @@
 {
 	u32 reg = 0;
 
-	reg = DSI_R32(ctrl, DSI_LANE_CTRL);
+	/*
+	 * Clear out any phy errors prior to exiting ULPS
+	 * This fixes certain instances where phy does not exit
+	 * ULPS cleanly. Also, do not print error during such cases.
+	 */
+	dsi_ctrl_hw_dln0_phy_err(ctrl);
+
 	if (lanes & DSI_CLOCK_LANE)
-		reg |= BIT(12);
+		reg = BIT(12);
 	if (lanes & DSI_DATA_LANE_0)
 		reg |= BIT(8);
 	if (lanes & DSI_DATA_LANE_1)
@@ -646,45 +733,26 @@
 	if (lanes & DSI_DATA_LANE_3)
 		reg |= BIT(11);
 
+	/*
+	 * ULPS Exit Request
+	 * Hardware requirement is to wait for at least 1ms
+	 */
 	DSI_W32(ctrl, DSI_LANE_CTRL, reg);
+	usleep_range(1000, 1010);
+	/*
+	 * Sometimes when exiting ULPS, it is possible that some DSI
+	 * lanes are not in the stop state which could lead to DSI
+	 * commands not going through. To avoid this, force the lanes
+	 * to be in stop state.
+	 */
+	DSI_W32(ctrl, DSI_LANE_CTRL, reg << 8);
+	DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
 
 	pr_debug("[DSI_%d] ULPS exit request for lanes=0x%x\n",
 		 ctrl->index, lanes);
 }
 
 /**
- * clear_ulps_request() - clear ulps request once all lanes are active
- * @ctrl:          Pointer to controller host hardware.
- * @lanes:         ORed list of lanes (enum dsi_data_lanes).
- *
- * ULPS request should be cleared after the lanes have exited ULPS.
- */
-void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes)
-{
-	u32 reg = 0;
-
-	reg = DSI_R32(ctrl, DSI_LANE_CTRL);
-	reg &= ~BIT(4); /* clock lane */
-	if (lanes & DSI_DATA_LANE_0)
-		reg &= ~BIT(0);
-	if (lanes & DSI_DATA_LANE_1)
-		reg &= ~BIT(1);
-	if (lanes & DSI_DATA_LANE_2)
-		reg &= ~BIT(2);
-	if (lanes & DSI_DATA_LANE_3)
-		reg &= ~BIT(3);
-
-	DSI_W32(ctrl, DSI_LANE_CTRL, reg);
-	/*
-	 * HPG recommends separate writes for clearing ULPS_REQUEST and
-	 * ULPS_EXIT.
-	 */
-	DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
-
-	pr_debug("[DSI_%d] ULPS request cleared\n", ctrl->index);
-}
-
-/**
  * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
  * @ctrl:          Pointer to the controller host hardware.
  *
@@ -718,7 +786,7 @@
  * clamp_enable() - enable DSI clamps to keep PHY driving a stable link
  * @ctrl:          Pointer to the controller host hardware.
  * @lanes:         ORed list of lanes which need to be clamped.
- * @enable_ulps:   TODO:??
+ * @enable_ulps:   Boolean to specify if ULPS is enabled in DSI controller
  */
 void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
 				 u32 lanes,
@@ -761,15 +829,16 @@
 			clamp_reg |= BIT(0);
 	}
 
-	clamp_reg |= BIT(15); /* Enable clamp */
-
 	reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
 	reg |= (clamp_reg << bit_shift);
 	DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
 
+	reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
+	reg |= (BIT(15) << bit_shift);	/* Enable clamp */
+	DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
 
 	reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
-	reg |= BIT(30);
+	reg |= BIT(30); /* Disable PHY reset */
 	DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
 
 	pr_debug("[DSI_%d] Clamps enabled for lanes=0x%x\n", ctrl->index,
@@ -780,7 +849,7 @@
  * clamp_disable() - disable DSI clamps
  * @ctrl:          Pointer to the controller host hardware.
  * @lanes:         ORed list of lanes which need to have clamps released.
- * @disable_ulps:   TODO:??
+ * @disable_ulps:   Boolean to specify if ULPS is enabled in DSI controller
  */
 void dsi_ctrl_hw_14_clamp_disable(struct dsi_ctrl_hw *ctrl,
 				  u32 lanes,
@@ -826,7 +895,7 @@
 	clamp_reg |= BIT(15); /* Enable clamp */
 	clamp_reg <<= bit_shift;
 
-	/* Disable PHY reset skip */
+	/* Clear disable PHY reset bit */
 	reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
 	reg &= ~BIT(30);
 	DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 5a166a4..06f9ccb 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, 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
@@ -23,6 +23,8 @@
 #include "dsi_ctrl.h"
 #include "dsi_ctrl_hw.h"
 #include "dsi_drm.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
 
 #define to_dsi_display(x) container_of(x, struct dsi_display, host)
 
@@ -164,6 +166,273 @@
 	}
 }
 
+static int dsi_display_is_ulps_req_valid(struct dsi_display *display,
+		bool enable)
+{
+	/* TODO: make checks based on cont. splash */
+	int splash_enabled = false;
+
+	pr_debug("checking ulps req validity\n");
+
+	if (!dsi_panel_ulps_feature_enabled(display->panel))
+		return false;
+
+	/* TODO: ULPS during suspend */
+	if (!dsi_panel_initialized(display->panel))
+		return false;
+
+	if (enable && display->ulps_enabled) {
+		pr_debug("ULPS already enabled\n");
+		return false;
+	} else if (!enable && !display->ulps_enabled) {
+		pr_debug("ULPS already disabled\n");
+		return false;
+	}
+
+	/*
+	 * No need to enter ULPS when transitioning from splash screen to
+	 * boot animation since it is expected that the clocks would be turned
+	 * right back on.
+	 */
+	if (enable && splash_enabled)
+		return false;
+
+	return true;
+}
+
+
+/**
+ * dsi_display_set_ulps() - set ULPS state for DSI lanes.
+ * @dsi_display:         DSI display handle.
+ * @enable:           enable/disable ULPS.
+ *
+ * ULPS can be enabled/disabled after DSI host engine is turned on.
+ *
+ * Return: error code.
+ */
+static int dsi_display_set_ulps(struct dsi_display *display, bool enable)
+{
+	int rc = 0;
+	int i = 0;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+
+	if (!display) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	if (!dsi_display_is_ulps_req_valid(display, enable)) {
+		pr_debug("%s: skipping ULPS config, enable=%d\n",
+			__func__, enable);
+		return 0;
+	}
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+	rc = dsi_ctrl_set_ulps(m_ctrl->ctrl, enable);
+	if (rc) {
+		pr_err("Ulps controller state change(%d) failed\n", enable);
+		return rc;
+	}
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || (ctrl == m_ctrl))
+			continue;
+
+		rc = dsi_ctrl_set_ulps(ctrl->ctrl, enable);
+		if (rc) {
+			pr_err("Ulps controller state change(%d) failed\n",
+				enable);
+			return rc;
+		}
+	}
+	display->ulps_enabled = enable;
+	return 0;
+}
+
+/**
+ * dsi_display_set_clamp() - set clamp state for DSI IO.
+ * @dsi_display:         DSI display handle.
+ * @enable:           enable/disable clamping.
+ *
+ * Return: error code.
+ */
+static int dsi_display_set_clamp(struct dsi_display *display, bool enable)
+{
+	int rc = 0;
+	int i = 0;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+	bool ulps_enabled = false;
+
+
+	if (!display) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+	ulps_enabled = display->ulps_enabled;
+
+	rc = dsi_ctrl_set_clamp_state(m_ctrl->ctrl, enable, ulps_enabled);
+	if (rc) {
+		pr_err("DSI Clamp state change(%d) failed\n", enable);
+		return rc;
+	}
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || (ctrl == m_ctrl))
+			continue;
+
+		rc = dsi_ctrl_set_clamp_state(ctrl->ctrl, enable, ulps_enabled);
+		if (rc) {
+			pr_err("DSI Clamp state change(%d) failed\n", enable);
+			return rc;
+		}
+	}
+	display->clamp_enabled = enable;
+	return 0;
+}
+
+/**
+ * dsi_display_setup_ctrl() - setup DSI controller.
+ * @dsi_display:         DSI display handle.
+ *
+ * Return: error code.
+ */
+static int dsi_display_ctrl_setup(struct dsi_display *display)
+{
+	int rc = 0;
+	int i = 0;
+	struct dsi_display_ctrl *ctrl, *m_ctrl;
+
+
+	if (!display) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+	rc = dsi_ctrl_setup(m_ctrl->ctrl);
+	if (rc) {
+		pr_err("DSI controller setup failed\n");
+		return rc;
+	}
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || (ctrl == m_ctrl))
+			continue;
+
+		rc = dsi_ctrl_setup(ctrl->ctrl);
+		if (rc) {
+			pr_err("DSI controller setup failed\n");
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int dsi_display_phy_enable(struct dsi_display *display);
+
+/**
+ * dsi_display_phy_idle_on() - enable DSI PHY while coming out of idle screen.
+ * @dsi_display:         DSI display handle.
+ * @enable:           enable/disable DSI PHY.
+ *
+ * Return: error code.
+ */
+static int dsi_display_phy_idle_on(struct dsi_display *display,
+		bool mmss_clamp)
+{
+	int rc = 0;
+	int i = 0;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+
+	if (!display) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	if (mmss_clamp && !display->phy_idle_power_off) {
+		dsi_display_phy_enable(display);
+		return 0;
+	}
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+	rc = dsi_phy_idle_ctrl(m_ctrl->phy, true);
+	if (rc) {
+		pr_err("DSI controller setup failed\n");
+		return rc;
+	}
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || (ctrl == m_ctrl))
+			continue;
+
+		rc = dsi_phy_idle_ctrl(ctrl->phy, true);
+		if (rc) {
+			pr_err("DSI controller setup failed\n");
+			return rc;
+		}
+	}
+	display->phy_idle_power_off = false;
+	return 0;
+}
+
+/**
+ * dsi_display_phy_idle_off() - disable DSI PHY while going to idle screen.
+ * @dsi_display:         DSI display handle.
+ * @enable:           enable/disable DSI PHY.
+ *
+ * Return: error code.
+ */
+static int dsi_display_phy_idle_off(struct dsi_display *display)
+{
+	int rc = 0;
+	int i = 0;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+	if (!display) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	if (!display->panel->allow_phy_power_off) {
+		pr_debug("panel doesn't support this feature\n");
+		return 0;
+	}
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+	rc = dsi_phy_idle_ctrl(m_ctrl->phy, false);
+	if (rc) {
+		pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+		       display->name, rc);
+		return rc;
+	}
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || (ctrl == m_ctrl))
+			continue;
+
+		rc = dsi_phy_idle_ctrl(ctrl->phy, false);
+		if (rc) {
+			pr_err("DSI controller setup failed\n");
+			return rc;
+		}
+	}
+	display->phy_idle_power_off = true;
+	return 0;
+}
+
+
+
 static int dsi_display_ctrl_power_on(struct dsi_display *display)
 {
 	int rc = 0;
@@ -171,7 +440,6 @@
 	struct dsi_display_ctrl *ctrl;
 
 	/* Sequence does not matter for split dsi usecases */
-
 	for (i = 0; i < display->ctrl_count; i++) {
 		ctrl = &display->ctrl[i];
 		if (!ctrl->ctrl)
@@ -192,7 +460,8 @@
 		ctrl = &display->ctrl[i];
 		if (!ctrl->ctrl)
 			continue;
-		(void)dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+		(void)dsi_ctrl_set_power_state(ctrl->ctrl,
+			DSI_CTRL_POWER_VREG_OFF);
 	}
 	return rc;
 }
@@ -204,13 +473,13 @@
 	struct dsi_display_ctrl *ctrl;
 
 	/* Sequence does not matter for split dsi usecases */
-
 	for (i = 0; i < display->ctrl_count; i++) {
 		ctrl = &display->ctrl[i];
 		if (!ctrl->ctrl)
 			continue;
 
-		rc = dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+		rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+			DSI_CTRL_POWER_VREG_OFF);
 		if (rc) {
 			pr_err("[%s] Failed to power off, rc=%d\n",
 			       ctrl->ctrl->name, rc);
@@ -228,7 +497,6 @@
 	struct dsi_display_ctrl *ctrl;
 
 	/* Sequence does not matter for split dsi usecases */
-
 	for (i = 0; i < display->ctrl_count; i++) {
 		ctrl = &display->ctrl[i];
 		if (!ctrl->ctrl)
@@ -260,7 +528,6 @@
 	struct dsi_display_ctrl *ctrl;
 
 	/* Sequence does not matter for split dsi usecases */
-
 	for (i = 0; i < display->ctrl_count; i++) {
 		ctrl = &display->ctrl[i];
 		if (!ctrl->phy)
@@ -277,7 +544,7 @@
 	return rc;
 }
 
-static int dsi_display_ctrl_core_clk_on(struct dsi_display *display)
+static int dsi_display_set_clk_src(struct dsi_display *display)
 {
 	int rc = 0;
 	int i;
@@ -288,64 +555,14 @@
 	 * be enabled before the other controller. Master controller in the
 	 * clock context refers to the controller that sources the clock.
 	 */
-
-	m_ctrl = &display->ctrl[display->clk_master_idx];
-
-	rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
-	if (rc) {
-		pr_err("[%s] failed to turn on clocks, rc=%d\n",
-		       display->name, rc);
-		goto error;
-	}
-
-	/* Turn on rest of the controllers */
-	for (i = 0; i < display->ctrl_count; i++) {
-		ctrl = &display->ctrl[i];
-		if (!ctrl->ctrl || (ctrl == m_ctrl))
-			continue;
-
-		rc = dsi_ctrl_set_power_state(ctrl->ctrl,
-					      DSI_CTRL_POWER_CORE_CLK_ON);
-		if (rc) {
-			pr_err("[%s] failed to turn on clock, rc=%d\n",
-			       display->name, rc);
-			goto error_disable_master;
-		}
-	}
-	return rc;
-error_disable_master:
-	(void)dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
-error:
-	return rc;
-}
-
-static int dsi_display_ctrl_link_clk_on(struct dsi_display *display)
-{
-	int rc = 0;
-	int i;
-	struct dsi_display_ctrl *m_ctrl, *ctrl;
-
-	/*
-	 * In case of split DSI usecases, the clock for master controller should
-	 * be enabled before the other controller. Master controller in the
-	 * clock context refers to the controller that sources the clock.
-	 */
-
 	m_ctrl = &display->ctrl[display->clk_master_idx];
 
 	rc = dsi_ctrl_set_clock_source(m_ctrl->ctrl,
-				       &display->clock_info.src_clks);
+		   &display->clock_info.src_clks);
 	if (rc) {
 		pr_err("[%s] failed to set source clocks for master, rc=%d\n",
-		       display->name, rc);
-		goto error;
-	}
-
-	rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_LINK_CLK_ON);
-	if (rc) {
-		pr_err("[%s] failed to turn on clocks, rc=%d\n",
-		       display->name, rc);
-		goto error;
+			   display->name, rc);
+		return rc;
 	}
 
 	/* Turn on rest of the controllers */
@@ -355,97 +572,14 @@
 			continue;
 
 		rc = dsi_ctrl_set_clock_source(ctrl->ctrl,
-					       &display->clock_info.src_clks);
+			   &display->clock_info.src_clks);
 		if (rc) {
 			pr_err("[%s] failed to set source clocks, rc=%d\n",
-			       display->name, rc);
-			goto error_disable_master;
-		}
-
-		rc = dsi_ctrl_set_power_state(ctrl->ctrl,
-					      DSI_CTRL_POWER_LINK_CLK_ON);
-		if (rc) {
-			pr_err("[%s] failed to turn on clock, rc=%d\n",
-			       display->name, rc);
-			goto error_disable_master;
+				   display->name, rc);
+			return rc;
 		}
 	}
-	return rc;
-error_disable_master:
-	(void)dsi_ctrl_set_power_state(m_ctrl->ctrl,
-				       DSI_CTRL_POWER_CORE_CLK_ON);
-error:
-	return rc;
-}
-
-static int dsi_display_ctrl_core_clk_off(struct dsi_display *display)
-{
-	int rc = 0;
-	int i;
-	struct dsi_display_ctrl *m_ctrl, *ctrl;
-
-	/*
-	 * In case of split DSI usecases, clock for slave DSI controllers should
-	 * be disabled first before disabling clock for master controller. Slave
-	 * controllers in the clock context refer to controller which source
-	 * clock from another controller.
-	 */
-
-	m_ctrl = &display->ctrl[display->clk_master_idx];
-
-	for (i = 0; i < display->ctrl_count; i++) {
-		ctrl = &display->ctrl[i];
-		if (!ctrl->ctrl || (ctrl == m_ctrl))
-			continue;
-
-		rc = dsi_ctrl_set_power_state(ctrl->ctrl,
-					      DSI_CTRL_POWER_VREG_ON);
-		if (rc) {
-			pr_err("[%s] failed to turn off clock, rc=%d\n",
-			       display->name, rc);
-		}
-	}
-
-	rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
-	if (rc)
-		pr_err("[%s] failed to turn off clocks, rc=%d\n",
-		       display->name, rc);
-
-	return rc;
-}
-
-static int dsi_display_ctrl_link_clk_off(struct dsi_display *display)
-{
-	int rc = 0;
-	int i;
-	struct dsi_display_ctrl *m_ctrl, *ctrl;
-
-	/*
-	 * In case of split DSI usecases, clock for slave DSI controllers should
-	 * be disabled first before disabling clock for master controller. Slave
-	 * controllers in the clock context refer to controller which source
-	 * clock from another controller.
-	 */
-
-	m_ctrl = &display->ctrl[display->clk_master_idx];
-
-	for (i = 0; i < display->ctrl_count; i++) {
-		ctrl = &display->ctrl[i];
-		if (!ctrl->ctrl || (ctrl == m_ctrl))
-			continue;
-
-		rc = dsi_ctrl_set_power_state(ctrl->ctrl,
-					      DSI_CTRL_POWER_CORE_CLK_ON);
-		if (rc) {
-			pr_err("[%s] failed to turn off clock, rc=%d\n",
-			       display->name, rc);
-		}
-	}
-	rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
-	if (rc)
-		pr_err("[%s] failed to turn off clocks, rc=%d\n",
-		       display->name, rc);
-	return rc;
+	return 0;
 }
 
 static int dsi_display_ctrl_init(struct dsi_display *display)
@@ -893,18 +1027,26 @@
 		return 0;
 	}
 
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+	if (rc) {
+		pr_err("[%s] failed to enable all DSI clocks, rc=%d\n",
+		       display->name, rc);
+		goto error;
+	}
+
 	rc = dsi_display_wake_up(display);
 	if (rc) {
 		pr_err("[%s] failed to wake up display, rc=%d\n",
 		       display->name, rc);
-		goto error;
+		goto error_disable_clks;
 	}
 
 	rc = dsi_display_cmd_engine_enable(display);
 	if (rc) {
 		pr_err("[%s] failed to enable cmd engine, rc=%d\n",
 		       display->name, rc);
-		goto error;
+		goto error_disable_clks;
 	}
 
 	if (display->ctrl_count > 1) {
@@ -925,6 +1067,13 @@
 	}
 error_disable_cmd_engine:
 	(void)dsi_display_cmd_engine_disable(display);
+error_disable_clks:
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+	if (rc) {
+		pr_err("[%s] failed to enable all DSI clocks, rc=%d\n",
+		       display->name, rc);
+	}
 error:
 	return rc;
 }
@@ -1094,6 +1243,231 @@
 	return rc;
 }
 
+static int dsi_display_clk_ctrl_cb(void *priv,
+	struct dsi_clk_ctrl_info clk_state_info)
+{
+	int rc = 0;
+	struct dsi_display *display = NULL;
+	void *clk_handle = NULL;
+
+	if (!priv) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	display = priv;
+
+	if (clk_state_info.client == DSI_CLK_REQ_MDP_CLIENT) {
+		clk_handle = display->mdp_clk_handle;
+	} else if (clk_state_info.client == DSI_CLK_REQ_DSI_CLIENT) {
+		clk_handle = display->dsi_clk_handle;
+	} else {
+		pr_err("invalid clk handle, return error\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * TODO: Wait for CMD_MDP_DONE interrupt if MDP client tries
+	 * to turn off DSI clocks.
+	 */
+	rc = dsi_display_clk_ctrl(clk_handle,
+		clk_state_info.clk_type, clk_state_info.clk_state);
+	if (rc) {
+		pr_err("[%s] failed to %d DSI %d clocks, rc=%d\n",
+		       display->name, clk_state_info.clk_state,
+		       clk_state_info.clk_type, rc);
+		return rc;
+	}
+	return 0;
+}
+
+int dsi_pre_clkoff_cb(void *priv,
+			   enum dsi_clk_type clk,
+			   enum dsi_clk_state new_state)
+{
+	int rc = 0;
+	struct dsi_display *display = priv;
+
+	if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF)) {
+		/*
+		 * If ULPS feature is enabled, enter ULPS first.
+		 */
+		if (dsi_panel_initialized(display->panel) &&
+			dsi_panel_ulps_feature_enabled(display->panel)) {
+			rc = dsi_display_set_ulps(display, true);
+			if (rc) {
+				pr_err("%s: failed enable ulps, rc = %d\n",
+			       __func__, rc);
+			}
+		}
+	}
+
+	if ((clk & DSI_CORE_CLK) && (new_state == DSI_CLK_OFF)) {
+		/*
+		 * Enable DSI clamps only if entering idle power collapse.
+		 */
+		if (dsi_panel_initialized(display->panel) &&
+			dsi_panel_ulps_feature_enabled(display->panel)) {
+			dsi_display_phy_idle_off(display);
+			rc = dsi_display_set_clamp(display, true);
+			if (rc)
+				pr_err("%s: Failed to enable dsi clamps. rc=%d\n",
+					__func__, rc);
+		} else {
+			/* Make sure that controller is not in ULPS state when
+			 * the DSI link is not active.
+			 */
+			rc = dsi_display_set_ulps(display, false);
+			if (rc)
+				pr_err("%s: failed to disable ulps. rc=%d\n",
+					__func__, rc);
+		}
+	}
+
+	return rc;
+}
+
+int dsi_post_clkon_cb(void *priv,
+			   enum dsi_clk_type clk,
+			   enum dsi_clk_state curr_state)
+{
+	int rc = 0;
+	struct dsi_display *display = priv;
+	bool mmss_clamp = false;
+
+	if (clk & DSI_CORE_CLK) {
+		mmss_clamp = display->clamp_enabled;
+		/*
+		 * controller setup is needed if coming out of idle
+		 * power collapse with clamps enabled.
+		 */
+		if (mmss_clamp)
+			dsi_display_ctrl_setup(display);
+
+		if (display->ulps_enabled) {
+			/*
+			 * ULPS Entry Request. This is needed if the lanes were
+			 * in ULPS prior to power collapse, since after
+			 * power collapse and reset, the DSI controller resets
+			 * back to idle state and not ULPS. This ulps entry
+			 * request will transition the state of the DSI
+			 * controller to ULPS which will match the state of the
+			 * DSI phy. This needs to be done prior to disabling
+			 * the DSI clamps.
+			 *
+			 * Also, reset the ulps flag so that ulps_config
+			 * function would reconfigure the controller state to
+			 * ULPS.
+			 */
+			display->ulps_enabled = false;
+			rc = dsi_display_set_ulps(display, true);
+			if (rc) {
+				pr_err("%s: Failed to enter ULPS. rc=%d\n",
+					__func__, rc);
+				goto error;
+			}
+		}
+
+		rc = dsi_display_set_clamp(display, false);
+		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 (display->phy_idle_power_off || mmss_clamp)
+			dsi_display_phy_idle_on(display, mmss_clamp);
+	}
+	if (clk & DSI_LINK_CLK) {
+		if (display->ulps_enabled) {
+			rc = dsi_display_set_ulps(display, false);
+			if (rc) {
+				pr_err("%s: failed to disable ulps, rc= %d\n",
+				       __func__, rc);
+				goto error;
+			}
+		}
+	}
+error:
+	return rc;
+}
+
+int dsi_post_clkoff_cb(void *priv,
+			    enum dsi_clk_type clk_type,
+			    enum dsi_clk_state curr_state)
+{
+	int rc = 0;
+	struct dsi_display *display = priv;
+
+	if (!display) {
+		pr_err("%s: Invalid arg\n", __func__);
+		return -EINVAL;
+	}
+
+	if ((clk_type & DSI_CORE_CLK) &&
+	    (curr_state == DSI_CLK_OFF)) {
+
+		rc = dsi_display_phy_power_off(display);
+		if (rc)
+			pr_err("[%s] failed to power off PHY, rc=%d\n",
+				   display->name, rc);
+
+		rc = dsi_display_ctrl_power_off(display);
+		if (rc)
+			pr_err("[%s] failed to power DSI vregs, rc=%d\n",
+				   display->name, rc);
+	}
+	return rc;
+}
+
+int dsi_pre_clkon_cb(void *priv,
+			  enum dsi_clk_type clk_type,
+			  enum dsi_clk_state new_state)
+{
+	int rc = 0;
+	struct dsi_display *display = priv;
+
+	if (!display) {
+		pr_err("%s: invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	if ((clk_type & DSI_CORE_CLK) && (new_state == DSI_CLK_ON)) {
+		/*
+		 * Enable DSI core power
+		 * 1.> PANEL_PM are controlled as part of
+		 *     panel_power_ctrl. Needed not be handled here.
+		 * 2.> CORE_PM are controlled by dsi clk manager.
+		 * 3.> CTRL_PM need to be enabled/disabled
+		 *     only during unblank/blank. Their state should
+		 *     not be changed during static screen.
+		 */
+
+		rc = dsi_display_ctrl_power_on(display);
+		if (rc) {
+			pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
+				   display->name, rc);
+			return rc;
+		}
+
+		rc = dsi_display_phy_power_on(display);
+		if (rc) {
+			pr_err("[%s] failed to power on dsi phy, rc = %d\n",
+				   display->name, rc);
+			return rc;
+		}
+
+		pr_debug("%s: Enable DSI core power\n", __func__);
+	}
+
+	return rc;
+}
+
+
 static int dsi_display_parse_lane_map(struct dsi_display *display)
 {
 	int rc = 0;
@@ -1509,7 +1883,7 @@
 	}
 
 	/* TODO: Remove this direct reference to the dsi_ctrl */
-	clk_hz = m_ctrl->clk_info.link_clks.pixel_clk_rate;
+	clk_hz = m_ctrl->clk_freq.pix_clk_rate;
 	timing = &per_ctrl_mode.timing;
 
 	switch (dfps_caps.type) {
@@ -1598,7 +1972,7 @@
 	for (i = 0; i < display->ctrl_count; i++) {
 		ctrl = &display->ctrl[i];
 		rc = dsi_ctrl_update_host_config(ctrl->ctrl, &display->config,
-				mode->flags);
+				mode->flags, display->dsi_clk_handle);
 		if (rc) {
 			pr_err("[%s] failed to update ctrl config, rc=%d\n",
 			       display->name, rc);
@@ -1686,7 +2060,12 @@
 	struct dsi_display_ctrl *display_ctrl;
 	struct drm_device *drm;
 	struct dsi_display *display;
+	struct dsi_clk_info info;
+	struct clk_ctrl_cb clk_cb;
+	void *handle = NULL;
 	struct platform_device *pdev = to_platform_device(dev);
+	char *client1 = "dsi_clk_client";
+	char *client2 = "mdp_event_client";
 	int i, rc = 0;
 
 	if (!dev || !pdev || !master) {
@@ -1711,6 +2090,8 @@
 		goto error;
 	}
 
+	memset(&info, 0x0, sizeof(info));
+
 	for (i = 0; i < display->ctrl_count; i++) {
 		display_ctrl = &display->ctrl[i];
 
@@ -1728,6 +2109,72 @@
 			(void)dsi_ctrl_drv_deinit(display_ctrl->ctrl);
 			goto error_ctrl_deinit;
 		}
+
+		memcpy(&info.c_clks[i], &display_ctrl->ctrl->clk_info.core_clks,
+			sizeof(struct dsi_core_clk_info));
+		memcpy(&info.l_clks[i], &display_ctrl->ctrl->clk_info.link_clks,
+			sizeof(struct dsi_link_clk_info));
+		info.bus_handle[i] =
+			display_ctrl->ctrl->axi_bus_info.bus_handle;
+	}
+
+	info.pre_clkoff_cb = dsi_pre_clkoff_cb;
+	info.pre_clkon_cb = dsi_pre_clkon_cb;
+	info.post_clkoff_cb = dsi_post_clkoff_cb;
+	info.post_clkon_cb = dsi_post_clkon_cb;
+	info.priv_data = display;
+	info.master_ndx = display->clk_master_idx;
+	info.dsi_ctrl_count = display->ctrl_count;
+	snprintf(info.name, MAX_STRING_LEN,
+			"DSI_MNGR-%s", display->name);
+
+	display->clk_mngr = dsi_display_clk_mngr_register(&info);
+	if (IS_ERR_OR_NULL(display->clk_mngr)) {
+		rc = PTR_ERR(display->clk_mngr);
+		display->clk_mngr = NULL;
+		pr_err("dsi clock registration failed, rc = %d\n", rc);
+		goto error_ctrl_deinit;
+	}
+
+	handle = dsi_register_clk_handle(display->clk_mngr, client1);
+	if (IS_ERR_OR_NULL(handle)) {
+		rc = PTR_ERR(handle);
+		pr_err("failed to register %s client, rc = %d\n",
+		       client1, rc);
+		goto error_clk_deinit;
+	} else {
+		display->dsi_clk_handle = handle;
+	}
+
+	handle = dsi_register_clk_handle(display->clk_mngr, client2);
+	if (IS_ERR_OR_NULL(handle)) {
+		rc = PTR_ERR(handle);
+		pr_err("failed to register %s client, rc = %d\n",
+		       client2, rc);
+		goto error_clk_client_deinit;
+	} else {
+		display->mdp_clk_handle = handle;
+	}
+
+	clk_cb.priv = display;
+	clk_cb.dsi_clk_cb = dsi_display_clk_ctrl_cb;
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		display_ctrl = &display->ctrl[i];
+
+		rc = dsi_ctrl_clk_cb_register(display_ctrl->ctrl, &clk_cb);
+		if (rc) {
+			pr_err("[%s] failed to register ctrl clk_cb[%d], rc=%d\n",
+			       display->name, i, rc);
+			goto error_ctrl_deinit;
+		}
+
+		rc = dsi_phy_clk_cb_register(display_ctrl->phy, &clk_cb);
+		if (rc) {
+			pr_err("[%s] failed to register phy clk_cb[%d], rc=%d\n",
+			       display->name, i, rc);
+			goto error_ctrl_deinit;
+		}
 	}
 
 	rc = dsi_display_mipi_host_init(display);
@@ -1759,6 +2206,10 @@
 	(void)dsi_panel_drv_deinit(display->panel);
 error_host_deinit:
 	(void)dsi_display_mipi_host_deinit(display);
+error_clk_client_deinit:
+	(void)dsi_deregister_clk_handle(display->dsi_clk_handle);
+error_clk_deinit:
+	(void)dsi_display_clk_mngr_deregister(display->clk_mngr);
 error_ctrl_deinit:
 	for (i = i - 1; i >= 0; i--) {
 		display_ctrl = &display->ctrl[i];
@@ -2294,25 +2745,12 @@
 		goto error;
 	}
 
-	rc = dsi_display_ctrl_power_on(display);
-	if (rc) {
-		pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
-		       display->name, rc);
-		goto error_panel_post_unprep;
-	}
-
-	rc = dsi_display_phy_power_on(display);
-	if (rc) {
-		pr_err("[%s] failed to power on dsi phy, rc = %d\n",
-		       display->name, rc);
-		goto error_ctrl_pwr_off;
-	}
-
-	rc = dsi_display_ctrl_core_clk_on(display);
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_CORE_CLK, DSI_CLK_ON);
 	if (rc) {
 		pr_err("[%s] failed to enable DSI core clocks, rc=%d\n",
 		       display->name, rc);
-		goto error_phy_pwr_off;
+		goto error_panel_post_unprep;
 	}
 
 	rc = dsi_display_phy_sw_reset(display);
@@ -2335,7 +2773,15 @@
 		goto error_phy_disable;
 	}
 
-	rc = dsi_display_ctrl_link_clk_on(display);
+	rc = dsi_display_set_clk_src(display);
+	if (rc) {
+		pr_err("[%s] failed to set DSI link clock source, rc=%d\n",
+			display->name, rc);
+		goto error_ctrl_deinit;
+	}
+
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_LINK_CLK, DSI_CLK_ON);
 	if (rc) {
 		pr_err("[%s] failed to enable DSI link clocks, rc=%d\n",
 		       display->name, rc);
@@ -2360,17 +2806,15 @@
 error_host_engine_off:
 	(void)dsi_display_ctrl_host_disable(display);
 error_ctrl_link_off:
-	(void)dsi_display_ctrl_link_clk_off(display);
+	(void)dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_LINK_CLK, DSI_CLK_OFF);
 error_ctrl_deinit:
 	(void)dsi_display_ctrl_deinit(display);
 error_phy_disable:
 	(void)dsi_display_phy_disable(display);
 error_ctrl_clk_off:
-	(void)dsi_display_ctrl_core_clk_off(display);
-error_phy_pwr_off:
-	(void)dsi_display_phy_power_off(display);
-error_ctrl_pwr_off:
-	(void)dsi_display_ctrl_power_off(display);
+	(void)dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_CORE_CLK, DSI_CLK_OFF);
 error_panel_post_unprep:
 	(void)dsi_panel_post_unprepare(display->panel);
 error:
@@ -2531,7 +2975,8 @@
 		pr_err("[%s] failed to disable DSI host, rc=%d\n",
 		       display->name, rc);
 
-	rc = dsi_display_ctrl_link_clk_off(display);
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_LINK_CLK, DSI_CLK_OFF);
 	if (rc)
 		pr_err("[%s] failed to disable Link clocks, rc=%d\n",
 		       display->name, rc);
@@ -2546,21 +2991,12 @@
 		pr_err("[%s] failed to disable DSI PHY, rc=%d\n",
 		       display->name, rc);
 
-	rc = dsi_display_ctrl_core_clk_off(display);
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_CORE_CLK, DSI_CLK_OFF);
 	if (rc)
 		pr_err("[%s] failed to disable DSI clocks, rc=%d\n",
 		       display->name, rc);
 
-	rc = dsi_display_phy_power_off(display);
-	if (rc)
-		pr_err("[%s] failed to power off PHY, rc=%d\n",
-		       display->name, rc);
-
-	rc = dsi_display_ctrl_power_off(display);
-	if (rc)
-		pr_err("[%s] failed to power DSI vregs, rc=%d\n",
-		       display->name, rc);
-
 	rc = dsi_panel_post_unprepare(display->panel);
 	if (rc)
 		pr_err("[%s] panel post-unprepare failed, rc=%d\n",
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index b77bf26..1c1f4b4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -110,14 +110,20 @@
  * @cmd_master_idx:   The master controller for sending DSI commands to panel.
  * @video_master_idx: The master controller for enabling video engine.
  * @clock_info:       Clock sourcing for DSI display.
+ * @config:           DSI host configuration information.
  * @lane_map:         Lane mapping between DSI host and Panel.
  * @num_of_modes:     Number of modes supported by display.
  * @is_tpg_enabled:   TPG state.
+ * @ulps_enabled:     ulps state.
+ * @clamp_enabled:    clamp state.
+ * @phy_idle_power_off:   PHY power state.
  * @host:             DRM MIPI DSI Host.
- * @connector:        Pointer to DRM connector object.
  * @bridge:           Pointer to DRM bridge object.
  * @cmd_engine_refcount:  Reference count enforcing single instance of cmd eng
- * @root:                 Debugfs root directory
+ * @clk_mngr:         DSI clock manager.
+ * @dsi_clk_handle:   DSI clock handle.
+ * @mdp_clk_handle:   MDP clock handle.
+ * @root:             Debugfs root directory
  */
 struct dsi_display {
 	struct platform_device *pdev;
@@ -146,11 +152,18 @@
 	struct dsi_lane_mapping lane_map;
 	u32 num_of_modes;
 	bool is_tpg_enabled;
+	bool ulps_enabled;
+	bool clamp_enabled;
+	bool phy_idle_power_off;
 
 	struct mipi_dsi_host host;
 	struct dsi_bridge    *bridge;
 	u32 cmd_engine_refcount;
 
+	void *clk_mngr;
+	void *dsi_clk_handle;
+	void *mdp_clk_handle;
+
 	/* DEBUG FS */
 	struct dentry *root;
 };
@@ -317,6 +330,51 @@
 int dsi_display_disable(struct dsi_display *display);
 
 /**
+ * dsi_pre_clkoff_cb() - Callback before clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_pre_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
+	enum dsi_clk_state new_state);
+
+/**
+ * dsi_post_clkoff_cb() - Callback after clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_post_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
+	enum dsi_clk_state curr_state);
+
+/**
+ * dsi_post_clkon_cb() - Callback after clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_post_clkon_cb(void *priv, enum dsi_clk_type clk_type,
+	enum dsi_clk_state curr_state);
+
+
+/**
+ * dsi_pre_clkon_cb() - Callback before clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_pre_clkon_cb(void *priv, enum dsi_clk_type clk_type,
+	enum dsi_clk_state new_state);
+
+/**
  * dsi_display_unprepare() - power off display hardware.
  * @display:            Handle to display.
  *
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index a7a39e6..b9052ec 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, 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
@@ -1331,7 +1331,7 @@
 {
 	int rc = 0;
 
-	rc = dsi_clk_pwr_of_get_vreg_data(of_node,
+	rc = dsi_pwr_of_get_vreg_data(of_node,
 					  &panel->power_info,
 					  "qcom,panel-supply-entries");
 	if (rc) {
@@ -1871,6 +1871,7 @@
 		pr_err("[%s] failed to send DSI_CMD_SET_ON cmds, rc=%d\n",
 		       panel->name, rc);
 	}
+	panel->panel_initialized = true;
 	mutex_unlock(&panel->panel_lock);
 	return rc;
 }
@@ -1892,6 +1893,7 @@
 		       panel->name, rc);
 		goto error;
 	}
+	panel->panel_initialized = false;
 error:
 	mutex_unlock(&panel->panel_lock);
 	return rc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index 4d21a4c..1bf8787 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, 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
@@ -25,7 +25,8 @@
 
 #include "dsi_defs.h"
 #include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
 
 #define MAX_BL_LEVEL 4096
 
@@ -159,8 +160,22 @@
 	struct dsi_pinctrl_info pinctrl;
 
 	bool lp11_init;
+	bool ulps_enabled;
+	bool allow_phy_power_off;
+
+	bool panel_initialized;
 };
 
+static inline bool dsi_panel_ulps_feature_enabled(struct dsi_panel *panel)
+{
+	return panel->ulps_enabled;
+}
+
+static inline bool dsi_panel_initialized(struct dsi_panel *panel)
+{
+	return panel->panel_initialized;
+}
+
 struct dsi_panel *dsi_panel_get(struct device *parent,
 				struct device_node *of_node);
 void dsi_panel_put(struct dsi_panel *panel);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index 1ccbbe7..4249ac4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, 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
@@ -25,7 +25,8 @@
 #include "msm_gpu.h"
 #include "dsi_phy.h"
 #include "dsi_phy_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
 #include "dsi_catalog.h"
 
 #define DSI_PHY_DEFAULT_LABEL "MDSS PHY CTRL"
@@ -104,65 +105,6 @@
 	return 0;
 }
 
-static int dsi_phy_clocks_deinit(struct msm_dsi_phy *phy)
-{
-	int rc = 0;
-	struct dsi_core_clk_info *core = &phy->clks.core_clks;
-
-	if (core->mdp_core_clk)
-		devm_clk_put(&phy->pdev->dev, core->mdp_core_clk);
-	if (core->iface_clk)
-		devm_clk_put(&phy->pdev->dev, core->iface_clk);
-	if (core->core_mmss_clk)
-		devm_clk_put(&phy->pdev->dev, core->core_mmss_clk);
-	if (core->bus_clk)
-		devm_clk_put(&phy->pdev->dev, core->bus_clk);
-
-	memset(core, 0x0, sizeof(*core));
-
-	return rc;
-}
-
-static int dsi_phy_clocks_init(struct platform_device *pdev,
-			       struct msm_dsi_phy *phy)
-{
-	int rc = 0;
-	struct dsi_core_clk_info *core = &phy->clks.core_clks;
-
-	core->mdp_core_clk = devm_clk_get(&pdev->dev, "mdp_core_clk");
-	if (IS_ERR(core->mdp_core_clk)) {
-		rc = PTR_ERR(core->mdp_core_clk);
-		pr_err("failed to get mdp_core_clk, rc=%d\n", rc);
-		goto fail;
-	}
-
-	core->iface_clk = devm_clk_get(&pdev->dev, "iface_clk");
-	if (IS_ERR(core->iface_clk)) {
-		rc = PTR_ERR(core->iface_clk);
-		pr_err("failed to get iface_clk, rc=%d\n", rc);
-		goto fail;
-	}
-
-	core->core_mmss_clk = devm_clk_get(&pdev->dev, "core_mmss_clk");
-	if (IS_ERR(core->core_mmss_clk)) {
-		rc = PTR_ERR(core->core_mmss_clk);
-		pr_err("failed to get core_mmss_clk, rc=%d\n", rc);
-		goto fail;
-	}
-
-	core->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
-	if (IS_ERR(core->bus_clk)) {
-		rc = PTR_ERR(core->bus_clk);
-		pr_err("failed to get bus_clk, rc=%d\n", rc);
-		goto fail;
-	}
-
-	return rc;
-fail:
-	dsi_phy_clocks_deinit(phy);
-	return rc;
-}
-
 static int dsi_phy_supplies_init(struct platform_device *pdev,
 				 struct msm_dsi_phy *phy)
 {
@@ -182,7 +124,7 @@
 		 ARRAY_SIZE(regs->vregs[i].vreg_name),
 		 "%s", "gdsc");
 
-	rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+	rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
 					  &phy->pwr_info.phy_pwr,
 					  "qcom,phy-supply-entries");
 	if (rc) {
@@ -404,16 +346,10 @@
 		goto fail;
 	}
 
-	rc = dsi_phy_clocks_init(pdev, dsi_phy);
-	if (rc) {
-		pr_err("failed to parse clock information, rc = %d\n", rc);
-		goto fail_regmap;
-	}
-
 	rc = dsi_phy_supplies_init(pdev, dsi_phy);
 	if (rc) {
 		pr_err("failed to parse voltage supplies, rc = %d\n", rc);
-		goto fail_clks;
+		goto fail_regmap;
 	}
 
 	rc = dsi_catalog_phy_setup(&dsi_phy->hw, ver_info->version,
@@ -446,8 +382,6 @@
 
 fail_supplies:
 	(void)dsi_phy_supplies_deinit(dsi_phy);
-fail_clks:
-	(void)dsi_phy_clocks_deinit(dsi_phy);
 fail_regmap:
 	(void)dsi_phy_regmap_deinit(dsi_phy);
 fail:
@@ -489,10 +423,6 @@
 	if (rc)
 		pr_err("failed to deinitialize voltage supplies, rc=%d\n", rc);
 
-	rc = dsi_phy_clocks_deinit(phy);
-	if (rc)
-		pr_err("failed to deinitialize clocks, rc=%d\n", rc);
-
 	rc = dsi_phy_regmap_deinit(phy);
 	if (rc)
 		pr_err("failed to deinitialize regmap, rc=%d\n", rc);
@@ -622,6 +552,19 @@
 	return 0;
 }
 
+int dsi_phy_clk_cb_register(struct msm_dsi_phy *dsi_phy,
+	struct clk_ctrl_cb *clk_cb)
+{
+	if (!dsi_phy || !clk_cb) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	dsi_phy->clk_cb.priv = clk_cb->priv;
+	dsi_phy->clk_cb.dsi_clk_cb = clk_cb->dsi_clk_cb;
+	return 0;
+}
+
 /**
  * dsi_phy_validate_mode() - validate a display mode
  * @dsi_phy:            DSI PHY handle.
@@ -679,22 +622,27 @@
 			pr_err("failed to enable digital regulator\n");
 			goto error;
 		}
-		rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.phy_pwr, true);
-		if (rc) {
-			pr_err("failed to enable phy power\n");
-			(void)dsi_pwr_enable_regulator(
-						&dsi_phy->pwr_info.digital,
-						false
-						);
-			goto error;
+
+		if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+			rc = dsi_pwr_enable_regulator(
+				&dsi_phy->pwr_info.phy_pwr, true);
+			if (rc) {
+				pr_err("failed to enable phy power\n");
+				(void)dsi_pwr_enable_regulator(
+					&dsi_phy->pwr_info.digital, false);
+				goto error;
+			}
 		}
 	} else {
-		rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.phy_pwr,
-					      false);
-		if (rc) {
-			pr_err("failed to enable digital regulator\n");
-			goto error;
+		if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+			rc = dsi_pwr_enable_regulator(
+				&dsi_phy->pwr_info.phy_pwr, false);
+			if (rc) {
+				pr_err("failed to enable digital regulator\n");
+				goto error;
+			}
 		}
+
 		rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.digital,
 					      false);
 		if (rc) {
@@ -726,23 +674,28 @@
 		   bool skip_validation)
 {
 	int rc = 0;
+	struct dsi_clk_ctrl_info clk_info;
 
 	if (!phy || !config) {
 		pr_err("Invalid params\n");
 		return -EINVAL;
 	}
 
+	clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+	clk_info.clk_type = DSI_CORE_CLK;
+	clk_info.clk_state = DSI_CLK_ON;
+
+	rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+	if (rc) {
+		pr_err("failed to enable DSI core clocks\n");
+		return rc;
+	}
+
 	mutex_lock(&phy->phy_lock);
 
 	if (!skip_validation)
 		pr_debug("[PHY_%d] TODO: perform validation\n", phy->index);
 
-	rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, true);
-	if (rc) {
-		pr_err("failed to enable core clocks, rc=%d\n", rc);
-		goto error;
-	}
-
 	memcpy(&phy->mode, &config->video_timing, sizeof(phy->mode));
 	phy->data_lanes = config->common_config.data_lanes;
 	phy->dst_format = config->common_config.dst_format;
@@ -755,19 +708,19 @@
 						 &phy->cfg.timing);
 	if (rc) {
 		pr_err("[%s] failed to set timing, rc=%d\n", phy->name, rc);
-		goto error_disable_clks;
+		goto error;
 	}
 
 	dsi_phy_enable_hw(phy);
+	phy->dsi_phy_state = DSI_PHY_ENGINE_ON;
 
-error_disable_clks:
-	rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, false);
-	if (rc) {
-		pr_err("failed to disable clocks, skip phy disable\n");
-		goto error;
-	}
 error:
 	mutex_unlock(&phy->phy_lock);
+
+	clk_info.clk_state = DSI_CLK_OFF;
+	rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+	if (rc)
+		pr_err("failed to disable DSI core clocks\n");
 	return rc;
 }
 
@@ -780,31 +733,67 @@
 int dsi_phy_disable(struct msm_dsi_phy *phy)
 {
 	int rc = 0;
+	struct dsi_clk_ctrl_info clk_info;
 
 	if (!phy) {
 		pr_err("Invalid params\n");
 		return -EINVAL;
 	}
 
+	clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+	clk_info.clk_type = DSI_CORE_CLK;
+	clk_info.clk_state = DSI_CLK_ON;
+
+	rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+	if (rc) {
+		pr_err("failed to enable DSI core clocks\n");
+		return rc;
+	}
+
 	mutex_lock(&phy->phy_lock);
-
-	rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, true);
-	if (rc) {
-		pr_err("failed to enable core clocks, rc=%d\n", rc);
-		goto error;
-	}
-
 	dsi_phy_disable_hw(phy);
+	phy->dsi_phy_state = DSI_PHY_ENGINE_OFF;
+	mutex_unlock(&phy->phy_lock);
 
-	rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, false);
-	if (rc) {
-		pr_err("failed to disable core clocks, rc=%d\n", rc);
-		goto error;
+	clk_info.clk_state = DSI_CLK_OFF;
+
+	rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+	if (rc)
+		pr_err("failed to disable DSI core clocks\n");
+
+	return rc;
+}
+
+/**
+ * dsi_phy_idle_ctrl() - enable/disable DSI PHY during idle screen
+ * @phy:          DSI PHY handle
+ * @enable:       boolean to specify PHY enable/disable.
+ *
+ * Return: error code.
+ */
+
+int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable)
+{
+	if (!phy) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
 	}
 
-error:
+	mutex_lock(&phy->phy_lock);
+	if (enable) {
+		if (phy->hw.ops.phy_idle_on)
+			phy->hw.ops.phy_idle_on(&phy->hw, &phy->cfg);
+
+		if (phy->hw.ops.regulator_enable)
+			phy->hw.ops.regulator_enable(&phy->hw,
+				&phy->cfg.regulators);
+	} else {
+		if (phy->hw.ops.phy_idle_off)
+			phy->hw.ops.phy_idle_off(&phy->hw);
+	}
 	mutex_unlock(&phy->phy_lock);
-	return rc;
+
+	return 0;
 }
 
 /**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index 6c31bfa..d8bfd15 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, 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
@@ -15,7 +15,8 @@
 #define _DSI_PHY_H_
 
 #include "dsi_defs.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
 #include "dsi_phy_hw.h"
 
 struct dsi_ver_spec_info {
@@ -27,14 +28,6 @@
 };
 
 /**
- * struct dsi_phy_clk_info - clock information for DSI controller
- * @core_clks:         Core clocks needed to access PHY registers.
- */
-struct dsi_phy_clk_info {
-	struct dsi_core_clk_info core_clks;
-};
-
-/**
  * struct dsi_phy_power_info - digital and analog power supplies for DSI PHY
  * @digital:       Digital power supply for DSI PHY.
  * @phy_pwr:       Analog power supplies for DSI PHY to work.
@@ -45,6 +38,19 @@
 };
 
 /**
+ * enum phy_engine_state - define engine status for dsi phy.
+ * @DSI_PHY_ENGINE_OFF:  Engine is turned off.
+ * @DSI_PHY_ENGINE_ON:   Engine is turned on.
+ * @DSI_PHY_ENGINE_MAX:  Maximum value.
+ */
+enum phy_engine_state {
+	DSI_PHY_ENGINE_OFF = 0,
+	DSI_PHY_ENGINE_ON,
+	DSI_PHY_ENGINE_MAX,
+};
+
+
+/**
  * struct msm_dsi_phy - DSI PHY object
  * @pdev:              Pointer to platform device.
  * @index:             Instance id.
@@ -53,8 +59,11 @@
  * @phy_lock:          Mutex for hardware and object access.
  * @ver_info:          Version specific phy parameters.
  * @hw:                DSI PHY hardware object.
+ * @pwr_info:          Power information.
  * @cfg:               DSI phy configuration.
+ * @clk_cb:	       structure containing call backs for clock control
  * @power_state:       True if PHY is powered on.
+ * @dsi_phy_state:     PHY state information.
  * @mode:              Current mode.
  * @data_lanes:        Number of data lanes used.
  * @dst_format:        Destination format.
@@ -70,11 +79,12 @@
 	const struct dsi_ver_spec_info *ver_info;
 	struct dsi_phy_hw hw;
 
-	struct dsi_phy_clk_info clks;
 	struct dsi_phy_power_info pwr_info;
 
 	struct dsi_phy_cfg cfg;
+	struct clk_ctrl_cb clk_cb;
 
+	enum phy_engine_state dsi_phy_state;
 	bool power_state;
 	struct dsi_mode_info mode;
 	enum dsi_data_lanes data_lanes;
@@ -170,6 +180,25 @@
 int dsi_phy_disable(struct msm_dsi_phy *phy);
 
 /**
+ * dsi_phy_clk_cb_register() - Register PHY clock control callback
+ * @phy:          DSI PHY handle
+ * @clk_cb:	  Structure containing call back for clock control
+ *
+ * Return: error code.
+ */
+int dsi_phy_clk_cb_register(struct msm_dsi_phy *phy,
+	struct clk_ctrl_cb *clk_cb);
+
+/**
+ * dsi_phy_idle_ctrl() - enable/disable DSI PHY during idle screen
+ * @phy:          DSI PHY handle
+ * @enable:       boolean to specify PHY enable/disable.
+ *
+ * Return: error code.
+ */
+int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable);
+
+/**
  * dsi_phy_set_timing_params() - timing parameters for the panel
  * @phy:          DSI PHY handle
  * @timing:       array holding timing params.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
index 5edfd5e..78c59e2 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -129,6 +129,20 @@
 	void (*disable)(struct dsi_phy_hw *phy);
 
 	/**
+	 * phy_idle_on() - Enable PHY hardware when entering idle screen
+	 * @phy:      Pointer to DSI PHY hardware object.
+	 * @cfg:      Per lane configurations for timing, strength and lane
+	 *	      configurations.
+	 */
+	void (*phy_idle_on)(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+
+	/**
+	 * phy_idle_off() - Disable PHY hardware when exiting idle screen
+	 * @phy:      Pointer to DSI PHY hardware object.
+	 */
+	void (*phy_idle_off)(struct dsi_phy_hw *phy);
+
+	/**
 	 * calculate_timing_params() - calculates timing parameters.
 	 * @phy:      Pointer to DSI PHY hardware object.
 	 * @mode:     Mode information for which timing has to be calculated.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
index 512352d..d0aaa6b 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, 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
@@ -242,6 +242,44 @@
 	pr_debug("[DSI_%d]Phy disabled ", phy->index);
 }
 
+/**
+ * dsi_phy_hw_v4_0_idle_on() - Enable DSI PHY hardware during idle screen
+ * @phy:      Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v4_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg)
+{
+	int i = 0;
+
+	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_0(i),
+			cfg->strength.lane[i][0]);
+		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i),
+			cfg->strength.lane[i][1]);
+	}
+	wmb(); /* make sure write happens */
+	pr_debug("[DSI_%d]Phy enabled out of idle screen\n", phy->index);
+}
+
+
+/**
+ * dsi_phy_hw_v4_0_idle_off() - Disable DSI PHY hardware during idle screen
+ * @phy:      Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v4_0_idle_off(struct dsi_phy_hw *phy)
+{
+	int i = 0;
+
+	DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f);
+	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
+		DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(i), 0x1c);
+	DSI_W32(phy, DSIPHY_CMN_LDO_CNTRL, 0x1C);
+
+	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
+		DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i), 0x0);
+	wmb(); /* make sure write happens */
+	pr_debug("[DSI_%d]Phy disabled during idle screen\n", phy->index);
+}
+
 static const u32 bits_per_pixel[DSI_PIXEL_FORMAT_MAX] = {
 	16, 18, 18, 24, 3, 8, 12 };
 
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c
new file mode 100644
index 0000000..609c5ff
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2016-2017, 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/of.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "dsi_pwr.h"
+
+/*
+ * dsi_pwr_parse_supply_node() - parse power supply node from root device node
+ */
+static int dsi_pwr_parse_supply_node(struct device_node *root,
+				     struct dsi_regulator_info *regs)
+{
+	int rc = 0;
+	int i = 0;
+	u32 tmp = 0;
+	struct device_node *node = NULL;
+
+	for_each_child_of_node(root, node) {
+		const char *st = NULL;
+
+		rc = of_property_read_string(node, "qcom,supply-name", &st);
+		if (rc) {
+			pr_err("failed to read name, rc = %d\n", rc);
+			goto error;
+		}
+
+		snprintf(regs->vregs[i].vreg_name,
+			 ARRAY_SIZE(regs->vregs[i].vreg_name),
+			 "%s", st);
+
+		rc = of_property_read_u32(node, "qcom,supply-min-voltage",
+					  &tmp);
+		if (rc) {
+			pr_err("failed to read min voltage, rc = %d\n", rc);
+			goto error;
+		}
+		regs->vregs[i].min_voltage = tmp;
+
+		rc = of_property_read_u32(node, "qcom,supply-max-voltage",
+					  &tmp);
+		if (rc) {
+			pr_err("failed to read max voltage, rc = %d\n", rc);
+			goto error;
+		}
+		regs->vregs[i].max_voltage = tmp;
+
+		rc = of_property_read_u32(node, "qcom,supply-enable-load",
+					  &tmp);
+		if (rc) {
+			pr_err("failed to read enable load, rc = %d\n", rc);
+			goto error;
+		}
+		regs->vregs[i].enable_load = tmp;
+
+		rc = of_property_read_u32(node, "qcom,supply-disable-load",
+					  &tmp);
+		if (rc) {
+			pr_err("failed to read disable load, rc = %d\n", rc);
+			goto error;
+		}
+		regs->vregs[i].disable_load = tmp;
+
+		/* Optional values */
+		rc = of_property_read_u32(node, "qcom,supply-pre-on-sleep",
+					  &tmp);
+		if (rc) {
+			pr_debug("pre-on-sleep not specified\n");
+			rc = 0;
+		} else {
+			regs->vregs[i].pre_on_sleep = tmp;
+		}
+
+		rc = of_property_read_u32(node, "qcom,supply-pre-off-sleep",
+					  &tmp);
+		if (rc) {
+			pr_debug("pre-off-sleep not specified\n");
+			rc = 0;
+		} else {
+			regs->vregs[i].pre_off_sleep = tmp;
+		}
+
+		rc = of_property_read_u32(node, "qcom,supply-post-on-sleep",
+					  &tmp);
+		if (rc) {
+			pr_debug("post-on-sleep not specified\n");
+			rc = 0;
+		} else {
+			regs->vregs[i].post_on_sleep = tmp;
+		}
+
+		rc = of_property_read_u32(node, "qcom,supply-post-off-sleep",
+					  &tmp);
+		if (rc) {
+			pr_debug("post-off-sleep not specified\n");
+			rc = 0;
+		} else {
+			regs->vregs[i].post_off_sleep = tmp;
+		}
+
+		++i;
+		pr_debug("[%s] minv=%d maxv=%d, en_load=%d, dis_load=%d\n",
+			 regs->vregs[i].vreg_name,
+			 regs->vregs[i].min_voltage,
+			 regs->vregs[i].max_voltage,
+			 regs->vregs[i].enable_load,
+			 regs->vregs[i].disable_load);
+	}
+
+error:
+	return rc;
+}
+
+/**
+ * dsi_pwr_enable_vregs() - enable/disable regulators
+ */
+static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable)
+{
+	int rc = 0, i = 0;
+	struct dsi_vreg *vreg;
+	int num_of_v = 0;
+
+	if (enable) {
+		for (i = 0; i < regs->count; i++) {
+			vreg = &regs->vregs[i];
+			if (vreg->pre_on_sleep)
+				msleep(vreg->pre_on_sleep);
+
+			rc = regulator_set_load(vreg->vreg,
+						vreg->enable_load);
+			if (rc < 0) {
+				pr_err("Setting optimum mode failed for %s\n",
+				       vreg->vreg_name);
+				goto error;
+			}
+			num_of_v = regulator_count_voltages(vreg->vreg);
+			if (num_of_v > 0) {
+				rc = regulator_set_voltage(vreg->vreg,
+							   vreg->min_voltage,
+							   vreg->max_voltage);
+				if (rc) {
+					pr_err("Set voltage(%s) fail, rc=%d\n",
+						 vreg->vreg_name, rc);
+					goto error_disable_opt_mode;
+				}
+			}
+
+			rc = regulator_enable(vreg->vreg);
+			if (rc) {
+				pr_err("enable failed for %s, rc=%d\n",
+				       vreg->vreg_name, rc);
+				goto error_disable_voltage;
+			}
+
+			if (vreg->post_on_sleep)
+				msleep(vreg->post_on_sleep);
+		}
+	} else {
+		for (i = (regs->count - 1); i >= 0; i--) {
+			if (regs->vregs[i].pre_off_sleep)
+				msleep(regs->vregs[i].pre_off_sleep);
+
+			(void)regulator_set_load(regs->vregs[i].vreg,
+						regs->vregs[i].disable_load);
+			(void)regulator_disable(regs->vregs[i].vreg);
+
+			if (regs->vregs[i].post_off_sleep)
+				msleep(regs->vregs[i].post_off_sleep);
+		}
+	}
+
+	return 0;
+error_disable_opt_mode:
+	(void)regulator_set_load(regs->vregs[i].vreg,
+				 regs->vregs[i].disable_load);
+
+error_disable_voltage:
+	if (num_of_v > 0)
+		(void)regulator_set_voltage(regs->vregs[i].vreg,
+					    0, regs->vregs[i].max_voltage);
+error:
+	for (i--; i >= 0; i--) {
+		if (regs->vregs[i].pre_off_sleep)
+			msleep(regs->vregs[i].pre_off_sleep);
+
+		(void)regulator_set_load(regs->vregs[i].vreg,
+					 regs->vregs[i].disable_load);
+
+		num_of_v = regulator_count_voltages(regs->vregs[i].vreg);
+		if (num_of_v > 0)
+			(void)regulator_set_voltage(regs->vregs[i].vreg,
+					    0, regs->vregs[i].max_voltage);
+
+		(void)regulator_disable(regs->vregs[i].vreg);
+
+		if (regs->vregs[i].post_off_sleep)
+			msleep(regs->vregs[i].post_off_sleep);
+	}
+
+	return rc;
+}
+
+/**
+* dsi_pwr_of_get_vreg_data - Parse regulator supply information
+* @of_node:        Device of node to parse for supply information.
+* @regs:           Pointer where regulator information will be copied to.
+* @supply_name:    Name of the supply node.
+*
+* return: error code in case of failure or 0 for success.
+*/
+int dsi_pwr_of_get_vreg_data(struct device_node *of_node,
+				 struct dsi_regulator_info *regs,
+				 char *supply_name)
+{
+	int rc = 0;
+	struct device_node *supply_root_node = NULL;
+
+	if (!of_node || !regs) {
+		pr_err("Bad params\n");
+		return -EINVAL;
+	}
+
+	regs->count = 0;
+	supply_root_node = of_get_child_by_name(of_node, supply_name);
+	if (!supply_root_node) {
+		supply_root_node = of_parse_phandle(of_node, supply_name, 0);
+		if (!supply_root_node) {
+			pr_err("No supply entry present for %s\n", supply_name);
+			return -EINVAL;
+		}
+	}
+
+	regs->count = of_get_available_child_count(supply_root_node);
+	if (regs->count == 0) {
+		pr_err("No vregs defined for %s\n", supply_name);
+		return -EINVAL;
+	}
+
+	regs->vregs = kcalloc(regs->count, sizeof(*regs->vregs), GFP_KERNEL);
+	if (!regs->vregs) {
+		regs->count = 0;
+		return -ENOMEM;
+	}
+
+	rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
+	if (rc) {
+		pr_err("failed to parse supply node for %s, rc = %d\n",
+			supply_name, rc);
+
+		kfree(regs->vregs);
+		regs->vregs = NULL;
+		regs->count = 0;
+	}
+
+	return rc;
+}
+
+/**
+ * dsi_pwr_get_dt_vreg_data - parse regulator supply information
+ * @dev:            Device whose of_node needs to be parsed.
+ * @regs:           Pointer where regulator information will be copied to.
+ * @supply_name:    Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_get_dt_vreg_data(struct device *dev,
+				 struct dsi_regulator_info *regs,
+				 char *supply_name)
+{
+	int rc = 0;
+	struct device_node *of_node = NULL;
+	struct device_node *supply_node = NULL;
+	struct device_node *supply_root_node = NULL;
+
+	if (!dev || !regs) {
+		pr_err("Bad params\n");
+		return -EINVAL;
+	}
+
+	of_node = dev->of_node;
+	regs->count = 0;
+	supply_root_node = of_get_child_by_name(of_node, supply_name);
+	if (!supply_root_node) {
+		supply_root_node = of_parse_phandle(of_node, supply_name, 0);
+		if (!supply_root_node) {
+			pr_err("No supply entry present for %s\n", supply_name);
+			return -EINVAL;
+		}
+	}
+
+	for_each_child_of_node(supply_root_node, supply_node)
+		regs->count++;
+
+	if (regs->count == 0) {
+		pr_err("No vregs defined for %s\n", supply_name);
+		return -EINVAL;
+	}
+
+	regs->vregs = devm_kcalloc(dev, regs->count, sizeof(*regs->vregs),
+				   GFP_KERNEL);
+	if (!regs->vregs) {
+		regs->count = 0;
+		return -ENOMEM;
+	}
+
+	rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
+	if (rc) {
+		pr_err("failed to parse supply node for %s, rc = %d\n",
+		       supply_name, rc);
+		devm_kfree(dev, regs->vregs);
+		regs->vregs = NULL;
+		regs->count = 0;
+	}
+
+	return rc;
+}
+
+/**
+ * dsi_pwr_enable_regulator() - enable a set of regulators
+ * @regs:       Pointer to set of regulators to enable or disable.
+ * @enable:     Enable/Disable regulators.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable)
+{
+	int rc = 0;
+
+	if (enable) {
+		if (regs->refcount == 0) {
+			rc = dsi_pwr_enable_vregs(regs, true);
+			if (rc)
+				pr_err("failed to enable regulators\n");
+		}
+		regs->refcount++;
+	} else {
+		if (regs->refcount == 0) {
+			pr_err("Unbalanced regulator off\n");
+		} else {
+			regs->refcount--;
+			if (regs->refcount == 0) {
+				rc = dsi_pwr_enable_vregs(regs, false);
+				if (rc)
+					pr_err("failed to disable vregs\n");
+			}
+		}
+	}
+
+	return rc;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h
new file mode 100644
index 0000000..4d85244
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016-2017, 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_PWR_H_
+#define _DSI_PWR_H_
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/regulator/consumer.h>
+
+/**
+ * struct dsi_vreg - regulator information for DSI regulators
+ * @vreg:            Handle to the regulator.
+ * @vreg_name:       Regulator name.
+ * @min_voltage:     Minimum voltage in uV.
+ * @max_voltage:     Maximum voltage in uV.
+ * @enable_load:     Load, in uA, when enabled.
+ * @disable_load:    Load, in uA, when disabled.
+ * @pre_on_sleep:    Sleep, in ms, before enabling the regulator.
+ * @post_on_sleep:   Sleep, in ms, after enabling the regulator.
+ * @pre_off_sleep:   Sleep, in ms, before disabling the regulator.
+ * @post_off_sleep:  Sleep, in ms, after disabling the regulator.
+ */
+struct dsi_vreg {
+	struct regulator *vreg;
+	char vreg_name[32];
+	u32 min_voltage;
+	u32 max_voltage;
+	u32 enable_load;
+	u32 disable_load;
+	u32 pre_on_sleep;
+	u32 post_on_sleep;
+	u32 pre_off_sleep;
+	u32 post_off_sleep;
+};
+
+/**
+ * struct dsi_regulator_info - set of vregs that are turned on/off together.
+ * @vregs:       Array of dsi_vreg structures.
+ * @count:       Number of vregs.
+ * @refcount:    Reference counting for enabling.
+ */
+struct dsi_regulator_info {
+	struct dsi_vreg *vregs;
+	u32 count;
+	u32 refcount;
+};
+
+/**
+ * dsi_pwr_of_get_vreg_data - parse regulator supply information
+ * @of_node:        Device of node to parse for supply information.
+ * @regs:           Pointer where regulator information will be copied to.
+ * @supply_name:    Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_of_get_vreg_data(struct device_node *of_node,
+				 struct dsi_regulator_info *regs,
+				 char *supply_name);
+
+/**
+ * dsi_pwr_get_dt_vreg_data - parse regulator supply information
+ * @dev:            Device whose of_node needs to be parsed.
+ * @regs:           Pointer where regulator information will be copied to.
+ * @supply_name:    Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_get_dt_vreg_data(struct device *dev,
+				 struct dsi_regulator_info *regs,
+				 char *supply_name);
+
+/**
+ * dsi_pwr_enable_regulator() - enable a set of regulators
+ * @regs:       Pointer to set of regulators to enable or disable.
+ * @enable:     Enable/Disable regulators.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable);
+#endif /* _DSI_PWR_H_ */