.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 Simon Budig, <simon.budig@kernelconcepts.de> |
---|
3 | 4 | * Daniel Wagener <daniel.wagener@kernelconcepts.de> (M09 firmware support) |
---|
4 | 5 | * Lothar Waßmann <LW@KARO-electronics.de> (DT support) |
---|
5 | | - * |
---|
6 | | - * This software is licensed under the terms of the GNU General Public |
---|
7 | | - * License version 2, as published by the Free Software Foundation, and |
---|
8 | | - * may be copied, distributed, and modified under those terms. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope that it will be useful, |
---|
11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | | - * GNU General Public License for more details. |
---|
14 | | - * |
---|
15 | | - * You should have received a copy of the GNU General Public |
---|
16 | | - * License along with this library; if not, write to the Free Software |
---|
17 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
18 | 6 | */ |
---|
19 | 7 | |
---|
20 | 8 | /* |
---|
.. | .. |
---|
25 | 13 | * http://www.glyn.com/Products/Displays |
---|
26 | 14 | */ |
---|
27 | 15 | |
---|
28 | | -#include <linux/module.h> |
---|
29 | | -#include <linux/ratelimit.h> |
---|
30 | | -#include <linux/irq.h> |
---|
| 16 | +#include <linux/debugfs.h> |
---|
| 17 | +#include <linux/delay.h> |
---|
| 18 | +#include <linux/gpio/consumer.h> |
---|
| 19 | +#include <linux/i2c.h> |
---|
31 | 20 | #include <linux/interrupt.h> |
---|
32 | 21 | #include <linux/input.h> |
---|
33 | | -#include <linux/i2c.h> |
---|
34 | | -#include <linux/uaccess.h> |
---|
35 | | -#include <linux/delay.h> |
---|
36 | | -#include <linux/debugfs.h> |
---|
37 | | -#include <linux/slab.h> |
---|
38 | | -#include <linux/gpio/consumer.h> |
---|
39 | 22 | #include <linux/input/mt.h> |
---|
40 | 23 | #include <linux/input/touchscreen.h> |
---|
41 | | -#include <linux/of_device.h> |
---|
| 24 | +#include <linux/irq.h> |
---|
| 25 | +#include <linux/kernel.h> |
---|
| 26 | +#include <linux/module.h> |
---|
| 27 | +#include <linux/ratelimit.h> |
---|
| 28 | +#include <linux/regulator/consumer.h> |
---|
| 29 | +#include <linux/slab.h> |
---|
| 30 | +#include <linux/uaccess.h> |
---|
| 31 | + |
---|
| 32 | +#include <asm/unaligned.h> |
---|
42 | 33 | |
---|
43 | 34 | #define WORK_REGISTER_THRESHOLD 0x00 |
---|
44 | 35 | #define WORK_REGISTER_REPORT_RATE 0x08 |
---|
.. | .. |
---|
47 | 38 | #define WORK_REGISTER_NUM_X 0x33 |
---|
48 | 39 | #define WORK_REGISTER_NUM_Y 0x34 |
---|
49 | 40 | |
---|
| 41 | +#define PMOD_REGISTER_ACTIVE 0x00 |
---|
| 42 | +#define PMOD_REGISTER_HIBERNATE 0x03 |
---|
| 43 | + |
---|
50 | 44 | #define M09_REGISTER_THRESHOLD 0x80 |
---|
51 | 45 | #define M09_REGISTER_GAIN 0x92 |
---|
52 | 46 | #define M09_REGISTER_OFFSET 0x93 |
---|
53 | 47 | #define M09_REGISTER_NUM_X 0x94 |
---|
54 | 48 | #define M09_REGISTER_NUM_Y 0x95 |
---|
55 | 49 | |
---|
| 50 | +#define EV_REGISTER_THRESHOLD 0x40 |
---|
| 51 | +#define EV_REGISTER_GAIN 0x41 |
---|
| 52 | +#define EV_REGISTER_OFFSET_Y 0x45 |
---|
| 53 | +#define EV_REGISTER_OFFSET_X 0x46 |
---|
| 54 | + |
---|
56 | 55 | #define NO_REGISTER 0xff |
---|
57 | 56 | |
---|
58 | 57 | #define WORK_REGISTER_OPMODE 0x3c |
---|
59 | 58 | #define FACTORY_REGISTER_OPMODE 0x01 |
---|
| 59 | +#define PMOD_REGISTER_OPMODE 0xa5 |
---|
60 | 60 | |
---|
61 | 61 | #define TOUCH_EVENT_DOWN 0x00 |
---|
62 | 62 | #define TOUCH_EVENT_UP 0x01 |
---|
.. | .. |
---|
69 | 69 | #define EDT_RAW_DATA_RETRIES 100 |
---|
70 | 70 | #define EDT_RAW_DATA_DELAY 1000 /* usec */ |
---|
71 | 71 | |
---|
| 72 | +enum edt_pmode { |
---|
| 73 | + EDT_PMODE_NOT_SUPPORTED, |
---|
| 74 | + EDT_PMODE_HIBERNATE, |
---|
| 75 | + EDT_PMODE_POWEROFF, |
---|
| 76 | +}; |
---|
| 77 | + |
---|
72 | 78 | enum edt_ver { |
---|
73 | 79 | EDT_M06, |
---|
74 | 80 | EDT_M09, |
---|
75 | 81 | EDT_M12, |
---|
| 82 | + EV_FT, |
---|
76 | 83 | GENERIC_FT, |
---|
77 | 84 | }; |
---|
78 | 85 | |
---|
.. | .. |
---|
81 | 88 | int reg_report_rate; |
---|
82 | 89 | int reg_gain; |
---|
83 | 90 | int reg_offset; |
---|
| 91 | + int reg_offset_x; |
---|
| 92 | + int reg_offset_y; |
---|
84 | 93 | int reg_num_x; |
---|
85 | 94 | int reg_num_y; |
---|
86 | 95 | }; |
---|
.. | .. |
---|
91 | 100 | struct touchscreen_properties prop; |
---|
92 | 101 | u16 num_x; |
---|
93 | 102 | u16 num_y; |
---|
| 103 | + struct regulator *vcc; |
---|
94 | 104 | |
---|
95 | 105 | struct gpio_desc *reset_gpio; |
---|
96 | 106 | struct gpio_desc *wake_gpio; |
---|
.. | .. |
---|
103 | 113 | |
---|
104 | 114 | struct mutex mutex; |
---|
105 | 115 | bool factory_mode; |
---|
| 116 | + enum edt_pmode suspend_mode; |
---|
106 | 117 | int threshold; |
---|
107 | 118 | int gain; |
---|
108 | 119 | int offset; |
---|
| 120 | + int offset_x; |
---|
| 121 | + int offset_y; |
---|
109 | 122 | int report_rate; |
---|
110 | 123 | int max_support_points; |
---|
111 | 124 | |
---|
.. | .. |
---|
190 | 203 | |
---|
191 | 204 | case EDT_M09: |
---|
192 | 205 | case EDT_M12: |
---|
| 206 | + case EV_FT: |
---|
193 | 207 | case GENERIC_FT: |
---|
194 | 208 | cmd = 0x0; |
---|
195 | 209 | offset = 3; |
---|
.. | .. |
---|
229 | 243 | |
---|
230 | 244 | for (i = 0; i < tsdata->max_support_points; i++) { |
---|
231 | 245 | u8 *buf = &rdbuf[i * tplen + offset]; |
---|
232 | | - bool down; |
---|
233 | 246 | |
---|
234 | 247 | type = buf[0] >> 6; |
---|
235 | 248 | /* ignore Reserved events */ |
---|
.. | .. |
---|
240 | 253 | if (tsdata->version == EDT_M06 && type == TOUCH_EVENT_DOWN) |
---|
241 | 254 | continue; |
---|
242 | 255 | |
---|
243 | | - x = ((buf[0] << 8) | buf[1]) & 0x0fff; |
---|
244 | | - y = ((buf[2] << 8) | buf[3]) & 0x0fff; |
---|
| 256 | + x = get_unaligned_be16(buf) & 0x0fff; |
---|
| 257 | + y = get_unaligned_be16(buf + 2) & 0x0fff; |
---|
| 258 | + /* The FT5x26 send the y coordinate first */ |
---|
| 259 | + if (tsdata->version == EV_FT) |
---|
| 260 | + swap(x, y); |
---|
| 261 | + |
---|
245 | 262 | id = (buf[2] >> 4) & 0x0f; |
---|
246 | | - down = type != TOUCH_EVENT_UP; |
---|
247 | 263 | |
---|
248 | 264 | input_mt_slot(tsdata->input, id); |
---|
249 | | - input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); |
---|
250 | | - |
---|
251 | | - if (!down) |
---|
252 | | - continue; |
---|
253 | | - |
---|
254 | | - touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y, |
---|
255 | | - true); |
---|
| 265 | + if (input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, |
---|
| 266 | + type != TOUCH_EVENT_UP)) |
---|
| 267 | + touchscreen_report_pos(tsdata->input, &tsdata->prop, |
---|
| 268 | + x, y, true); |
---|
256 | 269 | } |
---|
257 | 270 | |
---|
258 | 271 | input_mt_report_pointer_emulation(tsdata->input, true); |
---|
.. | .. |
---|
275 | 288 | wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; |
---|
276 | 289 | return edt_ft5x06_ts_readwrite(tsdata->client, 4, |
---|
277 | 290 | wrbuf, 0, NULL); |
---|
| 291 | + |
---|
278 | 292 | case EDT_M09: |
---|
279 | 293 | case EDT_M12: |
---|
| 294 | + case EV_FT: |
---|
280 | 295 | case GENERIC_FT: |
---|
281 | 296 | wrbuf[0] = addr; |
---|
282 | 297 | wrbuf[1] = value; |
---|
.. | .. |
---|
317 | 332 | |
---|
318 | 333 | case EDT_M09: |
---|
319 | 334 | case EDT_M12: |
---|
| 335 | + case EV_FT: |
---|
320 | 336 | case GENERIC_FT: |
---|
321 | 337 | wrbuf[0] = addr; |
---|
322 | 338 | error = edt_ft5x06_ts_readwrite(tsdata->client, 1, |
---|
.. | .. |
---|
339 | 355 | u8 limit_high; |
---|
340 | 356 | u8 addr_m06; |
---|
341 | 357 | u8 addr_m09; |
---|
| 358 | + u8 addr_ev; |
---|
342 | 359 | }; |
---|
343 | 360 | |
---|
344 | | -#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \ |
---|
| 361 | +#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev, \ |
---|
345 | 362 | _limit_low, _limit_high) \ |
---|
346 | 363 | struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ |
---|
347 | 364 | .dattr = __ATTR(_field, _mode, \ |
---|
.. | .. |
---|
350 | 367 | .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ |
---|
351 | 368 | .addr_m06 = _addr_m06, \ |
---|
352 | 369 | .addr_m09 = _addr_m09, \ |
---|
| 370 | + .addr_ev = _addr_ev, \ |
---|
353 | 371 | .limit_low = _limit_low, \ |
---|
354 | 372 | .limit_high = _limit_high, \ |
---|
355 | 373 | } |
---|
.. | .. |
---|
384 | 402 | case EDT_M12: |
---|
385 | 403 | case GENERIC_FT: |
---|
386 | 404 | addr = attr->addr_m09; |
---|
| 405 | + break; |
---|
| 406 | + |
---|
| 407 | + case EV_FT: |
---|
| 408 | + addr = attr->addr_ev; |
---|
387 | 409 | break; |
---|
388 | 410 | |
---|
389 | 411 | default: |
---|
.. | .. |
---|
457 | 479 | addr = attr->addr_m09; |
---|
458 | 480 | break; |
---|
459 | 481 | |
---|
| 482 | + case EV_FT: |
---|
| 483 | + addr = attr->addr_ev; |
---|
| 484 | + break; |
---|
| 485 | + |
---|
460 | 486 | default: |
---|
461 | 487 | error = -ENODEV; |
---|
462 | 488 | goto out; |
---|
.. | .. |
---|
480 | 506 | |
---|
481 | 507 | /* m06, m09: range 0-31, m12: range 0-5 */ |
---|
482 | 508 | static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, |
---|
483 | | - M09_REGISTER_GAIN, 0, 31); |
---|
| 509 | + M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31); |
---|
484 | 510 | /* m06, m09: range 0-31, m12: range 0-16 */ |
---|
485 | 511 | static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, |
---|
486 | | - M09_REGISTER_OFFSET, 0, 31); |
---|
| 512 | + M09_REGISTER_OFFSET, NO_REGISTER, 0, 31); |
---|
| 513 | +/* m06, m09, m12: no supported, ev_ft: range 0-80 */ |
---|
| 514 | +static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, |
---|
| 515 | + EV_REGISTER_OFFSET_X, 0, 80); |
---|
| 516 | +/* m06, m09, m12: no supported, ev_ft: range 0-80 */ |
---|
| 517 | +static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER, |
---|
| 518 | + EV_REGISTER_OFFSET_Y, 0, 80); |
---|
487 | 519 | /* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ |
---|
488 | 520 | static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, |
---|
489 | | - M09_REGISTER_THRESHOLD, 0, 255); |
---|
| 521 | + M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255); |
---|
490 | 522 | /* m06: range 3 to 14, m12: (0x64: 100Hz) */ |
---|
491 | 523 | static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, |
---|
492 | | - NO_REGISTER, 0, 255); |
---|
| 524 | + NO_REGISTER, NO_REGISTER, 0, 255); |
---|
493 | 525 | |
---|
494 | 526 | static struct attribute *edt_ft5x06_attrs[] = { |
---|
495 | 527 | &edt_ft5x06_attr_gain.dattr.attr, |
---|
496 | 528 | &edt_ft5x06_attr_offset.dattr.attr, |
---|
| 529 | + &edt_ft5x06_attr_offset_x.dattr.attr, |
---|
| 530 | + &edt_ft5x06_attr_offset_y.dattr.attr, |
---|
497 | 531 | &edt_ft5x06_attr_threshold.dattr.attr, |
---|
498 | 532 | &edt_ft5x06_attr_report_rate.dattr.attr, |
---|
499 | 533 | NULL |
---|
.. | .. |
---|
502 | 536 | static const struct attribute_group edt_ft5x06_attr_group = { |
---|
503 | 537 | .attrs = edt_ft5x06_attrs, |
---|
504 | 538 | }; |
---|
| 539 | + |
---|
| 540 | +static void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata) |
---|
| 541 | +{ |
---|
| 542 | + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; |
---|
| 543 | + |
---|
| 544 | + edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, |
---|
| 545 | + tsdata->threshold); |
---|
| 546 | + edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, |
---|
| 547 | + tsdata->gain); |
---|
| 548 | + if (reg_addr->reg_offset != NO_REGISTER) |
---|
| 549 | + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, |
---|
| 550 | + tsdata->offset); |
---|
| 551 | + if (reg_addr->reg_offset_x != NO_REGISTER) |
---|
| 552 | + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, |
---|
| 553 | + tsdata->offset_x); |
---|
| 554 | + if (reg_addr->reg_offset_y != NO_REGISTER) |
---|
| 555 | + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, |
---|
| 556 | + tsdata->offset_y); |
---|
| 557 | + if (reg_addr->reg_report_rate != NO_REGISTER) |
---|
| 558 | + edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, |
---|
| 559 | + tsdata->report_rate); |
---|
| 560 | + |
---|
| 561 | +} |
---|
505 | 562 | |
---|
506 | 563 | #ifdef CONFIG_DEBUG_FS |
---|
507 | 564 | static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) |
---|
.. | .. |
---|
568 | 625 | { |
---|
569 | 626 | struct i2c_client *client = tsdata->client; |
---|
570 | 627 | int retries = EDT_SWITCH_MODE_RETRIES; |
---|
571 | | - struct edt_reg_addr *reg_addr = &tsdata->reg_addr; |
---|
572 | 628 | int ret; |
---|
573 | 629 | int error; |
---|
574 | 630 | |
---|
.. | .. |
---|
600 | 656 | kfree(tsdata->raw_buffer); |
---|
601 | 657 | tsdata->raw_buffer = NULL; |
---|
602 | 658 | |
---|
603 | | - /* restore parameters */ |
---|
604 | | - edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, |
---|
605 | | - tsdata->threshold); |
---|
606 | | - edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, |
---|
607 | | - tsdata->gain); |
---|
608 | | - edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, |
---|
609 | | - tsdata->offset); |
---|
610 | | - if (reg_addr->reg_report_rate != NO_REGISTER) |
---|
611 | | - edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, |
---|
612 | | - tsdata->report_rate); |
---|
613 | | - |
---|
| 659 | + edt_ft5x06_restore_reg_parameters(tsdata); |
---|
614 | 660 | enable_irq(client->irq); |
---|
615 | 661 | |
---|
616 | 662 | return 0; |
---|
.. | .. |
---|
731 | 777 | .read = edt_ft5x06_debugfs_raw_data_read, |
---|
732 | 778 | }; |
---|
733 | 779 | |
---|
734 | | -static void |
---|
735 | | -edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, |
---|
736 | | - const char *debugfs_name) |
---|
| 780 | +static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, |
---|
| 781 | + const char *debugfs_name) |
---|
737 | 782 | { |
---|
738 | 783 | tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); |
---|
739 | | - if (!tsdata->debug_dir) |
---|
740 | | - return; |
---|
741 | 784 | |
---|
742 | 785 | debugfs_create_u16("num_x", S_IRUSR, tsdata->debug_dir, &tsdata->num_x); |
---|
743 | 786 | debugfs_create_u16("num_y", S_IRUSR, tsdata->debug_dir, &tsdata->num_y); |
---|
.. | .. |
---|
748 | 791 | tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); |
---|
749 | 792 | } |
---|
750 | 793 | |
---|
751 | | -static void |
---|
752 | | -edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) |
---|
| 794 | +static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) |
---|
753 | 795 | { |
---|
754 | 796 | debugfs_remove_recursive(tsdata->debug_dir); |
---|
755 | 797 | kfree(tsdata->raw_buffer); |
---|
.. | .. |
---|
757 | 799 | |
---|
758 | 800 | #else |
---|
759 | 801 | |
---|
760 | | -static inline void |
---|
761 | | -edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, |
---|
762 | | - const char *debugfs_name) |
---|
| 802 | +static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) |
---|
| 803 | +{ |
---|
| 804 | + return -ENOSYS; |
---|
| 805 | +} |
---|
| 806 | + |
---|
| 807 | +static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, |
---|
| 808 | + const char *debugfs_name) |
---|
763 | 809 | { |
---|
764 | 810 | } |
---|
765 | 811 | |
---|
766 | | -static inline void |
---|
767 | | -edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) |
---|
| 812 | +static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) |
---|
768 | 813 | { |
---|
769 | 814 | } |
---|
770 | 815 | |
---|
.. | .. |
---|
867 | 912 | case 0x5a: /* Solomon Goldentek Display */ |
---|
868 | 913 | snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0"); |
---|
869 | 914 | break; |
---|
| 915 | + case 0x59: /* Evervision Display with FT5xx6 TS */ |
---|
| 916 | + tsdata->version = EV_FT; |
---|
| 917 | + error = edt_ft5x06_ts_readwrite(client, 1, "\x53", |
---|
| 918 | + 1, rdbuf); |
---|
| 919 | + if (error) |
---|
| 920 | + return error; |
---|
| 921 | + strlcpy(fw_version, rdbuf, 1); |
---|
| 922 | + snprintf(model_name, EDT_NAME_LEN, |
---|
| 923 | + "EVERVISION-FT5726NEi"); |
---|
| 924 | + break; |
---|
870 | 925 | default: |
---|
871 | 926 | snprintf(model_name, EDT_NAME_LEN, |
---|
872 | 927 | "generic ft5x06 (%02x)", |
---|
.. | .. |
---|
899 | 954 | |
---|
900 | 955 | error = device_property_read_u32(dev, "offset", &val); |
---|
901 | 956 | if (!error) { |
---|
902 | | - edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val); |
---|
| 957 | + if (reg_addr->reg_offset != NO_REGISTER) |
---|
| 958 | + edt_ft5x06_register_write(tsdata, |
---|
| 959 | + reg_addr->reg_offset, val); |
---|
903 | 960 | tsdata->offset = val; |
---|
| 961 | + } |
---|
| 962 | + |
---|
| 963 | + error = device_property_read_u32(dev, "offset-x", &val); |
---|
| 964 | + if (!error) { |
---|
| 965 | + if (reg_addr->reg_offset_x != NO_REGISTER) |
---|
| 966 | + edt_ft5x06_register_write(tsdata, |
---|
| 967 | + reg_addr->reg_offset_x, val); |
---|
| 968 | + tsdata->offset_x = val; |
---|
| 969 | + } |
---|
| 970 | + |
---|
| 971 | + error = device_property_read_u32(dev, "offset-y", &val); |
---|
| 972 | + if (!error) { |
---|
| 973 | + if (reg_addr->reg_offset_y != NO_REGISTER) |
---|
| 974 | + edt_ft5x06_register_write(tsdata, |
---|
| 975 | + reg_addr->reg_offset_y, val); |
---|
| 976 | + tsdata->offset_y = val; |
---|
904 | 977 | } |
---|
905 | 978 | } |
---|
906 | 979 | |
---|
.. | .. |
---|
912 | 985 | tsdata->threshold = edt_ft5x06_register_read(tsdata, |
---|
913 | 986 | reg_addr->reg_threshold); |
---|
914 | 987 | tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); |
---|
915 | | - tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); |
---|
| 988 | + if (reg_addr->reg_offset != NO_REGISTER) |
---|
| 989 | + tsdata->offset = |
---|
| 990 | + edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); |
---|
| 991 | + if (reg_addr->reg_offset_x != NO_REGISTER) |
---|
| 992 | + tsdata->offset_x = edt_ft5x06_register_read(tsdata, |
---|
| 993 | + reg_addr->reg_offset_x); |
---|
| 994 | + if (reg_addr->reg_offset_y != NO_REGISTER) |
---|
| 995 | + tsdata->offset_y = edt_ft5x06_register_read(tsdata, |
---|
| 996 | + reg_addr->reg_offset_y); |
---|
916 | 997 | if (reg_addr->reg_report_rate != NO_REGISTER) |
---|
917 | 998 | tsdata->report_rate = edt_ft5x06_register_read(tsdata, |
---|
918 | 999 | reg_addr->reg_report_rate); |
---|
.. | .. |
---|
940 | 1021 | reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; |
---|
941 | 1022 | reg_addr->reg_gain = WORK_REGISTER_GAIN; |
---|
942 | 1023 | reg_addr->reg_offset = WORK_REGISTER_OFFSET; |
---|
| 1024 | + reg_addr->reg_offset_x = NO_REGISTER; |
---|
| 1025 | + reg_addr->reg_offset_y = NO_REGISTER; |
---|
943 | 1026 | reg_addr->reg_num_x = WORK_REGISTER_NUM_X; |
---|
944 | 1027 | reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; |
---|
945 | 1028 | break; |
---|
.. | .. |
---|
950 | 1033 | reg_addr->reg_report_rate = NO_REGISTER; |
---|
951 | 1034 | reg_addr->reg_gain = M09_REGISTER_GAIN; |
---|
952 | 1035 | reg_addr->reg_offset = M09_REGISTER_OFFSET; |
---|
| 1036 | + reg_addr->reg_offset_x = NO_REGISTER; |
---|
| 1037 | + reg_addr->reg_offset_y = NO_REGISTER; |
---|
953 | 1038 | reg_addr->reg_num_x = M09_REGISTER_NUM_X; |
---|
954 | 1039 | reg_addr->reg_num_y = M09_REGISTER_NUM_Y; |
---|
| 1040 | + break; |
---|
| 1041 | + |
---|
| 1042 | + case EV_FT: |
---|
| 1043 | + reg_addr->reg_threshold = EV_REGISTER_THRESHOLD; |
---|
| 1044 | + reg_addr->reg_gain = EV_REGISTER_GAIN; |
---|
| 1045 | + reg_addr->reg_offset = NO_REGISTER; |
---|
| 1046 | + reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X; |
---|
| 1047 | + reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y; |
---|
| 1048 | + reg_addr->reg_num_x = NO_REGISTER; |
---|
| 1049 | + reg_addr->reg_num_y = NO_REGISTER; |
---|
| 1050 | + reg_addr->reg_report_rate = NO_REGISTER; |
---|
955 | 1051 | break; |
---|
956 | 1052 | |
---|
957 | 1053 | case GENERIC_FT: |
---|
.. | .. |
---|
959 | 1055 | reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; |
---|
960 | 1056 | reg_addr->reg_gain = M09_REGISTER_GAIN; |
---|
961 | 1057 | reg_addr->reg_offset = M09_REGISTER_OFFSET; |
---|
| 1058 | + reg_addr->reg_offset_x = NO_REGISTER; |
---|
| 1059 | + reg_addr->reg_offset_y = NO_REGISTER; |
---|
962 | 1060 | break; |
---|
963 | 1061 | } |
---|
| 1062 | +} |
---|
| 1063 | + |
---|
| 1064 | +static void edt_ft5x06_disable_regulator(void *arg) |
---|
| 1065 | +{ |
---|
| 1066 | + struct edt_ft5x06_ts_data *data = arg; |
---|
| 1067 | + |
---|
| 1068 | + regulator_disable(data->vcc); |
---|
964 | 1069 | } |
---|
965 | 1070 | |
---|
966 | 1071 | static int edt_ft5x06_ts_probe(struct i2c_client *client, |
---|
.. | .. |
---|
982 | 1087 | return -ENOMEM; |
---|
983 | 1088 | } |
---|
984 | 1089 | |
---|
985 | | - chip_data = of_device_get_match_data(&client->dev); |
---|
| 1090 | + chip_data = device_get_match_data(&client->dev); |
---|
986 | 1091 | if (!chip_data) |
---|
987 | 1092 | chip_data = (const struct edt_i2c_chip_data *)id->driver_data; |
---|
988 | 1093 | if (!chip_data || !chip_data->max_support_points) { |
---|
.. | .. |
---|
991 | 1096 | } |
---|
992 | 1097 | |
---|
993 | 1098 | tsdata->max_support_points = chip_data->max_support_points; |
---|
| 1099 | + |
---|
| 1100 | + tsdata->vcc = devm_regulator_get(&client->dev, "vcc"); |
---|
| 1101 | + if (IS_ERR(tsdata->vcc)) { |
---|
| 1102 | + error = PTR_ERR(tsdata->vcc); |
---|
| 1103 | + if (error != -EPROBE_DEFER) |
---|
| 1104 | + dev_err(&client->dev, |
---|
| 1105 | + "failed to request regulator: %d\n", error); |
---|
| 1106 | + return error; |
---|
| 1107 | + } |
---|
| 1108 | + |
---|
| 1109 | + error = regulator_enable(tsdata->vcc); |
---|
| 1110 | + if (error < 0) { |
---|
| 1111 | + dev_err(&client->dev, "failed to enable vcc: %d\n", error); |
---|
| 1112 | + return error; |
---|
| 1113 | + } |
---|
| 1114 | + |
---|
| 1115 | + error = devm_add_action_or_reset(&client->dev, |
---|
| 1116 | + edt_ft5x06_disable_regulator, |
---|
| 1117 | + tsdata); |
---|
| 1118 | + if (error) |
---|
| 1119 | + return error; |
---|
994 | 1120 | |
---|
995 | 1121 | tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev, |
---|
996 | 1122 | "reset", GPIOD_OUT_HIGH); |
---|
.. | .. |
---|
1009 | 1135 | "Failed to request GPIO wake pin, error %d\n", error); |
---|
1010 | 1136 | return error; |
---|
1011 | 1137 | } |
---|
| 1138 | + |
---|
| 1139 | + /* |
---|
| 1140 | + * Check which sleep modes we can support. Power-off requieres the |
---|
| 1141 | + * reset-pin to ensure correct power-down/power-up behaviour. Start with |
---|
| 1142 | + * the EDT_PMODE_POWEROFF test since this is the deepest possible sleep |
---|
| 1143 | + * mode. |
---|
| 1144 | + */ |
---|
| 1145 | + if (tsdata->reset_gpio) |
---|
| 1146 | + tsdata->suspend_mode = EDT_PMODE_POWEROFF; |
---|
| 1147 | + else if (tsdata->wake_gpio) |
---|
| 1148 | + tsdata->suspend_mode = EDT_PMODE_HIBERNATE; |
---|
| 1149 | + else |
---|
| 1150 | + tsdata->suspend_mode = EDT_PMODE_NOT_SUPPORTED; |
---|
1012 | 1151 | |
---|
1013 | 1152 | if (tsdata->wake_gpio) { |
---|
1014 | 1153 | usleep_range(5000, 6000); |
---|
.. | .. |
---|
1104 | 1243 | return error; |
---|
1105 | 1244 | |
---|
1106 | 1245 | edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); |
---|
1107 | | - device_init_wakeup(&client->dev, 1); |
---|
1108 | 1246 | |
---|
1109 | 1247 | dev_dbg(&client->dev, |
---|
1110 | 1248 | "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", |
---|
.. | .. |
---|
1127 | 1265 | static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev) |
---|
1128 | 1266 | { |
---|
1129 | 1267 | struct i2c_client *client = to_i2c_client(dev); |
---|
| 1268 | + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); |
---|
| 1269 | + struct gpio_desc *reset_gpio = tsdata->reset_gpio; |
---|
| 1270 | + int ret; |
---|
1130 | 1271 | |
---|
1131 | 1272 | if (device_may_wakeup(dev)) |
---|
1132 | | - enable_irq_wake(client->irq); |
---|
| 1273 | + return 0; |
---|
| 1274 | + |
---|
| 1275 | + if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED) |
---|
| 1276 | + return 0; |
---|
| 1277 | + |
---|
| 1278 | + /* Enter hibernate mode. */ |
---|
| 1279 | + ret = edt_ft5x06_register_write(tsdata, PMOD_REGISTER_OPMODE, |
---|
| 1280 | + PMOD_REGISTER_HIBERNATE); |
---|
| 1281 | + if (ret) |
---|
| 1282 | + dev_warn(dev, "Failed to set hibernate mode\n"); |
---|
| 1283 | + |
---|
| 1284 | + if (tsdata->suspend_mode == EDT_PMODE_HIBERNATE) |
---|
| 1285 | + return 0; |
---|
| 1286 | + |
---|
| 1287 | + /* |
---|
| 1288 | + * Power-off according the datasheet. Cut the power may leaf the irq |
---|
| 1289 | + * line in an undefined state depending on the host pull resistor |
---|
| 1290 | + * settings. Disable the irq to avoid adjusting each host till the |
---|
| 1291 | + * device is back in a full functional state. |
---|
| 1292 | + */ |
---|
| 1293 | + disable_irq(tsdata->client->irq); |
---|
| 1294 | + |
---|
| 1295 | + gpiod_set_value_cansleep(reset_gpio, 1); |
---|
| 1296 | + usleep_range(1000, 2000); |
---|
| 1297 | + |
---|
| 1298 | + ret = regulator_disable(tsdata->vcc); |
---|
| 1299 | + if (ret) |
---|
| 1300 | + dev_warn(dev, "Failed to disable vcc\n"); |
---|
1133 | 1301 | |
---|
1134 | 1302 | return 0; |
---|
1135 | 1303 | } |
---|
.. | .. |
---|
1137 | 1305 | static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev) |
---|
1138 | 1306 | { |
---|
1139 | 1307 | struct i2c_client *client = to_i2c_client(dev); |
---|
| 1308 | + struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); |
---|
| 1309 | + int ret = 0; |
---|
1140 | 1310 | |
---|
1141 | 1311 | if (device_may_wakeup(dev)) |
---|
1142 | | - disable_irq_wake(client->irq); |
---|
| 1312 | + return 0; |
---|
1143 | 1313 | |
---|
1144 | | - return 0; |
---|
| 1314 | + if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED) |
---|
| 1315 | + return 0; |
---|
| 1316 | + |
---|
| 1317 | + if (tsdata->suspend_mode == EDT_PMODE_POWEROFF) { |
---|
| 1318 | + struct gpio_desc *reset_gpio = tsdata->reset_gpio; |
---|
| 1319 | + |
---|
| 1320 | + /* |
---|
| 1321 | + * We can't check if the regulator is a dummy or a real |
---|
| 1322 | + * regulator. So we need to specify the 5ms reset time (T_rst) |
---|
| 1323 | + * here instead of the 100us T_rtp time. We also need to wait |
---|
| 1324 | + * 300ms in case it was a real supply and the power was cutted |
---|
| 1325 | + * of. Toggle the reset pin is also a way to exit the hibernate |
---|
| 1326 | + * mode. |
---|
| 1327 | + */ |
---|
| 1328 | + gpiod_set_value_cansleep(reset_gpio, 1); |
---|
| 1329 | + usleep_range(5000, 6000); |
---|
| 1330 | + |
---|
| 1331 | + ret = regulator_enable(tsdata->vcc); |
---|
| 1332 | + if (ret) { |
---|
| 1333 | + dev_err(dev, "Failed to enable vcc\n"); |
---|
| 1334 | + return ret; |
---|
| 1335 | + } |
---|
| 1336 | + |
---|
| 1337 | + usleep_range(1000, 2000); |
---|
| 1338 | + gpiod_set_value_cansleep(reset_gpio, 0); |
---|
| 1339 | + msleep(300); |
---|
| 1340 | + |
---|
| 1341 | + edt_ft5x06_restore_reg_parameters(tsdata); |
---|
| 1342 | + enable_irq(tsdata->client->irq); |
---|
| 1343 | + |
---|
| 1344 | + if (tsdata->factory_mode) |
---|
| 1345 | + ret = edt_ft5x06_factory_mode(tsdata); |
---|
| 1346 | + } else { |
---|
| 1347 | + struct gpio_desc *wake_gpio = tsdata->wake_gpio; |
---|
| 1348 | + |
---|
| 1349 | + gpiod_set_value_cansleep(wake_gpio, 0); |
---|
| 1350 | + usleep_range(5000, 6000); |
---|
| 1351 | + gpiod_set_value_cansleep(wake_gpio, 1); |
---|
| 1352 | + } |
---|
| 1353 | + |
---|
| 1354 | + |
---|
| 1355 | + return ret; |
---|
1145 | 1356 | } |
---|
1146 | 1357 | |
---|
1147 | 1358 | static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, |
---|
.. | .. |
---|
1162 | 1373 | static const struct i2c_device_id edt_ft5x06_ts_id[] = { |
---|
1163 | 1374 | { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, |
---|
1164 | 1375 | { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, |
---|
| 1376 | + { .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data }, |
---|
1165 | 1377 | /* Note no edt- prefix for compatibility with the ft6236.c driver */ |
---|
1166 | 1378 | { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, |
---|
1167 | 1379 | { /* sentinel */ } |
---|
1168 | 1380 | }; |
---|
1169 | 1381 | MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); |
---|
1170 | 1382 | |
---|
1171 | | -#ifdef CONFIG_OF |
---|
1172 | 1383 | static const struct of_device_id edt_ft5x06_of_match[] = { |
---|
1173 | 1384 | { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data }, |
---|
1174 | 1385 | { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, |
---|
1175 | 1386 | { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, |
---|
1176 | 1387 | { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, |
---|
| 1388 | + { .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data }, |
---|
1177 | 1389 | /* Note focaltech vendor prefix for compatibility with ft6236.c */ |
---|
1178 | 1390 | { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, |
---|
1179 | 1391 | { /* sentinel */ } |
---|
1180 | 1392 | }; |
---|
1181 | 1393 | MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); |
---|
1182 | | -#endif |
---|
1183 | 1394 | |
---|
1184 | 1395 | static struct i2c_driver edt_ft5x06_ts_driver = { |
---|
1185 | 1396 | .driver = { |
---|
1186 | 1397 | .name = "edt_ft5x06", |
---|
1187 | | - .of_match_table = of_match_ptr(edt_ft5x06_of_match), |
---|
| 1398 | + .of_match_table = edt_ft5x06_of_match, |
---|
1188 | 1399 | .pm = &edt_ft5x06_ts_pm_ops, |
---|
| 1400 | + .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
---|
1189 | 1401 | }, |
---|
1190 | 1402 | .id_table = edt_ft5x06_ts_id, |
---|
1191 | 1403 | .probe = edt_ft5x06_ts_probe, |
---|
.. | .. |
---|
1196 | 1408 | |
---|
1197 | 1409 | MODULE_AUTHOR("Simon Budig <simon.budig@kernelconcepts.de>"); |
---|
1198 | 1410 | MODULE_DESCRIPTION("EDT FT5x06 I2C Touchscreen Driver"); |
---|
1199 | | -MODULE_LICENSE("GPL"); |
---|
| 1411 | +MODULE_LICENSE("GPL v2"); |
---|