Sachin Bhayare | eeb8889 | 2018-01-02 16:36:01 +0530 | [diff] [blame] | 1 | /* Copyright (c) 2013-2015, 2017-2018, The Linux Foundation. All rights reserved. |
| 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | */ |
| 12 | #include <linux/workqueue.h> |
| 13 | #include <linux/delay.h> |
| 14 | #include <linux/kobject.h> |
| 15 | #include <linux/string.h> |
| 16 | #include <linux/sysfs.h> |
| 17 | #include <linux/interrupt.h> |
| 18 | |
| 19 | #include "mdss_dsi.h" |
| 20 | #include "mdp3_ctrl.h" |
Arun kumar | db96281 | 2018-05-30 16:31:52 +0530 | [diff] [blame] | 21 | #include "mdss_spi_panel.h" |
Sachin Bhayare | eeb8889 | 2018-01-02 16:36:01 +0530 | [diff] [blame] | 22 | |
| 23 | /* |
| 24 | * mdp3_check_te_status() - Check the status of panel for TE based ESD. |
| 25 | * @ctrl_pdata : dsi controller data |
| 26 | * @pstatus_data : dsi status data |
| 27 | * @interval : duration in milliseconds for panel TE wait |
| 28 | * |
| 29 | * This function waits for TE signal from the panel for a maximum |
| 30 | * duration of 3 vsyncs. If timeout occurs, report the panel to be |
| 31 | * dead due to ESD attack. |
| 32 | * NOTE: The TE IRQ handling is linked to the ESD thread scheduling, |
| 33 | * i.e. rate of TE IRQs firing is bound by the ESD interval. |
| 34 | */ |
| 35 | static int mdp3_check_te_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata, |
| 36 | struct dsi_status_data *pstatus_data, uint32_t interval) |
| 37 | { |
| 38 | int ret; |
| 39 | |
| 40 | pr_debug("%s: Checking panel TE status\n", __func__); |
| 41 | |
| 42 | atomic_set(&ctrl_pdata->te_irq_ready, 0); |
| 43 | reinit_completion(&ctrl_pdata->te_irq_comp); |
| 44 | enable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); |
| 45 | |
| 46 | ret = wait_for_completion_timeout(&ctrl_pdata->te_irq_comp, |
| 47 | msecs_to_jiffies(interval)); |
| 48 | |
| 49 | disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); |
| 50 | pr_debug("%s: Panel TE check done with ret = %d\n", __func__, ret); |
| 51 | |
| 52 | return ret; |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | * mdp3_check_dsi_ctrl_status() - Check MDP3 DSI controller status periodically. |
| 57 | * @work : dsi controller status data |
| 58 | * @interval : duration in milliseconds to schedule work queue |
| 59 | * |
| 60 | * This function calls check_status API on DSI controller to send the BTA |
| 61 | * command. If DSI controller fails to acknowledge the BTA command, it sends |
| 62 | * the PANEL_ALIVE=0 status to HAL layer. |
| 63 | */ |
| 64 | void mdp3_check_dsi_ctrl_status(struct work_struct *work, |
| 65 | uint32_t interval) |
| 66 | { |
| 67 | struct dsi_status_data *pdsi_status = NULL; |
| 68 | struct mdss_panel_data *pdata = NULL; |
| 69 | struct mipi_panel_info *mipi = NULL; |
| 70 | struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; |
| 71 | struct mdp3_session_data *mdp3_session = NULL; |
| 72 | int ret = 0; |
| 73 | |
| 74 | pdsi_status = container_of(to_delayed_work(work), |
| 75 | struct dsi_status_data, check_status); |
| 76 | |
| 77 | if (!pdsi_status || !(pdsi_status->mfd)) { |
| 78 | pr_err("%s: mfd not available\n", __func__); |
| 79 | return; |
| 80 | } |
| 81 | |
| 82 | pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev); |
| 83 | if (!pdata) { |
| 84 | pr_err("%s: Panel data not available\n", __func__); |
| 85 | return; |
| 86 | } |
| 87 | |
| 88 | mipi = &pdata->panel_info.mipi; |
| 89 | ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, |
| 90 | panel_data); |
| 91 | |
| 92 | if (!ctrl_pdata || (!ctrl_pdata->check_status && |
| 93 | (ctrl_pdata->status_mode != ESD_TE))) { |
| 94 | pr_err("%s: DSI ctrl or status_check callback not available\n", |
| 95 | __func__); |
| 96 | return; |
| 97 | } |
| 98 | |
| 99 | if (!pdata->panel_info.esd_rdy) { |
| 100 | pr_err("%s: unblank not complete, reschedule check status\n", |
| 101 | __func__); |
| 102 | schedule_delayed_work(&pdsi_status->check_status, |
| 103 | msecs_to_jiffies(interval)); |
| 104 | return; |
| 105 | } |
| 106 | |
| 107 | mdp3_session = pdsi_status->mfd->mdp.private1; |
| 108 | if (!mdp3_session) { |
| 109 | pr_err("%s: Display is off\n", __func__); |
| 110 | return; |
| 111 | } |
| 112 | |
| 113 | if (mdp3_session->in_splash_screen) { |
| 114 | schedule_delayed_work(&pdsi_status->check_status, |
| 115 | msecs_to_jiffies(interval)); |
| 116 | pr_debug("%s: cont splash is on\n", __func__); |
| 117 | return; |
| 118 | } |
| 119 | |
| 120 | if (mipi->mode == DSI_CMD_MODE && |
| 121 | mipi->hw_vsync_mode && |
| 122 | mdss_dsi_is_te_based_esd(ctrl_pdata)) { |
| 123 | uint32_t fps = mdss_panel_get_framerate(&pdata->panel_info, |
| 124 | FPS_RESOLUTION_HZ); |
| 125 | uint32_t timeout = ((1000 / fps) + 1) * |
| 126 | MDSS_STATUS_TE_WAIT_MAX; |
| 127 | |
| 128 | if (mdp3_check_te_status(ctrl_pdata, pdsi_status, timeout) > 0) |
| 129 | goto sim; |
| 130 | goto status_dead; |
| 131 | } |
| 132 | |
| 133 | mutex_lock(&mdp3_session->lock); |
| 134 | if (!mdp3_session->status) { |
| 135 | pr_debug("%s: display off already\n", __func__); |
| 136 | mutex_unlock(&mdp3_session->lock); |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | if (mdp3_session->wait_for_dma_done) |
| 141 | ret = mdp3_session->wait_for_dma_done(mdp3_session); |
| 142 | mutex_unlock(&mdp3_session->lock); |
| 143 | |
| 144 | if (!ret) |
| 145 | ret = ctrl_pdata->check_status(ctrl_pdata); |
| 146 | else |
| 147 | pr_err("%s: wait_for_dma_done error\n", __func__); |
| 148 | |
| 149 | if (mdss_fb_is_power_on_interactive(pdsi_status->mfd)) { |
| 150 | if (ret > 0) |
| 151 | schedule_delayed_work(&pdsi_status->check_status, |
| 152 | msecs_to_jiffies(interval)); |
| 153 | else |
| 154 | goto status_dead; |
| 155 | } |
| 156 | sim: |
| 157 | if (pdata->panel_info.panel_force_dead) { |
| 158 | pr_debug("force_dead=%d\n", pdata->panel_info.panel_force_dead); |
| 159 | pdata->panel_info.panel_force_dead--; |
| 160 | if (!pdata->panel_info.panel_force_dead) |
| 161 | goto status_dead; |
| 162 | } |
| 163 | return; |
| 164 | |
| 165 | status_dead: |
| 166 | mdss_fb_report_panel_dead(pdsi_status->mfd); |
| 167 | } |
| 168 | |
Arun kumar | db96281 | 2018-05-30 16:31:52 +0530 | [diff] [blame] | 169 | #if defined(CONFIG_FB_MSM_MDSS_SPI_PANEL) |
| 170 | void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval) |
| 171 | { |
| 172 | struct dsi_status_data *pdsi_status = NULL; |
| 173 | struct mdss_panel_data *pdata = NULL; |
| 174 | struct spi_panel_data *ctrl_pdata = NULL; |
| 175 | struct mdp3_session_data *mdp3_session = NULL; |
| 176 | int ret = 0; |
| 177 | |
| 178 | pdsi_status = container_of(to_delayed_work(work), |
| 179 | struct dsi_status_data, check_status); |
| 180 | |
| 181 | if (!pdsi_status || !(pdsi_status->mfd)) { |
| 182 | pr_err("%s: mfd not available\n", __func__); |
| 183 | return; |
| 184 | } |
| 185 | |
| 186 | pdata = dev_get_platdata(&pdsi_status->mfd->pdev->dev); |
| 187 | if (!pdata) { |
| 188 | pr_err("%s: panel data not available\n", __func__); |
| 189 | return; |
| 190 | } |
| 191 | |
| 192 | ctrl_pdata = container_of(pdata, struct spi_panel_data, panel_data); |
| 193 | if (!ctrl_pdata || !ctrl_pdata->check_status) { |
| 194 | pr_err("%s: Dsi ctrl or status_check callback not available\n", |
| 195 | __func__); |
| 196 | return; |
| 197 | } |
| 198 | |
| 199 | mdp3_session = pdsi_status->mfd->mdp.private1; |
| 200 | if (!mdp3_session) { |
| 201 | pr_err("%s: Display is off\n", __func__); |
| 202 | return; |
| 203 | } |
| 204 | |
| 205 | if (mdp3_session->in_splash_screen) { |
| 206 | schedule_delayed_work(&pdsi_status->check_status, |
| 207 | msecs_to_jiffies(interval)); |
| 208 | pr_debug("%s: cont splash is on\n", __func__); |
| 209 | return; |
| 210 | } |
| 211 | |
| 212 | mutex_lock(&mdp3_session->lock); |
| 213 | if (!mdp3_session->status) { |
| 214 | pr_debug("%s: display off already\n", __func__); |
| 215 | mutex_unlock(&mdp3_session->lock); |
| 216 | return; |
| 217 | } |
| 218 | |
| 219 | if (!ret) |
| 220 | ret = ctrl_pdata->check_status(ctrl_pdata); |
| 221 | else |
| 222 | pr_err("%s:wait_for_dma_done error\n", __func__); |
| 223 | mutex_unlock(&mdp3_session->lock); |
| 224 | |
| 225 | if (mdss_fb_is_power_on_interactive(pdsi_status->mfd)) { |
| 226 | if (ret > 0) { |
| 227 | schedule_delayed_work(&pdsi_status->check_status, |
| 228 | msecs_to_jiffies(interval)); |
| 229 | } else { |
| 230 | char *envp[2] = {"PANEL_ALIVE=0", NULL}; |
| 231 | |
| 232 | pdata->panel_info.panel_dead = true; |
| 233 | ret = kobject_uevent_env( |
| 234 | &pdsi_status->mfd->fbi->dev->kobj, KOBJ_CHANGE, envp); |
| 235 | pr_err("%s:panel has gone bad, sending uevent - %s\n", |
| 236 | __func__, envp[0]); |
| 237 | } |
| 238 | } |
| 239 | } |
| 240 | #else |
| 241 | void mdp3_check_spi_panel_status(struct work_struct *work, uint32_t interval) |
| 242 | { |
| 243 | } |
| 244 | #endif |