diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-04 11:14:22 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-07-04 11:14:22 -0400 |
commit | ec0337e089b21d1d81e23d31807a9b02df11daac (patch) | |
tree | cef4dfcfa876cf1d4c54e346f263f85c14498071 | |
parent | 5c65e7be4cbb015e39759275746f31bb6fa74f77 (diff) | |
parent | a5cba18c4de288335a975b5d57b68f6787330985 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull second round of input updates from Dmitry Torokhov:
"A new driver for Weida wdt87xx touch controllers, and a bunch of
fixups for other drivers"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
Input: wdt87xx_i2c - add a scaling factor for TOUCH_MAJOR event
Input: wdt87xx_i2c - remove stray newline in diagnostic message
Input: arc_ps2 - add HAS_IOMEM dependency
Input: wdt87xx_i2c - fix format warning
Input: improve parsing OF parameters for touchscreens
Input: edt-ft5x06 - mark as direct input device
Input: use for_each_set_bit() where appropriate
Input: add a driver for wdt87xx touchscreen controller
Input: axp20x-pek - fix reporting button state as inverted
Input: xpad - re-send LED command on present event
Input: xpad - set the LEDs properly on XBox Wireless controllers
Input: imx_keypad - check for clk_prepare_enable() error
-rw-r--r-- | drivers/input/input.c | 34 | ||||
-rw-r--r-- | drivers/input/joystick/xpad.c | 78 | ||||
-rw-r--r-- | drivers/input/keyboard/imx_keypad.c | 4 | ||||
-rw-r--r-- | drivers/input/misc/axp20x-pek.c | 8 | ||||
-rw-r--r-- | drivers/input/serio/Kconfig | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/edt-ft5x06.c | 9 | ||||
-rw-r--r-- | drivers/input/touchscreen/of_touchscreen.c | 69 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2005.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/wdt87xx_i2c.c | 1149 | ||||
-rw-r--r-- | include/linux/input/touchscreen.h | 5 |
12 files changed, 1296 insertions, 76 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c index f31578423636..78d24990a816 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c | |||
@@ -677,12 +677,9 @@ static void input_dev_release_keys(struct input_dev *dev) | |||
677 | int code; | 677 | int code; |
678 | 678 | ||
679 | if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { | 679 | if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { |
680 | for (code = 0; code <= KEY_MAX; code++) { | 680 | for_each_set_bit(code, dev->key, KEY_CNT) |
681 | if (is_event_supported(code, dev->keybit, KEY_MAX) && | 681 | input_pass_event(dev, EV_KEY, code, 0); |
682 | __test_and_clear_bit(code, dev->key)) { | 682 | memset(dev->key, 0, sizeof(dev->key)); |
683 | input_pass_event(dev, EV_KEY, code, 0); | ||
684 | } | ||
685 | } | ||
686 | input_pass_event(dev, EV_SYN, SYN_REPORT, 1); | 683 | input_pass_event(dev, EV_SYN, SYN_REPORT, 1); |
687 | } | 684 | } |
688 | } | 685 | } |
@@ -1626,10 +1623,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) | |||
1626 | if (!test_bit(EV_##type, dev->evbit)) \ | 1623 | if (!test_bit(EV_##type, dev->evbit)) \ |
1627 | break; \ | 1624 | break; \ |
1628 | \ | 1625 | \ |
1629 | for (i = 0; i < type##_MAX; i++) { \ | 1626 | for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ |
1630 | if (!test_bit(i, dev->bits##bit)) \ | ||
1631 | continue; \ | ||
1632 | \ | ||
1633 | active = test_bit(i, dev->bits); \ | 1627 | active = test_bit(i, dev->bits); \ |
1634 | if (!active && !on) \ | 1628 | if (!active && !on) \ |
1635 | continue; \ | 1629 | continue; \ |
@@ -1980,22 +1974,12 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev) | |||
1980 | 1974 | ||
1981 | events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */ | 1975 | events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */ |
1982 | 1976 | ||
1983 | if (test_bit(EV_ABS, dev->evbit)) { | 1977 | if (test_bit(EV_ABS, dev->evbit)) |
1984 | for (i = 0; i < ABS_CNT; i++) { | 1978 | for_each_set_bit(i, dev->absbit, ABS_CNT) |
1985 | if (test_bit(i, dev->absbit)) { | 1979 | events += input_is_mt_axis(i) ? mt_slots : 1; |
1986 | if (input_is_mt_axis(i)) | ||
1987 | events += mt_slots; | ||
1988 | else | ||
1989 | events++; | ||
1990 | } | ||
1991 | } | ||
1992 | } | ||
1993 | 1980 | ||
1994 | if (test_bit(EV_REL, dev->evbit)) { | 1981 | if (test_bit(EV_REL, dev->evbit)) |
1995 | for (i = 0; i < REL_CNT; i++) | 1982 | events += bitmap_weight(dev->relbit, REL_CNT); |
1996 | if (test_bit(i, dev->relbit)) | ||
1997 | events++; | ||
1998 | } | ||
1999 | 1983 | ||
2000 | /* Make room for KEY and MSC events */ | 1984 | /* Make room for KEY and MSC events */ |
2001 | events += 7; | 1985 | events += 7; |
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 61c761156371..f8850f9cb331 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c | |||
@@ -344,6 +344,7 @@ struct usb_xpad { | |||
344 | 344 | ||
345 | int mapping; /* map d-pad to buttons or to axes */ | 345 | int mapping; /* map d-pad to buttons or to axes */ |
346 | int xtype; /* type of xbox device */ | 346 | int xtype; /* type of xbox device */ |
347 | unsigned long led_no; /* led to lit on xbox360 controllers */ | ||
347 | }; | 348 | }; |
348 | 349 | ||
349 | /* | 350 | /* |
@@ -488,6 +489,8 @@ static void xpad360_process_packet(struct usb_xpad *xpad, | |||
488 | input_sync(dev); | 489 | input_sync(dev); |
489 | } | 490 | } |
490 | 491 | ||
492 | static void xpad_identify_controller(struct usb_xpad *xpad); | ||
493 | |||
491 | /* | 494 | /* |
492 | * xpad360w_process_packet | 495 | * xpad360w_process_packet |
493 | * | 496 | * |
@@ -510,6 +513,11 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha | |||
510 | if (data[1] & 0x80) { | 513 | if (data[1] & 0x80) { |
511 | xpad->pad_present = 1; | 514 | xpad->pad_present = 1; |
512 | usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); | 515 | usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); |
516 | /* | ||
517 | * Light up the segment corresponding to | ||
518 | * controller number. | ||
519 | */ | ||
520 | xpad_identify_controller(xpad); | ||
513 | } else | 521 | } else |
514 | xpad->pad_present = 0; | 522 | xpad->pad_present = 0; |
515 | } | 523 | } |
@@ -881,17 +889,63 @@ struct xpad_led { | |||
881 | struct usb_xpad *xpad; | 889 | struct usb_xpad *xpad; |
882 | }; | 890 | }; |
883 | 891 | ||
892 | /** | ||
893 | * @param command | ||
894 | * 0: off | ||
895 | * 1: all blink, then previous setting | ||
896 | * 2: 1/top-left blink, then on | ||
897 | * 3: 2/top-right blink, then on | ||
898 | * 4: 3/bottom-left blink, then on | ||
899 | * 5: 4/bottom-right blink, then on | ||
900 | * 6: 1/top-left on | ||
901 | * 7: 2/top-right on | ||
902 | * 8: 3/bottom-left on | ||
903 | * 9: 4/bottom-right on | ||
904 | * 10: rotate | ||
905 | * 11: blink, based on previous setting | ||
906 | * 12: slow blink, based on previous setting | ||
907 | * 13: rotate with two lights | ||
908 | * 14: persistent slow all blink | ||
909 | * 15: blink once, then previous setting | ||
910 | */ | ||
884 | static void xpad_send_led_command(struct usb_xpad *xpad, int command) | 911 | static void xpad_send_led_command(struct usb_xpad *xpad, int command) |
885 | { | 912 | { |
886 | if (command >= 0 && command < 14) { | 913 | command %= 16; |
887 | mutex_lock(&xpad->odata_mutex); | 914 | |
915 | mutex_lock(&xpad->odata_mutex); | ||
916 | |||
917 | switch (xpad->xtype) { | ||
918 | case XTYPE_XBOX360: | ||
888 | xpad->odata[0] = 0x01; | 919 | xpad->odata[0] = 0x01; |
889 | xpad->odata[1] = 0x03; | 920 | xpad->odata[1] = 0x03; |
890 | xpad->odata[2] = command; | 921 | xpad->odata[2] = command; |
891 | xpad->irq_out->transfer_buffer_length = 3; | 922 | xpad->irq_out->transfer_buffer_length = 3; |
892 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); | 923 | break; |
893 | mutex_unlock(&xpad->odata_mutex); | 924 | case XTYPE_XBOX360W: |
925 | xpad->odata[0] = 0x00; | ||
926 | xpad->odata[1] = 0x00; | ||
927 | xpad->odata[2] = 0x08; | ||
928 | xpad->odata[3] = 0x40 + command; | ||
929 | xpad->odata[4] = 0x00; | ||
930 | xpad->odata[5] = 0x00; | ||
931 | xpad->odata[6] = 0x00; | ||
932 | xpad->odata[7] = 0x00; | ||
933 | xpad->odata[8] = 0x00; | ||
934 | xpad->odata[9] = 0x00; | ||
935 | xpad->odata[10] = 0x00; | ||
936 | xpad->odata[11] = 0x00; | ||
937 | xpad->irq_out->transfer_buffer_length = 12; | ||
938 | break; | ||
894 | } | 939 | } |
940 | |||
941 | usb_submit_urb(xpad->irq_out, GFP_KERNEL); | ||
942 | mutex_unlock(&xpad->odata_mutex); | ||
943 | } | ||
944 | |||
945 | static void xpad_identify_controller(struct usb_xpad *xpad) | ||
946 | { | ||
947 | /* Light up the segment corresponding to controller number */ | ||
948 | xpad_send_led_command(xpad, (xpad->led_no % 4) + 2); | ||
895 | } | 949 | } |
896 | 950 | ||
897 | static void xpad_led_set(struct led_classdev *led_cdev, | 951 | static void xpad_led_set(struct led_classdev *led_cdev, |
@@ -905,22 +959,21 @@ static void xpad_led_set(struct led_classdev *led_cdev, | |||
905 | 959 | ||
906 | static int xpad_led_probe(struct usb_xpad *xpad) | 960 | static int xpad_led_probe(struct usb_xpad *xpad) |
907 | { | 961 | { |
908 | static atomic_t led_seq = ATOMIC_INIT(-1); | 962 | static atomic_t led_seq = ATOMIC_INIT(-1); |
909 | unsigned long led_no; | ||
910 | struct xpad_led *led; | 963 | struct xpad_led *led; |
911 | struct led_classdev *led_cdev; | 964 | struct led_classdev *led_cdev; |
912 | int error; | 965 | int error; |
913 | 966 | ||
914 | if (xpad->xtype != XTYPE_XBOX360) | 967 | if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W) |
915 | return 0; | 968 | return 0; |
916 | 969 | ||
917 | xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); | 970 | xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); |
918 | if (!led) | 971 | if (!led) |
919 | return -ENOMEM; | 972 | return -ENOMEM; |
920 | 973 | ||
921 | led_no = atomic_inc_return(&led_seq); | 974 | xpad->led_no = atomic_inc_return(&led_seq); |
922 | 975 | ||
923 | snprintf(led->name, sizeof(led->name), "xpad%lu", led_no); | 976 | snprintf(led->name, sizeof(led->name), "xpad%lu", xpad->led_no); |
924 | led->xpad = xpad; | 977 | led->xpad = xpad; |
925 | 978 | ||
926 | led_cdev = &led->led_cdev; | 979 | led_cdev = &led->led_cdev; |
@@ -934,10 +987,8 @@ static int xpad_led_probe(struct usb_xpad *xpad) | |||
934 | return error; | 987 | return error; |
935 | } | 988 | } |
936 | 989 | ||
937 | /* | 990 | /* Light up the segment corresponding to controller number */ |
938 | * Light up the segment corresponding to controller number | 991 | xpad_identify_controller(xpad); |
939 | */ | ||
940 | xpad_send_led_command(xpad, (led_no % 4) + 2); | ||
941 | 992 | ||
942 | return 0; | 993 | return 0; |
943 | } | 994 | } |
@@ -954,6 +1005,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) | |||
954 | #else | 1005 | #else |
955 | static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } | 1006 | static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } |
956 | static void xpad_led_disconnect(struct usb_xpad *xpad) { } | 1007 | static void xpad_led_disconnect(struct usb_xpad *xpad) { } |
1008 | static void xpad_identify_controller(struct usb_xpad *xpad) { } | ||
957 | #endif | 1009 | #endif |
958 | 1010 | ||
959 | 1011 | ||
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 2e855e6f3565..d2ea863d6a45 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c | |||
@@ -506,7 +506,9 @@ static int imx_keypad_probe(struct platform_device *pdev) | |||
506 | input_set_drvdata(input_dev, keypad); | 506 | input_set_drvdata(input_dev, keypad); |
507 | 507 | ||
508 | /* Ensure that the keypad will stay dormant until opened */ | 508 | /* Ensure that the keypad will stay dormant until opened */ |
509 | clk_prepare_enable(keypad->clk); | 509 | error = clk_prepare_enable(keypad->clk); |
510 | if (error) | ||
511 | return error; | ||
510 | imx_keypad_inhibit(keypad); | 512 | imx_keypad_inhibit(keypad); |
511 | clk_disable_unprepare(keypad->clk); | 513 | clk_disable_unprepare(keypad->clk); |
512 | 514 | ||
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index f1c844739cd7..10e140af5aac 100644 --- a/drivers/input/misc/axp20x-pek.c +++ b/drivers/input/misc/axp20x-pek.c | |||
@@ -167,9 +167,13 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr) | |||
167 | struct input_dev *idev = pwr; | 167 | struct input_dev *idev = pwr; |
168 | struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); | 168 | struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); |
169 | 169 | ||
170 | if (irq == axp20x_pek->irq_dbr) | 170 | /* |
171 | * The power-button is connected to ground so a falling edge (dbf) | ||
172 | * means it is pressed. | ||
173 | */ | ||
174 | if (irq == axp20x_pek->irq_dbf) | ||
171 | input_report_key(idev, KEY_POWER, true); | 175 | input_report_key(idev, KEY_POWER, true); |
172 | else if (irq == axp20x_pek->irq_dbf) | 176 | else if (irq == axp20x_pek->irq_dbr) |
173 | input_report_key(idev, KEY_POWER, false); | 177 | input_report_key(idev, KEY_POWER, false); |
174 | 178 | ||
175 | input_sync(idev); | 179 | input_sync(idev); |
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 77833d7a004b..200841b77edb 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig | |||
@@ -244,6 +244,7 @@ config SERIO_PS2MULT | |||
244 | 244 | ||
245 | config SERIO_ARC_PS2 | 245 | config SERIO_ARC_PS2 |
246 | tristate "ARC PS/2 support" | 246 | tristate "ARC PS/2 support" |
247 | depends on HAS_IOMEM | ||
247 | help | 248 | help |
248 | Say Y here if you have an ARC FPGA platform with a PS/2 | 249 | Say Y here if you have an ARC FPGA platform with a PS/2 |
249 | controller in it. | 250 | controller in it. |
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d20fe1dff403..a854c6e5f09e 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
@@ -658,6 +658,18 @@ config TOUCHSCREEN_PIXCIR | |||
658 | To compile this driver as a module, choose M here: the | 658 | To compile this driver as a module, choose M here: the |
659 | module will be called pixcir_i2c_ts. | 659 | module will be called pixcir_i2c_ts. |
660 | 660 | ||
661 | config TOUCHSCREEN_WDT87XX_I2C | ||
662 | tristate "Weida HiTech I2C touchscreen" | ||
663 | depends on I2C | ||
664 | help | ||
665 | Say Y here if you have a Weida WDT87XX I2C touchscreen | ||
666 | connected to your system. | ||
667 | |||
668 | If unsure, say N. | ||
669 | |||
670 | To compile this driver as a module, choose M here: the | ||
671 | module will be called wdt87xx_i2c. | ||
672 | |||
661 | config TOUCHSCREEN_WM831X | 673 | config TOUCHSCREEN_WM831X |
662 | tristate "Support for WM831x touchscreen controllers" | 674 | tristate "Support for WM831x touchscreen controllers" |
663 | depends on MFD_WM831X | 675 | depends on MFD_WM831X |
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 44deea743d02..fa3d33bac7fc 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile | |||
@@ -72,6 +72,7 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o | |||
72 | obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o | 72 | obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o |
73 | obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o | 73 | obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o |
74 | obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o | 74 | obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o |
75 | obj-$(CONFIG_TOUCHSCREEN_WDT87XX_I2C) += wdt87xx_i2c.o | ||
75 | obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o | 76 | obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o |
76 | obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o | 77 | obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o |
77 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o | 78 | wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o |
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index e6aef3e48bd9..394b1de9a2a3 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c | |||
@@ -1035,20 +1035,15 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, | |||
1035 | input->id.bustype = BUS_I2C; | 1035 | input->id.bustype = BUS_I2C; |
1036 | input->dev.parent = &client->dev; | 1036 | input->dev.parent = &client->dev; |
1037 | 1037 | ||
1038 | __set_bit(EV_KEY, input->evbit); | ||
1039 | __set_bit(EV_ABS, input->evbit); | ||
1040 | __set_bit(BTN_TOUCH, input->keybit); | ||
1041 | input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); | ||
1042 | input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); | ||
1043 | input_set_abs_params(input, ABS_MT_POSITION_X, | 1038 | input_set_abs_params(input, ABS_MT_POSITION_X, |
1044 | 0, tsdata->num_x * 64 - 1, 0, 0); | 1039 | 0, tsdata->num_x * 64 - 1, 0, 0); |
1045 | input_set_abs_params(input, ABS_MT_POSITION_Y, | 1040 | input_set_abs_params(input, ABS_MT_POSITION_Y, |
1046 | 0, tsdata->num_y * 64 - 1, 0, 0); | 1041 | 0, tsdata->num_y * 64 - 1, 0, 0); |
1047 | 1042 | ||
1048 | if (!pdata) | 1043 | if (!pdata) |
1049 | touchscreen_parse_of_params(input); | 1044 | touchscreen_parse_of_params(input, true); |
1050 | 1045 | ||
1051 | error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); | 1046 | error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT); |
1052 | if (error) { | 1047 | if (error) { |
1053 | dev_err(&client->dev, "Unable to init MT slots.\n"); | 1048 | dev_err(&client->dev, "Unable to init MT slots.\n"); |
1054 | return error; | 1049 | return error; |
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index b82b5207c78b..806cd0ad160f 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c | |||
@@ -14,14 +14,22 @@ | |||
14 | #include <linux/input/mt.h> | 14 | #include <linux/input/mt.h> |
15 | #include <linux/input/touchscreen.h> | 15 | #include <linux/input/touchscreen.h> |
16 | 16 | ||
17 | static u32 of_get_optional_u32(struct device_node *np, | 17 | static bool touchscreen_get_prop_u32(struct device_node *np, |
18 | const char *property) | 18 | const char *property, |
19 | unsigned int default_value, | ||
20 | unsigned int *value) | ||
19 | { | 21 | { |
20 | u32 val = 0; | 22 | u32 val; |
23 | int error; | ||
21 | 24 | ||
22 | of_property_read_u32(np, property, &val); | 25 | error = of_property_read_u32(np, property, &val); |
26 | if (error) { | ||
27 | *value = default_value; | ||
28 | return false; | ||
29 | } | ||
23 | 30 | ||
24 | return val; | 31 | *value = val; |
32 | return true; | ||
25 | } | 33 | } |
26 | 34 | ||
27 | static void touchscreen_set_params(struct input_dev *dev, | 35 | static void touchscreen_set_params(struct input_dev *dev, |
@@ -54,34 +62,45 @@ static void touchscreen_set_params(struct input_dev *dev, | |||
54 | * input device accordingly. The function keeps previously setuped default | 62 | * input device accordingly. The function keeps previously setuped default |
55 | * values if no value is specified via DT. | 63 | * values if no value is specified via DT. |
56 | */ | 64 | */ |
57 | void touchscreen_parse_of_params(struct input_dev *dev) | 65 | void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch) |
58 | { | 66 | { |
59 | struct device_node *np = dev->dev.parent->of_node; | 67 | struct device_node *np = dev->dev.parent->of_node; |
60 | u32 maximum, fuzz; | 68 | unsigned int axis; |
69 | unsigned int maximum, fuzz; | ||
70 | bool data_present; | ||
61 | 71 | ||
62 | input_alloc_absinfo(dev); | 72 | input_alloc_absinfo(dev); |
63 | if (!dev->absinfo) | 73 | if (!dev->absinfo) |
64 | return; | 74 | return; |
65 | 75 | ||
66 | maximum = of_get_optional_u32(np, "touchscreen-size-x"); | 76 | axis = multitouch ? ABS_MT_POSITION_X : ABS_X; |
67 | fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x"); | 77 | data_present = touchscreen_get_prop_u32(np, "touchscreen-size-x", |
68 | if (maximum || fuzz) { | 78 | input_abs_get_max(dev, axis), |
69 | touchscreen_set_params(dev, ABS_X, maximum, fuzz); | 79 | &maximum) | |
70 | touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz); | 80 | touchscreen_get_prop_u32(np, "touchscreen-fuzz-x", |
71 | } | 81 | input_abs_get_fuzz(dev, axis), |
82 | &fuzz); | ||
83 | if (data_present) | ||
84 | touchscreen_set_params(dev, axis, maximum, fuzz); | ||
72 | 85 | ||
73 | maximum = of_get_optional_u32(np, "touchscreen-size-y"); | 86 | axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y; |
74 | fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y"); | 87 | data_present = touchscreen_get_prop_u32(np, "touchscreen-size-y", |
75 | if (maximum || fuzz) { | 88 | input_abs_get_max(dev, axis), |
76 | touchscreen_set_params(dev, ABS_Y, maximum, fuzz); | 89 | &maximum) | |
77 | touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz); | 90 | touchscreen_get_prop_u32(np, "touchscreen-fuzz-y", |
78 | } | 91 | input_abs_get_fuzz(dev, axis), |
92 | &fuzz); | ||
93 | if (data_present) | ||
94 | touchscreen_set_params(dev, axis, maximum, fuzz); | ||
79 | 95 | ||
80 | maximum = of_get_optional_u32(np, "touchscreen-max-pressure"); | 96 | axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; |
81 | fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure"); | 97 | data_present = touchscreen_get_prop_u32(np, "touchscreen-max-pressure", |
82 | if (maximum || fuzz) { | 98 | input_abs_get_max(dev, axis), |
83 | touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz); | 99 | &maximum) | |
84 | touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz); | 100 | touchscreen_get_prop_u32(np, "touchscreen-fuzz-pressure", |
85 | } | 101 | input_abs_get_fuzz(dev, axis), |
102 | &fuzz); | ||
103 | if (data_present) | ||
104 | touchscreen_set_params(dev, axis, maximum, fuzz); | ||
86 | } | 105 | } |
87 | EXPORT_SYMBOL(touchscreen_parse_of_params); | 106 | EXPORT_SYMBOL(touchscreen_parse_of_params); |
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 72657c579430..d8c025b0f88c 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c | |||
@@ -709,7 +709,7 @@ static int tsc2005_probe(struct spi_device *spi) | |||
709 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); | 709 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); |
710 | 710 | ||
711 | if (np) | 711 | if (np) |
712 | touchscreen_parse_of_params(input_dev); | 712 | touchscreen_parse_of_params(input_dev, false); |
713 | 713 | ||
714 | input_dev->open = tsc2005_open; | 714 | input_dev->open = tsc2005_open; |
715 | input_dev->close = tsc2005_close; | 715 | input_dev->close = tsc2005_close; |
diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c new file mode 100644 index 000000000000..fb92ae1c5fae --- /dev/null +++ b/drivers/input/touchscreen/wdt87xx_i2c.c | |||
@@ -0,0 +1,1149 @@ | |||
1 | /* | ||
2 | * Weida HiTech WDT87xx TouchScreen I2C driver | ||
3 | * | ||
4 | * Copyright (c) 2015 Weida Hi-Tech Co., Ltd. | ||
5 | * HN Chen <hn.chen@weidahitech.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | */ | ||
11 | |||
12 | #include <linux/i2c.h> | ||
13 | #include <linux/input.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/firmware.h> | ||
21 | #include <linux/input/mt.h> | ||
22 | #include <linux/acpi.h> | ||
23 | #include <asm/unaligned.h> | ||
24 | |||
25 | #define WDT87XX_NAME "wdt87xx_i2c" | ||
26 | #define WDT87XX_DRV_VER "0.9.6" | ||
27 | #define WDT87XX_FW_NAME "wdt87xx_fw.bin" | ||
28 | #define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" | ||
29 | |||
30 | #define MODE_ACTIVE 0x01 | ||
31 | #define MODE_READY 0x02 | ||
32 | #define MODE_IDLE 0x03 | ||
33 | #define MODE_SLEEP 0x04 | ||
34 | #define MODE_STOP 0xFF | ||
35 | |||
36 | #define WDT_MAX_FINGER 10 | ||
37 | #define WDT_RAW_BUF_COUNT 54 | ||
38 | #define WDT_V1_RAW_BUF_COUNT 74 | ||
39 | #define WDT_FIRMWARE_ID 0xa9e368f5 | ||
40 | |||
41 | #define PG_SIZE 0x1000 | ||
42 | #define MAX_RETRIES 3 | ||
43 | |||
44 | #define MAX_UNIT_AXIS 0x7FFF | ||
45 | |||
46 | #define PKT_READ_SIZE 72 | ||
47 | #define PKT_WRITE_SIZE 80 | ||
48 | |||
49 | /* the finger definition of the report event */ | ||
50 | #define FINGER_EV_OFFSET_ID 0 | ||
51 | #define FINGER_EV_OFFSET_X 1 | ||
52 | #define FINGER_EV_OFFSET_Y 3 | ||
53 | #define FINGER_EV_SIZE 5 | ||
54 | |||
55 | #define FINGER_EV_V1_OFFSET_ID 0 | ||
56 | #define FINGER_EV_V1_OFFSET_W 1 | ||
57 | #define FINGER_EV_V1_OFFSET_P 2 | ||
58 | #define FINGER_EV_V1_OFFSET_X 3 | ||
59 | #define FINGER_EV_V1_OFFSET_Y 5 | ||
60 | #define FINGER_EV_V1_SIZE 7 | ||
61 | |||
62 | /* The definition of a report packet */ | ||
63 | #define TOUCH_PK_OFFSET_REPORT_ID 0 | ||
64 | #define TOUCH_PK_OFFSET_EVENT 1 | ||
65 | #define TOUCH_PK_OFFSET_SCAN_TIME 51 | ||
66 | #define TOUCH_PK_OFFSET_FNGR_NUM 53 | ||
67 | |||
68 | #define TOUCH_PK_V1_OFFSET_REPORT_ID 0 | ||
69 | #define TOUCH_PK_V1_OFFSET_EVENT 1 | ||
70 | #define TOUCH_PK_V1_OFFSET_SCAN_TIME 71 | ||
71 | #define TOUCH_PK_V1_OFFSET_FNGR_NUM 73 | ||
72 | |||
73 | /* The definition of the controller parameters */ | ||
74 | #define CTL_PARAM_OFFSET_FW_ID 0 | ||
75 | #define CTL_PARAM_OFFSET_PLAT_ID 2 | ||
76 | #define CTL_PARAM_OFFSET_XMLS_ID1 4 | ||
77 | #define CTL_PARAM_OFFSET_XMLS_ID2 6 | ||
78 | #define CTL_PARAM_OFFSET_PHY_CH_X 8 | ||
79 | #define CTL_PARAM_OFFSET_PHY_CH_Y 10 | ||
80 | #define CTL_PARAM_OFFSET_PHY_X0 12 | ||
81 | #define CTL_PARAM_OFFSET_PHY_X1 14 | ||
82 | #define CTL_PARAM_OFFSET_PHY_Y0 16 | ||
83 | #define CTL_PARAM_OFFSET_PHY_Y1 18 | ||
84 | #define CTL_PARAM_OFFSET_PHY_W 22 | ||
85 | #define CTL_PARAM_OFFSET_PHY_H 24 | ||
86 | #define CTL_PARAM_OFFSET_FACTOR 32 | ||
87 | |||
88 | /* Communication commands */ | ||
89 | #define PACKET_SIZE 56 | ||
90 | #define VND_REQ_READ 0x06 | ||
91 | #define VND_READ_DATA 0x07 | ||
92 | #define VND_REQ_WRITE 0x08 | ||
93 | |||
94 | #define VND_CMD_START 0x00 | ||
95 | #define VND_CMD_STOP 0x01 | ||
96 | #define VND_CMD_RESET 0x09 | ||
97 | |||
98 | #define VND_CMD_ERASE 0x1A | ||
99 | |||
100 | #define VND_GET_CHECKSUM 0x66 | ||
101 | |||
102 | #define VND_SET_DATA 0x83 | ||
103 | #define VND_SET_COMMAND_DATA 0x84 | ||
104 | #define VND_SET_CHECKSUM_CALC 0x86 | ||
105 | #define VND_SET_CHECKSUM_LENGTH 0x87 | ||
106 | |||
107 | #define VND_CMD_SFLCK 0xFC | ||
108 | #define VND_CMD_SFUNL 0xFD | ||
109 | |||
110 | #define CMD_SFLCK_KEY 0xC39B | ||
111 | #define CMD_SFUNL_KEY 0x95DA | ||
112 | |||
113 | #define STRIDX_PLATFORM_ID 0x80 | ||
114 | #define STRIDX_PARAMETERS 0x81 | ||
115 | |||
116 | #define CMD_BUF_SIZE 8 | ||
117 | #define PKT_BUF_SIZE 64 | ||
118 | |||
119 | /* The definition of the command packet */ | ||
120 | #define CMD_REPORT_ID_OFFSET 0x0 | ||
121 | #define CMD_TYPE_OFFSET 0x1 | ||
122 | #define CMD_INDEX_OFFSET 0x2 | ||
123 | #define CMD_KEY_OFFSET 0x3 | ||
124 | #define CMD_LENGTH_OFFSET 0x4 | ||
125 | #define CMD_DATA_OFFSET 0x8 | ||
126 | |||
127 | /* The definition of firmware chunk tags */ | ||
128 | #define FOURCC_ID_RIFF 0x46464952 | ||
129 | #define FOURCC_ID_WHIF 0x46494857 | ||
130 | #define FOURCC_ID_FRMT 0x544D5246 | ||
131 | #define FOURCC_ID_FRWR 0x52575246 | ||
132 | #define FOURCC_ID_CNFG 0x47464E43 | ||
133 | |||
134 | #define CHUNK_ID_FRMT FOURCC_ID_FRMT | ||
135 | #define CHUNK_ID_FRWR FOURCC_ID_FRWR | ||
136 | #define CHUNK_ID_CNFG FOURCC_ID_CNFG | ||
137 | |||
138 | #define FW_FOURCC1_OFFSET 0 | ||
139 | #define FW_SIZE_OFFSET 4 | ||
140 | #define FW_FOURCC2_OFFSET 8 | ||
141 | #define FW_PAYLOAD_OFFSET 40 | ||
142 | |||
143 | #define FW_CHUNK_ID_OFFSET 0 | ||
144 | #define FW_CHUNK_SIZE_OFFSET 4 | ||
145 | #define FW_CHUNK_TGT_START_OFFSET 8 | ||
146 | #define FW_CHUNK_PAYLOAD_LEN_OFFSET 12 | ||
147 | #define FW_CHUNK_SRC_START_OFFSET 16 | ||
148 | #define FW_CHUNK_VERSION_OFFSET 20 | ||
149 | #define FW_CHUNK_ATTR_OFFSET 24 | ||
150 | #define FW_CHUNK_PAYLOAD_OFFSET 32 | ||
151 | |||
152 | /* Controller requires minimum 300us between commands */ | ||
153 | #define WDT_COMMAND_DELAY_MS 2 | ||
154 | #define WDT_FLASH_WRITE_DELAY_MS 4 | ||
155 | |||
156 | struct wdt87xx_sys_param { | ||
157 | u16 fw_id; | ||
158 | u16 plat_id; | ||
159 | u16 xmls_id1; | ||
160 | u16 xmls_id2; | ||
161 | u16 phy_ch_x; | ||
162 | u16 phy_ch_y; | ||
163 | u16 phy_w; | ||
164 | u16 phy_h; | ||
165 | u16 scaling_factor; | ||
166 | u32 max_x; | ||
167 | u32 max_y; | ||
168 | }; | ||
169 | |||
170 | struct wdt87xx_data { | ||
171 | struct i2c_client *client; | ||
172 | struct input_dev *input; | ||
173 | /* Mutex for fw update to prevent concurrent access */ | ||
174 | struct mutex fw_mutex; | ||
175 | struct wdt87xx_sys_param param; | ||
176 | u8 phys[32]; | ||
177 | }; | ||
178 | |||
179 | static int wdt87xx_i2c_xfer(struct i2c_client *client, | ||
180 | void *txdata, size_t txlen, | ||
181 | void *rxdata, size_t rxlen) | ||
182 | { | ||
183 | struct i2c_msg msgs[] = { | ||
184 | { | ||
185 | .addr = client->addr, | ||
186 | .flags = 0, | ||
187 | .len = txlen, | ||
188 | .buf = txdata, | ||
189 | }, | ||
190 | { | ||
191 | .addr = client->addr, | ||
192 | .flags = I2C_M_RD, | ||
193 | .len = rxlen, | ||
194 | .buf = rxdata, | ||
195 | }, | ||
196 | }; | ||
197 | int error; | ||
198 | int ret; | ||
199 | |||
200 | ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); | ||
201 | if (ret != ARRAY_SIZE(msgs)) { | ||
202 | error = ret < 0 ? ret : -EIO; | ||
203 | dev_err(&client->dev, "%s: i2c transfer failed: %d\n", | ||
204 | __func__, error); | ||
205 | return error; | ||
206 | } | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int wdt87xx_get_string(struct i2c_client *client, u8 str_idx, | ||
212 | u8 *buf, size_t len) | ||
213 | { | ||
214 | u8 tx_buf[] = { 0x22, 0x00, 0x13, 0x0E, str_idx, 0x23, 0x00 }; | ||
215 | u8 rx_buf[PKT_WRITE_SIZE]; | ||
216 | size_t rx_len = len + 2; | ||
217 | int error; | ||
218 | |||
219 | if (rx_len > sizeof(rx_buf)) | ||
220 | return -EINVAL; | ||
221 | |||
222 | error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf), | ||
223 | rx_buf, rx_len); | ||
224 | if (error) { | ||
225 | dev_err(&client->dev, "get string failed: %d\n", error); | ||
226 | return error; | ||
227 | } | ||
228 | |||
229 | if (rx_buf[1] != 0x03) { | ||
230 | dev_err(&client->dev, "unexpected response to get string: %d\n", | ||
231 | rx_buf[1]); | ||
232 | return -EINVAL; | ||
233 | } | ||
234 | |||
235 | rx_len = min_t(size_t, len, rx_buf[0]); | ||
236 | memcpy(buf, &rx_buf[2], rx_len); | ||
237 | |||
238 | mdelay(WDT_COMMAND_DELAY_MS); | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int wdt87xx_get_feature(struct i2c_client *client, | ||
244 | u8 *buf, size_t buf_size) | ||
245 | { | ||
246 | u8 tx_buf[8]; | ||
247 | u8 rx_buf[PKT_WRITE_SIZE]; | ||
248 | size_t tx_len = 0; | ||
249 | size_t rx_len = buf_size + 2; | ||
250 | int error; | ||
251 | |||
252 | if (rx_len > sizeof(rx_buf)) | ||
253 | return -EINVAL; | ||
254 | |||
255 | /* Get feature command packet */ | ||
256 | tx_buf[tx_len++] = 0x22; | ||
257 | tx_buf[tx_len++] = 0x00; | ||
258 | if (buf[CMD_REPORT_ID_OFFSET] > 0xF) { | ||
259 | tx_buf[tx_len++] = 0x30; | ||
260 | tx_buf[tx_len++] = 0x02; | ||
261 | tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET]; | ||
262 | } else { | ||
263 | tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET]; | ||
264 | tx_buf[tx_len++] = 0x02; | ||
265 | } | ||
266 | tx_buf[tx_len++] = 0x23; | ||
267 | tx_buf[tx_len++] = 0x00; | ||
268 | |||
269 | error = wdt87xx_i2c_xfer(client, tx_buf, tx_len, rx_buf, rx_len); | ||
270 | if (error) { | ||
271 | dev_err(&client->dev, "get feature failed: %d\n", error); | ||
272 | return error; | ||
273 | } | ||
274 | |||
275 | rx_len = min_t(size_t, buf_size, get_unaligned_le16(rx_buf)); | ||
276 | memcpy(buf, &rx_buf[2], rx_len); | ||
277 | |||
278 | mdelay(WDT_COMMAND_DELAY_MS); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | static int wdt87xx_set_feature(struct i2c_client *client, | ||
284 | const u8 *buf, size_t buf_size) | ||
285 | { | ||
286 | u8 tx_buf[PKT_WRITE_SIZE]; | ||
287 | int tx_len = 0; | ||
288 | int error; | ||
289 | |||
290 | /* Set feature command packet */ | ||
291 | tx_buf[tx_len++] = 0x22; | ||
292 | tx_buf[tx_len++] = 0x00; | ||
293 | if (buf[CMD_REPORT_ID_OFFSET] > 0xF) { | ||
294 | tx_buf[tx_len++] = 0x30; | ||
295 | tx_buf[tx_len++] = 0x03; | ||
296 | tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET]; | ||
297 | } else { | ||
298 | tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET]; | ||
299 | tx_buf[tx_len++] = 0x03; | ||
300 | } | ||
301 | tx_buf[tx_len++] = 0x23; | ||
302 | tx_buf[tx_len++] = 0x00; | ||
303 | tx_buf[tx_len++] = (buf_size & 0xFF); | ||
304 | tx_buf[tx_len++] = ((buf_size & 0xFF00) >> 8); | ||
305 | |||
306 | if (tx_len + buf_size > sizeof(tx_buf)) | ||
307 | return -EINVAL; | ||
308 | |||
309 | memcpy(&tx_buf[tx_len], buf, buf_size); | ||
310 | tx_len += buf_size; | ||
311 | |||
312 | error = i2c_master_send(client, tx_buf, tx_len); | ||
313 | if (error < 0) { | ||
314 | dev_err(&client->dev, "set feature failed: %d\n", error); | ||
315 | return error; | ||
316 | } | ||
317 | |||
318 | mdelay(WDT_COMMAND_DELAY_MS); | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int wdt87xx_send_command(struct i2c_client *client, int cmd, int value) | ||
324 | { | ||
325 | u8 cmd_buf[CMD_BUF_SIZE]; | ||
326 | |||
327 | /* Set the command packet */ | ||
328 | cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; | ||
329 | cmd_buf[CMD_TYPE_OFFSET] = VND_SET_COMMAND_DATA; | ||
330 | put_unaligned_le16((u16)cmd, &cmd_buf[CMD_INDEX_OFFSET]); | ||
331 | |||
332 | switch (cmd) { | ||
333 | case VND_CMD_START: | ||
334 | case VND_CMD_STOP: | ||
335 | case VND_CMD_RESET: | ||
336 | /* Mode selector */ | ||
337 | put_unaligned_le32((value & 0xFF), &cmd_buf[CMD_LENGTH_OFFSET]); | ||
338 | break; | ||
339 | |||
340 | case VND_CMD_SFLCK: | ||
341 | put_unaligned_le16(CMD_SFLCK_KEY, &cmd_buf[CMD_KEY_OFFSET]); | ||
342 | break; | ||
343 | |||
344 | case VND_CMD_SFUNL: | ||
345 | put_unaligned_le16(CMD_SFUNL_KEY, &cmd_buf[CMD_KEY_OFFSET]); | ||
346 | break; | ||
347 | |||
348 | case VND_CMD_ERASE: | ||
349 | case VND_SET_CHECKSUM_CALC: | ||
350 | case VND_SET_CHECKSUM_LENGTH: | ||
351 | put_unaligned_le32(value, &cmd_buf[CMD_KEY_OFFSET]); | ||
352 | break; | ||
353 | |||
354 | default: | ||
355 | cmd_buf[CMD_REPORT_ID_OFFSET] = 0; | ||
356 | dev_err(&client->dev, "Invalid command: %d\n", cmd); | ||
357 | return -EINVAL; | ||
358 | } | ||
359 | |||
360 | return wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf)); | ||
361 | } | ||
362 | |||
363 | static int wdt87xx_sw_reset(struct i2c_client *client) | ||
364 | { | ||
365 | int error; | ||
366 | |||
367 | dev_dbg(&client->dev, "resetting device now\n"); | ||
368 | |||
369 | error = wdt87xx_send_command(client, VND_CMD_RESET, 0); | ||
370 | if (error) { | ||
371 | dev_err(&client->dev, "reset failed\n"); | ||
372 | return error; | ||
373 | } | ||
374 | |||
375 | /* Wait the device to be ready */ | ||
376 | msleep(200); | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static const void *wdt87xx_get_fw_chunk(const struct firmware *fw, u32 id) | ||
382 | { | ||
383 | size_t pos = FW_PAYLOAD_OFFSET; | ||
384 | u32 chunk_id, chunk_size; | ||
385 | |||
386 | while (pos < fw->size) { | ||
387 | chunk_id = get_unaligned_le32(fw->data + | ||
388 | pos + FW_CHUNK_ID_OFFSET); | ||
389 | if (chunk_id == id) | ||
390 | return fw->data + pos; | ||
391 | |||
392 | chunk_size = get_unaligned_le32(fw->data + | ||
393 | pos + FW_CHUNK_SIZE_OFFSET); | ||
394 | pos += chunk_size + 2 * sizeof(u32); /* chunk ID + size */ | ||
395 | } | ||
396 | |||
397 | return NULL; | ||
398 | } | ||
399 | |||
400 | static int wdt87xx_get_sysparam(struct i2c_client *client, | ||
401 | struct wdt87xx_sys_param *param) | ||
402 | { | ||
403 | u8 buf[PKT_READ_SIZE]; | ||
404 | int error; | ||
405 | |||
406 | error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34); | ||
407 | if (error) { | ||
408 | dev_err(&client->dev, "failed to get parameters\n"); | ||
409 | return error; | ||
410 | } | ||
411 | |||
412 | param->xmls_id1 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID1); | ||
413 | param->xmls_id2 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID2); | ||
414 | param->phy_ch_x = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_X); | ||
415 | param->phy_ch_y = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_Y); | ||
416 | param->phy_w = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_W) / 10; | ||
417 | param->phy_h = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_H) / 10; | ||
418 | |||
419 | /* Get the scaling factor of pixel to logical coordinate */ | ||
420 | param->scaling_factor = | ||
421 | get_unaligned_le16(buf + CTL_PARAM_OFFSET_FACTOR); | ||
422 | |||
423 | param->max_x = MAX_UNIT_AXIS; | ||
424 | param->max_y = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS * param->phy_h, | ||
425 | param->phy_w); | ||
426 | |||
427 | error = wdt87xx_get_string(client, STRIDX_PLATFORM_ID, buf, 8); | ||
428 | if (error) { | ||
429 | dev_err(&client->dev, "failed to get platform id\n"); | ||
430 | return error; | ||
431 | } | ||
432 | |||
433 | param->plat_id = buf[1]; | ||
434 | |||
435 | buf[0] = 0xf2; | ||
436 | error = wdt87xx_get_feature(client, buf, 16); | ||
437 | if (error) { | ||
438 | dev_err(&client->dev, "failed to get firmware id\n"); | ||
439 | return error; | ||
440 | } | ||
441 | |||
442 | if (buf[0] != 0xf2) { | ||
443 | dev_err(&client->dev, "wrong id of fw response: 0x%x\n", | ||
444 | buf[0]); | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | param->fw_id = get_unaligned_le16(&buf[1]); | ||
449 | |||
450 | dev_info(&client->dev, | ||
451 | "fw_id: 0x%x, plat_id: 0x%x, xml_id1: %04x, xml_id2: %04x\n", | ||
452 | param->fw_id, param->plat_id, | ||
453 | param->xmls_id1, param->xmls_id2); | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static int wdt87xx_validate_firmware(struct wdt87xx_data *wdt, | ||
459 | const struct firmware *fw) | ||
460 | { | ||
461 | const void *fw_chunk; | ||
462 | u32 data1, data2; | ||
463 | u32 size; | ||
464 | u8 fw_chip_id; | ||
465 | u8 chip_id; | ||
466 | |||
467 | data1 = get_unaligned_le32(fw->data + FW_FOURCC1_OFFSET); | ||
468 | data2 = get_unaligned_le32(fw->data + FW_FOURCC2_OFFSET); | ||
469 | if (data1 != FOURCC_ID_RIFF || data2 != FOURCC_ID_WHIF) { | ||
470 | dev_err(&wdt->client->dev, "check fw tag failed\n"); | ||
471 | return -EINVAL; | ||
472 | } | ||
473 | |||
474 | size = get_unaligned_le32(fw->data + FW_SIZE_OFFSET); | ||
475 | if (size != fw->size) { | ||
476 | dev_err(&wdt->client->dev, | ||
477 | "fw size mismatch: expected %d, actual %zu\n", | ||
478 | size, fw->size); | ||
479 | return -EINVAL; | ||
480 | } | ||
481 | |||
482 | /* | ||
483 | * Get the chip_id from the firmware. Make sure that it is the | ||
484 | * right controller to do the firmware and config update. | ||
485 | */ | ||
486 | fw_chunk = wdt87xx_get_fw_chunk(fw, CHUNK_ID_FRWR); | ||
487 | if (!fw_chunk) { | ||
488 | dev_err(&wdt->client->dev, | ||
489 | "unable to locate firmware chunk\n"); | ||
490 | return -EINVAL; | ||
491 | } | ||
492 | |||
493 | fw_chip_id = (get_unaligned_le32(fw_chunk + | ||
494 | FW_CHUNK_VERSION_OFFSET) >> 12) & 0xF; | ||
495 | chip_id = (wdt->param.fw_id >> 12) & 0xF; | ||
496 | |||
497 | if (fw_chip_id != chip_id) { | ||
498 | dev_err(&wdt->client->dev, | ||
499 | "fw version mismatch: fw %d vs. chip %d\n", | ||
500 | fw_chip_id, chip_id); | ||
501 | return -ENODEV; | ||
502 | } | ||
503 | |||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | static int wdt87xx_validate_fw_chunk(const void *data, int id) | ||
508 | { | ||
509 | if (id == CHUNK_ID_FRWR) { | ||
510 | u32 fw_id; | ||
511 | |||
512 | fw_id = get_unaligned_le32(data + FW_CHUNK_PAYLOAD_OFFSET); | ||
513 | if (fw_id != WDT_FIRMWARE_ID) | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | |||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static int wdt87xx_write_data(struct i2c_client *client, const char *data, | ||
521 | u32 address, int length) | ||
522 | { | ||
523 | u16 packet_size; | ||
524 | int count = 0; | ||
525 | int error; | ||
526 | u8 pkt_buf[PKT_BUF_SIZE]; | ||
527 | |||
528 | /* Address and length should be 4 bytes aligned */ | ||
529 | if ((address & 0x3) != 0 || (length & 0x3) != 0) { | ||
530 | dev_err(&client->dev, | ||
531 | "addr & len must be 4 bytes aligned %x, %x\n", | ||
532 | address, length); | ||
533 | return -EINVAL; | ||
534 | } | ||
535 | |||
536 | while (length) { | ||
537 | packet_size = min(length, PACKET_SIZE); | ||
538 | |||
539 | pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; | ||
540 | pkt_buf[CMD_TYPE_OFFSET] = VND_SET_DATA; | ||
541 | put_unaligned_le16(packet_size, &pkt_buf[CMD_INDEX_OFFSET]); | ||
542 | put_unaligned_le32(address, &pkt_buf[CMD_LENGTH_OFFSET]); | ||
543 | memcpy(&pkt_buf[CMD_DATA_OFFSET], data, packet_size); | ||
544 | |||
545 | error = wdt87xx_set_feature(client, pkt_buf, sizeof(pkt_buf)); | ||
546 | if (error) | ||
547 | return error; | ||
548 | |||
549 | length -= packet_size; | ||
550 | data += packet_size; | ||
551 | address += packet_size; | ||
552 | |||
553 | /* Wait for the controller to finish the write */ | ||
554 | mdelay(WDT_FLASH_WRITE_DELAY_MS); | ||
555 | |||
556 | if ((++count % 32) == 0) { | ||
557 | /* Delay for fw to clear watch dog */ | ||
558 | msleep(20); | ||
559 | } | ||
560 | } | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | static u16 misr(u16 cur_value, u8 new_value) | ||
566 | { | ||
567 | u32 a, b; | ||
568 | u32 bit0; | ||
569 | u32 y; | ||
570 | |||
571 | a = cur_value; | ||
572 | b = new_value; | ||
573 | bit0 = a ^ (b & 1); | ||
574 | bit0 ^= a >> 1; | ||
575 | bit0 ^= a >> 2; | ||
576 | bit0 ^= a >> 4; | ||
577 | bit0 ^= a >> 5; | ||
578 | bit0 ^= a >> 7; | ||
579 | bit0 ^= a >> 11; | ||
580 | bit0 ^= a >> 15; | ||
581 | y = (a << 1) ^ b; | ||
582 | y = (y & ~1) | (bit0 & 1); | ||
583 | |||
584 | return (u16)y; | ||
585 | } | ||
586 | |||
587 | static u16 wdt87xx_calculate_checksum(const u8 *data, size_t length) | ||
588 | { | ||
589 | u16 checksum = 0; | ||
590 | size_t i; | ||
591 | |||
592 | for (i = 0; i < length; i++) | ||
593 | checksum = misr(checksum, data[i]); | ||
594 | |||
595 | return checksum; | ||
596 | } | ||
597 | |||
598 | static int wdt87xx_get_checksum(struct i2c_client *client, u16 *checksum, | ||
599 | u32 address, int length) | ||
600 | { | ||
601 | int error; | ||
602 | int time_delay; | ||
603 | u8 pkt_buf[PKT_BUF_SIZE]; | ||
604 | u8 cmd_buf[CMD_BUF_SIZE]; | ||
605 | |||
606 | error = wdt87xx_send_command(client, VND_SET_CHECKSUM_LENGTH, length); | ||
607 | if (error) { | ||
608 | dev_err(&client->dev, "failed to set checksum length\n"); | ||
609 | return error; | ||
610 | } | ||
611 | |||
612 | error = wdt87xx_send_command(client, VND_SET_CHECKSUM_CALC, address); | ||
613 | if (error) { | ||
614 | dev_err(&client->dev, "failed to set checksum address\n"); | ||
615 | return error; | ||
616 | } | ||
617 | |||
618 | /* Wait the operation to complete */ | ||
619 | time_delay = DIV_ROUND_UP(length, 1024); | ||
620 | msleep(time_delay * 30); | ||
621 | |||
622 | memset(cmd_buf, 0, sizeof(cmd_buf)); | ||
623 | cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_READ; | ||
624 | cmd_buf[CMD_TYPE_OFFSET] = VND_GET_CHECKSUM; | ||
625 | error = wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf)); | ||
626 | if (error) { | ||
627 | dev_err(&client->dev, "failed to request checksum\n"); | ||
628 | return error; | ||
629 | } | ||
630 | |||
631 | memset(pkt_buf, 0, sizeof(pkt_buf)); | ||
632 | pkt_buf[CMD_REPORT_ID_OFFSET] = VND_READ_DATA; | ||
633 | error = wdt87xx_get_feature(client, pkt_buf, sizeof(pkt_buf)); | ||
634 | if (error) { | ||
635 | dev_err(&client->dev, "failed to read checksum\n"); | ||
636 | return error; | ||
637 | } | ||
638 | |||
639 | *checksum = get_unaligned_le16(&pkt_buf[CMD_DATA_OFFSET]); | ||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) | ||
644 | { | ||
645 | u32 start_addr = get_unaligned_le32(chunk + FW_CHUNK_TGT_START_OFFSET); | ||
646 | u32 size = get_unaligned_le32(chunk + FW_CHUNK_PAYLOAD_LEN_OFFSET); | ||
647 | const void *data = chunk + FW_CHUNK_PAYLOAD_OFFSET; | ||
648 | int error; | ||
649 | int err1; | ||
650 | int page_size; | ||
651 | int retry = 0; | ||
652 | u16 device_checksum, firmware_checksum; | ||
653 | |||
654 | dev_dbg(&client->dev, "start 4k page program\n"); | ||
655 | |||
656 | error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_STOP); | ||
657 | if (error) { | ||
658 | dev_err(&client->dev, "stop report mode failed\n"); | ||
659 | return error; | ||
660 | } | ||
661 | |||
662 | error = wdt87xx_send_command(client, VND_CMD_SFUNL, 0); | ||
663 | if (error) { | ||
664 | dev_err(&client->dev, "unlock failed\n"); | ||
665 | goto out_enable_reporting; | ||
666 | } | ||
667 | |||
668 | mdelay(10); | ||
669 | |||
670 | while (size) { | ||
671 | dev_dbg(&client->dev, "%s: %x, %x\n", __func__, | ||
672 | start_addr, size); | ||
673 | |||
674 | page_size = min_t(u32, size, PG_SIZE); | ||
675 | size -= page_size; | ||
676 | |||
677 | for (retry = 0; retry < MAX_RETRIES; retry++) { | ||
678 | error = wdt87xx_send_command(client, VND_CMD_ERASE, | ||
679 | start_addr); | ||
680 | if (error) { | ||
681 | dev_err(&client->dev, | ||
682 | "erase failed at %#08x\n", start_addr); | ||
683 | break; | ||
684 | } | ||
685 | |||
686 | msleep(50); | ||
687 | |||
688 | error = wdt87xx_write_data(client, data, start_addr, | ||
689 | page_size); | ||
690 | if (error) { | ||
691 | dev_err(&client->dev, | ||
692 | "write failed at %#08x (%d bytes)\n", | ||
693 | start_addr, page_size); | ||
694 | break; | ||
695 | } | ||
696 | |||
697 | error = wdt87xx_get_checksum(client, &device_checksum, | ||
698 | start_addr, page_size); | ||
699 | if (error) { | ||
700 | dev_err(&client->dev, | ||
701 | "failed to retrieve checksum for %#08x (len: %d)\n", | ||
702 | start_addr, page_size); | ||
703 | break; | ||
704 | } | ||
705 | |||
706 | firmware_checksum = | ||
707 | wdt87xx_calculate_checksum(data, page_size); | ||
708 | |||
709 | if (device_checksum == firmware_checksum) | ||
710 | break; | ||
711 | |||
712 | dev_err(&client->dev, | ||
713 | "checksum fail: %d vs %d, retry %d\n", | ||
714 | device_checksum, firmware_checksum, retry); | ||
715 | } | ||
716 | |||
717 | if (retry == MAX_RETRIES) { | ||
718 | dev_err(&client->dev, "page write failed\n"); | ||
719 | error = -EIO; | ||
720 | goto out_lock_device; | ||
721 | } | ||
722 | |||
723 | start_addr = start_addr + page_size; | ||
724 | data = data + page_size; | ||
725 | } | ||
726 | |||
727 | out_lock_device: | ||
728 | err1 = wdt87xx_send_command(client, VND_CMD_SFLCK, 0); | ||
729 | if (err1) | ||
730 | dev_err(&client->dev, "lock failed\n"); | ||
731 | |||
732 | mdelay(10); | ||
733 | |||
734 | out_enable_reporting: | ||
735 | err1 = wdt87xx_send_command(client, VND_CMD_START, 0); | ||
736 | if (err1) | ||
737 | dev_err(&client->dev, "start to report failed\n"); | ||
738 | |||
739 | return error ? error : err1; | ||
740 | } | ||
741 | |||
742 | static int wdt87xx_load_chunk(struct i2c_client *client, | ||
743 | const struct firmware *fw, u32 ck_id) | ||
744 | { | ||
745 | const void *chunk; | ||
746 | int error; | ||
747 | |||
748 | chunk = wdt87xx_get_fw_chunk(fw, ck_id); | ||
749 | if (!chunk) { | ||
750 | dev_err(&client->dev, "unable to locate chunk (type %d)\n", | ||
751 | ck_id); | ||
752 | return -EINVAL; | ||
753 | } | ||
754 | |||
755 | error = wdt87xx_validate_fw_chunk(chunk, ck_id); | ||
756 | if (error) { | ||
757 | dev_err(&client->dev, "invalid chunk (type %d): %d\n", | ||
758 | ck_id, error); | ||
759 | return error; | ||
760 | } | ||
761 | |||
762 | error = wdt87xx_write_firmware(client, chunk); | ||
763 | if (error) { | ||
764 | dev_err(&client->dev, | ||
765 | "failed to write fw chunk (type %d): %d\n", | ||
766 | ck_id, error); | ||
767 | return error; | ||
768 | } | ||
769 | |||
770 | return 0; | ||
771 | } | ||
772 | |||
773 | static int wdt87xx_do_update_firmware(struct i2c_client *client, | ||
774 | const struct firmware *fw, | ||
775 | unsigned int chunk_id) | ||
776 | { | ||
777 | struct wdt87xx_data *wdt = i2c_get_clientdata(client); | ||
778 | int error; | ||
779 | |||
780 | error = wdt87xx_validate_firmware(wdt, fw); | ||
781 | if (error) | ||
782 | return error; | ||
783 | |||
784 | error = mutex_lock_interruptible(&wdt->fw_mutex); | ||
785 | if (error) | ||
786 | return error; | ||
787 | |||
788 | disable_irq(client->irq); | ||
789 | |||
790 | error = wdt87xx_load_chunk(client, fw, chunk_id); | ||
791 | if (error) { | ||
792 | dev_err(&client->dev, | ||
793 | "firmware load failed (type: %d): %d\n", | ||
794 | chunk_id, error); | ||
795 | goto out; | ||
796 | } | ||
797 | |||
798 | error = wdt87xx_sw_reset(client); | ||
799 | if (error) { | ||
800 | dev_err(&client->dev, "soft reset failed: %d\n", error); | ||
801 | goto out; | ||
802 | } | ||
803 | |||
804 | /* Refresh the parameters */ | ||
805 | error = wdt87xx_get_sysparam(client, &wdt->param); | ||
806 | if (error) | ||
807 | dev_err(&client->dev, | ||
808 | "failed to refresh system paramaters: %d\n", error); | ||
809 | out: | ||
810 | enable_irq(client->irq); | ||
811 | mutex_unlock(&wdt->fw_mutex); | ||
812 | |||
813 | return error ? error : 0; | ||
814 | } | ||
815 | |||
816 | static int wdt87xx_update_firmware(struct device *dev, | ||
817 | const char *fw_name, unsigned int chunk_id) | ||
818 | { | ||
819 | struct i2c_client *client = to_i2c_client(dev); | ||
820 | const struct firmware *fw; | ||
821 | int error; | ||
822 | |||
823 | error = request_firmware(&fw, fw_name, dev); | ||
824 | if (error) { | ||
825 | dev_err(&client->dev, "unable to retrieve firmware %s: %d\n", | ||
826 | fw_name, error); | ||
827 | return error; | ||
828 | } | ||
829 | |||
830 | error = wdt87xx_do_update_firmware(client, fw, chunk_id); | ||
831 | |||
832 | release_firmware(fw); | ||
833 | |||
834 | return error ? error : 0; | ||
835 | } | ||
836 | |||
837 | static ssize_t config_csum_show(struct device *dev, | ||
838 | struct device_attribute *attr, char *buf) | ||
839 | { | ||
840 | struct i2c_client *client = to_i2c_client(dev); | ||
841 | struct wdt87xx_data *wdt = i2c_get_clientdata(client); | ||
842 | u32 cfg_csum; | ||
843 | |||
844 | cfg_csum = wdt->param.xmls_id1; | ||
845 | cfg_csum = (cfg_csum << 16) | wdt->param.xmls_id2; | ||
846 | |||
847 | return scnprintf(buf, PAGE_SIZE, "%x\n", cfg_csum); | ||
848 | } | ||
849 | |||
850 | static ssize_t fw_version_show(struct device *dev, | ||
851 | struct device_attribute *attr, char *buf) | ||
852 | { | ||
853 | struct i2c_client *client = to_i2c_client(dev); | ||
854 | struct wdt87xx_data *wdt = i2c_get_clientdata(client); | ||
855 | |||
856 | return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.fw_id); | ||
857 | } | ||
858 | |||
859 | static ssize_t plat_id_show(struct device *dev, | ||
860 | struct device_attribute *attr, char *buf) | ||
861 | { | ||
862 | struct i2c_client *client = to_i2c_client(dev); | ||
863 | struct wdt87xx_data *wdt = i2c_get_clientdata(client); | ||
864 | |||
865 | return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.plat_id); | ||
866 | } | ||
867 | |||
868 | static ssize_t update_config_store(struct device *dev, | ||
869 | struct device_attribute *attr, | ||
870 | const char *buf, size_t count) | ||
871 | { | ||
872 | int error; | ||
873 | |||
874 | error = wdt87xx_update_firmware(dev, WDT87XX_CFG_NAME, CHUNK_ID_CNFG); | ||
875 | |||
876 | return error ? error : count; | ||
877 | } | ||
878 | |||
879 | static ssize_t update_fw_store(struct device *dev, | ||
880 | struct device_attribute *attr, | ||
881 | const char *buf, size_t count) | ||
882 | { | ||
883 | int error; | ||
884 | |||
885 | error = wdt87xx_update_firmware(dev, WDT87XX_FW_NAME, CHUNK_ID_FRWR); | ||
886 | |||
887 | return error ? error : count; | ||
888 | } | ||
889 | |||
890 | static DEVICE_ATTR_RO(config_csum); | ||
891 | static DEVICE_ATTR_RO(fw_version); | ||
892 | static DEVICE_ATTR_RO(plat_id); | ||
893 | static DEVICE_ATTR_WO(update_config); | ||
894 | static DEVICE_ATTR_WO(update_fw); | ||
895 | |||
896 | static struct attribute *wdt87xx_attrs[] = { | ||
897 | &dev_attr_config_csum.attr, | ||
898 | &dev_attr_fw_version.attr, | ||
899 | &dev_attr_plat_id.attr, | ||
900 | &dev_attr_update_config.attr, | ||
901 | &dev_attr_update_fw.attr, | ||
902 | NULL | ||
903 | }; | ||
904 | |||
905 | static const struct attribute_group wdt87xx_attr_group = { | ||
906 | .attrs = wdt87xx_attrs, | ||
907 | }; | ||
908 | |||
909 | static void wdt87xx_report_contact(struct input_dev *input, | ||
910 | struct wdt87xx_sys_param *param, | ||
911 | u8 *buf) | ||
912 | { | ||
913 | int finger_id; | ||
914 | u32 x, y, w; | ||
915 | u8 p; | ||
916 | |||
917 | finger_id = (buf[FINGER_EV_V1_OFFSET_ID] >> 3) - 1; | ||
918 | if (finger_id < 0) | ||
919 | return; | ||
920 | |||
921 | /* Check if this is an active contact */ | ||
922 | if (!(buf[FINGER_EV_V1_OFFSET_ID] & 0x1)) | ||
923 | return; | ||
924 | |||
925 | w = buf[FINGER_EV_V1_OFFSET_W]; | ||
926 | w *= param->scaling_factor; | ||
927 | |||
928 | p = buf[FINGER_EV_V1_OFFSET_P]; | ||
929 | |||
930 | x = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_X); | ||
931 | |||
932 | y = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_Y); | ||
933 | y = DIV_ROUND_CLOSEST(y * param->phy_h, param->phy_w); | ||
934 | |||
935 | /* Refuse incorrect coordinates */ | ||
936 | if (x > param->max_x || y > param->max_y) | ||
937 | return; | ||
938 | |||
939 | dev_dbg(input->dev.parent, "tip on (%d), x(%d), y(%d)\n", | ||
940 | finger_id, x, y); | ||
941 | |||
942 | input_mt_slot(input, finger_id); | ||
943 | input_mt_report_slot_state(input, MT_TOOL_FINGER, 1); | ||
944 | input_report_abs(input, ABS_MT_TOUCH_MAJOR, w); | ||
945 | input_report_abs(input, ABS_MT_PRESSURE, p); | ||
946 | input_report_abs(input, ABS_MT_POSITION_X, x); | ||
947 | input_report_abs(input, ABS_MT_POSITION_Y, y); | ||
948 | } | ||
949 | |||
950 | static irqreturn_t wdt87xx_ts_interrupt(int irq, void *dev_id) | ||
951 | { | ||
952 | struct wdt87xx_data *wdt = dev_id; | ||
953 | struct i2c_client *client = wdt->client; | ||
954 | int i, fingers; | ||
955 | int error; | ||
956 | u8 raw_buf[WDT_V1_RAW_BUF_COUNT] = {0}; | ||
957 | |||
958 | error = i2c_master_recv(client, raw_buf, WDT_V1_RAW_BUF_COUNT); | ||
959 | if (error < 0) { | ||
960 | dev_err(&client->dev, "read v1 raw data failed: %d\n", error); | ||
961 | goto irq_exit; | ||
962 | } | ||
963 | |||
964 | fingers = raw_buf[TOUCH_PK_V1_OFFSET_FNGR_NUM]; | ||
965 | if (!fingers) | ||
966 | goto irq_exit; | ||
967 | |||
968 | for (i = 0; i < WDT_MAX_FINGER; i++) | ||
969 | wdt87xx_report_contact(wdt->input, | ||
970 | &wdt->param, | ||
971 | &raw_buf[TOUCH_PK_V1_OFFSET_EVENT + | ||
972 | i * FINGER_EV_V1_SIZE]); | ||
973 | |||
974 | input_mt_sync_frame(wdt->input); | ||
975 | input_sync(wdt->input); | ||
976 | |||
977 | irq_exit: | ||
978 | return IRQ_HANDLED; | ||
979 | } | ||
980 | |||
981 | static int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt) | ||
982 | { | ||
983 | struct device *dev = &wdt->client->dev; | ||
984 | struct input_dev *input; | ||
985 | unsigned int res = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS, wdt->param.phy_w); | ||
986 | int error; | ||
987 | |||
988 | input = devm_input_allocate_device(dev); | ||
989 | if (!input) { | ||
990 | dev_err(dev, "failed to allocate input device\n"); | ||
991 | return -ENOMEM; | ||
992 | } | ||
993 | wdt->input = input; | ||
994 | |||
995 | input->name = "WDT87xx Touchscreen"; | ||
996 | input->id.bustype = BUS_I2C; | ||
997 | input->phys = wdt->phys; | ||
998 | |||
999 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, | ||
1000 | wdt->param.max_x, 0, 0); | ||
1001 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, | ||
1002 | wdt->param.max_y, 0, 0); | ||
1003 | input_abs_set_res(input, ABS_MT_POSITION_X, res); | ||
1004 | input_abs_set_res(input, ABS_MT_POSITION_Y, res); | ||
1005 | |||
1006 | input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, | ||
1007 | 0, wdt->param.max_x, 0, 0); | ||
1008 | input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); | ||
1009 | |||
1010 | input_mt_init_slots(input, WDT_MAX_FINGER, | ||
1011 | INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); | ||
1012 | |||
1013 | error = input_register_device(input); | ||
1014 | if (error) { | ||
1015 | dev_err(dev, "failed to register input device: %d\n", error); | ||
1016 | return error; | ||
1017 | } | ||
1018 | |||
1019 | return 0; | ||
1020 | } | ||
1021 | |||
1022 | static int wdt87xx_ts_probe(struct i2c_client *client, | ||
1023 | const struct i2c_device_id *id) | ||
1024 | { | ||
1025 | struct wdt87xx_data *wdt; | ||
1026 | int error; | ||
1027 | |||
1028 | dev_dbg(&client->dev, "adapter=%d, client irq: %d\n", | ||
1029 | client->adapter->nr, client->irq); | ||
1030 | |||
1031 | /* Check if the I2C function is ok in this adaptor */ | ||
1032 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | ||
1033 | return -ENXIO; | ||
1034 | |||
1035 | wdt = devm_kzalloc(&client->dev, sizeof(*wdt), GFP_KERNEL); | ||
1036 | if (!wdt) | ||
1037 | return -ENOMEM; | ||
1038 | |||
1039 | wdt->client = client; | ||
1040 | mutex_init(&wdt->fw_mutex); | ||
1041 | i2c_set_clientdata(client, wdt); | ||
1042 | |||
1043 | snprintf(wdt->phys, sizeof(wdt->phys), "i2c-%u-%04x/input0", | ||
1044 | client->adapter->nr, client->addr); | ||
1045 | |||
1046 | error = wdt87xx_get_sysparam(client, &wdt->param); | ||
1047 | if (error) | ||
1048 | return error; | ||
1049 | |||
1050 | error = wdt87xx_ts_create_input_device(wdt); | ||
1051 | if (error) | ||
1052 | return error; | ||
1053 | |||
1054 | error = devm_request_threaded_irq(&client->dev, client->irq, | ||
1055 | NULL, wdt87xx_ts_interrupt, | ||
1056 | IRQF_ONESHOT, | ||
1057 | client->name, wdt); | ||
1058 | if (error) { | ||
1059 | dev_err(&client->dev, "request irq failed: %d\n", error); | ||
1060 | return error; | ||
1061 | } | ||
1062 | |||
1063 | error = sysfs_create_group(&client->dev.kobj, &wdt87xx_attr_group); | ||
1064 | if (error) { | ||
1065 | dev_err(&client->dev, "create sysfs failed: %d\n", error); | ||
1066 | return error; | ||
1067 | } | ||
1068 | |||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | static int wdt87xx_ts_remove(struct i2c_client *client) | ||
1073 | { | ||
1074 | sysfs_remove_group(&client->dev.kobj, &wdt87xx_attr_group); | ||
1075 | |||
1076 | return 0; | ||
1077 | } | ||
1078 | |||
1079 | static int __maybe_unused wdt87xx_suspend(struct device *dev) | ||
1080 | { | ||
1081 | struct i2c_client *client = to_i2c_client(dev); | ||
1082 | int error; | ||
1083 | |||
1084 | disable_irq(client->irq); | ||
1085 | |||
1086 | error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_IDLE); | ||
1087 | if (error) { | ||
1088 | enable_irq(client->irq); | ||
1089 | dev_err(&client->dev, | ||
1090 | "failed to stop device when suspending: %d\n", | ||
1091 | error); | ||
1092 | return error; | ||
1093 | } | ||
1094 | |||
1095 | return 0; | ||
1096 | } | ||
1097 | |||
1098 | static int __maybe_unused wdt87xx_resume(struct device *dev) | ||
1099 | { | ||
1100 | struct i2c_client *client = to_i2c_client(dev); | ||
1101 | int error; | ||
1102 | |||
1103 | /* | ||
1104 | * The chip may have been reset while system is resuming, | ||
1105 | * give it some time to settle. | ||
1106 | */ | ||
1107 | mdelay(100); | ||
1108 | |||
1109 | error = wdt87xx_send_command(client, VND_CMD_START, 0); | ||
1110 | if (error) | ||
1111 | dev_err(&client->dev, | ||
1112 | "failed to start device when resuming: %d\n", | ||
1113 | error); | ||
1114 | |||
1115 | enable_irq(client->irq); | ||
1116 | |||
1117 | return 0; | ||
1118 | } | ||
1119 | |||
1120 | static SIMPLE_DEV_PM_OPS(wdt87xx_pm_ops, wdt87xx_suspend, wdt87xx_resume); | ||
1121 | |||
1122 | static const struct i2c_device_id wdt87xx_dev_id[] = { | ||
1123 | { WDT87XX_NAME, 0 }, | ||
1124 | { } | ||
1125 | }; | ||
1126 | MODULE_DEVICE_TABLE(i2c, wdt87xx_dev_id); | ||
1127 | |||
1128 | static const struct acpi_device_id wdt87xx_acpi_id[] = { | ||
1129 | { "WDHT0001", 0 }, | ||
1130 | { } | ||
1131 | }; | ||
1132 | MODULE_DEVICE_TABLE(acpi, wdt87xx_acpi_id); | ||
1133 | |||
1134 | static struct i2c_driver wdt87xx_driver = { | ||
1135 | .probe = wdt87xx_ts_probe, | ||
1136 | .remove = wdt87xx_ts_remove, | ||
1137 | .id_table = wdt87xx_dev_id, | ||
1138 | .driver = { | ||
1139 | .name = WDT87XX_NAME, | ||
1140 | .pm = &wdt87xx_pm_ops, | ||
1141 | .acpi_match_table = ACPI_PTR(wdt87xx_acpi_id), | ||
1142 | }, | ||
1143 | }; | ||
1144 | module_i2c_driver(wdt87xx_driver); | ||
1145 | |||
1146 | MODULE_AUTHOR("HN Chen <hn.chen@weidahitech.com>"); | ||
1147 | MODULE_DESCRIPTION("WeidaHiTech WDT87XX Touchscreen driver"); | ||
1148 | MODULE_VERSION(WDT87XX_DRV_VER); | ||
1149 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/input/touchscreen.h b/include/linux/input/touchscreen.h index 08a5ef6e8f25..eecc9ea6cd58 100644 --- a/include/linux/input/touchscreen.h +++ b/include/linux/input/touchscreen.h | |||
@@ -12,9 +12,10 @@ | |||
12 | #include <linux/input.h> | 12 | #include <linux/input.h> |
13 | 13 | ||
14 | #ifdef CONFIG_OF | 14 | #ifdef CONFIG_OF |
15 | void touchscreen_parse_of_params(struct input_dev *dev); | 15 | void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch); |
16 | #else | 16 | #else |
17 | static inline void touchscreen_parse_of_params(struct input_dev *dev) | 17 | static inline void touchscreen_parse_of_params(struct input_dev *dev, |
18 | bool multitouch) | ||
18 | { | 19 | { |
19 | } | 20 | } |
20 | #endif | 21 | #endif |