浏览代码

disp: msm: dsi: Add support for clk switch with constant FPS

There is lag or lead in the FPS during dynamic clock change,
along with the increment or decrement in clock. So, HFP or
VFP are adjusted to ensure a constant FPS.

Change-Id: I87ba7a185104a0f5f1d13734a7e487e728d6b2c0
Signed-off-by: Lipsa Rout <[email protected]>
Signed-off-by: Satya Rama Aditya Pinapala <[email protected]>
Lipsa Rout 5 年之前
父节点
当前提交
5e09ea2aed
共有 4 个文件被更改,包括 143 次插入11 次删除
  1. 16 0
      msm/dsi/dsi_defs.h
  2. 106 11
      msm/dsi/dsi_display.c
  3. 19 0
      msm/dsi/dsi_panel.c
  4. 2 0
      msm/dsi/dsi_panel.h

+ 16 - 0
msm/dsi/dsi_defs.h

@@ -217,6 +217,22 @@ enum dsi_dfps_type {
 	DSI_DFPS_MAX
 };
 
+/**
+ * enum dsi_dyn_clk_feature_type - Dynamic clock feature support type
+ * @DSI_DYN_CLK_TYPE_LEGACY:			Constant FPS is not supported
+ * @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP:	Constant FPS supported with
+ *						change in hfp
+ * @DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP:	Constant FPS supported with
+ *						change in vfp
+ * @DSI_DYN_CLK_TYPE_MAX:
+ */
+enum dsi_dyn_clk_feature_type {
+	DSI_DYN_CLK_TYPE_LEGACY = 0,
+	DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP,
+	DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP,
+	DSI_DYN_CLK_TYPE_MAX
+};
+
 /**
  * enum dsi_cmd_set_type  - DSI command set type
  * @DSI_CMD_SET_PRE_ON:	                   Panel pre on

+ 106 - 11
msm/dsi/dsi_display.c

@@ -4222,6 +4222,7 @@ static int dsi_display_dfps_update(struct dsi_display *display,
 	struct dsi_dfps_capabilities dfps_caps;
 	int rc = 0;
 	int i = 0;
+	struct dsi_dyn_clk_caps *dyn_clk_caps;
 
 	if (!display || !dsi_mode || !display->panel) {
 		DSI_ERR("Invalid params\n");
@@ -4230,8 +4231,9 @@ static int dsi_display_dfps_update(struct dsi_display *display,
 	timing = &dsi_mode->timing;
 
 	dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
-	if (!dfps_caps.dfps_support) {
-		DSI_ERR("dfps not supported\n");
+	dyn_clk_caps = &(display->panel->dyn_clk_caps);
+	if (!dfps_caps.dfps_support && !dyn_clk_caps->maintain_const_fps) {
+		DSI_ERR("dfps or constant fps not supported\n");
 		return -ENOTSUPP;
 	}
 
@@ -4508,7 +4510,32 @@ static int dsi_display_set_mode_sub(struct dsi_display *display,
 					display->name, rc);
 			goto error;
 		}
-	} else if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) {
+		display_for_each_ctrl(i, display) {
+			ctrl = &display->ctrl[i];
+			rc = dsi_ctrl_update_host_config(ctrl->ctrl,
+				&display->config, mode, mode->dsi_mode_flags,
+					display->dsi_clk_handle);
+			if (rc) {
+				DSI_ERR("failed to update ctrl config\n");
+				goto error;
+			}
+		}
+		if (priv_info->phy_timing_len) {
+			display_for_each_ctrl(i, display) {
+				ctrl = &display->ctrl[i];
+				rc = dsi_phy_set_timing_params(ctrl->phy,
+						priv_info->phy_timing_val,
+						priv_info->phy_timing_len,
+						commit_phy_timing);
+				if (rc)
+					DSI_ERR("Fail to add timing params\n");
+			}
+		}
+		if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK))
+			return rc;
+	}
+
+	if (mode->dsi_mode_flags & DSI_MODE_FLAG_DYN_CLK) {
 		if (display->panel->panel_mode == DSI_OP_VIDEO_MODE) {
 			rc = dsi_display_dynamic_clk_switch_vid(display, mode);
 			if (rc)
@@ -5899,6 +5926,51 @@ int dsi_display_get_mode_count(struct dsi_display *display,
 	return 0;
 }
 
+void dsi_display_adjust_mode_timing(
+			struct dsi_dyn_clk_caps *dyn_clk_caps,
+			struct dsi_display_mode *dsi_mode,
+			int lanes, int bpp)
+{
+	u32 new_htotal, new_vtotal, htotal, vtotal, old_htotal;
+
+	if (!dyn_clk_caps->maintain_const_fps)
+		return;
+	/*
+	 * When there is a dynamic clock switch, there is small change
+	 * in FPS. To compensate for this difference in FPS, hfp or vfp
+	 * is adjusted. It has been assumed that the refined porch values
+	 * are supported by the panel. This logic can be enhanced further
+	 * in future by taking min/max porches supported by the panel.
+	 */
+	switch (dyn_clk_caps->type) {
+	case DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP:
+		vtotal = DSI_V_TOTAL(&dsi_mode->timing);
+		old_htotal = dsi_h_total_dce(&dsi_mode->timing);
+		new_htotal = (dsi_mode->timing.clk_rate_hz * lanes);
+		new_htotal /= (bpp * vtotal * dsi_mode->timing.refresh_rate);
+		if (old_htotal > new_htotal)
+			dsi_mode->timing.h_front_porch -=
+					(old_htotal - new_htotal);
+		else
+			dsi_mode->timing.h_front_porch +=
+					(new_htotal - old_htotal);
+		break;
+
+	case DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP:
+		htotal = dsi_h_total_dce(&dsi_mode->timing);
+		new_vtotal = (dsi_mode->timing.clk_rate_hz * lanes);
+		new_vtotal /= (bpp * htotal * dsi_mode->timing.refresh_rate);
+		dsi_mode->timing.v_front_porch = new_vtotal -
+				dsi_mode->timing.v_back_porch -
+				dsi_mode->timing.v_sync_width -
+				dsi_mode->timing.v_active;
+		break;
+
+	default:
+		break;
+	}
+}
+
 static void _dsi_display_populate_bit_clks(struct dsi_display *display,
 					   int start, int end, u32 *mode_idx)
 {
@@ -5938,6 +6010,9 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display,
 		 * be based on user or device tree preferrence.
 		 */
 		src->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[0];
+
+		dsi_display_adjust_mode_timing(dyn_clk_caps, src, lanes, bpp);
+
 		src->pixel_clk_khz =
 			div_u64(src->timing.clk_rate_hz * lanes, bpp);
 		src->pixel_clk_khz /= 1000;
@@ -5957,6 +6032,10 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display,
 			}
 			memcpy(dst, src, sizeof(struct dsi_display_mode));
 			dst->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[i];
