diff options
Diffstat (limited to 'drivers/input')
63 files changed, 2690 insertions, 443 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a11ff74a5..a35532ec0 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,19 @@ config INPUT if INPUT +config INPUT_LEDS + tristate "Export input device LEDs in sysfs" + depends on LEDS_CLASS + default INPUT + help + Say Y here if you would like to export LEDs on input devices + as standard LED class devices in sysfs. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called input-leds. + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 5ca3f6314..0c9302ca9 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o +obj-$(CONFIG_INPUT_LEDS) += input-leds.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a18f41b89..9d35499fa 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -422,10 +422,7 @@ static int evdev_release(struct inode *inode, struct file *file) evdev_detach_client(evdev, client); - if (is_vmalloc_addr(client)) - vfree(client); - else - kfree(client); + kvfree(client); evdev_close_device(evdev); diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index b81c88c43..8f4a30fcc 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -70,7 +70,7 @@ static int compat_effect(struct ff_device *ff, struct ff_effect *effect) return -EINVAL; /* - * calculate manginude of sine wave as average of rumble's + * calculate magnitude of sine wave as average of rumble's * 2/3 of strong magnitude and 1/3 of weak magnitude */ magnitude = effect->u.rumble.strong_magnitude / 3 + @@ -213,7 +213,7 @@ static int erase_effect(struct input_dev *dev, int effect_id, /** * input_ff_erase - erase a force-feedback effect from device * @dev: input device to erase effect from - * @effect_id: id of the ffect to be erased + * @effect_id: id of the effect to be erased * @file: purported owner of the request * * This function erases a force-feedback effect from specified device. diff --git a/drivers/input/input-leds.c b/drivers/input/input-leds.c new file mode 100644 index 000000000..766bf2660 --- /dev/null +++ b/drivers/input/input-leds.c @@ -0,0 +1,224 @@ +/* + * LED support for the input layer + * + * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/leds.h> +#include <linux/input.h> + +#if IS_ENABLED(CONFIG_VT) +#define VT_TRIGGER(_name) .trigger = _name +#else +#define VT_TRIGGER(_name) .trigger = NULL +#endif + +static const struct { + const char *name; + const char *trigger; +} input_led_info[LED_CNT] = { + [LED_NUML] = { "numlock", VT_TRIGGER("kbd-numlock") }, + [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") }, + [LED_SCROLLL] = { "scrolllock", VT_TRIGGER("kbd-scrolllock") }, + [LED_COMPOSE] = { "compose" }, + [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") }, + [LED_SLEEP] = { "sleep" } , + [LED_SUSPEND] = { "suspend" }, + [LED_MUTE] = { "mute" }, + [LED_MISC] = { "misc" }, + [LED_MAIL] = { "mail" }, + [LED_CHARGING] = { "charging" }, +}; + +struct input_led { + struct led_classdev cdev; + struct input_handle *handle; + unsigned int code; /* One of LED_* constants */ +}; + +struct input_leds { + struct input_handle handle; + unsigned int num_leds; + struct input_led leds[]; +}; + +static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + struct input_dev *input = led->handle->dev; + + return test_bit(led->code, input->led) ? cdev->max_brightness : 0; +} + +static void input_leds_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct input_led *led = container_of(cdev, struct input_led, cdev); + + input_inject_event(led->handle, EV_LED, led->code, !!brightness); +} + +static void input_leds_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ +} + +static int input_leds_get_count(struct input_dev *dev) +{ + unsigned int led_code; + int count = 0; + + for_each_set_bit(led_code, dev->ledbit, LED_CNT) + if (input_led_info[led_code].name) + count++; + + return count; +} + +static int input_leds_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_leds *leds; + unsigned int num_leds; + unsigned int led_code; + int led_no; + int error; + + num_leds = input_leds_get_count(dev); + if (!num_leds) + return -ENXIO; + + leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds), + GFP_KERNEL); + if (!leds) + return -ENOMEM; + + leds->num_leds = num_leds; + + leds->handle.dev = dev; + leds->handle.handler = handler; + leds->handle.name = "leds"; + leds->handle.private = leds; + + error = input_register_handle(&leds->handle); + if (error) + goto err_free_mem; + + error = input_open_device(&leds->handle); + if (error) + goto err_unregister_handle; + + led_no = 0; + for_each_set_bit(led_code, dev->ledbit, LED_CNT) { + struct input_led *led = &leds->leds[led_no]; + + led->handle = &leds->handle; + led->code = led_code; + + if (!input_led_info[led_code].name) + continue; + + led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s", + dev_name(&dev->dev), + input_led_info[led_code].name); + if (!led->cdev.name) { + error = -ENOMEM; + goto err_unregister_leds; + } + + led->cdev.max_brightness = 1; + led->cdev.brightness_get = input_leds_brightness_get; + led->cdev.brightness_set = input_leds_brightness_set; + led->cdev.default_trigger = input_led_info[led_code].trigger; + + error = led_classdev_register(&dev->dev, &led->cdev); + if (error) { + dev_err(&dev->dev, "failed to register LED %s: %d\n", + led->cdev.name, error); + kfree(led->cdev.name); + goto err_unregister_leds; + } + + led_no++; + } + + return 0; + +err_unregister_leds: + while (--led_no >= 0) { + struct input_led *led = &leds->leds[led_no]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(&leds->handle); + +err_unregister_handle: + input_unregister_handle(&leds->handle); + +err_free_mem: + kfree(leds); + return error; +} + +static void input_leds_disconnect(struct input_handle *handle) +{ + struct input_leds *leds = handle->private; + int i; + + for (i = 0; i < leds->num_leds; i++) { + struct input_led *led = &leds->leds[i]; + + led_classdev_unregister(&led->cdev); + kfree(led->cdev.name); + } + + input_close_device(handle); + input_unregister_handle(handle); + + kfree(leds); +} + +static const struct input_device_id input_leds_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_LED) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, input_leds_ids); + +static struct input_handler input_leds_handler = { + .event = input_leds_event, + .connect = input_leds_connect, + .disconnect = input_leds_disconnect, + .name = "leds", + .id_table = input_leds_ids, +}; + +static int __init input_leds_init(void) +{ + return input_register_handler(&input_leds_handler); +} +module_init(input_leds_init); + +static void __exit input_leds_exit(void) +{ + input_unregister_handler(&input_leds_handler); +} +module_exit(input_leds_exit); + +MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>"); +MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); +MODULE_DESCRIPTION("Input -> LEDs Bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/input.c b/drivers/input/input.c index cc357f151..78d24990a 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) int code; if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { - for (code = 0; code <= KEY_MAX; code++) { - if (is_event_supported(code, dev->keybit, KEY_MAX) && - __test_and_clear_bit(code, dev->key)) { - input_pass_event(dev, EV_KEY, code, 0); - } - } + for_each_set_bit(code, dev->key, KEY_CNT) + input_pass_event(dev, EV_KEY, code, 0); + memset(dev->key, 0, sizeof(dev->key)); input_pass_event(dev, EV_SYN, SYN_REPORT, 1); } } @@ -1626,10 +1623,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) if (!test_bit(EV_##type, dev->evbit)) \ break; \ \ - for (i = 0; i < type##_MAX; i++) { \ - if (!test_bit(i, dev->bits##bit)) \ - continue; \ - \ + for_each_set_bit(i, dev->bits##bit, type##_CNT) { \ active = test_bit(i, dev->bits); \ if (!active && !on) \ continue; \ @@ -1980,22 +1974,12 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev) events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */ - if (test_bit(EV_ABS, dev->evbit)) { - for (i = 0; i < ABS_CNT; i++) { - if (test_bit(i, dev->absbit)) { - if (input_is_mt_axis(i)) - events += mt_slots; - else - events++; - } - } - } + if (test_bit(EV_ABS, dev->evbit)) + for_each_set_bit(i, dev->absbit, ABS_CNT) + events += input_is_mt_axis(i) ? mt_slots : 1; - if (test_bit(EV_REL, dev->evbit)) { - for (i = 0; i < REL_CNT; i++) - if (test_bit(i, dev->relbit)) - events++; - } + if (test_bit(EV_REL, dev->evbit)) + events += bitmap_weight(dev->relbit, REL_CNT); /* Make room for KEY and MSC events */ events += 7; @@ -2262,7 +2246,7 @@ EXPORT_SYMBOL(input_unregister_handler); * * Iterate over @bus's list of devices, and call @fn for each, passing * it @data and stop when @fn returns a non-zero value. The function is - * using RCU to traverse the list and therefore may be usind in atonic + * using RCU to traverse the list and therefore may be using in atomic * contexts. The @fn callback is invoked from RCU critical section and * thus must not sleep. */ diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index 27b6a3ce1..891797ad7 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -196,7 +196,7 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs) if (n_buttons[i] < 1) continue; - if (n_buttons[i] > 6) { + if (n_buttons[i] > ARRAY_SIZE(tgfx_buttons)) { printk(KERN_ERR "turbografx.c: Invalid number of buttons %d\n", n_buttons[i]); err = -EINVAL; goto err_unreg_devs; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 61c761156..f8850f9cb 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -344,6 +344,7 @@ struct usb_xpad { int mapping; /* map d-pad to buttons or to axes */ int xtype; /* type of xbox device */ + unsigned long led_no; /* led to lit on xbox360 controllers */ }; /* @@ -488,6 +489,8 @@ static void xpad360_process_packet(struct usb_xpad *xpad, input_sync(dev); } +static void xpad_identify_controller(struct usb_xpad *xpad); + /* * xpad360w_process_packet * @@ -510,6 +513,11 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha if (data[1] & 0x80) { xpad->pad_present = 1; usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); + /* + * Light up the segment corresponding to + * controller number. + */ + xpad_identify_controller(xpad); } else xpad->pad_present = 0; } @@ -881,17 +889,63 @@ struct xpad_led { struct usb_xpad *xpad; }; +/** + * @param command + * 0: off + * 1: all blink, then previous setting + * 2: 1/top-left blink, then on + * 3: 2/top-right blink, then on + * 4: 3/bottom-left blink, then on + * 5: 4/bottom-right blink, then on + * 6: 1/top-left on + * 7: 2/top-right on + * 8: 3/bottom-left on + * 9: 4/bottom-right on + * 10: rotate + * 11: blink, based on previous setting + * 12: slow blink, based on previous setting + * 13: rotate with two lights + * 14: persistent slow all blink + * 15: blink once, then previous setting + */ static void xpad_send_led_command(struct usb_xpad *xpad, int command) { - if (command >= 0 && command < 14) { - mutex_lock(&xpad->odata_mutex); + command %= 16; + + mutex_lock(&xpad->odata_mutex); + + switch (xpad->xtype) { + case XTYPE_XBOX360: xpad->odata[0] = 0x01; xpad->odata[1] = 0x03; xpad->odata[2] = command; xpad->irq_out->transfer_buffer_length = 3; - usb_submit_urb(xpad->irq_out, GFP_KERNEL); - mutex_unlock(&xpad->odata_mutex); + break; + case XTYPE_XBOX360W: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x00; + xpad->odata[2] = 0x08; + xpad->odata[3] = 0x40 + command; + xpad->odata[4] = 0x00; + xpad->odata[5] = 0x00; + xpad->odata[6] = 0x00; + xpad->odata[7] = 0x00; + xpad->odata[8] = 0x00; + xpad->odata[9] = 0x00; + xpad->odata[10] = 0x00; + xpad->odata[11] = 0x00; + xpad->irq_out->transfer_buffer_length = 12; + break; } + + usb_submit_urb(xpad->irq_out, GFP_KERNEL); + mutex_unlock(&xpad->odata_mutex); +} + +static void xpad_identify_controller(struct usb_xpad *xpad) +{ + /* Light up the segment corresponding to controller number */ + xpad_send_led_command(xpad, (xpad->led_no % 4) + 2); } static void xpad_led_set(struct led_classdev *led_cdev, @@ -905,22 +959,21 @@ static void xpad_led_set(struct led_classdev *led_cdev, static int xpad_led_probe(struct usb_xpad *xpad) { - static atomic_t led_seq = ATOMIC_INIT(-1); - unsigned long led_no; + static atomic_t led_seq = ATOMIC_INIT(-1); struct xpad_led *led; struct led_classdev *led_cdev; int error; - if (xpad->xtype != XTYPE_XBOX360) + if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W) return 0; xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); if (!led) return -ENOMEM; - led_no = atomic_inc_return(&led_seq); + xpad->led_no = atomic_inc_return(&led_seq); - snprintf(led->name, sizeof(led->name), "xpad%lu", led_no); + snprintf(led->name, sizeof(led->name), "xpad%lu", xpad->led_no); led->xpad = xpad; led_cdev = &led->led_cdev; @@ -934,10 +987,8 @@ static int xpad_led_probe(struct usb_xpad *xpad) return error; } - /* - * Light up the segment corresponding to controller number - */ - xpad_send_led_command(xpad, (led_no % 4) + 2); + /* Light up the segment corresponding to controller number */ + xpad_identify_controller(xpad); return 0; } @@ -954,6 +1005,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) #else static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } static void xpad_led_disconnect(struct usb_xpad *xpad) { } +static void xpad_identify_controller(struct usb_xpad *xpad) { } #endif diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 106fbac7f..4cd94fd6c 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -367,6 +367,7 @@ config KEYBOARD_MAPLE config KEYBOARD_MAX7359 tristate "Maxim MAX7359 Key Switch Controller" + select INPUT_MATRIXKMAP depends on I2C help If you say yes here you get support for the Maxim MAX7359 Key @@ -677,7 +678,7 @@ config KEYBOARD_W90P910 config KEYBOARD_CROS_EC tristate "ChromeOS EC keyboard" select INPUT_MATRIXKMAP - depends on MFD_CROS_EC + depends on CROS_EC_PROTO help Say Y here to enable the matrix keyboard used by ChromeOS devices and implemented on the ChromeOS EC. You must enable one bus option diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index a45267729..6ed83cf8b 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -180,7 +180,7 @@ #define LOGIC2_STAT (1 << 7) /* ADP5589 only */ #define LOGIC1_STAT (1 << 6) #define LOCK_STAT (1 << 5) /* ADP5589 only */ -#define KEC 0xF +#define KEC 0x1F /* PIN_CONFIG_D Register */ #define C4_EXTEND_CFG (1 << 6) /* RESET2 */ @@ -726,7 +726,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_row_num) { + if (i % 4 == 3 || i == kpad->var->max_row_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_A) + (i >> 2), pull_mask); pull_mask = 0; @@ -746,7 +746,7 @@ static int adp5589_setup(struct adp5589_kpad *kpad) pull_mask |= val << (2 * (i & 0x3)); - if (i == 3 || i == kpad->var->max_col_num) { + if (i % 4 == 3 || i == kpad->var->max_col_num) { ret |= adp5589_write(client, reg(ADP5585_RPULL_CONFIG_C) + (i >> 2), pull_mask); diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index 27ef29f8f..b637f1af8 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -120,14 +120,9 @@ static int clps711x_keypad_probe(struct platform_device *pdev) for (i = 0; i < priv->row_count; i++) { struct clps711x_gpio_data *data = &priv->gpio_data[i]; - data->desc = devm_gpiod_get_index(dev, "row", i); - if (!data->desc) - return -EINVAL; - + data->desc = devm_gpiod_get_index(dev, "row", i, GPIOD_IN); if (IS_ERR(data->desc)) return PTR_ERR(data->desc); - - gpiod_direction_input(data->desc); } err = of_property_read_u32(np, "poll-interval", &poll_interval); diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index b50c5b8b8..b01966dc7 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -148,19 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { - int ret; - struct cros_ec_command msg = { - .command = EC_CMD_MKBP_STATE, - .insize = ckdev->cols, - }; + int ret = 0; + struct cros_ec_command *msg; - ret = cros_ec_cmd_xfer(ckdev->ec, &msg); - if (ret < 0) - return ret; + msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL); + if (!msg) + return -ENOMEM; - memcpy(kb_state, msg.indata, ckdev->cols); + msg->version = 0; + msg->command = EC_CMD_MKBP_STATE; + msg->insize = ckdev->cols; + msg->outsize = 0; - return 0; + ret = cros_ec_cmd_xfer(ckdev->ec, msg); + if (ret < 0) { + dev_err(ckdev->dev, "Error transferring EC message %d\n", ret); + goto exit; + } + + memcpy(kb_state, msg->data, ckdev->cols); +exit: + kfree(msg); + return ret; } static irqreturn_t cros_ec_keyb_irq(int irq, void *data) @@ -266,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) ckdev->dev = dev; dev_set_drvdata(&pdev->dev, ckdev); - idev->name = ec->ec_name; + idev->name = CROS_EC_DEV_NAME; idev->phys = ec->phys_name; __set_bit(EV_REP, idev->evbit); diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 097d7216d..c6dc644aa 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -246,7 +246,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) * convert it to descriptor. */ if (!button->gpiod && gpio_is_valid(button->gpio)) { - unsigned flags = 0; + unsigned flags = GPIOF_IN; if (button->active_low) flags |= GPIOF_ACTIVE_LOW; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 2e855e6f3..d2ea863d6 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) input_set_drvdata(input_dev, keypad); /* Ensure that the keypad will stay dormant until opened */ - clk_prepare_enable(keypad->clk); + error = clk_prepare_enable(keypad->clk); + if (error) + return error; imx_keypad_inhibit(keypad); clk_disable_unprepare(keypad->clk); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index faa6da53e..5091133b7 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -84,26 +84,6 @@ static int max7359_read_reg(struct i2c_client *client, int reg) return ret; } -static void max7359_build_keycode(struct max7359_keypad *keypad, - const struct matrix_keymap_data *keymap_data) -{ - struct input_dev *input_dev = keypad->input_dev; - int i; - - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned int scancode = MATRIX_SCAN_CODE(row, col, - MAX7359_ROW_SHIFT); - unsigned short keycode = KEY_VAL(key); - - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); - } - __clear_bit(KEY_RESERVED, input_dev->keybit); -} - /* runs in an IRQ thread -- can (and will!) sleep */ static irqreturn_t max7359_interrupt(int irq, void *dev_id) { @@ -166,7 +146,6 @@ static void max7359_close(struct input_dev *dev) static void max7359_initialize(struct i2c_client *client) { max7359_write_reg(client, MAX7359_REG_CONFIG, - MAX7359_CFG_INTERRUPT | /* Irq clears after host read */ MAX7359_CFG_KEY_RELEASE | /* Key release enable */ MAX7359_CFG_WAKEUP); /* Key press wakeup enable */ @@ -233,7 +212,15 @@ static int max7359_probe(struct i2c_client *client, input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); - max7359_build_keycode(keypad, keymap_data); + error = matrix_keypad_build_keymap(keymap_data, NULL, + MAX7359_MAX_KEY_ROWS, + MAX7359_MAX_KEY_COLS, + keypad->keycodes, + input_dev); + if (error) { + dev_err(&client->dev, "failed to build keymap\n"); + return error; + } error = devm_request_threaded_irq(&client->dev, client->irq, NULL, max7359_interrupt, diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 6b9fdf6cf..43e48dac7 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -585,7 +585,7 @@ static const struct of_device_id samsung_keypad_dt_match[] = { MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match); #endif -static struct platform_device_id samsung_keypad_driver_ids[] = { +static const struct platform_device_id samsung_keypad_driver_ids[] = { { .name = "samsung-keypad", .driver_data = KEYPAD_TYPE_SAMSUNG, diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index f42a543db..623d45176 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -3,7 +3,7 @@ * Based on omap-keypad driver * * Copyright (C) 2010 ST Microelectronics - * Rajeev Kumar<rajeev-dlh.kumar@st.com> + * Rajeev Kumar <rajeevkumar.linux@gmail.com> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 4436ab1b9..d4f0a817e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -610,6 +610,16 @@ config INPUT_DA9055_ONKEY To compile this driver as a module, choose M here: the module will be called da9055_onkey. +config INPUT_DA9063_ONKEY + tristate "Dialog DA9063 OnKey" + depends on MFD_DA9063 + help + Support the ONKEY of Dialog DA9063 Power Management IC as an + input device reporting power button statue. + + To compile this driver as a module, choose M here: the module + will be called da9063_onkey. + config INPUT_DM355EVM tristate "TI DaVinci DM355 EVM Keypad and IR Remote" depends on MFD_DM355EVM_MSP @@ -775,6 +785,17 @@ config INPUT_DRV260X_HAPTICS To compile this driver as a module, choose M here: the module will be called drv260x-haptics. +config INPUT_DRV2665_HAPTICS + tristate "TI DRV2665 haptics support" + depends on INPUT && I2C + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y to enable support for the TI DRV2665 haptics driver. + + To compile this driver as a module, choose M here: the + module will be called drv2665-haptics. + config INPUT_DRV2667_HAPTICS tristate "TI DRV2667 haptics support" depends on INPUT && I2C @@ -784,6 +805,6 @@ config INPUT_DRV2667_HAPTICS Say Y to enable support for the TI DRV2667 haptics driver. To compile this driver as a module, choose M here: the - module will be called drv260x-haptics. + module will be called drv2667-haptics. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 78ba4c1b8..53df07dcc 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -25,9 +25,11 @@ obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o +obj-$(CONFIG_INPUT_DA9063_ONKEY) += da9063_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o +obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index 470bfd6f0..bdb5d03b2 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -10,6 +10,7 @@ #include <linux/input.h> /* BUS_I2C */ #include <linux/i2c.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/types.h> #include <linux/pm.h> #include "adxl34x.h" @@ -135,11 +136,31 @@ static const struct i2c_device_id adxl34x_id[] = { MODULE_DEVICE_TABLE(i2c, adxl34x_id); +#ifdef CONFIG_OF +static const struct of_device_id adxl34x_of_id[] = { + /* + * The ADXL346 is backward-compatible with the ADXL345. Differences are + * handled by runtime detection of the device model, there's thus no + * need for listing the "adi,adxl346" compatible value explicitly. + */ + { .compatible = "adi,adxl345", }, + /* + * Deprecated, DT nodes should use one or more of the device-specific + * compatible values "adi,adxl345" and "adi,adxl346". + */ + { .compatible = "adi,adxl34x", }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl34x_of_id); +#endif + static struct i2c_driver adxl34x_driver = { .driver = { .name = "adxl34x", .owner = THIS_MODULE, .pm = &adxl34x_i2c_pm, + .of_match_table = of_match_ptr(adxl34x_of_id), }, .probe = adxl34x_i2c_probe, .remove = adxl34x_i2c_remove, diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index f63341f20..cfd58e87d 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -94,7 +94,7 @@ static int ati_remote2_get_mode_mask(char *buffer, static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; #define param_check_channel_mask(name, p) __param_check(name, p, unsigned int) -static struct kernel_param_ops param_ops_channel_mask = { +static const struct kernel_param_ops param_ops_channel_mask = { .set = ati_remote2_set_channel_mask, .get = ati_remote2_get_channel_mask, }; @@ -103,7 +103,7 @@ MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...< static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK; #define param_check_mode_mask(name, p) __param_check(name, p, unsigned int) -static struct kernel_param_ops param_ops_mode_mask = { +static const struct kernel_param_ops param_ops_mode_mask = { .set = ati_remote2_set_mode_mask, .get = ati_remote2_get_mode_mask, }; diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index f1c844739..1ac898db3 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) struct input_dev *idev = pwr; struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); - if (irq == axp20x_pek->irq_dbr) + /* + * The power-button is connected to ground so a falling edge (dbf) + * means it is pressed. + */ + if (irq == axp20x_pek->irq_dbf) input_report_key(idev, KEY_POWER, true); - else if (irq == axp20x_pek->irq_dbf) + else if (irq == axp20x_pek->irq_dbr) input_report_key(idev, KEY_POWER, false); input_sync(idev); @@ -288,3 +292,4 @@ module_platform_driver(axp20x_pek_driver); MODULE_DESCRIPTION("axp20x Power Button"); MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:axp20x-pek"); diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c new file mode 100644 index 000000000..f577585ef --- /dev/null +++ b/drivers/input/misc/da9063_onkey.c @@ -0,0 +1,226 @@ +/* + * OnKey device driver for DA9063 + * Copyright (C) 2015 Dialog Semiconductor Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <linux/mfd/da9063/core.h> +#include <linux/mfd/da9063/pdata.h> +#include <linux/mfd/da9063/registers.h> + +struct da9063_onkey { + struct da9063 *hw; + struct delayed_work work; + struct input_dev *input; + struct device *dev; + bool key_power; +}; + +static void da9063_poll_on(struct work_struct *work) +{ + struct da9063_onkey *onkey = container_of(work, struct da9063_onkey, + work.work); + unsigned int val; + int fault_log = 0; + bool poll = true; + int error; + + /* Poll to see when the pin is released */ + error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); + if (error) { + dev_err(onkey->dev, + "Failed to read ON status: %d\n", error); + goto err_poll; + } + + if (!(val & DA9063_NONKEY)) { + error = regmap_update_bits(onkey->hw->regmap, + DA9063_REG_CONTROL_B, + DA9063_NONKEY_LOCK, 0); + if (error) { + dev_err(onkey->dev, + "Failed to reset the Key Delay %d\n", error); + goto err_poll; + } + + input_report_key(onkey->input, KEY_POWER, 0); + input_sync(onkey->input); + + poll = false; + } + + /* + * If the fault log KEY_RESET is detected, then clear it + * and shut down the system. + */ + error = regmap_read(onkey->hw->regmap, + DA9063_REG_FAULT_LOG, &fault_log); + if (error) { + dev_warn(&onkey->input->dev, + "Cannot read FAULT_LOG: %d\n", error); + } else if (fault_log & DA9063_KEY_RESET) { + error = regmap_write(onkey->hw->regmap, + DA9063_REG_FAULT_LOG, + DA9063_KEY_RESET); + if (error) { + dev_warn(&onkey->input->dev, + "Cannot reset KEY_RESET fault log: %d\n", + error); + } else { + /* at this point we do any S/W housekeeping + * and then send shutdown command + */ + dev_dbg(&onkey->input->dev, + "Sending SHUTDOWN to DA9063 ...\n"); + error = regmap_write(onkey->hw->regmap, + DA9063_REG_CONTROL_F, + DA9063_SHUTDOWN); + if (error) + dev_err(&onkey->input->dev, + "Cannot SHUTDOWN DA9063: %d\n", + error); + } + } + +err_poll: + if (poll) + schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); +} + +static irqreturn_t da9063_onkey_irq_handler(int irq, void *data) +{ + struct da9063_onkey *onkey = data; + unsigned int val; + int error; + + error = regmap_read(onkey->hw->regmap, DA9063_REG_STATUS_A, &val); + if (onkey->key_power && !error && (val & DA9063_NONKEY)) { + input_report_key(onkey->input, KEY_POWER, 1); + input_sync(onkey->input); + schedule_delayed_work(&onkey->work, 0); + dev_dbg(onkey->dev, "KEY_POWER pressed.\n"); + } else { + input_report_key(onkey->input, KEY_SLEEP, 1); + input_sync(onkey->input); + input_report_key(onkey->input, KEY_SLEEP, 0); + input_sync(onkey->input); + dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n"); + } + + return IRQ_HANDLED; +} + +static void da9063_cancel_poll(void *data) +{ + struct da9063_onkey *onkey = data; + + cancel_delayed_work_sync(&onkey->work); +} + +static int da9063_onkey_probe(struct platform_device *pdev) +{ + struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent); + struct da9063_pdata *pdata = dev_get_platdata(da9063->dev); + struct da9063_onkey *onkey; + int irq; + int error; + + onkey = devm_kzalloc(&pdev->dev, sizeof(struct da9063_onkey), + GFP_KERNEL); + if (!onkey) { + dev_err(&pdev->dev, "Failed to allocate memory.\n"); + return -ENOMEM; + } + + onkey->dev = &pdev->dev; + onkey->hw = da9063; + + if (pdata) + onkey->key_power = pdata->key_power; + else + onkey->key_power = + !of_property_read_bool(pdev->dev.of_node, + "dlg,disable-key-power"); + + onkey->input = devm_input_allocate_device(&pdev->dev); + if (!onkey->input) { + dev_err(&pdev->dev, "Failed to allocated input device.\n"); + return -ENOMEM; + } + + onkey->input->name = DA9063_DRVNAME_ONKEY; + onkey->input->phys = DA9063_DRVNAME_ONKEY "/input0"; + onkey->input->dev.parent = &pdev->dev; + + if (onkey->key_power) + input_set_capability(onkey->input, EV_KEY, KEY_POWER); + + input_set_capability(onkey->input, EV_KEY, KEY_SLEEP); + + INIT_DELAYED_WORK(&onkey->work, da9063_poll_on); + + error = devm_add_action(&pdev->dev, da9063_cancel_poll, onkey); + if (error) { + dev_err(&pdev->dev, + "Failed to add cancel poll action: %d\n", + error); + return error; + } + + irq = platform_get_irq_byname(pdev, "ONKEY"); + if (irq < 0) { + error = irq; + dev_err(&pdev->dev, "Failed to get platform IRQ: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&pdev->dev, irq, + NULL, da9063_onkey_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "ONKEY", onkey); + if (error) { + dev_err(&pdev->dev, + "Failed to request IRQ %d: %d\n", irq, error); + return error; + } + + error = input_register_device(onkey->input); + if (error) { + dev_err(&pdev->dev, + "Failed to register input device: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, onkey); + return 0; +} + +static struct platform_driver da9063_onkey_driver = { + .probe = da9063_onkey_probe, + .driver = { + .name = DA9063_DRVNAME_ONKEY, + }, +}; +module_platform_driver(da9063_onkey_driver); + +MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); +MODULE_DESCRIPTION("Onkey device driver for Dialog DA9063"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DA9063_DRVNAME_ONKEY); diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index 599578042..e5d60ecd2 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -580,15 +580,10 @@ static int drv260x_probe(struct i2c_client *client, return error; } - haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable"); - if (IS_ERR(haptics->enable_gpio)) { - error = PTR_ERR(haptics->enable_gpio); - if (error != -ENOENT && error != -ENOSYS) - return error; - haptics->enable_gpio = NULL; - } else { - gpiod_direction_output(haptics->enable_gpio, 1); - } + haptics->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(haptics->enable_gpio)) + return PTR_ERR(haptics->enable_gpio); haptics->input_dev = devm_input_allocate_device(&client->dev); if (!haptics->input_dev) { diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c new file mode 100644 index 000000000..0afaa33de --- /dev/null +++ b/drivers/input/misc/drv2665.c @@ -0,0 +1,322 @@ +/* + * DRV2665 haptics driver family + * + * Author: Dan Murphy <dmurphy@ti.com> + * + * Copyright: (C) 2015 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> + +/* Contol registers */ +#define DRV2665_STATUS 0x00 +#define DRV2665_CTRL_1 0x01 +#define DRV2665_CTRL_2 0x02 +#define DRV2665_FIFO 0x0b + +/* Status Register */ +#define DRV2665_FIFO_FULL BIT(0) +#define DRV2665_FIFO_EMPTY BIT(1) + +/* Control 1 Register */ +#define DRV2665_25_VPP_GAIN 0x00 +#define DRV2665_50_VPP_GAIN 0x01 +#define DRV2665_75_VPP_GAIN 0x02 +#define DRV2665_100_VPP_GAIN 0x03 +#define DRV2665_DIGITAL_IN 0xfc +#define DRV2665_ANALOG_IN BIT(2) + +/* Control 2 Register */ +#define DRV2665_BOOST_EN BIT(1) +#define DRV2665_STANDBY BIT(6) +#define DRV2665_DEV_RST BIT(7) +#define DRV2665_5_MS_IDLE_TOUT 0x00 +#define DRV2665_10_MS_IDLE_TOUT 0x04 +#define DRV2665_15_MS_IDLE_TOUT 0x08 +#define DRV2665_20_MS_IDLE_TOUT 0x0c + +/** + * struct drv2665_data - + * @input_dev - Pointer to the input device + * @client - Pointer to the I2C client + * @regmap - Register map of the device + * @work - Work item used to off load the enable/disable of the vibration + * @regulator - Pointer to the regulator for the IC + */ +struct drv2665_data { + struct input_dev *input_dev; + struct i2c_client *client; + struct regmap *regmap; + struct work_struct work; + struct regulator *regulator; +}; + +/* 8kHz Sine wave to stream to the FIFO */ +static const u8 drv2665_sine_wave_form[] = { + 0x00, 0x10, 0x20, 0x2e, 0x3c, 0x48, 0x53, 0x5b, 0x61, 0x65, 0x66, + 0x65, 0x61, 0x5b, 0x53, 0x48, 0x3c, 0x2e, 0x20, 0x10, + 0x00, 0xf0, 0xe0, 0xd2, 0xc4, 0xb8, 0xad, 0xa5, 0x9f, 0x9b, 0x9a, + 0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00, +}; + +static struct reg_default drv2665_reg_defs[] = { + { DRV2665_STATUS, 0x02 }, + { DRV2665_CTRL_1, 0x28 }, + { DRV2665_CTRL_2, 0x40 }, + { DRV2665_FIFO, 0x00 }, +}; + +static void drv2665_worker(struct work_struct *work) +{ + struct drv2665_data *haptics = + container_of(work, struct drv2665_data, work); + unsigned int read_buf; + int error; + + error = regmap_read(haptics->regmap, DRV2665_STATUS, &read_buf); + if (error) { + dev_err(&haptics->client->dev, + "Failed to read status: %d\n", error); + return; + } + + if (read_buf & DRV2665_FIFO_EMPTY) { + error = regmap_bulk_write(haptics->regmap, + DRV2665_FIFO, + drv2665_sine_wave_form, + ARRAY_SIZE(drv2665_sine_wave_form)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write FIFO: %d\n", error); + return; + } + } +} + +static int drv2665_haptics_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct drv2665_data *haptics = input_get_drvdata(input); + + schedule_work(&haptics->work); + + return 0; +} + +static void drv2665_close(struct input_dev *input) +{ + struct drv2665_data *haptics = input_get_drvdata(input); + int error; + + cancel_work_sync(&haptics->work); + + error = regmap_update_bits(haptics->regmap, + DRV2665_CTRL_2, DRV2665_STANDBY, 1); + if (error) + dev_err(&haptics->client->dev, + "Failed to enter standby mode: %d\n", error); +} + +static const struct reg_default drv2665_init_regs[] = { + { DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT }, + { DRV2665_CTRL_1, DRV2665_25_VPP_GAIN }, +}; + +static int drv2665_init(struct drv2665_data *haptics) +{ + int error; + + error = regmap_register_patch(haptics->regmap, + drv2665_init_regs, + ARRAY_SIZE(drv2665_init_regs)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write init registers: %d\n", + error); + return error; + } + + return 0; +} + +static const struct regmap_config drv2665_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DRV2665_FIFO, + .reg_defaults = drv2665_reg_defs, + .num_reg_defaults = ARRAY_SIZE(drv2665_reg_defs), + .cache_type = REGCACHE_NONE, +}; + +static int drv2665_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct drv2665_data *haptics; + int error; + + haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->regulator = devm_regulator_get(&client->dev, "vbat"); + if (IS_ERR(haptics->regulator)) { + error = PTR_ERR(haptics->regulator); + dev_err(&client->dev, + "unable to get regulator, error: %d\n", error); + return error; + } + + haptics->input_dev = devm_input_allocate_device(&client->dev); + if (!haptics->input_dev) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + haptics->input_dev->name = "drv2665:haptics"; + haptics->input_dev->dev.parent = client->dev.parent; + haptics->input_dev->close = drv2665_close; + input_set_drvdata(haptics->input_dev, haptics); + input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptics->input_dev, NULL, + drv2665_haptics_play); + if (error) { + dev_err(&client->dev, "input_ff_create() failed: %d\n", + error); + return error; + } + + INIT_WORK(&haptics->work, drv2665_worker); + + haptics->client = client; + i2c_set_clientdata(client, haptics); + + haptics->regmap = devm_regmap_init_i2c(client, &drv2665_regmap_config); + if (IS_ERR(haptics->regmap)) { + error = PTR_ERR(haptics->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + error = drv2665_init(haptics); + if (error) { + dev_err(&client->dev, "Device init failed: %d\n", error); + return error; + } + + error = input_register_device(haptics->input_dev); + if (error) { + dev_err(&client->dev, "couldn't register input device: %d\n", + error); + return error; + } + + return 0; +} + +static int __maybe_unused drv2665_suspend(struct device *dev) +{ + struct drv2665_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, + DRV2665_STANDBY, 1); + if (ret) { + dev_err(dev, "Failed to set standby mode\n"); + regulator_disable(haptics->regulator); + goto out; + } + + ret = regulator_disable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to disable regulator\n"); + regmap_update_bits(haptics->regmap, + DRV2665_CTRL_2, + DRV2665_STANDBY, 0); + } + } +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} + +static int __maybe_unused drv2665_resume(struct device *dev) +{ + struct drv2665_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regulator_enable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator\n"); + goto out; + } + + ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2, + DRV2665_STANDBY, 0); + if (ret) { + dev_err(dev, "Failed to unset standby mode\n"); + regulator_disable(haptics->regulator); + goto out; + } + + } + +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} + +static SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume); + +static const struct i2c_device_id drv2665_id[] = { + { "drv2665", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, drv2665_id); + +#ifdef CONFIG_OF +static const struct of_device_id drv2665_of_match[] = { + { .compatible = "ti,drv2665", }, + { } +}; +MODULE_DEVICE_TABLE(of, drv2665_of_match); +#endif + +static struct i2c_driver drv2665_driver = { + .probe = drv2665_probe, + .driver = { + .name = "drv2665-haptics", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(drv2665_of_match), + .pm = &drv2665_pm_ops, + }, + .id_table = drv2665_id, +}; +module_i2c_driver(drv2665_driver); + +MODULE_DESCRIPTION("TI DRV2665 haptics driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); diff --git a/drivers/input/misc/gpio-beeper.c b/drivers/input/misc/gpio-beeper.c index 4817c5f0c..16272fffe 100644 --- a/drivers/input/misc/gpio-beeper.c +++ b/drivers/input/misc/gpio-beeper.c @@ -66,13 +66,12 @@ static int gpio_beeper_probe(struct platform_device *pdev) { struct gpio_beeper *beep; struct input_dev *input; - int err; beep = devm_kzalloc(&pdev->dev, sizeof(*beep), GFP_KERNEL); if (!beep) return -ENOMEM; - beep->desc = devm_gpiod_get(&pdev->dev, NULL); + beep->desc = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW); if (IS_ERR(beep->desc)) return PTR_ERR(beep->desc); @@ -92,10 +91,6 @@ static int gpio_beeper_probe(struct platform_device *pdev) input_set_capability(input, EV_SND, SND_BELL); - err = gpiod_direction_output(beep->desc, 0); - if (err) - return err; - input_set_drvdata(input, beep); return input_register_device(input); diff --git a/drivers/input/misc/retu-pwrbutton.c b/drivers/input/misc/retu-pwrbutton.c index 0c8ac60e2..30b459b6b 100644 --- a/drivers/input/misc/retu-pwrbutton.c +++ b/drivers/input/misc/retu-pwrbutton.c @@ -63,7 +63,8 @@ static int retu_pwrbutton_probe(struct platform_device *pdev) input_set_drvdata(idev, rdev); error = devm_request_threaded_irq(&pdev->dev, irq, - NULL, retu_pwrbutton_irq, 0, + NULL, retu_pwrbutton_irq, + IRQF_ONESHOT, "retu-pwrbutton", idev); if (error) return error; diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index e8e010a85..c14b82709 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -18,7 +18,6 @@ #include <linux/gpio/consumer.h> #include <linux/gpio_keys.h> #include <linux/platform_device.h> -#include <linux/acpi.h> /* * Definition of buttons on the tablet. The ACPI index of each button diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index e98cc81a8..603fc2fad 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -71,7 +71,8 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev) pwr->dev.parent = &pdev->dev; err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "twl4030_pwrbutton", pwr); if (err < 0) { dev_err(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index fc17b9592..10c4e3d46 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -183,7 +183,8 @@ static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, if (pdata && pdata->coexist) return true; - if (of_find_node_by_name(node, "codec")) { + node = of_find_node_by_name(node, "codec"); + if (node) { of_node_put(node); return true; } diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 0e0d094df..ea63fad48 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -308,7 +308,8 @@ static int twl6040_vibra_probe(struct platform_device *pdev) mutex_init(&info->mutex); error = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, - twl6040_vib_irq_handler, 0, + twl6040_vib_irq_handler, + IRQF_ONESHOT, "twl6040_irq_vib", info); if (error) { dev_err(info->dev, "VIB IRQ request failed: %d\n", error); diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c index 59d4f7bcb..1b44de265 100644 --- a/drivers/input/misc/wm831x-on.c +++ b/drivers/input/misc/wm831x-on.c @@ -99,7 +99,8 @@ static int wm831x_on_probe(struct platform_device *pdev) wm831x_on->dev->dev.parent = &pdev->dev; ret = request_threaded_irq(irq, NULL, wm831x_on_irq, - IRQF_TRIGGER_RISING, "wm831x_on", + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "wm831x_on", wm831x_on); if (ret < 0) { dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index bc7eed679..4d246861d 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -161,8 +161,8 @@ static const struct alps_protocol_info alps_v8_protocol_data = { static void alps_set_abs_params_st(struct alps_data *priv, struct input_dev *dev1); -static void alps_set_abs_params_mt(struct alps_data *priv, - struct input_dev *dev1); +static void alps_set_abs_params_semi_mt(struct alps_data *priv, + struct input_dev *dev1); static void alps_set_abs_params_v7(struct alps_data *priv, struct input_dev *dev1); static void alps_set_abs_params_ss4_v2(struct alps_data *priv, @@ -312,53 +312,6 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) input_sync(dev); } -/* - * Process bitmap data for V5 protocols. Return value is null. - * - * The bitmaps don't have enough data to track fingers, so this function - * only generates points representing a bounding box of at most two contacts. - * These two points are returned in fields->mt. - */ -static void alps_process_bitmap_dolphin(struct alps_data *priv, - struct alps_fields *fields) -{ - int box_middle_x, box_middle_y; - unsigned int x_map, y_map; - unsigned char start_bit, end_bit; - unsigned char x_msb, x_lsb, y_msb, y_lsb; - - x_map = fields->x_map; - y_map = fields->y_map; - - if (!x_map || !y_map) - return; - - /* Get Most-significant and Least-significant bit */ - x_msb = fls(x_map); - x_lsb = ffs(x_map); - y_msb = fls(y_map); - y_lsb = ffs(y_map); - - /* Most-significant bit should never exceed max sensor line number */ - if (x_msb > priv->x_bits || y_msb > priv->y_bits) - return; - - if (fields->fingers > 1) { - start_bit = priv->x_bits - x_msb; - end_bit = priv->x_bits - x_lsb; - box_middle_x = (priv->x_max * (start_bit + end_bit)) / - (2 * (priv->x_bits - 1)); - - start_bit = y_lsb - 1; - end_bit = y_msb - 1; - box_middle_y = (priv->y_max * (start_bit + end_bit)) / - (2 * (priv->y_bits - 1)); - fields->mt[0] = fields->st; - fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x; - fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y; - } -} - static void alps_get_bitmap_points(unsigned int map, struct alps_bitmap_point *low, struct alps_bitmap_point *high, @@ -386,7 +339,7 @@ static void alps_get_bitmap_points(unsigned int map, } /* - * Process bitmap data from v3 and v4 protocols. Returns the number of + * Process bitmap data from semi-mt protocols. Returns the number of * fingers detected. A return value of 0 means at least one of the * bitmaps was empty. * @@ -398,9 +351,10 @@ static void alps_get_bitmap_points(unsigned int map, static int alps_process_bitmap(struct alps_data *priv, struct alps_fields *fields) { - int i, fingers_x = 0, fingers_y = 0, fingers; + int i, fingers_x = 0, fingers_y = 0, fingers, closest; struct alps_bitmap_point x_low = {0,}, x_high = {0,}; struct alps_bitmap_point y_low = {0,}, y_high = {0,}; + struct input_mt_pos corner[4]; if (!fields->x_map || !fields->y_map) return 0; @@ -431,26 +385,76 @@ static int alps_process_bitmap(struct alps_data *priv, y_high.num_bits = max(i, 1); } - fields->mt[0].x = + /* top-left corner */ + corner[0].x = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / (2 * (priv->x_bits - 1)); - fields->mt[0].y = + corner[0].y = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / (2 * (priv->y_bits - 1)); - fields->mt[1].x = + /* top-right corner */ + corner[1].x = (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / (2 * (priv->x_bits - 1)); - fields->mt[1].y = + corner[1].y = + (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-right corner */ + corner[2].x = + (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[2].y = + (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* bottom-left corner */ + corner[3].x = + (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + corner[3].y = (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / (2 * (priv->y_bits - 1)); - /* y-bitmap order is reversed, except on rushmore */ - if (priv->proto_version != ALPS_PROTO_V3_RUSHMORE) { - fields->mt[0].y = priv->y_max - fields->mt[0].y; - fields->mt[1].y = priv->y_max - fields->mt[1].y; + /* x-bitmap order is reversed on v5 touchpads */ + if (priv->proto_version == ALPS_PROTO_V5) { + for (i = 0; i < 4; i++) + corner[i].x = priv->x_max - corner[i].x; + } + + /* y-bitmap order is reversed on v3 and v4 touchpads */ + if (priv->proto_version == ALPS_PROTO_V3 || + priv->proto_version == ALPS_PROTO_V4) { + for (i = 0; i < 4; i++) + corner[i].y = priv->y_max - corner[i].y; + } + + /* + * We only select a corner for the second touch once per 2 finger + * touch sequence to avoid the chosen corner (and thus the coordinates) + * jumping around when the first touch is in the middle. + */ + if (priv->second_touch == -1) { + /* Find corner closest to our st coordinates */ + closest = 0x7fffffff; + for (i = 0; i < 4; i++) { + int dx = fields->st.x - corner[i].x; + int dy = fields->st.y - corner[i].y; + int distance = dx * dx + dy * dy; + + if (distance < closest) { + priv->second_touch = i; + closest = distance; + } + } + /* And select the opposite corner to use for the 2nd touch */ + priv->second_touch = (priv->second_touch + 2) % 4; } + fields->mt[0] = fields->st; + fields->mt[1] = corner[priv->second_touch]; + return fingers; } @@ -487,9 +491,14 @@ static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers) f->mt[0].x = f->st.x; f->mt[0].y = f->st.y; fingers = f->pressure > 0 ? 1 : 0; + priv->second_touch = -1; } - alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2); + if (fingers >= 1) + alps_set_slot(dev, 0, f->mt[0].x, f->mt[0].y); + if (fingers >= 2) + alps_set_slot(dev, 1, f->mt[1].x, f->mt[1].y); + input_mt_sync_frame(dev); input_mt_report_finger_count(dev, fingers); @@ -586,20 +595,22 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, f->first_mp = !!(p[4] & 0x40); f->is_mp = !!(p[0] & 0x40); - f->fingers = (p[5] & 0x3) + 1; - f->x_map = ((p[4] & 0x7e) << 8) | - ((p[1] & 0x7f) << 2) | - ((p[0] & 0x30) >> 4); - f->y_map = ((p[3] & 0x70) << 4) | - ((p[2] & 0x7f) << 1) | - (p[4] & 0x01); - - f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | - ((p[0] & 0x30) >> 4); - f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); - f->pressure = p[5] & 0x7f; + if (f->is_mp) { + f->fingers = (p[5] & 0x3) + 1; + f->x_map = ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; - alps_decode_buttons_v3(f, p); + alps_decode_buttons_v3(f, p); + } return 0; } @@ -607,13 +618,27 @@ static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { - alps_decode_pinnacle(f, p, psmouse); - - /* Rushmore's packet decode has a bit difference with Pinnacle's */ + f->first_mp = !!(p[4] & 0x40); f->is_mp = !!(p[5] & 0x40); - f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; - f->x_map |= (p[5] & 0x10) << 11; - f->y_map |= (p[5] & 0x20) << 6; + + if (f->is_mp) { + f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; + f->x_map = ((p[5] & 0x10) << 11) | + ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[5] & 0x20) << 6) | + ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + } else { + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); + } return 0; } @@ -682,30 +707,13 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) */ if (f->is_mp) { fingers = f->fingers; - if (priv->proto_version == ALPS_PROTO_V3 || - priv->proto_version == ALPS_PROTO_V3_RUSHMORE) { - if (alps_process_bitmap(priv, f) == 0) - fingers = 0; /* Use st data */ - - /* Now process position packet */ - priv->decode_fields(f, priv->multi_data, - psmouse); - } else { - /* - * Because Dolphin uses position packet's - * coordinate data as Pt1 and uses it to - * calculate Pt2, so we need to do position - * packet decode first. - */ - priv->decode_fields(f, priv->multi_data, - psmouse); - - /* - * Since Dolphin's finger number is reliable, - * there is no need to compare with bmap_fn. - */ - alps_process_bitmap_dolphin(priv, f); - } + /* + * Bitmap processing uses position packet's coordinate + * data, so we need to do decode it first. + */ + priv->decode_fields(f, priv->multi_data, psmouse); + if (alps_process_bitmap(priv, f) == 0) + fingers = 0; /* Use st data */ } else { priv->multi_packet = 0; } @@ -867,6 +875,14 @@ static void alps_process_packet_v4(struct psmouse *psmouse) priv->multi_data[offset] = packet[6]; priv->multi_data[offset + 1] = packet[7]; + f->left = !!(packet[4] & 0x01); + f->right = !!(packet[4] & 0x02); + + f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + f->pressure = packet[5] & 0x7f; + if (++priv->multi_packet > 2) { priv->multi_packet = 0; @@ -881,14 +897,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse) f->fingers = alps_process_bitmap(priv, f); } - f->left = !!(packet[4] & 0x01); - f->right = !!(packet[4] & 0x02); - - f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | - ((packet[0] & 0x30) >> 4); - f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); - f->pressure = packet[5] & 0x7f; - alps_report_semi_mt_data(psmouse, f->fingers); } @@ -2565,7 +2573,7 @@ static int alps_set_protocol(struct psmouse *psmouse, case ALPS_PROTO_V3: priv->hw_init = alps_hw_init_v3; priv->process_packet = alps_process_packet_v3; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->decode_fields = alps_decode_pinnacle; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; @@ -2574,7 +2582,7 @@ static int alps_set_protocol(struct psmouse *psmouse, case ALPS_PROTO_V3_RUSHMORE: priv->hw_init = alps_hw_init_rushmore_v3; priv->process_packet = alps_process_packet_v3; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->decode_fields = alps_decode_rushmore; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; @@ -2590,7 +2598,7 @@ static int alps_set_protocol(struct psmouse *psmouse, case ALPS_PROTO_V4: priv->hw_init = alps_hw_init_v4; priv->process_packet = alps_process_packet_v4; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->nibble_commands = alps_v4_nibble_commands; priv->addr_command = PSMOUSE_CMD_DISABLE; break; @@ -2599,7 +2607,7 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->hw_init = alps_hw_init_dolphin_v1; priv->process_packet = alps_process_touchpad_packet_v3_v5; priv->decode_fields = alps_decode_dolphin; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_semi_mt; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; priv->x_bits = 23; @@ -2781,15 +2789,15 @@ static void alps_set_abs_params_mt_common(struct alps_data *priv, set_bit(BTN_TOOL_QUADTAP, dev1->keybit); } -static void alps_set_abs_params_mt(struct alps_data *priv, - struct input_dev *dev1) +static void alps_set_abs_params_semi_mt(struct alps_data *priv, + struct input_dev *dev1) { alps_set_abs_params_mt_common(priv, dev1); input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | - INPUT_MT_TRACK | INPUT_MT_SEMI_MT); + INPUT_MT_SEMI_MT); } static void alps_set_abs_params_v7(struct alps_data *priv, diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 6dfdccc3a..d37f814dc 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -278,6 +278,7 @@ struct alps_data { int prev_fin; int multi_packet; + int second_touch; unsigned char multi_data[6]; struct alps_fields f; u8 quirks; diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index b10709f04..30e344251 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -2,6 +2,7 @@ * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver * * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) + * Copyright (C) 2015 John Horan (knasher@gmail.com) * * The USB initialization and package decoding was made by * Scott Shawcroft as part of the touchd user-space driver project: @@ -91,6 +92,10 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 +/* MacbookPro12,1 (2015) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272 +#define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273 +#define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ @@ -152,6 +157,10 @@ static const struct usb_device_id bcm5974_table[] = { BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), + /* MacbookPro12,1 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_JIS), /* Terminating entry */ {} }; @@ -180,21 +189,47 @@ struct bt_data { enum tp_type { TYPE1, /* plain trackpad */ TYPE2, /* button integrated in trackpad */ - TYPE3 /* additional header fields since June 2013 */ + TYPE3, /* additional header fields since June 2013 */ + TYPE4 /* additional header field for pressure data */ }; /* trackpad finger data offsets, le16-aligned */ -#define FINGER_TYPE1 (13 * sizeof(__le16)) -#define FINGER_TYPE2 (15 * sizeof(__le16)) -#define FINGER_TYPE3 (19 * sizeof(__le16)) +#define HEADER_TYPE1 (13 * sizeof(__le16)) +#define HEADER_TYPE2 (15 * sizeof(__le16)) +#define HEADER_TYPE3 (19 * sizeof(__le16)) +#define HEADER_TYPE4 (23 * sizeof(__le16)) /* trackpad button data offsets */ +#define BUTTON_TYPE1 0 #define BUTTON_TYPE2 15 #define BUTTON_TYPE3 23 +#define BUTTON_TYPE4 31 /* list of device capability bits */ #define HAS_INTEGRATED_BUTTON 1 +/* trackpad finger data block size */ +#define FSIZE_TYPE1 (14 * sizeof(__le16)) +#define FSIZE_TYPE2 (14 * sizeof(__le16)) +#define FSIZE_TYPE3 (14 * sizeof(__le16)) +#define FSIZE_TYPE4 (15 * sizeof(__le16)) + +/* offset from header to finger struct */ +#define DELTA_TYPE1 (0 * sizeof(__le16)) +#define DELTA_TYPE2 (0 * sizeof(__le16)) +#define DELTA_TYPE3 (0 * sizeof(__le16)) +#define DELTA_TYPE4 (1 * sizeof(__le16)) + +/* usb control message mode switch data */ +#define USBMSG_TYPE1 8, 0x300, 0, 0, 0x1, 0x8 +#define USBMSG_TYPE2 8, 0x300, 0, 0, 0x1, 0x8 +#define USBMSG_TYPE3 8, 0x300, 0, 0, 0x1, 0x8 +#define USBMSG_TYPE4 2, 0x302, 2, 1, 0x1, 0x0 + +/* Wellspring initialization constants */ +#define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1 +#define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9 + /* trackpad finger structure, le16-aligned */ struct tp_finger { __le16 origin; /* zero when switching track finger */ @@ -207,14 +242,13 @@ struct tp_finger { __le16 orientation; /* 16384 when point, else 15 bit angle */ __le16 touch_major; /* touch area, major axis */ __le16 touch_minor; /* touch area, minor axis */ - __le16 unused[3]; /* zeros */ + __le16 unused[2]; /* zeros */ + __le16 pressure; /* pressure on forcetouch touchpad */ __le16 multi; /* one finger: varies, more fingers: constant */ } __attribute__((packed,aligned(2))); /* trackpad finger data size, empirically at least ten fingers */ #define MAX_FINGERS 16 -#define SIZEOF_FINGER sizeof(struct tp_finger) -#define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER) #define MAX_FINGER_ORIENTATION 16384 /* device-specific parameters */ @@ -232,8 +266,17 @@ struct bcm5974_config { int bt_datalen; /* data length of the button interface */ int tp_ep; /* the endpoint of the trackpad interface */ enum tp_type tp_type; /* type of trackpad interface */ - int tp_offset; /* offset to trackpad finger data */ + int tp_header; /* bytes in header block */ int tp_datalen; /* data length of the trackpad interface */ + int tp_button; /* offset to button data */ + int tp_fsize; /* bytes in single finger block */ + int tp_delta; /* offset from header to finger struct */ + int um_size; /* usb control message length */ + int um_req_val; /* usb control message value */ + int um_req_idx; /* usb control message index */ + int um_switch_idx; /* usb control message mode switch index */ + int um_switch_on; /* usb control message mode switch on */ + int um_switch_off; /* usb control message mode switch off */ struct bcm5974_param p; /* finger pressure limits */ struct bcm5974_param w; /* finger width limits */ struct bcm5974_param x; /* horizontal limits */ @@ -259,6 +302,24 @@ struct bcm5974 { int slots[MAX_FINGERS]; /* slot assignments */ }; +/* trackpad finger block data, le16-aligned */ +static const struct tp_finger *get_tp_finger(const struct bcm5974 *dev, int i) +{ + const struct bcm5974_config *c = &dev->cfg; + u8 *f_base = dev->tp_data + c->tp_header + c->tp_delta; + + return (const struct tp_finger *)(f_base + i * c->tp_fsize); +} + +#define DATAFORMAT(type) \ + type, \ + HEADER_##type, \ + HEADER_##type + (MAX_FINGERS) * (FSIZE_##type), \ + BUTTON_##type, \ + FSIZE_##type, \ + DELTA_##type, \ + USBMSG_##type + /* logical signal quality */ #define SN_PRESSURE 45 /* pressure signal-to-noise ratio */ #define SN_WIDTH 25 /* width signal-to-noise ratio */ @@ -273,7 +334,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING_JIS, 0, 0x84, sizeof(struct bt_data), - 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE1), { SN_PRESSURE, 0, 256 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4824, 5342 }, @@ -286,7 +347,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, 0, 0x84, sizeof(struct bt_data), - 0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE1), { SN_PRESSURE, 0, 256 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4824, 4824 }, @@ -299,7 +360,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING3_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4460, 5166 }, @@ -312,7 +373,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING4_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4620, 5140 }, @@ -325,7 +386,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4616, 5112 }, @@ -338,7 +399,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING5_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4415, 5050 }, @@ -351,7 +412,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING6_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4620, 5140 }, @@ -364,7 +425,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4750, 5280 }, @@ -377,7 +438,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4620, 5140 }, @@ -390,7 +451,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING7_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4750, 5280 }, @@ -403,7 +464,7 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS, HAS_INTEGRATED_BUTTON, 0x84, sizeof(struct bt_data), - 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + 0x81, DATAFORMAT(TYPE2), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4750, 5280 }, @@ -416,13 +477,26 @@ static const struct bcm5974_config bcm5974_config_table[] = { USB_DEVICE_ID_APPLE_WELLSPRING8_JIS, HAS_INTEGRATED_BUTTON, 0, sizeof(struct bt_data), - 0x83, TYPE3, FINGER_TYPE3, FINGER_TYPE3 + SIZEOF_ALL_FINGERS, + 0x83, DATAFORMAT(TYPE3), { SN_PRESSURE, 0, 300 }, { SN_WIDTH, 0, 2048 }, { SN_COORD, -4620, 5140 }, { SN_COORD, -150, 6600 }, { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } }, + { + USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING9_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING9_JIS, + HAS_INTEGRATED_BUTTON, + 0, sizeof(struct bt_data), + 0x83, DATAFORMAT(TYPE4), + { SN_PRESSURE, 0, 300 }, + { SN_WIDTH, 0, 2048 }, + { SN_COORD, -4828, 5345 }, + { SN_COORD, -203, 6803 }, + { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } + }, {} }; @@ -549,19 +623,18 @@ static int report_tp_state(struct bcm5974 *dev, int size) struct input_dev *input = dev->input; int raw_n, i, n = 0; - if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0) + if (size < c->tp_header || (size - c->tp_header) % c->tp_fsize != 0) return -EIO; - /* finger data, le16-aligned */ - f = (const struct tp_finger *)(dev->tp_data + c->tp_offset); - raw_n = (size - c->tp_offset) / SIZEOF_FINGER; + raw_n = (size - c->tp_header) / c->tp_fsize; for (i = 0; i < raw_n; i++) { - if (raw2int(f[i].touch_major) == 0) + f = get_tp_finger(dev, i); + if (raw2int(f->touch_major) == 0) continue; - dev->pos[n].x = raw2int(f[i].abs_x); - dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y); - dev->index[n++] = &f[i]; + dev->pos[n].x = raw2int(f->abs_x); + dev->pos[n].y = c->y.min + c->y.max - raw2int(f->abs_y); + dev->index[n++] = f; } input_mt_assign_slots(input, dev->slots, dev->pos, n, 0); @@ -572,32 +645,22 @@ static int report_tp_state(struct bcm5974 *dev, int size) input_mt_sync_frame(input); - report_synaptics_data(input, c, f, raw_n); + report_synaptics_data(input, c, get_tp_finger(dev, 0), raw_n); - /* type 2 reports button events via ibt only */ - if (c->tp_type == TYPE2) { - int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]); + /* later types report button events via integrated button only */ + if (c->caps & HAS_INTEGRATED_BUTTON) { + int ibt = raw2int(dev->tp_data[c->tp_button]); input_report_key(input, BTN_LEFT, ibt); } - if (c->tp_type == TYPE3) - input_report_key(input, BTN_LEFT, dev->tp_data[BUTTON_TYPE3]); - input_sync(input); return 0; } -/* Wellspring initialization constants */ -#define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1 -#define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9 -#define BCM5974_WELLSPRING_MODE_REQUEST_VALUE 0x300 -#define BCM5974_WELLSPRING_MODE_REQUEST_INDEX 0 -#define BCM5974_WELLSPRING_MODE_VENDOR_VALUE 0x01 -#define BCM5974_WELLSPRING_MODE_NORMAL_VALUE 0x08 - static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) { + const struct bcm5974_config *c = &dev->cfg; int retval = 0, size; char *data; @@ -605,7 +668,7 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) if (dev->cfg.tp_type == TYPE3) return 0; - data = kmalloc(8, GFP_KERNEL); + data = kmalloc(c->um_size, GFP_KERNEL); if (!data) { dev_err(&dev->intf->dev, "out of memory\n"); retval = -ENOMEM; @@ -616,28 +679,24 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), BCM5974_WELLSPRING_MODE_READ_REQUEST_ID, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - BCM5974_WELLSPRING_MODE_REQUEST_VALUE, - BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000); + c->um_req_val, c->um_req_idx, data, c->um_size, 5000); - if (size != 8) { + if (size != c->um_size) { dev_err(&dev->intf->dev, "could not read from device\n"); retval = -EIO; goto out; } /* apply the mode switch */ - data[0] = on ? - BCM5974_WELLSPRING_MODE_VENDOR_VALUE : - BCM5974_WELLSPRING_MODE_NORMAL_VALUE; + data[c->um_switch_idx] = on ? c->um_switch_on : c->um_switch_off; /* write configuration */ size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - BCM5974_WELLSPRING_MODE_REQUEST_VALUE, - BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000); + c->um_req_val, c->um_req_idx, data, c->um_size, 5000); - if (size != 8) { + if (size != c->um_size) { dev_err(&dev->intf->dev, "could not write to device\n"); retval = -EIO; goto out; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 1e2291c37..3faf01c1b 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -950,14 +950,13 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) * Device power mode can only be set when device is in operational mode. */ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, - u16 always_unused) + u16 always_unused) { int ret; u8 power; int tries; u16 sleep_time; - always_unused = 0; if (cyapa->state != CYAPA_STATE_OP) return 0; diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index 5b611dd71..afc39e799 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -352,7 +352,7 @@ struct gen5_app_cmd_head { u8 parameter_data[0]; /* Parameter data variable based on cmd_code */ } __packed; -/* Applicaton get/set parameter command data structure */ +/* Application get/set parameter command data structure */ struct gen5_app_set_parameter_data { u8 parameter_id; u8 parameter_size; @@ -832,7 +832,7 @@ static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data) int ret; /* 0x20 0x00 0xF7 is Gen5 Application HID Description Header; - * 0x20 0x00 0xFF is Gen5 Booloader HID Description Header. + * 0x20 0x00 0xFF is Gen5 Bootloader HID Description Header. * * Must read HID Description content through out, * otherwise Gen5 trackpad cannot response next command @@ -1654,8 +1654,8 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, * that trackpad unable to report signal to wake system up * in the special situation that system is in suspending, and * at the same time, user touch trackpad to wake system up. - * This function can avoid the data to be buffured when system - * is suspending which may cause interrput line unable to be + * This function can avoid the data to be buffered when system + * is suspending which may cause interrupt line unable to be * asserted again. */ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); @@ -2546,16 +2546,11 @@ static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa) gen5_pip->resp_sort_func(cyapa, gen5_pip->irq_cmd_buf, length))) { /* - * Cover the Gen5 V1 firmware issue. - * The issue is there is no interrut will be - * asserted to notityf host to read a command - * data out when always has finger touch on - * trackpad during the command is issued to - * trackad device. - * This issue has the scenario is that, - * user always has his fingers touched on - * trackpad device when booting/rebooting - * their chrome book. + * Work around the Gen5 V1 firmware + * that does not assert interrupt signalling + * that command response is ready if user + * keeps touching the trackpad while command + * is sent to the device. */ length = 0; if (gen5_pip->resp_len) diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index 9be9ebe40..8c8ea5680 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -28,14 +28,13 @@ #define ETP_PRESSURE_OFFSET 25 /* IAP Firmware handling */ +#define ETP_PRODUCT_ID_FORMAT_STRING "%d.0" #define ETP_FW_NAME "/*(DEBLOBBED)*/" #define ETP_IAP_START_ADDR 0x0083 #define ETP_FW_IAP_PAGE_ERR (1 << 5) #define ETP_FW_IAP_INTF_ERR (1 << 4) #define ETP_FW_PAGE_SIZE 64 -#define ETP_FW_VAILDPAGE_COUNT 768 #define ETP_FW_SIGNATURE_SIZE 6 -#define ETP_FW_SIGNATURE_ADDRESS 0xBFFA struct i2c_client; struct completion; @@ -58,7 +57,8 @@ struct elan_transport_ops { bool max_baseliune, u8 *value); int (*get_version)(struct i2c_client *client, bool iap, u8 *version); - int (*get_sm_version)(struct i2c_client *client, u8 *version); + int (*get_sm_version)(struct i2c_client *client, + u8* ic_type, u8 *version); int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum); int (*get_product_id)(struct i2c_client *client, u8 *id); diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 7837ba0f8..4ca73013a 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: æž—æ”¿ç¶ (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.7 + * Version: 1.5.9 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -40,7 +40,7 @@ #include "elan_i2c.h" #define DRIVER_NAME "elan_i2c" -#define ELAN_DRIVER_VERSION "1.5.7" +#define ELAN_DRIVER_VERSION "1.5.9" #define ETP_MAX_PRESSURE 255 #define ETP_FWIDTH_REDUCE 90 #define ETP_FINGER_WIDTH 15 @@ -83,6 +83,9 @@ struct elan_tp_data { u16 fw_checksum; int pressure_adjustment; u8 mode; + u8 ic_type; + u16 fw_vaildpage_count; + u16 fw_signature_address; bool irq_wake; @@ -91,6 +94,29 @@ struct elan_tp_data { bool baseline_ready; }; +static int elan_get_fwinfo(u8 ic_type, u16 *vaildpage_count, + u16 *signature_address) +{ + switch(ic_type) { + case 0x09: + *vaildpage_count = 768; + break; + case 0x0D: + *vaildpage_count = 896; + break; + default: + /* unknown ic type clear value */ + *vaildpage_count = 0; + *signature_address = 0; + return -ENXIO; + } + + *signature_address = + (*vaildpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE; + + return 0; +} + static int elan_enable_power(struct elan_tp_data *data) { int repeat = ETP_RETRY_COUNT; @@ -221,7 +247,8 @@ static int elan_query_device_info(struct elan_tp_data *data) if (error) return error; - error = data->ops->get_sm_version(data->client, &data->sm_version); + error = data->ops->get_sm_version(data->client, &data->ic_type, + &data->sm_version); if (error) return error; @@ -234,6 +261,14 @@ static int elan_query_device_info(struct elan_tp_data *data) if (error) return error; + error = elan_get_fwinfo(data->ic_type, &data->fw_vaildpage_count, + &data->fw_signature_address); + if (error) { + dev_err(&data->client->dev, + "unknown ic type %d\n", data->ic_type); + return error; + } + return 0; } @@ -318,7 +353,7 @@ static int __elan_update_firmware(struct elan_tp_data *data, iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]); boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE; - for (i = boot_page_count; i < ETP_FW_VAILDPAGE_COUNT; i++) { + for (i = boot_page_count; i < data->fw_vaildpage_count; i++) { u16 checksum = 0; const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE]; @@ -403,7 +438,8 @@ static ssize_t elan_sysfs_read_product_id(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct elan_tp_data *data = i2c_get_clientdata(client); - return sprintf(buf, "%d.0\n", data->product_id); + return sprintf(buf, ETP_PRODUCT_ID_FORMAT_STRING "\n", + data->product_id); } static ssize_t elan_sysfs_read_fw_ver(struct device *dev, @@ -442,19 +478,28 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, { struct elan_tp_data *data = dev_get_drvdata(dev); const struct firmware *fw; + char *fw_name; int error; const u8 *fw_signature; static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF}; - error = reject_firmware(&fw, ETP_FW_NAME, dev); + /* Look for a firmware with the product id appended. */ + fw_name = kasprintf(GFP_KERNEL, ETP_FW_NAME, data->product_id); + if (!fw_name) { + dev_err(dev, "failed to allocate memory for firmware name\n"); + return -ENOMEM; + } + + dev_info(dev, "requesting fw '%s'\n", fw_name); + error = reject_firmware(&fw, fw_name, dev); + kfree(fw_name); if (error) { - dev_err(dev, "cannot load firmware %s: %d\n", - ETP_FW_NAME, error); + dev_err(dev, "failed to request firmware: %d\n", error); return error; } /* Firmware file must match signature data */ - fw_signature = &fw->data[ETP_FW_SIGNATURE_ADDRESS]; + fw_signature = &fw->data[data->fw_signature_address]; if (memcmp(fw_signature, signature, sizeof(signature)) != 0) { dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n", (int)sizeof(signature), signature, @@ -726,7 +771,7 @@ static const struct attribute_group *elan_sysfs_groups[] = { */ static void elan_report_contact(struct elan_tp_data *data, int contact_num, bool contact_valid, - bool hover_event, u8 *finger_data) + u8 *finger_data) { struct input_dev *input = data->input; unsigned int pos_x, pos_y; @@ -770,9 +815,7 @@ static void elan_report_contact(struct elan_tp_data *data, input_mt_report_slot_state(input, MT_TOOL_FINGER, true); input_report_abs(input, ABS_MT_POSITION_X, pos_x); input_report_abs(input, ABS_MT_POSITION_Y, data->max_y - pos_y); - input_report_abs(input, ABS_MT_DISTANCE, hover_event); - input_report_abs(input, ABS_MT_PRESSURE, - hover_event ? 0 : scaled_pressure); + input_report_abs(input, ABS_MT_PRESSURE, scaled_pressure); input_report_abs(input, ABS_TOOL_WIDTH, mk_x); input_report_abs(input, ABS_MT_TOUCH_MAJOR, major); input_report_abs(input, ABS_MT_TOUCH_MINOR, minor); @@ -794,14 +837,14 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet) hover_event = hover_info & 0x40; for (i = 0; i < ETP_MAX_FINGERS; i++) { contact_valid = tp_info & (1U << (3 + i)); - elan_report_contact(data, i, contact_valid, hover_event, - finger_data); + elan_report_contact(data, i, contact_valid, finger_data); if (contact_valid) finger_data += ETP_FINGER_DATA_LEN; } input_report_key(input, BTN_LEFT, tp_info & 0x01); + input_report_abs(input, ABS_DISTANCE, hover_event != 0); input_mt_report_pointer_emulation(input, true); input_sync(input); } @@ -877,6 +920,7 @@ static int elan_setup_input_device(struct elan_tp_data *data) input_abs_set_res(input, ABS_Y, data->y_res); input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0); input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0); + input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0); /* And MT parameters */ input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0); @@ -889,7 +933,6 @@ static int elan_setup_input_device(struct elan_tp_data *data) ETP_FINGER_WIDTH * max_width, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, ETP_FINGER_WIDTH * min_width, 0, 0); - input_set_abs_params(input, ABS_MT_DISTANCE, 0, 1, 0, 0); data->input = input; diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index a0acbbf83..683c840c9 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -259,7 +259,8 @@ static int elan_i2c_get_version(struct i2c_client *client, return 0; } -static int elan_i2c_get_sm_version(struct i2c_client *client, u8 *version) +static int elan_i2c_get_sm_version(struct i2c_client *client, + u8 *ic_type, u8 *version) { int error; u8 val[3]; @@ -271,6 +272,7 @@ static int elan_i2c_get_sm_version(struct i2c_client *client, u8 *version) } *version = val[0]; + *ic_type = val[1]; return 0; } diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index 30ab80dbc..ff36a366b 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -165,7 +165,8 @@ static int elan_smbus_get_version(struct i2c_client *client, return 0; } -static int elan_smbus_get_sm_version(struct i2c_client *client, u8 *version) +static int elan_smbus_get_sm_version(struct i2c_client *client, + u8 *ic_type, u8 *version) { int error; u8 val[3]; @@ -177,7 +178,8 @@ static int elan_smbus_get_sm_version(struct i2c_client *client, u8 *version) return error; } - *version = val[0]; /* XXX Why 0 and not 2 as in IAP/FW versions? */ + *version = val[0]; + *ic_type = val[1]; return 0; } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index ce3d40004..2955f1d0c 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -783,19 +783,26 @@ static int elantech_packet_check_v4(struct psmouse *psmouse) struct elantech_data *etd = psmouse->private; unsigned char *packet = psmouse->packet; unsigned char packet_type = packet[3] & 0x03; + unsigned int ic_version; bool sanity_check; if (etd->tp_dev && (packet[3] & 0x0f) == 0x06) return PACKET_TRACKPOINT; + /* This represents the version of IC body. */ + ic_version = (etd->fw_version & 0x0f0000) >> 16; + /* * Sanity check based on the constant bits of a packet. * The constant bits change depending on the value of - * the hardware flag 'crc_enabled' but are the same for - * every packet, regardless of the type. + * the hardware flag 'crc_enabled' and the version of + * the IC body, but are the same for every packet, + * regardless of the type. */ if (etd->crc_enabled) sanity_check = ((packet[3] & 0x08) == 0x00); + else if (ic_version == 7 && etd->samples[1] == 0x2A) + sanity_check = ((packet[3] & 0x1c) == 0x10); else sanity_check = ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0x1c) == 0x10); @@ -1116,6 +1123,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Avatar AVIU-145A2 0x361f00 ? clickpad * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons + * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) @@ -1167,7 +1175,7 @@ static int elantech_set_input_params(struct psmouse *psmouse) struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; - unsigned int x_res = 0, y_res = 0; + unsigned int x_res = 31, y_res = 31; if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) return -1; @@ -1232,8 +1240,6 @@ static int elantech_set_input_params(struct psmouse *psmouse) /* For X to recognize me as touchpad. */ input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); - input_abs_set_res(dev, ABS_X, x_res); - input_abs_set_res(dev, ABS_Y, y_res); /* * range of pressure and width is the same as v2, * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility. @@ -1246,8 +1252,6 @@ static int elantech_set_input_params(struct psmouse *psmouse) input_mt_init_slots(dev, ETP_MAX_FINGERS, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); - input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); - input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res); input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2, ETP_PMAX_V2, 0, 0); /* @@ -1259,6 +1263,13 @@ static int elantech_set_input_params(struct psmouse *psmouse) break; } + input_abs_set_res(dev, ABS_X, x_res); + input_abs_set_res(dev, ABS_Y, y_res); + if (etd->hw_version > 1) { + input_abs_set_res(dev, ABS_MT_POSITION_X, x_res); + input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res); + } + etd->y_max = y_max; etd->width = width; @@ -1648,6 +1659,16 @@ int elantech_init(struct psmouse *psmouse) etd->capabilities[0], etd->capabilities[1], etd->capabilities[2]); + if (etd->hw_version != 1) { + if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, etd->samples)) { + psmouse_err(psmouse, "failed to query sample data\n"); + goto init_fail; + } + psmouse_info(psmouse, + "Elan sample query result %02x, %02x, %02x\n", + etd->samples[0], etd->samples[1], etd->samples[2]); + } + if (elantech_set_absolute_mode(psmouse)) { psmouse_err(psmouse, "failed to put touchpad into absolute mode.\n"); diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index f965d1569..e1cbf409d 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -129,6 +129,7 @@ struct elantech_data { unsigned char reg_26; unsigned char debug; unsigned char capabilities[3]; + unsigned char samples[3]; bool paritycheck; bool jumpy_cursor; bool reports_pressure; diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index 23d259416..4d5576de8 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -103,6 +103,16 @@ struct focaltech_hw_state { */ struct focaltech_finger_state fingers[FOC_MAX_FINGERS]; + /* + * Finger width 0-7 and 15 for a very big contact area. + * 15 value stays until the finger is released. + * Width is reported only in absolute packets. + * Since hardware reports width only for last touching finger, + * there is no need to store width for every specific finger, + * so we keep only last value reported. + */ + unsigned int width; + /* True if the clickpad has been pressed. */ bool pressed; }; @@ -137,6 +147,7 @@ static void focaltech_report_state(struct psmouse *psmouse) input_report_abs(dev, ABS_MT_POSITION_X, clamped_x); input_report_abs(dev, ABS_MT_POSITION_Y, priv->y_max - clamped_y); + input_report_abs(dev, ABS_TOOL_WIDTH, state->width); } } input_mt_report_pointer_emulation(dev, true); @@ -187,6 +198,7 @@ static void focaltech_process_abs_packet(struct psmouse *psmouse, state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2]; state->fingers[finger].y = (packet[3] << 8) | packet[4]; + state->width = packet[5] >> 4; state->fingers[finger].valid = true; } @@ -331,6 +343,7 @@ static void focaltech_set_input_params(struct psmouse *psmouse) __set_bit(EV_ABS, dev->evbit); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); + input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); input_mt_init_slots(dev, 5, INPUT_MT_POINTER); __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 5bb1658f6..ec3477036 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -47,7 +47,7 @@ MODULE_LICENSE("GPL"); static unsigned int psmouse_max_proto = PSMOUSE_AUTO; static int psmouse_set_maxproto(const char *val, const struct kernel_param *); static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp); -static struct kernel_param_ops param_ops_proto_abbrev = { +static const struct kernel_param_ops param_ops_proto_abbrev = { .set = psmouse_set_maxproto, .get = psmouse_get_maxproto, }; @@ -63,7 +63,7 @@ static unsigned int psmouse_rate = 100; module_param_named(rate, psmouse_rate, uint, 0644); MODULE_PARM_DESC(rate, "Report rate, in reports per second."); -static bool psmouse_smartscroll = 1; +static bool psmouse_smartscroll = true; module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h index aa697ece4..42df9e3be 100644 --- a/drivers/input/mouse/sentelic.h +++ b/drivers/input/mouse/sentelic.h @@ -123,11 +123,11 @@ struct fsp_data { extern int fsp_detect(struct psmouse *psmouse, bool set_properties); extern int fsp_init(struct psmouse *psmouse); #else -inline int fsp_detect(struct psmouse *psmouse, bool set_properties) +static inline int fsp_detect(struct psmouse *psmouse, bool set_properties) { return -ENOSYS; } -inline int fsp_init(struct psmouse *psmouse) +static inline int fsp_init(struct psmouse *psmouse) { return -ENOSYS; } diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 287208c45..e678212f1 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1486,12 +1486,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; psmouse_info(psmouse, - "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n", + "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n", SYN_ID_MODEL(priv->identity), SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c, - priv->board_id, priv->firmware_id); + priv->ext_cap_10, priv->board_id, priv->firmware_id); set_input_params(psmouse, priv); diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c index 878f18498..ffceedcaf 100644 --- a/drivers/input/mouse/synaptics_i2c.c +++ b/drivers/input/mouse/synaptics_i2c.c @@ -185,7 +185,7 @@ #define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4) /* Control touchpad's No Deceleration option */ -static bool no_decel = 1; +static bool no_decel = true; module_param(no_decel, bool, 0644); MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)"); @@ -340,9 +340,9 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch) s32 data; s8 x_delta, y_delta; - /* Deal with spontanious resets and errors */ + /* Deal with spontaneous resets and errors */ if (synaptics_i2c_check_error(touch->client)) - return 0; + return false; /* Get Gesture Bit */ data = synaptics_i2c_reg_get(touch->client, DATA_REG0); diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 77833d7a0..200841b77 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -244,6 +244,7 @@ config SERIO_PS2MULT config SERIO_ARC_PS2 tristate "ARC PS/2 support" + depends on HAS_IOMEM help Say Y here if you have an ARC FPGA platform with a PS/2 controller in it. diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 69175b825..9c927d35c 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -167,7 +167,6 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u { struct serport *serport = (struct serport*) tty->disc_data; struct serio *serio; - char name[64]; if (test_and_set_bit(SERPORT_BUSY, &serport->flags)) return -EBUSY; @@ -177,7 +176,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u return -ENOMEM; strlcpy(serio->name, "Serial port", sizeof(serio->name)); - snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty)); serio->id = serport->id; serio->id.type = SERIO_RS232; serio->write = serport_serio_write; @@ -187,7 +186,7 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u serio->dev.parent = tty->dev; serio_register_port(serport->serio); - printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); + printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty)); wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags)); serio_unregister_port(serport->serio); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 80f638670..a854c6e5f 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -658,6 +658,18 @@ config TOUCHSCREEN_PIXCIR To compile this driver as a module, choose M here: the module will be called pixcir_i2c_ts. +config TOUCHSCREEN_WDT87XX_I2C + tristate "Weida HiTech I2C touchscreen" + depends on I2C + help + Say Y here if you have a Weida WDT87XX I2C touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wdt87xx_i2c. + config TOUCHSCREEN_WM831X tristate "Support for WM831x touchscreen controllers" depends on MFD_WM831X @@ -958,6 +970,7 @@ config TOUCHSCREEN_ST1232 config TOUCHSCREEN_STMPE tristate "STMicroelectronics STMPE touchscreens" depends on MFD_STMPE + depends on (OF || COMPILE_TEST) help Say Y here if you want support for STMicroelectronics STMPE touchscreen controllers. @@ -979,8 +992,7 @@ config TOUCHSCREEN_SUN4I config TOUCHSCREEN_SUR40 tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" - depends on USB - depends on MEDIA_USB_SUPPORT + depends on USB && MEDIA_USB_SUPPORT && HAS_DMA select INPUT_POLLDEV select VIDEOBUF2_DMA_SG help diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 44deea743..fa3d33bac 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o +obj-$(CONFIG_TOUCHSCREEN_WDT87XX_I2C) += wdt87xx_i2c.o obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b406ed0af..e4220f243 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -726,15 +726,15 @@ static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; const struct mxt_platform_data *pdata = data->pdata; - bool button; int i; - /* Active-low switch */ for (i = 0; i < pdata->t19_num_keys; i++) { if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message[1] & (1 << i)); - input_report_key(input, pdata->t19_keymap[i], button); + + /* Active-low switch */ + input_report_key(input, pdata->t19_keymap[i], + !(message[1] & BIT(i))); } } diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 568a3d340..5ed310574 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -775,7 +775,6 @@ static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, struct device *dev = &md->input->dev; struct cyttsp4_sysinfo *si = md->si; enum cyttsp4_tch_abs abs; - int tmp; bool flipped; for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { @@ -790,9 +789,7 @@ static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, } if (md->pdata->flags & CY_FLAG_FLIP) { - tmp = touch->abs[CY_TCH_X]; - touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y]; - touch->abs[CY_TCH_Y] = tmp; + swap(touch->abs[CY_TCH_X], touch->abs[CY_TCH_Y]); flipped = true; } else flipped = false; diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index e6aef3e48..394b1de9a 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, input->id.bustype = BUS_I2C; input->dev.parent = &client->dev; - __set_bit(EV_KEY, input->evbit); - __set_bit(EV_ABS, input->evbit); - __set_bit(BTN_TOUCH, input->keybit); - input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); - input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_X, 0, tsdata->num_x * 64 - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y * 64 - 1, 0, 0); if (!pdata) - touchscreen_parse_of_params(input); + touchscreen_parse_of_params(input, true); - error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); + error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); return error; diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index 3af16984d..e36162b28 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -15,6 +15,7 @@ */ #include <linux/kernel.h> +#include <linux/dmi.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> @@ -34,6 +35,7 @@ struct goodix_ts_data { int abs_y_max; unsigned int max_touch_num; unsigned int int_trigger_type; + bool rotated_screen; }; #define GOODIX_MAX_HEIGHT 4096 @@ -47,7 +49,7 @@ struct goodix_ts_data { /* Register defines */ #define GOODIX_READ_COOR_ADDR 0x814E #define GOODIX_REG_CONFIG_DATA 0x8047 -#define GOODIX_REG_VERSION 0x8140 +#define GOODIX_REG_ID 0x8140 #define RESOLUTION_LOC 1 #define MAX_CONTACTS_LOC 5 @@ -60,6 +62,30 @@ static const unsigned long goodix_irq_flags[] = { IRQ_TYPE_LEVEL_HIGH, }; +/* + * Those tablets have their coordinates origin at the bottom right + * of the tablet, as if rotated 180 degrees + */ +static const struct dmi_system_id rotated_screen[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) + { + .ident = "WinBook TW100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "WinBook"), + DMI_MATCH(DMI_PRODUCT_NAME, "TW100") + } + }, + { + .ident = "WinBook TW700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "WinBook"), + DMI_MATCH(DMI_PRODUCT_NAME, "TW700") + }, + }, +#endif + {} +}; + /** * goodix_i2c_read - read data from a register of the i2c slave device. * @@ -69,7 +95,7 @@ static const unsigned long goodix_irq_flags[] = { * @len: length of the buffer to write */ static int goodix_i2c_read(struct i2c_client *client, - u16 reg, u8 *buf, int len) + u16 reg, u8 *buf, int len) { struct i2c_msg msgs[2]; u16 wbuf = cpu_to_be16(reg); @@ -78,7 +104,7 @@ static int goodix_i2c_read(struct i2c_client *client, msgs[0].flags = 0; msgs[0].addr = client->addr; msgs[0].len = 2; - msgs[0].buf = (u8 *) &wbuf; + msgs[0].buf = (u8 *)&wbuf; msgs[1].flags = I2C_M_RD; msgs[1].addr = client->addr; @@ -101,6 +127,9 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) return error; } + if (!(data[0] & 0x80)) + return -EAGAIN; + touch_num = data[0] & 0x0f; if (touch_num > ts->max_touch_num) return -EPROTO; @@ -126,6 +155,11 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) int input_y = get_unaligned_le16(&coor_data[3]); int input_w = get_unaligned_le16(&coor_data[5]); + if (ts->rotated_screen) { + input_x = ts->abs_x_max - input_x; + input_y = ts->abs_y_max - input_y; + } + input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); @@ -144,7 +178,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) */ static void goodix_process_events(struct goodix_ts_data *ts) { - u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num]; + u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; int touch_num; int i; @@ -196,8 +230,8 @@ static void goodix_read_config(struct goodix_ts_data *ts) int error; error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, - config, - GOODIX_CONFIG_MAX_LENGTH); + config, + GOODIX_CONFIG_MAX_LENGTH); if (error) { dev_warn(&ts->client->dev, "Error reading config (%d), using defaults\n", @@ -220,6 +254,11 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_y_max = GOODIX_MAX_HEIGHT; ts->max_touch_num = GOODIX_MAX_CONTACTS; } + + ts->rotated_screen = dmi_check_system(rotated_screen); + if (ts->rotated_screen) + dev_dbg(&ts->client->dev, + "Applying '180 degrees rotated screen' quirk\n"); } /** @@ -227,22 +266,28 @@ static void goodix_read_config(struct goodix_ts_data *ts) * * @client: the i2c client * @version: output buffer containing the version on success + * @id: output buffer containing the id on success */ -static int goodix_read_version(struct i2c_client *client, u16 *version) +static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id) { int error; u8 buf[6]; + char id_str[5]; - error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf)); + error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf)); if (error) { dev_err(&client->dev, "read version failed: %d\n", error); return error; } - if (version) - *version = get_unaligned_le16(&buf[4]); + memcpy(id_str, buf, 4); + id_str[4] = 0; + if (kstrtou16(id_str, 10, id)) + *id = 0x1001; + + *version = get_unaligned_le16(&buf[4]); - dev_info(&client->dev, "IC VERSION: %6ph\n", buf); + dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version); return 0; } @@ -276,10 +321,13 @@ static int goodix_i2c_test(struct i2c_client *client) * goodix_request_input_dev - Allocate, populate and register the input device * * @ts: our goodix_ts_data pointer + * @version: device firmware version + * @id: device ID * * Must be called during probe */ -static int goodix_request_input_dev(struct goodix_ts_data *ts) +static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version, + u16 id) { int error; @@ -289,14 +337,10 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) return -ENOMEM; } - ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | - BIT_MASK(EV_KEY) | - BIT_MASK(EV_ABS); - - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, - ts->abs_x_max, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, - ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, + 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, + 0, ts->abs_y_max, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); @@ -307,8 +351,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) ts->input_dev->phys = "input/ts"; ts->input_dev->id.bustype = BUS_I2C; ts->input_dev->id.vendor = 0x0416; - ts->input_dev->id.product = 0x1001; - ts->input_dev->id.version = 10427; + ts->input_dev->id.product = id; + ts->input_dev->id.version = version; error = input_register_device(ts->input_dev); if (error) { @@ -326,7 +370,7 @@ static int goodix_ts_probe(struct i2c_client *client, struct goodix_ts_data *ts; unsigned long irq_flags; int error; - u16 version_info; + u16 version_info, id_info; dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -348,7 +392,7 @@ static int goodix_ts_probe(struct i2c_client *client, return error; } - error = goodix_read_version(client, &version_info); + error = goodix_read_version(client, &version_info, &id_info); if (error) { dev_err(&client->dev, "Read version failed.\n"); return error; @@ -356,7 +400,7 @@ static int goodix_ts_probe(struct i2c_client *client, goodix_read_config(ts); - error = goodix_request_input_dev(ts); + error = goodix_request_input_dev(ts, version_info, id_info); if (error) return error; diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index b82b5207c..806cd0ad1 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -14,14 +14,22 @@ #include <linux/input/mt.h> #include <linux/input/touchscreen.h> -static u32 of_get_optional_u32(struct device_node *np, - const char *property) +static bool touchscreen_get_prop_u32(struct device_node *np, + const char *property, + unsigned int default_value, + unsigned int *value) { - u32 val = 0; + u32 val; + int error; - of_property_read_u32(np, property, &val); + error = of_property_read_u32(np, property, &val); + if (error) { + *value = default_value; + return false; + } - return val; + *value = val; + return true; } static void touchscreen_set_params(struct input_dev *dev, @@ -54,34 +62,45 @@ static void touchscreen_set_params(struct input_dev *dev, * input device accordingly. The function keeps previously setuped default * values if no value is specified via DT. */ -void touchscreen_parse_of_params(struct input_dev *dev) +void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch) { struct device_node *np = dev->dev.parent->of_node; - u32 maximum, fuzz; + unsigned int axis; + unsigned int maximum, fuzz; + bool data_present; input_alloc_absinfo(dev); if (!dev->absinfo) return; - maximum = of_get_optional_u32(np, "touchscreen-size-x"); - fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x"); - if (maximum || fuzz) { - touchscreen_set_params(dev, ABS_X, maximum, fuzz); - touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz); - } + axis = multitouch ? ABS_MT_POSITION_X : ABS_X; + data_present = touchscreen_get_prop_u32(np, "touchscreen-size-x", + input_abs_get_max(dev, axis), + &maximum) | + touchscreen_get_prop_u32(np, "touchscreen-fuzz-x", + input_abs_get_fuzz(dev, axis), + &fuzz); + if (data_present) + touchscreen_set_params(dev, axis, maximum, fuzz); - maximum = of_get_optional_u32(np, "touchscreen-size-y"); - fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y"); - if (maximum || fuzz) { - touchscreen_set_params(dev, ABS_Y, maximum, fuzz); - touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz); - } + axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y; + data_present = touchscreen_get_prop_u32(np, "touchscreen-size-y", + input_abs_get_max(dev, axis), + &maximum) | + touchscreen_get_prop_u32(np, "touchscreen-fuzz-y", + input_abs_get_fuzz(dev, axis), + &fuzz); + if (data_present) + touchscreen_set_params(dev, axis, maximum, fuzz); - maximum = of_get_optional_u32(np, "touchscreen-max-pressure"); - fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure"); - if (maximum || fuzz) { - touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz); - touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz); - } + axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; + data_present = touchscreen_get_prop_u32(np, "touchscreen-max-pressure", + input_abs_get_max(dev, axis), + &maximum) | + touchscreen_get_prop_u32(np, "touchscreen-fuzz-pressure", + input_abs_get_fuzz(dev, axis), + &fuzz); + if (data_present) + touchscreen_set_params(dev, axis, maximum, fuzz); } EXPORT_SYMBOL(touchscreen_parse_of_params); diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index bdfa27dc0..a4a103e1d 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -411,7 +411,7 @@ static const struct dev_pm_ops s3c_ts_pmops = { }; #endif -static struct platform_device_id s3cts_driver_ids[] = { +static const struct platform_device_id s3cts_driver_ids[] = { { "s3c2410-ts", 0 }, { "s3c2440-ts", 0 }, { "s3c64xx-ts", FEAT_PEN_IRQ }, diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index e4c31256a..e414d43e5 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -267,27 +267,10 @@ static void stmpe_ts_close(struct input_dev *dev) static void stmpe_ts_get_platform_info(struct platform_device *pdev, struct stmpe_touch *ts) { - struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); struct device_node *np = pdev->dev.of_node; - struct stmpe_ts_platform_data *ts_pdata = NULL; - - ts->stmpe = stmpe; - - if (stmpe->pdata && stmpe->pdata->ts) { - ts_pdata = stmpe->pdata->ts; - - ts->sample_time = ts_pdata->sample_time; - ts->mod_12b = ts_pdata->mod_12b; - ts->ref_sel = ts_pdata->ref_sel; - ts->adc_freq = ts_pdata->adc_freq; - ts->ave_ctrl = ts_pdata->ave_ctrl; - ts->touch_det_delay = ts_pdata->touch_det_delay; - ts->settling = ts_pdata->settling; - ts->fraction_z = ts_pdata->fraction_z; - ts->i_drive = ts_pdata->i_drive; - } else if (np) { - u32 val; + u32 val; + if (np) { if (!of_property_read_u32(np, "st,sample-time", &val)) ts->sample_time = val; if (!of_property_read_u32(np, "st,mod-12b", &val)) @@ -311,6 +294,7 @@ static void stmpe_ts_get_platform_info(struct platform_device *pdev, static int stmpe_input_probe(struct platform_device *pdev) { + struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); struct stmpe_touch *ts; struct input_dev *idev; int error; @@ -329,6 +313,7 @@ static int stmpe_input_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, ts); + ts->stmpe = stmpe; ts->idev = idev; ts->dev = &pdev->dev; @@ -351,14 +336,13 @@ static int stmpe_input_probe(struct platform_device *pdev) idev->name = STMPE_TS_NAME; idev->phys = STMPE_TS_NAME"/input0"; idev->id.bustype = BUS_I2C; - idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); idev->open = stmpe_ts_open; idev->close = stmpe_ts_close; input_set_drvdata(idev, ts); + input_set_capability(idev, EV_KEY, BTN_TOUCH); input_set_abs_params(idev, ABS_X, 0, XY_MASK, 0, 0); input_set_abs_params(idev, ABS_Y, 0, XY_MASK, 0, 0); input_set_abs_params(idev, ABS_PRESSURE, 0x0, 0xff, 0, 0); @@ -383,14 +367,19 @@ static int stmpe_ts_remove(struct platform_device *pdev) static struct platform_driver stmpe_ts_driver = { .driver = { - .name = STMPE_TS_NAME, - }, + .name = STMPE_TS_NAME, + }, .probe = stmpe_input_probe, .remove = stmpe_ts_remove, }; module_platform_driver(stmpe_ts_driver); +static const struct of_device_id stmpe_ts_ids[] = { + { .compatible = "st,stmpe-ts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, stmpe_ts_ids); + MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); MODULE_DESCRIPTION("STMPEXXX touchscreen driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" STMPE_TS_NAME); diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index a24eba5ea..8be7b9b79 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -125,7 +125,7 @@ struct sur40_image_header { #define VIDEO_PACKET_SIZE 16384 /* polling interval (ms) */ -#define POLL_INTERVAL 10 +#define POLL_INTERVAL 4 /* maximum number of contacts FIXME: this is a guess? */ #define MAX_CONTACTS 64 @@ -342,7 +342,7 @@ static void sur40_poll(struct input_polled_dev *polldev) * instead of at the end. */ if (packet_id != header->packet_id) - dev_warn(sur40->dev, "packet ID mismatch\n"); + dev_dbg(sur40->dev, "packet ID mismatch\n"); packet_blobs = result / sizeof(struct sur40_blob); dev_dbg(sur40->dev, "received %d blobs\n", packet_blobs); @@ -389,6 +389,8 @@ static void sur40_process_video(struct sur40_state *sur40) list_del(&new_buf->list); spin_unlock(&sur40->qlock); + dev_dbg(sur40->dev, "buffer acquired\n"); + /* retrieve data via bulk read */ result = usb_bulk_msg(sur40->usbdev, usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), @@ -416,6 +418,8 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; } + dev_dbg(sur40->dev, "header acquired\n"); + sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0); result = usb_sg_init(&sgr, sur40->usbdev, @@ -432,11 +436,18 @@ static void sur40_process_video(struct sur40_state *sur40) goto err_poll; } + dev_dbg(sur40->dev, "image acquired\n"); + + /* return error if streaming was stopped in the meantime */ + if (sur40->sequence == -1) + goto err_poll; + /* mark as finished */ v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp); new_buf->vb.v4l2_buf.sequence = sur40->sequence++; new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE; vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE); + dev_dbg(sur40->dev, "buffer marked done\n"); return; err_poll: @@ -716,6 +727,7 @@ static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count) static void sur40_stop_streaming(struct vb2_queue *vq) { struct sur40_state *sur40 = vb2_get_drv_priv(vq); + sur40->sequence = -1; /* Release all active buffers */ return_all_buffers(sur40, VB2_BUF_STATE_ERROR); @@ -778,6 +790,33 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, return 0; } +static int sur40_vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *f) +{ + if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY)) + return -EINVAL; + + f->type = V4L2_FRMSIZE_TYPE_DISCRETE; + f->discrete.width = sur40_video_format.width; + f->discrete.height = sur40_video_format.height; + return 0; +} + +static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *f) +{ + if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY) + || (f->width != sur40_video_format.width) + || (f->height != sur40_video_format.height)) + return -EINVAL; + + f->type = V4L2_FRMIVAL_TYPE_DISCRETE; + f->discrete.denominator = 60/(f->index+1); + f->discrete.numerator = 1; + return 0; +} + + static const struct usb_device_id sur40_table[] = { { USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */ { } /* terminating null entry */ @@ -829,6 +868,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = { .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt, .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt, + .vidioc_enum_framesizes = sur40_vidioc_enum_framesizes, + .vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals, + .vidioc_enum_input = sur40_vidioc_enum_input, .vidioc_g_input = sur40_vidioc_g_input, .vidioc_s_input = sur40_vidioc_s_input, diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 72657c579..d8c025b0f 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) input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); if (np) - touchscreen_parse_of_params(input_dev); + touchscreen_parse_of_params(input_dev, false); input_dev->open = tsc2005_open; 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 000000000..3ee535026 --- /dev/null +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -0,0 +1,1149 @@ +/* + * Weida HiTech WDT87xx TouchScreen I2C driver + * + * Copyright (c) 2015 Weida Hi-Tech Co., Ltd. + * HN Chen <hn.chen@weidahitech.com> + * + * This software is licensed under the terms of the GNU General Public + * License, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/firmware.h> +#include <linux/input/mt.h> +#include <linux/acpi.h> +#include <asm/unaligned.h> + +#define WDT87XX_NAME "wdt87xx_i2c" +#define WDT87XX_DRV_VER "0.9.6" +#define WDT87XX_FW_NAME "/*(DEBLOBBED)*/" +#define WDT87XX_CFG_NAME "/*(DEBLOBBED)*/" + +#define MODE_ACTIVE 0x01 +#define MODE_READY 0x02 +#define MODE_IDLE 0x03 +#define MODE_SLEEP 0x04 +#define MODE_STOP 0xFF + +#define WDT_MAX_FINGER 10 +#define WDT_RAW_BUF_COUNT 54 +#define WDT_V1_RAW_BUF_COUNT 74 +#define WDT_FIRMWARE_ID 0xa9e368f5 + +#define PG_SIZE 0x1000 +#define MAX_RETRIES 3 + +#define MAX_UNIT_AXIS 0x7FFF + +#define PKT_READ_SIZE 72 +#define PKT_WRITE_SIZE 80 + +/* the finger definition of the report event */ +#define FINGER_EV_OFFSET_ID 0 +#define FINGER_EV_OFFSET_X 1 +#define FINGER_EV_OFFSET_Y 3 +#define FINGER_EV_SIZE 5 + +#define FINGER_EV_V1_OFFSET_ID 0 +#define FINGER_EV_V1_OFFSET_W 1 +#define FINGER_EV_V1_OFFSET_P 2 +#define FINGER_EV_V1_OFFSET_X 3 +#define FINGER_EV_V1_OFFSET_Y 5 +#define FINGER_EV_V1_SIZE 7 + +/* The definition of a report packet */ +#define TOUCH_PK_OFFSET_REPORT_ID 0 +#define TOUCH_PK_OFFSET_EVENT 1 +#define TOUCH_PK_OFFSET_SCAN_TIME 51 +#define TOUCH_PK_OFFSET_FNGR_NUM 53 + +#define TOUCH_PK_V1_OFFSET_REPORT_ID 0 +#define TOUCH_PK_V1_OFFSET_EVENT 1 +#define TOUCH_PK_V1_OFFSET_SCAN_TIME 71 +#define TOUCH_PK_V1_OFFSET_FNGR_NUM 73 + +/* The definition of the controller parameters */ +#define CTL_PARAM_OFFSET_FW_ID 0 +#define CTL_PARAM_OFFSET_PLAT_ID 2 +#define CTL_PARAM_OFFSET_XMLS_ID1 4 +#define CTL_PARAM_OFFSET_XMLS_ID2 6 +#define CTL_PARAM_OFFSET_PHY_CH_X 8 +#define CTL_PARAM_OFFSET_PHY_CH_Y 10 +#define CTL_PARAM_OFFSET_PHY_X0 12 +#define CTL_PARAM_OFFSET_PHY_X1 14 +#define CTL_PARAM_OFFSET_PHY_Y0 16 +#define CTL_PARAM_OFFSET_PHY_Y1 18 +#define CTL_PARAM_OFFSET_PHY_W 22 +#define CTL_PARAM_OFFSET_PHY_H 24 +#define CTL_PARAM_OFFSET_FACTOR 32 + +/* Communication commands */ +#define PACKET_SIZE 56 +#define VND_REQ_READ 0x06 +#define VND_READ_DATA 0x07 +#define VND_REQ_WRITE 0x08 + +#define VND_CMD_START 0x00 +#define VND_CMD_STOP 0x01 +#define VND_CMD_RESET 0x09 + +#define VND_CMD_ERASE 0x1A + +#define VND_GET_CHECKSUM 0x66 + +#define VND_SET_DATA 0x83 +#define VND_SET_COMMAND_DATA 0x84 +#define VND_SET_CHECKSUM_CALC 0x86 +#define VND_SET_CHECKSUM_LENGTH 0x87 + +#define VND_CMD_SFLCK 0xFC +#define VND_CMD_SFUNL 0xFD + +#define CMD_SFLCK_KEY 0xC39B +#define CMD_SFUNL_KEY 0x95DA + +#define STRIDX_PLATFORM_ID 0x80 +#define STRIDX_PARAMETERS 0x81 + +#define CMD_BUF_SIZE 8 +#define PKT_BUF_SIZE 64 + +/* The definition of the command packet */ +#define CMD_REPORT_ID_OFFSET 0x0 +#define CMD_TYPE_OFFSET 0x1 +#define CMD_INDEX_OFFSET 0x2 +#define CMD_KEY_OFFSET 0x3 +#define CMD_LENGTH_OFFSET 0x4 +#define CMD_DATA_OFFSET 0x8 + +/* The definition of firmware chunk tags */ +#define FOURCC_ID_RIFF 0x46464952 +#define FOURCC_ID_WHIF 0x46494857 +#define FOURCC_ID_FRMT 0x544D5246 +#define FOURCC_ID_FRWR 0x52575246 +#define FOURCC_ID_CNFG 0x47464E43 + +#define CHUNK_ID_FRMT FOURCC_ID_FRMT +#define CHUNK_ID_FRWR FOURCC_ID_FRWR +#define CHUNK_ID_CNFG FOURCC_ID_CNFG + +#define FW_FOURCC1_OFFSET 0 +#define FW_SIZE_OFFSET 4 +#define FW_FOURCC2_OFFSET 8 +#define FW_PAYLOAD_OFFSET 40 + +#define FW_CHUNK_ID_OFFSET 0 +#define FW_CHUNK_SIZE_OFFSET 4 +#define FW_CHUNK_TGT_START_OFFSET 8 +#define FW_CHUNK_PAYLOAD_LEN_OFFSET 12 +#define FW_CHUNK_SRC_START_OFFSET 16 +#define FW_CHUNK_VERSION_OFFSET 20 +#define FW_CHUNK_ATTR_OFFSET 24 +#define FW_CHUNK_PAYLOAD_OFFSET 32 + +/* Controller requires minimum 300us between commands */ +#define WDT_COMMAND_DELAY_MS 2 +#define WDT_FLASH_WRITE_DELAY_MS 4 + +struct wdt87xx_sys_param { + u16 fw_id; + u16 plat_id; + u16 xmls_id1; + u16 xmls_id2; + u16 phy_ch_x; + u16 phy_ch_y; + u16 phy_w; + u16 phy_h; + u16 scaling_factor; + u32 max_x; + u32 max_y; +}; + +struct wdt87xx_data { + struct i2c_client *client; + struct input_dev *input; + /* Mutex for fw update to prevent concurrent access */ + struct mutex fw_mutex; + struct wdt87xx_sys_param param; + u8 phys[32]; +}; + +static int wdt87xx_i2c_xfer(struct i2c_client *client, + void *txdata, size_t txlen, + void *rxdata, size_t rxlen) +{ + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = txlen, + .buf = txdata, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = rxlen, + .buf = rxdata, + }, + }; + int error; + int ret; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s: i2c transfer failed: %d\n", + __func__, error); + return error; + } + + return 0; +} + +static int wdt87xx_get_string(struct i2c_client *client, u8 str_idx, + u8 *buf, size_t len) +{ + u8 tx_buf[] = { 0x22, 0x00, 0x13, 0x0E, str_idx, 0x23, 0x00 }; + u8 rx_buf[PKT_WRITE_SIZE]; + size_t rx_len = len + 2; + int error; + + if (rx_len > sizeof(rx_buf)) + return -EINVAL; + + error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf), + rx_buf, rx_len); + if (error) { + dev_err(&client->dev, "get string failed: %d\n", error); + return error; + } + + if (rx_buf[1] != 0x03) { + dev_err(&client->dev, "unexpected response to get string: %d\n", + rx_buf[1]); + return -EINVAL; + } + + rx_len = min_t(size_t, len, rx_buf[0]); + memcpy(buf, &rx_buf[2], rx_len); + + mdelay(WDT_COMMAND_DELAY_MS); + + return 0; +} + +static int wdt87xx_get_feature(struct i2c_client *client, + u8 *buf, size_t buf_size) +{ + u8 tx_buf[8]; + u8 rx_buf[PKT_WRITE_SIZE]; + size_t tx_len = 0; + size_t rx_len = buf_size + 2; + int error; + + if (rx_len > sizeof(rx_buf)) + return -EINVAL; + + /* Get feature command packet */ + tx_buf[tx_len++] = 0x22; + tx_buf[tx_len++] = 0x00; + if (buf[CMD_REPORT_ID_OFFSET] > 0xF) { + tx_buf[tx_len++] = 0x30; + tx_buf[tx_len++] = 0x02; + tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET]; + } else { + tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET]; + tx_buf[tx_len++] = 0x02; + } + tx_buf[tx_len++] = 0x23; + tx_buf[tx_len++] = 0x00; + + error = wdt87xx_i2c_xfer(client, tx_buf, tx_len, rx_buf, rx_len); + if (error) { + dev_err(&client->dev, "get feature failed: %d\n", error); + return error; + } + + rx_len = min_t(size_t, buf_size, get_unaligned_le16(rx_buf)); + memcpy(buf, &rx_buf[2], rx_len); + + mdelay(WDT_COMMAND_DELAY_MS); + + return 0; +} + +static int wdt87xx_set_feature(struct i2c_client *client, + const u8 *buf, size_t buf_size) +{ + u8 tx_buf[PKT_WRITE_SIZE]; + int tx_len = 0; + int error; + + /* Set feature command packet */ + tx_buf[tx_len++] = 0x22; + tx_buf[tx_len++] = 0x00; + if (buf[CMD_REPORT_ID_OFFSET] > 0xF) { + tx_buf[tx_len++] = 0x30; + tx_buf[tx_len++] = 0x03; + tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET]; + } else { + tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET]; + tx_buf[tx_len++] = 0x03; + } + tx_buf[tx_len++] = 0x23; + tx_buf[tx_len++] = 0x00; + tx_buf[tx_len++] = (buf_size & 0xFF); + tx_buf[tx_len++] = ((buf_size & 0xFF00) >> 8); + + if (tx_len + buf_size > sizeof(tx_buf)) + return -EINVAL; + + memcpy(&tx_buf[tx_len], buf, buf_size); + tx_len += buf_size; + + error = i2c_master_send(client, tx_buf, tx_len); + if (error < 0) { + dev_err(&client->dev, "set feature failed: %d\n", error); + return error; + } + + mdelay(WDT_COMMAND_DELAY_MS); + + return 0; +} + +static int wdt87xx_send_command(struct i2c_client *client, int cmd, int value) +{ + u8 cmd_buf[CMD_BUF_SIZE]; + + /* Set the command packet */ + cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; + cmd_buf[CMD_TYPE_OFFSET] = VND_SET_COMMAND_DATA; + put_unaligned_le16((u16)cmd, &cmd_buf[CMD_INDEX_OFFSET]); + + switch (cmd) { + case VND_CMD_START: + case VND_CMD_STOP: + case VND_CMD_RESET: + /* Mode selector */ + put_unaligned_le32((value & 0xFF), &cmd_buf[CMD_LENGTH_OFFSET]); + break; + + case VND_CMD_SFLCK: + put_unaligned_le16(CMD_SFLCK_KEY, &cmd_buf[CMD_KEY_OFFSET]); + break; + + case VND_CMD_SFUNL: + put_unaligned_le16(CMD_SFUNL_KEY, &cmd_buf[CMD_KEY_OFFSET]); + break; + + case VND_CMD_ERASE: + case VND_SET_CHECKSUM_CALC: + case VND_SET_CHECKSUM_LENGTH: + put_unaligned_le32(value, &cmd_buf[CMD_KEY_OFFSET]); + break; + + default: + cmd_buf[CMD_REPORT_ID_OFFSET] = 0; + dev_err(&client->dev, "Invalid command: %d\n", cmd); + return -EINVAL; + } + + return wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf)); +} + +static int wdt87xx_sw_reset(struct i2c_client *client) +{ + int error; + + dev_dbg(&client->dev, "resetting device now\n"); + + error = wdt87xx_send_command(client, VND_CMD_RESET, 0); + if (error) { + dev_err(&client->dev, "reset failed\n"); + return error; + } + + /* Wait the device to be ready */ + msleep(200); + + return 0; +} + +static const void *wdt87xx_get_fw_chunk(const struct firmware *fw, u32 id) +{ + size_t pos = FW_PAYLOAD_OFFSET; + u32 chunk_id, chunk_size; + + while (pos < fw->size) { + chunk_id = get_unaligned_le32(fw->data + + pos + FW_CHUNK_ID_OFFSET); + if (chunk_id == id) + return fw->data + pos; + + chunk_size = get_unaligned_le32(fw->data + + pos + FW_CHUNK_SIZE_OFFSET); + pos += chunk_size + 2 * sizeof(u32); /* chunk ID + size */ + } + + return NULL; +} + +static int wdt87xx_get_sysparam(struct i2c_client *client, + struct wdt87xx_sys_param *param) +{ + u8 buf[PKT_READ_SIZE]; + int error; + + error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34); + if (error) { + dev_err(&client->dev, "failed to get parameters\n"); + return error; + } + + param->xmls_id1 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID1); + param->xmls_id2 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID2); + param->phy_ch_x = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_X); + param->phy_ch_y = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_Y); + param->phy_w = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_W) / 10; + param->phy_h = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_H) / 10; + + /* Get the scaling factor of pixel to logical coordinate */ + param->scaling_factor = + get_unaligned_le16(buf + CTL_PARAM_OFFSET_FACTOR); + + param->max_x = MAX_UNIT_AXIS; + param->max_y = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS * param->phy_h, + param->phy_w); + + error = wdt87xx_get_string(client, STRIDX_PLATFORM_ID, buf, 8); + if (error) { + dev_err(&client->dev, "failed to get platform id\n"); + return error; + } + + param->plat_id = buf[1]; + + buf[0] = 0xf2; + error = wdt87xx_get_feature(client, buf, 16); + if (error) { + dev_err(&client->dev, "failed to get firmware id\n"); + return error; + } + + if (buf[0] != 0xf2) { + dev_err(&client->dev, "wrong id of fw response: 0x%x\n", + buf[0]); + return -EINVAL; + } + + param->fw_id = get_unaligned_le16(&buf[1]); + + dev_info(&client->dev, + "fw_id: 0x%x, plat_id: 0x%x, xml_id1: %04x, xml_id2: %04x\n", + param->fw_id, param->plat_id, + param->xmls_id1, param->xmls_id2); + + return 0; +} + +static int wdt87xx_validate_firmware(struct wdt87xx_data *wdt, + const struct firmware *fw) +{ + const void *fw_chunk; + u32 data1, data2; + u32 size; + u8 fw_chip_id; + u8 chip_id; + + data1 = get_unaligned_le32(fw->data + FW_FOURCC1_OFFSET); + data2 = get_unaligned_le32(fw->data + FW_FOURCC2_OFFSET); + if (data1 != FOURCC_ID_RIFF || data2 != FOURCC_ID_WHIF) { + dev_err(&wdt->client->dev, "check fw tag failed\n"); + return -EINVAL; + } + + size = get_unaligned_le32(fw->data + FW_SIZE_OFFSET); + if (size != fw->size) { + dev_err(&wdt->client->dev, + "fw size mismatch: expected %d, actual %zu\n", + size, fw->size); + return -EINVAL; + } + + /* + * Get the chip_id from the firmware. Make sure that it is the + * right controller to do the firmware and config update. + */ + fw_chunk = wdt87xx_get_fw_chunk(fw, CHUNK_ID_FRWR); + if (!fw_chunk) { + dev_err(&wdt->client->dev, + "unable to locate firmware chunk\n"); + return -EINVAL; + } + + fw_chip_id = (get_unaligned_le32(fw_chunk + + FW_CHUNK_VERSION_OFFSET) >> 12) & 0xF; + chip_id = (wdt->param.fw_id >> 12) & 0xF; + + if (fw_chip_id != chip_id) { + dev_err(&wdt->client->dev, + "fw version mismatch: fw %d vs. chip %d\n", + fw_chip_id, chip_id); + return -ENODEV; + } + + return 0; +} + +static int wdt87xx_validate_fw_chunk(const void *data, int id) +{ + if (id == CHUNK_ID_FRWR) { + u32 fw_id; + + fw_id = get_unaligned_le32(data + FW_CHUNK_PAYLOAD_OFFSET); + if (fw_id != WDT_FIRMWARE_ID) + return -EINVAL; + } + + return 0; +} + +static int wdt87xx_write_data(struct i2c_client *client, const char *data, + u32 address, int length) +{ + u16 packet_size; + int count = 0; + int error; + u8 pkt_buf[PKT_BUF_SIZE]; + + /* Address and length should be 4 bytes aligned */ + if ((address & 0x3) != 0 || (length & 0x3) != 0) { + dev_err(&client->dev, + "addr & len must be 4 bytes aligned %x, %x\n", + address, length); + return -EINVAL; + } + + while (length) { + packet_size = min(length, PACKET_SIZE); + + pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; + pkt_buf[CMD_TYPE_OFFSET] = VND_SET_DATA; + put_unaligned_le16(packet_size, &pkt_buf[CMD_INDEX_OFFSET]); + put_unaligned_le32(address, &pkt_buf[CMD_LENGTH_OFFSET]); + memcpy(&pkt_buf[CMD_DATA_OFFSET], data, packet_size); + + error = wdt87xx_set_feature(client, pkt_buf, sizeof(pkt_buf)); + if (error) + return error; + + length -= packet_size; + data += packet_size; + address += packet_size; + + /* Wait for the controller to finish the write */ + mdelay(WDT_FLASH_WRITE_DELAY_MS); + + if ((++count % 32) == 0) { + /* Delay for fw to clear watch dog */ + msleep(20); + } + } + + return 0; +} + +static u16 misr(u16 cur_value, u8 new_value) +{ + u32 a, b; + u32 bit0; + u32 y; + + a = cur_value; + b = new_value; + bit0 = a ^ (b & 1); + bit0 ^= a >> 1; + bit0 ^= a >> 2; + bit0 ^= a >> 4; + bit0 ^= a >> 5; + bit0 ^= a >> 7; + bit0 ^= a >> 11; + bit0 ^= a >> 15; + y = (a << 1) ^ b; + y = (y & ~1) | (bit0 & 1); + + return (u16)y; +} + +static u16 wdt87xx_calculate_checksum(const u8 *data, size_t length) +{ + u16 checksum = 0; + size_t i; + + for (i = 0; i < length; i++) + checksum = misr(checksum, data[i]); + + return checksum; +} + +static int wdt87xx_get_checksum(struct i2c_client *client, u16 *checksum, + u32 address, int length) +{ + int error; + int time_delay; + u8 pkt_buf[PKT_BUF_SIZE]; + u8 cmd_buf[CMD_BUF_SIZE]; + + error = wdt87xx_send_command(client, VND_SET_CHECKSUM_LENGTH, length); + if (error) { + dev_err(&client->dev, "failed to set checksum length\n"); + return error; + } + + error = wdt87xx_send_command(client, VND_SET_CHECKSUM_CALC, address); + if (error) { + dev_err(&client->dev, "failed to set checksum address\n"); + return error; + } + + /* Wait the operation to complete */ + time_delay = DIV_ROUND_UP(length, 1024); + msleep(time_delay * 30); + + memset(cmd_buf, 0, sizeof(cmd_buf)); + cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_READ; + cmd_buf[CMD_TYPE_OFFSET] = VND_GET_CHECKSUM; + error = wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf)); + if (error) { + dev_err(&client->dev, "failed to request checksum\n"); + return error; + } + + memset(pkt_buf, 0, sizeof(pkt_buf)); + pkt_buf[CMD_REPORT_ID_OFFSET] = VND_READ_DATA; + error = wdt87xx_get_feature(client, pkt_buf, sizeof(pkt_buf)); + if (error) { + dev_err(&client->dev, "failed to read checksum\n"); + return error; + } + + *checksum = get_unaligned_le16(&pkt_buf[CMD_DATA_OFFSET]); + return 0; +} + +static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) +{ + u32 start_addr = get_unaligned_le32(chunk + FW_CHUNK_TGT_START_OFFSET); + u32 size = get_unaligned_le32(chunk + FW_CHUNK_PAYLOAD_LEN_OFFSET); + const void *data = chunk + FW_CHUNK_PAYLOAD_OFFSET; + int error; + int err1; + int page_size; + int retry = 0; + u16 device_checksum, firmware_checksum; + + dev_dbg(&client->dev, "start 4k page program\n"); + + error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_STOP); + if (error) { + dev_err(&client->dev, "stop report mode failed\n"); + return error; + } + + error = wdt87xx_send_command(client, VND_CMD_SFUNL, 0); + if (error) { + dev_err(&client->dev, "unlock failed\n"); + goto out_enable_reporting; + } + + mdelay(10); + + while (size) { + dev_dbg(&client->dev, "%s: %x, %x\n", __func__, + start_addr, size); + + page_size = min_t(u32, size, PG_SIZE); + size -= page_size; + + for (retry = 0; retry < MAX_RETRIES; retry++) { + error = wdt87xx_send_command(client, VND_CMD_ERASE, + start_addr); + if (error) { + dev_err(&client->dev, + "erase failed at %#08x\n", start_addr); + break; + } + + msleep(50); + + error = wdt87xx_write_data(client, data, start_addr, + page_size); + if (error) { + dev_err(&client->dev, + "write failed at %#08x (%d bytes)\n", + start_addr, page_size); + break; + } + + error = wdt87xx_get_checksum(client, &device_checksum, + start_addr, page_size); + if (error) { + dev_err(&client->dev, + "failed to retrieve checksum for %#08x (len: %d)\n", + start_addr, page_size); + break; + } + + firmware_checksum = + wdt87xx_calculate_checksum(data, page_size); + + if (device_checksum == firmware_checksum) + break; + + dev_err(&client->dev, + "checksum fail: %d vs %d, retry %d\n", + device_checksum, firmware_checksum, retry); + } + + if (retry == MAX_RETRIES) { + dev_err(&client->dev, "page write failed\n"); + error = -EIO; + goto out_lock_device; + } + + start_addr = start_addr + page_size; + data = data + page_size; + } + +out_lock_device: + err1 = wdt87xx_send_command(client, VND_CMD_SFLCK, 0); + if (err1) + dev_err(&client->dev, "lock failed\n"); + + mdelay(10); + +out_enable_reporting: + err1 = wdt87xx_send_command(client, VND_CMD_START, 0); + if (err1) + dev_err(&client->dev, "start to report failed\n"); + + return error ? error : err1; +} + +static int wdt87xx_load_chunk(struct i2c_client *client, + const struct firmware *fw, u32 ck_id) +{ + const void *chunk; + int error; + + chunk = wdt87xx_get_fw_chunk(fw, ck_id); + if (!chunk) { + dev_err(&client->dev, "unable to locate chunk (type %d)\n", + ck_id); + return -EINVAL; + } + + error = wdt87xx_validate_fw_chunk(chunk, ck_id); + if (error) { + dev_err(&client->dev, "invalid chunk (type %d): %d\n", + ck_id, error); + return error; + } + + error = wdt87xx_write_firmware(client, chunk); + if (error) { + dev_err(&client->dev, + "failed to write fw chunk (type %d): %d\n", + ck_id, error); + return error; + } + + return 0; +} + +static int wdt87xx_do_update_firmware(struct i2c_client *client, + const struct firmware *fw, + unsigned int chunk_id) +{ + struct wdt87xx_data *wdt = i2c_get_clientdata(client); + int error; + + error = wdt87xx_validate_firmware(wdt, fw); + if (error) + return error; + + error = mutex_lock_interruptible(&wdt->fw_mutex); + if (error) + return error; + + disable_irq(client->irq); + + error = wdt87xx_load_chunk(client, fw, chunk_id); + if (error) { + dev_err(&client->dev, + "firmware load failed (type: %d): %d\n", + chunk_id, error); + goto out; + } + + error = wdt87xx_sw_reset(client); + if (error) { + dev_err(&client->dev, "soft reset failed: %d\n", error); + goto out; + } + + /* Refresh the parameters */ + error = wdt87xx_get_sysparam(client, &wdt->param); + if (error) + dev_err(&client->dev, + "failed to refresh system paramaters: %d\n", error); +out: + enable_irq(client->irq); + mutex_unlock(&wdt->fw_mutex); + + return error ? error : 0; +} + +static int wdt87xx_update_firmware(struct device *dev, + const char *fw_name, unsigned int chunk_id) +{ + struct i2c_client *client = to_i2c_client(dev); + const struct firmware *fw; + int error; + + error = reject_firmware(&fw, fw_name, dev); + if (error) { + dev_err(&client->dev, "unable to retrieve firmware %s: %d\n", + fw_name, error); + return error; + } + + error = wdt87xx_do_update_firmware(client, fw, chunk_id); + + release_firmware(fw); + + return error ? error : 0; +} + +static ssize_t config_csum_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct wdt87xx_data *wdt = i2c_get_clientdata(client); + u32 cfg_csum; + + cfg_csum = wdt->param.xmls_id1; + cfg_csum = (cfg_csum << 16) | wdt->param.xmls_id2; + + return scnprintf(buf, PAGE_SIZE, "%x\n", cfg_csum); +} + +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct wdt87xx_data *wdt = i2c_get_clientdata(client); + + return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.fw_id); +} + +static ssize_t plat_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct wdt87xx_data *wdt = i2c_get_clientdata(client); + + return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.plat_id); +} + +static ssize_t update_config_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int error; + + error = wdt87xx_update_firmware(dev, WDT87XX_CFG_NAME, CHUNK_ID_CNFG); + + return error ? error : count; +} + +static ssize_t update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int error; + + error = wdt87xx_update_firmware(dev, WDT87XX_FW_NAME, CHUNK_ID_FRWR); + + return error ? error : count; +} + +static DEVICE_ATTR_RO(config_csum); +static DEVICE_ATTR_RO(fw_version); +static DEVICE_ATTR_RO(plat_id); +static DEVICE_ATTR_WO(update_config); +static DEVICE_ATTR_WO(update_fw); + +static struct attribute *wdt87xx_attrs[] = { + &dev_attr_config_csum.attr, + &dev_attr_fw_version.attr, + &dev_attr_plat_id.attr, + &dev_attr_update_config.attr, + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group wdt87xx_attr_group = { + .attrs = wdt87xx_attrs, +}; + +static void wdt87xx_report_contact(struct input_dev *input, + struct wdt87xx_sys_param *param, + u8 *buf) +{ + int finger_id; + u32 x, y, w; + u8 p; + + finger_id = (buf[FINGER_EV_V1_OFFSET_ID] >> 3) - 1; + if (finger_id < 0) + return; + + /* Check if this is an active contact */ + if (!(buf[FINGER_EV_V1_OFFSET_ID] & 0x1)) + return; + + w = buf[FINGER_EV_V1_OFFSET_W]; + w *= param->scaling_factor; + + p = buf[FINGER_EV_V1_OFFSET_P]; + + x = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_X); + + y = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_Y); + y = DIV_ROUND_CLOSEST(y * param->phy_h, param->phy_w); + + /* Refuse incorrect coordinates */ + if (x > param->max_x || y > param->max_y) + return; + + dev_dbg(input->dev.parent, "tip on (%d), x(%d), y(%d)\n", + finger_id, x, y); + + input_mt_slot(input, finger_id); + input_mt_report_slot_state(input, MT_TOOL_FINGER, 1); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, w); + input_report_abs(input, ABS_MT_PRESSURE, p); + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); +} + +static irqreturn_t wdt87xx_ts_interrupt(int irq, void *dev_id) +{ + struct wdt87xx_data *wdt = dev_id; + struct i2c_client *client = wdt->client; + int i, fingers; + int error; + u8 raw_buf[WDT_V1_RAW_BUF_COUNT] = {0}; + + error = i2c_master_recv(client, raw_buf, WDT_V1_RAW_BUF_COUNT); + if (error < 0) { + dev_err(&client->dev, "read v1 raw data failed: %d\n", error); + goto irq_exit; + } + + fingers = raw_buf[TOUCH_PK_V1_OFFSET_FNGR_NUM]; + if (!fingers) + goto irq_exit; + + for (i = 0; i < WDT_MAX_FINGER; i++) + wdt87xx_report_contact(wdt->input, + &wdt->param, + &raw_buf[TOUCH_PK_V1_OFFSET_EVENT + + i * FINGER_EV_V1_SIZE]); + + input_mt_sync_frame(wdt->input); + input_sync(wdt->input); + +irq_exit: + return IRQ_HANDLED; +} + +static int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt) +{ + struct device *dev = &wdt->client->dev; + struct input_dev *input; + unsigned int res = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS, wdt->param.phy_w); + int error; + + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + wdt->input = input; + + input->name = "WDT87xx Touchscreen"; + input->id.bustype = BUS_I2C; + input->phys = wdt->phys; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + wdt->param.max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + wdt->param.max_y, 0, 0); + input_abs_set_res(input, ABS_MT_POSITION_X, res); + input_abs_set_res(input, ABS_MT_POSITION_Y, res); + + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, + 0, wdt->param.max_x, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); + + input_mt_init_slots(input, WDT_MAX_FINGER, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + + error = input_register_device(input); + if (error) { + dev_err(dev, "failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +static int wdt87xx_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wdt87xx_data *wdt; + int error; + + dev_dbg(&client->dev, "adapter=%d, client irq: %d\n", + client->adapter->nr, client->irq); + + /* Check if the I2C function is ok in this adaptor */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENXIO; + + wdt = devm_kzalloc(&client->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->client = client; + mutex_init(&wdt->fw_mutex); + i2c_set_clientdata(client, wdt); + + snprintf(wdt->phys, sizeof(wdt->phys), "i2c-%u-%04x/input0", + client->adapter->nr, client->addr); + + error = wdt87xx_get_sysparam(client, &wdt->param); + if (error) + return error; + + error = wdt87xx_ts_create_input_device(wdt); + if (error) + return error; + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, wdt87xx_ts_interrupt, + IRQF_ONESHOT, + client->name, wdt); + if (error) { + dev_err(&client->dev, "request irq failed: %d\n", error); + return error; + } + + error = sysfs_create_group(&client->dev.kobj, &wdt87xx_attr_group); + if (error) { + dev_err(&client->dev, "create sysfs failed: %d\n", error); + return error; + } + + return 0; +} + +static int wdt87xx_ts_remove(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &wdt87xx_attr_group); + + return 0; +} + +static int __maybe_unused wdt87xx_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int error; + + disable_irq(client->irq); + + error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_IDLE); + if (error) { + enable_irq(client->irq); + dev_err(&client->dev, + "failed to stop device when suspending: %d\n", + error); + return error; + } + + return 0; +} + +static int __maybe_unused wdt87xx_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int error; + + /* + * The chip may have been reset while system is resuming, + * give it some time to settle. + */ + mdelay(100); + + error = wdt87xx_send_command(client, VND_CMD_START, 0); + if (error) + dev_err(&client->dev, + "failed to start device when resuming: %d\n", + error); + + enable_irq(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(wdt87xx_pm_ops, wdt87xx_suspend, wdt87xx_resume); + +static const struct i2c_device_id wdt87xx_dev_id[] = { + { WDT87XX_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wdt87xx_dev_id); + +static const struct acpi_device_id wdt87xx_acpi_id[] = { + { "WDHT0001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, wdt87xx_acpi_id); + +static struct i2c_driver wdt87xx_driver = { + .probe = wdt87xx_ts_probe, + .remove = wdt87xx_ts_remove, + .id_table = wdt87xx_dev_id, + .driver = { + .name = WDT87XX_NAME, + .pm = &wdt87xx_pm_ops, + .acpi_match_table = ACPI_PTR(wdt87xx_acpi_id), + }, +}; +module_i2c_driver(wdt87xx_driver); + +MODULE_AUTHOR("HN Chen <hn.chen@weidahitech.com>"); +MODULE_DESCRIPTION("WeidaHiTech WDT87XX Touchscreen driver"); +MODULE_VERSION(WDT87XX_DRV_VER); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 19880c738..f58a19652 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -30,7 +30,6 @@ #include <linux/input/mt.h> #include <linux/platform_data/zforce_ts.h> #include <linux/regulator/consumer.h> -#include <linux/delay.h> #include <linux/of.h> #include <linux/of_gpio.h> |