소스 검색

disp: msm: dsi: add pre and post DSI command transfer controller APIs

To transfer a command on the DSI lanes, the following programming needs
to happen prior and post a command transfer:
- Vote/Unvote for clocks
- Enable/Disable command engine
- Mask/Unmask overflow and underflow errors.
These operations are done from the display context currently. This can
lead to issues during an ASYNC command wait, where in we queue the
dma_wait_for_done rather than wait in the same display thread.

The following change adds new DSI controller API that does the above
programming from the controller context. This way, post command transfer
operations can only happen once command is successfully transferred and
dma_wait_for_done completes.

Change-Id: I61db0d8a2656dc6e0e56864dbef01283b813d7c6
Signed-off-by: Satya Rama Aditya Pinapala <[email protected]>
Satya Rama Aditya Pinapala 4 년 전
부모
커밋
aecc86dfb2
4개의 변경된 파일346개의 추가작업 그리고 320개의 파일을 삭제
  1. 212 82
      msm/dsi/dsi_ctrl.c
  2. 36 15
      msm/dsi/dsi_ctrl.h
  3. 96 220
      msm/dsi/dsi_display.c
  4. 2 3
      msm/dsi/dsi_display.h

+ 212 - 82
msm/dsi/dsi_ctrl.c

@@ -370,33 +370,13 @@ dsi_ctrl_get_aspace(struct dsi_ctrl *dsi_ctrl,
 	return msm_gem_smmu_address_space_get(dsi_ctrl->drm_dev, domain);
 }
 
-static void dsi_ctrl_flush_cmd_dma_queue(struct dsi_ctrl *dsi_ctrl)
-{
-	/*
-	 * If a command is triggered right after another command,
-	 * check if the previous command transfer is completed. If
-	 * transfer is done, cancel any work that has been
-	 * queued. Otherwise wait till the work is scheduled and
-	 * completed before triggering the next command by
-	 * flushing the workqueue.
-	 */
-	if (atomic_read(&dsi_ctrl->dma_irq_trig)) {
-		cancel_work_sync(&dsi_ctrl->dma_cmd_wait);
-	} else {
-		flush_workqueue(dsi_ctrl->dma_cmd_workq);
-		SDE_EVT32(SDE_EVTLOG_FUNC_CASE2);
-	}
-}
-
-static void dsi_ctrl_dma_cmd_wait_for_done(struct work_struct *work)
+static void dsi_ctrl_dma_cmd_wait_for_done(struct dsi_ctrl *dsi_ctrl)
 {
 	int ret = 0;
-	struct dsi_ctrl *dsi_ctrl = NULL;
 	u32 status;
 	u32 mask = DSI_CMD_MODE_DMA_DONE;
 	struct dsi_ctrl_hw_ops dsi_hw_ops;
 
-	dsi_ctrl = container_of(work, struct dsi_ctrl, dma_cmd_wait);
 	dsi_hw_ops = dsi_ctrl->hw.ops;
 	SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY);
 
@@ -405,7 +385,7 @@ static void dsi_ctrl_dma_cmd_wait_for_done(struct work_struct *work)
 	 * so the wait is not needed.
 	 */
 	if (atomic_read(&dsi_ctrl->dma_irq_trig))
-		goto done;
+		return;
 
 	ret = wait_for_completion_timeout(
 			&dsi_ctrl->irq_info.cmd_dma_done,
@@ -416,9 +396,11 @@ static void dsi_ctrl_dma_cmd_wait_for_done(struct work_struct *work)
 			status |= (DSI_CMD_MODE_DMA_DONE | DSI_BTA_DONE);
 			dsi_hw_ops.clear_interrupt_status(&dsi_ctrl->hw,
 					status);
+			SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_CASE1);
 			DSI_CTRL_WARN(dsi_ctrl,
 					"dma_tx done but irq not triggered\n");
 		} else {
+			SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_ERROR);
 			DSI_CTRL_ERR(dsi_ctrl,
 					"Command transfer failed\n");
 		}
@@ -426,8 +408,86 @@ static void dsi_ctrl_dma_cmd_wait_for_done(struct work_struct *work)
 					DSI_SINT_CMD_MODE_DMA_DONE);
 	}
 
