diff options
Diffstat (limited to 'drivers')
39 files changed, 1211 insertions, 536 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 509608c95994..cbd75cf44739 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD if INPUT_KEYBOARD +config KEYBOARD_ADC + tristate "ADC Ladder Buttons" + depends on IIO + select INPUT_POLLDEV + help + This driver implements support for buttons connected + to an ADC using a resistor ladder. + + Say Y here if your device has such buttons connected to an ADC. Your + board-specific setup logic must also provide a configuration data + for mapping voltages to buttons. + + To compile this driver as a module, choose M here: the + module will be called adc_keys. + config KEYBOARD_ADP5520 tristate "Keypad Support for ADP5520 PMIC" depends on PMIC_ADP5520 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 1d416ddf84e4..d9f4cfcf3410 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -4,6 +4,7 @@ # Each configuration option enables a list of files. +obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c new file mode 100644 index 000000000000..f8cf2ccacefd --- /dev/null +++ b/drivers/input/keyboard/adc-keys.c @@ -0,0 +1,210 @@ +/* + * Input driver for resistor ladder connected on ADC + * + * Copyright (c) 2016 Alexandre Belloni + * + * 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/err.h> +#include <linux/iio/consumer.h> +#include <linux/iio/types.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> + +struct adc_keys_button { + u32 voltage; + u32 keycode; +}; + +struct adc_keys_state { + struct iio_channel *channel; + u32 num_keys; + u32 last_key; + u32 keyup_voltage; + const struct adc_keys_button *map; +}; + +static void adc_keys_poll(struct input_polled_dev *dev) +{ + struct adc_keys_state *st = dev->private; + int i, value, ret; + u32 diff, closest = 0xffffffff; + int keycode = 0; + + ret = iio_read_channel_processed(st->channel, &value); + if (unlikely(ret < 0)) { + /* Forcibly release key if any was pressed */ + value = st->keyup_voltage; + } else { + for (i = 0; i < st->num_keys; i++) { + diff = abs(st->map[i].voltage - value); + if (diff < closest) { + closest = diff; + keycode = st->map[i].keycode; + } + } + } + + if (abs(st->keyup_voltage - value) < closest) + keycode = 0; + + if (st->last_key && st->last_key != keycode) + input_report_key(dev->input, st->last_key, 0); + + if (keycode) + input_report_key(dev->input, keycode, 1); + + input_sync(dev->input); + st->last_key = keycode; +} + +static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st) +{ + struct adc_keys_button *map; + struct fwnode_handle *child; + int i; + + st->num_keys = device_get_child_node_count(dev); + if (st->num_keys == 0) { + dev_err(dev, "keymap is missing\n"); + return -EINVAL; + } + + map = devm_kmalloc_array(dev, st->num_keys, sizeof(*map), GFP_KERNEL); + if (!map) + return -ENOMEM; + + i = 0; + device_for_each_child_node(dev, child) { + if (fwnode_property_read_u32(child, "press-threshold-microvolt", + &map[i].voltage)) { + dev_err(dev, "Key with invalid or missing voltage\n"); + fwnode_handle_put(child); + return -EINVAL; + } + map[i].voltage /= 1000; + + if (fwnode_property_read_u32(child, "linux,code", + &map[i].keycode)) { + dev_err(dev, "Key with invalid or missing linux,code\n"); + fwnode_handle_put(child); + return -EINVAL; + } + + i++; + } + + st->map = map; + return 0; +} + +static int adc_keys_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct adc_keys_state *st; + struct input_polled_dev *poll_dev; + struct input_dev *input; + enum iio_chan_type type; + int i, value; + int error; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->channel = devm_iio_channel_get(dev, "buttons"); + if (IS_ERR(st->channel)) + return PTR_ERR(st->channel); + + if (!st->channel->indio_dev) + return -ENXIO; + + error = iio_get_channel_type(st->channel, &type); + if (error < 0) + return error; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "Incompatible channel type %d\n", type); + return -EINVAL; + } + + if (device_property_read_u32(dev, "keyup-threshold-microvolt", + &st->keyup_voltage)) { + dev_err(dev, "Invalid or missing keyup voltage\n"); + return -EINVAL; + } + st->keyup_voltage /= 1000; + + error = adc_keys_load_keymap(dev, st); + if (error) + return error; + + platform_set_drvdata(pdev, st); + + poll_dev = devm_input_allocate_polled_device(dev); + if (!poll_dev) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + if (!device_property_read_u32(dev, "poll-interval", &value)) + poll_dev->poll_interval = value; + + poll_dev->poll = adc_keys_poll; + poll_dev->private = st; + + input = poll_dev->input; + + input->name = pdev->name; + input->phys = "adc-keys/input0"; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + __set_bit(EV_KEY, input->evbit); + for (i = 0; i < st->num_keys; i++) + __set_bit(st->map[i].keycode, input->keybit); + + if (device_property_read_bool(dev, "autorepeat")) + __set_bit(EV_REP, input->evbit); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "Unable to register input device: %d\n", error); + return error; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id adc_keys_of_match[] = { + { .compatible = "adc-keys", }, + { } +}; +MODULE_DEVICE_TABLE(of, adc_keys_of_match); +#endif + +static struct platform_driver __refdata adc_keys_driver = { + .driver = { + .name = "adc_keys", + .of_match_table = of_match_ptr(adc_keys_of_match), + }, + .probe = adc_keys_probe, +}; +module_platform_driver(adc_keys_driver); + +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); +MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index 421d9c55b0e8..1277c39f9482 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -25,8 +25,6 @@ #include <linux/slab.h> #include <mach/jornada720.h> -#include <mach/hardware.h> -#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); @@ -66,10 +64,8 @@ static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id) jornada_ssp_start(); if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) { - printk(KERN_DEBUG - "jornada720_kbd: " - "GetKeycode command failed with ETIMEDOUT, " - "flushed bus\n"); + dev_dbg(&pdev->dev, + "GetKeycode command failed with ETIMEDOUT, flushed bus\n"); } else { /* How many keycodes are waiting for us? */ count = jornada_ssp_byte(TXDUMMY); @@ -97,14 +93,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev) { struct jornadakbd *jornadakbd; struct input_dev *input_dev; - int i, err; + int i, err, irq; - jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!jornadakbd || !input_dev) { - err = -ENOMEM; - goto fail1; - } + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return irq < 0 ? irq : -EINVAL; + + jornadakbd = devm_kzalloc(&pdev->dev, sizeof(*jornadakbd), GFP_KERNEL); + input_dev = devm_input_allocate_device(&pdev->dev); + if (!jornadakbd || !input_dev) + return -ENOMEM; platform_set_drvdata(pdev, jornadakbd); @@ -127,40 +125,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev) input_set_capability(input_dev, EV_MSC, MSC_SCAN); - err = request_irq(IRQ_GPIO0, - jornada720_kbd_interrupt, - IRQF_TRIGGER_FALLING, - "jornadakbd", pdev); + err = devm_request_irq(&pdev->dev, irq, jornada720_kbd_interrupt, + IRQF_TRIGGER_FALLING, "jornadakbd", pdev); if (err) { - printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n"); - goto fail1; + dev_err(&pdev->dev, "unable to grab IRQ%d: %d\n", irq, err); + return err; } - err = input_register_device(jornadakbd->input); - if (err) - goto fail2; - - return 0; - - fail2: /* IRQ, DEVICE, MEMORY */ - free_irq(IRQ_GPIO0, pdev); - fail1: /* DEVICE, MEMORY */ - input_free_device(input_dev); - kfree(jornadakbd); - return err; + return input_register_device(jornadakbd->input); }; -static int jornada720_kbd_remove(struct platform_device *pdev) -{ - struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); - - free_irq(IRQ_GPIO0, pdev); - input_unregister_device(jornadakbd->input); - kfree(jornadakbd); - - return 0; -} - /* work with hotplug and coldplug */ MODULE_ALIAS("platform:jornada720_kbd"); @@ -169,6 +143,5 @@ static struct platform_driver jornada720_kbd_driver = { .name = "jornada720_kbd", }, .probe = jornada720_kbd_probe, - .remove = jornada720_kbd_remove, }; module_platform_driver(jornada720_kbd_driver); diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c index 24a9f599082f..7544888c4749 100644 --- a/drivers/input/keyboard/snvs_pwrkey.c +++ b/drivers/input/keyboard/snvs_pwrkey.c @@ -168,7 +168,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) error = input_register_device(input); if (error < 0) { dev_err(&pdev->dev, "failed to register input device\n"); - input_free_device(input); return error; } diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index efb0ca871327..7ffb614ce566 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -292,6 +292,18 @@ config INPUT_GPIO_TILT_POLLED To compile this driver as a module, choose M here: the module will be called gpio_tilt_polled. +config INPUT_GPIO_DECODER + tristate "Polled GPIO Decoder Input driver" + depends on GPIOLIB || COMPILE_TEST + select INPUT_POLLDEV + help + Say Y here if you want driver to read status of multiple GPIO + lines and report the encoded value as an absolute integer to + input subsystem. + + To compile this driver as a module, choose M here: the module + will be called gpio_decoder. + config INPUT_IXP4XX_BEEPER tristate "IXP4XX Beeper support" depends on ARCH_IXP4XX @@ -454,10 +466,10 @@ config INPUT_RETU_PWRBUTTON config INPUT_TPS65218_PWRBUTTON tristate "TPS65218 Power button driver" - depends on MFD_TPS65218 + depends on (MFD_TPS65217 || MFD_TPS65218) help Say Y here if you want to enable power buttong reporting for - the TPS65218 Power Management IC device. + TPS65217 and TPS65218 Power Management IC devices. To compile this driver as a module, choose M here. The module will be called tps65218-pwrbutton. diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 6a1e5e20fc1c..0b6d025f0487 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o +obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o diff --git a/drivers/input/misc/gpio_decoder.c b/drivers/input/misc/gpio_decoder.c new file mode 100644 index 000000000000..ca7e0bacb2d8 --- /dev/null +++ b/drivers/input/misc/gpio_decoder.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * A generic driver to read multiple gpio lines and translate the + * encoded numeric value into an input event. + */ + +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +struct gpio_decoder { + struct input_polled_dev *poll_dev; + struct gpio_descs *input_gpios; + struct device *dev; + u32 axis; + u32 last_stable; +}; + +static int gpio_decoder_get_gpios_state(struct gpio_decoder *decoder) +{ + struct gpio_descs *gpios = decoder->input_gpios; + unsigned int ret = 0; + int i, val; + + for (i = 0; i < gpios->ndescs; i++) { + val = gpiod_get_value_cansleep(gpios->desc[i]); + if (val < 0) { + dev_err(decoder->dev, + "Error reading gpio %d: %d\n", + desc_to_gpio(gpios->desc[i]), val); + return val; + } + + val = !!val; + ret = (ret << 1) | val; + } + + return ret; +} + +static void gpio_decoder_poll_gpios(struct input_polled_dev *poll_dev) +{ + struct gpio_decoder *decoder = poll_dev->private; + int state; + + state = gpio_decoder_get_gpios_state(decoder); + if (state >= 0 && state != decoder->last_stable) { + input_report_abs(poll_dev->input, decoder->axis, state); + input_sync(poll_dev->input); + decoder->last_stable = state; + } +} + +static int gpio_decoder_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_decoder *decoder; + struct input_polled_dev *poll_dev; + u32 max; + int err; + + decoder = devm_kzalloc(dev, sizeof(struct gpio_decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + device_property_read_u32(dev, "linux,axis", &decoder->axis); + decoder->input_gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); + if (IS_ERR(decoder->input_gpios)) { + dev_err(dev, "unable to acquire input gpios\n"); + return PTR_ERR(decoder->input_gpios); + } + if (decoder->input_gpios->ndescs < 2) { + dev_err(dev, "not enough gpios found\n"); + return -EINVAL; + } + + if (device_property_read_u32(dev, "decoder-max-value", &max)) + max = (1U << decoder->input_gpios->ndescs) - 1; + + decoder->dev = dev; + poll_dev = devm_input_allocate_polled_device(decoder->dev); + if (!poll_dev) + return -ENOMEM; + + poll_dev->private = decoder; + poll_dev->poll = gpio_decoder_poll_gpios; + decoder->poll_dev = poll_dev; + + poll_dev->input->name = pdev->name; + poll_dev->input->id.bustype = BUS_HOST; + input_set_abs_params(poll_dev->input, decoder->axis, 0, max, 0, 0); + + err = input_register_polled_device(poll_dev); + if (err) { + dev_err(dev, "failed to register polled device\n"); + return err; + } + platform_set_drvdata(pdev, decoder); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id gpio_decoder_of_match[] = { + { .compatible = "gpio-decoder", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_decoder_of_match); +#endif + +static struct platform_driver gpio_decoder_driver = { + .probe = gpio_decoder_probe, + .driver = { + .name = "gpio-decoder", + .of_match_table = of_match_ptr(gpio_decoder_of_match), + } +}; +module_platform_driver(gpio_decoder_driver); + +MODULE_DESCRIPTION("GPIO decoder input driver"); +MODULE_AUTHOR("Vignesh R <vigneshr@ti.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c index 29ddeb7be84b..46b0f48fbf49 100644 --- a/drivers/input/misc/max77693-haptic.c +++ b/drivers/input/misc/max77693-haptic.c @@ -3,7 +3,7 @@ * * Copyright (C) 2014,2015 Samsung Electronics * Jaewon Kim <jaewon02.kim@samsung.com> - * Krzysztof Kozlowski <k.kozlowski@samsung.com> + * Krzysztof Kozlowski <krzk@kernel.org> * * This program is not provided / owned by Maxim Integrated Products. * @@ -426,7 +426,7 @@ static struct platform_driver max77693_haptic_driver = { module_platform_driver(max77693_haptic_driver); MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); -MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>"); +MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver"); MODULE_ALIAS("platform:max77693-haptic"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index a39b62651a4b..3273217ce80c 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -1,8 +1,9 @@ /* - * Texas Instruments' TPS65218 Power Button Input Driver + * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver * * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * Author: Felipe Balbi <balbi@ti.com> + * Author: Marcin Niestroj <m.niestroj@grinn-global.com> * * 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 @@ -18,31 +19,61 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/kernel.h> +#include <linux/mfd/tps65217.h> #include <linux/mfd/tps65218.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/slab.h> -struct tps65218_pwrbutton { +struct tps6521x_data { + unsigned int reg_status; + unsigned int pb_mask; + const char *name; +}; + +static const struct tps6521x_data tps65217_data = { + .reg_status = TPS65217_REG_STATUS, + .pb_mask = TPS65217_STATUS_PB, + .name = "tps65217_pwrbutton", +}; + +static const struct tps6521x_data tps65218_data = { + .reg_status = TPS65218_REG_STATUS, + .pb_mask = TPS65218_STATUS_PB_STATE, + .name = "tps65218_pwrbutton", +}; + +struct tps6521x_pwrbutton { struct device *dev; - struct tps65218 *tps; + struct regmap *regmap; struct input_dev *idev; + const struct tps6521x_data *data; + char phys[32]; +}; + +static const struct of_device_id of_tps6521x_pb_match[] = { + { .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data }, + { .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data }, + { }, }; +MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match); -static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr) +static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr) { - struct tps65218_pwrbutton *pwr = _pwr; + struct tps6521x_pwrbutton *pwr = _pwr; + const struct tps6521x_data *tps_data = pwr->data; unsigned int reg; int error; - error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, ®); + error = regmap_read(pwr->regmap, tps_data->reg_status, ®); if (error) { dev_err(pwr->dev, "can't read register: %d\n", error); goto out; } - if (reg & TPS65218_STATUS_PB_STATE) { + if (reg & tps_data->pb_mask) { input_report_key(pwr->idev, KEY_POWER, 1); pm_wakeup_event(pwr->dev, 0); } else { @@ -55,42 +86,55 @@ out: return IRQ_HANDLED; } -static int tps65218_pwron_probe(struct platform_device *pdev) +static int tps6521x_pb_probe(struct platform_device *pdev) { - struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; - struct tps65218_pwrbutton *pwr; + struct tps6521x_pwrbutton *pwr; struct input_dev *idev; + const struct of_device_id *match; int error; int irq; + match = of_match_node(of_tps6521x_pb_match, pdev->dev.of_node); + if (!match) + return -ENXIO; + pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); if (!pwr) return -ENOMEM; + pwr->data = match->data; + idev = devm_input_allocate_device(dev); if (!idev) return -ENOMEM; - idev->name = "tps65218_pwrbutton"; - idev->phys = "tps65218_pwrbutton/input0"; + idev->name = pwr->data->name; + snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", + pwr->data->name); + idev->phys = pwr->phys; idev->dev.parent = dev; idev->id.bustype = BUS_I2C; input_set_capability(idev, EV_KEY, KEY_POWER); - pwr->tps = tps; + pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL); pwr->dev = dev; pwr->idev = idev; platform_set_drvdata(pdev, pwr); device_init_wakeup(dev, true); irq = platform_get_irq(pdev, 0); - error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq, + if (irq < 0) { + dev_err(dev, "No IRQ resource!\n"); + return -EINVAL; + } + + error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "tps65218-pwrbutton", pwr); + pwr->data->name, pwr); if (error) { dev_err(dev, "failed to request IRQ #%d: %d\n", irq, error); @@ -106,21 +150,15 @@ static int tps65218_pwron_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id of_tps65218_pwr_match[] = { - { .compatible = "ti,tps65218-pwrbutton" }, - { }, -}; -MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match); - -static struct platform_driver tps65218_pwron_driver = { - .probe = tps65218_pwron_probe, +static struct platform_driver tps6521x_pb_driver = { + .probe = tps6521x_pb_probe, .driver = { - .name = "tps65218_pwrbutton", - .of_match_table = of_tps65218_pwr_match, + .name = "tps6521x_pwrbutton", + .of_match_table = of_tps6521x_pb_match, }, }; -module_platform_driver(tps65218_pwron_driver); +module_platform_driver(tps6521x_pb_driver); -MODULE_DESCRIPTION("TPS65218 Power Button"); +MODULE_DESCRIPTION("TPS6521X Power Button"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 936f07a4e35f..6d7de9bfed9a 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -103,6 +103,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { 6-byte ALPS packet */ #define ALPS_STICK_BITS 0x100 /* separate stick button bits */ #define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ +#define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */ static const struct alps_model_info alps_model_data[] = { { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */ @@ -1156,15 +1157,28 @@ static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte) { unsigned char pkt_id = SS4_PACKET_ID_IDLE; - if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 && - (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) { - pkt_id = SS4_PACKET_ID_IDLE; - } else if (!(byte[3] & 0x10)) { - pkt_id = SS4_PACKET_ID_ONE; - } else if (!(byte[3] & 0x20)) { + switch (byte[3] & 0x30) { + case 0x00: + if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 && + (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && + byte[5] == 0x00) { + pkt_id = SS4_PACKET_ID_IDLE; + } else { + pkt_id = SS4_PACKET_ID_ONE; + } + break; + case 0x10: + /* two-finger finger positions */ pkt_id = SS4_PACKET_ID_TWO; - } else { + break; + case 0x20: + /* stick pointer */ + pkt_id = SS4_PACKET_ID_STICK; + break; + case 0x30: + /* third and fourth finger positions */ pkt_id = SS4_PACKET_ID_MULTI; + break; } return pkt_id; @@ -1185,7 +1199,13 @@ static int alps_decode_ss4_v2(struct alps_fields *f, f->mt[0].x = SS4_1F_X_V2(p); f->mt[0].y = SS4_1F_Y_V2(p); f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f; - f->fingers = 1; + /* + * When a button is held the device will give us events + * with x, y, and pressure of 0. This causes annoying jumps + * if a touch is released while the button is held. + * Handle this by claiming zero contacts. + */ + f->fingers = f->pressure > 0 ? 1 : 0; f->first_mp = 0; f->is_mp = 0; break; @@ -1246,16 +1266,40 @@ static int alps_decode_ss4_v2(struct alps_fields *f, } break; + case SS4_PACKET_ID_STICK: + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); + } else { + int x = (s8)(((p[0] & 1) << 7) | (p[1] & 0x7f)); + int y = (s8)(((p[3] & 1) << 7) | (p[2] & 0x7f)); + int pressure = (s8)(p[4] & 0x7f); + + input_report_rel(priv->dev2, REL_X, x); + input_report_rel(priv->dev2, REL_Y, -y); + input_report_abs(priv->dev2, ABS_PRESSURE, pressure); + } + break; + case SS4_PACKET_ID_IDLE: default: memset(f, 0, sizeof(struct alps_fields)); break; } - f->left = !!(SS4_BTN_V2(p) & 0x01); - if (!(priv->flags & ALPS_BUTTONPAD)) { - f->right = !!(SS4_BTN_V2(p) & 0x02); - f->middle = !!(SS4_BTN_V2(p) & 0x04); + /* handle buttons */ + if (pkt_id == SS4_PACKET_ID_STICK) { + f->ts_left = !!(SS4_BTN_V2(p) & 0x01); + if (!(priv->flags & ALPS_BUTTONPAD)) { + f->ts_right = !!(SS4_BTN_V2(p) & 0x02); + f->ts_middle = !!(SS4_BTN_V2(p) & 0x04); + } + } else { + f->left = !!(SS4_BTN_V2(p) & 0x01); + if (!(priv->flags & ALPS_BUTTONPAD)) { + f->right = !!(SS4_BTN_V2(p) & 0x02); + f->middle = !!(SS4_BTN_V2(p) & 0x04); + } } return 0; @@ -1266,6 +1310,7 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; + struct input_dev *dev2 = priv->dev2; struct alps_fields *f = &priv->f; memset(f, 0, sizeof(struct alps_fields)); @@ -1311,6 +1356,13 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) input_report_abs(dev, ABS_PRESSURE, f->pressure); input_sync(dev); + + if (priv->flags & ALPS_DUALPOINT) { + input_report_key(dev2, BTN_LEFT, f->ts_left); + input_report_key(dev2, BTN_RIGHT, f->ts_right); + input_report_key(dev2, BTN_MIDDLE, f->ts_middle); + input_sync(dev2); + } } static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse) @@ -2695,6 +2747,10 @@ static int alps_set_protocol(struct psmouse *psmouse, if (alps_set_defaults_ss4_v2(psmouse, priv)) return -EIO; + if (priv->fw_ver[1] == 0x1) + priv->flags |= ALPS_DUALPOINT | + ALPS_DUALPOINT_WITH_PRESSURE; + break; } @@ -2767,6 +2823,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x14 && ec[1] == 0x02) { protocol = &alps_v8_protocol_data; + } else if (e7[0] == 0x73 && e7[1] == 0x03 && + e7[2] == 0x28 && ec[1] == 0x01) { + protocol = &alps_v8_protocol_data; } else { psmouse_dbg(psmouse, "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); @@ -2949,6 +3008,10 @@ int alps_init(struct psmouse *psmouse) input_set_capability(dev2, EV_REL, REL_X); input_set_capability(dev2, EV_REL, REL_Y); + if (priv->flags & ALPS_DUALPOINT_WITH_PRESSURE) { + input_set_capability(dev2, EV_ABS, ABS_PRESSURE); + input_set_abs_params(dev2, ABS_PRESSURE, 0, 127, 0, 0); + } input_set_capability(dev2, EV_KEY, BTN_LEFT); input_set_capability(dev2, EV_KEY, BTN_RIGHT); input_set_capability(dev2, EV_KEY, BTN_MIDDLE); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index d37f814dc447..b9417e2d7ad3 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -37,12 +37,14 @@ * or there's button activities. * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad + * SS4_PACKET_ID_STICK: A stick pointer packet */ enum SS4_PACKET_ID { SS4_PACKET_ID_IDLE = 0, SS4_PACKET_ID_ONE, SS4_PACKET_ID_TWO, SS4_PACKET_ID_MULTI, + SS4_PACKET_ID_STICK, }; #define SS4_COUNT_PER_ELECTRODE 256 diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index cb6aecbc1dc2..e23b2495d52e 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -222,11 +222,13 @@ static int elan_smbus_get_checksum(struct i2c_client *client, static int elan_smbus_get_max(struct i2c_client *client, unsigned int *max_x, unsigned int *max_y) { + int ret; int error; u8 val[3]; - error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val); - if (error) { + ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; dev_err(&client->dev, "failed to get dimensions: %d\n", error); return error; } @@ -240,12 +242,13 @@ static int elan_smbus_get_max(struct i2c_client *client, static int elan_smbus_get_resolution(struct i2c_client *client, u8 *hw_res_x, u8 *hw_res_y) { + int ret; int error; u8 val[3]; - error = i2c_smbus_read_block_data(client, - ETP_SMBUS_RESOLUTION_CMD, val); - if (error) { + ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; dev_err(&client->dev, "failed to get resolution: %d\n", error); return error; } @@ -260,12 +263,13 @@ static int elan_smbus_get_num_traces(struct i2c_client *client, unsigned int *x_traces, unsigned int *y_traces) { + int ret; int error; u8 val[3]; - error = i2c_smbus_read_block_data(client, - ETP_SMBUS_XY_TRACENUM_CMD, val); - if (error) { + ret = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; dev_err(&client->dev, "failed to get trace info: %d\n", error); return error; } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 08e252a42480..db7d1d666ac1 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1134,7 +1134,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * System76 Pangolin 0x250f01 ? 2 hw buttons * (*) + 3 trackpoint buttons * (**) + 0 trackpoint buttons - * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps + * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps */ static void elantech_set_buttonpad_prop(struct psmouse *psmouse) { @@ -1159,6 +1159,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = { DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), }, }, + { + /* Fujitsu H760 also has a middle button */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"), + }, + }, #endif { } }; @@ -1503,10 +1510,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { - /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ + /* Fujitsu H760 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), - DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"), }, }, { @@ -1517,6 +1524,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { + /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"), + }, + }, + { + /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"), + }, + }, + { /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index c8c6a8cc329d..54eceb30ede5 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -390,7 +390,8 @@ static int focaltech_read_size(struct psmouse *psmouse) return 0; } -void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution) +static void focaltech_set_resolution(struct psmouse *psmouse, + unsigned int resolution) { /* not supported yet */ } diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 5784e20542a4..fb4b185dea96 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1916,7 +1916,7 @@ static int __init psmouse_init(void) synaptics_module_init(); hgpk_module_init(); - kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); + kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0); if (!kpsmoused_wq) { pr_err("failed to create kpsmoused workqueue\n"); return -ENOMEM; diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c index 6f2e0e4f0296..1ebc2c1debae 100644 --- a/drivers/input/rmi4/rmi_i2c.c +++ b/drivers/input/rmi4/rmi_i2c.c @@ -221,6 +221,21 @@ static const struct of_device_id rmi_i2c_of_match[] = { MODULE_DEVICE_TABLE(of, rmi_i2c_of_match); #endif +static void rmi_i2c_regulator_bulk_disable(void *data) +{ + struct rmi_i2c_xport *rmi_i2c = data; + + regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), + rmi_i2c->supplies); +} + +static void rmi_i2c_unregister_transport(void *data) +{ + struct rmi_i2c_xport *rmi_i2c = data; + + rmi_unregister_transport_device(&rmi_i2c->xport); +} + static int rmi_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -264,6 +279,12 @@ static int rmi_i2c_probe(struct i2c_client *client, if (retval < 0) return retval; + retval = devm_add_action_or_reset(&client->dev, + rmi_i2c_regulator_bulk_disable, + rmi_i2c); + if (retval) + return retval; + of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms", &rmi_i2c->startup_delay); @@ -294,6 +315,11 @@ static int rmi_i2c_probe(struct i2c_client *client, client->addr); return retval; } + retval = devm_add_action_or_reset(&client->dev, + rmi_i2c_unregister_transport, + rmi_i2c); + if (retval) + return retval; retval = rmi_i2c_init_irq(client); if (retval < 0) @@ -304,17 +330,6 @@ static int rmi_i2c_probe(struct i2c_client *client, return 0; } -static int rmi_i2c_remove(struct i2c_client *client) -{ - struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); - - rmi_unregister_transport_device(&rmi_i2c->xport); - regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies), - rmi_i2c->supplies); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int rmi_i2c_suspend(struct device *dev) { @@ -431,7 +446,6 @@ static struct i2c_driver rmi_i2c_driver = { }, .id_table = rmi_id, .probe = rmi_i2c_probe, - .remove = rmi_i2c_remove, }; module_i2c_driver(rmi_i2c_driver); diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c index 55bd1b34970c..4ebef607e214 100644 --- a/drivers/input/rmi4/rmi_spi.c +++ b/drivers/input/rmi4/rmi_spi.c @@ -396,6 +396,13 @@ static inline int rmi_spi_of_probe(struct spi_device *spi, } #endif +static void rmi_spi_unregister_transport(void *data) +{ + struct rmi_spi_xport *rmi_spi = data; + + rmi_unregister_transport_device(&rmi_spi->xport); +} + static int rmi_spi_probe(struct spi_device *spi) { struct rmi_spi_xport *rmi_spi; @@ -464,6 +471,11 @@ static int rmi_spi_probe(struct spi_device *spi) dev_err(&spi->dev, "failed to register transport.\n"); return retval; } + retval = devm_add_action_or_reset(&spi->dev, + rmi_spi_unregister_transport, + rmi_spi); + if (retval) + return retval; retval = rmi_spi_init_irq(spi); if (retval < 0) @@ -473,15 +485,6 @@ static int rmi_spi_probe(struct spi_device *spi) return 0; } -static int rmi_spi_remove(struct spi_device *spi) -{ - struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); - - rmi_unregister_transport_device(&rmi_spi->xport); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int rmi_spi_suspend(struct device *dev) { @@ -577,7 +580,6 @@ static struct spi_driver rmi_spi_driver = { }, .id_table = rmi_id, .probe = rmi_spi_probe, - .remove = rmi_spi_remove, }; module_spi_driver(rmi_spi_driver); diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index a5eed2ade53d..34da81c006b6 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -81,7 +81,7 @@ static inline int i8042_platform_init(void) return -EBUSY; #endif - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h index ee1ad27d6ed0..08a1c10a1448 100644 --- a/drivers/input/serio/i8042-ip22io.h +++ b/drivers/input/serio/i8042-ip22io.h @@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) return -EBUSY; #endif - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h index f708c75d16f1..1aabea43329e 100644 --- a/drivers/input/serio/i8042-ppcio.h +++ b/drivers/input/serio/i8042-ppcio.h @@ -44,7 +44,7 @@ static inline void i8042_write_command(int val) static inline int i8042_platform_init(void) { - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index afcd1c1a05b2..6231d63860ee 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -130,7 +130,7 @@ static int __init i8042_platform_init(void) } } - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-unicore32io.h b/drivers/input/serio/i8042-unicore32io.h index 73f5cc124a36..455747552f85 100644 --- a/drivers/input/serio/i8042-unicore32io.h +++ b/drivers/input/serio/i8042-unicore32io.h @@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) return -EBUSY; - i8042_reset = 1; + i8042_reset = I8042_RESET_ALWAYS; return 0; } diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 68f5f4a0f1e7..f4bfb4b2d50a 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { { } }; +/* + * On some Asus laptops, just running self tests cause problems. + */ +static const struct dmi_system_id i8042_dmi_noselftest_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "R409L"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"), + }, + }, + { } +}; static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { { /* MSI Wind U-100 */ @@ -1072,12 +1156,18 @@ static int __init i8042_platform_init(void) return retval; #if defined(__ia64__) - i8042_reset = true; + i8042_reset = I8042_RESET_ALWAYS; #endif #ifdef CONFIG_X86 - if (dmi_check_system(i8042_dmi_reset_table)) - i8042_reset = true; + /* Honor module parameter when value is not default */ + if (i8042_reset == I8042_RESET_DEFAULT) { + if (dmi_check_system(i8042_dmi_reset_table)) + i8042_reset = I8042_RESET_ALWAYS; + + if (dmi_check_system(i8042_dmi_noselftest_table)) + i8042_reset = I8042_RESET_NEVER; + } if (dmi_check_system(i8042_dmi_noloop_table)) i8042_noloop = true; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 405252a884dd..89abfdb539ac 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -48,9 +48,39 @@ static bool i8042_unlock; module_param_named(unlock, i8042_unlock, bool, 0); MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); -static bool i8042_reset; -module_param_named(reset, i8042_reset, bool, 0); -MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); +enum i8042_controller_reset_mode { + I8042_RESET_NEVER, + I8042_RESET_ALWAYS, + I8042_RESET_ON_S2RAM, +#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM +}; +static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT; +static int i8042_set_reset(const char *val, const struct kernel_param *kp) +{ + enum i8042_controller_reset_mode *arg = kp->arg; + int error; + bool reset; + + if (val) { + error = kstrtobool(val, &reset); + if (error) + return error; + } else { + reset = true; + } + + *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER; + return 0; +} + +static const struct kernel_param_ops param_ops_reset_param = { + .flags = KERNEL_PARAM_OPS_FL_NOARG, + .set = i8042_set_reset, +}; +#define param_check_reset_param(name, p) \ + __param_check(name, p, enum i8042_controller_reset_mode) +module_param_named(reset, i8042_reset, reset_param, 0); +MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both"); static bool i8042_direct; module_param_named(direct, i8042_direct, bool, 0); @@ -1019,7 +1049,7 @@ static int i8042_controller_init(void) * Reset the controller and reset CRT to the original value set by BIOS. */ -static void i8042_controller_reset(bool force_reset) +static void i8042_controller_reset(bool s2r_wants_reset) { i8042_flush(); @@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset) * Reset the controller if requested. */ - if (i8042_reset || force_reset) + if (i8042_reset == I8042_RESET_ALWAYS || + (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { i8042_controller_selftest(); + } /* * Restore the original control register setting. @@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void) * before suspending. */ -static int i8042_controller_resume(bool force_reset) +static int i8042_controller_resume(bool s2r_wants_reset) { int error; @@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset) if (error) return error; - if (i8042_reset || force_reset) { + if (i8042_reset == I8042_RESET_ALWAYS || + (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) { error = i8042_controller_selftest(); if (error) return error; @@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev) static int i8042_pm_resume(struct device *dev) { - bool force_reset; + bool want_reset; int i; for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev) * off control to the platform firmware, otherwise we can simply restore * the mode. */ - force_reset = pm_resume_via_firmware(); + want_reset = pm_resume_via_firmware(); - return i8042_controller_resume(force_reset); + return i8042_controller_resume(want_reset); } static int i8042_pm_thaw(struct device *dev) @@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev) i8042_platform_device = dev; - if (i8042_reset) { + if (i8042_reset == I8042_RESET_ALWAYS) { error = i8042_controller_selftest(); if (error) return error; diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 9c927d35c1f5..d189843f3727 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -71,10 +71,7 @@ static void serport_serio_close(struct serio *serio) spin_lock_irqsave(&serport->lock, flags); clear_bit(SERPORT_ACTIVE, &serport->flags); - set_bit(SERPORT_DEAD, &serport->flags); spin_unlock_irqrestore(&serport->lock, flags); - - wake_up_interruptible(&serport->wait); } /* @@ -248,6 +245,19 @@ static long serport_ldisc_compat_ioctl(struct tty_struct *tty, } #endif +static int serport_ldisc_hangup(struct tty_struct *tty) +{ + struct serport *serport = (struct serport *) tty->disc_data; + unsigned long flags; + + spin_lock_irqsave(&serport->lock, flags); + set_bit(SERPORT_DEAD, &serport->flags); + spin_unlock_irqrestore(&serport->lock, flags); + + wake_up_interruptible(&serport->wait); + return 0; +} + static void serport_ldisc_write_wakeup(struct tty_struct * tty) { struct serport *serport = (struct serport *) tty->disc_data; @@ -274,6 +284,7 @@ static struct tty_ldisc_ops serport_ldisc = { .compat_ioctl = serport_ldisc_compat_ioctl, #endif .receive_buf = serport_ldisc_receive, + .hangup = serport_ldisc_hangup, .write_wakeup = serport_ldisc_write_wakeup }; diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c index 949dacc78664..47de5a81172f 100644 --- a/drivers/input/tablet/pegasus_notetaker.c +++ b/drivers/input/tablet/pegasus_notetaker.c @@ -40,6 +40,7 @@ #include <linux/input.h> #include <linux/usb/input.h> #include <linux/slab.h> +#include <linux/workqueue.h> /* USB HID defines */ #define USB_REQ_GET_REPORT 0x01 diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2fb1f430a431..507981356921 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -305,19 +305,6 @@ config TOUCHSCREEN_EGALAX_SERIAL To compile this driver as a module, choose M here: the module will be called egalax_ts_serial. -config TOUCHSCREEN_FT6236 - tristate "FT6236 I2C touchscreen" - depends on I2C - depends on GPIOLIB || COMPILE_TEST - help - Say Y here to enable support for the I2C connected FT6x06 and - FT6x36 family of capacitive touchscreen drivers. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called ft6236. - config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO @@ -397,6 +384,18 @@ config TOUCHSCREEN_GUNZE To compile this driver as a module, choose M here: the module will be called gunze. +config TOUCHSCREEN_EKTF2127 + tristate "Elan eKTF2127 I2C touchscreen" + depends on I2C + help + Say Y here if you have an Elan eKTF2127 touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ektf2127. + config TOUCHSCREEN_ELAN tristate "Elan eKTH I2C touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index b4373d6be402..81b86451782d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -32,11 +32,11 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +obj-$(CONFIG_TOUCHSCREEN_EKTF2127) += ektf2127.o obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o -obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 703e295a37ed..28466e358fee 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1063,9 +1063,15 @@ static const struct edt_i2c_chip_data edt_ft5506_data = { .max_support_points = 10, }; +static const struct edt_i2c_chip_data edt_ft6236_data = { + .max_support_points = 2, +}; + static const struct i2c_device_id edt_ft5x06_ts_id[] = { { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, + /* Note no edt- prefix for compatibility with the ft6236.c driver */ + { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); @@ -1076,6 +1082,8 @@ static const struct of_device_id edt_ft5x06_of_match[] = { { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, + /* Note focaltech vendor prefix for compatibility with ft6236.c */ + { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); diff --git a/drivers/input/touchscreen/ektf2127.c b/drivers/input/touchscreen/ektf2127.c new file mode 100644 index 000000000000..0ed34ff787ce --- /dev/null +++ b/drivers/input/touchscreen/ektf2127.c @@ -0,0 +1,336 @@ +/* + * Driver for ELAN eKTF2127 i2c touchscreen controller + * + * For this driver the layout of the Chipone icn8318 i2c + * touchscreencontroller is used. + * + * 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. + * + * Author: + * Michel Verlaan <michel.verl@gmail.com> + * Siebren Vroegindeweij <siebren.vroegindeweij@hotmail.com> + * + * Original chipone_icn8318 driver: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/delay.h> + +/* Packet header defines (first byte of data send / received) */ +#define EKTF2127_NOISE 0x40 +#define EKTF2127_RESPONSE 0x52 +#define EKTF2127_REQUEST 0x53 +#define EKTF2127_HELLO 0x55 +#define EKTF2127_REPORT 0x5d +#define EKTF2127_CALIB_DONE 0x66 + +/* Register defines (second byte of data send / received) */ +#define EKTF2127_ENV_NOISY 0x41 +#define EKTF2127_HEIGHT 0x60 +#define EKTF2127_WIDTH 0x63 + +/* 2 bytes header + 5 * 3 bytes coordinates + 3 bytes pressure info + footer */ +#define EKTF2127_TOUCH_REPORT_SIZE 21 +#define EKTF2127_MAX_TOUCHES 5 + +struct ektf2127_ts { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *power_gpios; + struct touchscreen_properties prop; +}; + +static void ektf2127_parse_coordinates(const u8* buf, unsigned int touch_count, + struct input_mt_pos *touches) +{ + int index = 0; + int i; + + for (i = 0; i < touch_count; i++) { + index = 2 + i * 3; + + touches[i].x = (buf[index] & 0x0f); + touches[i].x <<= 8; + touches[i].x |= buf[index + 2]; + + touches[i].y = (buf[index] & 0xf0); + touches[i].y <<= 4; + touches[i].y |= buf[index + 1]; + } +} + +static void ektf2127_report_event(struct ektf2127_ts *ts, const u8 *buf) +{ + struct input_mt_pos touches[EKTF2127_MAX_TOUCHES]; + int slots[EKTF2127_MAX_TOUCHES]; + unsigned int touch_count, i; + + touch_count = buf[1] & 0x07; + if (touch_count > EKTF2127_MAX_TOUCHES) { + dev_err(&ts->client->dev, + "Too many touches %d > %d\n", + touch_count, EKTF2127_MAX_TOUCHES); + touch_count = EKTF2127_MAX_TOUCHES; + } + + ektf2127_parse_coordinates(buf, touch_count, touches); + input_mt_assign_slots(ts->input, slots, touches, + touch_count, 0); + + for (i = 0; i < touch_count; i++) { + input_mt_slot(ts->input, slots[i]); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); + touchscreen_report_pos(ts->input, &ts->prop, + touches[i].x, touches[i].y, true); + } + + input_mt_sync_frame(ts->input); + input_sync(ts->input); +} + +static irqreturn_t ektf2127_irq(int irq, void *dev_id) +{ + struct ektf2127_ts *ts = dev_id; + struct device *dev = &ts->client->dev; + char buf[EKTF2127_TOUCH_REPORT_SIZE]; + int ret; + + ret = i2c_master_recv(ts->client, buf, EKTF2127_TOUCH_REPORT_SIZE); + if (ret != EKTF2127_TOUCH_REPORT_SIZE) { + dev_err(dev, "Error reading touch data: %d\n", ret); + goto out; + } + + switch (buf[0]) { + case EKTF2127_REPORT: + ektf2127_report_event(ts, buf); + break; + + case EKTF2127_NOISE: + if (buf[1] == EKTF2127_ENV_NOISY) + dev_dbg(dev, "Environment is electrically noisy\n"); + break; + + case EKTF2127_HELLO: + case EKTF2127_CALIB_DONE: + break; + + default: + dev_err(dev, "Unexpected packet header byte %#02x\n", buf[0]); + break; + } + +out: + return IRQ_HANDLED; +} + +static int ektf2127_start(struct input_dev *dev) +{ + struct ektf2127_ts *ts = input_get_drvdata(dev); + + enable_irq(ts->client->irq); + gpiod_set_value_cansleep(ts->power_gpios, 1); + + return 0; +} + +static void ektf2127_stop(struct input_dev *dev) +{ + struct ektf2127_ts *ts = input_get_drvdata(dev); + + disable_irq(ts->client->irq); + gpiod_set_value_cansleep(ts->power_gpios, 0); +} + +static int __maybe_unused ektf2127_suspend(struct device *dev) +{ + struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); + + mutex_lock(&ts->input->mutex); + if (ts->input->users) + ektf2127_stop(ts->input); + mutex_unlock(&ts->input->mutex); + + return 0; +} + +static int __maybe_unused ektf2127_resume(struct device *dev) +{ + struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); + + mutex_lock(&ts->input->mutex); + if (ts->input->users) + ektf2127_start(ts->input); + mutex_unlock(&ts->input->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ektf2127_pm_ops, ektf2127_suspend, + ektf2127_resume); + +static int ektf2127_query_dimension(struct i2c_client *client, bool width) +{ + struct device *dev = &client->dev; + const char *what = width ? "width" : "height"; + u8 what_code = width ? EKTF2127_WIDTH : EKTF2127_HEIGHT; + u8 buf[4]; + int ret; + int error; + + /* Request dimension */ + buf[0] = EKTF2127_REQUEST; + buf[1] = width ? EKTF2127_WIDTH : EKTF2127_HEIGHT; + buf[2] = 0x00; + buf[3] = 0x00; + ret = i2c_master_send(client, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + error = ret < 0 ? ret : -EIO; + dev_err(dev, "Failed to request %s: %d\n", what, error); + return error; + } + + msleep(20); + + /* Read response */ + ret = i2c_master_recv(client, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + error = ret < 0 ? ret : -EIO; + dev_err(dev, "Failed to receive %s data: %d\n", what, error); + return error; + } + + if (buf[0] != EKTF2127_RESPONSE || buf[1] != what_code) { + dev_err(dev, "Unexpected %s data: %#02x %#02x\n", + what, buf[0], buf[1]); + return -EIO; + } + + return (((buf[3] & 0xf0) << 4) | buf[2]) - 1; +} + +static int ektf2127_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ektf2127_ts *ts; + struct input_dev *input; + u8 buf[4]; + int max_x, max_y; + int error; + + if (!client->irq) { + dev_err(dev, "Error no irq specified\n"); + return -EINVAL; + } + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + /* This requests the gpio *and* turns on the touchscreen controller */ + ts->power_gpios = devm_gpiod_get(dev, "power", GPIOD_OUT_HIGH); + if (IS_ERR(ts->power_gpios)) { + error = PTR_ERR(ts->power_gpios); + if (error != -EPROBE_DEFER) + dev_err(dev, "Error getting power gpio: %d\n", error); + return error; + } + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = client->name; + input->id.bustype = BUS_I2C; + input->open = ektf2127_start; + input->close = ektf2127_stop; + + ts->client = client; + + /* Read hello (ignore result, depends on initial power state) */ + msleep(20); + i2c_master_recv(ts->client, buf, sizeof(buf)); + + /* Read resolution from chip */ + max_x = ektf2127_query_dimension(client, true); + if (max_x < 0) + return max_x; + + max_y = ektf2127_query_dimension(client, false); + if (max_y < 0) + return max_y; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0); + touchscreen_parse_properties(input, true, &ts->prop); + + error = input_mt_init_slots(input, EKTF2127_MAX_TOUCHES, + INPUT_MT_DIRECT | + INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK); + if (error) + return error; + + ts->input = input; + input_set_drvdata(input, ts); + + error = devm_request_threaded_irq(dev, client->irq, + NULL, ektf2127_irq, + IRQF_ONESHOT, client->name, ts); + if (error) { + dev_err(dev, "Error requesting irq: %d\n", error); + return error; + } + + /* Stop device till opened */ + ektf2127_stop(ts->input); + + error = input_register_device(input); + if (error) + return error; + + i2c_set_clientdata(client, ts); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ektf2127_of_match[] = { + { .compatible = "elan,ektf2127" }, + {} +}; +MODULE_DEVICE_TABLE(of, ektf2127_of_match); +#endif + +static const struct i2c_device_id ektf2127_i2c_id[] = { + { "ektf2127", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ektf2127_i2c_id); + +static struct i2c_driver ektf2127_driver = { + .driver = { + .name = "elan_ektf2127", + .pm = &ektf2127_pm_ops, + .of_match_table = of_match_ptr(ektf2127_of_match), + }, + .probe = ektf2127_probe, + .id_table = ektf2127_i2c_id, +}; +module_i2c_driver(ektf2127_driver); + +MODULE_DESCRIPTION("ELAN eKTF2127 I2C Touchscreen Driver"); +MODULE_AUTHOR("Michel Verlaan, Siebren Vroegindeweij"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index ac09855fa435..02aec284deca 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -298,7 +298,7 @@ static u16 elants_i2c_parse_version(u8 *buf) return get_unaligned_be32(buf) >> 4; } -static int elants_i2c_query_fw_id(struct elants_data *ts) +static int elants_i2c_query_hw_version(struct elants_data *ts) { struct i2c_client *client = ts->client; int error, retry_cnt; @@ -318,8 +318,13 @@ static int elants_i2c_query_fw_id(struct elants_data *ts) error, (int)sizeof(resp), resp); } - dev_err(&client->dev, - "Failed to read fw id or fw id is invalid\n"); + if (error) { + dev_err(&client->dev, + "Failed to read fw id: %d\n", error); + return error; + } + + dev_err(&client->dev, "Invalid fw id: %#04x\n", ts->hw_version); return -EINVAL; } @@ -508,7 +513,7 @@ static int elants_i2c_fastboot(struct i2c_client *client) static int elants_i2c_initialize(struct elants_data *ts) { struct i2c_client *client = ts->client; - int error, retry_cnt; + int error, error2, retry_cnt; const u8 hello_packet[] = { 0x55, 0x55, 0x55, 0x55 }; const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 }; u8 buf[HEADER_SIZE]; @@ -553,18 +558,22 @@ static int elants_i2c_initialize(struct elants_data *ts) } } + /* hw version is available even if device in recovery state */ + error2 = elants_i2c_query_hw_version(ts); if (!error) - error = elants_i2c_query_fw_id(ts); + error = error2; + if (!error) error = elants_i2c_query_fw_version(ts); + if (!error) + error = elants_i2c_query_test_version(ts); + if (!error) + error = elants_i2c_query_bc_version(ts); + if (!error) + error = elants_i2c_query_ts_info(ts); - if (error) { + if (error) ts->iap_mode = ELAN_IAP_RECOVERY; - } else { - elants_i2c_query_test_version(ts); - elants_i2c_query_bc_version(ts); - elants_i2c_query_ts_info(ts); - } return 0; } diff --git a/drivers/input/touchscreen/ft6236.c b/drivers/input/touchscreen/ft6236.c deleted file mode 100644 index d240d2e212bd..000000000000 --- a/drivers/input/touchscreen/ft6236.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * FocalTech FT6236 TouchScreen driver. - * - * Copyright (c) 2010 Focal tech Ltd. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/i2c.h> -#include <linux/input.h> -#include <linux/input/mt.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/property.h> - -#define FT6236_MAX_TOUCH_POINTS 2 - -#define FT6236_REG_TH_GROUP 0x80 -#define FT6236_REG_PERIODACTIVE 0x88 -#define FT6236_REG_LIB_VER_H 0xa1 -#define FT6236_REG_LIB_VER_L 0xa2 -#define FT6236_REG_CIPHER 0xa3 -#define FT6236_REG_FIRMID 0xa6 -#define FT6236_REG_FOCALTECH_ID 0xa8 -#define FT6236_REG_RELEASE_CODE_ID 0xaf - -#define FT6236_EVENT_PRESS_DOWN 0 -#define FT6236_EVENT_LIFT_UP 1 -#define FT6236_EVENT_CONTACT 2 -#define FT6236_EVENT_NO_EVENT 3 - -struct ft6236_data { - struct i2c_client *client; - struct input_dev *input; - struct gpio_desc *reset_gpio; - u32 max_x; - u32 max_y; - bool invert_x; - bool invert_y; - bool swap_xy; -}; - -/* - * This struct is a touchpoint as stored in hardware. Note that the id, - * as well as the event, are stored in the upper nybble of the hi byte. - */ -struct ft6236_touchpoint { - union { - u8 xhi; - u8 event; - }; - u8 xlo; - union { - u8 yhi; - u8 id; - }; - u8 ylo; - u8 weight; - u8 misc; -} __packed; - -/* This packet represents the register map as read from offset 0 */ -struct ft6236_packet { - u8 dev_mode; - u8 gest_id; - u8 touches; - struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS]; -} __packed; - -static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data) -{ - int error; - - error = i2c_smbus_read_i2c_block_data(client, reg, len, data); - if (error < 0) - return error; - - if (error != len) - return -EIO; - - return 0; -} - -static irqreturn_t ft6236_interrupt(int irq, void *dev_id) -{ - struct ft6236_data *ft6236 = dev_id; - struct device *dev = &ft6236->client->dev; - struct input_dev *input = ft6236->input; - struct ft6236_packet buf; - u8 touches; - int i, error; - - error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf); - if (error) { - dev_err(dev, "read touchdata failed %d\n", error); - return IRQ_HANDLED; - } - - touches = buf.touches & 0xf; - if (touches > FT6236_MAX_TOUCH_POINTS) { - dev_dbg(dev, - "%d touch points reported, only %d are supported\n", - touches, FT6236_MAX_TOUCH_POINTS); - touches = FT6236_MAX_TOUCH_POINTS; - } - - for (i = 0; i < touches; i++) { - struct ft6236_touchpoint *point = &buf.points[i]; - u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo; - u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo; - u8 event = point->event >> 6; - u8 id = point->id >> 4; - bool act = (event == FT6236_EVENT_PRESS_DOWN || - event == FT6236_EVENT_CONTACT); - - input_mt_slot(input, id); - input_mt_report_slot_state(input, MT_TOOL_FINGER, act); - if (!act) - continue; - - if (ft6236->invert_x) - x = ft6236->max_x - x; - - if (ft6236->invert_y) - y = ft6236->max_y - y; - - if (ft6236->swap_xy) { - input_report_abs(input, ABS_MT_POSITION_X, y); - input_report_abs(input, ABS_MT_POSITION_Y, x); - } else { - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); - } - } - - input_mt_sync_frame(input); - input_sync(input); - - return IRQ_HANDLED; -} - -static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg) -{ - struct i2c_client *client = ft6236->client; - u8 val = 0; - int error; - - error = ft6236_read(client, reg, 1, &val); - if (error) - dev_dbg(&client->dev, - "error reading register 0x%02x: %d\n", reg, error); - - return val; -} - -static void ft6236_debug_info(struct ft6236_data *ft6236) -{ - struct device *dev = &ft6236->client->dev; - - dev_dbg(dev, "Touch threshold is %d\n", - ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4); - dev_dbg(dev, "Report rate is %dHz\n", - ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10); - dev_dbg(dev, "Firmware library version 0x%02x%02x\n", - ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H), - ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L)); - dev_dbg(dev, "Firmware version 0x%02x\n", - ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID)); - dev_dbg(dev, "Chip vendor ID 0x%02x\n", - ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER)); - dev_dbg(dev, "CTPM vendor ID 0x%02x\n", - ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID)); - dev_dbg(dev, "Release code version 0x%02x\n", - ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID)); -} - -static void ft6236_reset(struct ft6236_data *ft6236) -{ - if (!ft6236->reset_gpio) - return; - - gpiod_set_value_cansleep(ft6236->reset_gpio, 1); - usleep_range(5000, 20000); - gpiod_set_value_cansleep(ft6236->reset_gpio, 0); - msleep(300); -} - -static int ft6236_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct device *dev = &client->dev; - struct ft6236_data *ft6236; - struct input_dev *input; - u32 fuzz_x = 0, fuzz_y = 0; - u8 val; - int error; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -ENXIO; - - if (!client->irq) { - dev_err(dev, "irq is missing\n"); - return -EINVAL; - } - - ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL); - if (!ft6236) - return -ENOMEM; - - ft6236->client = client; - ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ft6236->reset_gpio)) { - error = PTR_ERR(ft6236->reset_gpio); - if (error != -EPROBE_DEFER) - dev_err(dev, "error getting reset gpio: %d\n", error); - return error; - } - - ft6236_reset(ft6236); - - /* verify that the controller is present */ - error = ft6236_read(client, 0x00, 1, &val); - if (error) { - dev_err(dev, "failed to read from controller: %d\n", error); - return error; - } - - ft6236_debug_info(ft6236); - - input = devm_input_allocate_device(dev); - if (!input) - return -ENOMEM; - - ft6236->input = input; - input->name = client->name; - input->id.bustype = BUS_I2C; - - if (device_property_read_u32(dev, "touchscreen-size-x", - &ft6236->max_x) || - device_property_read_u32(dev, "touchscreen-size-y", - &ft6236->max_y)) { - dev_err(dev, "touchscreen-size-x and/or -y missing\n"); - return -EINVAL; - } - - device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x); - device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y); - ft6236->invert_x = device_property_read_bool(dev, - "touchscreen-inverted-x"); - ft6236->invert_y = device_property_read_bool(dev, - "touchscreen-inverted-y"); - ft6236->swap_xy = device_property_read_bool(dev, - "touchscreen-swapped-x-y"); - - if (ft6236->swap_xy) { - input_set_abs_params(input, ABS_MT_POSITION_X, 0, - ft6236->max_y, fuzz_y, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, - ft6236->max_x, fuzz_x, 0); - } else { - input_set_abs_params(input, ABS_MT_POSITION_X, 0, - ft6236->max_x, fuzz_x, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, - ft6236->max_y, fuzz_y, 0); - } - - error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS, - INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); - if (error) - return error; - - error = devm_request_threaded_irq(dev, client->irq, NULL, - ft6236_interrupt, IRQF_ONESHOT, - client->name, ft6236); - if (error) { - dev_err(dev, "request irq %d failed: %d\n", client->irq, error); - return error; - } - - error = input_register_device(input); - if (error) { - dev_err(dev, "failed to register input device: %d\n", error); - return error; - } - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id ft6236_of_match[] = { - { .compatible = "focaltech,ft6236", }, - { } -}; -MODULE_DEVICE_TABLE(of, ft6236_of_match); -#endif - -static const struct i2c_device_id ft6236_id[] = { - { "ft6236", }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ft6236_id); - -static struct i2c_driver ft6236_driver = { - .driver = { - .name = "ft6236", - .of_match_table = of_match_ptr(ft6236_of_match), - }, - .probe = ft6236_probe, - .id_table = ft6236_id, -}; -module_i2c_driver(ft6236_driver); - -MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); -MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>"); -MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index ea3b6a5b83e6..729b3c89324c 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -13,6 +13,7 @@ * HP Jornada 710/720/729 Touchscreen Driver */ +#include <linux/gpio/consumer.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -20,9 +21,7 @@ #include <linux/slab.h> #include <linux/io.h> -#include <mach/hardware.h> #include <mach/jornada720.h> -#include <mach/irqs.h> MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); @@ -30,6 +29,7 @@ MODULE_LICENSE("GPL v2"); struct jornada_ts { struct input_dev *dev; + struct gpio_desc *gpio; int x_data[4]; /* X sample values */ int y_data[4]; /* Y sample values */ }; @@ -71,8 +71,8 @@ static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id) struct input_dev *input = jornada_ts->dev; int x, y; - /* If GPIO_GPIO9 is set to high then report pen up */ - if (GPLR & GPIO_GPIO(9)) { + /* If gpio is high then report pen up */ + if (gpiod_get_value(jornada_ts->gpio)) { input_report_key(input, BTN_TOUCH, 0); input_sync(input); } else { @@ -101,7 +101,7 @@ static int jornada720_ts_probe(struct platform_device *pdev) { struct jornada_ts *jornada_ts; struct input_dev *input_dev; - int error; + int error, irq; jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL); if (!jornada_ts) @@ -113,6 +113,14 @@ static int jornada720_ts_probe(struct platform_device *pdev) platform_set_drvdata(pdev, jornada_ts); + jornada_ts->gpio = devm_gpiod_get(&pdev->dev, "penup", GPIOD_IN); + if (IS_ERR(jornada_ts->gpio)) + return PTR_ERR(jornada_ts->gpio); + + irq = gpiod_to_irq(jornada_ts->gpio); + if (irq <= 0) + return irq < 0 ? irq : -EINVAL; + jornada_ts->dev = input_dev; input_dev->name = "HP Jornada 7xx Touchscreen"; @@ -125,8 +133,7 @@ static int jornada720_ts_probe(struct platform_device *pdev) input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0); input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0); - error = devm_request_irq(&pdev->dev, IRQ_GPIO9, - jornada720_ts_interrupt, + error = devm_request_irq(&pdev->dev, irq, jornada720_ts_interrupt, IRQF_TRIGGER_RISING, "HP7XX Touchscreen driver", pdev); if (error) { diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index 913e25a994b4..ef64f36c5ffc 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -37,7 +37,6 @@ struct mc13783_ts_priv { struct input_dev *idev; struct mc13xxx *mc13xxx; struct delayed_work work; - struct workqueue_struct *workq; unsigned int sample[4]; struct mc13xxx_ts_platform_data *touch; }; @@ -54,7 +53,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data) * be rescheduled for immediate execution here. However the rearm * delay is HZ / 50 which is acceptable. */ - queue_delayed_work(priv->workq, &priv->work, 0); + schedule_delayed_work(&priv->work, 0); return IRQ_HANDLED; } @@ -106,16 +105,18 @@ static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv) dev_dbg(&idev->dev, "report (%d, %d, %d)\n", x1, y1, 0x1000 - cr0); - queue_delayed_work(priv->workq, &priv->work, HZ / 50); - } else + schedule_delayed_work(&priv->work, HZ / 50); + } else { dev_dbg(&idev->dev, "report release\n"); + } input_report_abs(idev, ABS_PRESSURE, cr0 ? 0x1000 - cr0 : cr0); input_report_key(idev, BTN_TOUCH, cr0); input_sync(idev); - } else + } else { dev_dbg(&idev->dev, "discard event\n"); + } } static void mc13783_ts_work(struct work_struct *work) @@ -189,14 +190,6 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) goto err_free_mem; } - /* - * We need separate workqueue because mc13783_adc_do_conversion - * uses keventd and thus would deadlock. - */ - priv->workq = create_singlethread_workqueue("mc13783_ts"); - if (!priv->workq) - goto err_free_mem; - idev->name = MC13783_TS_NAME; idev->dev.parent = &pdev->dev; @@ -215,14 +208,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "register input device failed with %d\n", ret); - goto err_destroy_wq; + goto err_free_mem; } platform_set_drvdata(pdev, priv); return 0; -err_destroy_wq: - destroy_workqueue(priv->workq); err_free_mem: input_free_device(idev); kfree(priv); @@ -233,7 +224,6 @@ static int mc13783_ts_remove(struct platform_device *pdev) { struct mc13783_ts_priv *priv = platform_get_drvdata(pdev); - destroy_workqueue(priv->workq); input_unregister_device(priv->idev); kfree(priv); diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index fb5fb9140ca9..552a3773f79d 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -157,6 +157,7 @@ struct mip4_ts { char phys[32]; char product_name[16]; + char ic_name[4]; unsigned int max_x; unsigned int max_y; @@ -263,6 +264,18 @@ static int mip4_query_device(struct mip4_ts *ts) dev_dbg(&ts->client->dev, "product name: %.*s\n", (int)sizeof(ts->product_name), ts->product_name); + /* IC name */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_IC_NAME; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), + ts->ic_name, sizeof(ts->ic_name)); + if (error) + dev_warn(&ts->client->dev, + "Failed to retrieve IC name: %d\n", error); + else + dev_dbg(&ts->client->dev, "IC name: %.*s\n", + (int)sizeof(ts->ic_name), ts->ic_name); + /* Firmware version */ error = mip4_get_fw_version(ts); if (error) @@ -1326,7 +1339,7 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, * paired with current firmware in the chip. */ count = snprintf(buf, PAGE_SIZE, "%.*s\n", - (int)sizeof(ts->product_name), ts->product_name); + (int)sizeof(ts->product_name), ts->product_name); mutex_unlock(&ts->input->mutex); @@ -1335,9 +1348,30 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL); +static ssize_t mip4_sysfs_read_ic_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + size_t count; + + mutex_lock(&ts->input->mutex); + + count = snprintf(buf, PAGE_SIZE, "%.*s\n", + (int)sizeof(ts->ic_name), ts->ic_name); + + mutex_unlock(&ts->input->mutex); + + return count; +} + +static DEVICE_ATTR(ic_name, S_IRUGO, mip4_sysfs_read_ic_name, NULL); + static struct attribute *mip4_attrs[] = { &dev_attr_fw_version.attr, &dev_attr_hw_version.attr, + &dev_attr_ic_name.attr, &dev_attr_update_fw.attr, NULL, }; @@ -1538,6 +1572,6 @@ static struct i2c_driver mip4_driver = { module_i2c_driver(mip4_driver); MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); -MODULE_VERSION("2016.03.12"); +MODULE_VERSION("2016.09.28"); MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index d159e14f4d20..3bb0637d832e 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -11,10 +11,6 @@ * 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. - * - * You should have received a copy of the GNU General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/delay.h> @@ -404,7 +400,6 @@ static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev) mutex_lock(&input->mutex); if (device_may_wakeup(&client->dev)) { - if (!input->users) { ret = pixcir_stop(ts); if (ret) { @@ -431,13 +426,7 @@ static const struct of_device_id pixcir_of_match[]; static int pixcir_parse_dt(struct device *dev, struct pixcir_i2c_ts_data *tsdata) { - const struct of_device_id *match; - - match = of_match_device(of_match_ptr(pixcir_of_match), dev); - if (!match) - return -EINVAL; - - tsdata->chip = (const struct pixcir_i2c_chip_data *)match->data; + tsdata->chip = of_device_get_match_data(dev); if (!tsdata->chip) return -EINVAL; diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index 73861ad22df4..a9132603ab34 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -23,7 +23,7 @@ #include <asm/unaligned.h> #define WDT87XX_NAME "wdt87xx_i2c" -#define WDT87XX_DRV_VER "0.9.7" +#define WDT87XX_DRV_VER "0.9.8" #define WDT87XX_FW_NAME "wdt87xx_fw.bin" #define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" @@ -157,6 +157,7 @@ /* Controller requires minimum 300us between commands */ #define WDT_COMMAND_DELAY_MS 2 #define WDT_FLASH_WRITE_DELAY_MS 4 +#define WDT_FLASH_ERASE_DELAY_MS 200 #define WDT_FW_RESET_TIME 2500 struct wdt87xx_sys_param { @@ -726,7 +727,7 @@ static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) break; } - msleep(50); + msleep(WDT_FLASH_ERASE_DELAY_MS); error = wdt87xx_write_data(client, data, start_addr, page_size); diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 1534e9b0788c..90d6be3c26cc 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -500,7 +500,7 @@ static int wm97xx_ts_input_open(struct input_dev *idev) { struct wm97xx *wm = input_get_drvdata(idev); - wm->ts_workq = create_singlethread_workqueue("kwm97xx"); + wm->ts_workq = alloc_ordered_workqueue("kwm97xx", 0); if (wm->ts_workq == NULL) { dev_err(wm->dev, "Failed to create workqueue\n"); |