Sfoglia il codice sorgente

disp: msm: sde: add all frame-trigger modes support for writeback

Currently, writeback frame-triggers are serialized by default. Add
logic to support the different frame-trigger modes which can be set
through the connector property or encoder debugfs node.

- default: waits for frame(N-1) completion (wb-done-irq) before
  configuring current frame(N) and releases the commit-thread on
  frame-start (ctl-start-irq)
- posted-start: no previous frame(N-1) completion wait. Configures
  frame(N) and releases the commit-thread on frame-start (ctl-start-irq)
- serialize: no previous frame(N-1) completion wait. Configures frame(N)
  and releases the commit-thread on frame(N) completion (wb-done-irq)
  (wb-done-irq) before configuring the next frame.

Restrict wb posted-start support only for MDSS 9.x+ targets, with older
targets defaulted to default-mode.

Change-Id: Id441378fd79ecbfcfb820da1ff23b14ccfd8e798
Signed-off-by: Veera Sundaram Sankaran <[email protected]>
Veera Sundaram Sankaran 4 anni fa
parent
commit
0cf7ba9a4a
4 ha cambiato i file con 178 aggiunte e 130 eliminazioni
  1. 6 7
      msm/sde/sde_connector.c
  2. 10 9
      msm/sde/sde_encoder.c
  3. 9 4
      msm/sde/sde_encoder_phys.h
  4. 153 110
      msm/sde/sde_encoder_phys_wb.c

+ 6 - 7
msm/sde/sde_connector.c

@@ -3099,13 +3099,6 @@ static int _sde_connector_install_properties(struct drm_device *dev,
 		msm_property_install_enum(&c_conn->property_info, "dsc_mode", 0,
 			0, e_dsc_mode, ARRAY_SIZE(e_dsc_mode), 0, CONNECTOR_PROP_DSC_MODE);
 
-		if (display_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)
-			msm_property_install_enum(&c_conn->property_info,
-				"frame_trigger_mode", 0, 0,
-				e_frame_trigger_mode,
-				ARRAY_SIZE(e_frame_trigger_mode), 0,
-				CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE);
-
 		if (display_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE &&
 			display_info->capabilities & MSM_DISPLAY_CAP_VID_MODE)
 			msm_property_install_enum(&c_conn->property_info,
@@ -3126,6 +3119,12 @@ static int _sde_connector_install_properties(struct drm_device *dev,
 		}
 	}
 
+	if ((display_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)
+			|| (connector_type == DRM_MODE_CONNECTOR_VIRTUAL))
+		msm_property_install_enum(&c_conn->property_info, "frame_trigger_mode",
+			0, 0, e_frame_trigger_mode, ARRAY_SIZE(e_frame_trigger_mode), 0,
+			CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE);
+
 	msm_property_install_range(&c_conn->property_info, "bl_scale",
 		0x0, 0, MAX_BL_SCALE_LEVEL, MAX_BL_SCALE_LEVEL,
 		CONNECTOR_PROP_BL_SCALE);

+ 10 - 9
msm/sde/sde_encoder.c

@@ -3603,8 +3603,10 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
 	/* update pending counts and trigger kickoff ctl flush atomically */
 	spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
 
-	if (phys->ops.is_master && phys->ops.is_master(phys) && config_changed)
+	if (phys->ops.is_master && phys->ops.is_master(phys) && config_changed) {
 		atomic_inc(&phys->pending_retire_fence_cnt);
+		atomic_inc(&phys->pending_ctl_start_cnt);
+	}
 
 	pend_ret_fence_cnt = atomic_read(&phys->pending_retire_fence_cnt);
 
@@ -4280,7 +4282,7 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
 		struct sde_encoder_kickoff_params *params)
 {
 	struct sde_encoder_virt *sde_enc;
-	struct sde_encoder_phys *phys;
+	struct sde_encoder_phys *phys, *cur_master;
 	struct sde_kms *sde_kms = NULL;
 	struct sde_crtc *sde_crtc;
 	bool needs_hw_reset = false, is_cmd_mode;
@@ -4303,13 +4305,12 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
 	SDE_DEBUG_ENC(sde_enc, "\n");
 	SDE_EVT32(DRMID(drm_enc));
 
-	is_cmd_mode = sde_encoder_check_curr_mode(drm_enc,
-				MSM_DISPLAY_CMD_MODE);
-	if (sde_enc->cur_master && sde_enc->cur_master->connector
-			&& is_cmd_mode)
-		sde_enc->frame_trigger_mode = sde_connector_get_property(
-			sde_enc->cur_master->connector->state,
-			CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE);
+	cur_master = sde_enc->cur_master;
+	is_cmd_mode = sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE);
+	if (cur_master && cur_master->connector)
+		sde_enc->frame_trigger_mode =
+				sde_connector_get_property(cur_master->connector->state,
+					CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE);
 
 	_sde_encoder_helper_hdr_plus_mempool_update(sde_enc);
 