-done:
-	dsi_ctrl->dma_wait_queued = false;
+}
+
+static void dsi_ctrl_post_cmd_transfer(struct dsi_ctrl *dsi_ctrl)
+{
+	int rc = 0;
+	struct dsi_clk_ctrl_info clk_info;
+	bool skip_wait_for_done = false;
+	u32 mask = BIT(DSI_FIFO_OVERFLOW);
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+
+	SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY, dsi_ctrl->cell_index, dsi_ctrl->pending_cmd_flags);
+
+	/* In case of broadcast messages, we needn't wait on the slave controller */
+	if ((dsi_ctrl->pending_cmd_flags & DSI_CTRL_CMD_BROADCAST) &&
+			!(dsi_ctrl->pending_cmd_flags & DSI_CTRL_CMD_BROADCAST_MASTER))
+		skip_wait_for_done = true;
+
+	if (!skip_wait_for_done)
+		dsi_ctrl_dma_cmd_wait_for_done(dsi_ctrl);
+
+	/* Command engine disable, unmask overflow, remove vote on clocks and gdsc */
+	rc = dsi_ctrl_set_cmd_engine_state(dsi_ctrl, DSI_CTRL_ENGINE_OFF, false);
+	if (rc)
+		DSI_CTRL_ERR(dsi_ctrl, "failed to disable command engine\n");
+
+	if (dsi_ctrl->pending_cmd_flags & DSI_CTRL_CMD_READ)
+		mask |= BIT(DSI_FIFO_UNDERFLOW);
+
+	dsi_ctrl_mask_error_status_interrupts(dsi_ctrl, mask, false);
+
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+	clk_info.clk_type = DSI_ALL_CLKS;
+	clk_info.clk_state = DSI_CLK_OFF;
+
+	rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+	if (rc)
+		DSI_CTRL_ERR(dsi_ctrl, "failed to disable clocks\n");
+
+	(void)pm_runtime_put_sync(dsi_ctrl->drm_dev->dev);
+
+}
+
+static void dsi_ctrl_post_cmd_transfer_work(struct work_struct *work)
+{
+	struct dsi_ctrl *dsi_ctrl = NULL;
+
+	dsi_ctrl = container_of(work, struct dsi_ctrl, post_cmd_tx_work);
+
+	dsi_ctrl_post_cmd_transfer(dsi_ctrl);
+	dsi_ctrl->post_tx_queued = false;
+}
+
+static void dsi_ctrl_flush_cmd_dma_queue(struct dsi_ctrl *dsi_ctrl)
+{
+	/*
+	 * If a command is triggered right after another command,
+	 * check if the previous command transfer is completed. If
+	 * transfer is done, cancel any work that has been
+	 * queued. Otherwise wait till the work is scheduled and
+	 * completed before triggering the next command by
+	 * flushing the workqueue.
+	 *
+	 * cancel_work_sync returns true if the work has not yet been scheduled, in that case as
+	 * we are cancelling the work we need to explicitly call the post_cmd_transfer API to
+	 * clean up the states.
+	 */
+	SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY);
+
+	if (atomic_read(&dsi_ctrl->dma_irq_trig)) {
+		if (cancel_work_sync(&dsi_ctrl->post_cmd_tx_work)) {
+			dsi_ctrl_post_cmd_transfer(dsi_ctrl);
+			dsi_ctrl->post_tx_queued = false;
+		}
+	} else {
+		flush_workqueue(dsi_ctrl->post_cmd_tx_workq);
+		SDE_EVT32(SDE_EVTLOG_FUNC_CASE2);
+	}
 }
 
 static int dsi_ctrl_check_state(struct dsi_ctrl *dsi_ctrl,
@@ -437,7 +497,7 @@ static int dsi_ctrl_check_state(struct dsi_ctrl *dsi_ctrl,
 	int rc = 0;
 	struct dsi_ctrl_state_info *state = &dsi_ctrl->current_state;
 
-	SDE_EVT32(dsi_ctrl->cell_index, op, op_state);
+	SDE_EVT32_VERBOSE(dsi_ctrl->cell_index, op, op_state);
 
 	switch (op) {
 	case DSI_CTRL_OP_POWER_STATE_CHANGE:
@@ -1414,7 +1474,6 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl,
 
 	if (!(flags & DSI_CTRL_CMD_DEFER_TRIGGER)) {
 		dsi_ctrl_wait_for_video_done(dsi_ctrl);
-		dsi_ctrl_mask_overflow(dsi_ctrl, true);
 
 		atomic_set(&dsi_ctrl->dma_irq_trig, 0);
 		dsi_ctrl_enable_status_interrupt(dsi_ctrl,
@@ -1449,16 +1508,6 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl,
 					dsi_ctrl->cmd_trigger_frame);
 		}
 
-		if (flags & DSI_CTRL_CMD_ASYNC_WAIT) {
-			dsi_ctrl->dma_wait_queued = true;
-			queue_work(dsi_ctrl->dma_cmd_workq,
-					&dsi_ctrl->dma_cmd_wait);
-		} else {
-			dsi_ctrl->dma_wait_queued = false;
-			dsi_ctrl_dma_cmd_wait_for_done(&dsi_ctrl->dma_cmd_wait);
-		}
-
-		dsi_ctrl_mask_overflow(dsi_ctrl, false);
 
 		dsi_hw_ops.reset_cmd_fifo(&dsi_ctrl->hw);
 
@@ -1500,9 +1549,7 @@ static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, struct dsi_cmd_desc *cmd_de
 		goto error;
 	}
 
-	SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY, flags);
-	if (dsi_ctrl->dma_wait_queued)
-		dsi_ctrl_flush_cmd_dma_queue(dsi_ctrl);
+	SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY, *flags);
 
 	if (*flags & DSI_CTRL_CMD_NON_EMBEDDED_MODE) {
 		cmd_mem.offset = dsi_ctrl->cmd_buffer_iova;
@@ -1545,13 +1592,11 @@ static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl, struct dsi_cmd_desc *cmd_de
 	 * as specified by HW limitations. Need to overwrite the flags to
 	 * set the LAST_COMMAND flag to ensure no command transfer failures.
 	 */
-	if ((*flags & DSI_CTRL_CMD_FETCH_MEMORY) &&
-			(*flags & DSI_CTRL_CMD_BROADCAST)) {
-		if ((dsi_ctrl->cmd_len + length) > 240) {
-			dsi_ctrl_mask_overflow(dsi_ctrl, true);
+	if ((*flags & DSI_CTRL_CMD_FETCH_MEMORY) && (*flags & DSI_CTRL_CMD_BROADCAST)) {
+		if (((dsi_ctrl->cmd_len + length) > 240) && !(*flags & DSI_CTRL_CMD_LAST_COMMAND)) {
 			*flags |= DSI_CTRL_CMD_LAST_COMMAND;
-			SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_CASE1,
-					flags);
+			SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_CASE1, *flags);
+			dsi_ctrl_transfer_prepare(dsi_ctrl, *flags);
 		}
 	}
 
@@ -1734,6 +1779,10 @@ static int dsi_message_rx(struct dsi_ctrl *dsi_ctrl, struct dsi_cmd_desc *cmd_de
 					rc);
 			goto error;
 		}
