Commit | Line | Data |
---|---|---|
4065d1e7 JMC |
1 | /* |
2 | * Core Source for: | |
3 | * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. | |
4 | * For use with Cypress Txx3xx parts. | |
5 | * Supported parts include: | |
6 | * CY8CTST341 | |
7 | * CY8CTMA340 | |
8 | * | |
9 | * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. | |
10 | * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or | |
13 | * modify it under the terms of the GNU General Public License | |
14 | * version 2, and only version 2, as published by the | |
15 | * Free Software Foundation. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License along | |
23 | * with this program; if not, write to the Free Software Foundation, Inc., | |
24 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
25 | * | |
26 | * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com> | |
27 | * | |
28 | */ | |
29 | ||
30 | #include <linux/delay.h> | |
31 | #include <linux/input.h> | |
32 | #include <linux/input/mt.h> | |
33 | #include <linux/gpio.h> | |
34 | #include <linux/interrupt.h> | |
35 | #include <linux/slab.h> | |
36 | ||
37 | #include "cyttsp_core.h" | |
38 | ||
39 | /* Bootloader number of command keys */ | |
40 | #define CY_NUM_BL_KEYS 8 | |
41 | ||
42 | /* helpers */ | |
43 | #define GET_NUM_TOUCHES(x) ((x) & 0x0F) | |
44 | #define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) | |
45 | #define IS_BAD_PKT(x) ((x) & 0x20) | |
46 | #define IS_VALID_APP(x) ((x) & 0x01) | |
47 | #define IS_OPERATIONAL_ERR(x) ((x) & 0x3F) | |
48 | #define GET_HSTMODE(reg) (((reg) & 0x70) >> 4) | |
49 | #define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4) | |
50 | ||
51 | #define CY_REG_BASE 0x00 | |
52 | #define CY_REG_ACT_DIST 0x1E | |
53 | #define CY_REG_ACT_INTRVL 0x1D | |
54 | #define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1) | |
55 | #define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1) | |
56 | #define CY_MAXZ 255 | |
57 | #define CY_DELAY_DFLT 20 /* ms */ | |
58 | #define CY_DELAY_MAX 500 | |
59 | #define CY_ACT_DIST_DFLT 0xF8 | |
60 | #define CY_HNDSHK_BIT 0x80 | |
61 | /* device mode bits */ | |
62 | #define CY_OPERATE_MODE 0x00 | |
63 | #define CY_SYSINFO_MODE 0x10 | |
64 | /* power mode select bits */ | |
65 | #define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ | |
66 | #define CY_DEEP_SLEEP_MODE 0x02 | |
67 | #define CY_LOW_POWER_MODE 0x04 | |
68 | ||
69 | /* Slots management */ | |
70 | #define CY_MAX_FINGER 4 | |
71 | #define CY_MAX_ID 16 | |
72 | ||
73 | static const u8 bl_command[] = { | |
74 | 0x00, /* file offset */ | |
75 | 0xFF, /* command */ | |
76 | 0xA5, /* exit bootloader command */ | |
77 | 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */ | |
78 | }; | |
79 | ||
80 | static int ttsp_read_block_data(struct cyttsp *ts, u8 command, | |
81 | u8 length, void *buf) | |
82 | { | |
83 | int error; | |
84 | int tries; | |
85 | ||
86 | for (tries = 0; tries < CY_NUM_RETRY; tries++) { | |
9664877e FY |
87 | error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command, |
88 | length, buf); | |
4065d1e7 JMC |
89 | if (!error) |
90 | return 0; | |
91 | ||
92 | msleep(CY_DELAY_DFLT); | |
93 | } | |
94 | ||
95 | return -EIO; | |
96 | } | |
97 | ||
98 | static int ttsp_write_block_data(struct cyttsp *ts, u8 command, | |
99 | u8 length, void *buf) | |
100 | { | |
101 | int error; | |
102 | int tries; | |
103 | ||
104 | for (tries = 0; tries < CY_NUM_RETRY; tries++) { | |
9664877e FY |
105 | error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command, |
106 | length, buf); | |
4065d1e7 JMC |
107 | if (!error) |
108 | return 0; | |
109 | ||
110 | msleep(CY_DELAY_DFLT); | |
111 | } | |
112 | ||
113 | return -EIO; | |
114 | } | |
115 | ||
116 | static int ttsp_send_command(struct cyttsp *ts, u8 cmd) | |
117 | { | |
118 | return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); | |
119 | } | |
120 | ||
fbd5e77e FY |
121 | static int cyttsp_handshake(struct cyttsp *ts) |
122 | { | |
123 | if (ts->pdata->use_hndshk) | |
124 | return ttsp_send_command(ts, | |
125 | ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
4065d1e7 JMC |
130 | static int cyttsp_load_bl_regs(struct cyttsp *ts) |
131 | { | |
132 | memset(&ts->bl_data, 0, sizeof(ts->bl_data)); | |
133 | ts->bl_data.bl_status = 0x10; | |
134 | ||
135 | return ttsp_read_block_data(ts, CY_REG_BASE, | |
136 | sizeof(ts->bl_data), &ts->bl_data); | |
137 | } | |
138 | ||
139 | static int cyttsp_exit_bl_mode(struct cyttsp *ts) | |
140 | { | |
141 | int error; | |
142 | u8 bl_cmd[sizeof(bl_command)]; | |
143 | ||
144 | memcpy(bl_cmd, bl_command, sizeof(bl_command)); | |
145 | if (ts->pdata->bl_keys) | |
146 | memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], | |
d2983cdb | 147 | ts->pdata->bl_keys, CY_NUM_BL_KEYS); |
4065d1e7 JMC |
148 | |
149 | error = ttsp_write_block_data(ts, CY_REG_BASE, | |
150 | sizeof(bl_cmd), bl_cmd); | |
151 | if (error) | |
152 | return error; | |
153 | ||
154 | /* wait for TTSP Device to complete the operation */ | |
155 | msleep(CY_DELAY_DFLT); | |
156 | ||
157 | error = cyttsp_load_bl_regs(ts); | |
158 | if (error) | |
159 | return error; | |
160 | ||
161 | if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) | |
162 | return -EIO; | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
167 | static int cyttsp_set_operational_mode(struct cyttsp *ts) | |
168 | { | |
169 | int error; | |
170 | ||
171 | error = ttsp_send_command(ts, CY_OPERATE_MODE); | |
172 | if (error) | |
173 | return error; | |
174 | ||
175 | /* wait for TTSP Device to complete switch to Operational mode */ | |
176 | error = ttsp_read_block_data(ts, CY_REG_BASE, | |
177 | sizeof(ts->xy_data), &ts->xy_data); | |
178 | if (error) | |
179 | return error; | |
180 | ||
fbd5e77e FY |
181 | error = cyttsp_handshake(ts); |
182 | if (error) | |
183 | return error; | |
184 | ||
4065d1e7 JMC |
185 | return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; |
186 | } | |
187 | ||
188 | static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) | |
189 | { | |
190 | int error; | |
191 | ||
192 | memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); | |
193 | ||
194 | /* switch to sysinfo mode */ | |
195 | error = ttsp_send_command(ts, CY_SYSINFO_MODE); | |
196 | if (error) | |
197 | return error; | |
198 | ||
199 | /* read sysinfo registers */ | |
200 | msleep(CY_DELAY_DFLT); | |
201 | error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), | |
202 | &ts->sysinfo_data); | |
203 | if (error) | |
204 | return error; | |
205 | ||
fbd5e77e FY |
206 | error = cyttsp_handshake(ts); |
207 | if (error) | |
208 | return error; | |
209 | ||
4065d1e7 JMC |
210 | if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) |
211 | return -EIO; | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
216 | static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) | |
217 | { | |
218 | int retval = 0; | |
219 | ||
220 | if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || | |
221 | ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || | |
222 | ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { | |
223 | ||
224 | u8 intrvl_ray[] = { | |
225 | ts->pdata->act_intrvl, | |
226 | ts->pdata->tch_tmout, | |
227 | ts->pdata->lp_intrvl | |
228 | }; | |
229 | ||
230 | /* set intrvl registers */ | |
231 | retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, | |
232 | sizeof(intrvl_ray), intrvl_ray); | |
233 | msleep(CY_DELAY_DFLT); | |
234 | } | |
235 | ||
236 | return retval; | |
237 | } | |
238 | ||
239 | static int cyttsp_soft_reset(struct cyttsp *ts) | |
240 | { | |
241 | unsigned long timeout; | |
242 | int retval; | |
243 | ||
244 | /* wait for interrupt to set ready completion */ | |
16735d02 | 245 | reinit_completion(&ts->bl_ready); |
4065d1e7 JMC |
246 | ts->state = CY_BL_STATE; |
247 | ||
248 | enable_irq(ts->irq); | |
249 | ||
250 | retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); | |
251 | if (retval) | |
252 | goto out; | |
253 | ||
254 | timeout = wait_for_completion_timeout(&ts->bl_ready, | |
255 | msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); | |
256 | retval = timeout ? 0 : -EIO; | |
257 | ||
258 | out: | |
259 | ts->state = CY_IDLE_STATE; | |
260 | disable_irq(ts->irq); | |
261 | return retval; | |
262 | } | |
263 | ||
264 | static int cyttsp_act_dist_setup(struct cyttsp *ts) | |
265 | { | |
266 | u8 act_dist_setup = ts->pdata->act_dist; | |
267 | ||
268 | /* Init gesture; active distance setup */ | |
269 | return ttsp_write_block_data(ts, CY_REG_ACT_DIST, | |
270 | sizeof(act_dist_setup), &act_dist_setup); | |
271 | } | |
272 | ||
273 | static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) | |
274 | { | |
275 | ids[0] = xy_data->touch12_id >> 4; | |
276 | ids[1] = xy_data->touch12_id & 0xF; | |
277 | ids[2] = xy_data->touch34_id >> 4; | |
278 | ids[3] = xy_data->touch34_id & 0xF; | |
279 | } | |
280 | ||
281 | static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, | |
282 | int idx) | |
283 | { | |
284 | switch (idx) { | |
285 | case 0: | |
286 | return &xy_data->tch1; | |
287 | case 1: | |
288 | return &xy_data->tch2; | |
289 | case 2: | |
290 | return &xy_data->tch3; | |
291 | case 3: | |
292 | return &xy_data->tch4; | |
293 | default: | |
294 | return NULL; | |
295 | } | |
296 | } | |
297 | ||
298 | static void cyttsp_report_tchdata(struct cyttsp *ts) | |
299 | { | |
300 | struct cyttsp_xydata *xy_data = &ts->xy_data; | |
301 | struct input_dev *input = ts->input; | |
302 | int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); | |
303 | const struct cyttsp_tch *tch; | |
304 | int ids[CY_MAX_ID]; | |
305 | int i; | |
306 | DECLARE_BITMAP(used, CY_MAX_ID); | |
307 | ||
308 | if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { | |
309 | /* terminate all active tracks */ | |
310 | num_tch = 0; | |
311 | dev_dbg(ts->dev, "%s: Large area detected\n", __func__); | |
312 | } else if (num_tch > CY_MAX_FINGER) { | |
313 | /* terminate all active tracks */ | |
314 | num_tch = 0; | |
315 | dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); | |
316 | } else if (IS_BAD_PKT(xy_data->tt_mode)) { | |
317 | /* terminate all active tracks */ | |
318 | num_tch = 0; | |
319 | dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); | |
320 | } | |
321 | ||
322 | cyttsp_extract_track_ids(xy_data, ids); | |
323 | ||
324 | bitmap_zero(used, CY_MAX_ID); | |
325 | ||
326 | for (i = 0; i < num_tch; i++) { | |
327 | tch = cyttsp_get_tch(xy_data, i); | |
328 | ||
329 | input_mt_slot(input, ids[i]); | |
330 | input_mt_report_slot_state(input, MT_TOOL_FINGER, true); | |
331 | input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); | |
332 | input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); | |
333 | input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); | |
334 | ||
335 | __set_bit(ids[i], used); | |
336 | } | |
337 | ||
338 | for (i = 0; i < CY_MAX_ID; i++) { | |
339 | if (test_bit(i, used)) | |
340 | continue; | |
341 | ||
342 | input_mt_slot(input, i); | |
343 | input_mt_report_slot_state(input, MT_TOOL_FINGER, false); | |
344 | } | |
345 | ||
346 | input_sync(input); | |
347 | } | |
348 | ||
349 | static irqreturn_t cyttsp_irq(int irq, void *handle) | |
350 | { | |
351 | struct cyttsp *ts = handle; | |
352 | int error; | |
353 | ||
354 | if (unlikely(ts->state == CY_BL_STATE)) { | |
355 | complete(&ts->bl_ready); | |
356 | goto out; | |
357 | } | |
358 | ||
359 | /* Get touch data from CYTTSP device */ | |
360 | error = ttsp_read_block_data(ts, CY_REG_BASE, | |
361 | sizeof(struct cyttsp_xydata), &ts->xy_data); | |
362 | if (error) | |
363 | goto out; | |
364 | ||
365 | /* provide flow control handshake */ | |
fbd5e77e FY |
366 | error = cyttsp_handshake(ts); |
367 | if (error) | |
368 | goto out; | |
4065d1e7 JMC |
369 | |
370 | if (unlikely(ts->state == CY_IDLE_STATE)) | |
371 | goto out; | |
372 | ||
373 | if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { | |
374 | /* | |
375 | * TTSP device has reset back to bootloader mode. | |
376 | * Restore to operational mode. | |
377 | */ | |
378 | error = cyttsp_exit_bl_mode(ts); | |
379 | if (error) { | |
380 | dev_err(ts->dev, | |
381 | "Could not return to operational mode, err: %d\n", | |
382 | error); | |
383 | ts->state = CY_IDLE_STATE; | |
384 | } | |
385 | } else { | |
386 | cyttsp_report_tchdata(ts); | |
387 | } | |
388 | ||
389 | out: | |
390 | return IRQ_HANDLED; | |
391 | } | |
392 | ||
393 | static int cyttsp_power_on(struct cyttsp *ts) | |
394 | { | |
395 | int error; | |
396 | ||
397 | error = cyttsp_soft_reset(ts); | |
398 | if (error) | |
399 | return error; | |
400 | ||
401 | error = cyttsp_load_bl_regs(ts); | |
402 | if (error) | |
403 | return error; | |
404 | ||
405 | if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && | |
406 | IS_VALID_APP(ts->bl_data.bl_status)) { | |
407 | error = cyttsp_exit_bl_mode(ts); | |
408 | if (error) | |
409 | return error; | |
410 | } | |
411 | ||
412 | if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || | |
413 | IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { | |
414 | return -ENODEV; | |
415 | } | |
416 | ||
417 | error = cyttsp_set_sysinfo_mode(ts); | |
418 | if (error) | |
419 | return error; | |
420 | ||
421 | error = cyttsp_set_sysinfo_regs(ts); | |
422 | if (error) | |
423 | return error; | |
424 | ||
425 | error = cyttsp_set_operational_mode(ts); | |
426 | if (error) | |
427 | return error; | |
428 | ||
429 | /* init active distance */ | |
430 | error = cyttsp_act_dist_setup(ts); | |
431 | if (error) | |
432 | return error; | |
433 | ||
434 | ts->state = CY_ACTIVE_STATE; | |
435 | ||
436 | return 0; | |
437 | } | |
438 | ||
439 | static int cyttsp_enable(struct cyttsp *ts) | |
440 | { | |
441 | int error; | |
442 | ||
443 | /* | |
444 | * The device firmware can wake on an I2C or SPI memory slave | |
445 | * address match. So just reading a register is sufficient to | |
446 | * wake up the device. The first read attempt will fail but it | |
447 | * will wake it up making the second read attempt successful. | |
448 | */ | |
449 | error = ttsp_read_block_data(ts, CY_REG_BASE, | |
450 | sizeof(ts->xy_data), &ts->xy_data); | |
451 | if (error) | |
452 | return error; | |
453 | ||
454 | if (GET_HSTMODE(ts->xy_data.hst_mode)) | |
455 | return -EIO; | |
456 | ||
457 | enable_irq(ts->irq); | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
462 | static int cyttsp_disable(struct cyttsp *ts) | |
463 | { | |
464 | int error; | |
465 | ||
466 | error = ttsp_send_command(ts, CY_LOW_POWER_MODE); | |
467 | if (error) | |
468 | return error; | |
469 | ||
470 | disable_irq(ts->irq); | |
471 | ||
472 | return 0; | |
473 | } | |
474 | ||
02b6a58b | 475 | static int __maybe_unused cyttsp_suspend(struct device *dev) |
4065d1e7 JMC |
476 | { |
477 | struct cyttsp *ts = dev_get_drvdata(dev); | |
478 | int retval = 0; | |
479 | ||
480 | mutex_lock(&ts->input->mutex); | |
481 | ||
482 | if (ts->input->users) { | |
483 | retval = cyttsp_disable(ts); | |
484 | if (retval == 0) | |
485 | ts->suspended = true; | |
486 | } | |
487 | ||
488 | mutex_unlock(&ts->input->mutex); | |
489 | ||
490 | return retval; | |
491 | } | |
492 | ||
02b6a58b | 493 | static int __maybe_unused cyttsp_resume(struct device *dev) |
4065d1e7 JMC |
494 | { |
495 | struct cyttsp *ts = dev_get_drvdata(dev); | |
496 | ||
497 | mutex_lock(&ts->input->mutex); | |
498 | ||
499 | if (ts->input->users) | |
500 | cyttsp_enable(ts); | |
501 | ||
502 | ts->suspended = false; | |
503 | ||
504 | mutex_unlock(&ts->input->mutex); | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
4065d1e7 JMC |
509 | SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); |
510 | EXPORT_SYMBOL_GPL(cyttsp_pm_ops); | |
511 | ||
512 | static int cyttsp_open(struct input_dev *dev) | |
513 | { | |
514 | struct cyttsp *ts = input_get_drvdata(dev); | |
515 | int retval = 0; | |
516 | ||
517 | if (!ts->suspended) | |
518 | retval = cyttsp_enable(ts); | |
519 | ||
520 | return retval; | |
521 | } | |
522 | ||
523 | static void cyttsp_close(struct input_dev *dev) | |
524 | { | |
525 | struct cyttsp *ts = input_get_drvdata(dev); | |
526 | ||
527 | if (!ts->suspended) | |
528 | cyttsp_disable(ts); | |
529 | } | |
530 | ||
531 | struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, | |
532 | struct device *dev, int irq, size_t xfer_buf_size) | |
533 | { | |
c838cb3d | 534 | const struct cyttsp_platform_data *pdata = dev_get_platdata(dev); |
4065d1e7 JMC |
535 | struct cyttsp *ts; |
536 | struct input_dev *input_dev; | |
537 | int error; | |
538 | ||
aaa60fa7 | 539 | if (!pdata || !pdata->name || irq <= 0) { |
4065d1e7 JMC |
540 | error = -EINVAL; |
541 | goto err_out; | |
542 | } | |
543 | ||
544 | ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); | |
545 | input_dev = input_allocate_device(); | |
546 | if (!ts || !input_dev) { | |
547 | error = -ENOMEM; | |
548 | goto err_free_mem; | |
549 | } | |
550 | ||
551 | ts->dev = dev; | |
552 | ts->input = input_dev; | |
c838cb3d | 553 | ts->pdata = dev_get_platdata(dev); |
4065d1e7 JMC |
554 | ts->bus_ops = bus_ops; |
555 | ts->irq = irq; | |
556 | ||
557 | init_completion(&ts->bl_ready); | |
558 | snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); | |
559 | ||
560 | if (pdata->init) { | |
561 | error = pdata->init(); | |
562 | if (error) { | |
563 | dev_err(ts->dev, "platform init failed, err: %d\n", | |
564 | error); | |
565 | goto err_free_mem; | |
566 | } | |
567 | } | |
568 | ||
569 | input_dev->name = pdata->name; | |
570 | input_dev->phys = ts->phys; | |
571 | input_dev->id.bustype = bus_ops->bustype; | |
572 | input_dev->dev.parent = ts->dev; | |
573 | ||
574 | input_dev->open = cyttsp_open; | |
575 | input_dev->close = cyttsp_close; | |
576 | ||
577 | input_set_drvdata(input_dev, ts); | |
578 | ||
579 | __set_bit(EV_ABS, input_dev->evbit); | |
580 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, | |
581 | 0, pdata->maxx, 0, 0); | |
582 | input_set_abs_params(input_dev, ABS_MT_POSITION_Y, | |
583 | 0, pdata->maxy, 0, 0); | |
584 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | |
585 | 0, CY_MAXZ, 0, 0); | |
586 | ||
b4adbbef | 587 | input_mt_init_slots(input_dev, CY_MAX_ID, 0); |
4065d1e7 JMC |
588 | |
589 | error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, | |
590 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
591 | pdata->name, ts); | |
592 | if (error) { | |
593 | dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", | |
594 | ts->irq, error); | |
595 | goto err_platform_exit; | |
596 | } | |
597 | ||
598 | disable_irq(ts->irq); | |
599 | ||
600 | error = cyttsp_power_on(ts); | |
601 | if (error) | |
602 | goto err_free_irq; | |
603 | ||
604 | error = input_register_device(input_dev); | |
605 | if (error) { | |
606 | dev_err(ts->dev, "failed to register input device: %d\n", | |
607 | error); | |
608 | goto err_free_irq; | |
609 | } | |
610 | ||
611 | return ts; | |
612 | ||
613 | err_free_irq: | |
614 | free_irq(ts->irq, ts); | |
615 | err_platform_exit: | |
616 | if (pdata->exit) | |
617 | pdata->exit(); | |
618 | err_free_mem: | |
619 | input_free_device(input_dev); | |
620 | kfree(ts); | |
621 | err_out: | |
622 | return ERR_PTR(error); | |
623 | } | |
624 | EXPORT_SYMBOL_GPL(cyttsp_probe); | |
625 | ||
626 | void cyttsp_remove(struct cyttsp *ts) | |
627 | { | |
628 | free_irq(ts->irq, ts); | |
629 | input_unregister_device(ts->input); | |
630 | if (ts->pdata->exit) | |
631 | ts->pdata->exit(); | |
632 | kfree(ts); | |
633 | } | |
634 | EXPORT_SYMBOL_GPL(cyttsp_remove); | |
635 | ||
636 | MODULE_LICENSE("GPL"); | |
637 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); | |
638 | MODULE_AUTHOR("Cypress"); |