Browse Source

disp: msm: dsi: implement ESD recovery cleanup

After an ESD failure, the PHY lanes and controller can be stuck in
an unknown state. This can result in interrupt storms and watchdog
failures, if these error states are not handled correctly. The following
change implements the below mechanism to avoid failures.

1) Disable error interrupts during an ESD reg read, which are re-enabled once
ESD check is successful.
2) On ESD failure, before turning off LP clocks, reset the PHY lanes and DSI
controller.
3) After the HS clocks are turned off, issue a PHY hard reset.
4) Before enabling/disabling error interrupts, clear the error status registers
as they are not cleared as part of controller reset.

Change-Id: If10e4edf095a334a9416d109ec4b1401d1a84505
Signed-off-by: Satya Rama Aditya Pinapala <[email protected]>
Satya Rama Aditya Pinapala 3 năm trước cách đây
mục cha
commit
1317b11bc2
4 tập tin đã thay đổi với 95 bổ sung20 xóa
  1. 17 19
      msm/dsi/dsi_ctrl.c
  2. 4 0
      msm/dsi/dsi_ctrl.h
  3. 16 0
      msm/dsi/dsi_ctrl_hw_cmn.c
  4. 58 1
      msm/dsi/dsi_display.c

+ 17 - 19
msm/dsi/dsi_ctrl.c

@@ -448,10 +448,8 @@ static void dsi_ctrl_post_cmd_transfer(struct dsi_ctrl *dsi_ctrl)
 	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);
+	if (!(dsi_ctrl->pending_cmd_flags & DSI_CTRL_CMD_READ))
+		dsi_ctrl_mask_error_status_interrupts(dsi_ctrl, mask, false);
 
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
 
@@ -1909,16 +1907,18 @@ static int dsi_disable_ulps(struct dsi_ctrl *dsi_ctrl)
 	return rc;
 }
 
-static void dsi_ctrl_enable_error_interrupts(struct dsi_ctrl *dsi_ctrl)
+void dsi_ctrl_toggle_error_interrupt_status(struct dsi_ctrl *dsi_ctrl, bool enable)
 {
-	if (dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE &&
-			!dsi_ctrl->host_config.u.video_engine.bllp_lp11_en &&
-			!dsi_ctrl->host_config.u.video_engine.eof_bllp_lp11_en)
-		dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw,
-				0xFF00A0);
-	else
-		dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw,
-				0xFF00E0);
+	if (!enable) {
+		dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0);
+	} else {
+		if (dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE &&
+				!dsi_ctrl->host_config.u.video_engine.bllp_lp11_en &&
+				!dsi_ctrl->host_config.u.video_engine.eof_bllp_lp11_en)
+			dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw,	0xFF00A0);
+		else
+			dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0xFF00E0);
+	}
 }
 
 static int dsi_ctrl_drv_state_init(struct dsi_ctrl *dsi_ctrl)
@@ -2597,7 +2597,7 @@ int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
 				    &dsi_ctrl->host_config.common_config);
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl_enable_error_interrupts(dsi_ctrl);
+	dsi_ctrl_toggle_error_interrupt_status(dsi_ctrl, true);
 
 	dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true);
 
@@ -3131,7 +3131,7 @@ int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl, bool skip_op)
 	}
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl_enable_error_interrupts(dsi_ctrl);
+	dsi_ctrl_toggle_error_interrupt_status(dsi_ctrl, true);
 
 	DSI_CTRL_DEBUG(dsi_ctrl, "Host initialization complete, skip op: %d\n",
 			skip_op);
@@ -3417,10 +3417,8 @@ int dsi_ctrl_transfer_prepare(struct dsi_ctrl *dsi_ctrl, u32 flags)
 
 	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);