+
+		/* Wait for read command transfer success */
+		dsi_ctrl_dma_cmd_wait_for_done(dsi_ctrl);
+
 		/*
 		 * wait before reading rdbk_data register, if any delay is
 		 * required after sending the read command.
@@ -2058,7 +2107,7 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev)
 	dsi_ctrl->irq_info.irq_num = -1;
 	dsi_ctrl->irq_info.irq_stat_mask = 0x0;
 
-	INIT_WORK(&dsi_ctrl->dma_cmd_wait, dsi_ctrl_dma_cmd_wait_for_done);
+	INIT_WORK(&dsi_ctrl->post_cmd_tx_work, dsi_ctrl_post_cmd_transfer_work);
 	atomic_set(&dsi_ctrl->dma_irq_trig, 0);
 
 	spin_lock_init(&dsi_ctrl->irq_info.irq_lock);
@@ -3336,6 +3385,79 @@ int dsi_ctrl_validate_timing(struct dsi_ctrl *dsi_ctrl,
 	return rc;
 }
 
+/**
+ * dsi_ctrl_transfer_prepare() - Set up a command transfer
+ * @dsi_ctrl:             DSI controller handle.
+ * @flags:                Controller flags of the command.
+ *
+ * Command transfer requires command engine to be enabled, along with
+ * clock votes and masking the overflow bits.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_transfer_prepare(struct dsi_ctrl *dsi_ctrl, u32 flags)
+{
+	int rc = 0;
+	struct dsi_clk_ctrl_info clk_info;
+	u32 mask = BIT(DSI_FIFO_OVERFLOW);
+
+	if (!dsi_ctrl)
+		return -EINVAL;
+
+	if (!(flags & DSI_CTRL_CMD_LAST_COMMAND))
+		return rc;
+
+	SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY, dsi_ctrl->cell_index, flags);
+
+	/* Vote for clocks, gdsc, enable command engine, mask overflow */
+	rc = pm_runtime_get_sync(dsi_ctrl->drm_dev->dev);
+	if (rc < 0) {
+		DSI_CTRL_ERR(dsi_ctrl, "failed gdsc voting\n");
+		return rc;
+	}
+
+	clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+	clk_info.clk_type = DSI_ALL_CLKS;
+	clk_info.clk_state = DSI_CLK_ON;
+
+	rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+	if (rc) {
+		DSI_CTRL_ERR(dsi_ctrl, "failed to enable clocks\n");
+		goto error_disable_gdsc;
+	}
+
+	/* Wait till any previous ASYNC waits are scheduled and completed */
+	if (dsi_ctrl->post_tx_queued)
+		dsi_ctrl_flush_cmd_dma_queue(dsi_ctrl);
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+
+	if (flags & DSI_CTRL_CMD_READ)
+		mask |= BIT(DSI_FIFO_UNDERFLOW);
+
+	dsi_ctrl_mask_error_status_interrupts(dsi_ctrl, mask, true);
+
+	rc = dsi_ctrl_set_cmd_engine_state(dsi_ctrl, DSI_CTRL_ENGINE_ON, false);
+	if (rc) {
+		DSI_CTRL_ERR(dsi_ctrl, "failed to enable command engine: %d\n", rc);
+		mutex_unlock(&dsi_ctrl->ctrl_lock);
+		goto error_disable_clks;
+	}
+
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	return rc;
+
+error_disable_clks:
+	clk_info.clk_state = DSI_CLK_OFF;
+	(void)dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+
+error_disable_gdsc:
+	(void)pm_runtime_put_sync(dsi_ctrl->drm_dev->dev);
+
+	return rc;
+}
+
 /**
  * dsi_ctrl_cmd_transfer() - Transfer commands on DSI link
  * @dsi_ctrl:             DSI controller handle.
@@ -3360,13 +3482,6 @@ int dsi_ctrl_cmd_transfer(struct dsi_ctrl *dsi_ctrl, struct dsi_cmd_desc *cmd)
 
 	mutex_lock(&dsi_ctrl->ctrl_lock);
 
-	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_CMD_TX, 0x0);
-	if (rc) {
-		DSI_CTRL_ERR(dsi_ctrl, "Controller state check failed, rc=%d\n",
-				rc);
-		goto error;
-	}
-
 	if (cmd->ctrl_flags & DSI_CTRL_CMD_READ) {
 		rc = dsi_message_rx(dsi_ctrl, cmd);
 		if (rc <= 0)
@@ -3381,30 +3496,39 @@ int dsi_ctrl_cmd_transfer(struct dsi_ctrl *dsi_ctrl, struct dsi_cmd_desc *cmd)
 
 	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CMD_TX, 0x0);
 
-error:
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
 	return rc;
 }
 
 /**
- * dsi_ctrl_mask_overflow() -	API to mask/unmask overflow error.
- * @dsi_ctrl:			DSI controller handle.
- * @enable:			variable to control masking/unmasking.
+ * dsi_ctrl_transfer_unprepare() - Clean up post a command transfer
+ * @dsi_ctrl:                 DSI controller handle.
+ * @flags:                    Controller flags of the command
+ *
+ * After the DSI controller has been programmed to trigger a DCS command
+ * the post transfer API is used to check for success and clean up the
+ * resources. Depending on the controller flags, this check is either
+ * scheduled on the same thread or queued.
+ *
  */
-void dsi_ctrl_mask_overflow(struct dsi_ctrl *dsi_ctrl, bool enable)
+void dsi_ctrl_transfer_unprepare(struct dsi_ctrl *dsi_ctrl, u32 flags)
 {
-	struct dsi_ctrl_hw_ops dsi_hw_ops;
+	if (!dsi_ctrl)
+		return;
 
-	dsi_hw_ops = dsi_ctrl->hw.ops;
+	if (!(flags & DSI_CTRL_CMD_LAST_COMMAND))
+		return;
 
-	if (enable) {
-		if (dsi_hw_ops.mask_error_intr)
-			dsi_hw_ops.mask_error_intr(&dsi_ctrl->hw,
-					BIT(DSI_FIFO_OVERFLOW), true);
+	SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY, dsi_ctrl->cell_index, flags);
+
+	dsi_ctrl->pending_cmd_flags = flags;
+
+	if (flags & DSI_CTRL_CMD_ASYNC_WAIT) {
+		dsi_ctrl->post_tx_queued = true;
+		queue_work(dsi_ctrl->post_cmd_tx_workq, &dsi_ctrl->post_cmd_tx_work);
 	} else {
-		if (dsi_hw_ops.mask_error_intr && !dsi_ctrl->esd_check_underway)
-			dsi_hw_ops.mask_error_intr(&dsi_ctrl->hw,
-					BIT(DSI_FIFO_OVERFLOW), false);
+		dsi_ctrl->post_tx_queued = false;
+		dsi_ctrl_post_cmd_transfer(dsi_ctrl);
 	}
 }
 
@@ -3512,15 +3636,6 @@ int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags)
 					dsi_ctrl->cmd_trigger_frame);
 		}
 
-		if (flags & DSI_CTRL_CMD_ASYNC_WAIT) {
-			dsi_ctrl->dma_wait_queued = true;
-			queue_work(dsi_ctrl->dma_cmd_workq,
-					&dsi_ctrl->dma_cmd_wait);
-		} else {
-			dsi_ctrl->dma_wait_queued = false;
-			dsi_ctrl_dma_cmd_wait_for_done(&dsi_ctrl->dma_cmd_wait);
-		}
-
 		if (flags & DSI_CTRL_CMD_NON_EMBEDDED_MODE) {
 			if (dsi_ctrl->version < DSI_CTRL_VERSION_2_4)
 				dsi_hw_ops.soft_reset(&dsi_ctrl->hw);
@@ -3745,10 +3860,21 @@ int dsi_ctrl_set_cmd_engine_state(struct dsi_ctrl *dsi_ctrl,
 		return -EINVAL;
 	}
 
+	if (state == DSI_CTRL_ENGINE_ON) {
+		if (dsi_ctrl->cmd_engine_refcount > 0) {
+			dsi_ctrl->cmd_engine_refcount++;
+			goto error;
+		}
+	} else {
+		if (dsi_ctrl->cmd_engine_refcount > 1) {
+			dsi_ctrl->cmd_engine_refcount--;
+			goto error;
+		}
+	}
+
 	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_CMD_ENGINE, state);
 	if (rc) {
-		DSI_CTRL_ERR(dsi_ctrl, "Controller state check failed, rc=%d\n",
-				rc);
+		DSI_CTRL_ERR(dsi_ctrl, "Controller state check failed, rc=%d\n", rc);
 		goto error;
 	}
 
