diff options
author | Javier Martinez Canillas <javier@dowhile0.org> | 2012-01-31 03:18:00 -0500 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2012-01-31 03:18:36 -0500 |
commit | 4065d1e7b2164cff4af57b58fac887df2fe75d2a (patch) | |
tree | cb54ecc552144ba47a91bd2fec6b624e244485be /drivers/input/touchscreen/cyttsp_core.c | |
parent | 31175a8348af76aea2f557857c90467d13632dc3 (diff) |
Input: add Cypress TTSP capacitive multi-touch screen support
Cypress TrueTouch(tm) Standard Product controllers are found in
a wide range of embedded devices. This driver add support for a
variety of TTSP controllers.
Since the hardware is capable of tracking identifiable contacts, multi-touch
protocol type B (stateful) is used to report contact information.
The driver is composed of a core driver that process the data sent by
the contacts and a set of bus specific interface modules. This patch
adds the base core TTSP driver.
Signed-off-by: Javier Martinez Canillas <javier@dowhile0.org>
Reviewed-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/touchscreen/cyttsp_core.c')
-rw-r--r-- | drivers/input/touchscreen/cyttsp_core.c | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100644 index 000000000000..8be22479b41c --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c | |||
@@ -0,0 +1,625 @@ | |||
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++) { | ||
87 | error = ts->bus_ops->read(ts, command, length, buf); | ||
88 | if (!error) | ||
89 | return 0; | ||
90 | |||
91 | msleep(CY_DELAY_DFLT); | ||
92 | } | ||
93 | |||
94 | return -EIO; | ||
95 | } | ||
96 | |||
97 | static int ttsp_write_block_data(struct cyttsp *ts, u8 command, | ||
98 | u8 length, void *buf) | ||
99 | { | ||
100 | int error; | ||
101 | int tries; | ||
102 | |||
103 | for (tries = 0; tries < CY_NUM_RETRY; tries++) { | ||
104 | error = ts->bus_ops->write(ts, command, length, buf); | ||
105 | if (!error) | ||
106 | return 0; | ||
107 | |||
108 | msleep(CY_DELAY_DFLT); | ||
109 | } | ||
110 | |||
111 | return -EIO; | ||
112 | } | ||
113 | |||
114 | static int ttsp_send_command(struct cyttsp *ts, u8 cmd) | ||
115 | { | ||
116 | return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); | ||
117 | } | ||
118 | |||
119 | static int cyttsp_load_bl_regs(struct cyttsp *ts) | ||
120 | { | ||
121 | memset(&ts->bl_data, 0, sizeof(ts->bl_data)); | ||
122 | ts->bl_data.bl_status = 0x10; | ||
123 | |||
124 | return ttsp_read_block_data(ts, CY_REG_BASE, | ||
125 | sizeof(ts->bl_data), &ts->bl_data); | ||
126 | } | ||
127 | |||
128 | static int cyttsp_exit_bl_mode(struct cyttsp *ts) | ||
129 | { | ||
130 | int error; | ||
131 | u8 bl_cmd[sizeof(bl_command)]; | ||
132 | |||
133 | memcpy(bl_cmd, bl_command, sizeof(bl_command)); | ||
134 | if (ts->pdata->bl_keys) | ||
135 | memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], | ||
136 | ts->pdata->bl_keys, sizeof(bl_command)); | ||
137 | |||
138 | error = ttsp_write_block_data(ts, CY_REG_BASE, | ||
139 | sizeof(bl_cmd), bl_cmd); | ||
140 | if (error) | ||
141 | return error; | ||
142 | |||
143 | /* wait for TTSP Device to complete the operation */ | ||
144 | msleep(CY_DELAY_DFLT); | ||
145 | |||
146 | error = cyttsp_load_bl_regs(ts); | ||
147 | if (error) | ||
148 | return error; | ||
149 | |||
150 | if (GET_BOOTLOADERMODE(ts->bl_data.bl_status)) | ||
151 | return -EIO; | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static int cyttsp_set_operational_mode(struct cyttsp *ts) | ||
157 | { | ||
158 | int error; | ||
159 | |||
160 | error = ttsp_send_command(ts, CY_OPERATE_MODE); | ||
161 | if (error) | ||
162 | return error; | ||
163 | |||
164 | /* wait for TTSP Device to complete switch to Operational mode */ | ||
165 | error = ttsp_read_block_data(ts, CY_REG_BASE, | ||
166 | sizeof(ts->xy_data), &ts->xy_data); | ||
167 | if (error) | ||
168 | return error; | ||
169 | |||
170 | return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; | ||
171 | } | ||
172 | |||
173 | static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) | ||
174 | { | ||
175 | int error; | ||
176 | |||
177 | memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data)); | ||
178 | |||
179 | /* switch to sysinfo mode */ | ||
180 | error = ttsp_send_command(ts, CY_SYSINFO_MODE); | ||
181 | if (error) | ||
182 | return error; | ||
183 | |||
184 | /* read sysinfo registers */ | ||
185 | msleep(CY_DELAY_DFLT); | ||
186 | error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data), | ||
187 | &ts->sysinfo_data); | ||
188 | if (error) | ||
189 | return error; | ||
190 | |||
191 | if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) | ||
192 | return -EIO; | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) | ||
198 | { | ||
199 | int retval = 0; | ||
200 | |||
201 | if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || | ||
202 | ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || | ||
203 | ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { | ||
204 | |||
205 | u8 intrvl_ray[] = { | ||
206 | ts->pdata->act_intrvl, | ||
207 | ts->pdata->tch_tmout, | ||
208 | ts->pdata->lp_intrvl | ||
209 | }; | ||
210 | |||
211 | /* set intrvl registers */ | ||
212 | retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL, | ||
213 | sizeof(intrvl_ray), intrvl_ray); | ||
214 | msleep(CY_DELAY_DFLT); | ||
215 | } | ||
216 | |||
217 | return retval; | ||
218 | } | ||
219 | |||
220 | static int cyttsp_soft_reset(struct cyttsp *ts) | ||
221 | { | ||
222 | unsigned long timeout; | ||
223 | int retval; | ||
224 | |||
225 | /* wait for interrupt to set ready completion */ | ||
226 | INIT_COMPLETION(ts->bl_ready); | ||
227 | ts->state = CY_BL_STATE; | ||
228 | |||
229 | enable_irq(ts->irq); | ||
230 | |||
231 | retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE); | ||
232 | if (retval) | ||
233 | goto out; | ||
234 | |||
235 | timeout = wait_for_completion_timeout(&ts->bl_ready, | ||
236 | msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); | ||
237 | retval = timeout ? 0 : -EIO; | ||
238 | |||
239 | out: | ||
240 | ts->state = CY_IDLE_STATE; | ||
241 | disable_irq(ts->irq); | ||
242 | return retval; | ||
243 | } | ||
244 | |||
245 | static int cyttsp_act_dist_setup(struct cyttsp *ts) | ||
246 | { | ||
247 | u8 act_dist_setup = ts->pdata->act_dist; | ||
248 | |||
249 | /* Init gesture; active distance setup */ | ||
250 | return ttsp_write_block_data(ts, CY_REG_ACT_DIST, | ||
251 | sizeof(act_dist_setup), &act_dist_setup); | ||
252 | } | ||
253 | |||
254 | static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids) | ||
255 | { | ||
256 | ids[0] = xy_data->touch12_id >> 4; | ||
257 | ids[1] = xy_data->touch12_id & 0xF; | ||
258 | ids[2] = xy_data->touch34_id >> 4; | ||
259 | ids[3] = xy_data->touch34_id & 0xF; | ||
260 | } | ||
261 | |||
262 | static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data, | ||
263 | int idx) | ||
264 | { | ||
265 | switch (idx) { | ||
266 | case 0: | ||
267 | return &xy_data->tch1; | ||
268 | case 1: | ||
269 | return &xy_data->tch2; | ||
270 | case 2: | ||
271 | return &xy_data->tch3; | ||
272 | case 3: | ||
273 | return &xy_data->tch4; | ||
274 | default: | ||
275 | return NULL; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | static void cyttsp_report_tchdata(struct cyttsp *ts) | ||
280 | { | ||
281 | struct cyttsp_xydata *xy_data = &ts->xy_data; | ||
282 | struct input_dev *input = ts->input; | ||
283 | int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat); | ||
284 | const struct cyttsp_tch *tch; | ||
285 | int ids[CY_MAX_ID]; | ||
286 | int i; | ||
287 | DECLARE_BITMAP(used, CY_MAX_ID); | ||
288 | |||
289 | if (IS_LARGE_AREA(xy_data->tt_stat) == 1) { | ||
290 | /* terminate all active tracks */ | ||
291 | num_tch = 0; | ||
292 | dev_dbg(ts->dev, "%s: Large area detected\n", __func__); | ||
293 | } else if (num_tch > CY_MAX_FINGER) { | ||
294 | /* terminate all active tracks */ | ||
295 | num_tch = 0; | ||
296 | dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__); | ||
297 | } else if (IS_BAD_PKT(xy_data->tt_mode)) { | ||
298 | /* terminate all active tracks */ | ||
299 | num_tch = 0; | ||
300 | dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__); | ||
301 | } | ||
302 | |||
303 | cyttsp_extract_track_ids(xy_data, ids); | ||
304 | |||
305 | bitmap_zero(used, CY_MAX_ID); | ||
306 | |||
307 | for (i = 0; i < num_tch; i++) { | ||
308 | tch = cyttsp_get_tch(xy_data, i); | ||
309 | |||
310 | input_mt_slot(input, ids[i]); | ||
311 | input_mt_report_slot_state(input, MT_TOOL_FINGER, true); | ||
312 | input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x)); | ||
313 | input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y)); | ||
314 | input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z); | ||
315 | |||
316 | __set_bit(ids[i], used); | ||
317 | } | ||
318 | |||
319 | for (i = 0; i < CY_MAX_ID; i++) { | ||
320 | if (test_bit(i, used)) | ||
321 | continue; | ||
322 | |||
323 | input_mt_slot(input, i); | ||
324 | input_mt_report_slot_state(input, MT_TOOL_FINGER, false); | ||
325 | } | ||
326 | |||
327 | input_sync(input); | ||
328 | } | ||
329 | |||
330 | static irqreturn_t cyttsp_irq(int irq, void *handle) | ||
331 | { | ||
332 | struct cyttsp *ts = handle; | ||
333 | int error; | ||
334 | |||
335 | if (unlikely(ts->state == CY_BL_STATE)) { | ||
336 | complete(&ts->bl_ready); | ||
337 | goto out; | ||
338 | } | ||
339 | |||
340 | /* Get touch data from CYTTSP device */ | ||
341 | error = ttsp_read_block_data(ts, CY_REG_BASE, | ||
342 | sizeof(struct cyttsp_xydata), &ts->xy_data); | ||
343 | if (error) | ||
344 | goto out; | ||
345 | |||
346 | /* provide flow control handshake */ | ||
347 | if (ts->pdata->use_hndshk) { | ||
348 | error = ttsp_send_command(ts, | ||
349 | ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); | ||
350 | if (error) | ||
351 | goto out; | ||
352 | } | ||
353 | |||
354 | if (unlikely(ts->state == CY_IDLE_STATE)) | ||
355 | goto out; | ||
356 | |||
357 | if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) { | ||
358 | /* | ||
359 | * TTSP device has reset back to bootloader mode. | ||
360 | * Restore to operational mode. | ||
361 | */ | ||
362 | error = cyttsp_exit_bl_mode(ts); | ||
363 | if (error) { | ||
364 | dev_err(ts->dev, | ||
365 | "Could not return to operational mode, err: %d\n", | ||
366 | error); | ||
367 | ts->state = CY_IDLE_STATE; | ||
368 | } | ||
369 | } else { | ||
370 | cyttsp_report_tchdata(ts); | ||
371 | } | ||
372 | |||
373 | out: | ||
374 | return IRQ_HANDLED; | ||
375 | } | ||
376 | |||
377 | static int cyttsp_power_on(struct cyttsp *ts) | ||
378 | { | ||
379 | int error; | ||
380 | |||
381 | error = cyttsp_soft_reset(ts); | ||
382 | if (error) | ||
383 | return error; | ||
384 | |||
385 | error = cyttsp_load_bl_regs(ts); | ||
386 | if (error) | ||
387 | return error; | ||
388 | |||
389 | if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && | ||
390 | IS_VALID_APP(ts->bl_data.bl_status)) { | ||
391 | error = cyttsp_exit_bl_mode(ts); | ||
392 | if (error) | ||
393 | return error; | ||
394 | } | ||
395 | |||
396 | if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE || | ||
397 | IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) { | ||
398 | return -ENODEV; | ||
399 | } | ||
400 | |||
401 | error = cyttsp_set_sysinfo_mode(ts); | ||
402 | if (error) | ||
403 | return error; | ||
404 | |||
405 | error = cyttsp_set_sysinfo_regs(ts); | ||
406 | if (error) | ||
407 | return error; | ||
408 | |||
409 | error = cyttsp_set_operational_mode(ts); | ||
410 | if (error) | ||
411 | return error; | ||
412 | |||
413 | /* init active distance */ | ||
414 | error = cyttsp_act_dist_setup(ts); | ||
415 | if (error) | ||
416 | return error; | ||
417 | |||
418 | ts->state = CY_ACTIVE_STATE; | ||
419 | |||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | static int cyttsp_enable(struct cyttsp *ts) | ||
424 | { | ||
425 | int error; | ||
426 | |||
427 | /* | ||
428 | * The device firmware can wake on an I2C or SPI memory slave | ||
429 | * address match. So just reading a register is sufficient to | ||
430 | * wake up the device. The first read attempt will fail but it | ||
431 | * will wake it up making the second read attempt successful. | ||
432 | */ | ||
433 | error = ttsp_read_block_data(ts, CY_REG_BASE, | ||
434 | sizeof(ts->xy_data), &ts->xy_data); | ||
435 | if (error) | ||
436 | return error; | ||
437 | |||
438 | if (GET_HSTMODE(ts->xy_data.hst_mode)) | ||
439 | return -EIO; | ||
440 | |||
441 | enable_irq(ts->irq); | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static int cyttsp_disable(struct cyttsp *ts) | ||
447 | { | ||
448 | int error; | ||
449 | |||
450 | error = ttsp_send_command(ts, CY_LOW_POWER_MODE); | ||
451 | if (error) | ||
452 | return error; | ||
453 | |||
454 | disable_irq(ts->irq); | ||
455 | |||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | #ifdef CONFIG_PM_SLEEP | ||
460 | static int cyttsp_suspend(struct device *dev) | ||
461 | { | ||
462 | struct cyttsp *ts = dev_get_drvdata(dev); | ||
463 | int retval = 0; | ||
464 | |||
465 | mutex_lock(&ts->input->mutex); | ||
466 | |||
467 | if (ts->input->users) { | ||
468 | retval = cyttsp_disable(ts); | ||
469 | if (retval == 0) | ||
470 | ts->suspended = true; | ||
471 | } | ||
472 | |||
473 | mutex_unlock(&ts->input->mutex); | ||
474 | |||
475 | return retval; | ||
476 | } | ||
477 | |||
478 | static int cyttsp_resume(struct device *dev) | ||
479 | { | ||
480 | struct cyttsp *ts = dev_get_drvdata(dev); | ||
481 | |||
482 | mutex_lock(&ts->input->mutex); | ||
483 | |||
484 | if (ts->input->users) | ||
485 | cyttsp_enable(ts); | ||
486 | |||
487 | ts->suspended = false; | ||
488 | |||
489 | mutex_unlock(&ts->input->mutex); | ||
490 | |||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | #endif | ||
495 | |||
496 | SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume); | ||
497 | EXPORT_SYMBOL_GPL(cyttsp_pm_ops); | ||
498 | |||
499 | static int cyttsp_open(struct input_dev *dev) | ||
500 | { | ||
501 | struct cyttsp *ts = input_get_drvdata(dev); | ||
502 | int retval = 0; | ||
503 | |||
504 | if (!ts->suspended) | ||
505 | retval = cyttsp_enable(ts); | ||
506 | |||
507 | return retval; | ||
508 | } | ||
509 | |||
510 | static void cyttsp_close(struct input_dev *dev) | ||
511 | { | ||
512 | struct cyttsp *ts = input_get_drvdata(dev); | ||
513 | |||
514 | if (!ts->suspended) | ||
515 | cyttsp_disable(ts); | ||
516 | } | ||
517 | |||
518 | struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, | ||
519 | struct device *dev, int irq, size_t xfer_buf_size) | ||
520 | { | ||
521 | const struct cyttsp_platform_data *pdata = dev->platform_data; | ||
522 | struct cyttsp *ts; | ||
523 | struct input_dev *input_dev; | ||
524 | int error; | ||
525 | |||
526 | if (!dev || !bus_ops || !pdata || !pdata->name || irq <= 0) { | ||
527 | error = -EINVAL; | ||
528 | goto err_out; | ||
529 | } | ||
530 | |||
531 | ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); | ||
532 | input_dev = input_allocate_device(); | ||
533 | if (!ts || !input_dev) { | ||
534 | error = -ENOMEM; | ||
535 | goto err_free_mem; | ||
536 | } | ||
537 | |||
538 | ts->dev = dev; | ||
539 | ts->input = input_dev; | ||
540 | ts->pdata = dev->platform_data; | ||
541 | ts->bus_ops = bus_ops; | ||
542 | ts->irq = irq; | ||
543 | |||
544 | init_completion(&ts->bl_ready); | ||
545 | snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); | ||
546 | |||
547 | if (pdata->init) { | ||
548 | error = pdata->init(); | ||
549 | if (error) { | ||
550 | dev_err(ts->dev, "platform init failed, err: %d\n", | ||
551 | error); | ||
552 | goto err_free_mem; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | input_dev->name = pdata->name; | ||
557 | input_dev->phys = ts->phys; | ||
558 | input_dev->id.bustype = bus_ops->bustype; | ||
559 | input_dev->dev.parent = ts->dev; | ||
560 | |||
561 | input_dev->open = cyttsp_open; | ||
562 | input_dev->close = cyttsp_close; | ||
563 | |||
564 | input_set_drvdata(input_dev, ts); | ||
565 | |||
566 | __set_bit(EV_ABS, input_dev->evbit); | ||
567 | input_set_abs_params(input_dev, ABS_MT_POSITION_X, | ||
568 | 0, pdata->maxx, 0, 0); | ||
569 | input_set_abs_params(input_dev, ABS_MT_POSITION_Y, | ||
570 | 0, pdata->maxy, 0, 0); | ||
571 | input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, | ||
572 | 0, CY_MAXZ, 0, 0); | ||
573 | |||
574 | input_mt_init_slots(input_dev, CY_MAX_ID); | ||
575 | |||
576 | error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, | ||
577 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
578 | pdata->name, ts); | ||
579 | if (error) { | ||
580 | dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", | ||
581 | ts->irq, error); | ||
582 | goto err_platform_exit; | ||
583 | } | ||
584 | |||
585 | disable_irq(ts->irq); | ||
586 | |||
587 | error = cyttsp_power_on(ts); | ||
588 | if (error) | ||
589 | goto err_free_irq; | ||
590 | |||
591 | error = input_register_device(input_dev); | ||
592 | if (error) { | ||
593 | dev_err(ts->dev, "failed to register input device: %d\n", | ||
594 | error); | ||
595 | goto err_free_irq; | ||
596 | } | ||
597 | |||
598 | return ts; | ||
599 | |||
600 | err_free_irq: | ||
601 | free_irq(ts->irq, ts); | ||
602 | err_platform_exit: | ||
603 | if (pdata->exit) | ||
604 | pdata->exit(); | ||
605 | err_free_mem: | ||
606 | input_free_device(input_dev); | ||
607 | kfree(ts); | ||
608 | err_out: | ||
609 | return ERR_PTR(error); | ||
610 | } | ||
611 | EXPORT_SYMBOL_GPL(cyttsp_probe); | ||
612 | |||
613 | void cyttsp_remove(struct cyttsp *ts) | ||
614 | { | ||
615 | free_irq(ts->irq, ts); | ||
616 | input_unregister_device(ts->input); | ||
617 | if (ts->pdata->exit) | ||
618 | ts->pdata->exit(); | ||
619 | kfree(ts); | ||
620 | } | ||
621 | EXPORT_SYMBOL_GPL(cyttsp_remove); | ||
622 | |||
623 | MODULE_LICENSE("GPL"); | ||
624 | MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); | ||
625 | MODULE_AUTHOR("Cypress"); | ||