+	if (!(flags & DSI_CTRL_CMD_READ))
+		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) {

+ 4 - 0
msm/dsi/dsi_ctrl.h

@@ -920,4 +920,8 @@ int dsi_ctrl_wait4dynamic_refresh_done(struct dsi_ctrl *ctrl);
  */
 int dsi_ctrl_get_io_resources(struct msm_io_res *io_res);
 
+/**
+ * dsi_ctrl_toggle_error_interrupt_status() - Toggles error interrupt status
+ */
+void dsi_ctrl_toggle_error_interrupt_status(struct dsi_ctrl *dsi_ctrl, bool enable);
 #endif /* _DSI_CTRL_H_ */

+ 16 - 0
msm/dsi/dsi_ctrl_hw_cmn.c

@@ -1411,6 +1411,22 @@ void dsi_ctrl_hw_cmn_enable_error_interrupts(struct dsi_ctrl_hw *ctrl,
 {
 	u32 int_ctrl = 0;
 	u32 int_mask0 = 0x7FFF3BFF;
+	u32 dln0_phy_err = 0x11111;
+	u32 fifo_status = 0xCCCC0789;
+	u32 ack_error = 0x1193BFFF;
+	u32 timeout_status = 0x11111111;
+	u32 clk_status = 0x10000;
+	u32 dsi_status_error = 0x80000000;
+	u32 reg = 0;
+
+	DSI_W32(ctrl, DSI_DLN0_PHY_ERR, dln0_phy_err);
+	DSI_W32(ctrl, DSI_FIFO_STATUS, fifo_status);
+	DSI_W32(ctrl, DSI_TIMEOUT_STATUS, timeout_status);
+	DSI_W32(ctrl, DSI_ACK_ERR_STATUS, ack_error);
+	reg = DSI_R32(ctrl, DSI_CLK_STATUS);
+	DSI_W32(ctrl, DSI_CLK_STATUS, reg | clk_status);
+	reg = DSI_R32(ctrl, DSI_STATUS);
+	DSI_W32(ctrl, DSI_STATUS, reg | dsi_status_error);
 
 	int_ctrl = DSI_R32(ctrl, DSI_INT_CTRL);
 	if (errors)

+ 58 - 1
msm/dsi/dsi_display.c

@@ -921,6 +921,19 @@ static int dsi_display_status_check_te(struct dsi_display *display,
 	return rc;
 }
 
+void dsi_display_toggle_error_interrupt_status(struct dsi_display * display, bool enable)
+{
+	int i = 0;
+	struct dsi_display_ctrl *ctrl;
+
+	display_for_each_ctrl(i, display) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl)
+			continue;
+		dsi_ctrl_toggle_error_interrupt_status(ctrl->ctrl, enable);
+	}
+}
+
 int dsi_display_check_status(struct drm_connector *connector, void *display,
 					bool te_check_override)
 {
@@ -966,6 +979,11 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
 
 	dsi_display_set_ctrl_esd_check_flag(dsi_display, true);
 
+	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON);
+
+	/* Disable error interrupts while doing an ESD check */
+	dsi_display_toggle_error_interrupt_status(dsi_display, false);
+
 	if (status_mode == ESD_MODE_REG_READ) {
 		rc = dsi_display_status_reg_read(dsi_display);
 	} else if (status_mode == ESD_MODE_SW_BTA) {
@@ -990,7 +1008,11 @@ int dsi_display_check_status(struct drm_connector *connector, void *display,
 	/* Handle Panel failures during display disable sequence */
 	if (rc <=0)
 		atomic_set(&panel->esd_recovery_pending, 1);
+	else
+		/* Enable error interrupts post an ESD success */
+		dsi_display_toggle_error_interrupt_status(dsi_display, true);
 
+	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_OFF);
 release_panel_lock:
 	dsi_panel_release_panel_lock(panel);
 	SDE_EVT32(SDE_EVTLOG_FUNC_EXIT, rc);
@@ -1040,18 +1062,23 @@ static int dsi_display_cmd_rx(struct dsi_display *display,
 
 	flags = DSI_CTRL_CMD_READ;
 
+	dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_ALL_CLKS, DSI_CLK_ON);
+	dsi_display_toggle_error_interrupt_status(display, false);
 	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;
+		goto enable_error_interrupts;
 	}
 	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);
 
+enable_error_interrupts:
+	dsi_display_toggle_error_interrupt_status(display, true);
+	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;
@@ -3573,6 +3600,21 @@ static void dsi_display_ctrl_isr_configure(struct dsi_display *display, bool en)
 	}
 }
 
+static void dsi_display_cleanup_post_esd_failure(struct dsi_display *display)
+{
+	int i = 0;
+	struct dsi_display_ctrl *ctrl;
+
+	display_for_each_ctrl(i, display) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl)
+			continue;
+
+		dsi_phy_lane_reset(ctrl->phy);
+		dsi_ctrl_soft_reset(ctrl->ctrl);
+	}
+}
+
 int dsi_pre_clkoff_cb(void *priv,
 			   enum dsi_clk_type clk,
 			   enum dsi_lclk_type l_type,
@@ -3584,6 +3626,14 @@ int dsi_pre_clkoff_cb(void *priv,
 
 	if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF) &&
 		(l_type & DSI_LINK_LP_CLK)) {
+
+		/*
+		 * Clean up the DSI controller on a previous ESD failure. This requires a DSI
+		 * controller soft reset. Also reset PHY lanes before resetting controller.
+		 */
+		if (atomic_read(&display->panel->esd_recovery_pending))
+			dsi_display_cleanup_post_esd_failure(display);
+
 		/*
 		 * If continuous clock is enabled then disable it
 		 * before entering into ULPS Mode.
@@ -3777,6 +3827,13 @@ int dsi_post_clkoff_cb(void *priv,
 		return -EINVAL;
 	}
 
+	/* Reset PHY to clear the PHY status once the HS clocks are turned off */
+	if ((clk_type & DSI_LINK_CLK) && (curr_state == DSI_CLK_OFF)
+			&& (l_type == DSI_LINK_HS_CLK)) {
+		if (atomic_read(&display->panel->esd_recovery_pending))
+			dsi_display_phy_sw_reset(display);
+	}
+
 	if ((clk_type & DSI_CORE_CLK) &&
 	    (curr_state == DSI_CLK_OFF)) {
 		rc = dsi_display_phy_power_off(display);