@@ -3759,11 +3885,17 @@ int dsi_ctrl_set_cmd_engine_state(struct dsi_ctrl *dsi_ctrl,
 			dsi_ctrl->hw.ops.cmd_engine_en(&dsi_ctrl->hw, false);
 	}
 
+	if (state == DSI_CTRL_ENGINE_ON)
+		dsi_ctrl->cmd_engine_refcount++;
+	else
+		dsi_ctrl->cmd_engine_refcount = 0;
+
 	SDE_EVT32(dsi_ctrl->cell_index, state, skip_op);
-	DSI_CTRL_DEBUG(dsi_ctrl, "Set cmd engine state:%d, skip_op:%d\n",
-					state, skip_op);
+
 	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CMD_ENGINE, state);
 error:
+	DSI_CTRL_DEBUG(dsi_ctrl, "Set cmd engine state:%d, skip_op:%d, enable count: %d\n",
+			state, skip_op, dsi_ctrl->cmd_engine_refcount);
 	return rc;
 }
 
@@ -4009,7 +4141,6 @@ void dsi_ctrl_mask_error_status_interrupts(struct dsi_ctrl *dsi_ctrl, u32 idx,
 	 * Mask DSI error status interrupts and clear error status
 	 * register
 	 */
-	mutex_lock(&dsi_ctrl->ctrl_lock);
 	if (idx & BIT(DSI_ERR_INTR_ALL)) {
 		/*
 		 * The behavior of mask_enable is different in ctrl register
@@ -4027,7 +4158,6 @@ void dsi_ctrl_mask_error_status_interrupts(struct dsi_ctrl *dsi_ctrl, u32 idx,
 		dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
 					DSI_ERROR_INTERRUPT_COUNT);
 	}
-	mutex_unlock(&dsi_ctrl->ctrl_lock);
 }
 
 /**

+ 36 - 15
msm/dsi/dsi_ctrl.h

@@ -219,11 +219,10 @@ struct dsi_ctrl_interrupts {
  * @vaddr:               CPU virtual address of cmd buffer.
  * @secure_mode:         Indicates if secure-session is in progress
  * @esd_check_underway:  Indicates if esd status check is in progress
- * @dma_cmd_wait:	Work object waiting on DMA command transfer done.
- * @dma_cmd_workq:	Pointer to the workqueue of DMA command transfer done
- *				wait sequence.
- * @dma_wait_queued:	Indicates if any DMA command transfer wait work
- *				is queued.
+ * @post_cmd_tx_work:	 Work object to clean up post command transfer.
+ * @post_cmd_tx_workq:   Pointer to the workqueue of post command transfer work.
+ * @post_tx_queued:	     Indicates if any DMA command post transfer work
+ *				         is queued.
  * @dma_irq_trig:		 Atomic state to indicate DMA done IRQ
  *				triggered.
  * @debugfs_root:        Root for debugfs entries.
@@ -251,6 +250,8 @@ struct dsi_ctrl_interrupts {
  *				which command transfer is successful.
  * @cmd_success_frame:		unsigned integer that indicates the frame at
  *				which command transfer is successful.
+ * @cmd_engine_refcount: Reference count enforcing single instance of cmd engine
+ * @pending_cmd_flags: Flags associated with command that is currently being txed or pending.
  */
 struct dsi_ctrl {
 	struct platform_device *pdev;
@@ -289,9 +290,9 @@ struct dsi_ctrl {
 	void *vaddr;
 	bool secure_mode;
 	bool esd_check_underway;
-	struct work_struct dma_cmd_wait;
-	struct workqueue_struct *dma_cmd_workq;
-	bool dma_wait_queued;
+	struct work_struct post_cmd_tx_work;
+	struct workqueue_struct *post_cmd_tx_workq;
+	bool post_tx_queued;
 	atomic_t dma_irq_trig;
 
 	/* Debug Information */
@@ -317,6 +318,8 @@ struct dsi_ctrl {
 	u32 cmd_trigger_frame;
 	u32 cmd_success_line;
 	u32 cmd_success_frame;
+	u32 cmd_engine_refcount;
+	u32 pending_cmd_flags;
 };
 
 /**
@@ -578,6 +581,18 @@ int dsi_ctrl_set_roi(struct dsi_ctrl *dsi_ctrl, struct dsi_rect *roi,
  */
 int dsi_ctrl_set_tpg_state(struct dsi_ctrl *dsi_ctrl, bool on);
 
+/**
+ * dsi_ctrl_transfer_prepare() - Set up a command transfer
+ * @dsi_ctrl:             DSI controller handle.
+ * @flags:                Controller flags of the command.
+ *
+ * Command transfer requires command engine to be enabled, along with
+ * clock votes and masking the overflow bits.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_transfer_prepare(struct dsi_ctrl *dsi_ctrl, u32 flags);
+
 /**
  * dsi_ctrl_cmd_transfer() - Transfer commands on DSI link
  * @dsi_ctrl:             DSI controller handle.
@@ -592,6 +607,19 @@ int dsi_ctrl_set_tpg_state(struct dsi_ctrl *dsi_ctrl, bool on);
  */
 int dsi_ctrl_cmd_transfer(struct dsi_ctrl *dsi_ctrl, struct dsi_cmd_desc *cmd);
 
+/**
+ * dsi_ctrl_transfer_unprepare() - Clean up post a command transfer
+ * @dsi_ctrl:                 DSI controller handle.
+ * @flags:                    Controller flags of the command
+ *
+ * After the DSI controller has been programmed to trigger a DCS command
+ * the post transfer API is used to check for success and clean up the
+ * resources. Depending on the controller flags, this check is either
+ * scheduled on the same thread or queued.
+ *
+ */
+void dsi_ctrl_transfer_unprepare(struct dsi_ctrl *dsi_ctrl, u32 flags);
+
 /**
  * dsi_ctrl_cmd_tx_trigger() - Trigger a deferred command.
  * @dsi_ctrl:              DSI controller handle.
@@ -892,11 +920,4 @@ int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl);
  */
 int dsi_ctrl_get_io_resources(struct msm_io_res *io_res);
 
-/**
- * dsi_ctrl_mask_overflow() -	API to mask/unmask overflow errors.
- * @dsi_ctrl:			DSI controller handle.
- * @enable:			variable to control masking/unmasking.
- */
-void dsi_ctrl_mask_overflow(struct dsi_ctrl *dsi_ctrl, bool enable);
-
 #endif /* _DSI_CTRL_H_ */

+ 96 - 220
msm/dsi/dsi_display.c

@@ -78,9 +78,14 @@ static void dsi_display_mask_ctrl_error_interrupts(struct dsi_display *display,
 
 	display_for_each_ctrl(i, display) {
 		ctrl = &display->ctrl[i];
-		if (!ctrl)
+		if ((!ctrl) || (!ctrl->ctrl))
 			continue;
+
+		mutex_lock(&ctrl->ctrl->ctrl_lock);
+
 		dsi_ctrl_mask_error_status_interrupts(ctrl->ctrl, mask, enable);
+
+		mutex_unlock(&ctrl->ctrl->ctrl_lock);
 	}
 }
 
@@ -245,26 +250,11 @@ int dsi_display_set_backlight(struct drm_connector *connector,
 
 	DSI_DEBUG("bl_scale = %u, bl_scale_sv = %u, bl_lvl = %u\n",
 		bl_scale, bl_scale_sv, (u32)bl_temp);
-	rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
-			DSI_CORE_CLK, DSI_CLK_ON);
-	if (rc) {
-		DSI_ERR("[%s] failed to enable DSI core clocks, rc=%d\n",
-		       dsi_display->name, rc);
-		goto error;
-	}
 
 	rc = dsi_panel_set_backlight(panel, (u32)bl_temp);
 	if (rc)
 		DSI_ERR("unable to set backlight\n");
 
-	rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
-			DSI_CORE_CLK, DSI_CLK_OFF);
-	if (rc) {
-		DSI_ERR("[%s] failed to disable DSI core clocks, rc=%d\n",
-		       dsi_display->name, rc);
-		goto error;
-	}
-
 error:
 	mutex_unlock(&panel->panel_lock);
 	return rc;
@@ -280,11 +270,6 @@ static int dsi_display_cmd_engine_enable(struct dsi_display *display)
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
 	mutex_lock(&m_ctrl->ctrl->ctrl_lock);
 
-	if (display->cmd_engine_refcount > 0) {
-		display->cmd_engine_refcount++;
-		goto done;
-	}
-
 	rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl,
 				DSI_CTRL_ENGINE_ON, skip_op);
 	if (rc) {
@@ -308,7 +293,6 @@ static int dsi_display_cmd_engine_enable(struct dsi_display *display)
 		}
 	}
 
-	display->cmd_engine_refcount++;
 	goto done;
 error_disable_master:
 	(void)dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl,
@@ -328,14 +312,6 @@ static int dsi_display_cmd_engine_disable(struct dsi_display *display)
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
 	mutex_lock(&m_ctrl->ctrl->ctrl_lock);
 
-	if (display->cmd_engine_refcount == 0) {
-		DSI_ERR("[%s] Invalid refcount\n", display->name);
-		goto done;
-	} else if (display->cmd_engine_refcount > 1) {
-		display->cmd_engine_refcount--;
-		goto done;
-	}
-
 	display_for_each_ctrl(i, display) {
 		ctrl = &display->ctrl[i];
 		if (!ctrl->ctrl || (ctrl == m_ctrl))
@@ -351,15 +327,10 @@ static int dsi_display_cmd_engine_disable(struct dsi_display *display)
 
 	rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl,
 				DSI_CTRL_ENGINE_OFF, skip_op);
-	if (rc) {
+	if (rc)
 		DSI_ERR("[%s] disable mcmd engine failed, skip_op:%d rc:%d\n",
 			display->name, skip_op, rc);
-		goto error;
-	}
 
-error:
-	display->cmd_engine_refcount = 0;
-done:
 	mutex_unlock(&m_ctrl->ctrl->ctrl_lock);
 	return rc;
 }
