Browse Source

Merge branch 'display-kernel.lnx.5.10' into display-kernel.lnx.1.0

* waipio_base:
  disp: msm: sde: add new support for digital dimming
  disp: msm: sde: trigger tx_wait if panel resolution switch
  disp: msm: reserve core clock rate during display disable
  disp: msm: dsi: reorder DSI registration
  disp: msm: sde: account for pref lm when exposing avail resources
  disp: msm: reset lm blend stages for missing vsync
  disp: msm: dp: set drm mode clock same as clock value from EDID
  disp: msm: sde: set top left coordinates for noise and attenuation layers
  disp: msm: dp: disable ASSR before link training
  disp: msm: dp: retry the request to set USB mode during bootup
  disp: msm: sde: clear intf mux select on slave encoders
  disp: msm: retry dma buf attach on msm_gem_delayed_import error
  disp: msm: dp: check for aux abort in sim mode
  disp: msm: dsi: add qsync min fps val in dsi display mode priv info
  disp: msm: dp: read DPCD registers using debugfs
  disp: msm: dsi: mark signature for stub appropriately
  disp: msm: dp: check for DP stream during audio teardown
  display: msm: sde: reduce dbg mem usage for tui vm

Change-Id: I285e4557ad258f17fc2948b478198b9b81c18276
Jeykumar Sankaran 3 years ago
parent
commit
754552cfc4

+ 1 - 0
config/gki_waipiodisptui.conf

@@ -7,3 +7,4 @@ export CONFIG_QCOM_MDSS_PLL=y
 export CONFIG_DRM_MSM_REGISTER_LOGGING=y
 export CONFIG_DISPLAY_BUILD=m
 export CONFIG_DRM_SDE_VM=y
+export CONFIG_DRM_LOW_MSM_MEM_FOOTPRINT=y

+ 1 - 0
config/gki_waipiodisptuiconf.h

@@ -13,3 +13,4 @@
 #define CONFIG_QCOM_MDSS_PLL 1
 #define CONFIG_GKI_DISPLAY 1
 #define CONFIG_DRM_SDE_VM 1
+#define CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT 1

+ 4 - 0
include/uapi/display/drm/msm_drm_pp.h

@@ -719,6 +719,8 @@ struct drm_msm_fp16_csc {
 	__u32 cfg_param_1[FP16_CSC_CFG1_PARAM_LEN];
 };
 
+#define DIMMING_ENABLE (1 << 0)
+#define DIMMING_MIN_BL_VALID (1 << 1)
 struct drm_msm_backlight_info {
 	__u32 brightness_max;
 	__u32 brightness;
@@ -726,6 +728,8 @@ struct drm_msm_backlight_info {
 	__u32 bl_level;
 	__u32 bl_scale;
 	__u32 bl_scale_sv;
+	__u32 status;
+	__u32 min_bl;
 };
 
 #define DIMMING_BL_LUT_LEN 8192

+ 2 - 2
msm/dp/dp_altmode.c

@@ -43,7 +43,7 @@ enum dp_altmode_pin_assignment {
 
 static int dp_altmode_set_usb_dp_mode(struct dp_altmode_private *altmode)
 {
-	int rc;
+	int rc = 0;
 	struct device_node *np;
 	struct device_node *usb_node;
 	struct platform_device *usb_pdev;
@@ -71,7 +71,7 @@ static int dp_altmode_set_usb_dp_mode(struct dp_altmode_private *altmode)
 
 	while (timeout) {
 		rc = dwc3_msm_set_dp_mode(&usb_pdev->dev, altmode->connected, altmode->lanes);
-		if (rc != -EBUSY)
+		if (rc != -EBUSY && rc != -EAGAIN)
 			break;
 
 		DP_WARN("USB busy, retry\n");

+ 6 - 0
msm/dp/dp_audio.c

@@ -505,6 +505,12 @@ static void dp_audio_teardown_done(struct platform_device *pdev)
 		return;
 	}
 
+	if (audio->panel->stream_id >= DP_STREAM_MAX) {
+		DP_WARN("invalid stream id: %d\n",
+				audio->panel->stream_id);
+		return;
+	}
+
 	mutex_lock(&audio->ops_lock);
 	dp_audio_enable(audio, false);
 	mutex_unlock(&audio->ops_lock);

+ 10 - 1
msm/dp/dp_aux.c

@@ -560,6 +560,15 @@ static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
 	struct dp_aux_private *aux = container_of(drm_aux,
 			struct dp_aux_private, drm_aux);
 	ssize_t size;
+	int aborted;
+
+	mutex_lock(&aux->mutex);
+	aborted = atomic_read(&aux->aborted);
+	mutex_unlock(&aux->mutex);
+	if (aborted) {
+		size = -ETIMEDOUT;
+		goto end;
+	}
 
 	if (aux->sim_in_transfer) {
 		if (aux->aux_bridge && aux->aux_bridge->transfer)
@@ -572,7 +581,7 @@ static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
 				drm_aux, msg);
 		aux->sim_in_transfer = false;
 	}
-
+end:
 	return size;
 }
 

+ 6 - 1
msm/dp/dp_catalog.c

@@ -1084,7 +1084,12 @@ static void dp_catalog_ctrl_config_ctrl(struct dp_catalog_ctrl *ctrl, u8 ln_cnt)
 	io_data = catalog->io.dp_link;
 
 	cfg = dp_read(DP_CONFIGURATION_CTRL);
-	cfg &= ~(BIT(4) | BIT(5));
+	/*
+	 * Reset ASSR (alternate scrambler seed reset) by resetting BIT(10).
+	 * ASSR should be set to disable for TPS4 link training pattern.
+	 * Forcing it to 0 as the power on reset value of register enables it.
+	 */
+	cfg &= ~(BIT(4) | BIT(5) | BIT(10));
 	cfg |= (ln_cnt - 1) << 4;
 	dp_write(DP_CONFIGURATION_CTRL, cfg);
 

+ 16 - 10
msm/dp/dp_debug.c

@@ -212,7 +212,7 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
 
 	size = min_t(size_t, count, SZ_2K);
 
-	if (size <= 4)
+	if (size < 4)
 		goto bail;
 
 	buf = kzalloc(size, GFP_KERNEL);
@@ -231,9 +231,10 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
 		DP_ERR("offset kstrtoint error\n");
 		goto bail;
 	}
+	debug->dpcd_offset = offset;
 
 	size -= 4;
-	if (size == 0)
+	if (size < char_to_nib)
 		goto bail;
 
 	dpcd_size = size / char_to_nib;
@@ -277,7 +278,6 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
 
 	dp_sim_write_dpcd_reg(debug->sim_bridge,
 			dpcd, dpcd_buf_index, offset);
-	debug->dpcd_offset = offset;
 	debug->dpcd_size = dpcd_buf_index;
 
 bail:
