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

Commit aa0b303c authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mdss: dsi: Enhance the TE based ESD handling"

parents fc312f5a 389899b2
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
@@ -1623,8 +1623,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;
@@ -1694,11 +1692,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);
	}

@@ -3252,6 +3245,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: