| .. | .. |
|---|
| 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"); |
|---|