@@ -804,15 +775,22 @@ static int dsi_display_read_status(struct dsi_display_ctrl *ctrl,
 		cmds[i].msg.rx_len = config->status_cmds_rlen[i];
 		cmds[i].ctrl_flags = flags;
 		dsi_display_set_cmd_tx_ctrl_flags(display,&cmds[i]);
+		rc = dsi_ctrl_transfer_prepare(ctrl->ctrl, cmds[i].ctrl_flags);
+		if (rc) {
+			DSI_ERR("prepare for rx cmd transfer failed rc=%d\n", rc);
+			return rc;
+		}
+
 		rc = dsi_ctrl_cmd_transfer(ctrl->ctrl, &cmds[i]);
 		if (rc <= 0) {
 			DSI_ERR("rx cmd transfer failed rc=%d\n", rc);
-			return rc;
+		} else {
+			memcpy(config->return_buf + start,
+				config->status_buf, lenp[i]);
+			start += lenp[i];
 		}
 
-		memcpy(config->return_buf + start,
-			config->status_buf, lenp[i]);
-		start += lenp[i];
+		dsi_ctrl_transfer_unprepare(ctrl->ctrl, cmds[i].ctrl_flags);
 	}
 
 	return rc;
@@ -859,21 +837,15 @@ static int dsi_display_status_reg_read(struct dsi_display *display)
 		}
 	}
 
-	rc = dsi_display_cmd_engine_enable(display);
-	if (rc) {
-		DSI_ERR("cmd engine enable failed\n");
-		return -EPERM;
-	}
-
 	rc = dsi_display_validate_status(m_ctrl, display);
 	if (rc <= 0) {
 		DSI_ERR("[%s] read status failed on master,rc=%d\n",
 		       display->name, rc);
-		goto exit;
+		goto done;
 	}
 
 	if (!display->panel->sync_broadcast_en)
-		goto exit;
+		goto done;
 
 	display_for_each_ctrl(i, display) {
 		ctrl = &display->ctrl[i];
@@ -884,11 +856,10 @@ static int dsi_display_status_reg_read(struct dsi_display *display)
 		if (rc <= 0) {
 			DSI_ERR("[%s] read status failed on slave,rc=%d\n",
 			       display->name, rc);
-			goto exit;
+			goto done;
 		}
 	}
-exit:
-	dsi_display_cmd_engine_disable(display);
+
 done:
 	return rc;
 }
