diff options
| author | Marcin Niestroj <m.niestroj@grinn-global.com> | 2018-01-26 14:08:59 -0500 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2018-01-31 17:44:40 -0500 |
| commit | fafef982c7353e8982b951e40573e990ccf0ed00 (patch) | |
| tree | ac8aed03a26e7579f7fdb8ff7b95665f4d27d8dd | |
| parent | 13e945401a8dd5098ed07d242542e90be9f1c19d (diff) | |
Input: goodix - use generic touchscreen_properties
Use touchscreen_properties structure instead of implementing all
properties by our own. It allows us to reuse generic code for parsing
device-tree properties (which was implemented manually in the driver for
now). Additionally, it allows us to report events using generic
touchscreen_report_pos(), which automatically handles inverted and
swapped axes.
This fixes the issue with the custom code incorrectly handling case where
ts->inverted_x and ts->swapped_x_y were true, but ts->inverted_y was
false. Assuming we have 720x1280 touch panel, ts->abs_x_max == 1279 and
ts->abs_y_max == 719 (because we inverted that in goodix_read_config()).
Now let's assume that we received event from (0:0) position (in touch
panel original coordinates). In function goodix_ts_report_touch() we
calculate input_x as 1279, but after swapping input_y takes that value
(which is more that maximum 719 value reported during initialization).
Note that since touchscreen coordinates are 0-indexed, we now report
touchscreen range as (0:size-1).
Developed and tested on custom DT-based device with gt1151 touch
panel.
Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
[dtor: fix endianness annotation reported by sparse, handle errors when
initializing MT slots]
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
| -rw-r--r-- | drivers/input/touchscreen/goodix.c | 141 |
1 files changed, 58 insertions, 83 deletions
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index ecec8eb17f28..9736c83dd418 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/i2c.h> | 22 | #include <linux/i2c.h> |
| 23 | #include <linux/input.h> | 23 | #include <linux/input.h> |
| 24 | #include <linux/input/mt.h> | 24 | #include <linux/input/mt.h> |
| 25 | #include <linux/input/touchscreen.h> | ||
| 25 | #include <linux/module.h> | 26 | #include <linux/module.h> |
| 26 | #include <linux/delay.h> | 27 | #include <linux/delay.h> |
| 27 | #include <linux/irq.h> | 28 | #include <linux/irq.h> |
| @@ -43,11 +44,7 @@ struct goodix_ts_data { | |||
| 43 | struct i2c_client *client; | 44 | struct i2c_client *client; |
| 44 | struct input_dev *input_dev; | 45 | struct input_dev *input_dev; |
| 45 | const struct goodix_chip_data *chip; | 46 | const struct goodix_chip_data *chip; |
| 46 | int abs_x_max; | 47 | struct touchscreen_properties prop; |
| 47 | int abs_y_max; | ||
| 48 | bool swapped_x_y; | ||
| 49 | bool inverted_x; | ||
| 50 | bool inverted_y; | ||
| 51 | unsigned int max_touch_num; | 48 | unsigned int max_touch_num; |
| 52 | unsigned int int_trigger_type; | 49 | unsigned int int_trigger_type; |
| 53 | struct gpio_desc *gpiod_int; | 50 | struct gpio_desc *gpiod_int; |
| @@ -160,7 +157,7 @@ static int goodix_i2c_read(struct i2c_client *client, | |||
| 160 | u16 reg, u8 *buf, int len) | 157 | u16 reg, u8 *buf, int len) |
| 161 | { | 158 | { |
| 162 | struct i2c_msg msgs[2]; | 159 | struct i2c_msg msgs[2]; |
| 163 | u16 wbuf = cpu_to_be16(reg); | 160 | __be16 wbuf = cpu_to_be16(reg); |
| 164 | int ret; | 161 | int ret; |
| 165 | 162 | ||
| 166 | msgs[0].flags = 0; | 163 | msgs[0].flags = 0; |
| @@ -295,18 +292,10 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) | |||
| 295 | int input_y = get_unaligned_le16(&coor_data[3]); | 292 | int input_y = get_unaligned_le16(&coor_data[3]); |
| 296 | int input_w = get_unaligned_le16(&coor_data[5]); | 293 | int input_w = get_unaligned_le16(&coor_data[5]); |
| 297 | 294 | ||
| 298 | /* Inversions have to happen before axis swapping */ | ||
| 299 | if (ts->inverted_x) | ||
| 300 | input_x = ts->abs_x_max - input_x; | ||
| 301 | if (ts->inverted_y) | ||
| 302 | input_y = ts->abs_y_max - input_y; | ||
| 303 | if (ts->swapped_x_y) | ||
| 304 | swap(input_x, input_y); | ||
| 305 | |||
| 306 | input_mt_slot(ts->input_dev, id); | 295 | input_mt_slot(ts->input_dev, id); |
| 307 | input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); | 296 | input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); |
| 308 | input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); | 297 | touchscreen_report_pos(ts->input_dev, &ts->prop, |
| 309 | input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); | 298 | input_x, input_y, true); |
| 310 | input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); | 299 | input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); |
| 311 | input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); | 300 | input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); |
| 312 | } | 301 | } |
| @@ -579,44 +568,27 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) | |||
| 579 | static void goodix_read_config(struct goodix_ts_data *ts) | 568 | static void goodix_read_config(struct goodix_ts_data *ts) |
| 580 | { | 569 | { |
| 581 | u8 config[GOODIX_CONFIG_MAX_LENGTH]; | 570 | u8 config[GOODIX_CONFIG_MAX_LENGTH]; |
| 571 | int x_max, y_max; | ||
| 582 | int error; | 572 | int error; |
| 583 | 573 | ||
| 584 | error = goodix_i2c_read(ts->client, ts->chip->config_addr, | 574 | error = goodix_i2c_read(ts->client, ts->chip->config_addr, |
| 585 | config, ts->chip->config_len); | 575 | config, ts->chip->config_len); |
| 586 | if (error) { | 576 | if (error) { |
| 587 | dev_warn(&ts->client->dev, | 577 | dev_warn(&ts->client->dev, "Error reading config: %d\n", |
| 588 | "Error reading config (%d), using defaults\n", | ||
| 589 | error); | 578 | error); |
| 590 | ts->abs_x_max = GOODIX_MAX_WIDTH; | ||
| 591 | ts->abs_y_max = GOODIX_MAX_HEIGHT; | ||
| 592 | if (ts->swapped_x_y) | ||
| 593 | swap(ts->abs_x_max, ts->abs_y_max); | ||
| 594 | ts->int_trigger_type = GOODIX_INT_TRIGGER; | 579 | ts->int_trigger_type = GOODIX_INT_TRIGGER; |
| 595 | ts->max_touch_num = GOODIX_MAX_CONTACTS; | 580 | ts->max_touch_num = GOODIX_MAX_CONTACTS; |
| 596 | return; | 581 | return; |
| 597 | } | 582 | } |
| 598 | 583 | ||
| 599 | ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); | ||
| 600 | ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); | ||
| 601 | if (ts->swapped_x_y) | ||
| 602 | swap(ts->abs_x_max, ts->abs_y_max); | ||
| 603 | ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; | 584 | ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; |
| 604 | ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; | 585 | ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; |
| 605 | if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { | ||
| 606 | dev_err(&ts->client->dev, | ||
| 607 | "Invalid config, using defaults\n"); | ||
| 608 | ts->abs_x_max = GOODIX_MAX_WIDTH; | ||
| 609 | ts->abs_y_max = GOODIX_MAX_HEIGHT; | ||
| 610 | if (ts->swapped_x_y) | ||
| 611 | swap(ts->abs_x_max, ts->abs_y_max); | ||
| 612 | ts->max_touch_num = GOODIX_MAX_CONTACTS; | ||
| 613 | } | ||
| 614 | 586 | ||
| 615 | if (dmi_check_system(rotated_screen)) { | 587 | x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); |
| 616 | ts->inverted_x = true; | 588 | y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); |
| 617 | ts->inverted_y = true; | 589 | if (x_max && y_max) { |
| 618 | dev_dbg(&ts->client->dev, | 590 | input_abs_set_max(ts->input_dev, ABS_MT_POSITION_X, x_max - 1); |
| 619 | "Applying '180 degrees rotated screen' quirk\n"); | 591 | input_abs_set_max(ts->input_dev, ABS_MT_POSITION_Y, y_max - 1); |
| 620 | } | 592 | } |
| 621 | } | 593 | } |
| 622 | 594 | ||
| @@ -676,32 +648,28 @@ static int goodix_i2c_test(struct i2c_client *client) | |||
| 676 | } | 648 | } |
| 677 | 649 | ||
| 678 | /** | 650 | /** |
| 679 | * goodix_request_input_dev - Allocate, populate and register the input device | 651 | * goodix_configure_dev - Finish device initialization |
| 680 | * | 652 | * |
| 681 | * @ts: our goodix_ts_data pointer | 653 | * @ts: our goodix_ts_data pointer |
| 682 | * | 654 | * |
| 683 | * Must be called during probe | 655 | * Must be called from probe to finish initialization of the device. |
| 656 | * Contains the common initialization code for both devices that | ||
| 657 | * declare gpio pins and devices that do not. It is either called | ||
| 658 | * directly from probe or from request_firmware_wait callback. | ||
| 684 | */ | 659 | */ |
| 685 | static int goodix_request_input_dev(struct goodix_ts_data *ts) | 660 | static int goodix_configure_dev(struct goodix_ts_data *ts) |
| 686 | { | 661 | { |
| 687 | int error; | 662 | int error; |
| 688 | 663 | ||
| 664 | ts->int_trigger_type = GOODIX_INT_TRIGGER; | ||
| 665 | ts->max_touch_num = GOODIX_MAX_CONTACTS; | ||
| 666 | |||
| 689 | ts->input_dev = devm_input_allocate_device(&ts->client->dev); | 667 | ts->input_dev = devm_input_allocate_device(&ts->client->dev); |
| 690 | if (!ts->input_dev) { | 668 | if (!ts->input_dev) { |
| 691 | dev_err(&ts->client->dev, "Failed to allocate input device."); | 669 | dev_err(&ts->client->dev, "Failed to allocate input device."); |
| 692 | return -ENOMEM; | 670 | return -ENOMEM; |
| 693 | } | 671 | } |
| 694 | 672 | ||
| 695 | input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, | ||
| 696 | 0, ts->abs_x_max, 0, 0); | ||
| 697 | input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, | ||
| 698 | 0, ts->abs_y_max, 0, 0); | ||
| 699 | input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); | ||
| 700 | input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); | ||
| 701 | |||
| 702 | input_mt_init_slots(ts->input_dev, ts->max_touch_num, | ||
| 703 | INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); | ||
| 704 | |||
| 705 | ts->input_dev->name = "Goodix Capacitive TouchScreen"; | 673 | ts->input_dev->name = "Goodix Capacitive TouchScreen"; |
| 706 | ts->input_dev->phys = "input/ts"; | 674 | ts->input_dev->phys = "input/ts"; |
| 707 | ts->input_dev->id.bustype = BUS_I2C; | 675 | ts->input_dev->id.bustype = BUS_I2C; |
| @@ -712,42 +680,49 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) | |||
| 712 | /* Capacitive Windows/Home button on some devices */ | 680 | /* Capacitive Windows/Home button on some devices */ |
| 713 | input_set_capability(ts->input_dev, EV_KEY, KEY_LEFTMETA); | 681 | input_set_capability(ts->input_dev, EV_KEY, KEY_LEFTMETA); |
| 714 | 682 | ||
| 715 | error = input_register_device(ts->input_dev); | 683 | input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X); |
| 716 | if (error) { | 684 | input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y); |
| 717 | dev_err(&ts->client->dev, | 685 | input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); |
| 718 | "Failed to register input device: %d", error); | 686 | input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); |
| 719 | return error; | ||
| 720 | } | ||
| 721 | 687 | ||
| 722 | return 0; | 688 | /* Read configuration and apply touchscreen parameters */ |
| 723 | } | 689 | goodix_read_config(ts); |
| 724 | 690 | ||
| 725 | /** | 691 | /* Try overriding touchscreen parameters via device properties */ |
| 726 | * goodix_configure_dev - Finish device initialization | 692 | touchscreen_parse_properties(ts->input_dev, true, &ts->prop); |
| 727 | * | ||
| 728 | * @ts: our goodix_ts_data pointer | ||
| 729 | * | ||
| 730 | * Must be called from probe to finish initialization of the device. | ||
| 731 | * Contains the common initialization code for both devices that | ||
| 732 | * declare gpio pins and devices that do not. It is either called | ||
| 733 | * directly from probe or from request_firmware_wait callback. | ||
| 734 | */ | ||
| 735 | static int goodix_configure_dev(struct goodix_ts_data *ts) | ||
| 736 | { | ||
| 737 | int error; | ||
| 738 | 693 | ||
| 739 | ts->swapped_x_y = device_property_read_bool(&ts->client->dev, | 694 | if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) { |
| 740 | "touchscreen-swapped-x-y"); | 695 | dev_err(&ts->client->dev, "Invalid config, using defaults\n"); |
| 741 | ts->inverted_x = device_property_read_bool(&ts->client->dev, | 696 | ts->prop.max_x = GOODIX_MAX_WIDTH - 1; |
| 742 | "touchscreen-inverted-x"); | 697 | ts->prop.max_y = GOODIX_MAX_HEIGHT - 1; |
| 743 | ts->inverted_y = device_property_read_bool(&ts->client->dev, | 698 | ts->max_touch_num = GOODIX_MAX_CONTACTS; |
| 744 | "touchscreen-inverted-y"); | 699 | input_abs_set_max(ts->input_dev, |
| 700 | ABS_MT_POSITION_X, ts->prop.max_x); | ||
| 701 | input_abs_set_max(ts->input_dev, | ||
| 702 | ABS_MT_POSITION_Y, ts->prop.max_y); | ||
| 703 | } | ||
| 745 | 704 | ||
| 746 | goodix_read_config(ts); | 705 | if (dmi_check_system(rotated_screen)) { |
| 706 | ts->prop.invert_x = true; | ||
| 707 | ts->prop.invert_y = true; | ||
| 708 | dev_dbg(&ts->client->dev, | ||
| 709 | "Applying '180 degrees rotated screen' quirk\n"); | ||
| 710 | } | ||
| 747 | 711 | ||
| 748 | error = goodix_request_input_dev(ts); | 712 | error = input_mt_init_slots(ts->input_dev, ts->max_touch_num, |
| 749 | if (error) | 713 | INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); |
| 714 | if (error) { | ||
| 715 | dev_err(&ts->client->dev, | ||
| 716 | "Failed to initialize MT slots: %d", error); | ||
| 717 | return error; | ||
| 718 | } | ||
| 719 | |||
| 720 | error = input_register_device(ts->input_dev); | ||
| 721 | if (error) { | ||
| 722 | dev_err(&ts->client->dev, | ||
| 723 | "Failed to register input device: %d", error); | ||
| 750 | return error; | 724 | return error; |
| 725 | } | ||
| 751 | 726 | ||
| 752 | ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; | 727 | ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; |
| 753 | error = goodix_request_irq(ts); | 728 | error = goodix_request_irq(ts); |
