Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 979009e7 authored by Sandeep Panda's avatar Sandeep Panda
Browse files

drm/msm/sde: add recovery mechanism if TE signal not received



There can be scenarios where display panel might not send TE signal
at a regular intervals. It may take more time than what kms driver
can wait for and will be fatal. In those use cases add recovery mechanism
to reset the panel and display subsystem.

Change-Id: I8c28f6212b7fc68073d84b274cef92e670e0352e
Signed-off-by: default avatarSandeep Panda <spanda@codeaurora.org>
parent 6a9f3b75
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -681,7 +681,7 @@ static int dsi_display_status_check_te(struct dsi_display *display)
	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;
	}

@@ -690,7 +690,7 @@ static int dsi_display_status_check_te(struct dsi_display *display)
	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;
@@ -700,18 +700,20 @@ int dsi_display_check_status(void *display)
	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);

+2 −1
Original line number Diff line number Diff line
@@ -581,8 +581,9 @@ int dsi_display_set_backlight(void *display, u32 bl_lvl);
/**
 * 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
+51 −12
Original line number Diff line number Diff line
@@ -1724,12 +1724,59 @@ sde_connector_best_encoder(struct drm_connector *connector)
	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);
@@ -1746,7 +1793,7 @@ static void sde_connector_check_status_work(struct work_struct *work)
		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) {
@@ -1770,15 +1817,7 @@ static void sde_connector_check_status_work(struct work_struct *work)
	}

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 = {
+9 −1
Original line number Diff line number Diff line
@@ -228,9 +228,10 @@ struct sde_connector_ops {
	/**
	 * 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
@@ -768,4 +769,11 @@ void sde_connector_helper_bridge_disable(struct drm_connector *connector);
 */
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_ */
+7 −2
Original line number Diff line number Diff line
@@ -473,6 +473,10 @@ static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
			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 @@ static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
		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);