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

Commit f4976c00 authored by Shivaraj Shetty's avatar Shivaraj Shetty
Browse files

msm: mdss: esd recovery support for 8x10 targets



Add kernel side support for esd recovery for mdp3 targets. The
functionality of this driver is to check the panel status
periodicaly and send event to HAL if panel has gone bad.

Change-Id: I901e2908beb5de3f557da7d75cca876a40532efe
Signed-off-by: default avatarShivaraj Shetty <shivaraj@codeaurora.org>
parent f0778b70
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -42,4 +42,8 @@ obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o


obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o


ifeq ($(CONFIG_FB_MSM_MDSS_MDP3),y)
obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += dsi_status_v2.o
else
obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += mdss_dsi_status.o
obj-$(CONFIG_FB_MSM_MDSS_DSI_CTRL_STATUS) += mdss_dsi_status.o
endif
+34 −0
Original line number Original line Diff line number Diff line
@@ -220,6 +220,9 @@ irqreturn_t msm_dsi_isr_handler(int irq, void *ptr)


	spin_unlock(&ctrl->mdp_lock);
	spin_unlock(&ctrl->mdp_lock);


	if (isr & DSI_INTR_BTA_DONE)
		complete(&ctrl->bta_comp);

	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}


@@ -1166,6 +1169,35 @@ static int msm_dsi_cont_on(struct mdss_panel_data *pdata)
	return 0;
	return 0;
}
}


static int msm_dsi_bta_status_check(struct mdss_dsi_ctrl_pdata *ctrl_pdata)
{
	int ret = 0;

	if (ctrl_pdata == NULL) {
		pr_err("%s: Invalid input data\n", __func__);
		return 0;
	}

	mutex_lock(&ctrl_pdata->cmd_mutex);
	msm_dsi_set_irq(ctrl_pdata, DSI_INTR_BTA_DONE_MASK);
	INIT_COMPLETION(ctrl_pdata->bta_comp);

	/* BTA trigger */
	MIPI_OUTP(dsi_host_private->dsi_base + DSI_CMD_MODE_BTA_SW_TRIGGER,
									0x01);
	wmb();
	ret = wait_for_completion_killable_timeout(&ctrl_pdata->bta_comp,
									HZ/10);
	msm_dsi_clear_irq(ctrl_pdata, DSI_INTR_BTA_DONE_MASK);
	mutex_unlock(&ctrl_pdata->cmd_mutex);

	if (ret <= 0)
		pr_err("%s: DSI BTA error: %i\n", __func__, __LINE__);

	pr_debug("%s: BTA done with ret: %d\n", __func__, ret);
	return ret;
}

static void msm_dsi_debug_enable_clock(int on)
static void msm_dsi_debug_enable_clock(int on)
{
{
	if (dsi_host_private->debug_enable_clk)
	if (dsi_host_private->debug_enable_clk)
@@ -1329,6 +1361,7 @@ void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
{
{
	init_completion(&ctrl->dma_comp);
	init_completion(&ctrl->dma_comp);
	init_completion(&ctrl->mdp_comp);
	init_completion(&ctrl->mdp_comp);
	init_completion(&ctrl->bta_comp);
	init_completion(&ctrl->video_comp);
	init_completion(&ctrl->video_comp);
	spin_lock_init(&ctrl->irq_lock);
	spin_lock_init(&ctrl->irq_lock);
	spin_lock_init(&ctrl->mdp_lock);
	spin_lock_init(&ctrl->mdp_lock);
@@ -1339,6 +1372,7 @@ void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl)
	dsi_buf_alloc(&ctrl->rx_buf, SZ_4K);
	dsi_buf_alloc(&ctrl->rx_buf, SZ_4K);
	ctrl->cmdlist_commit = msm_dsi_cmdlist_commit;
	ctrl->cmdlist_commit = msm_dsi_cmdlist_commit;
	ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode;
	ctrl->panel_mode = ctrl->panel_data.panel_info.mipi.mode;
	ctrl->check_status = msm_dsi_bta_status_check;
}
}


static int msm_dsi_probe(struct platform_device *pdev)
static int msm_dsi_probe(struct platform_device *pdev)
+4 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,8 @@


#define DSI_INTR_ERROR_MASK			BIT(25)
#define DSI_INTR_ERROR_MASK			BIT(25)
#define DSI_INTR_ERROR				BIT(24)
#define DSI_INTR_ERROR				BIT(24)
#define DSI_INTR_BTA_DONE_MASK			BIT(21)
#define DSI_INTR_BTA_DONE			BIT(20)
#define DSI_INTR_VIDEO_DONE_MASK		BIT(17)
#define DSI_INTR_VIDEO_DONE_MASK		BIT(17)
#define DSI_INTR_VIDEO_DONE			BIT(16)
#define DSI_INTR_VIDEO_DONE			BIT(16)
#define DSI_INTR_CMD_MDP_DONE_MASK		BIT(9)
#define DSI_INTR_CMD_MDP_DONE_MASK		BIT(9)
@@ -24,6 +26,8 @@
#define DSI_INTR_CMD_DMA_DONE_MASK		BIT(1)
#define DSI_INTR_CMD_DMA_DONE_MASK		BIT(1)
#define DSI_INTR_CMD_DMA_DONE			BIT(0)
#define DSI_INTR_CMD_DMA_DONE			BIT(0)


#define DSI_BTA_TERM				BIT(1)

#define DSI_CTRL				0x0000
#define DSI_CTRL				0x0000
#define DSI_STATUS				0x0004
#define DSI_STATUS				0x0004
#define DSI_FIFO_STATUS			0x0008
#define DSI_FIFO_STATUS			0x0008
+188 −0
Original line number Original line Diff line number Diff line
/* Copyright (c) 2013, 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
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/notifier.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/iopoll.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>

#include "mdss_fb.h"
#include "mdss_dsi.h"
#include "mdss_panel.h"
#include "mdp3_ctrl.h"

#define STATUS_CHECK_INTERVAL 5000

/**
 * dsi_status_data - Stores all the data necessary for this module
 * @fb_notif: Used to egister for the fb events
 * @live_status: Delayed worker structure, used to associate the
 * delayed worker function
 * @mfd: Used to store the msm_fb_data_type received when the notifier
 * call back happens
 * @root: Stores the dir created by debuugfs
 * @debugfs_reset_panel: The debugfs variable used to inject errors
 */

struct dsi_status_data {
	struct notifier_block fb_notifier;
	struct delayed_work check_status;
	struct msm_fb_data_type *mfd;
	uint32_t check_interval;
};
static struct dsi_status_data *pstatus_data;
static uint32_t interval = STATUS_CHECK_INTERVAL;

static void check_dsi_ctrl_status(struct work_struct *work)
{
	struct dsi_status_data *pdsi_status = NULL;
	struct mdss_panel_data *pdata = NULL;
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
	struct mdp3_session_data *mdp3_session = NULL;
	int ret = 0;

	pdsi_status = container_of(to_delayed_work(work),
		struct dsi_status_data, check_status);
	if (!pdsi_status) {
		pr_err("%s: DSI status data not available\n", __func__);
		return;
	}

	pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev);
	if (!pdata) {
		pr_err("%s: Panel data not available\n", __func__);
		return;
	}
	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
							panel_data);
	if (!ctrl_pdata || !ctrl_pdata->check_status) {
		pr_err("%s: DSI ctrl or status_check callback not avilable\n",
								__func__);
		return;
	}
	mdp3_session = pdsi_status->mfd->mdp.private1;
	mutex_lock(&mdp3_session->lock);

	ret = ctrl_pdata->check_status(ctrl_pdata);

	mutex_unlock(&mdp3_session->lock);

	if ((pdsi_status->mfd->panel_power_on)) {
		if (ret > 0) {
			schedule_delayed_work(&pdsi_status->check_status,
				msecs_to_jiffies(pdsi_status->check_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]);
		}
	}
}