+ 9 - 4
msm/sde/sde_encoder_phys.h

@@ -286,6 +286,10 @@ struct sde_encoder_irq {
  *				scheduled. Decremented in irq handler
  * @pending_retire_fence_cnt:   Atomic counter tracking the pending retire
  *                              fences that have to be signalled.
+ * @pending_ctl_start_cnt:      Atomic counter tracking the pending ctl-start-irq,
+ *                              used to release commit thread. Currently managed
+ *                              only for writeback encoder and the counter keeps
+ *                              increasing for other type of encoders.
  * @pending_kickoff_wq:		Wait queue for blocking until kickoff completes
  * @kickoff_timeout_ms:		kickoff timeout in mill seconds
  * @irq:			IRQ tracking structures
@@ -333,6 +337,7 @@ struct sde_encoder_phys {
 	atomic_t underrun_cnt;
 	atomic_t pending_kickoff_cnt;
 	atomic_t pending_retire_fence_cnt;
+	atomic_t pending_ctl_start_cnt;
 	wait_queue_head_t pending_kickoff_wq;
 	u32 kickoff_timeout_ms;
 	struct sde_encoder_irq irq[INTR_IDX_MAX];
@@ -423,8 +428,8 @@ struct sde_encoder_phys_cmd {
  * @wb_fmt:		Writeback pixel format
  * @wb_fb:		Pointer to current writeback framebuffer
  * @wb_aspace:		Pointer to current writeback address space
- * @cwb_old_fb:		Pointer to old writeback framebuffer
- * @cwb_old_aspace:	Pointer to old writeback address space
+ * @old_fb:		Pointer to old writeback framebuffer
+ * @old_aspace:		Pointer to old writeback address space
  * @aspace:		address space identifier for non-secure/secure domain
  * @wb_dev:		Pointer to writeback device
  * @bo_disable:		Buffer object(s) to use during the disabling state
@@ -441,8 +446,8 @@ struct sde_encoder_phys_wb {
 	const struct sde_format *wb_fmt;
 	struct drm_framebuffer *wb_fb;
 	struct msm_gem_address_space *wb_aspace;
-	struct drm_framebuffer *cwb_old_fb;
-	struct msm_gem_address_space *cwb_old_aspace;
+	struct drm_framebuffer *old_fb;
+	struct msm_gem_address_space *old_aspace;
 	struct msm_gem_address_space *aspace[SDE_IOMMU_DOMAIN_MAX];
 	struct sde_wb_device *wb_dev;
 	struct drm_gem_object *bo_disable[SDE_MAX_PLANES];

+ 153 - 110
msm/sde/sde_encoder_phys_wb.c

@@ -1224,6 +1224,8 @@ static void sde_encoder_phys_wb_ctl_start_irq(void *arg, int irq_idx)
 		return;
 
 	phys_enc = &wb_enc->base;
+	if (atomic_add_unless(&phys_enc->pending_ctl_start_cnt, -1, 0))
+		wake_up_all(&phys_enc->pending_kickoff_wq);
 
 	SDE_EVT32_IRQ(DRMID(phys_enc->parent), WBID(wb_enc));
 }
@@ -1232,36 +1234,32 @@ static void _sde_encoder_phys_wb_frame_done_helper(void *arg, bool frame_error)
 {
 	struct sde_encoder_phys_wb *wb_enc = arg;
 	struct sde_encoder_phys *phys_enc = &wb_enc->base;
-	struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
 	u32 event = frame_error ? SDE_ENCODER_FRAME_EVENT_ERROR : 0;
 
 	/* don't notify upper layer for internal commit */
-	if (phys_enc->enable_state == SDE_ENC_DISABLING &&
-			!phys_enc->in_clone_mode)
-		goto complete;
+	if (phys_enc->enable_state == SDE_ENC_DISABLING && !phys_enc->in_clone_mode)
+		goto end;
 
 	if (phys_enc->parent_ops.handle_frame_done &&
-	    atomic_add_unless(&phys_enc->pending_retire_fence_cnt, -1, 0)) {
-		event |= SDE_ENCODER_FRAME_EVENT_DONE |
-			SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE;
+			atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0)) {
+		event |= SDE_ENCODER_FRAME_EVENT_DONE
+				| SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE;
 
 		if (phys_enc->in_clone_mode)
 			event |= SDE_ENCODER_FRAME_EVENT_CWB_DONE;
 		else
 			event |= SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE;
 
-		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
-				phys_enc, event);
+		phys_enc->parent_ops.handle_frame_done(phys_enc->parent, phys_enc, event);
 	}
 
 	if (!phys_enc->in_clone_mode && phys_enc->parent_ops.handle_vblank_virt)
-		phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
-				phys_enc);
+		phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent, phys_enc);
 
