Loading drivers/video/msm/mdss/Makefile +4 −0 Original line number Diff line number Diff line Loading @@ -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 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 endif drivers/video/msm/mdss/dsi_host_v2.c +34 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,9 @@ irqreturn_t msm_dsi_isr_handler(int irq, void *ptr) spin_unlock(&ctrl->mdp_lock); if (isr & DSI_INTR_BTA_DONE) complete(&ctrl->bta_comp); return IRQ_HANDLED; } Loading Loading @@ -1171,6 +1174,35 @@ static int msm_dsi_cont_on(struct mdss_panel_data *pdata) 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) { if (dsi_host_private->debug_enable_clk) Loading Loading @@ -1334,6 +1366,7 @@ void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) { init_completion(&ctrl->dma_comp); init_completion(&ctrl->mdp_comp); init_completion(&ctrl->bta_comp); init_completion(&ctrl->video_comp); spin_lock_init(&ctrl->irq_lock); spin_lock_init(&ctrl->mdp_lock); Loading @@ -1344,6 +1377,7 @@ void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) dsi_buf_alloc(&ctrl->rx_buf, SZ_4K); ctrl->cmdlist_commit = msm_dsi_cmdlist_commit; 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) Loading drivers/video/msm/mdss/dsi_host_v2.h +4 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #define DSI_INTR_ERROR_MASK BIT(25) #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 BIT(16) #define DSI_INTR_CMD_MDP_DONE_MASK BIT(9) Loading @@ -24,6 +26,8 @@ #define DSI_INTR_CMD_DMA_DONE_MASK BIT(1) #define DSI_INTR_CMD_DMA_DONE BIT(0) #define DSI_BTA_TERM BIT(1) #define DSI_CTRL 0x0000 #define DSI_STATUS 0x0004 #define DSI_FIFO_STATUS 0x0008 Loading drivers/video/msm/mdss/dsi_status_v2.c 0 → 100644 +188 −0 Original line number 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"); Loading
drivers/video/msm/mdss/Makefile +4 −0 Original line number Diff line number Diff line Loading @@ -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 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 endif
drivers/video/msm/mdss/dsi_host_v2.c +34 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,9 @@ irqreturn_t msm_dsi_isr_handler(int irq, void *ptr) spin_unlock(&ctrl->mdp_lock); if (isr & DSI_INTR_BTA_DONE) complete(&ctrl->bta_comp); return IRQ_HANDLED; } Loading Loading @@ -1171,6 +1174,35 @@ static int msm_dsi_cont_on(struct mdss_panel_data *pdata) 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) { if (dsi_host_private->debug_enable_clk) Loading Loading @@ -1334,6 +1366,7 @@ void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) { init_completion(&ctrl->dma_comp); init_completion(&ctrl->mdp_comp); init_completion(&ctrl->bta_comp); init_completion(&ctrl->video_comp); spin_lock_init(&ctrl->irq_lock); spin_lock_init(&ctrl->mdp_lock); Loading @@ -1344,6 +1377,7 @@ void msm_dsi_ctrl_init(struct mdss_dsi_ctrl_pdata *ctrl) dsi_buf_alloc(&ctrl->rx_buf, SZ_4K); ctrl->cmdlist_commit = msm_dsi_cmdlist_commit; 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) Loading
drivers/video/msm/mdss/dsi_host_v2.h +4 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #define DSI_INTR_ERROR_MASK BIT(25) #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 BIT(16) #define DSI_INTR_CMD_MDP_DONE_MASK BIT(9) Loading @@ -24,6 +26,8 @@ #define DSI_INTR_CMD_DMA_DONE_MASK BIT(1) #define DSI_INTR_CMD_DMA_DONE BIT(0) #define DSI_BTA_TERM BIT(1) #define DSI_CTRL 0x0000 #define DSI_STATUS 0x0004 #define DSI_FIFO_STATUS 0x0008 Loading
drivers/video/msm/mdss/dsi_status_v2.c 0 → 100644 +188 −0 Original line number 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");