1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * TSC2004/TSC2005 touchscreen driver core |
4 | * |
5 | * Copyright (C) 2006-2010 Nokia Corporation |
6 | * Copyright (C) 2015 QWERTY Embedded Design |
7 | * Copyright (C) 2015 EMAC Inc. |
8 | * |
9 | * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com> |
10 | * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com> |
11 | */ |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/input.h> |
16 | #include <linux/input/touchscreen.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/delay.h> |
19 | #include <linux/pm.h> |
20 | #include <linux/of.h> |
21 | #include <linux/regulator/consumer.h> |
22 | #include <linux/regmap.h> |
23 | #include <linux/gpio/consumer.h> |
24 | #include "tsc200x-core.h" |
25 | |
26 | /* |
27 | * The touchscreen interface operates as follows: |
28 | * |
29 | * 1) Pen is pressed against the touchscreen. |
30 | * 2) TSC200X performs AD conversion. |
31 | * 3) After the conversion is done TSC200X drives DAV line down. |
32 | * 4) GPIO IRQ is received and tsc200x_irq_thread() is scheduled. |
33 | * 5) tsc200x_irq_thread() queues up a transfer to fetch the x, y, z1, z2 |
34 | * values. |
35 | * 6) tsc200x_irq_thread() reports coordinates to input layer and sets up |
36 | * tsc200x_penup_timer() to be called after TSC200X_PENUP_TIME_MS (40ms). |
37 | * 7) When the penup timer expires, there have not been touch or DAV interrupts |
38 | * during the last 40ms which means the pen has been lifted. |
39 | * |
40 | * ESD recovery via a hardware reset is done if the TSC200X doesn't respond |
41 | * after a configurable period (in ms) of activity. If esd_timeout is 0, the |
42 | * watchdog is disabled. |
43 | */ |
44 | |
45 | static const struct regmap_range tsc200x_writable_ranges[] = { |
46 | regmap_reg_range(TSC200X_REG_AUX_HIGH, TSC200X_REG_CFR2), |
47 | }; |
48 | |
49 | static const struct regmap_access_table tsc200x_writable_table = { |
50 | .yes_ranges = tsc200x_writable_ranges, |
51 | .n_yes_ranges = ARRAY_SIZE(tsc200x_writable_ranges), |
52 | }; |
53 | |
54 | const struct regmap_config tsc200x_regmap_config = { |
55 | .reg_bits = 8, |
56 | .val_bits = 16, |
57 | .reg_stride = 0x08, |
58 | .max_register = 0x78, |
59 | .read_flag_mask = TSC200X_REG_READ, |
60 | .write_flag_mask = TSC200X_REG_PND0, |
61 | .wr_table = &tsc200x_writable_table, |
62 | .use_single_read = true, |
63 | .use_single_write = true, |
64 | }; |
65 | EXPORT_SYMBOL_GPL(tsc200x_regmap_config); |
66 | |
67 | struct tsc200x_data { |
68 | u16 x; |
69 | u16 y; |
70 | u16 z1; |
71 | u16 z2; |
72 | } __packed; |
73 | #define TSC200X_DATA_REGS 4 |
74 | |
75 | struct tsc200x { |
76 | struct device *dev; |
77 | struct regmap *regmap; |
78 | __u16 bustype; |
79 | |
80 | struct input_dev *idev; |
81 | char phys[32]; |
82 | |
83 | struct mutex mutex; |
84 | |
85 | /* raw copy of previous x,y,z */ |
86 | int in_x; |
87 | int in_y; |
88 | int in_z1; |
89 | int in_z2; |
90 | |
91 | struct touchscreen_properties prop; |
92 | |
93 | spinlock_t lock; |
94 | struct timer_list penup_timer; |
95 | |
96 | unsigned int esd_timeout; |
97 | struct delayed_work esd_work; |
98 | unsigned long last_valid_interrupt; |
99 | |
100 | unsigned int x_plate_ohm; |
101 | |
102 | bool opened; |
103 | bool suspended; |
104 | |
105 | bool pen_down; |
106 | |
107 | struct regulator *vio; |
108 | |
109 | struct gpio_desc *reset_gpio; |
110 | int (*tsc200x_cmd)(struct device *dev, u8 cmd); |
111 | int irq; |
112 | }; |
113 | |
114 | static void tsc200x_update_pen_state(struct tsc200x *ts, |
115 | int x, int y, int pressure) |
116 | { |
117 | if (pressure) { |
118 | touchscreen_report_pos(input: ts->idev, prop: &ts->prop, x, y, multitouch: false); |
119 | input_report_abs(dev: ts->idev, ABS_PRESSURE, value: pressure); |
120 | if (!ts->pen_down) { |
121 | input_report_key(dev: ts->idev, BTN_TOUCH, value: !!pressure); |
122 | ts->pen_down = true; |
123 | } |
124 | } else { |
125 | input_report_abs(dev: ts->idev, ABS_PRESSURE, value: 0); |
126 | if (ts->pen_down) { |
127 | input_report_key(dev: ts->idev, BTN_TOUCH, value: 0); |
128 | ts->pen_down = false; |
129 | } |
130 | } |
131 | input_sync(dev: ts->idev); |
132 | dev_dbg(ts->dev, "point(%4d,%4d), pressure (%4d)\n" , x, y, |
133 | pressure); |
134 | } |
135 | |
136 | static irqreturn_t tsc200x_irq_thread(int irq, void *_ts) |
137 | { |
138 | struct tsc200x *ts = _ts; |
139 | unsigned long flags; |
140 | unsigned int pressure; |
141 | struct tsc200x_data tsdata; |
142 | int error; |
143 | |
144 | /* read the coordinates */ |
145 | error = regmap_bulk_read(map: ts->regmap, TSC200X_REG_X, val: &tsdata, |
146 | TSC200X_DATA_REGS); |
147 | if (unlikely(error)) |
148 | goto out; |
149 | |
150 | /* validate position */ |
151 | if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT)) |
152 | goto out; |
153 | |
154 | /* Skip reading if the pressure components are out of range */ |
155 | if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT)) |
156 | goto out; |
157 | if (unlikely(tsdata.z1 >= tsdata.z2)) |
158 | goto out; |
159 | |
160 | /* |
161 | * Skip point if this is a pen down with the exact same values as |
162 | * the value before pen-up - that implies SPI fed us stale data |
163 | */ |
164 | if (!ts->pen_down && |
165 | ts->in_x == tsdata.x && ts->in_y == tsdata.y && |
166 | ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) { |
167 | goto out; |
168 | } |
169 | |
170 | /* |
171 | * At this point we are happy we have a valid and useful reading. |
172 | * Remember it for later comparisons. We may now begin downsampling. |
173 | */ |
174 | ts->in_x = tsdata.x; |
175 | ts->in_y = tsdata.y; |
176 | ts->in_z1 = tsdata.z1; |
177 | ts->in_z2 = tsdata.z2; |
178 | |
179 | /* Compute touch pressure resistance using equation #1 */ |
180 | pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1; |
181 | pressure = pressure * ts->x_plate_ohm / 4096; |
182 | if (unlikely(pressure > MAX_12BIT)) |
183 | goto out; |
184 | |
185 | spin_lock_irqsave(&ts->lock, flags); |
186 | |
187 | tsc200x_update_pen_state(ts, x: tsdata.x, y: tsdata.y, pressure); |
188 | mod_timer(timer: &ts->penup_timer, |
189 | expires: jiffies + msecs_to_jiffies(TSC200X_PENUP_TIME_MS)); |
190 | |
191 | spin_unlock_irqrestore(lock: &ts->lock, flags); |
192 | |
193 | ts->last_valid_interrupt = jiffies; |
194 | out: |
195 | return IRQ_HANDLED; |
196 | } |
197 | |
198 | static void tsc200x_penup_timer(struct timer_list *t) |
199 | { |
200 | struct tsc200x *ts = from_timer(ts, t, penup_timer); |
201 | unsigned long flags; |
202 | |
203 | spin_lock_irqsave(&ts->lock, flags); |
204 | tsc200x_update_pen_state(ts, x: 0, y: 0, pressure: 0); |
205 | spin_unlock_irqrestore(lock: &ts->lock, flags); |
206 | } |
207 | |
208 | static void tsc200x_start_scan(struct tsc200x *ts) |
209 | { |
210 | regmap_write(map: ts->regmap, TSC200X_REG_CFR0, TSC200X_CFR0_INITVALUE); |
211 | regmap_write(map: ts->regmap, TSC200X_REG_CFR1, TSC200X_CFR1_INITVALUE); |
212 | regmap_write(map: ts->regmap, TSC200X_REG_CFR2, TSC200X_CFR2_INITVALUE); |
213 | ts->tsc200x_cmd(ts->dev, TSC200X_CMD_NORMAL); |
214 | } |
215 | |
216 | static void tsc200x_stop_scan(struct tsc200x *ts) |
217 | { |
218 | ts->tsc200x_cmd(ts->dev, TSC200X_CMD_STOP); |
219 | } |
220 | |
221 | static void tsc200x_reset(struct tsc200x *ts) |
222 | { |
223 | if (ts->reset_gpio) { |
224 | gpiod_set_value_cansleep(desc: ts->reset_gpio, value: 1); |
225 | usleep_range(min: 100, max: 500); /* only 10us required */ |
226 | gpiod_set_value_cansleep(desc: ts->reset_gpio, value: 0); |
227 | } |
228 | } |
229 | |
230 | /* must be called with ts->mutex held */ |
231 | static void __tsc200x_disable(struct tsc200x *ts) |
232 | { |
233 | tsc200x_stop_scan(ts); |
234 | |
235 | disable_irq(irq: ts->irq); |
236 | del_timer_sync(timer: &ts->penup_timer); |
237 | |
238 | cancel_delayed_work_sync(dwork: &ts->esd_work); |
239 | |
240 | enable_irq(irq: ts->irq); |
241 | } |
242 | |
243 | /* must be called with ts->mutex held */ |
244 | static void __tsc200x_enable(struct tsc200x *ts) |
245 | { |
246 | tsc200x_start_scan(ts); |
247 | |
248 | if (ts->esd_timeout && ts->reset_gpio) { |
249 | ts->last_valid_interrupt = jiffies; |
250 | schedule_delayed_work(dwork: &ts->esd_work, |
251 | delay: round_jiffies_relative( |
252 | j: msecs_to_jiffies(m: ts->esd_timeout))); |
253 | } |
254 | } |
255 | |
256 | static ssize_t tsc200x_selftest_show(struct device *dev, |
257 | struct device_attribute *attr, |
258 | char *buf) |
259 | { |
260 | struct tsc200x *ts = dev_get_drvdata(dev); |
261 | unsigned int temp_high; |
262 | unsigned int temp_high_orig; |
263 | unsigned int temp_high_test; |
264 | bool success = true; |
265 | int error; |
266 | |
267 | mutex_lock(&ts->mutex); |
268 | |
269 | /* |
270 | * Test TSC200X communications via temp high register. |
271 | */ |
272 | __tsc200x_disable(ts); |
273 | |
274 | error = regmap_read(map: ts->regmap, TSC200X_REG_TEMP_HIGH, val: &temp_high_orig); |
275 | if (error) { |
276 | dev_warn(dev, "selftest failed: read error %d\n" , error); |
277 | success = false; |
278 | goto out; |
279 | } |
280 | |
281 | temp_high_test = (temp_high_orig - 1) & MAX_12BIT; |
282 | |
283 | error = regmap_write(map: ts->regmap, TSC200X_REG_TEMP_HIGH, val: temp_high_test); |
284 | if (error) { |
285 | dev_warn(dev, "selftest failed: write error %d\n" , error); |
286 | success = false; |
287 | goto out; |
288 | } |
289 | |
290 | error = regmap_read(map: ts->regmap, TSC200X_REG_TEMP_HIGH, val: &temp_high); |
291 | if (error) { |
292 | dev_warn(dev, "selftest failed: read error %d after write\n" , |
293 | error); |
294 | success = false; |
295 | goto out; |
296 | } |
297 | |
298 | if (temp_high != temp_high_test) { |
299 | dev_warn(dev, "selftest failed: %d != %d\n" , |
300 | temp_high, temp_high_test); |
301 | success = false; |
302 | } |
303 | |
304 | /* hardware reset */ |
305 | tsc200x_reset(ts); |
306 | |
307 | if (!success) |
308 | goto out; |
309 | |
310 | /* test that the reset really happened */ |
311 | error = regmap_read(map: ts->regmap, TSC200X_REG_TEMP_HIGH, val: &temp_high); |
312 | if (error) { |
313 | dev_warn(dev, "selftest failed: read error %d after reset\n" , |
314 | error); |
315 | success = false; |
316 | goto out; |
317 | } |
318 | |
319 | if (temp_high != temp_high_orig) { |
320 | dev_warn(dev, "selftest failed after reset: %d != %d\n" , |
321 | temp_high, temp_high_orig); |
322 | success = false; |
323 | } |
324 | |
325 | out: |
326 | __tsc200x_enable(ts); |
327 | mutex_unlock(lock: &ts->mutex); |
328 | |
329 | return sprintf(buf, fmt: "%d\n" , success); |
330 | } |
331 | |
332 | static DEVICE_ATTR(selftest, S_IRUGO, tsc200x_selftest_show, NULL); |
333 | |
334 | static struct attribute *tsc200x_attrs[] = { |
335 | &dev_attr_selftest.attr, |
336 | NULL |
337 | }; |
338 | |
339 | static umode_t tsc200x_attr_is_visible(struct kobject *kobj, |
340 | struct attribute *attr, int n) |
341 | { |
342 | struct device *dev = kobj_to_dev(kobj); |
343 | struct tsc200x *ts = dev_get_drvdata(dev); |
344 | umode_t mode = attr->mode; |
345 | |
346 | if (attr == &dev_attr_selftest.attr) { |
347 | if (!ts->reset_gpio) |
348 | mode = 0; |
349 | } |
350 | |
351 | return mode; |
352 | } |
353 | |
354 | static const struct attribute_group tsc200x_attr_group = { |
355 | .is_visible = tsc200x_attr_is_visible, |
356 | .attrs = tsc200x_attrs, |
357 | }; |
358 | |
359 | static void tsc200x_esd_work(struct work_struct *work) |
360 | { |
361 | struct tsc200x *ts = container_of(work, struct tsc200x, esd_work.work); |
362 | int error; |
363 | unsigned int r; |
364 | |
365 | if (!mutex_trylock(lock: &ts->mutex)) { |
366 | /* |
367 | * If the mutex is taken, it means that disable or enable is in |
368 | * progress. In that case just reschedule the work. If the work |
369 | * is not needed, it will be canceled by disable. |
370 | */ |
371 | goto reschedule; |
372 | } |
373 | |
374 | if (time_is_after_jiffies(ts->last_valid_interrupt + |
375 | msecs_to_jiffies(ts->esd_timeout))) |
376 | goto out; |
377 | |
378 | /* We should be able to read register without disabling interrupts. */ |
379 | error = regmap_read(map: ts->regmap, TSC200X_REG_CFR0, val: &r); |
380 | if (!error && |
381 | !((r ^ TSC200X_CFR0_INITVALUE) & TSC200X_CFR0_RW_MASK)) { |
382 | goto out; |
383 | } |
384 | |
385 | /* |
386 | * If we could not read our known value from configuration register 0 |
387 | * then we should reset the controller as if from power-up and start |
388 | * scanning again. |
389 | */ |
390 | dev_info(ts->dev, "TSC200X not responding - resetting\n" ); |
391 | |
392 | disable_irq(irq: ts->irq); |
393 | del_timer_sync(timer: &ts->penup_timer); |
394 | |
395 | tsc200x_update_pen_state(ts, x: 0, y: 0, pressure: 0); |
396 | |
397 | tsc200x_reset(ts); |
398 | |
399 | enable_irq(irq: ts->irq); |
400 | tsc200x_start_scan(ts); |
401 | |
402 | out: |
403 | mutex_unlock(lock: &ts->mutex); |
404 | reschedule: |
405 | /* re-arm the watchdog */ |
406 | schedule_delayed_work(dwork: &ts->esd_work, |
407 | delay: round_jiffies_relative( |
408 | j: msecs_to_jiffies(m: ts->esd_timeout))); |
409 | } |
410 | |
411 | static int tsc200x_open(struct input_dev *input) |
412 | { |
413 | struct tsc200x *ts = input_get_drvdata(dev: input); |
414 | |
415 | mutex_lock(&ts->mutex); |
416 | |
417 | if (!ts->suspended) |
418 | __tsc200x_enable(ts); |
419 | |
420 | ts->opened = true; |
421 | |
422 | mutex_unlock(lock: &ts->mutex); |
423 | |
424 | return 0; |
425 | } |
426 | |
427 | static void tsc200x_close(struct input_dev *input) |
428 | { |
429 | struct tsc200x *ts = input_get_drvdata(dev: input); |
430 | |
431 | mutex_lock(&ts->mutex); |
432 | |
433 | if (!ts->suspended) |
434 | __tsc200x_disable(ts); |
435 | |
436 | ts->opened = false; |
437 | |
438 | mutex_unlock(lock: &ts->mutex); |
439 | } |
440 | |
441 | int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, |
442 | struct regmap *regmap, |
443 | int (*tsc200x_cmd)(struct device *dev, u8 cmd)) |
444 | { |
445 | struct tsc200x *ts; |
446 | struct input_dev *input_dev; |
447 | u32 x_plate_ohm; |
448 | u32 esd_timeout; |
449 | int error; |
450 | |
451 | if (irq <= 0) { |
452 | dev_err(dev, "no irq\n" ); |
453 | return -ENODEV; |
454 | } |
455 | |
456 | if (IS_ERR(ptr: regmap)) |
457 | return PTR_ERR(ptr: regmap); |
458 | |
459 | if (!tsc200x_cmd) { |
460 | dev_err(dev, "no cmd function\n" ); |
461 | return -ENODEV; |
462 | } |
463 | |
464 | ts = devm_kzalloc(dev, size: sizeof(*ts), GFP_KERNEL); |
465 | if (!ts) |
466 | return -ENOMEM; |
467 | |
468 | input_dev = devm_input_allocate_device(dev); |
469 | if (!input_dev) |
470 | return -ENOMEM; |
471 | |
472 | ts->irq = irq; |
473 | ts->dev = dev; |
474 | ts->idev = input_dev; |
475 | ts->regmap = regmap; |
476 | ts->tsc200x_cmd = tsc200x_cmd; |
477 | |
478 | error = device_property_read_u32(dev, propname: "ti,x-plate-ohms" , val: &x_plate_ohm); |
479 | ts->x_plate_ohm = error ? TSC200X_DEF_RESISTOR : x_plate_ohm; |
480 | |
481 | error = device_property_read_u32(dev, propname: "ti,esd-recovery-timeout-ms" , |
482 | val: &esd_timeout); |
483 | ts->esd_timeout = error ? 0 : esd_timeout; |
484 | |
485 | ts->reset_gpio = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
486 | if (IS_ERR(ptr: ts->reset_gpio)) { |
487 | error = PTR_ERR(ptr: ts->reset_gpio); |
488 | dev_err(dev, "error acquiring reset gpio: %d\n" , error); |
489 | return error; |
490 | } |
491 | |
492 | ts->vio = devm_regulator_get(dev, id: "vio" ); |
493 | if (IS_ERR(ptr: ts->vio)) { |
494 | error = PTR_ERR(ptr: ts->vio); |
495 | dev_err(dev, "error acquiring vio regulator: %d" , error); |
496 | return error; |
497 | } |
498 | |
499 | mutex_init(&ts->mutex); |
500 | |
501 | spin_lock_init(&ts->lock); |
502 | timer_setup(&ts->penup_timer, tsc200x_penup_timer, 0); |
503 | |
504 | INIT_DELAYED_WORK(&ts->esd_work, tsc200x_esd_work); |
505 | |
506 | snprintf(buf: ts->phys, size: sizeof(ts->phys), |
507 | fmt: "%s/input-ts" , dev_name(dev)); |
508 | |
509 | if (tsc_id->product == 2004) { |
510 | input_dev->name = "TSC200X touchscreen" ; |
511 | } else { |
512 | input_dev->name = devm_kasprintf(dev, GFP_KERNEL, |
513 | fmt: "TSC%04d touchscreen" , |
514 | tsc_id->product); |
515 | if (!input_dev->name) |
516 | return -ENOMEM; |
517 | } |
518 | |
519 | input_dev->phys = ts->phys; |
520 | input_dev->id = *tsc_id; |
521 | |
522 | input_dev->open = tsc200x_open; |
523 | input_dev->close = tsc200x_close; |
524 | |
525 | input_set_drvdata(dev: input_dev, data: ts); |
526 | |
527 | __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); |
528 | input_set_capability(dev: input_dev, EV_KEY, BTN_TOUCH); |
529 | |
530 | input_set_abs_params(dev: input_dev, ABS_X, |
531 | min: 0, MAX_12BIT, TSC200X_DEF_X_FUZZ, flat: 0); |
532 | input_set_abs_params(dev: input_dev, ABS_Y, |
533 | min: 0, MAX_12BIT, TSC200X_DEF_Y_FUZZ, flat: 0); |
534 | input_set_abs_params(dev: input_dev, ABS_PRESSURE, |
535 | min: 0, MAX_12BIT, TSC200X_DEF_P_FUZZ, flat: 0); |
536 | |
537 | touchscreen_parse_properties(input: input_dev, multitouch: false, prop: &ts->prop); |
538 | |
539 | /* Ensure the touchscreen is off */ |
540 | tsc200x_stop_scan(ts); |
541 | |
542 | error = devm_request_threaded_irq(dev, irq, NULL, |
543 | thread_fn: tsc200x_irq_thread, |
544 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
545 | devname: "tsc200x" , dev_id: ts); |
546 | if (error) { |
547 | dev_err(dev, "Failed to request irq, err: %d\n" , error); |
548 | return error; |
549 | } |
550 | |
551 | error = regulator_enable(regulator: ts->vio); |
552 | if (error) |
553 | return error; |
554 | |
555 | dev_set_drvdata(dev, data: ts); |
556 | error = sysfs_create_group(kobj: &dev->kobj, grp: &tsc200x_attr_group); |
557 | if (error) { |
558 | dev_err(dev, |
559 | "Failed to create sysfs attributes, err: %d\n" , error); |
560 | goto disable_regulator; |
561 | } |
562 | |
563 | error = input_register_device(ts->idev); |
564 | if (error) { |
565 | dev_err(dev, |
566 | "Failed to register input device, err: %d\n" , error); |
567 | goto err_remove_sysfs; |
568 | } |
569 | |
570 | irq_set_irq_wake(irq, on: 1); |
571 | return 0; |
572 | |
573 | err_remove_sysfs: |
574 | sysfs_remove_group(kobj: &dev->kobj, grp: &tsc200x_attr_group); |
575 | disable_regulator: |
576 | regulator_disable(regulator: ts->vio); |
577 | return error; |
578 | } |
579 | EXPORT_SYMBOL_GPL(tsc200x_probe); |
580 | |
581 | void tsc200x_remove(struct device *dev) |
582 | { |
583 | struct tsc200x *ts = dev_get_drvdata(dev); |
584 | |
585 | sysfs_remove_group(kobj: &dev->kobj, grp: &tsc200x_attr_group); |
586 | |
587 | regulator_disable(regulator: ts->vio); |
588 | } |
589 | EXPORT_SYMBOL_GPL(tsc200x_remove); |
590 | |
591 | static int tsc200x_suspend(struct device *dev) |
592 | { |
593 | struct tsc200x *ts = dev_get_drvdata(dev); |
594 | |
595 | mutex_lock(&ts->mutex); |
596 | |
597 | if (!ts->suspended && ts->opened) |
598 | __tsc200x_disable(ts); |
599 | |
600 | ts->suspended = true; |
601 | |
602 | mutex_unlock(lock: &ts->mutex); |
603 | |
604 | return 0; |
605 | } |
606 | |
607 | static int tsc200x_resume(struct device *dev) |
608 | { |
609 | struct tsc200x *ts = dev_get_drvdata(dev); |
610 | |
611 | mutex_lock(&ts->mutex); |
612 | |
613 | if (ts->suspended && ts->opened) |
614 | __tsc200x_enable(ts); |
615 | |
616 | ts->suspended = false; |
617 | |
618 | mutex_unlock(lock: &ts->mutex); |
619 | |
620 | return 0; |
621 | } |
622 | |
623 | EXPORT_GPL_SIMPLE_DEV_PM_OPS(tsc200x_pm_ops, tsc200x_suspend, tsc200x_resume); |
624 | |
625 | MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>" ); |
626 | MODULE_DESCRIPTION("TSC200x Touchscreen Driver Core" ); |
627 | MODULE_LICENSE("GPL" ); |
628 | |