drm/msm/dsi-staging: enable dsi idle to turn off phy

Phy can be turned off during idle period to conserve power.
Add idle mode support to turn off phy and reinitialize PLL
CSRs upon resume from idle.

Change-Id: If2c11a5a82c839f88b39c9d5d6da97888e4457ba
Signed-off-by: Alan Kwong <akwong@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/display/msm/dsi.txt b/Documentation/devicetree/bindings/display/msm/dsi.txt
index 3534f04..70a20cb 100644
--- a/Documentation/devicetree/bindings/display/msm/dsi.txt
+++ b/Documentation/devicetree/bindings/display/msm/dsi.txt
@@ -121,6 +121,11 @@
 					If ping pong split is enabled, this time should not be higher
 					than two times the dsi link rate time.
 					If the property is not specified, then the default value is 14000 us.
+- qcom,panel-allow-phy-poweroff:	A boolean property indicates that panel allows to turn off the phy power
+					supply during idle screen. A panel should be able to handle the dsi lanes
+					in floating state(not LP00 or LP11) to turn on this property. Software
+					turns off PHY pmic power supply, phy ldo and DSI Lane ldo during
+					idle screen (footswitch control off) when this property is enabled.
 
 [1] Documentation/devicetree/bindings/clocks/clock-bindings.txt
 [2] Documentation/devicetree/bindings/graph.txt
@@ -229,4 +234,5 @@
 		vddio-supply = <&pma8084_l12>;
 
 		qcom,dsi-phy-regulator-ldo-mode;
+		qcom,panel-allow-phy-poweroff;
 	};
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index b4c115d..de5738e 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -36,6 +36,8 @@
 
 #define MISR_BUFF_SIZE	256
 
+#define MAX_NAME_SIZE	64
+
 static DEFINE_MUTEX(dsi_display_list_lock);
 static LIST_HEAD(dsi_display_list);
 static char dsi_display_primary[MAX_CMDLINE_PARAM_LEN];
@@ -687,6 +689,8 @@
 {
 	int rc = 0;
 	struct dentry *dir, *dump_file, *misr_data;
+	char name[MAX_NAME_SIZE];
+	int i;
 
 	dir = debugfs_create_dir(display->name, NULL);
 	if (IS_ERR_OR_NULL(dir)) {
@@ -720,6 +724,24 @@
 		goto error_remove_dir;
 	}
 
+	for (i = 0; i < display->ctrl_count; i++) {
+		struct msm_dsi_phy *phy = display->ctrl[i].phy;
+
+		if (!phy || !phy->name)
+			continue;
+
+		snprintf(name, ARRAY_SIZE(name),
+				"%s_allow_phy_power_off", phy->name);
+		dump_file = debugfs_create_bool(name, 0600, dir,
+				&phy->allow_phy_power_off);
+		if (IS_ERR_OR_NULL(dump_file)) {
+			rc = PTR_ERR(dump_file);
+			pr_err("[%s] debugfs create %s failed, rc=%d\n",
+			       display->name, name, rc);
+			goto error_remove_dir;
+		}
+	}
+
 	display->root = dir;
 	return rc;
 error_remove_dir:
@@ -937,7 +959,7 @@
 /**
  * dsi_display_phy_idle_on() - enable DSI PHY while coming out of idle screen.
  * @dsi_display:         DSI display handle.
- * @enable:           enable/disable DSI PHY.
+ * @mmss_clamp:          True if clamp is enabled.
  *
  * Return: error code.
  */
@@ -984,7 +1006,6 @@
 /**
  * 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.
  */
@@ -999,9 +1020,16 @@
 		return -EINVAL;
 	}
 
-	if (!display->panel->allow_phy_power_off) {
-		pr_debug("panel doesn't support this feature\n");
-		return 0;
+	for (i = 0; i < display->ctrl_count; i++) {
+		struct msm_dsi_phy *phy = display->ctrl[i].phy;
+
+		if (!phy)
+			continue;
+
+		if (!phy->allow_phy_power_off) {
+			pr_debug("phy doesn't support this feature\n");
+			return 0;
+		}
 	}
 
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index a91dba8..287afda 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -290,6 +290,10 @@
 
 	/* Actual timing values are dependent on panel */
 	timing->count_per_lane = phy->ver_info->timing_cfg_count;
+
+	phy->allow_phy_power_off = of_property_read_bool(pdev->dev.of_node,
+			"qcom,panel-allow-phy-poweroff");
+
 	return 0;
 err:
 	lane->count_per_lane = 0;
@@ -876,6 +880,8 @@
 		return -EINVAL;
 	}
 
+	pr_debug("[%s] enable=%d\n", phy->name, enable);
+
 	mutex_lock(&phy->phy_lock);
 	if (enable) {
 		if (phy->hw.ops.phy_idle_on)
@@ -884,7 +890,17 @@
 		if (phy->hw.ops.regulator_enable)
 			phy->hw.ops.regulator_enable(&phy->hw,
 				&phy->cfg.regulators);
+
+		if (phy->hw.ops.enable)
+			phy->hw.ops.enable(&phy->hw, &phy->cfg);
+
+		phy->dsi_phy_state = DSI_PHY_ENGINE_ON;
 	} else {
+		phy->dsi_phy_state = DSI_PHY_ENGINE_OFF;
+
+		if (phy->hw.ops.disable)
+			phy->hw.ops.disable(&phy->hw, &phy->cfg);
+
 		if (phy->hw.ops.phy_idle_off)
 			phy->hw.ops.phy_idle_off(&phy->hw);
 	}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index e721486..09ed9d6 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -67,6 +67,7 @@
  * @mode:              Current mode.
  * @data_lanes:        Number of data lanes used.
  * @dst_format:        Destination format.
+ * @allow_phy_power_off: True if PHY is allowed to power off when idle
  */
 struct msm_dsi_phy {
 	struct platform_device *pdev;
@@ -88,6 +89,8 @@
 	struct dsi_mode_info mode;
 	enum dsi_data_lanes data_lanes;
 	enum dsi_pixel_format dst_format;
+
+	bool allow_phy_power_off;
 };
 
 /**