1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
/* Copyright (c) 2013-2017, 2020, 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/workqueue.h>
#include <linux/delay.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include "mdss_dsi.h"
#include "mdss_mdp.h"
#include "mdss_debug.h"
/*
* 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 bool mdss_check_te_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata,
struct dsi_status_data *pstatus_data, uint32_t interval)
{
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,
msecs_to_jiffies(interval));
pr_debug("%s: TE IRQ line not enabled yet\n", __func__);
}
return ret;
}
/*
* mdss_check_dsi_ctrl_status() - Check MDP5 DSI controller status periodically.
* @work : dsi controller status data
* @interval : duration in milliseconds to schedule work queue
*
* This function calls check_status API on DSI controller to send the BTA
* command. If DSI controller fails to acknowledge the BTA command, it sends
* the PANEL_ALIVE=0 status to HAL layer.
*/
void mdss_check_dsi_ctrl_status(struct work_struct *work, uint32_t interval)
{
struct dsi_status_data *pstatus_data = NULL;
struct mdss_panel_data *pdata = NULL;
struct mipi_panel_info *mipi = NULL;
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
struct mdss_overlay_private *mdp5_data = NULL;
struct mdss_mdp_ctl *ctl = NULL;
int ret = 0;
pstatus_data = container_of(to_delayed_work(work),
struct dsi_status_data, check_status);
if (!pstatus_data || !(pstatus_data->mfd)) {
pr_err("%s: mfd not available\n", __func__);
return;
}
pdata = dev_get_platdata(&pstatus_data->mfd->pdev->dev);
if (!pdata) {
pr_err("%s: Panel data not available\n", __func__);
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 &&
(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_debug("%s: unblank not complete, reschedule check status\n",
__func__);
schedule_delayed_work(&pstatus_data->check_status,
msecs_to_jiffies(interval));
return;
}
mdp5_data = mfd_to_mdp5_data(pstatus_data->mfd);
ctl = mfd_to_ctl(pstatus_data->mfd);
if (!ctl) {
pr_err("%s: Display is off\n", __func__);
return;
}
if (ctrl_pdata->status_mode == ESD_TE) {
if (mdss_check_te_status(ctrl_pdata, pstatus_data, interval))
return;
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
* to acquire ov_lock in case of video mode. Removing this
* lock to fix issues so that ESD thread would not block other
* overlay operations. Need refine this lock for command mode
*
* If Burst mode is enabled then we dont have to acquire ov_lock as
* command and data arbitration is possible in h/w
*/
if ((mipi->mode == DSI_CMD_MODE) && !ctrl_pdata->burst_mode_enabled)
mutex_lock(&mdp5_data->ov_lock);
mutex_lock(&ctl->offlock);
if (mdss_panel_is_power_off(pstatus_data->mfd->panel_power_state) ||
pstatus_data->mfd->shutdown_pending) {
mutex_unlock(&ctl->offlock);
if ((mipi->mode == DSI_CMD_MODE) &&
!ctrl_pdata->burst_mode_enabled)
mutex_unlock(&mdp5_data->ov_lock);
pr_err("%s: DSI turning off, avoiding panel status check\n",
__func__);
return;
}
/*
* For the command mode panels, we return pan display
* IOCTL on vsync interrupt. So, after vsync interrupt comes
* and when DMA_P is in progress, if the panel stops responding
* and if we trigger BTA before DMA_P finishes, then the DSI
* FIFO will not be cleared since the DSI data bus control
* doesn't come back to the host after BTA. This may cause the
* display reset not to be proper. Hence, wait for DMA_P done
* for command mode panels before triggering BTA.
*/
if (ctl->ops.wait_pingpong && !ctrl_pdata->burst_mode_enabled)
ctl->ops.wait_pingpong(ctl, NULL);
pr_debug("%s: DSI ctrl wait for ping pong done\n", __func__);
MDSS_XLOG(mipi->mode);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
ret = ctrl_pdata->check_status(ctrl_pdata);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
mutex_unlock(&ctl->offlock);
if ((mipi->mode == DSI_CMD_MODE) && !ctrl_pdata->burst_mode_enabled)
mutex_unlock(&mdp5_data->ov_lock);
if (pstatus_data->mfd->panel_power_state == MDSS_PANEL_POWER_ON) {
if (ret > 0)
schedule_delayed_work(&pstatus_data->check_status,
msecs_to_jiffies(interval));
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(pstatus_data->mfd);
}
|