-	SDE_EVT32_IRQ(DRMID(phys_enc->parent), hw_wb->idx - WB_0, event,
-		frame_error);
+end:
+	SDE_EVT32_IRQ(DRMID(phys_enc->parent), WBID(wb_enc), phys_enc->in_clone_mode,
+			phys_enc->enable_state, event, frame_error);
 
-complete:
 	wake_up_all(&phys_enc->pending_kickoff_wq);
 }
 
@@ -1325,12 +1323,14 @@ static void sde_encoder_phys_wb_irq_ctrl(
 
 	if (enable && atomic_inc_return(&phys->wbirq_refcount) == 1) {
 		sde_encoder_helper_register_irq(phys, INTR_IDX_WB_DONE);
+		sde_encoder_helper_register_irq(phys, INTR_IDX_CTL_START);
 
 		for (index = 0; index < max_num_of_irqs; index++)
 			if (irq_table[index + pp] != SDE_NONE)
 				sde_encoder_helper_register_irq(phys, irq_table[index + pp]);
 	} else if (!enable && atomic_dec_return(&phys->wbirq_refcount) == 0) {
 		sde_encoder_helper_unregister_irq(phys, INTR_IDX_WB_DONE);
+		sde_encoder_helper_unregister_irq(phys, INTR_IDX_CTL_START);
 
 		for (index = 0; index < max_num_of_irqs; index++)
 			if (irq_table[index + pp] != SDE_NONE)
@@ -1404,35 +1404,8 @@ static void sde_encoder_phys_wb_mode_set(
 	irq->hw_idx = phys_enc->hw_ctl->idx;
 }
 
-static int sde_encoder_phys_wb_frame_timeout(struct sde_encoder_phys *phys_enc)
-{
-	u32 event = 0;
-
-	while (atomic_add_unless(&phys_enc->pending_retire_fence_cnt, -1, 0) &&
-			phys_enc->parent_ops.handle_frame_done) {
-
-		event = SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE
-			| SDE_ENCODER_FRAME_EVENT_ERROR;
-
-		if (phys_enc->in_clone_mode)
-			event |= SDE_ENCODER_FRAME_EVENT_CWB_DONE;
-		else
-			event |= SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE;
-
-		phys_enc->parent_ops.handle_frame_done(
-				phys_enc->parent, phys_enc, event);
-
-		SDE_EVT32(DRMID(phys_enc->parent), event,
-			atomic_read(&phys_enc->pending_retire_fence_cnt));
-	}
-
-	return event;
-}
-
-static bool _sde_encoder_phys_wb_is_idle(
-		struct sde_encoder_phys *phys_enc)
+static bool _sde_encoder_phys_wb_is_idle(struct sde_encoder_phys *phys_enc)
 {
-	bool ret = false;
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
 	struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
 	struct sde_vbif_get_xin_status_params xin_status = {0};
@@ -1440,32 +1413,39 @@ static bool _sde_encoder_phys_wb_is_idle(
 	xin_status.vbif_idx = hw_wb->caps->vbif_idx;
 	xin_status.xin_id = hw_wb->caps->xin_id;
 	xin_status.clk_ctrl = hw_wb->caps->clk_ctrl;
-	if (sde_vbif_get_xin_status(phys_enc->sde_kms, &xin_status)) {
-		_sde_encoder_phys_wb_frame_done_helper(wb_enc, false);
-		ret = true;
-	}
 
-	return ret;
+	return sde_vbif_get_xin_status(phys_enc->sde_kms, &xin_status);
 }
-static void _sde_encoder_phys_wb_reset_state(
-		struct sde_encoder_phys *phys_enc)
+
+static void _sde_encoder_phys_wb_reset_state(struct sde_encoder_phys *phys_enc)
 {
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
 
 	phys_enc->enable_state = SDE_ENC_DISABLED;
+
+	/* cleanup any pending buffer */
+	if (wb_enc->wb_fb && wb_enc->wb_aspace) {
+		msm_framebuffer_cleanup(wb_enc->wb_fb, wb_enc->wb_aspace);
+		drm_framebuffer_put(wb_enc->wb_fb);
+		wb_enc->wb_fb = NULL;
+		wb_enc->wb_aspace = NULL;
+	}
+
 	wb_enc->crtc = NULL;
 	phys_enc->hw_cdm = NULL;
 	phys_enc->hw_ctl = NULL;
 	phys_enc->in_clone_mode = false;
+	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
+	atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
+	atomic_set(&phys_enc->pending_ctl_start_cnt, 0);
 }
 
-static int _sde_encoder_phys_wb_wait_for_commit_done(
-		struct sde_encoder_phys *phys_enc, bool force_wait)
+static int _sde_encoder_phys_wb_wait_for_idle(struct sde_encoder_phys *phys_enc, bool force_wait)
 {
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
-	u32 event = 0;
-	int rc = 0;
 	struct sde_encoder_wait_info wait_info = {0};
+	int rc = 0;
+	bool is_idle;
 
 	/* Return EWOULDBLOCK since we know the wait isn't necessary */
 	if (phys_enc->enable_state == SDE_ENC_DISABLED) {
@@ -1474,56 +1454,72 @@ static int _sde_encoder_phys_wb_wait_for_commit_done(
 	}
 
 	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), phys_enc->in_clone_mode,
-			atomic_read(&phys_enc->pending_retire_fence_cnt),
-			!!wb_enc->wb_fb, force_wait);
+			atomic_read(&phys_enc->pending_kickoff_cnt), force_wait);
 
-	if (!force_wait && phys_enc->in_clone_mode &&
-			(atomic_read(&phys_enc->pending_retire_fence_cnt) <= 1))
-		goto skip_wait;
+	if (!force_wait && phys_enc->in_clone_mode
+			&& (atomic_read(&phys_enc->pending_kickoff_cnt) <= 1))
+		return 0;
 
-	/* signal completion if commit with no framebuffer */
-	if (!wb_enc->wb_fb) {
-		SDE_DEBUG("no output framebuffer\n");
-		_sde_encoder_phys_wb_frame_done_helper(wb_enc, false);
+	/*
+	 * signal completion if commit with no framebuffer
+	 * handle frame-done when WB HW is idle
+	 */
+	is_idle = _sde_encoder_phys_wb_is_idle(phys_enc);
+	if (!wb_enc->wb_fb || is_idle) {
+		SDE_EVT32((phys_enc->parent), WBID(wb_enc), !wb_enc->wb_fb, is_idle,
+				SDE_EVTLOG_FUNC_CASE1);
+		goto frame_done;
 	}
 
-	if (atomic_read(&phys_enc->pending_retire_fence_cnt) > 1)
+	if (atomic_read(&phys_enc->pending_kickoff_cnt) > 1)
 		wait_info.count_check = 1;
 
 	wait_info.wq = &phys_enc->pending_kickoff_wq;
-	wait_info.atomic_cnt = &phys_enc->pending_retire_fence_cnt;
-	wait_info.timeout_ms = max_t(u32, wb_enc->wbdone_timeout,
-			phys_enc->kickoff_timeout_ms);
-	rc = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE,
-		&wait_info);
-	if (rc == -ETIMEDOUT && _sde_encoder_phys_wb_is_idle(phys_enc)) {
-		rc = 0;
-	} else if (rc == -ETIMEDOUT) {
-		SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc),
-			atomic_read(&phys_enc->pending_retire_fence_cnt), SDE_EVTLOG_ERROR);
-		SDE_ERROR("wb:%d clone_mode:%d, kickoff timed out\n",
-				WBID(wb_enc), phys_enc->in_clone_mode);
+	wait_info.atomic_cnt = &phys_enc->pending_kickoff_cnt;
+	wait_info.timeout_ms = max_t(u32, wb_enc->wbdone_timeout, phys_enc->kickoff_timeout_ms);
 
-		event = sde_encoder_phys_wb_frame_timeout(phys_enc);
-	}
+	rc = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_WB_DONE, &wait_info);
+	if (rc == -ETIMEDOUT) {
+		/* handle frame-done when WB HW is idle */
+		if (_sde_encoder_phys_wb_is_idle(phys_enc))
+			rc = 0;
 
-	/* cleanup writeback framebuffer */
-	if (wb_enc->wb_fb && wb_enc->wb_aspace) {
-		msm_framebuffer_cleanup(wb_enc->wb_fb, wb_enc->wb_aspace);
-		drm_framebuffer_put(wb_enc->wb_fb);
-		wb_enc->wb_fb = NULL;
-		wb_enc->wb_aspace = NULL;
+		SDE_ERROR("caller:%pS - wb:%d, clone_mode:%d kickoff timed out\n",
+			__builtin_return_address(0), WBID(wb_enc), phys_enc->in_clone_mode);
+		SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc),
+			atomic_read(&phys_enc->pending_kickoff_cnt), SDE_EVTLOG_ERROR);
+		goto frame_done;
 	}
 
