aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2011-08-25 03:25:12 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2011-08-27 02:21:53 -0400
commit377dc5538c43052d2ee9bc89577cb07fe18f2520 (patch)
tree2183ea64740210993786f49f0e3561e814c41c52 /drivers/input/touchscreen
parent6a20baa9a40116715ebc99afe5b79b92f637ec23 (diff)
Input: tsc2007 - convert to threaded IRQ
Instead of using hard IRQ and workqueue solution switch to using threaded interrupt handler to simplify the code and locking rules. Tested-by: Thierry Reding <thierry.reding@avionic-design.de> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/tsc2007.c150
1 files changed, 66 insertions, 84 deletions
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index fadc11545b1e..e3aaf50ee8d3 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -66,7 +66,6 @@ struct ts_event {
66struct tsc2007 { 66struct tsc2007 {
67 struct input_dev *input; 67 struct input_dev *input;
68 char phys[32]; 68 char phys[32];
69 struct delayed_work work;
70 69
71 struct i2c_client *client; 70 struct i2c_client *client;
72 71
@@ -76,9 +75,11 @@ struct tsc2007 {
76 unsigned long poll_delay; 75 unsigned long poll_delay;
77 unsigned long poll_period; 76 unsigned long poll_period;
78 77
79 bool pendown;
80 int irq; 78 int irq;
81 79
80 wait_queue_head_t wait;
81 bool stopped;
82
82 int (*get_pendown_state)(void); 83 int (*get_pendown_state)(void);
83 void (*clear_penirq)(void); 84 void (*clear_penirq)(void);
84}; 85};
@@ -141,25 +142,8 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
141 return rt; 142 return rt;
142} 143}
143 144
144static void tsc2007_send_up_event(struct tsc2007 *tsc) 145static bool tsc2007_is_pen_down(struct tsc2007 *ts)
145{
146 struct input_dev *input = tsc->input;
147
148 dev_dbg(&tsc->client->dev, "UP\n");
149
150 input_report_key(input, BTN_TOUCH, 0);
151 input_report_abs(input, ABS_PRESSURE, 0);
152 input_sync(input);
153}
154
155static void tsc2007_work(struct work_struct *work)
156{ 146{
157 struct tsc2007 *ts =
158 container_of(to_delayed_work(work), struct tsc2007, work);
159 bool debounced = false;
160 struct ts_event tc;
161 u32 rt;
162
163 /* 147 /*
164 * NOTE: We can't rely on the pressure to determine the pen down 148 * NOTE: We can't rely on the pressure to determine the pen down
165 * state, even though this controller has a pressure sensor. 149 * state, even though this controller has a pressure sensor.
@@ -170,79 +154,82 @@ static void tsc2007_work(struct work_struct *work)
170 * The only safe way to check for the pen up condition is in the 154 * The only safe way to check for the pen up condition is in the
171 * work function by reading the pen signal state (it's a GPIO 155 * work function by reading the pen signal state (it's a GPIO
172 * and IRQ). Unfortunately such callback is not always available, 156 * and IRQ). Unfortunately such callback is not always available,
173 * in that case we have rely on the pressure anyway. 157 * in that case we assume that the pen is down and expect caller
158 * to fall back on the pressure reading.
174 */ 159 */
175 if (ts->get_pendown_state) {
176 if (unlikely(!ts->get_pendown_state())) {
177 tsc2007_send_up_event(ts);
178 ts->pendown = false;
179 goto out;
180 }
181 160
182 dev_dbg(&ts->client->dev, "pen is still down\n"); 161 if (!ts->get_pendown_state)
183 } 162 return true;
184 163
185 tsc2007_read_values(ts, &tc); 164 return ts->get_pendown_state();
165}
186 166
187 rt = tsc2007_calculate_pressure(ts, &tc); 167static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
188 if (rt > ts->max_rt) { 168{
189 /* 169 struct tsc2007 *ts = handle;
190 * Sample found inconsistent by debouncing or pressure is 170 struct input_dev *input = ts->input;
191 * beyond the maximum. Don't report it to user space, 171 struct ts_event tc;
192 * repeat at least once more the measurement. 172 u32 rt;
193 */
194 dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
195 debounced = true;
196 goto out;
197 173
198 } 174 while (!ts->stopped && tsc2007_is_pen_down(ts)) {
175
176 /* pen is down, continue with the measurement */
177 tsc2007_read_values(ts, &tc);
199 178
200 if (rt) { 179 rt = tsc2007_calculate_pressure(ts, &tc);
201 struct input_dev *input = ts->input;
202 180
203 if (!ts->pendown) { 181 if (rt == 0 && !ts->get_pendown_state) {
204 dev_dbg(&ts->client->dev, "DOWN\n"); 182 /*
183 * If pressure reported is 0 and we don't have
184 * callback to check pendown state, we have to
185 * assume that pen was lifted up.
186 */
187 break;
188 }
189
190 if (rt <= ts->max_rt) {
191 dev_dbg(&ts->client->dev,
192 "DOWN point(%4d,%4d), pressure (%4u)\n",
193 tc.x, tc.y, rt);
205 194
206 input_report_key(input, BTN_TOUCH, 1); 195 input_report_key(input, BTN_TOUCH, 1);
207 ts->pendown = true; 196 input_report_abs(input, ABS_X, tc.x);
197 input_report_abs(input, ABS_Y, tc.y);
198 input_report_abs(input, ABS_PRESSURE, rt);
199
200 input_sync(input);
201
202 } else {
203 /*
204 * Sample found inconsistent by debouncing or pressure is
205 * beyond the maximum. Don't report it to user space,
206 * repeat at least once more the measurement.
207 */
208 dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
208 } 209 }
209 210
210 input_report_abs(input, ABS_X, tc.x); 211 wait_event_timeout(ts->wait, ts->stopped,
211 input_report_abs(input, ABS_Y, tc.y); 212 msecs_to_jiffies(ts->poll_period));
212 input_report_abs(input, ABS_PRESSURE, rt); 213 }
213 214
214 input_sync(input); 215 dev_dbg(&ts->client->dev, "UP\n");
215 216
216 dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", 217 input_report_key(input, BTN_TOUCH, 0);
217 tc.x, tc.y, rt); 218 input_report_abs(input, ABS_PRESSURE, 0);
219 input_sync(input);
218 220
219 } else if (!ts->get_pendown_state && ts->pendown) { 221 if (ts->clear_penirq)
220 /* 222 ts->clear_penirq();
221 * We don't have callback to check pendown state, so we
222 * have to assume that since pressure reported is 0 the
223 * pen was lifted up.
224 */
225 tsc2007_send_up_event(ts);
226 ts->pendown = false;
227 }
228 223
229 out: 224 return IRQ_HANDLED;
230 if (ts->pendown || debounced)
231 schedule_delayed_work(&ts->work,
232 msecs_to_jiffies(ts->poll_period));
233 else
234 enable_irq(ts->irq);
235} 225}
236 226
237static irqreturn_t tsc2007_irq(int irq, void *handle) 227static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
238{ 228{
239 struct tsc2007 *ts = handle; 229 struct tsc2007 *ts = handle;
240 230
241 if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { 231 if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
242 disable_irq_nosync(ts->irq); 232 return IRQ_WAKE_THREAD;
243 schedule_delayed_work(&ts->work,
244 msecs_to_jiffies(ts->poll_delay));
245 }
246 233
247 if (ts->clear_penirq) 234 if (ts->clear_penirq)
248 ts->clear_penirq(); 235 ts->clear_penirq();
@@ -252,15 +239,10 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
252 239
253static void tsc2007_free_irq(struct tsc2007 *ts) 240static void tsc2007_free_irq(struct tsc2007 *ts)
254{ 241{
242 ts->stopped = true;
243 mb();
244 wake_up(&ts->wait);
255 free_irq(ts->irq, ts); 245 free_irq(ts->irq, ts);
256 if (cancel_delayed_work_sync(&ts->work)) {
257 /*
258 * Work was pending, therefore we need to enable
259 * IRQ here to balance the disable_irq() done in the
260 * interrupt handler.
261 */
262 enable_irq(ts->irq);
263 }
264} 246}
265 247
266static int __devinit tsc2007_probe(struct i2c_client *client, 248static int __devinit tsc2007_probe(struct i2c_client *client,
@@ -290,7 +272,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
290 ts->client = client; 272 ts->client = client;
291 ts->irq = client->irq; 273 ts->irq = client->irq;
292 ts->input = input_dev; 274 ts->input = input_dev;
293 INIT_DELAYED_WORK(&ts->work, tsc2007_work); 275 init_waitqueue_head(&ts->wait);
294 276
295 ts->model = pdata->model; 277 ts->model = pdata->model;
296 ts->x_plate_ohms = pdata->x_plate_ohms; 278 ts->x_plate_ohms = pdata->x_plate_ohms;
@@ -318,8 +300,8 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
318 if (pdata->init_platform_hw) 300 if (pdata->init_platform_hw)
319 pdata->init_platform_hw(); 301 pdata->init_platform_hw();
320 302
321 err = request_irq(ts->irq, tsc2007_irq, 0, 303 err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
322 client->dev.driver->name, ts); 304 IRQF_ONESHOT, client->dev.driver->name, ts);
323 if (err < 0) { 305 if (err < 0) {
324 dev_err(&client->dev, "irq %d busy?\n", ts->irq); 306 dev_err(&client->dev, "irq %d busy?\n", ts->irq);
325 goto err_free_mem; 307 goto err_free_mem;