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

Commit afb54903 authored by Sandeep Panda's avatar Sandeep Panda Committed by Shashank Babu Chinta Venkata
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>
Signed-off-by: default avatarShashank Babu Chinta Venkata <sbchin@codeaurora.org>
parent 1c1e23d9
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -688,7 +688,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;
	}

@@ -697,7 +697,8 @@ static int dsi_display_status_check_te(struct dsi_display *display)
	return rc;
}

int dsi_display_check_status(struct drm_connector *connector, void *display)
int dsi_display_check_status(struct drm_connector *connector, void *display,
					bool te_check_override)
{
	struct dsi_display *dsi_display = display;
	struct dsi_panel *panel;
@@ -707,18 +708,20 @@ int dsi_display_check_status(struct drm_connector *connector, 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);

+3 −1
Original line number Diff line number Diff line
@@ -581,8 +581,10 @@ int dsi_display_set_backlight(struct drm_connector *connector,
 * dsi_display_check_status() - check if panel is dead or alive
 * @connector:          Pointer to drm connector structure
 * @display:            Handle to display.
 * @te_check_override:	Whether check for TE from panel or default check
 */
int dsi_display_check_status(struct drm_connector *connector, void *display);
int dsi_display_check_status(struct drm_connector *connector, void *display,
				bool te_check_override);

/**
 * dsi_display_cmd_transfer() - transfer command to the panel
+52 −12
Original line number Diff line number Diff line
@@ -1749,12 +1749,60 @@ static int sde_connector_atomic_check(struct drm_connector *connector,
	return 0;
}

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->base, 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);
@@ -1771,7 +1819,7 @@ static void sde_connector_check_status_work(struct work_struct *work)
		return;
	}

	rc = conn->ops.check_status(&conn->base, conn->display);
	rc = conn->ops.check_status(&conn->base, conn->display, false);
	mutex_unlock(&conn->lock);

	if (conn->force_panel_dead) {
@@ -1795,15 +1843,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
@@ -236,9 +236,11 @@ struct sde_connector_ops {
	 * check_status - check status of connected display panel
	 * @connector: Pointer to drm connector structure
	 * @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)(struct drm_connector *connector, void *display);
	int (*check_status)(struct drm_connector *connector, void *display,
					bool te_check_override);

	/**
	 * cmd_transfer - Transfer command to the connected display panel
@@ -847,4 +849,10 @@ int sde_connector_event_notify(struct drm_connector *connector, uint32_t type,
 */
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
@@ -522,6 +522,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;

	/* to avoid flooding, only log first time, and "dead" time */
	if (cmd_enc->pp_timeout_report_cnt == 1) {
		SDE_ERROR_CMDENC(cmd_enc,
@@ -549,11 +553,12 @@ static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
		SDE_DBG_DUMP("panic");
	}

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