-skip_wait:
-	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), event, rc);
+	return 0;
+
+frame_done:
+	_sde_encoder_phys_wb_frame_done_helper(wb_enc, rc ? true : false);
+	return rc;
+}
+
+static int _sde_encoder_phys_wb_wait_for_ctl_start(struct sde_encoder_phys *phys_enc)
+{
+	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+	struct sde_encoder_wait_info wait_info = {0};
+	int rc = 0;
+
+	if (!atomic_read(&phys_enc->pending_ctl_start_cnt))
+		return 0;
 
-	/* cleanup previous buffer if pending */
-	if (wb_enc->cwb_old_fb && wb_enc->cwb_old_aspace) {
-		msm_framebuffer_cleanup(wb_enc->cwb_old_fb, wb_enc->cwb_old_aspace);
-		drm_framebuffer_put(wb_enc->cwb_old_fb);
-		wb_enc->cwb_old_fb = NULL;
-		wb_enc->cwb_old_aspace = NULL;
+	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), phys_enc->in_clone_mode,
+			atomic_read(&phys_enc->pending_kickoff_cnt),
+			atomic_read(&phys_enc->pending_ctl_start_cnt));
+
+	wait_info.wq = &phys_enc->pending_kickoff_wq;
+	wait_info.atomic_cnt = &phys_enc->pending_ctl_start_cnt;
+	wait_info.timeout_ms = max_t(u32, wb_enc->wbdone_timeout, phys_enc->kickoff_timeout_ms);
+
+	rc = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_CTL_START, &wait_info);
+	if (rc == -ETIMEDOUT) {
+		atomic_add_unless(&phys_enc->pending_ctl_start_cnt, -1, 0);
+		SDE_ERROR("wb:%d ctl_start timed out\n", WBID(wb_enc));
+		SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), SDE_EVTLOG_ERROR);
 	}
 
 	return rc;