/**
 * fb_notifier_callback() - Call back function for the fb_register_client()
 * notifying events
 * @self  : notifier block
 * @event : The event that was triggered
 * @data  : Of type struct fb_event
 *
 * - This function listens for FB_BLANK_UNBLANK and FB_BLANK_POWERDOWN events
 * - Based on the event the delayed work is either scheduled again after
 * PANEL_STATUS_CHECK_INTERVAL or cancelled
 */
static int fb_event_callback(struct notifier_block *self,
				unsigned long event, void *data)
{
	struct fb_event *evdata = (struct fb_event *)data;
	struct dsi_status_data *pdata = container_of(self,
				struct dsi_status_data, fb_notifier);
	pdata->mfd = (struct msm_fb_data_type *)evdata->info->par;

	if (event == FB_EVENT_BLANK && evdata) {
		int *blank = evdata->data;
		switch (*blank) {
		case FB_BLANK_UNBLANK:
			schedule_delayed_work(&pdata->check_status,
			msecs_to_jiffies(STATUS_CHECK_INTERVAL));
			break;
		case FB_BLANK_POWERDOWN:
			cancel_delayed_work(&pdata->check_status);
			break;
		}
	}
	return 0;
}

int __init mdss_dsi_status_init(void)
{
	int rc;

	pstatus_data = kzalloc(sizeof(struct dsi_status_data), GFP_KERNEL);
	if (!pstatus_data) {
		pr_err("%s: can't alloc mem\n", __func__);
		rc = -ENOMEM;
		return rc;
	}

	memset(pstatus_data, 0, sizeof(struct dsi_status_data));

	pstatus_data->fb_notifier.notifier_call = fb_event_callback;

	rc = fb_register_client(&pstatus_data->fb_notifier);
	if (rc < 0) {
		pr_err("%s: fb_register_client failed, returned with rc=%d\n",
								__func__, rc);
		kfree(pstatus_data);
		return -EPERM;
	}

	pstatus_data->check_interval = interval;
	pr_info("%s: DSI status check interval:%d\n", __func__, interval);

	INIT_DELAYED_WORK(&pstatus_data->check_status, check_dsi_ctrl_status);

	pr_debug("%s: DSI ctrl status thread initialized\n", __func__);

	return rc;
}

void __exit mdss_dsi_status_exit(void)
{
	fb_unregister_client(&pstatus_data->fb_notifier);
	cancel_delayed_work_sync(&pstatus_data->check_status);
	kfree(pstatus_data);
	pr_debug("%s: DSI ctrl status thread removed\n", __func__);
}

module_param(interval, uint, 0);
MODULE_PARM_DESC(interval,
	"Duration in milliseconds to send BTA command for checking"
	"DSI status periodically");

module_init(mdss_dsi_status_init);
module_exit(mdss_dsi_status_exit);

MODULE_LICENSE("GPL v2");