@@ -948,8 +919,7 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
 	struct dsi_display *dsi_display = display;
 	struct dsi_panel *panel;
 	u32 status_mode;
-	int rc = 0x1, ret;
-	u32 mask;
+	int rc = 0x1;
 	int te_rechecks = 1;
 
 	if (!dsi_display || !dsi_display->panel)
@@ -986,15 +956,7 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
 			(panel->panel_mode == DSI_OP_VIDEO_MODE))
 		te_rechecks = 0;
 
-	ret = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
-		DSI_ALL_CLKS, DSI_CLK_ON);
-	if (ret)
-		goto release_panel_lock;
-
-	/* Mask error interrupts before attempting ESD read */
-	mask = BIT(DSI_FIFO_OVERFLOW) | BIT(DSI_FIFO_UNDERFLOW);
 	dsi_display_set_ctrl_esd_check_flag(dsi_display, true);
-	dsi_display_mask_ctrl_error_interrupts(dsi_display, mask, true);
 
 	if (status_mode == ESD_MODE_REG_READ) {
 		rc = dsi_display_status_reg_read(dsi_display);
@@ -1010,19 +972,13 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
 
 	if (rc <= 0 && te_check_override)
 		rc = dsi_display_status_check_te(dsi_display, te_rechecks);
-	/* Unmask error interrupts if check passed*/
 	if (rc > 0) {
 		dsi_display_set_ctrl_esd_check_flag(dsi_display, false);
-		dsi_display_mask_ctrl_error_interrupts(dsi_display, mask,
-							false);
 		if (te_check_override && panel->esd_config.esd_enabled == false)
 			rc = dsi_display_status_check_te(dsi_display,
 					te_rechecks);
 	}
 
-	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
-		DSI_ALL_CLKS, DSI_CLK_OFF);
-
 	/* Handle Panel failures during display disable sequence */
 	if (rc <=0)
 		atomic_set(&panel->esd_recovery_pending, 1);
@@ -1053,7 +1009,7 @@ static int dsi_display_cmd_rx(struct dsi_display *display,
 			      struct dsi_cmd_desc *cmd)
 {
 	struct dsi_display_ctrl *m_ctrl = NULL;
-	u32 mask = 0, flags = 0;
+	u32 flags = 0;
 	int rc = 0;
 
 	if (!display || !display->panel)
@@ -1070,33 +1026,20 @@ static int dsi_display_cmd_rx(struct dsi_display *display,
 		goto release_panel_lock;
 	}
 
-	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
-		DSI_ALL_CLKS, DSI_CLK_ON);
-	if (rc)
-		goto release_panel_lock;
-
-	mask = BIT(DSI_FIFO_OVERFLOW) | BIT(DSI_FIFO_UNDERFLOW);
-	dsi_display_mask_ctrl_error_interrupts(display, mask, true);
-	rc = dsi_display_cmd_engine_enable(display);
-	if (rc) {
-		DSI_ERR("cmd engine enable failed rc = %d\n", rc);
-		goto error;
-	}
-
 	flags = DSI_CTRL_CMD_READ;
 
 	cmd->ctrl_flags = flags;
 	dsi_display_set_cmd_tx_ctrl_flags(display, cmd);
+	rc = dsi_ctrl_transfer_prepare(m_ctrl->ctrl, cmd->ctrl_flags);
+	if (rc) {
+		DSI_ERR("prepare for rx cmd transfer failed rc = %d\n", rc);
+		goto release_panel_lock;
+	}
 	rc = dsi_ctrl_cmd_transfer(m_ctrl->ctrl, cmd);
 	if (rc <= 0)
 		DSI_ERR("rx cmd transfer failed rc = %d\n", rc);
+	dsi_ctrl_transfer_unprepare(m_ctrl->ctrl, cmd->ctrl_flags);
 
-	dsi_display_cmd_engine_disable(display);
-
-error:
-	dsi_display_mask_ctrl_error_interrupts(display, mask, false);
-	dsi_display_clk_ctrl(display->dsi_clk_handle,
-		DSI_ALL_CLKS, DSI_CLK_OFF);
 release_panel_lock:
 	dsi_panel_release_panel_lock(display->panel);
 	return rc;
@@ -3016,6 +2959,20 @@ static int dsi_display_ctrl_host_disable(struct dsi_display *display)
 	struct dsi_display_ctrl *m_ctrl, *ctrl;
 	bool skip_op = is_skip_op_required(display);
 
+	/*
+	 * This is a defensive check. In reality as this is called after panel OFF commands, which
+	 * can never be ASYNC, the controller post_tx_queued flag will never be set when this API
+	 * is called.
+	 */
+	display_for_each_ctrl(i, display) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || !(ctrl->ctrl->post_tx_queued))
+			continue;
+		flush_workqueue(display->post_cmd_tx_workq);
+		cancel_work_sync(&ctrl->ctrl->post_cmd_tx_work);
+		ctrl->ctrl->post_tx_queued = false;
+	}
+
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
 	/*
 	 * For platforms where ULPS is controlled by DSI controller block,
@@ -3209,35 +3166,34 @@ static int dsi_display_wake_up(struct dsi_display *display)
 	return 0;
 }
 
-static void dsi_display_mask_overflow(struct dsi_display *display, u32 flags,
-						bool enable)
-{
-	struct dsi_display_ctrl *ctrl;
-	int i;
-
-	if (!(flags & DSI_CTRL_CMD_LAST_COMMAND))
-		return;
-
-	display_for_each_ctrl(i, display) {
-		ctrl = &display->ctrl[i];
-		if (!ctrl)
-			continue;
-		dsi_ctrl_mask_overflow(ctrl->ctrl, enable);
-	}
-}
-
 static int dsi_display_broadcast_cmd(struct dsi_display *display, struct dsi_cmd_desc *cmd)
 {
 	int rc = 0;
 	struct dsi_display_ctrl *ctrl, *m_ctrl;
 	int i;
+	u32 flags = 0;
 
 	/*
 	 * 1. Setup commands in FIFO
 	 * 2. Trigger commands
 	 */
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
-	dsi_display_mask_overflow(display, cmd->ctrl_flags, true);
+
+	display_for_each_ctrl(i, display) {
+		ctrl = &display->ctrl[i];
+		flags = cmd->ctrl_flags;
+		if (ctrl == m_ctrl)
+			flags |= DSI_CTRL_CMD_BROADCAST_MASTER;
+		rc = dsi_ctrl_transfer_prepare(ctrl->ctrl, flags);
+		if (rc) {
+			DSI_ERR("[%s] prepare for cmd transfer failed,rc=%d\n",
+				   display->name, rc);
+			if (ctrl != m_ctrl)
+				dsi_ctrl_transfer_unprepare(m_ctrl->ctrl, flags |
+						DSI_CTRL_CMD_BROADCAST_MASTER);
+			return rc;
+		}
+	}
 
 	cmd->ctrl_flags |= DSI_CTRL_CMD_BROADCAST_MASTER;
 	rc = dsi_ctrl_cmd_transfer(m_ctrl->ctrl, cmd);