@@ -1535,20 +1531,57 @@ skip_wait:
  */
 static int sde_encoder_phys_wb_wait_for_commit_done(struct sde_encoder_phys *phys_enc)
 {
-	return _sde_encoder_phys_wb_wait_for_commit_done(phys_enc, false);
+	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+	int rc, pending_cnt, i;
+	bool is_idle;
+
+	/* CWB - wait for previous frame completion */
+	if (phys_enc->in_clone_mode) {
+		rc = _sde_encoder_phys_wb_wait_for_idle(phys_enc, false);
+		goto end;
+	}
+
+	/*
+	 * WB - wait for ctl-start-irq by default and additionally for
+	 * wb-done-irq during timeout or serialize frame-trigger
+	 */
+	rc = _sde_encoder_phys_wb_wait_for_ctl_start(phys_enc);
+
+	pending_cnt = atomic_read(&phys_enc->pending_kickoff_cnt);
+	is_idle = _sde_encoder_phys_wb_is_idle(phys_enc);
+
+	if (rc || (pending_cnt > 1) || (pending_cnt && is_idle)
+			|| (!rc && (phys_enc->frame_trigger_mode == FRAME_DONE_WAIT_SERIALIZE))) {
+		for (i = 0; i < pending_cnt; i++)
+			rc |= _sde_encoder_phys_wb_wait_for_idle(phys_enc, true);
+
+		if (rc) {
+			SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc),
+					phys_enc->frame_trigger_mode,
+					atomic_read(&phys_enc->pending_kickoff_cnt), is_idle, rc);
+			SDE_ERROR("wb:%d failed wait_for_idle:%d\n", WBID(wb_enc), rc);
+		}
+	}
+
+end:
+	/* cleanup any pending previous buffer */
+	if (wb_enc->old_fb && wb_enc->old_aspace) {
+		msm_framebuffer_cleanup(wb_enc->old_fb, wb_enc->old_aspace);
+		drm_framebuffer_put(wb_enc->old_fb);
+		wb_enc->old_fb = NULL;
+		wb_enc->old_aspace = NULL;
+	}
+
+	return rc;
 }
 
