Browse Source

Merge "disp: msm: sde: add multi-mode RFI support"

qctecmdr 4 years ago
parent
commit
7d1ca7c295

+ 12 - 0
msm/dsi/dsi_defs.h

@@ -587,6 +587,16 @@ struct dsi_host_config {
 	struct dsi_lane_map lane_map;
 };
 
+/**
+ * struct dyn_clk_list - list of dynamic clock rates.
+ * @rates: list of supported clock rates
+ * @count: number of supported clock rates
+ */
+struct dyn_clk_list {
+	u32 *rates;
+	u32 count;
+};
+
 /**
  * struct dsi_display_mode_priv_info - private mode info that will be attached
  *                             with each drm mode
@@ -600,6 +610,7 @@ struct dsi_host_config {
  * @dsi_transfer_time_us: Specifies the dsi transfer time for cmd panels.
  * @clk_rate_hz:          DSI bit clock per lane in hz.
  * @min_dsi_clk_hz:       Min dsi clk per lane to transfer frame in vsync time.
+ * @bit_clk_list:         List of dynamic bit clock rates supported.
  * @topology:             Topology selected for the panel
  * @dsc:                  DSC compression info
  * @vdc:                  VDC compression info
@@ -623,6 +634,7 @@ struct dsi_display_mode_priv_info {
 	u32 dsi_transfer_time_us;
 	u64 clk_rate_hz;
 	u64 min_dsi_clk_hz;
+	struct dyn_clk_list bit_clk_list;
 
 	struct msm_display_topology topology;
 	struct msm_display_dsc_info dsc;

+ 107 - 12
msm/dsi/dsi_display.c

@@ -6678,12 +6678,11 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display,
 		src = &display->modes[i];
 		if (!src)
 			return;
-		/*
-		 * TODO: currently setting the first bit rate in
-		 * the list as preferred rate. But ideally should
-		 * be based on user or device tree preferrence.
-		 */
-		src->timing.clk_rate_hz = dyn_clk_caps->bit_clk_list[0];
+
+		if (!src->priv_info->bit_clk_list.count)
+			continue;
+
+		src->timing.clk_rate_hz = src->priv_info->bit_clk_list.rates[0];
 
 		dsi_display_adjust_mode_timing(display, src, lanes, bpp);
 
@@ -6726,6 +6725,37 @@ static void _dsi_display_populate_bit_clks(struct dsi_display *display,
 	}
 }
 
+int dsi_display_restore_bit_clk(struct dsi_display *display, struct dsi_display_mode *mode)
+{
+	int i;
+	u32 clk_rate_hz = 0;
+
+	if (!display || !mode || !mode->priv_info) {
+		DSI_ERR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	clk_rate_hz = display->cached_clk_rate;
+
+	if (mode->priv_info->bit_clk_list.count) {
+		/* use first entry as the default bit clk rate */
+		clk_rate_hz = mode->priv_info->bit_clk_list.rates[0];
+
+		for (i = 0; i < mode->priv_info->bit_clk_list.count; i++) {
+			if (display->dyn_bit_clk == mode->priv_info->bit_clk_list.rates[i])
+				clk_rate_hz = display->dyn_bit_clk;
+		}
+	}
+
+	mode->timing.clk_rate_hz = clk_rate_hz;
+	mode->priv_info->clk_rate_hz = clk_rate_hz;
+
+	SDE_EVT32(clk_rate_hz, display->cached_clk_rate, display->dyn_bit_clk);
+	DSI_DEBUG("clk_rate_hz:%u, cached_clk_rate:%u, dyn_bit_clk:%u\n",
+			clk_rate_hz, display->cached_clk_rate, display->dyn_bit_clk);
+	return 0;
+}
+
 void dsi_display_put_mode(struct dsi_display *display,
 	struct dsi_display_mode *mode)
 {
@@ -7237,9 +7267,11 @@ int dsi_display_set_mode(struct dsi_display *display,
 		}
 	}
 
-	/*For dynamic DSI setting, use specified clock rate */
-	if (display->cached_clk_rate > 0)
-		adj_mode.priv_info->clk_rate_hz = display->cached_clk_rate;
+	rc = dsi_display_restore_bit_clk(display, &adj_mode);
+	if (rc) {
+		DSI_ERR("[%s] bit clk rate cannot be restored\n", display->name);
+		goto error;
+	}
 
 	rc = dsi_display_validate_mode_set(display, &adj_mode, flags);
 	if (rc) {
@@ -7253,11 +7285,13 @@ int dsi_display_set_mode(struct dsi_display *display,
 		goto error;
 	}
 
-	DSI_INFO("mdp_transfer_time=%d, hactive=%d, vactive=%d, fps=%d\n",
+	DSI_INFO("mdp_transfer_time=%d, hactive=%d, vactive=%d, fps=%d, clk_rate=%llu\n",
 			adj_mode.priv_info->mdp_transfer_time_us,
-			timing.h_active, timing.v_active, timing.refresh_rate);
+			timing.h_active, timing.v_active, timing.refresh_rate,
+			adj_mode.priv_info->clk_rate_hz);
 	SDE_EVT32(adj_mode.priv_info->mdp_transfer_time_us,
-			timing.h_active, timing.v_active, timing.refresh_rate);
+			timing.h_active, timing.v_active, timing.refresh_rate,
+			adj_mode.priv_info->clk_rate_hz);
 
 	memcpy(display->panel->cur_mode, &adj_mode, sizeof(adj_mode));
 error:
@@ -8366,6 +8400,67 @@ int dsi_display_update_pps(char *pps_cmd, void *disp)
 	return 0;
 }
 
