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

Commit 389899b2 authored by Sachin Bhayare's avatar Sachin Bhayare Committed by Gerrit - the friendly Code Review server
Browse files

mdss: dsi: Enhance the TE based ESD handling



The current implementation enables TE based IRQ in resume and
disables only in suspend. This results in many IRQs to be fired
even when there are no active frame updates, causing unnecessary
CPU wakeups. To overcome this, following approach has been adopted:
* Enable TE IRQ only when ESD thread is scheduled
* Wait for IRQ to be asserted for a maximum duration of 3 vsyncs.
* If not asserted in the given time period, report panel dead due
to ESD attack.

In this way, TE IRQ rate is bound by ESD interval without
altering the main objective of this approach.

Change-Id: I59fe2ae79e88e5687a45209ebfb1e6b42af110bf
Signed-off-by: default avatarSachin Bhayare <sachin.bhayare@codeaurora.org>
parent f5be93c4
Loading
Loading
Loading
Loading
+18 −20
Original line number Diff line number Diff line
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
#include <linux/delay.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/interrupt.h>

#include "mdss_dsi.h"
#include "mdss_mdp.h"
@@ -22,7 +23,7 @@
 * mdss_check_te_status() - Check the status of panel for TE based ESD.
 * @ctrl_pdata   : dsi controller data
 * @pstatus_data : dsi status data
 * @interval     : duration in milliseconds to schedule work queue
 * @interval     : duration in milliseconds for panel TE wait
 *
 * This function is called when the TE signal from the panel doesn't arrive
 * after 'interval' milliseconds. If the TE IRQ is not ready, the workqueue
@@ -33,21 +34,14 @@ static bool mdss_check_te_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata,
{
	bool ret;

	/*
	 * During resume, the panel status will be ON but due to race condition
	 * between ESD thread and display UNBLANK (or rather can be put as
	 * asynchronuous nature between these two threads), the ESD thread might
	 * reach this point before the TE IRQ line is enabled or before the
	 * first TE interrupt arrives after the TE IRQ line is enabled. For such
	 * cases, re-schedule the ESD thread.
	 */
	ret = !atomic_read(&ctrl_pdata->te_irq_ready);
	if (ret) {
		schedule_delayed_work(&pstatus_data->check_status,
	atomic_set(&ctrl_pdata->te_irq_ready, 0);
	reinit_completion(&ctrl_pdata->te_irq_comp);
	enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
	/* Define TE interrupt timeout value as 3x(1/fps) */
	ret = wait_for_completion_timeout(&ctrl_pdata->te_irq_comp,
			msecs_to_jiffies(interval));
		pr_debug("%s: TE IRQ line not enabled yet\n", __func__);
	}

	disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
	pr_debug("%s: Panel TE check done with ret = %d\n", __func__, ret);
	return ret;
}

@@ -110,13 +104,17 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
	}

	if (ctrl_pdata->status_mode == ESD_TE) {
		if (mdss_check_te_status(ctrl_pdata, pstatus_data, interval))
			return;
		uint32_t fps = mdss_panel_get_framerate(&pdata->panel_info,
							FPS_RESOLUTION_HZ);
		uint32_t timeout = ((1000 / fps) + 1) *
					MDSS_STATUS_TE_WAIT_MAX;

		if (mdss_check_te_status(ctrl_pdata, pstatus_data, timeout))
			goto sim;
		else
			goto status_dead;
	}


	/*
	 * TODO: Because mdss_dsi_cmd_mdp_busy has made sure DMA to
	 * be idle in mdss_dsi_cmdlist_commit, it is not necessary
@@ -173,7 +171,7 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
		else
			goto status_dead;
	}

sim:
	if (pdata->panel_info.panel_force_dead) {
		pr_debug("force_dead=%d\n", pdata->panel_info.panel_force_dead);
		pdata->panel_info.panel_force_dead--;
+73 −13
Original line number Diff line number Diff line
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -9,16 +9,48 @@
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/interrupt.h>

#include "mdss_dsi.h"
#include "mdp3_ctrl.h"

/*
 * mdp3_check_te_status() - Check the status of panel for TE based ESD.
 * @ctrl_pdata   : dsi controller data
 * @pstatus_data : dsi status data
 * @interval     : duration in milliseconds for panel TE wait
 *
 * This function waits for TE signal from the panel for a maximum
 * duration of 3 vsyncs. If timeout occurs, report the panel to be
 * dead due to ESD attack.
 * NOTE: The TE IRQ handling is linked to the ESD thread scheduling,
 * i.e. rate of TE IRQs firing is bound by the ESD interval.
 */
