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

Commit 06a99d7c authored by Padmanabhan Komanduru's avatar Padmanabhan Komanduru
Browse files

msm: mdss: add support for ESD status thread based on TE signal



For some command mode panels, the HW Vsync signal from the panel
stops or becomes irregular if the panel goes bad due to ESD attack.
For such panels, the Vsync signal irregularity can be considered
as a trigger for recovery due to ESD attack. The ESD thread
interval needs to be set based on the irregularity pattern seen
that is specific to the panel. Add support for this.

Change-Id: I8a2408ac1b2c0e063446f8af60ed6fac4c53cb8c
Signed-off-by: default avatarPadmanabhan Komanduru <pkomandu@codeaurora.org>
parent 57301a09
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -344,6 +344,7 @@ Optional properties:
- qcom,mdss-dsi-panel-status-check-mode:Specifies the panel status check method for ESD recovery.
					"bta_check" = Uses BTA to check the panel status
					"reg_read" = Reads panel status register to check the panel status
					"te_signal_check" = Uses TE signal behaviour to check the panel status
- qcom,mdss-dsi-panel-status-value:	Specifies the value of the panel status register when the panel is
					in good state.
- qcom,dynamic-mode-switch-enabled:		Boolean used to mention whether panel supports
+69 −12
Original line number Diff line number Diff line
@@ -18,6 +18,64 @@
#include "mdss_dsi.h"
#include "mdss_mdp.h"

/*
 * mdss_report_panel_dead() - Sends the PANEL_ALIVE=0 status to HAL layer.
 * @pstatus_data   : dsi status data
 *
 * This function is called if the panel fails to respond as expected to
 * the register read/BTA or if the TE signal is not coming as expected
 * from the panel. The function sends the PANEL_ALIVE=0 status to HAL
 * layer.
 */
static void mdss_report_panel_dead(struct dsi_status_data *pstatus_data)
{
	char *envp[2] = {"PANEL_ALIVE=0", NULL};
	struct mdss_panel_data *pdata =
		dev_get_platdata(&pstatus_data->mfd->pdev->dev);
	if (!pdata) {
		pr_err("%s: Panel data not available\n", __func__);
		return;
	}

	pdata->panel_info.panel_dead = true;
	kobject_uevent_env(&pstatus_data->mfd->fbi->dev->kobj,
		KOBJ_CHANGE, envp);
	pr_err("%s: Panel has gone bad, sending uevent - %s\n",
		__func__, envp[0]);
	return;
}

/*
 * 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
 *
 * 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
 * gets re-scheduled. Otherwise, report the panel to be dead due to ESD attack.
 */
static void mdss_check_te_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata,
		struct dsi_status_data *pstatus_data, uint32_t interval)
{
	/*
	 * 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.
	 */
	if (!atomic_read(&ctrl_pdata->te_irq_ready)) {
		schedule_delayed_work(&pstatus_data->check_status,
			msecs_to_jiffies(interval));
		pr_debug("%s: TE IRQ line not enabled yet\n", __func__);
		return;
	}

	mdss_report_panel_dead(pstatus_data);
}