+int dsi_display_update_dyn_bit_clk(struct dsi_display *display,
+			struct dsi_display_mode *mode)
+{
+	struct dsi_dyn_clk_caps *dyn_clk_caps;
+	struct dsi_host_common_cfg *host_cfg;
+	int bpp, lanes = 0;
+
+	if (!display || !mode) {
+		DSI_ERR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	dyn_clk_caps = &(display->panel->dyn_clk_caps);
+	if (!dyn_clk_caps->dyn_clk_support) {
+		DSI_DEBUG("dynamic bit clock support not enabled\n");
+		return 0;
+	} else if (!display->dyn_bit_clk_pending) {
+		DSI_DEBUG("dynamic bit clock rate not updated\n");
+		return 0;
+	} else if (!display->dyn_bit_clk) {
+		DSI_DEBUG("dynamic bit clock rate cleared\n");
+		return 0;
+	} else if (display->dyn_bit_clk < mode->priv_info->min_dsi_clk_hz) {
+		DSI_ERR("dynamic bit clock rate %llu smaller than minimum value:%llu\n",
+				display->dyn_bit_clk, mode->priv_info->min_dsi_clk_hz);
+		return -EINVAL;
+	}
+
+	/* update mode clk rate with user value */
+	mode->timing.clk_rate_hz = display->dyn_bit_clk;
+	mode->priv_info->clk_rate_hz = display->dyn_bit_clk;
+
+	host_cfg = &(display->panel->host_config);
+	bpp = dsi_pixel_format_to_bpp(host_cfg->dst_format);
+
+	if (host_cfg->data_lanes & DSI_DATA_LANE_0)
+		lanes++;
+	if (host_cfg->data_lanes & DSI_DATA_LANE_1)
+		lanes++;
+	if (host_cfg->data_lanes & DSI_DATA_LANE_2)
+		lanes++;
+	if (host_cfg->data_lanes & DSI_DATA_LANE_3)
+		lanes++;
+
+	dsi_display_adjust_mode_timing(display, mode, lanes, bpp);
+
+	/* adjust pixel clock based on dynamic bit clock */
+	mode->pixel_clk_khz = div_u64(mode->timing.clk_rate_hz * lanes, bpp);
+	do_div(mode->pixel_clk_khz, 1000);
+	mode->pixel_clk_khz *= display->ctrl_count;
+
+	SDE_EVT32(display->dyn_bit_clk, mode->priv_info->min_dsi_clk_hz, mode->pixel_clk_khz);
+	DSI_DEBUG("dynamic bit clk:%u, min dsi clk:%llu, lanes:%d, bpp:%d, pck:%d Khz\n",
+			display->dyn_bit_clk, mode->priv_info->min_dsi_clk_hz, lanes, bpp,
+			mode->pixel_clk_khz);
+
+	display->dyn_bit_clk_pending = false;
+
+	return 0;
+}
+
 int dsi_display_dump_clks_state(struct dsi_display *display)
 {
 	int rc = 0;

+ 22 - 1
msm/dsi/dsi_display.h

@@ -154,6 +154,8 @@ struct dsi_display_ext_bridge {
  *		      index into the ctrl[MAX_DSI_CTRLS_PER_DISPLAY] array.
  * @cmd_master_idx:   The master controller for sending DSI commands to panel.
  * @video_master_idx: The master controller for enabling video engine.
+ * @dyn_bit_clk:      The DSI bit clock rate dynamically set by user mode client.
+ * @dyn_bit_clk_pending: Flag indicating the pending DSI dynamic bit clock rate change.
  * @cached_clk_rate:  The cached DSI clock rate set dynamically by sysfs.
  * @clkrate_change_pending: Flag indicating the pending DSI clock re-enabling.
  * @clock_info:       Clock sourcing for DSI display.
@@ -225,7 +227,9 @@ struct dsi_display {
 	u32 video_master_idx;
 
 	/* dynamic DSI clock info*/
-	u32  cached_clk_rate;
+	u32 dyn_bit_clk;
+	bool dyn_bit_clk_pending;
+	u32 cached_clk_rate;
 	atomic_t clkrate_change_pending;
 
 	struct dsi_display_clk_info clock_info;
@@ -776,4 +780,21 @@ int dsi_display_get_panel_vfp(void *display,
  */
 int dsi_display_dump_clks_state(struct dsi_display *display);
 
+/**
+ * dsi_display_update_dyn_bit_clk() - update mode timing to compensate for dynamic bit clock
+ * @display:         Handle to display
+ * @mode:            Mode to be updated
+ * Return: Zero on Success
+ */
+int dsi_display_update_dyn_bit_clk(struct dsi_display *display, struct dsi_display_mode *mode);
+
+/**
+ * dsi_display_restore_bit_clk() - restore mode bit clock rate value from dynamic bit clock
+ * @display:         Handle to display
+ * @mode:            Mode to be updated
+ * Return: Zero on Success
+ */
+int dsi_display_restore_bit_clk(struct dsi_display *display, struct dsi_display_mode *mode);
+
+
 #endif /* _DSI_DISPLAY_H_ */

+ 61 - 31
msm/dsi/dsi_drm.c

@@ -342,7 +342,9 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge,
 				const struct drm_display_mode *mode,
 				const struct drm_display_mode *adjusted_mode)
 {
-	struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
+	int rc = 0;
+	struct dsi_bridge *c_bridge = NULL;
+	struct dsi_display *display;
 	struct drm_connector *conn;
 	struct sde_connector_state *conn_state;
 
@@ -351,6 +353,18 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge,
 		return;
 	}
 
+	c_bridge = to_dsi_bridge(bridge);
+	if (!c_bridge) {
+		DSI_ERR("invalid dsi bridge\n");
+		return;
+	}
+
+	display = c_bridge->display;
+	if (!display || !display->drm_conn || !display->drm_conn->state) {
+		DSI_ERR("invalid display\n");
+		return;
+	}
+
 	memset(&(c_bridge->dsi_mode), 0x0, sizeof(struct dsi_display_mode));
 	convert_to_dsi_mode(adjusted_mode, &(c_bridge->dsi_mode));
 	conn = sde_encoder_get_connector(bridge->dev, bridge->encoder);
@@ -366,9 +380,11 @@ static void dsi_bridge_mode_set(struct drm_bridge *bridge,
 	msm_parse_mode_priv_info(&conn_state->msm_mode,
 					&(c_bridge->dsi_mode));
 
-	/* restore bit_clk_rate also for dynamic clk use cases */
-	c_bridge->dsi_mode.timing.clk_rate_hz =
-		dsi_drm_find_bit_clk_rate(c_bridge->display, adjusted_mode);
+	rc = dsi_display_restore_bit_clk(display, &c_bridge->dsi_mode);
+	if (rc) {
+		DSI_ERR("[%s] bit clk rate cannot be restored\n", display->name);
+		return;
+	}
 
 	DSI_DEBUG("clk_rate: %llu\n", c_bridge->dsi_mode.timing.clk_rate_hz);
 }
@@ -436,6 +452,18 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge,
 	dsi_mode.timing.dsc_enabled = dsi_mode.priv_info->dsc_enabled;
 	dsi_mode.timing.dsc = &dsi_mode.priv_info->dsc;
 
+	rc = dsi_display_restore_bit_clk(display, &dsi_mode);
+	if (rc) {
+		DSI_ERR("[%s] bit clk rate cannot be restored\n", display->name);
+		return false;
+	}
+
+	rc = dsi_display_update_dyn_bit_clk(display, &dsi_mode);
+	if (rc) {
+		DSI_ERR("[%s] failed to update bit clock\n", display->name);
+		return false;
+	}
+
 	rc = dsi_display_validate_mode(c_bridge->display, &dsi_mode,
 			DSI_VALIDATE_FLAG_ALLOW_ADJUST);
 	if (rc) {
@@ -517,33 +545,6 @@ u32 dsi_drm_get_dfps_maxfps(void *display)
 	return dfps_maxfps;
 }
 
-u64 dsi_drm_find_bit_clk_rate(void *display,
-			      const struct drm_display_mode *drm_mode)
-{
-	int i = 0, count = 0;
-	struct dsi_display *dsi_display = display;
-	struct dsi_display_mode *dsi_mode;
-	u64 bit_clk_rate = 0;
-
-	if (!dsi_display || !drm_mode)
-		return 0;
-
-	dsi_display_get_mode_count(dsi_display, &count);
-
-	for (i = 0; i < count; i++) {
-		dsi_mode = &dsi_display->modes[i];
-		if ((dsi_mode->timing.v_active == drm_mode->vdisplay) &&
-		    (dsi_mode->timing.h_active == drm_mode->hdisplay) &&
-		    (dsi_mode->pixel_clk_khz == drm_mode->clock) &&
-		    (dsi_mode->timing.refresh_rate == drm_mode_vrefresh(drm_mode))) {
-			bit_clk_rate = dsi_mode->timing.clk_rate_hz;
-			break;
-		}
-	}
-
-	return bit_clk_rate;
-}
-
 int dsi_conn_get_mode_info(struct drm_connector *connector,
 		const struct drm_display_mode *drm_mode,
 		struct msm_mode_info *mode_info,
@@ -578,6 +579,13 @@ int dsi_conn_get_mode_info(struct drm_connector *connector,
 	memcpy(&mode_info->topology, &dsi_mode->priv_info->topology,
 			sizeof(struct msm_display_topology));
 
+	if (dsi_mode->priv_info->bit_clk_list.count) {
+		mode_info->bit_clk_rates =
+				dsi_mode->priv_info->bit_clk_list.rates;
+		mode_info->bit_clk_count =
+				dsi_mode->priv_info->bit_clk_list.count;
+	}
+
 	if (dsi_mode->priv_info->dsc_enabled) {
 		mode_info->comp_info.comp_type = MSM_DISPLAY_COMPRESSION_DSC;
 		mode_info->topology.comp_type = MSM_DISPLAY_COMPRESSION_DSC;
@@ -1296,3 +1304,25 @@ void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector,
 		mode_idx++;
 	}
 }
+
+int dsi_conn_set_dyn_bit_clk(struct drm_connector *connector, uint64_t value)
+{
+	struct sde_connector *c_conn = NULL;
+	struct dsi_display *display;
+
+	if (!connector) {
+		DSI_ERR("invalid connector\n");
+		return -EINVAL;
+	}
+
+	c_conn = to_sde_connector(connector);
+	display = (struct dsi_display *) c_conn->display;
+
+	display->dyn_bit_clk = value;
+	display->dyn_bit_clk_pending = true;
+
+	SDE_EVT32(display->dyn_bit_clk);
+	DSI_DEBUG("update dynamic bit clock rate to %llu\n", display->dyn_bit_clk);
+
+	return 0;
+}

+ 10 - 4
msm/dsi/dsi_drm.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _DSI_DRM_H_
@@ -134,9 +134,6 @@ int dsi_conn_post_kickoff(struct drm_connector *connector,
 void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
 				struct drm_display_mode *drm_mode);
 
-u64 dsi_drm_find_bit_clk_rate(void *display,
-			      const struct drm_display_mode *drm_mode);
-
 /**
  * dsi_conn_prepare_commit - program pre commit time features
  * @display: Pointer to private display structure
@@ -154,4 +151,13 @@ int dsi_conn_prepare_commit(void *display,
 void dsi_conn_set_allowed_mode_switch(struct drm_connector *connector,
 		void *display);
 
+/**
+ * dsi_conn_set_dyn_bit_clk - set target dynamic clock rate
+ * @connector: Pointer to drm connector structure
+ * @value: Target dynamic clock rate
+ * Returns: Zero on success
+ */
+int dsi_conn_set_dyn_bit_clk(struct drm_connector *connector,
+		uint64_t value);
+
 #endif /* _DSI_DRM_H_ */

+ 42 - 23
msm/dsi/dsi_panel.c

@@ -1326,13 +1326,48 @@ error:
 	return rc;
 }
 
+static int dsi_panel_parse_dyn_clk_list(struct dsi_display_mode *mode,
+		struct dsi_parser_utils *utils)
+{
+	int i, rc = 0;
+	struct dyn_clk_list *bit_clk_list;
+
+	if (!mode || !mode->priv_info) {
+		DSI_ERR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	bit_clk_list = &mode->priv_info->bit_clk_list;
+
+	bit_clk_list->count = utils->count_u32_elems(utils->data, "qcom,dsi-dyn-clk-list");
+	if (bit_clk_list->count < 1)
+		return 0;
+
+	bit_clk_list->rates = kcalloc(bit_clk_list->count, sizeof(u32), GFP_KERNEL);
+	if (!bit_clk_list->rates) {
+		DSI_ERR("failed to allocate space for bit clock list\n");
+		return -ENOMEM;
+	}
+
+	rc = utils->read_u32_array(utils->data, "qcom,dsi-dyn-clk-list",
+			bit_clk_list->rates, bit_clk_list->count);
+	if (rc) {
+		DSI_ERR("failed to parse supported bit clk list, rc=%d\n", rc);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < bit_clk_list->count; i++)
+		DSI_DEBUG("bit clk rate[%d]:%d\n", i, bit_clk_list->rates[i]);
+
+	return 0;
+}
+
 static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel)
 {
 	int rc = 0;
 	bool supported = false;
 	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");
@@ -1342,28 +1377,6 @@ static int dsi_panel_parse_dyn_clk_caps(struct dsi_panel *panel)
 		return rc;
 	}
 
-	dyn_clk_caps->bit_clk_list_len = utils->count_u32_elems(utils->data,
-			"qcom,dsi-dyn-clk-list");
-
-	if (dyn_clk_caps->bit_clk_list_len < 1) {
-		DSI_ERR("[%s] failed to get supported bit clk list\n", name);
-		return -EINVAL;
-	}
-
-	dyn_clk_caps->bit_clk_list = kcalloc(dyn_clk_caps->bit_clk_list_len,
-			sizeof(u32), GFP_KERNEL);
-	if (!dyn_clk_caps->bit_clk_list)
-		return -ENOMEM;
-
-	rc = utils->read_u32_array(utils->data, "qcom,dsi-dyn-clk-list",
-			dyn_clk_caps->bit_clk_list,
-			dyn_clk_caps->bit_clk_list_len);
-
-	if (rc) {
-		DSI_ERR("[%s] failed to parse supported bit clk list\n", name);
-		return -EINVAL;
-	}
-
 	dyn_clk_caps->dyn_clk_support = true;
 
 	type = utils->get_property(utils->data,
@@ -4032,6 +4045,12 @@ int dsi_panel_get_mode(struct dsi_panel *panel,
 			goto parse_fail;
 		}
 
+		if (panel->dyn_clk_caps.dyn_clk_support) {
+			rc = dsi_panel_parse_dyn_clk_list(mode, utils);
+			if (rc)
+				DSI_ERR("failed to parse dynamic clk rates, rc=%d\n", rc);
+		}
+
 		rc = dsi_panel_parse_dsc_params(mode, utils);
 		if (rc) {
 			DSI_ERR("failed to parse dsc params, rc=%d\n", rc);

+ 0 - 2
msm/dsi/dsi_panel.h

@@ -97,8 +97,6 @@ struct dsi_qsync_capabilities {
 
 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;
 };

+ 5 - 0
msm/msm_drv.h

@@ -211,6 +211,7 @@ enum msm_mdp_conn_property {
 	CONNECTOR_PROP_BL_SCALE,
 	CONNECTOR_PROP_SV_BL_SCALE,
 	CONNECTOR_PROP_SUPPORTED_COLORSPACES,
+	CONNECTOR_PROP_DYN_BIT_CLK,
 
 	/* enum/bitmask properties */
 	CONNECTOR_PROP_TOPOLOGY_NAME,
@@ -702,6 +703,8 @@ struct msm_display_topology {
  * @mdp_transfer_time_us   Specifies the mdp transfer time for command mode
  *                         panels in microseconds.
  * @allowed_mode_switches: bit mask to indicate supported mode switch.
+ * @bit_clk_rates: list of supported bit clock rates
+ * @bit_clk_count: number of supported bit clock rates
  */
 struct msm_mode_info {
 	uint32_t frame_rate;
@@ -718,6 +721,8 @@ struct msm_mode_info {
 	u32 panel_mode_caps;
 	u32 mdp_transfer_time_us;
 	u32 allowed_mode_switches;
+	u32 *bit_clk_rates;
+	u32 bit_clk_count;
 };
 
 /**

+ 3 - 0
msm/msm_kms.h

@@ -292,6 +292,9 @@ static inline bool msm_is_private_mode_changed(
 	if (msm_is_mode_seamless_poms(msm_mode))
 		return true;
 
+	if (msm_is_mode_seamless_dyn_clk(msm_mode))
+		return true;
+
 	return false;
 }
 

+ 21 - 0
msm/sde/sde_connector.c

@@ -1595,6 +1595,15 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector,
 			c_conn->expected_panel_mode =
 				MSM_DISPLAY_CMD_MODE;
 		break;
+	case CONNECTOR_PROP_DYN_BIT_CLK:
+		if (!c_conn->ops.set_dyn_bit_clk)
+			break;
+
+		rc = c_conn->ops.set_dyn_bit_clk(connector, val);
+		if (rc)
+			SDE_ERROR_CONN(c_conn, "dynamic bit clock set failed, rc: %d", rc);
+
+		break;
 	default:
 		break;
 	}
@@ -2614,6 +2623,12 @@ static int sde_connector_populate_mode_info(struct drm_connector *conn,
 		sde_kms_info_add_keyint(info, "bit_clk_rate",
 					mode_info.clk_rate);
 
+		if (mode_info.bit_clk_count > 0)
+			sde_kms_info_add_list(info, "dyn_bitclk_list",
+					mode_info.bit_clk_rates,
+					mode_info.bit_clk_count);
+
+
 		topology_idx = (int)sde_rm_get_topology_name(&sde_kms->rm,
 					mode_info.topology);
 		if (topology_idx < SDE_RM_TOPOLOGY_MAX) {
@@ -2788,6 +2803,12 @@ static int _sde_connector_install_properties(struct drm_device *dev,
 				CONNECTOR_PROP_HDR_INFO);
 		}
 
+		if (dsi_display && dsi_display->panel &&
+				dsi_display->panel->dyn_clk_caps.dyn_clk_support)
+			msm_property_install_range(&c_conn->property_info, "dyn_bit_clk",
+					0x0, 0, ~0, 0, CONNECTOR_PROP_DYN_BIT_CLK);
+
+
 		mutex_lock(&c_conn->base.dev->mode_config.mutex);
 		sde_connector_fill_modes(&c_conn->base,
 						dev->mode_config.max_width,

+ 16 - 0
msm/sde/sde_connector.h

@@ -374,6 +374,14 @@ struct sde_connector_ops {
 	void (*set_allowed_mode_switch)(struct drm_connector *connector,
 			void *display);
 
+	/**
+	 * set_dyn_bit_clk - set target dynamic clock rate
+	 * @connector: Pointer to drm connector structure
+	 * @value: Target dynamic clock rate
+	 * Returns: Zero on success
+	 */
+	int (*set_dyn_bit_clk)(struct drm_connector *connector, uint64_t value);
+
 	/**
 	 * get_qsync_min_fps - Get qsync min fps from qsync-min-fps-list
 	 * @display: Pointer to private display structure
@@ -961,6 +969,14 @@ int sde_connector_set_blob_data(struct drm_connector *conn,
  */
 int sde_connector_roi_v1_check_roi(struct drm_connector_state *conn_state);
 
+/**
+ * sde_connector_set_dyn_bit_clk - set dynamic bit clock
+ * @conn: Pointer to drm_connector struct
+ * @value: Property value
+ * Returns: Zero on success
+ */
+int sde_connector_set_dyn_bit_clk(struct drm_connector *conn, uint64_t value);
+
 /**
  * sde_connector_schedule_status_work - manage ESD thread
  * conn: Pointer to drm_connector struct

+ 3 - 0
msm/sde/sde_kms.c

@@ -1736,6 +1736,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
 		.cmd_receive = dsi_display_cmd_receive,
 		.install_properties = NULL,
 		.set_allowed_mode_switch = dsi_conn_set_allowed_mode_switch,
+		.set_dyn_bit_clk = dsi_conn_set_dyn_bit_clk,
 		.get_qsync_min_fps = dsi_display_get_qsync_min_fps,
 		.prepare_commit = dsi_conn_prepare_commit,
 	};
@@ -1756,6 +1757,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
 		.get_panel_vfp = NULL,
 		.cmd_receive = NULL,
 		.install_properties = NULL,
+		.set_dyn_bit_clk = NULL,
 		.set_allowed_mode_switch = NULL,
 	};
 	static const struct sde_connector_ops dp_ops = {
@@ -1778,6 +1780,7 @@ static int _sde_kms_setup_displays(struct drm_device *dev,
 		.cmd_receive = NULL,
 		.install_properties = dp_connector_install_properties,
 		.set_allowed_mode_switch = NULL,
+		.set_dyn_bit_clk = NULL,
 	};
 	struct msm_display_info info;
 	struct drm_encoder *encoder;

+ 10 - 0
msm/sde/sde_kms.h

@@ -611,6 +611,16 @@ void sde_kms_info_append_format(struct sde_kms_info *info,
  */
 void sde_kms_info_stop(struct sde_kms_info *info);
 
+/**
+ * sde_kms_info_add_list - add a space separated list to 'sde_kms_info'
+ * @info: Pointer to sde_kms_info structure
+ * @key: Pointer to key string
+ * @item: Pointer to array of integer values
+ * @size: Number of integers to parse
+ */
+void sde_kms_info_add_list(struct sde_kms_info *info,
+		const char *key, uint32_t *item, size_t size);
+
 /**
  * sde_kms_rect_intersect - intersect two rectangles
  * @r1: first rectangle

+ 23 - 1
msm/sde/sde_kms_utils.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  */
 
 #define pr_fmt(fmt)	"sde-kms_utils:[%s] " fmt, __func__
@@ -91,6 +91,28 @@ void sde_kms_info_append(struct sde_kms_info *info,
 	}
 }
 
+void sde_kms_info_add_list(struct sde_kms_info *info, const char *key, uint32_t *value, size_t size)
+{
+	uint32_t i, len;
+
+	if (!info || !key || !value || !size)
+		return;
+
+	sde_kms_info_start(info, key);
+	for (i = 0; i < size; i++) {
+		len = scnprintf(info->data + info->staged_len,
+				SDE_KMS_INFO_MAX_SIZE - info->len, "%d ",
+				value[i]);
+
+		/* check if snprintf truncated the string */
+		if ((info->staged_len + len) < SDE_KMS_INFO_MAX_SIZE) {
+			info->staged_len += len;
+			info->start = false;
+		}
+	}
+	sde_kms_info_stop(info);
+}
+
 void sde_kms_info_append_format(struct sde_kms_info *info,
 		uint32_t pixel_format,
 		uint64_t modifier)