@@ -3246,8 +3202,8 @@ static int dsi_display_broadcast_cmd(struct dsi_display *display, struct dsi_cmd
 		       display->name, rc);
 		goto error;
 	}
-	cmd->ctrl_flags &= ~DSI_CTRL_CMD_BROADCAST_MASTER;
 
+	cmd->ctrl_flags &= ~DSI_CTRL_CMD_BROADCAST_MASTER;
 	display_for_each_ctrl(i, display) {
 		ctrl = &display->ctrl[i];
 		if (ctrl == m_ctrl)
@@ -3276,7 +3232,14 @@ static int dsi_display_broadcast_cmd(struct dsi_display *display, struct dsi_cmd
 	}
 
 error:
-	dsi_display_mask_overflow(display, cmd->ctrl_flags, false);
+	display_for_each_ctrl(i, display) {
+		ctrl = &display->ctrl[i];
+		flags = cmd->ctrl_flags;
+		if (ctrl == m_ctrl)
+			flags |= DSI_CTRL_CMD_BROADCAST_MASTER;
+		dsi_ctrl_transfer_unprepare(ctrl->ctrl, flags);
+	}
+
 	return rc;
 }
 
@@ -3337,7 +3300,7 @@ static int dsi_host_detach(struct mipi_dsi_host *host,
 int dsi_host_transfer_sub(struct mipi_dsi_host *host, struct dsi_cmd_desc *cmd)
 {
 	struct dsi_display *display;
-	int rc = 0, ret = 0;
+	int rc = 0;
 
 	if (!host || !cmd) {
 		DSI_ERR("Invalid params\n");
@@ -3352,33 +3315,17 @@ int dsi_host_transfer_sub(struct mipi_dsi_host *host, struct dsi_cmd_desc *cmd)
 		return 0;
 	}
 
-	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
-			DSI_ALL_CLKS, DSI_CLK_ON);
-	if (rc) {
-		DSI_ERR("[%s] failed to enable all DSI clocks, rc=%d\n",
-		       display->name, rc);
-		goto error;
-	}
-
 	rc = dsi_display_wake_up(display);
 	if (rc) {
-		DSI_ERR("[%s] failed to wake up display, rc=%d\n",
-		       display->name, rc);
-		goto error_disable_clks;
-	}
-
-	rc = dsi_display_cmd_engine_enable(display);
-	if (rc) {
-		DSI_ERR("[%s] failed to enable cmd engine, rc=%d\n",
-		       display->name, rc);
-		goto error_disable_clks;
+		DSI_ERR("[%s] failed to wake up display, rc=%d\n", display->name, rc);
+		goto error;
 	}
 
 	if (display->tx_cmd_buf == NULL) {
 		rc = dsi_host_alloc_cmd_tx_buffer(display);
 		if (rc) {
 			DSI_ERR("failed to allocate cmd tx buffer memory\n");
-			goto error_disable_cmd_engine;
+			goto error;
 		}
 	}
 
@@ -3388,32 +3335,24 @@ int dsi_host_transfer_sub(struct mipi_dsi_host *host, struct dsi_cmd_desc *cmd)
 		rc = dsi_display_broadcast_cmd(display, cmd);
 		if (rc) {
 			DSI_ERR("[%s] cmd broadcast failed, rc=%d\n", display->name, rc);
-			goto error_disable_cmd_engine;
+			goto error;
 		}
 	} else {
 		int idx = cmd->ctrl;
 
-		rc = dsi_ctrl_cmd_transfer(display->ctrl[idx].ctrl, cmd);
+		rc = dsi_ctrl_transfer_prepare(display->ctrl[idx].ctrl, cmd->ctrl_flags);
 		if (rc) {
-			DSI_ERR("[%s] cmd transfer failed, rc=%d\n",
-			       display->name, rc);
-			goto error_disable_cmd_engine;
+			DSI_ERR("failed to prepare for command transfer: %d\n", rc);
+			goto error;
 		}
-	}
 
-error_disable_cmd_engine:
-	ret = dsi_display_cmd_engine_disable(display);
-	if (ret) {
-		DSI_ERR("[%s]failed to disable DSI cmd engine, rc=%d\n",
-				display->name, ret);
-	}
-error_disable_clks:
-	ret = dsi_display_clk_ctrl(display->dsi_clk_handle,
-			DSI_ALL_CLKS, DSI_CLK_OFF);
-	if (ret) {
-		DSI_ERR("[%s] failed to disable all DSI clocks, rc=%d\n",
-		       display->name, ret);
+		rc = dsi_ctrl_cmd_transfer(display->ctrl[idx].ctrl, cmd);
+		if (rc)
+			DSI_ERR("[%s] cmd transfer failed, rc=%d\n", display->name, rc);
+
+		dsi_ctrl_transfer_unprepare(display->ctrl[idx].ctrl, cmd->ctrl_flags);
 	}
+
 error:
 	return rc;
 }