@@ -315,21 +315,27 @@ static ssize_t dp_debug_read_dpcd(struct file *file,
 
 	/*
 	 * In simulation mode, this function returns the last written DPCD node.
-	 * For a real monitor plug in, it always dumps the first 16 DPCD registers.
+	 * For a real monitor plug in, it dumps the first byte at the last written DPCD address
+	 * unless the address is 0, in which case the first 20 bytes are dumped
 	 */
 	if (debug->dp_debug.sim_mode) {
 		dp_sim_read_dpcd_reg(debug->sim_bridge, dpcd, debug->dpcd_size, debug->dpcd_offset);
 	} else {
-		debug->dpcd_size = sizeof(debug->panel->dpcd);
-		debug->dpcd_offset = 0;
-		memcpy(dpcd, debug->panel->dpcd, debug->dpcd_size);
+		if (debug->dpcd_offset) {
+			debug->dpcd_size = 1;
+			if (drm_dp_dpcd_read(debug->aux->drm_aux, debug->dpcd_offset, dpcd,
+					debug->dpcd_size) != 1)
+				goto bail;
+		} else {
+			debug->dpcd_size = sizeof(debug->panel->dpcd);
+			memcpy(dpcd, debug->panel->dpcd, debug->dpcd_size);
+		}
 	}
 
-	len += scnprintf(buf, buf_size, "%04x: ", debug->dpcd_offset);
+	len += scnprintf(buf + len , buf_size - len, "%04x: ", debug->dpcd_offset);
 
 	while (offset < debug->dpcd_size)
-		len += scnprintf(buf + len, buf_size - len, "%02x ",
-				dpcd[debug->dpcd_offset + offset++]);
+		len += scnprintf(buf + len, buf_size - len, "%02x ", dpcd[offset++]);
 
 	kfree(dpcd);
 

+ 1 - 2
msm/dp/dp_drm.c

@@ -40,8 +40,7 @@ void convert_to_drm_mode(const struct dp_display_mode *dp_mode,
 			      dp_mode->timing.v_sync_width;
 	drm_mode->vtotal = drm_mode->vsync_end + dp_mode->timing.v_back_porch;
 
-	drm_mode->clock = drm_mode->htotal * drm_mode->vtotal * dp_mode->timing.refresh_rate;
-	drm_mode->clock /= 1000;
+	drm_mode->clock = dp_mode->timing.pixel_clk_khz;
 
 	if (dp_mode->timing.h_active_low)
 		flags |= DRM_MODE_FLAG_NHSYNC;

+ 2 - 0
msm/dsi/dsi_defs.h

@@ -611,6 +611,7 @@ struct dsi_host_config {
  * @mdp_transfer_time_us:   Specifies the mdp transfer time for command mode
  *                          panels in microseconds.
  * @dsi_transfer_time_us: Specifies the dsi transfer time for cmd panels.
+ * @qsync_min_fps:        Qsync min fps value for the mode
  * @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.
@@ -636,6 +637,7 @@ struct dsi_display_mode_priv_info {
 	u32 panel_prefill_lines;
 	u32 mdp_transfer_time_us;
 	u32 dsi_transfer_time_us;
+	u32 qsync_min_fps;
 	u64 clk_rate_hz;
 	u64 min_dsi_clk_hz;
 	struct msm_dyn_clk_list bit_clk_list;

+ 12 - 5
msm/dsi/dsi_display.c

@@ -249,7 +249,7 @@ int dsi_display_set_backlight(struct drm_connector *connector,
 	/* use bl_temp as index of dimming bl lut to find the dimming panel backlight */
 	if (bl_temp != 0 && panel->bl_config.dimming_bl_lut &&
 	    bl_temp < panel->bl_config.dimming_bl_lut->length) {
-		pr_debug("before dimming bl_temp = %u, after dimming bl_temp = %lu\n",
+		DSI_DEBUG("before dimming bl_temp = %u, after dimming bl_temp = %lu\n",
 			bl_temp, panel->bl_config.dimming_bl_lut->mapped_bl[bl_temp]);
 		bl_temp = panel->bl_config.dimming_bl_lut->mapped_bl[bl_temp];
 	}
@@ -257,10 +257,10 @@ int dsi_display_set_backlight(struct drm_connector *connector,
 	if (bl_temp > panel->bl_config.bl_max_level)
 		bl_temp = panel->bl_config.bl_max_level;
 
-        if (bl_temp && (bl_temp < panel->bl_config.bl_min_level))
-                bl_temp = panel->bl_config.bl_min_level;
+	if (bl_temp && (bl_temp < panel->bl_config.bl_min_level))
+		bl_temp = panel->bl_config.bl_min_level;
 
-	pr_debug("bl_scale = %u, bl_scale_sv = %u, bl_lvl = %u\n",
+	DSI_DEBUG("bl_scale = %u, bl_scale_sv = %u, bl_lvl = %u\n",
 		bl_scale, bl_scale_sv, (u32)bl_temp);
 
 	rc = dsi_panel_set_backlight(panel, (u32)bl_temp);
@@ -6978,6 +6978,11 @@ int dsi_display_get_modes(struct dsi_display *display,
 			if (!sub_mode->timing.qsync_min_fps && qsync_caps->qsync_min_fps)
 				sub_mode->timing.qsync_min_fps = qsync_caps->qsync_min_fps;
 
+			/*
+			 * Qsync min fps for the mode will be populated in the timing info
+			 * in dsi_panel_get_mode function.
+			 */
+			sub_mode->priv_info->qsync_min_fps = sub_mode->timing.qsync_min_fps;
 			if (!dfps_caps.dfps_support || !support_video_mode)
 				continue;
 
@@ -6986,8 +6991,10 @@ int dsi_display_get_modes(struct dsi_display *display,
 			sub_mode->timing.refresh_rate = dfps_caps.dfps_list[i];
 
 			/* Override with qsync min fps list in dfps usecases */
-			if (qsync_caps->qsync_min_fps && qsync_caps->qsync_min_fps_list_len)
+			if (qsync_caps->qsync_min_fps && qsync_caps->qsync_min_fps_list_len) {
 				sub_mode->timing.qsync_min_fps = qsync_caps->qsync_min_fps_list[i];
+				sub_mode->priv_info->qsync_min_fps = sub_mode->timing.qsync_min_fps;
+			}
 
 			dsi_display_get_dfps_timing(display, sub_mode,
 					curr_refresh_rate);

+ 10 - 19
msm/dsi/dsi_drm.c

@@ -706,30 +706,21 @@ int dsi_conn_set_avr_step_info(struct dsi_panel *panel, void *info)
 	return 0;
 }
 
-int dsi_conn_get_qsync_min_fps(void *display_dsi, struct drm_connector_state *conn_state)
+int dsi_conn_get_qsync_min_fps(struct drm_connector_state *conn_state)
 {
-	struct dsi_display *display = (struct dsi_display *)display_dsi;
-	int rc = 0;
-	struct dsi_display_mode partial_dsi_mode, *dsi_mode;
-	struct msm_sub_mode new_sub_mode;
-	struct sde_connector_state *sde_conn_state;
-	struct drm_display_mode *drm_mode;
+	struct sde_connector_state *sde_conn_state = to_sde_connector_state(conn_state);
+	struct msm_display_mode *msm_mode;
+	struct dsi_display_mode_priv_info *priv_info;
 
-	if (!display || !display->drm_conn || !conn_state)
+	if (!sde_conn_state)
 		return -EINVAL;
 
-	sde_conn_state = to_sde_connector_state(conn_state);
-	drm_mode = sde_conn_state->msm_mode.base;
-	convert_to_dsi_mode(drm_mode, &partial_dsi_mode);
-	new_sub_mode.dsc_mode = sde_connector_get_property(conn_state, CONNECTOR_PROP_DSC_MODE);
-
-	rc = dsi_display_find_mode(display, &partial_dsi_mode, &new_sub_mode, &dsi_mode);
-	if (rc) {
-		DSI_ERR("invalid mode\n");
-		return rc;
-	}
+	msm_mode = &sde_conn_state->msm_mode;
+	if (!msm_mode || !msm_mode->private)
+		return -EINVAL;
 
-	return dsi_mode->timing.qsync_min_fps;
+	priv_info = (struct dsi_display_mode_priv_info *)(msm_mode->private);
+	return priv_info->qsync_min_fps;
 }
 
 int dsi_conn_set_info_blob(struct drm_connector *connector,

+ 1 - 2
msm/dsi/dsi_drm.h

@@ -174,10 +174,9 @@ void dsi_conn_set_submode_blob_info(struct drm_connector *conn,
 
 /**
  * dsi_conn_get_qsync_min_fps() - get qsync min fps for given fps
- * @display:            Handle to display.
  * @conn_state:         Pointer to drm_connector_state structure
  *
  * Return: Qsync min fps rate or -ve error code.
  */
-int dsi_conn_get_qsync_min_fps(void *dsi_display, struct drm_connector_state *conn_state);
+int dsi_conn_get_qsync_min_fps(struct drm_connector_state *conn_state);
 #endif /* _DSI_DRM_H_ */

+ 3 - 0
msm/dsi/dsi_panel.c

@@ -2445,6 +2445,9 @@ static int dsi_panel_parse_bl_config(struct dsi_panel *panel)
 
 	panel->bl_config.bl_scale = MAX_BL_SCALE_LEVEL;
 	panel->bl_config.bl_scale_sv = MAX_SV_BL_SCALE_LEVEL;
+	panel->bl_config.dimming_min_bl = 0;
+	panel->bl_config.dimming_status = DIMMING_ENABLE;
+	panel->bl_config.user_disable_notification = false;
 
 	rc = utils->read_u32(utils->data, "qcom,mdss-dsi-bl-min-level", &val);
 	if (rc) {

+ 4 - 0
msm/dsi/dsi_panel.h

@@ -13,6 +13,7 @@
 #include <linux/backlight.h>
 #include <drm/drm_panel.h>
 #include <drm/msm_drm.h>
+#include <drm/msm_drm_pp.h>
 
 #include "dsi_defs.h"
 #include "dsi_ctrl_hw.h"
@@ -134,6 +135,9 @@ struct dsi_backlight_config {
 	bool bl_inverted_dbv;
 	/* digital dimming backlight LUT */
 	struct drm_msm_dimming_bl_lut *dimming_bl_lut;
+	u32 dimming_min_bl;
+	u32 dimming_status;
+	bool user_disable_notification;
 
 	int en_gpio;
 	/* PWM params */

+ 1 - 1
msm/dsi/dsi_parser.h

@@ -92,7 +92,7 @@ static inline int dsi_parser_read_u32(const struct device_node *np,
 	return -ENODEV;
 }
 
-int dsi_parser_read_u32_index(const struct device_node *np,
+static inline int dsi_parser_read_u32_index(const struct device_node *np,
 			const char *propname, u32 index, u32 *out_value)
 {
 	return -ENODEV;

+ 5 - 4
msm/msm_drv.c

@@ -2188,21 +2188,21 @@ static int __init msm_drm_register(void)
 	DBG("init");
 	sde_rsc_rpmh_register();
 	sde_rsc_register();
+	msm_smmu_driver_init();
+	sde_wb_register();
+	platform_driver_register(&msm_platform_driver);
 	dsi_display_register();
 	msm_hdcp_register();
 	dp_display_register();
-	msm_smmu_driver_init();
 	msm_dsi_register();
 	msm_edp_register();
 	msm_hdmi_register();
-	sde_wb_register();
-	return platform_driver_register(&msm_platform_driver);
+	return 0;
 }
 
 static void __exit msm_drm_unregister(void)
 {
 	DBG("fini");
-	platform_driver_unregister(&msm_platform_driver);
 	sde_wb_unregister();
 	msm_hdmi_unregister();
 	msm_edp_unregister();
@@ -2214,6 +2214,7 @@ static void __exit msm_drm_unregister(void)
 	dp_display_unregister();
 	dsi_display_unregister();
 	sde_rsc_unregister();
+	platform_driver_unregister(&msm_platform_driver);
 }
 
 module_init(msm_drm_register);

+ 2 - 0
msm/msm_drv.h

@@ -216,6 +216,8 @@ enum msm_mdp_conn_property {
 	CONNECTOR_PROP_SV_BL_SCALE,
 	CONNECTOR_PROP_SUPPORTED_COLORSPACES,
 	CONNECTOR_PROP_DYN_BIT_CLK,
+	CONNECTOR_PROP_DIMMING_CTRL,
+	CONNECTOR_PROP_DIMMING_MIN_BL,
 
 	/* enum/bitmask properties */
 	CONNECTOR_PROP_TOPOLOGY_NAME,

+ 1 - 0
msm/msm_gem.c

@@ -481,6 +481,7 @@ static int msm_gem_get_iova_locked(struct drm_gem_object *obj,
 			if (ret) {
 				DRM_ERROR("delayed dma-buf import failed %d\n",
 						ret);
+				msm_obj->obj_dirty = true;
 				return ret;
 			}
 		}

+ 100 - 5
msm/sde/sde_connector.c

@@ -106,7 +106,10 @@ static void sde_dimming_bl_notify(struct sde_connector *conn, struct dsi_backlig
 
 	if (!conn || !config)
 		return;
-	if (!conn->dimming_bl_notify_enabled)
+
+	SDE_DEBUG("bl_config.dimming_status 0x%x user_disable_notify %d\n",
+		  config->dimming_status, config->user_disable_notification);
+	if (!conn->dimming_bl_notify_enabled || config->user_disable_notification)
 		return;
 
 	bl_info.brightness_max = config->brightness_max_level;
@@ -115,10 +118,13 @@ static void sde_dimming_bl_notify(struct sde_connector *conn, struct dsi_backlig
 	bl_info.bl_level = config->bl_level;
 	bl_info.bl_scale = config->bl_scale;
 	bl_info.bl_scale_sv = config->bl_scale_sv;
+	bl_info.status = config->dimming_status;
+	bl_info.min_bl = config->dimming_min_bl;
 	event.type = DRM_EVENT_DIMMING_BL;
 	event.length = sizeof(bl_info);
-	SDE_DEBUG("dimming BL event bl_level %d bl_scale %d, bl_scale_sv = %d\n",
-		  bl_info.bl_level, bl_info.bl_scale, bl_info.bl_scale_sv);
+	SDE_DEBUG("dimming BL event bl_level %d bl_scale %d, bl_scale_sv = %d "
+		  "min_bl %d status 0x%x\n", bl_info.bl_level, bl_info.bl_scale,
+		  bl_info.bl_scale_sv, bl_info.min_bl, bl_info.status);
 	msm_mode_object_event_notify(&conn->base.base, conn->base.dev, &event, (u8 *)&bl_info);
 }
 
@@ -181,7 +187,8 @@ static int sde_backlight_device_update_status(struct backlight_device *bd)
 		}
 		rc = c_conn->ops.set_backlight(&c_conn->base,
 				c_conn->display, bl_lvl);
-		sde_dimming_bl_notify(c_conn, &display->panel->bl_config);
+		if (!rc)
+			sde_dimming_bl_notify(c_conn, &display->panel->bl_config);
 		c_conn->unset_bl_level = 0;
 	}
 
@@ -693,6 +700,7 @@ static int _sde_connector_update_dimming_bl_lut(struct sde_connector *c_conn,
 	size_t sz = 0;
 	struct dsi_display *dsi_display;
 	struct dsi_backlight_config *bl_config;
+	int rc = 0;
 
 	if (!c_conn || !c_state) {
 		SDE_ERROR("invalid arguments\n");
@@ -716,6 +724,81 @@ static int _sde_connector_update_dimming_bl_lut(struct sde_connector *c_conn,
 	bl_config = &dsi_display->panel->bl_config;
 	bl_config->dimming_bl_lut = msm_property_get_blob(&c_conn->property_info,
 			&c_state->property_state, &sz, CONNECTOR_PROP_DIMMING_BL_LUT);
+	rc = c_conn->ops.set_backlight(&c_conn->base,
+			dsi_display, bl_config->bl_level);
+	if (!rc)
+		c_conn->unset_bl_level = 0;
+
+	return 0;
+}
+
+static int _sde_connector_update_dimming_ctrl(struct sde_connector *c_conn,
+		struct sde_connector_state *c_state, uint64_t val)
+{
+	struct dsi_display *dsi_display;
+	struct dsi_backlight_config *bl_config;
+	bool prev, curr = (bool)val;
+
+	if (!c_conn || !c_state) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	dsi_display = c_conn->display;
+	if (!dsi_display || !dsi_display->panel) {
+		SDE_ERROR("Invalid params(s) dsi_display %pK, panel %pK\n",
+			dsi_display,
+			((dsi_display) ? dsi_display->panel : NULL));
+		return -EINVAL;
+	}
+
+	bl_config = &dsi_display->panel->bl_config;
+	prev = (bool)(bl_config->dimming_status & DIMMING_ENABLE);
+	if (curr == prev)
+		return 0;
+
+	if(val) {
+		bl_config->dimming_status |= DIMMING_ENABLE;
+	} else {
+		bl_config->dimming_status &= ~DIMMING_ENABLE;
+	}
+	bl_config->user_disable_notification = false;
+	sde_dimming_bl_notify(c_conn, bl_config);
+	if (!val)
+		bl_config->user_disable_notification = true;
+
+	return 0;
+}
+
+static int _sde_connector_update_dimming_min_bl(struct sde_connector *c_conn,
+		struct sde_connector_state *c_state, uint64_t val)
+{
+	struct dsi_display *dsi_display;
+	struct dsi_backlight_config *bl_config;
+	uint32_t tmp = 0;
+
+	if (!c_conn || !c_state) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	dsi_display = c_conn->display;
+	if (!dsi_display || !dsi_display->panel) {
+		SDE_ERROR("Invalid params(s) dsi_display %pK, panel %pK\n",
+			dsi_display,
+			((dsi_display) ? dsi_display->panel : NULL));
+		return -EINVAL;
+	}
+
+	bl_config = &dsi_display->panel->bl_config;
+	tmp = (uint32_t)val;
+	if (tmp == bl_config->dimming_min_bl)
+		return 0;
+	bl_config->dimming_min_bl = tmp;
+	bl_config->dimming_status |= DIMMING_MIN_BL_VALID;
+	sde_dimming_bl_notify(c_conn, bl_config);
+	bl_config->dimming_status &= ~DIMMING_MIN_BL_VALID;
+
 	return 0;
 }
 
@@ -1673,6 +1756,12 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector,
 	case CONNECTOR_PROP_DIMMING_BL_LUT:
 		rc = _sde_connector_update_dimming_bl_lut(c_conn, c_state);
 		break;
+	case CONNECTOR_PROP_DIMMING_CTRL:
+		rc = _sde_connector_update_dimming_ctrl(c_conn, c_state, val);
+		break;
+	case CONNECTOR_PROP_DIMMING_MIN_BL:
+		rc = _sde_connector_update_dimming_min_bl(c_conn, c_state, val);
+		break;
 	case CONNECTOR_PROP_HDR_METADATA:
 		rc = _sde_connector_set_ext_hdr_info(c_conn,
 			c_state, (void *)(uintptr_t)val);
@@ -2911,10 +3000,16 @@ static int _sde_connector_install_properties(struct drm_device *dev,
 
 	if (connector_type == DRM_MODE_CONNECTOR_DSI) {
 		dsi_display = (struct dsi_display *)(display);
-		if (dsi_display && dsi_display->panel)
+		if (dsi_display && dsi_display->panel) {
 			msm_property_install_blob(&c_conn->property_info,
 				"dimming_bl_lut", DRM_MODE_PROP_BLOB,
 				CONNECTOR_PROP_DIMMING_BL_LUT);
+			msm_property_install_range(&c_conn->property_info, "dimming_dyn_ctrl",
+					0x0, 0, ~0, 0, CONNECTOR_PROP_DIMMING_CTRL);
+			msm_property_install_range(&c_conn->property_info, "dimming_min_bl",
+					0x0, 0, dsi_display->panel->bl_config.brightness_max_level, 0,
+					CONNECTOR_PROP_DIMMING_MIN_BL);
+		}
 
 		if (dsi_display && dsi_display->panel &&
 			dsi_display->panel->hdr_props.hdr_enabled == true) {

+ 1 - 2
msm/sde/sde_connector.h

@@ -399,11 +399,10 @@ struct sde_connector_ops {
 
 	/**
 	 * get_qsync_min_fps - Get qsync min fps from qsync-min-fps-list
-	 * @display: Pointer to private display structure
 	 * @conn_state: Pointer to drm_connector_state structure
 	 * Returns: Qsync min fps value on success
 	 */
-	int (*get_qsync_min_fps)(void *display, struct drm_connector_state *conn_state);
+	int (*get_qsync_min_fps)(struct drm_connector_state *conn_state);
 
 	/**
 	 * get_avr_step_req - Get the required avr_step for given fps rate

+ 25 - 0
msm/sde/sde_core_perf.c

@@ -829,6 +829,31 @@ void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc)
 	}
 }
 
+void sde_core_perf_crtc_reserve_res(struct drm_crtc *crtc, u64 reserve_rate)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_kms *kms;
+	struct msm_drm_private *priv;
+
+	if (!crtc) {
+		SDE_ERROR("invalid crtc\n");
+		return;
+	}
+
+	/* use current perf, which are the values voted */
+	sde_crtc = to_sde_crtc(crtc);
+	kms = _sde_crtc_get_kms(crtc);
+	priv = kms->dev->dev_private;
+
+	kms->perf.core_clk_reserve_rate = max(kms->perf.core_clk_reserve_rate, reserve_rate);
+	kms->perf.core_clk_reserve_rate = min(kms->perf.core_clk_reserve_rate,
+			kms->perf.max_core_clk_rate);
+	sde_power_clk_reserve_rate(&priv->phandle, kms->perf.clk_name,
+			kms->perf.core_clk_reserve_rate);
+
+	SDE_DEBUG("reserve clk:%llu\n", kms->perf.core_clk_reserve_rate);
+}
+
 static u64 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms)
 {
 	u64 clk_rate = kms->perf.perf_tune.min_core_clk;

+ 10 - 1
msm/sde/sde_core_perf.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 _SDE_CORE_PERF_H_
@@ -78,6 +78,7 @@ struct sde_core_perf_tune {
  * @uidle_enabled: indicates if uidle is already enabled
  * @idle_sys_cache_enabled: override system cache enable state
  *                          for idle usecase
+ * @core_clk_reserve_rate: reserve core clk rate for built-in display
  */
 struct sde_core_perf {
 	struct drm_device *dev;
@@ -99,6 +100,7 @@ struct sde_core_perf {
 	bool llcc_active[SDE_SYS_CACHE_MAX];
 	bool uidle_enabled;
 	bool idle_sys_cache_enabled;
+	u64 core_clk_reserve_rate;
 };
 
 /**
@@ -131,6 +133,13 @@ void sde_core_perf_crtc_update(struct drm_crtc *crtc,
  */
 void sde_core_perf_crtc_release_bw(struct drm_crtc *crtc);
 
+/**
+ * sde_core_perf_reserve_res - reserve core clock resource for built-in displays.
+ * @crtc: Pointer to crtc
+ * @reserve_rate: core clock rate for built-in display
+ */
+void sde_core_perf_crtc_reserve_res(struct drm_crtc *crtc, u64 reserve_rate);
+
 /**
  * sde_core_perf_crtc_update_uidle - attempts to enable uidle of the given crtc
  * @crtc: Pointer to crtc

+ 43 - 4
msm/sde/sde_crtc.c

@@ -3979,7 +3979,7 @@ int sde_crtc_reset_hw(struct drm_crtc *crtc, struct drm_crtc_state *old_state,
 			continue;
 
 		if (sde_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
-			sde_encoder_kickoff(encoder, false, true);
+			sde_encoder_kickoff(encoder, true);
 	}
 
 	/* panic the device if VBIF is not in good state */
@@ -4086,7 +4086,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
 		if (encoder->crtc != crtc)
 			continue;
 
-		sde_encoder_kickoff(encoder, false, true);
+		sde_encoder_kickoff(encoder, true);
 	}
 	sde_crtc->kickoff_in_progress = false;
 
@@ -4160,6 +4160,43 @@ static int _sde_crtc_vblank_enable(
 	return 0;
 }
 
+static void _sde_crtc_reserve_resource(struct drm_crtc *crtc, struct drm_connector *conn)
+{
+	u32 min_transfer_time = 0, lm_count = 1;
+	u64 mode_clock_hz = 0, updated_fps = 0, topology_id;
+	struct drm_encoder *encoder;
+
+	if (!crtc || !conn)
+		return;
+
+	encoder = conn->state->best_encoder;
+	if (!sde_encoder_is_built_in_display(encoder))
+		return;
+
+	if (sde_encoder_check_curr_mode(encoder, MSM_DISPLAY_CMD_MODE))
+		sde_encoder_get_transfer_time(encoder, &min_transfer_time);
+
+	if (min_transfer_time)
+		updated_fps = DIV_ROUND_UP(1000000, min_transfer_time);
+	else
+		updated_fps = drm_mode_vrefresh(&crtc->mode);
+
+	topology_id = sde_connector_get_topology_name(conn);
+	if (TOPOLOGY_DUALPIPE_MODE(topology_id))
+		lm_count = 2;
+	else if (TOPOLOGY_QUADPIPE_MODE(topology_id))
+		lm_count = 4;
+
+	/* mode clock = [(h * v * fps * 1.05) / (num_lm)] */
+	mode_clock_hz = mult_frac(crtc->mode.htotal * crtc->mode.vtotal * updated_fps, 105, 100);
+	mode_clock_hz = div_u64(mode_clock_hz, lm_count);
+	SDE_DEBUG("[%s] h=%d v=%d fps=%d lm=%d mode_clk=%u\n",
+			crtc->mode.name, crtc->mode.htotal, crtc->mode.vtotal,
+			updated_fps, lm_count, mode_clock_hz);
+
+	sde_core_perf_crtc_reserve_res(crtc, mode_clock_hz);
+}
+
 /**
  * sde_crtc_duplicate_state - state duplicate hook
  * @crtc: Pointer to drm crtc structure
@@ -4666,8 +4703,10 @@ static void sde_crtc_enable(struct drm_crtc *crtc,
 		sde_crtc_handle_power_event, crtc, sde_crtc->name);
 
 	/* Enable ESD thread */
-	for (i = 0; i < cstate->num_connectors; i++)
+	for (i = 0; i < cstate->num_connectors; i++) {
 		sde_connector_schedule_status_work(cstate->connectors[i], true);
+		_sde_crtc_reserve_resource(crtc, cstate->connectors[i]);
+	}
 }
 
 /* no input validation - caller API has all the checks */
@@ -7061,7 +7100,7 @@ void __sde_crtc_static_cache_read_work(struct kthread_work *work)
 		sde_plane_ctl_flush(plane, ctl, true);
 
 	/* kickoff encoder and wait for VBLANK */
-	sde_encoder_kickoff(drm_enc, false, false);
+	sde_encoder_kickoff(drm_enc, false);
 	sde_encoder_wait_for_event(drm_enc, MSM_ENC_VBLANK);
 
 	SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);

+ 57 - 60
msm/sde/sde_encoder.c

@@ -342,6 +342,15 @@ bool sde_encoder_is_primary_display(struct drm_encoder *drm_enc)
 		SDE_CONNECTOR_PRIMARY);
 }
 
+bool sde_encoder_is_built_in_display(struct drm_encoder *drm_enc)
+{
+	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+
+	return sde_enc &&
+		(sde_enc->disp_info.display_type == SDE_CONNECTOR_PRIMARY ||
+		sde_enc->disp_info.display_type == SDE_CONNECTOR_SECONDARY);
+}
+
 bool sde_encoder_is_dsi_display(struct drm_encoder *drm_enc)
 {
 	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
@@ -1053,7 +1062,7 @@ static void _sde_encoder_get_qsync_fps_callback(struct drm_encoder *drm_enc,
 
 	sde_conn = to_sde_connector(sde_enc->cur_master->connector);
 	if (sde_conn->ops.get_qsync_min_fps)
-		rc = sde_conn->ops.get_qsync_min_fps(sde_conn->display, conn_state);
+		rc = sde_conn->ops.get_qsync_min_fps(conn_state);
 
 	if (rc < 0) {
 		SDE_ERROR("invalid qsync min fps %d\n", rc);
@@ -2430,17 +2439,33 @@ static void _sde_encoder_virt_populate_hw_res(struct drm_encoder *drm_enc)
 }
 
 static int sde_encoder_virt_modeset_rc(struct drm_encoder *drm_enc,
-		struct msm_display_mode *msm_mode, bool pre_modeset)
+      struct drm_display_mode *adj_mode, struct msm_display_mode *msm_mode, bool pre_modeset)
 {
 	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
 	enum sde_intf_mode intf_mode;
+	struct drm_display_mode *old_adj_mode = NULL;
 	int ret;
-	bool is_cmd_mode = false;
+	bool is_cmd_mode = false, res_switch = false;
 
 	if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE))
 		is_cmd_mode = true;
 
 	if (pre_modeset) {
+		if (sde_enc->cur_master)
+			old_adj_mode = &sde_enc->cur_master->cached_mode;
+		if (old_adj_mode && is_cmd_mode)
+			res_switch = !drm_mode_match(old_adj_mode, adj_mode, DRM_MODE_MATCH_TIMINGS);
+
+		if (res_switch) {
+			/* avoid early tear check reconfigure for resolution switch */
+			ret = sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
+			if (ret && ret != -EWOULDBLOCK) {
+				SDE_ERROR_ENC(sde_enc, "wait for idle failed %d\n", ret);
+				SDE_EVT32(DRMID(drm_enc), ret, SDE_EVTLOG_ERROR);
+				return ret;
+			}
+		}
+
 		intf_mode = sde_encoder_get_intf_mode(drm_enc);
 		if (msm_is_mode_seamless_dms(msm_mode) ||
 				(msm_is_mode_seamless_dyn_clk(msm_mode) &&
@@ -2545,7 +2570,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
 
 	/* release resources before seamless mode change */
 	msm_mode = &c_state->msm_mode;
-	ret = sde_encoder_virt_modeset_rc(drm_enc, msm_mode, true);
+	ret = sde_encoder_virt_modeset_rc(drm_enc, adj_mode, msm_mode, true);
 	if (ret)
 		return;
 
@@ -2584,7 +2609,7 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
 	}
 
 	/* update resources after seamless mode change */
-	sde_encoder_virt_modeset_rc(drm_enc, msm_mode, false);
+	sde_encoder_virt_modeset_rc(drm_enc, adj_mode, msm_mode, false);
 }
 
 void sde_encoder_control_te(struct drm_encoder *drm_enc, bool enable)
@@ -3163,10 +3188,13 @@ void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc,
 	struct sde_encoder_virt *sde_enc;
 	struct sde_hw_ctl *ctl = phys_enc->hw_ctl;
 	struct sde_ctl_flush_cfg cfg;
+	int i;
 
 	ctl->ops.reset(ctl);
 	sde_encoder_helper_reset_mixers(phys_enc, NULL);
 
+	sde_enc = to_sde_encoder_virt(phys_enc->parent);
+
 	if (wb_enc) {
 		if (wb_enc->hw_wb->ops.bind_pingpong_blk) {
 			wb_enc->hw_wb->ops.bind_pingpong_blk(wb_enc->hw_wb,
@@ -3177,14 +3205,16 @@ void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc,
 						wb_enc->hw_wb->idx, true);
 		}
 	} else {
-		if (phys_enc->hw_intf->ops.bind_pingpong_blk) {
-			phys_enc->hw_intf->ops.bind_pingpong_blk(
-					phys_enc->hw_intf, false,
-					phys_enc->hw_pp->idx);
-
-			if (ctl->ops.update_bitmask)
-				ctl->ops.update_bitmask(ctl, SDE_HW_FLUSH_INTF,
-						phys_enc->hw_intf->idx, true);
+		for (i = 0; i < sde_enc->num_phys_encs; i++) {
+			if (sde_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk) {
+				phys_enc->hw_intf->ops.bind_pingpong_blk(
+						sde_enc->phys_encs[i]->hw_intf, false,
+						sde_enc->phys_encs[i]->hw_pp->idx);
+
+				if (ctl->ops.update_bitmask)
+					ctl->ops.update_bitmask(ctl, SDE_HW_FLUSH_INTF,
+							sde_enc->phys_encs[i]->hw_intf->idx, true);
+			}
 		}
 	}
 
@@ -3206,8 +3236,6 @@ void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc,
 					phys_enc->hw_cdm->idx, true);
 	}
 
-	sde_enc = to_sde_encoder_virt(phys_enc->parent);
-
 	if (phys_enc == sde_enc->cur_master && phys_enc->hw_pp &&
 			ctl->ops.reset_post_disable)
 		ctl->ops.reset_post_disable(ctl, &phys_enc->intf_cfg_v1,
@@ -3222,6 +3250,19 @@ void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc,
 	ctl->ops.clear_pending_flush(ctl);
 }
 
+void sde_encoder_helper_phys_reset(struct sde_encoder_phys *phys_enc)
+{
+	struct sde_hw_ctl *ctl = phys_enc->hw_ctl;
+	struct sde_ctl_flush_cfg cfg;
+
+	ctl->ops.reset(ctl);
+	sde_encoder_helper_reset_mixers(phys_enc, NULL);
+	ctl->ops.get_pending_flush(ctl, &cfg);
+	SDE_EVT32(DRMID(phys_enc->parent), cfg.pending_flush_mask);
+	ctl->ops.trigger_flush(ctl);
+	ctl->ops.trigger_start(ctl);
+}
+
 static enum sde_intf sde_encoder_get_intf(struct sde_mdss_cfg *catalog,
 		enum sde_intf_type type, u32 controller_id)
 {
@@ -4354,47 +4395,7 @@ end:
 	return ret;
 }
 
-/**
- * _sde_encoder_reset_ctl_hw - reset h/w configuration for all ctl's associated
- *	with the specified encoder, and unstage all pipes from it
- * @encoder:	encoder pointer
- * Returns: 0 on success
- */
-static int _sde_encoder_reset_ctl_hw(struct drm_encoder *drm_enc)
-{
-	struct sde_encoder_virt *sde_enc;
-	struct sde_encoder_phys *phys;
-	unsigned int i;
-	int rc = 0;
-
-	if (!drm_enc) {
-		SDE_ERROR("invalid encoder\n");
-		return -EINVAL;
-	}
-
-	sde_enc = to_sde_encoder_virt(drm_enc);
-
-	SDE_ATRACE_BEGIN("encoder_release_lm");
-	SDE_DEBUG_ENC(sde_enc, "\n");
-
-	for (i = 0; i < sde_enc->num_phys_encs; i++) {
-		phys = sde_enc->phys_encs[i];
-		if (!phys)
-			continue;
-
-		SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0);
-
-		rc = sde_encoder_helper_reset_mixers(phys, NULL);
-		if (rc)
-			SDE_EVT32(DRMID(drm_enc), rc, SDE_EVTLOG_ERROR);
-	}
-
-	SDE_ATRACE_END("encoder_release_lm");
-	return rc;
-}
-
-void sde_encoder_kickoff(struct drm_encoder *drm_enc, bool is_error,
-		bool config_changed)
+void sde_encoder_kickoff(struct drm_encoder *drm_enc, bool config_changed)
 {
 	struct sde_encoder_virt *sde_enc;
 	struct sde_encoder_phys *phys;
@@ -4409,10 +4410,6 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc, bool is_error,
 
 	SDE_DEBUG_ENC(sde_enc, "\n");
 
-	/* create a 'no pipes' commit to release buffers on errors */
-	if (is_error)
-		_sde_encoder_reset_ctl_hw(drm_enc);
-
 	if (sde_enc->delay_kickoff) {
 		u32 loop_count = 20;
 		u32 sleep = DELAY_KICKOFF_POLL_TIMEOUT_US / loop_count;

+ 11 - 6
msm/sde/sde_encoder.h

@@ -325,12 +325,9 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *encoder);
  * sde_encoder_kickoff - trigger a double buffer flip of the ctl path
  *	(i.e. ctl flush and start) immediately.
  * @encoder:	encoder pointer
- * @is_error:	whether the current commit needs to be aborted and replaced
- *		with a 'safe' commit
  * @config_changed: if true new configuration is applied on the control path
  */
-void sde_encoder_kickoff(struct drm_encoder *encoder, bool is_error,
-		bool config_changed);
+void sde_encoder_kickoff(struct drm_encoder *encoder, bool config_changed);
 
 /**
  * sde_encoder_wait_for_event - Waits for encoder events
@@ -515,15 +512,23 @@ bool sde_encoder_is_cwb_disabling(struct drm_encoder *drm_enc,
  * sde_encoder_is_primary_display - checks if underlying display is primary
  *     display or not.
  * @drm_enc:    Pointer to drm encoder structure
- * @Return:     true if it is primary display. false if secondary display
+ * @Return:     true if it is primary display. false otherwise
  */
 bool sde_encoder_is_primary_display(struct drm_encoder *enc);
 
+/**
+ * sde_encoder_is_built_in_display - checks if underlying display is built in
+ *     display or not.
+ * @drm_enc:    Pointer to drm encoder structure
+ * @Return:     true if it is a built in display. false otherwise
+ */
+bool sde_encoder_is_built_in_display(struct drm_encoder *enc);
+
 /**
  * sde_encoder_is_dsi_display - checks if underlying display is DSI
  *     display or not.
  * @drm_enc:    Pointer to drm encoder structure
- * @Return:     true if it is primary display. false if secondary display
+ * @Return:     true if it is a dsi display. false otherwise
  */
 bool sde_encoder_is_dsi_display(struct drm_encoder *enc);
 

+ 7 - 0
msm/sde/sde_encoder_phys.h

@@ -804,6 +804,13 @@ static inline bool sde_encoder_phys_needs_single_flush(
 void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc,
 		struct sde_encoder_phys_wb *wb_enc);
 
+/**
+ * sde_encoder_helper_phys_reset - helper function to reset virt encoder
+ *                 if vsync is missing on phys encoder
+ * @phys_enc: Pointer to physical encoder structure
+ */
+void sde_encoder_helper_phys_reset(struct sde_encoder_phys *phys_enc);
+
 /**
  * sde_encoder_helper_setup_misr - helper function to setup misr
  * @phys_enc: Pointer to physical encoder structure

+ 13 - 1
msm/sde/sde_encoder_phys_vid.c

@@ -920,6 +920,18 @@ static int sde_encoder_phys_vid_wait_for_vblank(
 	return _sde_encoder_phys_vid_wait_for_vblank(phys_enc, true);
 }
 
+static int sde_encoder_phys_vid_wait_for_commit_done(
+		struct sde_encoder_phys *phys_enc)
+{
+	int rc;
+
+	rc =  _sde_encoder_phys_vid_wait_for_vblank(phys_enc, true);
+	if (rc)
+		sde_encoder_helper_phys_reset(phys_enc);
+
+	return rc;
+}
+
 static int sde_encoder_phys_vid_wait_for_vblank_no_notify(
 		struct sde_encoder_phys *phys_enc)
 {
@@ -1310,7 +1322,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
 	ops->destroy = sde_encoder_phys_vid_destroy;
 	ops->get_hw_resources = sde_encoder_phys_vid_get_hw_resources;
 	ops->control_vblank_irq = sde_encoder_phys_vid_control_vblank_irq;
-	ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_vblank;
+	ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done;
 	ops->wait_for_vblank = sde_encoder_phys_vid_wait_for_vblank_no_notify;
 	ops->wait_for_tx_complete = sde_encoder_phys_vid_wait_for_vblank;
 	ops->irq_control = sde_encoder_phys_vid_irq_control;

+ 4 - 0
msm/sde/sde_hw_lm.c

@@ -337,6 +337,8 @@ static int sde_hw_lm_setup_noise_layer(struct sde_hw_mixer *ctx,
 	SDE_REG_WRITE(c, LM_BLEND0_CONST_ALPHA + stage_off, alpha);
 	val = ctx->cfg.out_width | (ctx->cfg.out_height << 16);
 	SDE_REG_WRITE(c, LM_FG_COLOR_FILL_SIZE + stage_off, val);
+	/* partial update is not supported in noise layer */
+	SDE_REG_WRITE(c, LM_FG_COLOR_FILL_XY + stage_off, 0);
 	val = SDE_REG_READ(c, LM_OP_MODE);
 	val = (1 << cfg->noise_blend_stage) | val;
 	SDE_REG_WRITE(c, LM_OP_MODE, val);
@@ -359,6 +361,8 @@ static int sde_hw_lm_setup_noise_layer(struct sde_hw_mixer *ctx,
 	SDE_REG_WRITE(c, LM_OP_MODE, val);
 	val = ctx->cfg.out_width | (ctx->cfg.out_height << 16);
 	SDE_REG_WRITE(c, LM_FG_COLOR_FILL_SIZE + stage_off, val);
+	/* partial update is not supported in noise layer */
+	SDE_REG_WRITE(c, LM_FG_COLOR_FILL_XY + stage_off, 0);
 
 	val = 1;
 	if (mixer->right_mixer)

+ 23 - 2
msm/sde/sde_rm.c

@@ -277,19 +277,40 @@ void sde_rm_get_resource_info(struct sde_rm *rm,
 	struct sde_rm_hw_blk *blk;
 	enum sde_hw_blk_type type;
 	struct sde_rm_rsvp rsvp;
+	const struct sde_lm_cfg *lm_cfg;
+	bool is_built_in, is_pref;
+	u32 lm_pref = (BIT(SDE_DISP_PRIMARY_PREF) | BIT(SDE_DISP_SECONDARY_PREF));
 
+	/* Get all currently available resources */
 	memcpy(avail_res, &rm->avail_res,
 			sizeof(rm->avail_res));
 
 	if (!drm_enc)
 		return;
 
+	is_built_in = sde_encoder_is_built_in_display(drm_enc);
+
 	rsvp.enc_id = drm_enc->base.id;
 
-	for (type = 0; type < SDE_HW_BLK_MAX; type++)
-		list_for_each_entry(blk, &rm->hw_blks[type], list)
+	for (type = 0; type < SDE_HW_BLK_MAX; type++) {
+		list_for_each_entry(blk, &rm->hw_blks[type], list) {
+			/* Add back resources allocated to the given encoder */
 			if (blk->rsvp && blk->rsvp->enc_id == rsvp.enc_id)
 				_sde_rm_inc_resource_info(rm, avail_res, blk);
+
+			/**
+			 * Remove unallocated preferred lms that cannot reserved
+			 * by non built-in displays.
+			 */
+			if (type == SDE_HW_BLK_LM) {
+				lm_cfg = to_sde_hw_mixer(blk->hw)->cap;
+				is_pref = lm_cfg->features & lm_pref;
+
+				if (!blk->rsvp && !is_built_in && is_pref)
+					_sde_rm_dec_resource_info(rm, avail_res, blk);
+			}
+		}
+	}
 }
 
 static void _sde_rm_print_rsvps(

+ 4 - 4
msm/sde_dbg.h

@@ -119,11 +119,11 @@ enum sde_dbg_dump_context {
  * entry array access.
  */
 
-#if IS_ENABLED(CONFIG_DRM_SDE_VM)
+#if IS_ENABLED(CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT)
 #define SDE_EVTLOG_ENTRY	(SDE_EVTLOG_PRINT_ENTRY * 8)
 #else
 #define SDE_EVTLOG_ENTRY	(SDE_EVTLOG_PRINT_ENTRY * 32)
-#endif /* IS_ENABLED(CONFIG_DRM_SDE_VM) */
+#endif /* IS_ENABLED(CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT) */
 
 #define SDE_EVTLOG_MAX_DATA 15
 #define SDE_EVTLOG_BUF_MAX 512
@@ -169,11 +169,11 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
  * number must be greater than number of possible writes in at least one
  * single commit.
  */
-#if IS_ENABLED(CONFIG_DRM_SDE_VM)
+#if IS_ENABLED(CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT)
 #define SDE_REGLOG_ENTRY 256
 #else
 #define SDE_REGLOG_ENTRY 1024
-#endif /* IS_ENABLED(CONFIG_DRM_SDE_VM) */
+#endif /* IS_ENABLED(CONFIG_DRM_MSM_LOW_MEM_FOOTPRINT) */
 
 struct sde_dbg_reglog_log {
 	s64 time;

+ 46 - 0
msm/sde_power_handle.c

@@ -800,6 +800,32 @@ void sde_power_resource_deinit(struct platform_device *pdev,
 		sde_rsc_client_destroy(phandle->rsc_client);
 }
 
+static void sde_power_mmrm_reserve(struct sde_power_handle *phandle)
+{
+	int i;
+	struct dss_module_power *mp = &phandle->mp;
+	u64 rate = phandle->mmrm_reserve.clk_rate;
+
+	if (!phandle->mmrm_enable)
+		return;
+
+	for (i = 0; i < mp->num_clk; i++) {
+		if (!strcmp(mp->clk_config[i].clk_name, phandle->mmrm_reserve.clk_name)) {
+			if (mp->clk_config[i].max_rate)
+				rate = min(rate, (u64)mp->clk_config[i].max_rate);
+
+			mp->clk_config[i].rate = rate;
+			mp->clk_config[i].mmrm.flags =
+				MMRM_CLIENT_DATA_FLAG_RESERVE_ONLY;
+
+			SDE_ATRACE_BEGIN("sde_clk_set_rate");
+			msm_dss_single_clk_set_rate(&mp->clk_config[i]);
+			SDE_ATRACE_END("sde_clk_set_rate");
+			break;
+		}
+	}
+}
+
 int sde_power_scale_reg_bus(struct sde_power_handle *phandle,
 	u32 usecase_ndx, bool skip_lock)
 {
@@ -897,6 +923,7 @@ int sde_power_resource_enable(struct sde_power_handle *phandle, bool enable)
 		SDE_EVT32_VERBOSE(enable, SDE_EVTLOG_FUNC_CASE2);
 		sde_power_rsc_update(phandle, false);
 
+		sde_power_mmrm_reserve(phandle);
 		msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
 
 		sde_power_scale_reg_bus(phandle, VOTE_INDEX_DISABLE, true);
@@ -936,6 +963,25 @@ vreg_err:
 	return rc;
 }
 
+int sde_power_clk_reserve_rate(struct sde_power_handle *phandle, char *clock_name, u64 rate)
+{
+	if (!phandle) {
+		pr_err("invalid input power handle\n");
+		return -EINVAL;
+	} else if (!phandle->mmrm_enable) {
+		pr_debug("mmrm disabled, return early\n");
+		return 0;
+	}
+
+	mutex_lock(&phandle->phandle_lock);
+	phandle->mmrm_reserve.clk_rate = rate;
+	strlcpy(phandle->mmrm_reserve.clk_name, clock_name,
+			sizeof(phandle->mmrm_reserve.clk_name));
+	mutex_unlock(&phandle->phandle_lock);
+
+	return 0;
+}
+
 int sde_power_clk_set_rate(struct sde_power_handle *phandle, char *clock_name,
 	u64 rate, u32 flags)
 {

+ 23 - 0
msm/sde_power_handle.h

@@ -147,6 +147,16 @@ struct sde_power_event {
 	bool active;
 };
 
+/*
+ * struct sde_power_mmrm_reserve - mmrm resource reservation for clk and bw
+ * @clk_name: name of resource reservation clock
+ * @clk_rate: resource reservation clock rate
+ */
+struct sde_power_mmrm_reserve {
+	char clk_name[32];
+	u64 clk_rate;
+};
+
 /**
  * struct sde_power_handle: power handle main struct
  * @mp:		module power for clock and regulator
@@ -159,6 +169,7 @@ struct sde_power_event {
  * @rsc_client_init: boolean to control rsc client create
  * @mmrm_enable: boolean to indicate if mmrm is enabled
  * @ib_quota: ib quota of the given bus
+ * @mmrm_reserve: mmrm resource reservation
  */
 struct sde_power_handle {
 	struct dss_module_power mp;
@@ -173,6 +184,8 @@ struct sde_power_handle {
 	bool rsc_client_init;
 	bool mmrm_enable;
 	u64 ib_quota[SDE_POWER_HANDLE_DBUS_ID_MAX];
+
+	struct sde_power_mmrm_reserve mmrm_reserve;
 };
 
 /**
@@ -226,6 +239,16 @@ int sde_power_scale_reg_bus(struct sde_power_handle *phandle,
 int sde_power_data_bus_state_update(struct sde_power_handle *phandle,
 							bool enable);
 
+/**
+ * sde_power_clk_reserve_rate() - reserve the clock rate with mmrm
+ * @pdata:  power handle containing the resources
+ * @clock_name: clock name which needs rate update.
+ * @rate:       Requested rate.
+ *
+ * Return: error code.
+ */
+int sde_power_clk_reserve_rate(struct sde_power_handle *pdata, char *clock_name, u64 rate);
+
 /**
  * sde_power_clk_set_rate() - set the clock rate
  * @pdata:  power handle containing the resources