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);