Merge "drm/msm/sde: add recovery mechanism if TE signal not received"
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index c79a72a..23249a0 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -694,7 +694,7 @@
 	reinit_completion(&display->esd_te_gate);
 	if (!wait_for_completion_timeout(&display->esd_te_gate,
 				esd_te_timeout)) {
-		pr_err("ESD check failed\n");
+		pr_err("TE check failed\n");
 		rc = -EINVAL;
 	}
 
@@ -703,7 +703,7 @@
 	return rc;
 }
 
-int dsi_display_check_status(void *display)
+int dsi_display_check_status(void *display, bool te_check_override)
 {
 	struct dsi_display *dsi_display = display;
 	struct dsi_panel *panel;
@@ -713,18 +713,20 @@
 	if (dsi_display == NULL)
 		return -EINVAL;
 
-	panel = dsi_display->panel;
-
-	status_mode = panel->esd_config.status_mode;
-
 	mutex_lock(&dsi_display->display_lock);
 
+	panel = dsi_display->panel;
 	if (!panel->panel_initialized) {
 		pr_debug("Panel not initialized\n");
 		mutex_unlock(&dsi_display->display_lock);
 		return rc;
 	}
 
+	if (te_check_override && gpio_is_valid(dsi_display->disp_te_gpio))
+		status_mode = ESD_MODE_PANEL_TE;
+	else
+		status_mode = panel->esd_config.status_mode;
+
 	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
 		DSI_ALL_CLKS, DSI_CLK_ON);
 
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index 85fd65c1..6b1c029 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -581,8 +581,9 @@
 /**
  * dsi_display_check_status() - check if panel is dead or alive
  * @display:            Handle to display.
+ * @te_check_override:	Whether check for TE from panel or default check
  */
-int dsi_display_check_status(void *display);
+int dsi_display_check_status(void *display, bool te_check_override);
 
 /**
  * dsi_display_cmd_transfer() - transfer command to the panel
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index e59c5f0..d2f8d12 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1731,12 +1731,59 @@
 	return c_conn->encoder;
 }
 
+static void _sde_connector_report_panel_dead(struct sde_connector *conn)
+{
+	struct drm_event event;
+	bool panel_dead = true;
+
+	if (!conn)
+		return;
+
+	event.type = DRM_EVENT_PANEL_DEAD;
+	event.length = sizeof(bool);
+	msm_mode_object_event_notify(&conn->base.base,
+		conn->base.dev, &event, (u8 *)&panel_dead);
+	sde_encoder_display_failure_notification(conn->encoder);
+	SDE_EVT32(SDE_EVTLOG_ERROR);
+	SDE_ERROR("esd check failed report PANEL_DEAD conn_id: %d enc_id: %d\n",
+			conn->base.base.id, conn->encoder->base.id);
+}
+
+int sde_connector_esd_status(struct drm_connector *conn)
+{
+	struct sde_connector *sde_conn = NULL;
+	int ret = 0;
+
+	if (!conn)
+		return ret;
+
+	sde_conn = to_sde_connector(conn);
+	if (!sde_conn || !sde_conn->ops.check_status)
+		return ret;
+
+	/* protect this call with ESD status check call */
+	mutex_lock(&sde_conn->lock);
+	ret = sde_conn->ops.check_status(sde_conn->display, true);
+	mutex_unlock(&sde_conn->lock);
+
+	if (ret <= 0) {
+		/* cancel if any pending esd work */
+		sde_connector_schedule_status_work(conn, false);
+		_sde_connector_report_panel_dead(sde_conn);
+		ret = -ETIMEDOUT;
+	} else {
+		SDE_DEBUG("Successfully received TE from panel\n");
+		ret = 0;
+	}
+	SDE_EVT32(ret);
+
+	return ret;
+}
+
 static void sde_connector_check_status_work(struct work_struct *work)
 {
 	struct sde_connector *conn;
-	struct drm_event event;
 	int rc = 0;
-	bool panel_dead = false;
 
 	conn = container_of(to_delayed_work(work),
 			struct sde_connector, status_work);
@@ -1753,7 +1800,7 @@
 		return;
 	}
 
-	rc = conn->ops.check_status(conn->display);
+	rc = conn->ops.check_status(conn->display, false);
 	mutex_unlock(&conn->lock);
 
 	if (conn->force_panel_dead) {
@@ -1777,15 +1824,7 @@
 	}
 
 status_dead:
-	SDE_EVT32(rc, SDE_EVTLOG_ERROR);
-	SDE_ERROR("esd check failed report PANEL_DEAD conn_id: %d enc_id: %d\n",
-			conn->base.base.id, conn->encoder->base.id);
-	panel_dead = true;
-	event.type = DRM_EVENT_PANEL_DEAD;
-	event.length = sizeof(bool);
-	msm_mode_object_event_notify(&conn->base.base,
-		conn->base.dev, &event, (u8 *)&panel_dead);
-	sde_encoder_display_failure_notification(conn->encoder);
+	_sde_connector_report_panel_dead(conn);
 }
 
 static const struct drm_connector_helper_funcs sde_connector_helper_ops = {
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index ef2385e..c6f348e 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -228,9 +228,10 @@
 	/**
 	 * check_status - check status of connected display panel
 	 * @display: Pointer to private display handle
+	 * @te_check_override: Whether check TE from panel or default check
 	 * Returns: positive value for success, negetive or zero for failure
 	 */
-	int (*check_status)(void *display);
+	int (*check_status)(void *display, bool te_check_override);
 
 	/**
 	 * cmd_transfer - Transfer command to the connected display panel
@@ -770,4 +771,11 @@
  */
 int sde_connector_get_panel_vfp(struct drm_connector *connector,
 	struct drm_display_mode *mode);
+
+/**
+ * sde_connector_esd_status - helper function to check te status
+ * @connector: Pointer to DRM connector object
+ */
+int sde_connector_esd_status(struct drm_connector *connector);
+
 #endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 3edaed3..828d771 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -473,6 +473,10 @@
 			atomic_read(&phys_enc->pending_kickoff_cnt),
 			frame_event);
 
+	/* check if panel is still sending TE signal or not */
+	if (sde_connector_esd_status(phys_enc->connector))
+		goto exit;
+
 	if (cmd_enc->pp_timeout_report_cnt >= PP_TIMEOUT_MAX_TRIALS) {
 		cmd_enc->pp_timeout_report_cnt = PP_TIMEOUT_MAX_TRIALS;
 		frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;
@@ -490,11 +494,12 @@
 		SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FATAL);
 	}
 
-	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
-
 	/* request a ctl reset before the next kickoff */
 	phys_enc->enable_state = SDE_ENC_ERR_NEEDS_HW_RESET;
 
+exit:
+	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
+
 	if (phys_enc->parent_ops.handle_frame_done)
 		phys_enc->parent_ops.handle_frame_done(
 				phys_enc->parent, phys_enc, frame_event);