static int mdp3_check_te_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata,
		struct dsi_status_data *pstatus_data, uint32_t interval)
{
	int ret;

	pr_debug("%s: Checking panel TE status\n", __func__);

	atomic_set(&ctrl_pdata->te_irq_ready, 0);
	reinit_completion(&ctrl_pdata->te_irq_comp);
	enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));

	ret = wait_for_completion_timeout(&ctrl_pdata->te_irq_comp,
			msecs_to_jiffies(interval));

	disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
	pr_debug("%s: Panel TE check done with ret = %d\n", __func__, ret);

	return ret;
}

/*
 * mdp3_check_dsi_ctrl_status() - Check MDP3 DSI controller status periodically.
 * @work     : dsi controller status data
@@ -33,6 +65,7 @@ void mdp3_check_dsi_ctrl_status(struct work_struct *work,
{
	struct dsi_status_data *pdsi_status = NULL;
	struct mdss_panel_data *pdata = NULL;
	struct mipi_panel_info *mipi = NULL;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
	struct mdp3_session_data *mdp3_session = NULL;
	int ret = 0;
@@ -51,15 +84,25 @@ void mdp3_check_dsi_ctrl_status(struct work_struct *work,
		return;
	}

	mipi = &pdata->panel_info.mipi;
	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
							panel_data);

	if (!ctrl_pdata || !ctrl_pdata->check_status) {
	if (!ctrl_pdata || (!ctrl_pdata->check_status &&
		(ctrl_pdata->status_mode != ESD_TE))) {
		pr_err("%s: DSI ctrl or status_check callback not available\n",
								__func__);
		return;
	}

	if (!pdata->panel_info.esd_rdy) {
		pr_err("%s: unblank not complete, reschedule check status\n",
			__func__);
		schedule_delayed_work(&pdsi_status->check_status,
				msecs_to_jiffies(interval));
		return;
	}

	mdp3_session = pdsi_status->mfd->mdp.private1;
	if (!mdp3_session) {
		pr_err("%s: Display is off\n", __func__);
@@ -73,6 +116,19 @@ void mdp3_check_dsi_ctrl_status(struct work_struct *work,
		return;
	}

	if (mipi->mode == DSI_CMD_MODE &&
		mipi->hw_vsync_mode &&
		mdss_dsi_is_te_based_esd(ctrl_pdata)) {
		uint32_t fps = mdss_panel_get_framerate(&pdata->panel_info,
					FPS_RESOLUTION_HZ);
		uint32_t timeout = ((1000 / fps) + 1) *
					MDSS_STATUS_TE_WAIT_MAX;

		if (mdp3_check_te_status(ctrl_pdata, pdsi_status, timeout) > 0)
			return;
		goto status_dead;
	}

	mutex_lock(&mdp3_session->lock);
	if (!mdp3_session->status) {
		pr_debug("%s: display off already\n", __func__);
@@ -90,18 +146,22 @@ void mdp3_check_dsi_ctrl_status(struct work_struct *work,
	mutex_unlock(&mdp3_session->lock);

	if (mdss_fb_is_power_on_interactive(pdsi_status->mfd)) {
		if (ret > 0) {
		if (ret > 0)
			schedule_delayed_work(&pdsi_status->check_status,
						msecs_to_jiffies(interval));
		} else {
			char *envp[2] = {"PANEL_ALIVE=0", NULL};
			pdata->panel_info.panel_dead = true;
			ret = kobject_uevent_env(
					&pdsi_status->mfd->fbi->dev->kobj,
					KOBJ_CHANGE, envp);
			pr_err("%s: Panel has gone bad, sending uevent - %s\n",
							__func__, envp[0]);
		else
			goto status_dead;
	}

	if (pdata->panel_info.panel_force_dead) {
		pr_debug("force_dead=%d\n", pdata->panel_info.panel_force_dead);
		pdata->panel_info.panel_force_dead--;
		if (!pdata->panel_info.panel_force_dead)
			goto status_dead;
	}
	return;

status_dead:
	mdss_fb_report_panel_dead(pdsi_status->mfd);
}
+1 −7
Original line number Diff line number Diff line
@@ -1539,8 +1539,6 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
	if ((pdata->panel_info.type == MIPI_CMD_PANEL) &&
		mipi->vsync_enable && mipi->hw_vsync_mode) {
		mdss_dsi_set_tear_on(ctrl_pdata);
		if (mdss_dsi_is_te_based_esd(ctrl_pdata))
			enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
	}

	ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
@@ -1610,11 +1608,6 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state)

	if ((pdata->panel_info.type == MIPI_CMD_PANEL) &&
		mipi->vsync_enable && mipi->hw_vsync_mode) {
		if (mdss_dsi_is_te_based_esd(ctrl_pdata)) {
				disable_irq(gpio_to_irq(
					ctrl_pdata->disp_te_gpio));
				atomic_dec(&ctrl_pdata->te_irq_ready);
		}
		mdss_dsi_set_tear_off(ctrl_pdata);
	}

@@ -3168,6 +3161,7 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
	}

	if (mdss_dsi_is_te_based_esd(ctrl_pdata)) {
		init_completion(&ctrl_pdata->te_irq_comp);
		rc = devm_request_irq(&pdev->dev,
			gpio_to_irq(ctrl_pdata->disp_te_gpio),
			hw_vsync_handler, IRQF_TRIGGER_FALLING,
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@
#define MDSS_DSI_HW_REV_STEP_1		0x1
#define MDSS_DSI_HW_REV_STEP_2		0x2

#define MDSS_STATUS_TE_WAIT_MAX		3
#define NONE_PANEL "none"

enum {		/* mipi dsi panel */
@@ -488,6 +489,7 @@ struct mdss_dsi_ctrl_pdata {
	struct completion video_comp;
	struct completion dynamic_comp;
	struct completion bta_comp;
	struct completion te_irq_comp;
	spinlock_t irq_lock;
	spinlock_t mdp_lock;
	int mdp_busy;
+9 −4
Original line number Diff line number Diff line
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -24,6 +24,7 @@
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/interrupt.h>

#include "mdss_fb.h"
#include "mdss_dsi.h"
@@ -94,8 +95,10 @@ irqreturn_t hw_vsync_handler(int irq, void *data)
	else
		pr_err("Pstatus data is NULL\n");

	if (!atomic_read(&ctrl_pdata->te_irq_ready))
	if (!atomic_read(&ctrl_pdata->te_irq_ready)) {
		complete_all(&ctrl_pdata->te_irq_comp);
		atomic_inc(&ctrl_pdata->te_irq_ready);
	}

	return IRQ_HANDLED;
}
@@ -160,10 +163,12 @@ static int fb_event_callback(struct notifier_block *self,
			schedule_delayed_work(&pdata->check_status,
				msecs_to_jiffies(interval));
			break;
		case FB_BLANK_POWERDOWN:
		case FB_BLANK_HSYNC_SUSPEND:
		case FB_BLANK_VSYNC_SUSPEND:
		case FB_BLANK_NORMAL:
			pr_debug("%s : ESD thread running\n", __func__);
			break;
		case FB_BLANK_POWERDOWN:
		case FB_BLANK_HSYNC_SUSPEND:
			cancel_delayed_work(&pdata->check_status);
			break;
		default: