drm: msm: dsi-staging: add support to specify DSI PHY timings in panel dtsi

Some DSI panels require non-standard values of DSI PHY timings for
certain parameters for correct operation. The auto-calculated values
of DSI PHY timings in these cases can show some corruption issues on
the panel. Add support to pass DSI PHY timing parameters from the panel
dtsi in such cases. These value will supersede the PHY timing calculation
logic which is present in the DSI PHY driver.

CRs-Fixed: 2008002
Change-Id: I4a9f7b3352c488070e54f455b30bcc45851f1286
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/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index f6cd2ad..976be99 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -145,6 +145,7 @@
 	phy->ops.phy_idle_off = dsi_phy_hw_v2_0_idle_off;
 	phy->ops.calculate_timing_params =
 		dsi_phy_hw_calculate_timing_params;
+	phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v2_0;
 }
 
 /**
@@ -166,6 +167,7 @@
 		dsi_phy_hw_v3_0_ulps_exit;
 	phy->ops.ulps_ops.get_lanes_in_ulps =
 		dsi_phy_hw_v3_0_get_lanes_in_ulps;
+	phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v3_0;
 }
 
 /**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index 32cdefb..1f0df0a 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -82,6 +82,8 @@
 void dsi_phy_hw_v2_0_disable(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
 void dsi_phy_hw_v2_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
 void dsi_phy_hw_v2_0_idle_off(struct dsi_phy_hw *phy);
+int dsi_phy_hw_timing_val_v2_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+		u32 *timing_val, u32 size);
 
 /* Definitions for 10nm PHY hardware driver */
 void dsi_phy_hw_v3_0_regulator_enable(struct dsi_phy_hw *phy,
@@ -95,6 +97,9 @@
 void dsi_phy_hw_v3_0_ulps_exit(struct dsi_phy_hw *phy,
 			struct dsi_phy_cfg *cfg, u32 lanes);
 u32 dsi_phy_hw_v3_0_get_lanes_in_ulps(struct dsi_phy_hw *phy);
+int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+		u32 *timing_val, u32 size);
+
 /* DSI controller common ops */
 u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
 void dsi_ctrl_hw_cmn_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index e1c6957..e87c3d3 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1698,6 +1698,17 @@
 		goto error_ctrl_put;
 	}
 
+	if (display->panel->phy_timing_len) {
+		for (i = 0; i < display->ctrl_count; i++) {
+			ctrl = &display->ctrl[i];
+			 rc = dsi_phy_set_timing_params(ctrl->phy,
+				display->panel->phy_timing_val,
+				display->panel->phy_timing_len);
+			if (rc)
+				pr_err("failed to add DSI PHY timing params");
+		}
+	}
+
 	rc = dsi_display_parse_lane_map(display);
 	if (rc) {
 		pr_err("Lane map not found, rc=%d\n", rc);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index 87c7dca..fa10b55 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -1562,6 +1562,8 @@
 				struct device_node *of_node)
 {
 	struct dsi_panel *panel;
+	const char *data;
+	u32 len = 0;
 	int rc = 0;
 
 	panel = kzalloc(sizeof(*panel), GFP_KERNEL);
@@ -1579,6 +1581,25 @@
 		goto error;
 	}
 
+	data = of_get_property(of_node,
+		"qcom,mdss-dsi-panel-phy-timings", &len);
+	if (!data) {
+		pr_debug("%s:%d, Unable to read Phy timing settings",
+		       __func__, __LINE__);
+	} else {
+		int i = 0;
+
+		panel->phy_timing_val = kzalloc((sizeof(u32) * len),
+			GFP_KERNEL);
+		if (!panel->phy_timing_val) {
+			kfree(panel);
+			return ERR_PTR(-ENOMEM);
+		}
+		for (i = 0; i < len; i++)
+			panel->phy_timing_val[i] = data[i];
+	}
+	panel->phy_timing_len = len;
+
 	panel->mode.pixel_clk_khz = (DSI_H_TOTAL(&panel->mode.timing) *
 				    DSI_V_TOTAL(&panel->mode.timing) *
 				    panel->mode.timing.refresh_rate) / 1000;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index de79dc7..7b60193 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -162,6 +162,9 @@
 	struct dsi_panel_cmd_set cmd_sets[DSI_CMD_SET_MAX];
 	struct dsi_panel_phy_props phy_props;
 
+	u32 *phy_timing_val;
+	u32 phy_timing_len;
+
 	struct dsi_regulator_info power_info;
 	struct dsi_display_mode mode;
 
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index 6def443..96a98bd 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -807,7 +807,12 @@
 	phy->dst_format = config->common_config.dst_format;
 	phy->cfg.pll_source = pll_source;
 