/*
 * mdss_check_dsi_ctrl_status() - Check MDP5 DSI controller status periodically.
 * @work     : dsi controller status data
@@ -53,7 +111,8 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)

	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;
@@ -70,7 +129,12 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
	if (ctl->power_state == MDSS_PANEL_POWER_OFF) {
		schedule_delayed_work(&pstatus_data->check_status,
			msecs_to_jiffies(interval));
		pr_err("%s: ctl not powered on\n", __func__);
		pr_debug("%s: ctl not powered on\n", __func__);
		return;
	}

	if (ctrl_pdata->status_mode == ESD_TE) {
		mdss_check_te_status(ctrl_pdata, pstatus_data, interval);
		return;
	}

@@ -119,17 +183,10 @@ void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
	mutex_unlock(&ctrl_pdata->mutex);

	if ((pstatus_data->mfd->panel_power_state == MDSS_PANEL_POWER_ON)) {
		if (ret > 0) {
		if (ret > 0)
			schedule_delayed_work(&pstatus_data->check_status,
				msecs_to_jiffies(interval));
		} else {
			char *envp[2] = {"PANEL_ALIVE=0", NULL};
			pdata->panel_info.panel_dead = true;
			ret = kobject_uevent_env(
				&pstatus_data->mfd->fbi->dev->kobj,
							KOBJ_CHANGE, envp);
			pr_err("%s: Panel has gone bad, sending uevent - %s\n",
							__func__, envp[0]);
		}
		else
			mdss_report_panel_dead(pstatus_data);
	}
}
+30 −2
Original line number Diff line number Diff line
@@ -706,8 +706,11 @@ 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)
		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));
	}

error:
	mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 0);
@@ -763,8 +766,14 @@ 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)
		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);
	}

	if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
		if (!pdata->panel_info.dynamic_switch_pending) {
@@ -1404,6 +1413,7 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
		platform_set_drvdata(pdev, ctrl_pdata);
	}
	ctrl_pdata->mdss_util = util;
	atomic_set(&ctrl_pdata->te_irq_ready, 0);

	ctrl_name = of_get_property(pdev->dev.of_node, "label", NULL);
	if (!ctrl_name)
@@ -1487,6 +1497,17 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev)
		goto error_pan_node;
	}

	if (mdss_dsi_is_te_based_esd(ctrl_pdata)) {
		rc = devm_request_irq(&pdev->dev,
			gpio_to_irq(ctrl_pdata->disp_te_gpio),
			hw_vsync_handler, IRQF_TRIGGER_FALLING,
			"VSYNC_GPIO", ctrl_pdata);
		if (rc) {
			pr_err("TE request_irq failed.\n");
			goto error_pan_node;
		}
		disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio));
	}
	pr_debug("%s: Dsi Ctrl->%d initialized\n", __func__, index);
	return 0;

@@ -1749,6 +1770,13 @@ int dsi_panel_device_register(struct device_node *pan_node,
					__func__, __LINE__);
	}

	ctrl_pdata->disp_te_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
		"qcom,platform-te-gpio", 0);

	if (!gpio_is_valid(ctrl_pdata->disp_te_gpio))
		pr_err("%s:%d, TE gpio not specified\n",
						__func__, __LINE__);

	ctrl_pdata->bklt_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node,
		"qcom,platform-bklight-en-gpio", 0);
	if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio))
+12 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/mdss_io_util.h>
#include <linux/irqreturn.h>
#include <linux/pinctrl/consumer.h>
#include <linux/gpio.h>

#include "mdss_panel.h"
#include "mdss_dsi_cmd.h"
@@ -96,6 +97,7 @@ enum dsi_panel_status_mode {
	ESD_BTA,
	ESD_REG,
	ESD_REG_NT35596,
	ESD_TE,
	ESD_MAX,
};

@@ -315,6 +317,7 @@ struct mdss_dsi_ctrl_pdata {
	u8 ctrl_state;
	int panel_mode;
	int irq_cnt;
	int disp_te_gpio;
	int rst_gpio;
	int disp_en_gpio;
	int bklt_en_gpio;
@@ -329,6 +332,7 @@ struct mdss_dsi_ctrl_pdata {
	int pwm_enabled;
	bool panel_bias_vreg;
	bool dsi_irq_line;
	atomic_t te_irq_ready;

	bool cmd_sync_wait_broadcast;
	bool cmd_sync_wait_trigger;
@@ -420,6 +424,7 @@ void mdss_dsi_controller_cfg(int enable,
void mdss_dsi_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl_pdata, bool restore);

irqreturn_t mdss_dsi_isr(int irq, void *ptr);
irqreturn_t hw_vsync_handler(int irq, void *data);
void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata);

void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata);
@@ -539,6 +544,13 @@ static inline bool mdss_dsi_is_ctrl_clk_slave(struct mdss_dsi_ctrl_pdata *ctrl)
		(ctrl->ndx == DSI_CTRL_CLK_SLAVE);
}

static inline bool mdss_dsi_is_te_based_esd(struct mdss_dsi_ctrl_pdata *ctrl)
{
	return (ctrl->status_mode == ESD_TE) &&
		gpio_is_valid(ctrl->disp_te_gpio) &&
		mdss_dsi_is_left_ctrl(ctrl);
}

static inline struct mdss_dsi_ctrl_pdata *mdss_dsi_get_ctrl_clk_master(void)
{
	return ctrl_list[DSI_CTRL_CLK_MASTER];
+5 −0
Original line number Diff line number Diff line
@@ -1589,6 +1589,11 @@ static int mdss_panel_parse_dt(struct device_node *np,
			ctrl_pdata->status_cmds_rlen = 8;
			ctrl_pdata->check_read_status =
						mdss_dsi_nt35596_read_status;
		} else if (!strcmp(data, "te_signal_check")) {
			if (pinfo->mipi.mode == DSI_CMD_MODE)
				ctrl_pdata->status_mode = ESD_TE;
			else
				pr_err("TE-ESD not valid for video mode\n");
		}
	}

Loading