-static int sde_encoder_phys_wb_wait_for_tx_complete(
-		struct sde_encoder_phys *phys_enc)
+static int sde_encoder_phys_wb_wait_for_tx_complete(struct sde_encoder_phys *phys_enc)
 {
-	int rc;
-
-	if (!atomic_read(&phys_enc->pending_retire_fence_cnt))
-		return 0;
+	int rc = 0;
 
-	rc = _sde_encoder_phys_wb_wait_for_commit_done(phys_enc, true);
+	if (atomic_read(&phys_enc->pending_kickoff_cnt))
+		rc = _sde_encoder_phys_wb_wait_for_idle(phys_enc, true);
 
-	/* cleanup for cwb disable case */
 	if ((phys_enc->enable_state == SDE_ENC_DISABLING) && phys_enc->in_clone_mode) {
 		_sde_encoder_phys_wb_reset_state(phys_enc);
 		sde_encoder_phys_wb_irq_ctrl(phys_enc, false);
@@ -1567,12 +1600,20 @@ static int sde_encoder_phys_wb_prepare_for_kickoff(struct sde_encoder_phys *phys
 		struct sde_encoder_kickoff_params *params)
 {
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+	int ret = 0;
 
-	if (phys_enc->in_clone_mode) {
-		wb_enc->cwb_old_fb = wb_enc->wb_fb;
-		wb_enc->cwb_old_aspace = wb_enc->wb_aspace;
+	phys_enc->frame_trigger_mode = params->frame_trigger_mode;
+	if (!phys_enc->in_clone_mode && (phys_enc->frame_trigger_mode == FRAME_DONE_WAIT_DEFAULT)
+			&& (atomic_read(&phys_enc->pending_kickoff_cnt))) {
+		ret = _sde_encoder_phys_wb_wait_for_idle(phys_enc, true);
+		if (ret)
+			atomic_set(&phys_enc->pending_kickoff_cnt, 0);
 	}
 
+	/* cache the framebuffer/aspace for cleanup later */
+	wb_enc->old_fb = wb_enc->wb_fb;
+	wb_enc->old_aspace = wb_enc->wb_aspace;
+
 	/* set OT limit & enable traffic shaper */
 	sde_encoder_phys_wb_setup(phys_enc);
 
@@ -1581,8 +1622,8 @@ static int sde_encoder_phys_wb_prepare_for_kickoff(struct sde_encoder_phys *phys
 	_sde_encoder_phys_wb_update_cwb_flush(phys_enc, true);
 
 	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), phys_enc->in_clone_mode,
-			atomic_read(&phys_enc->pending_retire_fence_cnt));
-	return 0;
+			phys_enc->frame_trigger_mode, ret);
+	return ret;
 }
 
 /**
@@ -1792,15 +1833,14 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc)
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
 	struct sde_hw_wb *hw_wb = wb_enc->hw_wb;
 
-	SDE_DEBUG("[wb:%d]\n", hw_wb->idx - WB_0);
-
 	if (phys_enc->enable_state == SDE_ENC_DISABLED) {
 		SDE_ERROR("encoder is already disabled\n");
 		return;
 	}
 
-	if (!phys_enc->in_clone_mode || !wb_enc->crtc->state->active)
-		_sde_encoder_phys_wb_wait_for_commit_done(phys_enc, true);
+	SDE_DEBUG("enc:%d, wb:%d, clone_mode:%d, kickoff_cnt:%u\n",
+			DRMID(phys_enc->parent), WBID(wb_enc), phys_enc->in_clone_mode,
+			atomic_read(&phys_enc->pending_kickoff_cnt));
 
 	if (!phys_enc->hw_ctl || !phys_enc->parent ||
 			!phys_enc->sde_kms || !wb_enc->fb_disable) {
@@ -1848,10 +1888,11 @@ static void sde_encoder_phys_wb_disable(struct sde_encoder_phys *phys_enc)
 		phys_enc->hw_ctl->ops.trigger_flush(phys_enc->hw_ctl);
 
 	sde_encoder_helper_trigger_start(phys_enc);
-	_sde_encoder_phys_wb_wait_for_commit_done(phys_enc, true);
+	_sde_encoder_phys_wb_wait_for_idle(phys_enc, true);
 	sde_encoder_phys_wb_irq_ctrl(phys_enc, false);
 
 exit:
+	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), phys_enc->in_clone_mode);
 	_sde_encoder_phys_wb_reset_state(phys_enc);
 }
 
@@ -2066,6 +2107,8 @@ struct sde_encoder_phys *sde_encoder_phys_wb_init(
 	phys_enc->intf_idx = p->intf_idx;
 	phys_enc->enc_spinlock = p->enc_spinlock;
 	atomic_set(&phys_enc->pending_retire_fence_cnt, 0);
+	atomic_set(&phys_enc->pending_kickoff_cnt, 0);
+	atomic_set(&phys_enc->pending_ctl_start_cnt, 0);
 	init_waitqueue_head(&phys_enc->pending_kickoff_wq);
 	wb_cfg = wb_enc->hw_wb->caps;