-	rc = phy->hw.ops.calculate_timing_params(&phy->hw,
+	/**
+	 * If PHY timing parameters are not present in panel dtsi file,
+	 * then calculate them in the driver
+	 */
+	if (!phy->cfg.is_phy_timing_present)
+		rc = phy->hw.ops.calculate_timing_params(&phy->hw,
 						 &phy->mode,
 						 &config->common_config,
 						 &phy->cfg.timing);
@@ -913,11 +918,9 @@
  * Return: error code.
  */
 int dsi_phy_set_timing_params(struct msm_dsi_phy *phy,
-			      u8 *timing, u32 size)
+			      u32 *timing, u32 size)
 {
 	int rc = 0;
-	int i, j;
-	struct dsi_phy_per_lane_cfgs *timing_cfg;
 
 	if (!phy || !timing || !size) {
 		pr_err("Invalid params\n");
@@ -926,18 +929,10 @@
 
 	mutex_lock(&phy->phy_lock);
 
-	if (size != (DSI_LANE_MAX * phy->cfg.timing.count_per_lane)) {
-		pr_err("Unexpected timing array size %d\n", size);
-		rc = -EINVAL;
-	} else {
-		timing_cfg = &phy->cfg.timing;
-		for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
-			for (j = 0; j < timing_cfg->count_per_lane; j++) {
-				timing_cfg->lane[i][j] = *timing;
-				timing++;
-			}
-		}
-	}
+	if (phy->hw.ops.phy_timing_val)
+		rc = phy->hw.ops.phy_timing_val(&phy->cfg.timing, timing, size);
+	if (!rc)
+		phy->cfg.is_phy_timing_present = true;
 	mutex_unlock(&phy->phy_lock);
 	return rc;
 }
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index 471db36..4a64855 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -219,7 +219,7 @@
  * Return: error code.
  */
 int dsi_phy_set_timing_params(struct msm_dsi_phy *phy,
-			      u8 *timing, u32 size);
+			      u32 *timing, u32 size);
 
 /**
  * dsi_phy_drv_register() - register platform driver for dsi phy
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 f5d5d48..daaa78a 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
@@ -17,7 +17,7 @@
 #include "dsi_defs.h"
 
 #define DSI_MAX_SETTINGS 8
-#define DSI_PHY_TIMING_V3 12
+#define DSI_PHY_TIMING_V3_SIZE 12
 
 /**
  * enum dsi_phy_version - DSI PHY version enumeration
@@ -75,7 +75,7 @@
  */
 struct dsi_phy_per_lane_cfgs {
 	u8 lane[DSI_LANE_MAX][DSI_MAX_SETTINGS];
-	u8 lane_v3[DSI_PHY_TIMING_V3];
+	u8 lane_v3[DSI_PHY_TIMING_V3_SIZE];
 	u32 count_per_lane;
 };
 
@@ -84,6 +84,7 @@
  * @lanecfg:          Lane configuration settings.
  * @strength:         Strength settings for lanes.
  * @timing:           Timing parameters for lanes.
+ * @is_phy_timing_present:	Boolean whether phy timings are defined.
  * @regulators:       Regulator settings for lanes.
  * @pll_source:       PLL source.
  * @lane_map:         DSI logical to PHY lane mapping.
@@ -92,6 +93,7 @@
 	struct dsi_phy_per_lane_cfgs lanecfg;
 	struct dsi_phy_per_lane_cfgs strength;
 	struct dsi_phy_per_lane_cfgs timing;
+	bool is_phy_timing_present;
 	struct dsi_phy_per_lane_cfgs regulators;
 	enum dsi_phy_pll_source pll_source;
 	struct dsi_lane_map lane_map;
@@ -215,6 +217,15 @@
 				       struct dsi_host_common_cfg *config,
 				       struct dsi_phy_per_lane_cfgs *timing);
 
+	/**
+	 * phy_timing_val() - Gets PHY timing values.
+	 * @timing_val: Timing parameters for each lane which will be returned.
+	 * @timing: Array containing PHY timing values
+	 * @size: Size of the array
+	 */
+	int (*phy_timing_val)(struct dsi_phy_per_lane_cfgs *timing_val,
+				u32 *timing, u32 size);
+
 	void *timing_ops;
 	struct phy_ulps_config_ops ulps_ops;
 };
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v2_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v2_0.c
index e3e5bf1..9cf542d 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v2_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v2_0.c
@@ -232,3 +232,21 @@
 	pr_debug("[DSI_%d]Phy disabled during idle screen\n", phy->index);
 }
 
+int dsi_phy_hw_timing_val_v2_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+		u32 *timing_val, u32 size)
+{
+	int i = 0, j = 0;
+
+	if (size != (DSI_LANE_MAX * DSI_MAX_SETTINGS)) {
+		pr_err("Unexpected timing array size %d\n", size);
+		return -EINVAL;
+	}
+
+	for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+		for (j = 0; j < DSI_MAX_SETTINGS; j++) {
+			timing_cfg->lane[i][j] = *timing_val;
+			timing_val++;
+		}
+	}
+	return 0;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
index d21fe28..96f5c19 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
@@ -419,3 +419,17 @@
 	return lanes;
 }
 
+int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
+		u32 *timing_val, u32 size)
+{
+	int i = 0;
+
+	if (size != DSI_PHY_TIMING_V3_SIZE) {
+		pr_err("Unexpected timing array size %d\n", size);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < size; i++)
+		timing_cfg->lane_v3[i] = timing_val[i];
+	return 0;
+}