+
+			dsi_display_adjust_mode_timing(dyn_clk_caps, dst, lanes,
+									bpp);
+
 			dst->pixel_clk_khz =
 				div_u64(dst->timing.clk_rate_hz * lanes, bpp);
 			dst->pixel_clk_khz /= 1000;
@@ -6251,13 +6330,28 @@ int dsi_display_find_mode(struct dsi_display *display,
 	return rc;
 }
 
+static inline bool dsi_display_mode_switch_dfps(struct dsi_display_mode *cur,
+						struct dsi_display_mode *adj)
+{
+	/*
+	 * If there is a change in the hfp or vfp of the current and adjoining
+	 * mode,then either it is a dfps mode switch or dynamic clk change with
+	 * constant fps.
+	 */
+	if ((cur->timing.h_front_porch != adj->timing.h_front_porch) ||
+		(cur->timing.v_front_porch != adj->timing.v_front_porch))
+		return true;
+	else
+		return false;
+}
+
 /**
  * dsi_display_validate_mode_change() - Validate mode change case.
  * @display:     DSI display handle.
  * @cur_mode:    Current mode.
  * @adj_mode:    Mode to be set.
  *               MSM_MODE_FLAG_SEAMLESS_VRR flag is set if there
- *               is change in fps but vactive and hactive are same.
+ *               is change in hfp or vfp but vactive and hactive are same.
  *               DSI_MODE_FLAG_DYN_CLK flag is set if there
  *               is change in clk but vactive and hactive are same.
  * Return: error code.
@@ -6281,14 +6375,14 @@ int dsi_display_validate_mode_change(struct dsi_display *display,
 	}
 
 	mutex_lock(&display->display_lock);
-
+	dyn_clk_caps = &(display->panel->dyn_clk_caps);
 	if ((cur_mode->timing.v_active == adj_mode->timing.v_active) &&
 		(cur_mode->timing.h_active == adj_mode->timing.h_active)) {
-		/* dfps change use case */
-		if (cur_mode->timing.refresh_rate !=
-		    adj_mode->timing.refresh_rate) {
+		/* dfps and dynamic clock with const fps use case */
+		if (dsi_display_mode_switch_dfps(cur_mode, adj_mode)) {
 			dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
-			if (dfps_caps.dfps_support) {
+			if (dfps_caps.dfps_support ||
+				dyn_clk_caps->maintain_const_fps) {
 				DSI_DEBUG("Mode switch is seamless variable refresh\n");
 				adj_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR;
 				SDE_EVT32(cur_mode->timing.refresh_rate,
@@ -6300,10 +6394,11 @@ int dsi_display_validate_mode_change(struct dsi_display *display,
 
 		/* dynamic clk change use case */
 		if (cur_mode->pixel_clk_khz != adj_mode->pixel_clk_khz) {
-			dyn_clk_caps = &(display->panel->dyn_clk_caps);
 			if (dyn_clk_caps->dyn_clk_support) {
 				DSI_DEBUG("dynamic clk change detected\n");
-				if (adj_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR) {
+				if ((adj_mode->dsi_mode_flags &
+					DSI_MODE_FLAG_VRR) &&
+					(!dyn_clk_caps->maintain_const_fps)) {
 					DSI_ERR("dfps and dyn clk not supported in same commit\n");
 					rc = -ENOTSUPP;
 					goto error;

+ 19 - 0
msm/dsi/dsi_panel.c

@@ -1175,6 +1175,7 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel)
 	struct dsi_dyn_clk_caps *dyn_clk_caps = &panel->dyn_clk_caps;
 	struct dsi_parser_utils *utils = &panel->utils;
 	const char *name = panel->name;
+	const char *type;
 
 	supported = utils->read_bool(utils->data, "qcom,dsi-dyn-clk-enable");
 
@@ -1207,6 +1208,24 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel)
 
 	dyn_clk_caps->dyn_clk_support = true;
 
+	type = utils->get_property(utils->data,
+		"qcom,dsi-dyn-clk-type", NULL);
+	if (!type) {
+		dyn_clk_caps->type = DSI_DYN_CLK_TYPE_LEGACY;
+		dyn_clk_caps->maintain_const_fps = false;
+		return 0;
+	}
+	if (!strcmp(type, "constant-fps-adjust-hfp")) {
+		dyn_clk_caps->type = DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_HFP;
+		dyn_clk_caps->maintain_const_fps = true;
+	} else if (!strcmp(type, "constant-fps-adjust-vfp")) {
+		dyn_clk_caps->type = DSI_DYN_CLK_TYPE_CONST_FPS_ADJUST_VFP;
+		dyn_clk_caps->maintain_const_fps = true;
+	} else {
+		dyn_clk_caps->type = DSI_DYN_CLK_TYPE_LEGACY;
+		dyn_clk_caps->maintain_const_fps = false;
+	}
+	DSI_DEBUG("Dynamic clock type is [%s]\n", type);
 	return 0;
 }
 

+ 2 - 0
msm/dsi/dsi_panel.h

@@ -82,6 +82,8 @@ struct dsi_dyn_clk_caps {
 	bool dyn_clk_support;
 	u32 *bit_clk_list;
 	u32 bit_clk_list_len;
+	enum dsi_dyn_clk_feature_type type;
+	bool maintain_const_fps;
 };
 
 struct dsi_pinctrl_info {