@@ -3623,22 +3562,6 @@ int dsi_pre_clkoff_cb(void *priv,
 	struct dsi_display *display = priv;
 	struct dsi_display_ctrl *ctrl;
 
-
-	/*
-	 * If Idle Power Collapse occurs immediately after a CMD
-	 * transfer with an asynchronous wait for DMA done, ensure
-	 * that the work queued is scheduled and completed before turning
-	 * off the clocks and disabling interrupts to validate the command
-	 * transfer.
-	 */
-	display_for_each_ctrl(i, display) {
-		ctrl = &display->ctrl[i];
-		if (!ctrl->ctrl || !ctrl->ctrl->dma_wait_queued)
-			continue;
-		flush_workqueue(display->dma_cmd_workq);
-		cancel_work_sync(&ctrl->ctrl->dma_cmd_wait);
-		ctrl->ctrl->dma_wait_queued = false;
-	}
 	if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF) &&
 		(l_type & DSI_LINK_LP_CLK)) {
 		/*
@@ -5588,7 +5511,7 @@ static int dsi_display_bind(struct device *dev,
 			goto error_ctrl_deinit;
 		}
 
-		display_ctrl->ctrl->dma_cmd_workq = display->dma_cmd_workq;
+		display_ctrl->ctrl->post_cmd_tx_workq = display->post_cmd_tx_workq;
 		memcpy(&info.c_clks[i],
 				(&display_ctrl->ctrl->clk_info.core_clks),
 				sizeof(struct dsi_core_clk_info));
@@ -5765,7 +5688,7 @@ static void dsi_display_unbind(struct device *dev,
 			DSI_ERR("[%s] failed to deinit phy%d driver, rc=%d\n",
 			       display->name, i, rc);
 
-		display->ctrl->ctrl->dma_cmd_workq = NULL;
+		display->ctrl->ctrl->post_cmd_tx_workq = NULL;
 		rc = dsi_ctrl_drv_deinit(display_ctrl->ctrl);
 		if (rc)
 			DSI_ERR("[%s] failed to deinit ctrl%d driver, rc=%d\n",
@@ -5882,9 +5805,9 @@ int dsi_display_dev_probe(struct platform_device *pdev)
 		goto end;
 	}
 
-	display->dma_cmd_workq = create_singlethread_workqueue(
-			"dsi_dma_cmd_workq");
-	if (!display->dma_cmd_workq)  {
+	display->post_cmd_tx_workq = create_singlethread_workqueue(
+			"dsi_post_cmd_tx_workq");
+	if (!display->post_cmd_tx_workq)  {
 		DSI_ERR("failed to create work queue\n");
 		rc =  -EINVAL;
 		goto end;
@@ -5992,15 +5915,15 @@ int dsi_display_dev_remove(struct platform_device *pdev)
 	/* decrement ref count */
 	of_node_put(display->panel_node);
 
-	if (display->dma_cmd_workq) {
-		flush_workqueue(display->dma_cmd_workq);
-		destroy_workqueue(display->dma_cmd_workq);
-		display->dma_cmd_workq = NULL;
+	if (display->post_cmd_tx_workq) {
+		flush_workqueue(display->post_cmd_tx_workq);
+		destroy_workqueue(display->post_cmd_tx_workq);
+		display->post_cmd_tx_workq = NULL;
 		display_for_each_ctrl(i, display) {
 			ctrl = &display->ctrl[i];
 			if (!ctrl->ctrl)
 				continue;
-			ctrl->ctrl->dma_cmd_workq = NULL;
+			ctrl->ctrl->post_cmd_tx_workq = NULL;
 		}
 	}
 
@@ -8449,44 +8372,15 @@ int dsi_display_pre_disable(struct dsi_display *display)
 		if (display->config.panel_mode == DSI_OP_CMD_MODE)
 			dsi_panel_switch_cmd_mode_out(display->panel);
 
-		if (display->config.panel_mode == DSI_OP_VIDEO_MODE) {
-			/*
-			 * Add unbalanced vote for clock & cmd engine to enable
-			 * async trigger of pre video to cmd mode switch.
-			 */
-			rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
-					DSI_ALL_CLKS, DSI_CLK_ON);
-			if (rc) {
-				DSI_ERR("[%s]failed to enable all clocks,rc=%d",
-						display->name, rc);
-				goto exit;
-			}
-
-			rc = dsi_display_cmd_engine_enable(display);
-			if (rc) {
-				DSI_ERR("[%s]failed to enable cmd engine,rc=%d",
-						display->name, rc);
-				goto error_disable_clks;
-			}
-
+		if (display->config.panel_mode == DSI_OP_VIDEO_MODE)
 			dsi_panel_switch_video_mode_out(display->panel);
-		}
 	} else {
 		rc = dsi_panel_pre_disable(display->panel);
 		if (rc)
 			DSI_ERR("[%s] panel pre-disable failed, rc=%d\n",
 				display->name, rc);
 	}
-	goto exit;
 
-error_disable_clks:
-	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
-			DSI_ALL_CLKS, DSI_CLK_OFF);
-	if (rc)
-		DSI_ERR("[%s] failed to disable all DSI clocks, rc=%d\n",
-		       display->name, rc);
-
-exit:
 	mutex_unlock(&display->display_lock);
 	return rc;
 }
@@ -8705,8 +8599,7 @@ end:
 
 int dsi_display_unprepare(struct dsi_display *display)
 {
-	int rc = 0, i;
-	struct dsi_display_ctrl *ctrl;
+	int rc = 0;
 
 	if (!display) {
 		DSI_ERR("Invalid params\n");
@@ -8727,23 +8620,6 @@ int dsi_display_unprepare(struct dsi_display *display)
 			       display->name, rc);
 	}
 
-	/* Remove additional vote added for pre_mode_switch_to_cmd */
-	if (display->poms_pending &&
-			display->config.panel_mode == DSI_OP_VIDEO_MODE) {
-		display_for_each_ctrl(i, display) {
-			ctrl = &display->ctrl[i];
-			if (!ctrl->ctrl || !ctrl->ctrl->dma_wait_queued)
-				continue;
-			flush_workqueue(display->dma_cmd_workq);
-			cancel_work_sync(&ctrl->ctrl->dma_cmd_wait);
-			ctrl->ctrl->dma_wait_queued = false;
-		}
-
-		dsi_display_cmd_engine_disable(display);
-		dsi_display_clk_ctrl(display->dsi_clk_handle,
-				DSI_ALL_CLKS, DSI_CLK_OFF);
-	}
-
 	rc = dsi_display_ctrl_host_disable(display);
 	if (rc)
 		DSI_ERR("[%s] failed to disable DSI host, rc=%d\n",

+ 2 - 3
msm/dsi/dsi_display.h

@@ -188,8 +188,7 @@ struct dsi_display_ext_bridge {
  * @te_source         vsync source pin information
  * @clk_gating_config Clocks for which clock gating needs to be enabled
  * @queue_cmd_waits   Indicates if wait for dma commands done has to be queued.
- * @dma_cmd_workq:	Pointer to the workqueue of DMA command transfer done
- *				wait sequence.
+ * @post_cmd_tx_workq: Pointer to the workqueue of post command transfer work.
  * @is_active:        status of the display
  * @trusted_vm_env:   Set to true, it the executing VM is Trusted VM.
  *                    Set to false, otherwise.
@@ -287,7 +286,7 @@ struct dsi_display {
 	u32 te_source;
 	u32 clk_gating_config;
 	bool queue_cmd_waits;
-	struct workqueue_struct *dma_cmd_workq;
+	struct workqueue_struct *post_cmd_tx_workq;
 
 	/* panel id of the display */
 	u64 panel_id;