Pull input subsystem updates from Dmitry Torokhov: "You will get - a new driver for Elan eKTF2127 touchscreen controllers - a new "gpio-decoder" driver to read and report state of several GPIO lines - an ADC resistor ladder driver - the ft6326 driver is removed because edt-ft5x06 handles the same devices just fine. .. plus the regular slew of driver fixes/enhancements" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (26 commits) Input: elan_i2c - fix return tests of i2c_smbus_read_block_data() Input: ektf2127 - mark PM functions as __maybe_unused Input: snvs_pwrkey - drop input_free_device call if input_register_device fails Input: add support for Elan eKTF2127 touchscreen controller Input: serio - add hangup support Input: tps65218-pwrbutton - add support for tps65217 variant Input: jornada720_ts - get rid of mach/irqs.h and mach/hardware.h includes Input: jornada720_kbd - remove unneeded mach/hardware.h include Input: focaltech - mark focaltech_set_resolution() static Input: wdt87xx_i2c - fix the flash erase issue Input: gpio-keys-polled - don't use unit-address with button nodes Input: add generic input driver to read encoded GPIO lines Input: add ADC resistor ladder driver Input: pegasus_notetaker - directly include workqueue header Input: elants_i2c - get product id on recovery mode for FW update Input: wm97xx - remove deprecated create_singletheread_workqueue Input: mc13783_ts - remove deprecated create_singletheread_workqueue Input: psmouse - remove deprecated create_singletheread_workqueue Input: jornada720_kbd - switch to using dev_dbg Input: jornada720_kbd - get rid of mach/irqs.h include ...
Unified · Split
| @@ -0,0 +1,49 @@ | ||
| 1 | +ADC attached resistor ladder buttons | |
| 2 | +------------------------------------ | |
| 3 | + | |
| 4 | +Required properties: | |
| 5 | + - compatible: "adc-keys" | |
| 6 | + - io-channels: Phandle to an ADC channel | |
| 7 | + - io-channel-names = "buttons"; | |
| 8 | + - keyup-threshold-microvolt: Voltage at which all the keys are considered up. | |
| 9 | + | |
| 10 | +Optional properties: | |
| 11 | + - poll-interval: Poll interval time in milliseconds | |
| 12 | + - autorepeat: Boolean, Enable auto repeat feature of Linux input | |
| 13 | + subsystem. | |
| 14 | + | |
| 15 | +Each button (key) is represented as a sub-node of "adc-keys": | |
| 16 | + | |
| 17 | +Required subnode-properties: | |
| 18 | + - label: Descriptive name of the key. | |
| 19 | + - linux,code: Keycode to emit. | |
| 20 | + - press-threshold-microvolt: Voltage ADC input when this key is pressed. | |
| 21 | + | |
| 22 | +Example: | |
| 23 | + | |
| 24 | +#include <dt-bindings/input/input.h> | |
| 25 | + | |
| 26 | + adc-keys { | |
| 27 | + compatible = "adc-keys"; | |
| 28 | + io-channels = <&lradc 0>; | |
| 29 | + io-channel-names = "buttons"; | |
| 30 | + keyup-threshold-microvolt = <2000000>; | |
| 31 | + | |
| 32 | + button-up { | |
| 33 | + label = "Volume Up"; | |
| 34 | + linux,code = <KEY_VOLUMEUP>; | |
| 35 | + press-threshold-microvolt = <1500000>; | |
| 36 | + }; | |
| 37 | + | |
| 38 | + button-down { | |
| 39 | + label = "Volume Down"; | |
| 40 | + linux,code = <KEY_VOLUMEDOWN>; | |
| 41 | + press-threshold-microvolt = <1000000>; | |
| 42 | + }; | |
| 43 | + | |
| 44 | + button-enter { | |
| 45 | + label = "Enter"; | |
| 46 | + linux,code = <KEY_ENTER>; | |
| 47 | + press-threshold-microvolt = <500000>; | |
| 48 | + }; | |
| 49 | + }; | |
| @@ -0,0 +1,23 @@ | ||
| 1 | +* GPIO Decoder DT bindings | |
| 2 | + | |
| 3 | +Required Properties: | |
| 4 | +- compatible: should be "gpio-decoder" | |
| 5 | +- gpios: a spec of gpios (at least two) to be decoded to a number with | |
| 6 | + first entry representing the MSB. | |
| 7 | + | |
| 8 | +Optional Properties: | |
| 9 | +- decoder-max-value: Maximum possible value that can be reported by | |
| 10 | + the gpios. | |
| 11 | +- linux,axis: the input subsystem axis to map to (ABS_X/ABS_Y). | |
| 12 | + Defaults to 0 (ABS_X). | |
| 13 | + | |
| 14 | +Example: | |
| 15 | + gpio-decoder0 { | |
| 16 | + compatible = "gpio-decoder"; | |
| 17 | + gpios = <&pca9536 3 GPIO_ACTIVE_HIGH>, | |
| 18 | + <&pca9536 2 GPIO_ACTIVE_HIGH>, | |
| 19 | + <&pca9536 1 GPIO_ACTIVE_HIGH>, | |
| 20 | + <&pca9536 0 GPIO_ACTIVE_HIGH>; | |
| 21 | + linux,axis = <0>; /* ABS_X */ | |
| 22 | + decoder-max-value = <9>; | |
| 23 | + }; | |
| @@ -34,11 +34,10 @@ Example nodes: · 33 unmodified lines | ||
| 34 | 34 | |
| 35 | 35 | gpio_keys_polled { |
| 36 | 36 | compatible = "gpio-keys-polled"; |
| 37 | − #address-cells = <1>; | |
| 38 | − #size-cells = <0>; | |
| 39 | 37 | poll-interval = <100>; |
| 40 | 38 | autorepeat; |
| 41 | − button@21 { | |
| 39 | + | |
| 40 | + button21 { | |
| 42 | 41 | label = "GPIO Key UP"; |
| 43 | 42 | linux,code = <103>; |
| 44 | 43 | gpios = <&gpio1 0 1>; |
| @@ -19,6 +19,7 @@ Required properties: · 18 unmodified lines | ||
| 19 | 19 | or: "edt,edt-ft5306" |
| 20 | 20 | or: "edt,edt-ft5406" |
| 21 | 21 | or: "edt,edt-ft5506" |
| 22 | + or: "focaltech,ft6236" | |
| 22 | 23 | |
| 23 | 24 | - reg: I2C slave address of the chip (0x38) |
| 24 | 25 | - interrupt-parent: a phandle pointing to the interrupt controller |
| @@ -43,6 +44,13 @@ Optional properties: · 18 unmodified lines | ||
| 43 | 44 | |
| 44 | 45 | - offset: allows setting the edge compensation in the range from |
| 45 | 46 | 0 to 31. |
| 47 | + - touchscreen-size-x : See touchscreen.txt | |
| 48 | + - touchscreen-size-y : See touchscreen.txt | |
| 49 | + - touchscreen-fuzz-x : See touchscreen.txt | |
| 50 | + - touchscreen-fuzz-y : See touchscreen.txt | |
| 51 | + - touchscreen-inverted-x : See touchscreen.txt | |
| 52 | + - touchscreen-inverted-y : See touchscreen.txt | |
| 53 | + - touchscreen-swapped-x-y : See touchscreen.txt | |
| 46 | 54 | |
| 47 | 55 | Example: |
| 48 | 56 | polytouch: edt-ft5x06@38 { |
| @@ -0,0 +1,27 @@ | ||
| 1 | +* Elan eKTF2127 I2C touchscreen controller | |
| 2 | + | |
| 3 | +Required properties: | |
| 4 | + - compatible : "elan,ektf2127" | |
| 5 | + - reg : I2C slave address of the chip (0x40) | |
| 6 | + - interrupt-parent : a phandle pointing to the interrupt controller | |
| 7 | + serving the interrupt for this chip | |
| 8 | + - interrupts : interrupt specification for the ektf2127 interrupt | |
| 9 | + - power-gpios : GPIO specification for the pin connected to the | |
| 10 | + ektf2127's wake input. This needs to be driven high | |
| 11 | + to take ektf2127 out of it's low power state | |
| 12 | + | |
| 13 | +For additional optional properties see: touchscreen.txt | |
| 14 | + | |
| 15 | +Example: | |
| 16 | + | |
| 17 | +i2c@00000000 { | |
| 18 | + ektf2127: touchscreen@15 { | |
| 19 | + compatible = "elan,ektf2127"; | |
| 20 | + reg = <0x15>; | |
| 21 | + interrupt-parent = <&pio>; | |
| 22 | + interrupts = <6 11 IRQ_TYPE_EDGE_FALLING> | |
| 23 | + power-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; | |
| 24 | + touchscreen-inverted-x; | |
| 25 | + touchscreen-swapped-x-y; | |
| 26 | + }; | |
| 27 | +}; | |
| @@ -1,35 +0,0 @@ | ||
| 1 | −* FocalTech FT6236 I2C touchscreen controller | |
| 2 | − | |
| 3 | −Required properties: | |
| 4 | − - compatible : "focaltech,ft6236" | |
| 5 | − - reg : I2C slave address of the chip (0x38) | |
| 6 | − - interrupt-parent : a phandle pointing to the interrupt controller | |
| 7 | − serving the interrupt for this chip | |
| 8 | − - interrupts : interrupt specification for the touch controller | |
| 9 | − interrupt | |
| 10 | − - reset-gpios : GPIO specification for the RSTN input | |
| 11 | − - touchscreen-size-x : horizontal resolution of touchscreen (in pixels) | |
| 12 | − - touchscreen-size-y : vertical resolution of touchscreen (in pixels) | |
| 13 | − | |
| 14 | −Optional properties: | |
| 15 | − - touchscreen-fuzz-x : horizontal noise value of the absolute input | |
| 16 | − device (in pixels) | |
| 17 | − - touchscreen-fuzz-y : vertical noise value of the absolute input | |
| 18 | − device (in pixels) | |
| 19 | − - touchscreen-inverted-x : X axis is inverted (boolean) | |
| 20 | − - touchscreen-inverted-y : Y axis is inverted (boolean) | |
| 21 | − - touchscreen-swapped-x-y: X and Y axis are swapped (boolean) | |
| 22 | − Swapping is done after inverting the axis | |
| 23 | − | |
| 24 | −Example: | |
| 25 | − | |
| 26 | − ft6x06@38 { | |
| 27 | − compatible = "focaltech,ft6236"; | |
| 28 | − reg = <0x38>; | |
| 29 | − interrupt-parent = <&gpio>; | |
| 30 | − interrupts = <23 2>; | |
| 31 | − touchscreen-size-x = <320>; | |
| 32 | − touchscreen-size-y = <480>; | |
| 33 | − touchscreen-inverted-x; | |
| 34 | − touchscreen-swapped-x-y; | |
| 35 | − }; | |
| @@ -1,13 +1,24 @@ | ||
| 1 | −Texas Instruments TPS65218 power button | |
| 1 | +Texas Instruments TPS65217 and TPS65218 power button | |
| 2 | + | |
| 3 | +This module is part of the TPS65217/TPS65218. For more details about the whole | |
| 4 | +TPS65217 chip see Documentation/devicetree/bindings/regulator/tps65217.txt. | |
| 2 | 5 | |
| 3 | 6 | This driver provides a simple power button event via an Interrupt. |
| 4 | 7 | |
| 5 | 8 | Required properties: |
| 6 | −- compatible: should be "ti,tps65218-pwrbutton" | |
| 9 | +- compatible: should be "ti,tps65217-pwrbutton" or "ti,tps65218-pwrbutton" | |
| 10 | + | |
| 11 | +Required properties for TPS65218: | |
| 7 | 12 | - interrupts: should be one of the following |
| 8 | 13 | - <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218 |
| 9 | 14 | |
| 10 | −Example: | |
| 15 | +Examples: | |
| 16 | + | |
| 17 | +&tps { | |
| 18 | + tps65217-pwrbutton { | |
| 19 | + compatible = "ti,tps65217-pwrbutton"; | |
| 20 | + }; | |
| 21 | +}; | |
| 11 | 22 | |
| 12 | 23 | &tps { |
| 13 | 24 | power-button { |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <linux/kernel.h> |
| 18 | 18 | #include <linux/tty.h> |
| 19 | 19 | #include <linux/delay.h> |
| 20 | +#include <linux/gpio/machine.h> | |
| 20 | 21 | #include <linux/platform_data/sa11x0-serial.h> |
| 21 | 22 | #include <linux/platform_device.h> |
| 22 | 23 | #include <linux/ioport.h> |
| @@ -217,9 +218,22 @@ static struct platform_device jornada_ssp_device = { · 194 unmodified lines | ||
| 217 | 218 | .id = -1, |
| 218 | 219 | }; |
| 219 | 220 | |
| 221 | +static struct resource jornada_kbd_resources[] = { | |
| 222 | + DEFINE_RES_IRQ(IRQ_GPIO0), | |
| 223 | +}; | |
| 224 | + | |
| 220 | 225 | static struct platform_device jornada_kbd_device = { |
| 221 | 226 | .name = "jornada720_kbd", |
| 222 | 227 | .id = -1, |
| 228 | + .num_resources = ARRAY_SIZE(jornada_kbd_resources), | |
| 229 | + .resource = jornada_kbd_resources, | |
| 230 | +}; | |
| 231 | + | |
| 232 | +static struct gpiod_lookup_table jornada_ts_gpiod_table = { | |
| 233 | + .dev_id = "jornada_ts", | |
| 234 | + .table = { | |
| 235 | + GPIO_LOOKUP("gpio", 9, "penup", GPIO_ACTIVE_HIGH), | |
| 236 | + }, | |
| 223 | 237 | }; |
| 224 | 238 | |
| 225 | 239 | static struct platform_device jornada_ts_device = { |
| @@ -250,6 +264,8 @@ static int __init jornada720_init(void) · 24 unmodified lines | ||
| 250 | 264 | GPSR = GPIO_GPIO20; /* restart gpio20 */ |
| 251 | 265 | udelay(20); /* give it some time to restart */ |
| 252 | 266 | |
| 267 | + gpiod_add_lookup_table(&jornada_ts_gpiod_table); | |
| 268 | + | |
| 253 | 269 | ret = platform_add_devices(devices, ARRAY_SIZE(devices)); |
| 254 | 270 | } |
| 255 | 271 | |
| @@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | if INPUT_KEYBOARD |
| 14 | 14 | |
| 15 | +config KEYBOARD_ADC | |
| 16 | + tristate "ADC Ladder Buttons" | |
| 17 | + depends on IIO | |
| 18 | + select INPUT_POLLDEV | |
| 19 | + help | |
| 20 | + This driver implements support for buttons connected | |
| 21 | + to an ADC using a resistor ladder. | |
| 22 | + | |
| 23 | + Say Y here if your device has such buttons connected to an ADC. Your | |
| 24 | + board-specific setup logic must also provide a configuration data | |
| 25 | + for mapping voltages to buttons. | |
| 26 | + | |
| 27 | + To compile this driver as a module, choose M here: the | |
| 28 | + module will be called adc_keys. | |
| 29 | + | |
| 15 | 30 | config KEYBOARD_ADP5520 |
| 16 | 31 | tristate "Keypad Support for ADP5520 PMIC" |
| 17 | 32 | depends on PMIC_ADP5520 |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | # Each configuration option enables a list of files. |
| 6 | 6 | |
| 7 | +obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o | |
| 7 | 8 | obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o |
| 8 | 9 | obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o |
| 9 | 10 | obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o |
| @@ -0,0 +1,210 @@ | ||
| 1 | +/* | |
| 2 | + * Input driver for resistor ladder connected on ADC | |
| 3 | + * | |
| 4 | + * Copyright (c) 2016 Alexandre Belloni | |
| 5 | + * | |
| 6 | + * This program is free software; you can redistribute it and/or modify it | |
| 7 | + * under the terms of the GNU General Public License version 2 as published by | |
| 8 | + * the Free Software Foundation. | |
| 9 | + */ | |
| 10 | + | |
| 11 | +#include <linux/err.h> | |
| 12 | +#include <linux/iio/consumer.h> | |
| 13 | +#include <linux/iio/types.h> | |
| 14 | +#include <linux/input.h> | |
| 15 | +#include <linux/input-polldev.h> | |
| 16 | +#include <linux/kernel.h> | |
| 17 | +#include <linux/module.h> | |
| 18 | +#include <linux/of.h> | |
| 19 | +#include <linux/platform_device.h> | |
| 20 | +#include <linux/property.h> | |
| 21 | +#include <linux/slab.h> | |
| 22 | + | |
| 23 | +struct adc_keys_button { | |
| 24 | + u32 voltage; | |
| 25 | + u32 keycode; | |
| 26 | +}; | |
| 27 | + | |
| 28 | +struct adc_keys_state { | |
| 29 | + struct iio_channel *channel; | |
| 30 | + u32 num_keys; | |
| 31 | + u32 last_key; | |
| 32 | + u32 keyup_voltage; | |
| 33 | + const struct adc_keys_button *map; | |
| 34 | +}; | |
| 35 | + | |
| 36 | +static void adc_keys_poll(struct input_polled_dev *dev) | |
| 37 | +{ | |
| 38 | + struct adc_keys_state *st = dev->private; | |
| 39 | + int i, value, ret; | |
| 40 | + u32 diff, closest = 0xffffffff; | |
| 41 | + int keycode = 0; | |
| 42 | + | |
| 43 | + ret = iio_read_channel_processed(st->channel, &value); | |
| 44 | + if (unlikely(ret < 0)) { | |
| 45 | + /* Forcibly release key if any was pressed */ | |
| 46 | + value = st->keyup_voltage; | |
| 47 | + } else { | |
| 48 | + for (i = 0; i < st->num_keys; i++) { | |
| 49 | + diff = abs(st->map[i].voltage - value); | |
| 50 | + if (diff < closest) { | |
| 51 | + closest = diff; | |
| 52 | + keycode = st->map[i].keycode; | |
| 53 | + } | |
| 54 | + } | |
| 55 | + } | |
| 56 | + | |
| 57 | + if (abs(st->keyup_voltage - value) < closest) | |
| 58 | + keycode = 0; | |
| 59 | + | |
| 60 | + if (st->last_key && st->last_key != keycode) | |
| 61 | + input_report_key(dev->input, st->last_key, 0); | |
| 62 | + | |
| 63 | + if (keycode) | |
| 64 | + input_report_key(dev->input, keycode, 1); | |
| 65 | + | |
| 66 | + input_sync(dev->input); | |
| 67 | + st->last_key = keycode; | |
| 68 | +} | |
| 69 | + | |
| 70 | +static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st) | |
| 71 | +{ | |
| 72 | + struct adc_keys_button *map; | |
| 73 | + struct fwnode_handle *child; | |
| 74 | + int i; | |
| 75 | + | |
| 76 | + st->num_keys = device_get_child_node_count(dev); | |
| 77 | + if (st->num_keys == 0) { | |
| 78 | + dev_err(dev, "keymap is missing\n"); | |
| 79 | + return -EINVAL; | |
| 80 | + } | |
| 81 | + | |
| 82 | + map = devm_kmalloc_array(dev, st->num_keys, sizeof(*map), GFP_KERNEL); | |
| 83 | + if (!map) | |
| 84 | + return -ENOMEM; | |
| 85 | + | |
| 86 | + i = 0; | |
| 87 | + device_for_each_child_node(dev, child) { | |
| 88 | + if (fwnode_property_read_u32(child, "press-threshold-microvolt", | |
| 89 | + &map[i].voltage)) { | |
| 90 | + dev_err(dev, "Key with invalid or missing voltage\n"); | |
| 91 | + fwnode_handle_put(child); | |
| 92 | + return -EINVAL; | |
| 93 | + } | |
| 94 | + map[i].voltage /= 1000; | |
| 95 | + | |
| 96 | + if (fwnode_property_read_u32(child, "linux,code", | |
| 97 | + &map[i].keycode)) { | |
| 98 | + dev_err(dev, "Key with invalid or missing linux,code\n"); | |
| 99 | + fwnode_handle_put(child); | |
| 100 | + return -EINVAL; | |
| 101 | + } | |
| 102 | + | |
| 103 | + i++; | |
| 104 | + } | |
| 105 | + | |
| 106 | + st->map = map; | |
| 107 | + return 0; | |
| 108 | +} | |
| 109 | + | |
| 110 | +static int adc_keys_probe(struct platform_device *pdev) | |
| 111 | +{ | |
| 112 | + struct device *dev = &pdev->dev; | |
| 113 | + struct adc_keys_state *st; | |
| 114 | + struct input_polled_dev *poll_dev; | |
| 115 | + struct input_dev *input; | |
| 116 | + enum iio_chan_type type; | |
| 117 | + int i, value; | |
| 118 | + int error; | |
| 119 | + | |
| 120 | + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); | |
| 121 | + if (!st) | |
| 122 | + return -ENOMEM; | |
| 123 | + | |
| 124 | + st->channel = devm_iio_channel_get(dev, "buttons"); | |
| 125 | + if (IS_ERR(st->channel)) | |
| 126 | + return PTR_ERR(st->channel); | |
| 127 | + | |
| 128 | + if (!st->channel->indio_dev) | |
| 129 | + return -ENXIO; | |
| 130 | + | |
| 131 | + error = iio_get_channel_type(st->channel, &type); | |
| 132 | + if (error < 0) | |
| 133 | + return error; | |
| 134 | + | |
| 135 | + if (type != IIO_VOLTAGE) { | |
| 136 | + dev_err(dev, "Incompatible channel type %d\n", type); | |
| 137 | + return -EINVAL; | |
| 138 | + } | |
| 139 | + | |
| 140 | + if (device_property_read_u32(dev, "keyup-threshold-microvolt", | |
| 141 | + &st->keyup_voltage)) { | |
| 142 | + dev_err(dev, "Invalid or missing keyup voltage\n"); | |
| 143 | + return -EINVAL; | |
| 144 | + } | |
| 145 | + st->keyup_voltage /= 1000; | |
| 146 | + | |
| 147 | + error = adc_keys_load_keymap(dev, st); | |
| 148 | + if (error) | |
| 149 | + return error; | |
| 150 | + | |
| 151 | + platform_set_drvdata(pdev, st); | |
| 152 | + | |
| 153 | + poll_dev = devm_input_allocate_polled_device(dev); | |
| 154 | + if (!poll_dev) { | |
| 155 | + dev_err(dev, "failed to allocate input device\n"); | |
| 156 | + return -ENOMEM; | |
| 157 | + } | |
| 158 | + | |
| 159 | + if (!device_property_read_u32(dev, "poll-interval", &value)) | |
| 160 | + poll_dev->poll_interval = value; | |
| 161 | + | |
| 162 | + poll_dev->poll = adc_keys_poll; | |
| 163 | + poll_dev->private = st; | |
| 164 | + | |
| 165 | + input = poll_dev->input; | |
| 166 | + | |
| 167 | + input->name = pdev->name; | |
| 168 | + input->phys = "adc-keys/input0"; | |
| 169 | + | |
| 170 | + input->id.bustype = BUS_HOST; | |
| 171 | + input->id.vendor = 0x0001; | |
| 172 | + input->id.product = 0x0001; | |
| 173 | + input->id.version = 0x0100; | |
| 174 | + | |
| 175 | + __set_bit(EV_KEY, input->evbit); | |
| 176 | + for (i = 0; i < st->num_keys; i++) | |
| 177 | + __set_bit(st->map[i].keycode, input->keybit); | |
| 178 | + | |
| 179 | + if (device_property_read_bool(dev, "autorepeat")) | |
| 180 | + __set_bit(EV_REP, input->evbit); | |
| 181 | + | |
| 182 | + error = input_register_polled_device(poll_dev); | |
| 183 | + if (error) { | |
| 184 | + dev_err(dev, "Unable to register input device: %d\n", error); | |
| 185 | + return error; | |
| 186 | + } | |
| 187 | + | |
| 188 | + return 0; | |
| 189 | +} | |
| 190 | + | |
| 191 | +#ifdef CONFIG_OF | |
| 192 | +static const struct of_device_id adc_keys_of_match[] = { | |
| 193 | + { .compatible = "adc-keys", }, | |
| 194 | + { } | |
| 195 | +}; | |
| 196 | +MODULE_DEVICE_TABLE(of, adc_keys_of_match); | |
| 197 | +#endif | |
| 198 | + | |
| 199 | +static struct platform_driver __refdata adc_keys_driver = { | |
| 200 | + .driver = { | |
| 201 | + .name = "adc_keys", | |
| 202 | + .of_match_table = of_match_ptr(adc_keys_of_match), | |
| 203 | + }, | |
| 204 | + .probe = adc_keys_probe, | |
| 205 | +}; | |
| 206 | +module_platform_driver(adc_keys_driver); | |
| 207 | + | |
| 208 | +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); | |
| 209 | +MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC"); | |
| 210 | +MODULE_LICENSE("GPL v2"); | |
| @@ -25,8 +25,6 @@ · 24 unmodified lines | ||
| 25 | 25 | #include <linux/slab.h> |
| 26 | 26 | |
| 27 | 27 | #include <mach/jornada720.h> |
| 28 | −#include <mach/hardware.h> | |
| 29 | −#include <mach/irqs.h> | |
| 30 | 28 | |
| 31 | 29 | MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>"); |
| 32 | 30 | MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); |
| @@ -66,10 +64,8 @@ static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id) · 33 unmodified lines | ||
| 66 | 64 | jornada_ssp_start(); |
| 67 | 65 | |
| 68 | 66 | if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) { |
| 69 | − printk(KERN_DEBUG | |
| 70 | − "jornada720_kbd: " | |
| 71 | − "GetKeycode command failed with ETIMEDOUT, " | |
| 72 | − "flushed bus\n"); | |
| 67 | + dev_dbg(&pdev->dev, | |
| 68 | + "GetKeycode command failed with ETIMEDOUT, flushed bus\n"); | |
| 73 | 69 | } else { |
| 74 | 70 | /* How many keycodes are waiting for us? */ |
| 75 | 71 | count = jornada_ssp_byte(TXDUMMY); |
| @@ -97,14 +93,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev) · 21 unmodified lines | ||
| 97 | 93 | { |
| 98 | 94 | struct jornadakbd *jornadakbd; |
| 99 | 95 | struct input_dev *input_dev; |
| 100 | − int i, err; | |
| 96 | + int i, err, irq; | |
| 101 | 97 | |
| 102 | − jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL); | |
| 103 | − input_dev = input_allocate_device(); | |
| 104 | − if (!jornadakbd || !input_dev) { | |
| 105 | − err = -ENOMEM; | |
| 106 | − goto fail1; | |
| 107 | − } | |
| 98 | + irq = platform_get_irq(pdev, 0); | |
| 99 | + if (irq <= 0) | |
| 100 | + return irq < 0 ? irq : -EINVAL; | |
| 101 | + | |
| 102 | + jornadakbd = devm_kzalloc(&pdev->dev, sizeof(*jornadakbd), GFP_KERNEL); | |
| 103 | + input_dev = devm_input_allocate_device(&pdev->dev); | |
| 104 | + if (!jornadakbd || !input_dev) | |
| 105 | + return -ENOMEM; | |
| 108 | 106 | |
| 109 | 107 | platform_set_drvdata(pdev, jornadakbd); |
| 110 | 108 | |
| @@ -127,40 +125,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev) · 16 unmodified lines | ||
| 127 | 125 | |
| 128 | 126 | input_set_capability(input_dev, EV_MSC, MSC_SCAN); |
| 129 | 127 | |
| 130 | − err = request_irq(IRQ_GPIO0, | |
| 131 | − jornada720_kbd_interrupt, | |
| 132 | − IRQF_TRIGGER_FALLING, | |
| 133 | − "jornadakbd", pdev); | |
| 128 | + err = devm_request_irq(&pdev->dev, irq, jornada720_kbd_interrupt, | |
| 129 | + IRQF_TRIGGER_FALLING, "jornadakbd", pdev); | |
| 134 | 130 | if (err) { |
| 135 | − printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n"); | |
| 136 | − goto fail1; | |
| 131 | + dev_err(&pdev->dev, "unable to grab IRQ%d: %d\n", irq, err); | |
| 132 | + return err; | |
| 137 | 133 | } |
| 138 | 134 | |
| 139 | − err = input_register_device(jornadakbd->input); | |
| 140 | − if (err) | |
| 141 | − goto fail2; | |
| 142 | − | |
| 143 | − return 0; | |
| 144 | − | |
| 145 | − fail2: /* IRQ, DEVICE, MEMORY */ | |
| 146 | − free_irq(IRQ_GPIO0, pdev); | |
| 147 | − fail1: /* DEVICE, MEMORY */ | |
| 148 | − input_free_device(input_dev); | |
| 149 | − kfree(jornadakbd); | |
| 150 | − return err; | |
| 135 | + return input_register_device(jornadakbd->input); | |
| 151 | 136 | }; |
| 152 | 137 | |
| 153 | −static int jornada720_kbd_remove(struct platform_device *pdev) | |
| 154 | −{ | |
| 155 | − struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); | |
| 156 | − | |
| 157 | − free_irq(IRQ_GPIO0, pdev); | |
| 158 | − input_unregister_device(jornadakbd->input); | |
| 159 | − kfree(jornadakbd); | |
| 160 | − | |
| 161 | − return 0; | |
| 162 | −} | |
| 163 | − | |
| 164 | 138 | /* work with hotplug and coldplug */ |
| 165 | 139 | MODULE_ALIAS("platform:jornada720_kbd"); |
| 166 | 140 | |
| @@ -169,6 +143,5 @@ static struct platform_driver jornada720_kbd_driver = { · 2 unmodified lines | ||
| 169 | 143 | .name = "jornada720_kbd", |
| 170 | 144 | }, |
| 171 | 145 | .probe = jornada720_kbd_probe, |
| 172 | − .remove = jornada720_kbd_remove, | |
| 173 | 146 | }; |
| 174 | 147 | module_platform_driver(jornada720_kbd_driver); |
| @@ -168,7 +168,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) · 167 unmodified lines | ||
| 168 | 168 | error = input_register_device(input); |
| 169 | 169 | if (error < 0) { |
| 170 | 170 | dev_err(&pdev->dev, "failed to register input device\n"); |
| 171 | − input_free_device(input); | |
| 172 | 171 | return error; |
| 173 | 172 | } |
| 174 | 173 | |
| @@ -292,6 +292,18 @@ config INPUT_GPIO_TILT_POLLED · 291 unmodified lines | ||
| 292 | 292 | To compile this driver as a module, choose M here: the |
| 293 | 293 | module will be called gpio_tilt_polled. |
| 294 | 294 | |
| 295 | +config INPUT_GPIO_DECODER | |
| 296 | + tristate "Polled GPIO Decoder Input driver" | |
| 297 | + depends on GPIOLIB || COMPILE_TEST | |
| 298 | + select INPUT_POLLDEV | |
| 299 | + help | |
| 300 | + Say Y here if you want driver to read status of multiple GPIO | |
| 301 | + lines and report the encoded value as an absolute integer to | |
| 302 | + input subsystem. | |
| 303 | + | |
| 304 | + To compile this driver as a module, choose M here: the module | |
| 305 | + will be called gpio_decoder. | |
| 306 | + | |
| 295 | 307 | config INPUT_IXP4XX_BEEPER |
| 296 | 308 | tristate "IXP4XX Beeper support" |
| 297 | 309 | depends on ARCH_IXP4XX |
| @@ -454,10 +466,10 @@ config INPUT_RETU_PWRBUTTON · 156 unmodified lines | ||
| 454 | 466 | |
| 455 | 467 | config INPUT_TPS65218_PWRBUTTON |
| 456 | 468 | tristate "TPS65218 Power button driver" |
| 457 | − depends on MFD_TPS65218 | |
| 469 | + depends on (MFD_TPS65217 || MFD_TPS65218) | |
| 458 | 470 | help |
| 459 | 471 | Say Y here if you want to enable power buttong reporting for |
| 460 | − the TPS65218 Power Management IC device. | |
| 472 | + TPS65217 and TPS65218 Power Management IC devices. | |
| 461 | 473 | |
| 462 | 474 | To compile this driver as a module, choose M here. The module will |
| 463 | 475 | be called tps65218-pwrbutton. |
| @@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o · 34 unmodified lines | ||
| 35 | 35 | obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o |
| 36 | 36 | obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o |
| 37 | 37 | obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o |
| 38 | +obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o | |
| 38 | 39 | obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o |
| 39 | 40 | obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o |
| 40 | 41 | obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o |
| @@ -0,0 +1,137 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ | |
| 3 | + * | |
| 4 | + * This program is free software; you can redistribute it and/or | |
| 5 | + * modify it under the terms of the GNU General Public License as | |
| 6 | + * published by the Free Software Foundation version 2. | |
| 7 | + * | |
| 8 | + * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
| 9 | + * kind, whether express or implied; without even the implied warranty | |
| 10 | + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 11 | + * GNU General Public License for more details. | |
| 12 | + * | |
| 13 | + * A generic driver to read multiple gpio lines and translate the | |
| 14 | + * encoded numeric value into an input event. | |
| 15 | + */ | |
| 16 | + | |
| 17 | +#include <linux/device.h> | |
| 18 | +#include <linux/gpio/consumer.h> | |
| 19 | +#include <linux/input.h> | |
| 20 | +#include <linux/input-polldev.h> | |
| 21 | +#include <linux/kernel.h> | |
| 22 | +#include <linux/module.h> | |
| 23 | +#include <linux/of.h> | |
| 24 | +#include <linux/platform_device.h> | |
| 25 | + | |
| 26 | +struct gpio_decoder { | |
| 27 | + struct input_polled_dev *poll_dev; | |
| 28 | + struct gpio_descs *input_gpios; | |
| 29 | + struct device *dev; | |
| 30 | + u32 axis; | |
| 31 | + u32 last_stable; | |
| 32 | +}; | |
| 33 | + | |
| 34 | +static int gpio_decoder_get_gpios_state(struct gpio_decoder *decoder) | |
| 35 | +{ | |
| 36 | + struct gpio_descs *gpios = decoder->input_gpios; | |
| 37 | + unsigned int ret = 0; | |
| 38 | + int i, val; | |
| 39 | + | |
| 40 | + for (i = 0; i < gpios->ndescs; i++) { | |
| 41 | + val = gpiod_get_value_cansleep(gpios->desc[i]); | |
| 42 | + if (val < 0) { | |
| 43 | + dev_err(decoder->dev, | |
| 44 | + "Error reading gpio %d: %d\n", | |
| 45 | + desc_to_gpio(gpios->desc[i]), val); | |
| 46 | + return val; | |
| 47 | + } | |
| 48 | + | |
| 49 | + val = !!val; | |
| 50 | + ret = (ret << 1) | val; | |
| 51 | + } | |
| 52 | + | |
| 53 | + return ret; | |
| 54 | +} | |
| 55 | + | |
| 56 | +static void gpio_decoder_poll_gpios(struct input_polled_dev *poll_dev) | |
| 57 | +{ | |
| 58 | + struct gpio_decoder *decoder = poll_dev->private; | |
| 59 | + int state; | |
| 60 | + | |
| 61 | + state = gpio_decoder_get_gpios_state(decoder); | |
| 62 | + if (state >= 0 && state != decoder->last_stable) { | |
| 63 | + input_report_abs(poll_dev->input, decoder->axis, state); | |
| 64 | + input_sync(poll_dev->input); | |
| 65 | + decoder->last_stable = state; | |
| 66 | + } | |
| 67 | +} | |
| 68 | + | |
| 69 | +static int gpio_decoder_probe(struct platform_device *pdev) | |
| 70 | +{ | |
| 71 | + struct device *dev = &pdev->dev; | |
| 72 | + struct gpio_decoder *decoder; | |
| 73 | + struct input_polled_dev *poll_dev; | |
| 74 | + u32 max; | |
| 75 | + int err; | |
| 76 | + | |
| 77 | + decoder = devm_kzalloc(dev, sizeof(struct gpio_decoder), GFP_KERNEL); | |
| 78 | + if (!decoder) | |
| 79 | + return -ENOMEM; | |
| 80 | + | |
| 81 | + device_property_read_u32(dev, "linux,axis", &decoder->axis); | |
| 82 | + decoder->input_gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); | |
| 83 | + if (IS_ERR(decoder->input_gpios)) { | |
| 84 | + dev_err(dev, "unable to acquire input gpios\n"); | |
| 85 | + return PTR_ERR(decoder->input_gpios); | |
| 86 | + } | |
| 87 | + if (decoder->input_gpios->ndescs < 2) { | |
| 88 | + dev_err(dev, "not enough gpios found\n"); | |
| 89 | + return -EINVAL; | |
| 90 | + } | |
| 91 | + | |
| 92 | + if (device_property_read_u32(dev, "decoder-max-value", &max)) | |
| 93 | + max = (1U << decoder->input_gpios->ndescs) - 1; | |
| 94 | + | |
| 95 | + decoder->dev = dev; | |
| 96 | + poll_dev = devm_input_allocate_polled_device(decoder->dev); | |
| 97 | + if (!poll_dev) | |
| 98 | + return -ENOMEM; | |
| 99 | + | |
| 100 | + poll_dev->private = decoder; | |
| 101 | + poll_dev->poll = gpio_decoder_poll_gpios; | |
| 102 | + decoder->poll_dev = poll_dev; | |
| 103 | + | |
| 104 | + poll_dev->input->name = pdev->name; | |
| 105 | + poll_dev->input->id.bustype = BUS_HOST; | |
| 106 | + input_set_abs_params(poll_dev->input, decoder->axis, 0, max, 0, 0); | |
| 107 | + | |
| 108 | + err = input_register_polled_device(poll_dev); | |
| 109 | + if (err) { | |
| 110 | + dev_err(dev, "failed to register polled device\n"); | |
| 111 | + return err; | |
| 112 | + } | |
| 113 | + platform_set_drvdata(pdev, decoder); | |
| 114 | + | |
| 115 | + return 0; | |
| 116 | +} | |
| 117 | + | |
| 118 | +#ifdef CONFIG_OF | |
| 119 | +static const struct of_device_id gpio_decoder_of_match[] = { | |
| 120 | + { .compatible = "gpio-decoder", }, | |
| 121 | + { }, | |
| 122 | +}; | |
| 123 | +MODULE_DEVICE_TABLE(of, gpio_decoder_of_match); | |
| 124 | +#endif | |
| 125 | + | |
| 126 | +static struct platform_driver gpio_decoder_driver = { | |
| 127 | + .probe = gpio_decoder_probe, | |
| 128 | + .driver = { | |
| 129 | + .name = "gpio-decoder", | |
| 130 | + .of_match_table = of_match_ptr(gpio_decoder_of_match), | |
| 131 | + } | |
| 132 | +}; | |
| 133 | +module_platform_driver(gpio_decoder_driver); | |
| 134 | + | |
| 135 | +MODULE_DESCRIPTION("GPIO decoder input driver"); | |
| 136 | +MODULE_AUTHOR("Vignesh R <vigneshr@ti.com>"); | |
| 137 | +MODULE_LICENSE("GPL v2"); | |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | * |
| 4 | 4 | * Copyright (C) 2014,2015 Samsung Electronics |
| 5 | 5 | * Jaewon Kim <jaewon02.kim@samsung.com> |
| 6 | − * Krzysztof Kozlowski <k.kozlowski@samsung.com> | |
| 6 | + * Krzysztof Kozlowski <krzk@kernel.org> | |
| 7 | 7 | * |
| 8 | 8 | * This program is not provided / owned by Maxim Integrated Products. |
| 9 | 9 | * |
| @@ -426,7 +426,7 @@ static struct platform_driver max77693_haptic_driver = { · 416 unmodified lines | ||
| 426 | 426 | module_platform_driver(max77693_haptic_driver); |
| 427 | 427 | |
| 428 | 428 | MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); |
| 429 | −MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>"); | |
| 429 | +MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); | |
| 430 | 430 | MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver"); |
| 431 | 431 | MODULE_ALIAS("platform:max77693-haptic"); |
| 432 | 432 | MODULE_LICENSE("GPL"); |
| @@ -1,8 +1,9 @@ | ||
| 1 | 1 | /* |
| 2 | − * Texas Instruments' TPS65218 Power Button Input Driver | |
| 2 | + * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver | |
| 3 | 3 | * |
| 4 | 4 | * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ |
| 5 | 5 | * Author: Felipe Balbi <balbi@ti.com> |
| 6 | + * Author: Marcin Niestroj <m.niestroj@grinn-global.com> | |
| 6 | 7 | * |
| 7 | 8 | * This program is free software; you can redistribute it and/or modify |
| 8 | 9 | * it under the terms of the GNU General Public License version 2 as |
| @@ -18,31 +19,61 @@ · 9 unmodified lines | ||
| 18 | 19 | #include <linux/input.h> |
| 19 | 20 | #include <linux/interrupt.h> |
| 20 | 21 | #include <linux/kernel.h> |
| 22 | +#include <linux/mfd/tps65217.h> | |
| 21 | 23 | #include <linux/mfd/tps65218.h> |
| 22 | 24 | #include <linux/module.h> |
| 23 | 25 | #include <linux/of.h> |
| 24 | 26 | #include <linux/platform_device.h> |
| 27 | +#include <linux/regmap.h> | |
| 25 | 28 | #include <linux/slab.h> |
| 26 | 29 | |
| 27 | −struct tps65218_pwrbutton { | |
| 30 | +struct tps6521x_data { | |
| 31 | + unsigned int reg_status; | |
| 32 | + unsigned int pb_mask; | |
| 33 | + const char *name; | |
| 34 | +}; | |
| 35 | + | |
| 36 | +static const struct tps6521x_data tps65217_data = { | |
| 37 | + .reg_status = TPS65217_REG_STATUS, | |
| 38 | + .pb_mask = TPS65217_STATUS_PB, | |
| 39 | + .name = "tps65217_pwrbutton", | |
| 40 | +}; | |
| 41 | + | |
| 42 | +static const struct tps6521x_data tps65218_data = { | |
| 43 | + .reg_status = TPS65218_REG_STATUS, | |
| 44 | + .pb_mask = TPS65218_STATUS_PB_STATE, | |
| 45 | + .name = "tps65218_pwrbutton", | |
| 46 | +}; | |
| 47 | + | |
| 48 | +struct tps6521x_pwrbutton { | |
| 28 | 49 | struct device *dev; |
| 29 | − struct tps65218 *tps; | |
| 50 | + struct regmap *regmap; | |
| 30 | 51 | struct input_dev *idev; |
| 52 | + const struct tps6521x_data *data; | |
| 53 | + char phys[32]; | |
| 54 | +}; | |
| 55 | + | |
| 56 | +static const struct of_device_id of_tps6521x_pb_match[] = { | |
| 57 | + { .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data }, | |
| 58 | + { .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data }, | |
| 59 | + { }, | |
| 31 | 60 | }; |
| 61 | +MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match); | |
| 32 | 62 | |
| 33 | −static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr) | |
| 63 | +static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr) | |
| 34 | 64 | { |
| 35 | − struct tps65218_pwrbutton *pwr = _pwr; | |
| 65 | + struct tps6521x_pwrbutton *pwr = _pwr; | |
| 66 | + const struct tps6521x_data *tps_data = pwr->data; | |
| 36 | 67 | unsigned int reg; |
| 37 | 68 | int error; |
| 38 | 69 | |
| 39 | − error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, ®); | |
| 70 | + error = regmap_read(pwr->regmap, tps_data->reg_status, ®); | |
| 40 | 71 | if (error) { |
| 41 | 72 | dev_err(pwr->dev, "can't read register: %d\n", error); |
| 42 | 73 | goto out; |
| 43 | 74 | } |
| 44 | 75 | |
| 45 | − if (reg & TPS65218_STATUS_PB_STATE) { | |
| 76 | + if (reg & tps_data->pb_mask) { | |
| 46 | 77 | input_report_key(pwr->idev, KEY_POWER, 1); |
| 47 | 78 | pm_wakeup_event(pwr->dev, 0); |
| 48 | 79 | } else { |
| @@ -55,42 +86,55 @@ out: · 6 unmodified lines | ||
| 55 | 86 | return IRQ_HANDLED; |
| 56 | 87 | } |
| 57 | 88 | |
| 58 | −static int tps65218_pwron_probe(struct platform_device *pdev) | |
| 89 | +static int tps6521x_pb_probe(struct platform_device *pdev) | |
| 59 | 90 | { |
| 60 | − struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent); | |
| 61 | 91 | struct device *dev = &pdev->dev; |
| 62 | − struct tps65218_pwrbutton *pwr; | |
| 92 | + struct tps6521x_pwrbutton *pwr; | |
| 63 | 93 | struct input_dev *idev; |
| 94 | + const struct of_device_id *match; | |
| 64 | 95 | int error; |
| 65 | 96 | int irq; |
| 66 | 97 | |
| 98 | + match = of_match_node(of_tps6521x_pb_match, pdev->dev.of_node); | |
| 99 | + if (!match) | |
| 100 | + return -ENXIO; | |
| 101 | + | |
| 67 | 102 | pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); |
| 68 | 103 | if (!pwr) |
| 69 | 104 | return -ENOMEM; |
| 70 | 105 | |
| 106 | + pwr->data = match->data; | |
| 107 | + | |
| 71 | 108 | idev = devm_input_allocate_device(dev); |
| 72 | 109 | if (!idev) |
| 73 | 110 | return -ENOMEM; |
| 74 | 111 | |
| 75 | − idev->name = "tps65218_pwrbutton"; | |
| 76 | − idev->phys = "tps65218_pwrbutton/input0"; | |
| 112 | + idev->name = pwr->data->name; | |
| 113 | + snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", | |
| 114 | + pwr->data->name); | |
| 115 | + idev->phys = pwr->phys; | |
| 77 | 116 | idev->dev.parent = dev; |
| 78 | 117 | idev->id.bustype = BUS_I2C; |
| 79 | 118 | |
| 80 | 119 | input_set_capability(idev, EV_KEY, KEY_POWER); |
| 81 | 120 | |
| 82 | − pwr->tps = tps; | |
| 121 | + pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL); | |
| 83 | 122 | pwr->dev = dev; |
| 84 | 123 | pwr->idev = idev; |
| 85 | 124 | platform_set_drvdata(pdev, pwr); |
| 86 | 125 | device_init_wakeup(dev, true); |
| 87 | 126 | |
| 88 | 127 | irq = platform_get_irq(pdev, 0); |
| 89 | − error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq, | |
| 128 | + if (irq < 0) { | |
| 129 | + dev_err(dev, "No IRQ resource!\n"); | |
| 130 | + return -EINVAL; | |
| 131 | + } | |
| 132 | + | |
| 133 | + error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq, | |
| 90 | 134 | IRQF_TRIGGER_RISING | |
| 91 | 135 | IRQF_TRIGGER_FALLING | |
| 92 | 136 | IRQF_ONESHOT, |
| 93 | − "tps65218-pwrbutton", pwr); | |
| 137 | + pwr->data->name, pwr); | |
| 94 | 138 | if (error) { |
| 95 | 139 | dev_err(dev, "failed to request IRQ #%d: %d\n", |
| 96 | 140 | irq, error); |
| @@ -106,21 +150,15 @@ static int tps65218_pwron_probe(struct platform_device *pdev) · 9 unmodified lines | ||
| 106 | 150 | return 0; |
| 107 | 151 | } |
| 108 | 152 | |
| 109 | −static const struct of_device_id of_tps65218_pwr_match[] = { | |
| 110 | − { .compatible = "ti,tps65218-pwrbutton" }, | |
| 111 | − { }, | |
| 112 | −}; | |
| 113 | −MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match); | |
| 114 | − | |
| 115 | −static struct platform_driver tps65218_pwron_driver = { | |
| 116 | − .probe = tps65218_pwron_probe, | |
| 153 | +static struct platform_driver tps6521x_pb_driver = { | |
| 154 | + .probe = tps6521x_pb_probe, | |
| 117 | 155 | .driver = { |
| 118 | − .name = "tps65218_pwrbutton", | |
| 119 | − .of_match_table = of_tps65218_pwr_match, | |
| 156 | + .name = "tps6521x_pwrbutton", | |
| 157 | + .of_match_table = of_tps6521x_pb_match, | |
| 120 | 158 | }, |
| 121 | 159 | }; |
| 122 | −module_platform_driver(tps65218_pwron_driver); | |
| 160 | +module_platform_driver(tps6521x_pb_driver); | |
| 123 | 161 | |
| 124 | −MODULE_DESCRIPTION("TPS65218 Power Button"); | |
| 162 | +MODULE_DESCRIPTION("TPS6521X Power Button"); | |
| 125 | 163 | MODULE_LICENSE("GPL v2"); |
| 126 | 164 | MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); |
| @@ -222,11 +222,13 @@ static int elan_smbus_get_checksum(struct i2c_client *client, · 221 unmodified lines | ||
| 222 | 222 | static int elan_smbus_get_max(struct i2c_client *client, |
| 223 | 223 | unsigned int *max_x, unsigned int *max_y) |
| 224 | 224 | { |
| 225 | + int ret; | |
| 225 | 226 | int error; |
| 226 | 227 | u8 val[3]; |
| 227 | 228 | |
| 228 | − error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val); | |
| 229 | − if (error) { | |
| 229 | + ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val); | |
| 230 | + if (ret != 3) { | |
| 231 | + error = ret < 0 ? ret : -EIO; | |
| 230 | 232 | dev_err(&client->dev, "failed to get dimensions: %d\n", error); |
| 231 | 233 | return error; |
| 232 | 234 | } |
| @@ -240,12 +242,13 @@ static int elan_smbus_get_max(struct i2c_client *client, · 7 unmodified lines | ||
| 240 | 242 | static int elan_smbus_get_resolution(struct i2c_client *client, |
| 241 | 243 | u8 *hw_res_x, u8 *hw_res_y) |
| 242 | 244 | { |
| 245 | + int ret; | |
| 243 | 246 | int error; |
| 244 | 247 | u8 val[3]; |
| 245 | 248 | |
| 246 | − error = i2c_smbus_read_block_data(client, | |
| 247 | − ETP_SMBUS_RESOLUTION_CMD, val); | |
| 248 | − if (error) { | |
| 249 | + ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val); | |
| 250 | + if (ret != 3) { | |
| 251 | + error = ret < 0 ? ret : -EIO; | |
| 249 | 252 | dev_err(&client->dev, "failed to get resolution: %d\n", error); |
| 250 | 253 | return error; |
| 251 | 254 | } |
| @@ -260,12 +263,13 @@ static int elan_smbus_get_num_traces(struct i2c_client *client, · 8 unmodified lines | ||
| 260 | 263 | unsigned int *x_traces, |
| 261 | 264 | unsigned int *y_traces) |
| 262 | 265 | { |
| 266 | + int ret; | |
| 263 | 267 | int error; |
| 264 | 268 | u8 val[3]; |
| 265 | 269 | |
| 266 | − error = i2c_smbus_read_block_data(client, | |
| 267 | − ETP_SMBUS_XY_TRACENUM_CMD, val); | |
| 268 | − if (error) { | |
| 270 | + ret = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val); | |
| 271 | + if (ret != 3) { | |
| 272 | + error = ret < 0 ? ret : -EIO; | |
| 269 | 273 | dev_err(&client->dev, "failed to get trace info: %d\n", error); |
| 270 | 274 | return error; |
| 271 | 275 | } |
| @@ -390,7 +390,8 @@ static int focaltech_read_size(struct psmouse *psmouse) · 389 unmodified lines | ||
| 390 | 390 | return 0; |
| 391 | 391 | } |
| 392 | 392 | |
| 393 | −void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution) | |
| 393 | +static void focaltech_set_resolution(struct psmouse *psmouse, | |
| 394 | + unsigned int resolution) | |
| 394 | 395 | { |
| 395 | 396 | /* not supported yet */ |
| 396 | 397 | } |
| @@ -1916,7 +1916,7 @@ static int __init psmouse_init(void) · 1915 unmodified lines | ||
| 1916 | 1916 | synaptics_module_init(); |
| 1917 | 1917 | hgpk_module_init(); |
| 1918 | 1918 | |
| 1919 | − kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); | |
| 1919 | + kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0); | |
| 1920 | 1920 | if (!kpsmoused_wq) { |
| 1921 | 1921 | pr_err("failed to create kpsmoused workqueue\n"); |
| 1922 | 1922 | return -ENOMEM; |
| @@ -71,10 +71,7 @@ static void serport_serio_close(struct serio *serio) · 70 unmodified lines | ||
| 71 | 71 | |
| 72 | 72 | spin_lock_irqsave(&serport->lock, flags); |
| 73 | 73 | clear_bit(SERPORT_ACTIVE, &serport->flags); |
| 74 | − set_bit(SERPORT_DEAD, &serport->flags); | |
| 75 | 74 | spin_unlock_irqrestore(&serport->lock, flags); |
| 76 | − | |
| 77 | − wake_up_interruptible(&serport->wait); | |
| 78 | 75 | } |
| 79 | 76 | |
| 80 | 77 | /* |
| @@ -248,6 +245,19 @@ static long serport_ldisc_compat_ioctl(struct tty_struct *tty, · 167 unmodified lines | ||
| 248 | 245 | } |
| 249 | 246 | #endif |
| 250 | 247 | |
| 248 | +static int serport_ldisc_hangup(struct tty_struct *tty) | |
| 249 | +{ | |
| 250 | + struct serport *serport = (struct serport *) tty->disc_data; | |
| 251 | + unsigned long flags; | |
| 252 | + | |
| 253 | + spin_lock_irqsave(&serport->lock, flags); | |
| 254 | + set_bit(SERPORT_DEAD, &serport->flags); | |
| 255 | + spin_unlock_irqrestore(&serport->lock, flags); | |
| 256 | + | |
| 257 | + wake_up_interruptible(&serport->wait); | |
| 258 | + return 0; | |
| 259 | +} | |
| 260 | + | |
| 251 | 261 | static void serport_ldisc_write_wakeup(struct tty_struct * tty) |
| 252 | 262 | { |
| 253 | 263 | struct serport *serport = (struct serport *) tty->disc_data; |
| @@ -274,6 +284,7 @@ static struct tty_ldisc_ops serport_ldisc = { · 20 unmodified lines | ||
| 274 | 284 | .compat_ioctl = serport_ldisc_compat_ioctl, |
| 275 | 285 | #endif |
| 276 | 286 | .receive_buf = serport_ldisc_receive, |
| 287 | + .hangup = serport_ldisc_hangup, | |
| 277 | 288 | .write_wakeup = serport_ldisc_write_wakeup |
| 278 | 289 | }; |
| 279 | 290 | |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | ||
| 40 | 40 | #include <linux/input.h> |
| 41 | 41 | #include <linux/usb/input.h> |
| 42 | 42 | #include <linux/slab.h> |
| 43 | +#include <linux/workqueue.h> | |
| 43 | 44 | |
| 44 | 45 | /* USB HID defines */ |
| 45 | 46 | #define USB_REQ_GET_REPORT 0x01 |
| @@ -305,19 +305,6 @@ config TOUCHSCREEN_EGALAX_SERIAL · 304 unmodified lines | ||
| 305 | 305 | To compile this driver as a module, choose M here: the |
| 306 | 306 | module will be called egalax_ts_serial. |
| 307 | 307 | |
| 308 | −config TOUCHSCREEN_FT6236 | |
| 309 | − tristate "FT6236 I2C touchscreen" | |
| 310 | − depends on I2C | |
| 311 | − depends on GPIOLIB || COMPILE_TEST | |
| 312 | − help | |
| 313 | − Say Y here to enable support for the I2C connected FT6x06 and | |
| 314 | − FT6x36 family of capacitive touchscreen drivers. | |
| 315 | − | |
| 316 | − If unsure, say N. | |
| 317 | − | |
| 318 | − To compile this driver as a module, choose M here: the | |
| 319 | − module will be called ft6236. | |
| 320 | − | |
| 321 | 308 | config TOUCHSCREEN_FUJITSU |
| 322 | 309 | tristate "Fujitsu serial touchscreen" |
| 323 | 310 | select SERIO |
| @@ -397,6 +384,18 @@ config TOUCHSCREEN_GUNZE · 73 unmodified lines | ||
| 397 | 384 | To compile this driver as a module, choose M here: the |
| 398 | 385 | module will be called gunze. |
| 399 | 386 | |
| 387 | +config TOUCHSCREEN_EKTF2127 | |
| 388 | + tristate "Elan eKTF2127 I2C touchscreen" | |
| 389 | + depends on I2C | |
| 390 | + help | |
| 391 | + Say Y here if you have an Elan eKTF2127 touchscreen | |
| 392 | + connected to your system. | |
| 393 | + | |
| 394 | + If unsure, say N. | |
| 395 | + | |
| 396 | + To compile this driver as a module, choose M here: the | |
| 397 | + module will be called ektf2127. | |
| 398 | + | |
| 400 | 399 | config TOUCHSCREEN_ELAN |
| 401 | 400 | tristate "Elan eKTH I2C touchscreen" |
| 402 | 401 | depends on I2C |
| @@ -32,11 +32,11 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o · 31 unmodified lines | ||
| 32 | 32 | obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o |
| 33 | 33 | obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o |
| 34 | 34 | obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o |
| 35 | +obj-$(CONFIG_TOUCHSCREEN_EKTF2127) += ektf2127.o | |
| 35 | 36 | obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o |
| 36 | 37 | obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o |
| 37 | 38 | obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o |
| 38 | 39 | obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o |
| 39 | −obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o | |
| 40 | 40 | obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o |
| 41 | 41 | obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o |
| 42 | 42 | obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o |
| @@ -1063,9 +1063,15 @@ static const struct edt_i2c_chip_data edt_ft5506_data = { · 1062 unmodified lines | ||
| 1063 | 1063 | .max_support_points = 10, |
| 1064 | 1064 | }; |
| 1065 | 1065 | |
| 1066 | +static const struct edt_i2c_chip_data edt_ft6236_data = { | |
| 1067 | + .max_support_points = 2, | |
| 1068 | +}; | |
| 1069 | + | |
| 1066 | 1070 | static const struct i2c_device_id edt_ft5x06_ts_id[] = { |
| 1067 | 1071 | { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, |
| 1068 | 1072 | { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, |
| 1073 | + /* Note no edt- prefix for compatibility with the ft6236.c driver */ | |
| 1074 | + { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, | |
| 1069 | 1075 | { /* sentinel */ } |
| 1070 | 1076 | }; |
| 1071 | 1077 | MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); |
| @@ -1076,6 +1082,8 @@ static const struct of_device_id edt_ft5x06_of_match[] = { · 4 unmodified lines | ||
| 1076 | 1082 | { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, |
| 1077 | 1083 | { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, |
| 1078 | 1084 | { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, |
| 1085 | + /* Note focaltech vendor prefix for compatibility with ft6236.c */ | |
| 1086 | + { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, | |
| 1079 | 1087 | { /* sentinel */ } |
| 1080 | 1088 | }; |
| 1081 | 1089 | MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); |
| @@ -0,0 +1,336 @@ | ||
| 1 | +/* | |
| 2 | + * Driver for ELAN eKTF2127 i2c touchscreen controller | |
| 3 | + * | |
| 4 | + * For this driver the layout of the Chipone icn8318 i2c | |
| 5 | + * touchscreencontroller is used. | |
| 6 | + * | |
| 7 | + * This program is free software; you can redistribute it and/or modify | |
| 8 | + * it under the terms of the GNU General Public License as published by | |
| 9 | + * the Free Software Foundation; either version 2 of the License, or | |
| 10 | + * (at your option) any later version. | |
| 11 | + * | |
| 12 | + * Author: | |
| 13 | + * Michel Verlaan <michel.verl@gmail.com> | |
| 14 | + * Siebren Vroegindeweij <siebren.vroegindeweij@hotmail.com> | |
| 15 | + * | |
| 16 | + * Original chipone_icn8318 driver: | |
| 17 | + * Hans de Goede <hdegoede@redhat.com> | |
| 18 | + */ | |
| 19 | + | |
| 20 | +#include <linux/gpio/consumer.h> | |
| 21 | +#include <linux/interrupt.h> | |
| 22 | +#include <linux/i2c.h> | |
| 23 | +#include <linux/input.h> | |
| 24 | +#include <linux/input/mt.h> | |
| 25 | +#include <linux/input/touchscreen.h> | |
| 26 | +#include <linux/module.h> | |
| 27 | +#include <linux/of.h> | |
| 28 | +#include <linux/delay.h> | |
| 29 | + | |
| 30 | +/* Packet header defines (first byte of data send / received) */ | |
| 31 | +#define EKTF2127_NOISE 0x40 | |
| 32 | +#define EKTF2127_RESPONSE 0x52 | |
| 33 | +#define EKTF2127_REQUEST 0x53 | |
| 34 | +#define EKTF2127_HELLO 0x55 | |
| 35 | +#define EKTF2127_REPORT 0x5d | |
| 36 | +#define EKTF2127_CALIB_DONE 0x66 | |
| 37 | + | |
| 38 | +/* Register defines (second byte of data send / received) */ | |
| 39 | +#define EKTF2127_ENV_NOISY 0x41 | |
| 40 | +#define EKTF2127_HEIGHT 0x60 | |
| 41 | +#define EKTF2127_WIDTH 0x63 | |
| 42 | + | |
| 43 | +/* 2 bytes header + 5 * 3 bytes coordinates + 3 bytes pressure info + footer */ | |
| 44 | +#define EKTF2127_TOUCH_REPORT_SIZE 21 | |
| 45 | +#define EKTF2127_MAX_TOUCHES 5 | |
| 46 | + | |
| 47 | +struct ektf2127_ts { | |
| 48 | + struct i2c_client *client; | |
| 49 | + struct input_dev *input; | |
| 50 | + struct gpio_desc *power_gpios; | |
| 51 | + struct touchscreen_properties prop; | |
| 52 | +}; | |
| 53 | + | |
| 54 | +static void ektf2127_parse_coordinates(const u8* buf, unsigned int touch_count, | |
| 55 | + struct input_mt_pos *touches) | |
| 56 | +{ | |
| 57 | + int index = 0; | |
| 58 | + int i; | |
| 59 | + | |
| 60 | + for (i = 0; i < touch_count; i++) { | |
| 61 | + index = 2 + i * 3; | |
| 62 | + | |
| 63 | + touches[i].x = (buf[index] & 0x0f); | |
| 64 | + touches[i].x <<= 8; | |
| 65 | + touches[i].x |= buf[index + 2]; | |
| 66 | + | |
| 67 | + touches[i].y = (buf[index] & 0xf0); | |
| 68 | + touches[i].y <<= 4; | |
| 69 | + touches[i].y |= buf[index + 1]; | |
| 70 | + } | |
| 71 | +} | |
| 72 | + | |
| 73 | +static void ektf2127_report_event(struct ektf2127_ts *ts, const u8 *buf) | |
| 74 | +{ | |
| 75 | + struct input_mt_pos touches[EKTF2127_MAX_TOUCHES]; | |
| 76 | + int slots[EKTF2127_MAX_TOUCHES]; | |
| 77 | + unsigned int touch_count, i; | |
| 78 | + | |
| 79 | + touch_count = buf[1] & 0x07; | |
| 80 | + if (touch_count > EKTF2127_MAX_TOUCHES) { | |
| 81 | + dev_err(&ts->client->dev, | |
| 82 | + "Too many touches %d > %d\n", | |
| 83 | + touch_count, EKTF2127_MAX_TOUCHES); | |
| 84 | + touch_count = EKTF2127_MAX_TOUCHES; | |
| 85 | + } | |
| 86 | + | |
| 87 | + ektf2127_parse_coordinates(buf, touch_count, touches); | |
| 88 | + input_mt_assign_slots(ts->input, slots, touches, | |
| 89 | + touch_count, 0); | |
| 90 | + | |
| 91 | + for (i = 0; i < touch_count; i++) { | |
| 92 | + input_mt_slot(ts->input, slots[i]); | |
| 93 | + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); | |
| 94 | + touchscreen_report_pos(ts->input, &ts->prop, | |
| 95 | + touches[i].x, touches[i].y, true); | |
| 96 | + } | |
| 97 | + | |
| 98 | + input_mt_sync_frame(ts->input); | |
| 99 | + input_sync(ts->input); | |
| 100 | +} | |
| 101 | + | |
| 102 | +static irqreturn_t ektf2127_irq(int irq, void *dev_id) | |
| 103 | +{ | |
| 104 | + struct ektf2127_ts *ts = dev_id; | |
| 105 | + struct device *dev = &ts->client->dev; | |
| 106 | + char buf[EKTF2127_TOUCH_REPORT_SIZE]; | |
| 107 | + int ret; | |
| 108 | + | |
| 109 | + ret = i2c_master_recv(ts->client, buf, EKTF2127_TOUCH_REPORT_SIZE); | |
| 110 | + if (ret != EKTF2127_TOUCH_REPORT_SIZE) { | |
| 111 | + dev_err(dev, "Error reading touch data: %d\n", ret); | |
| 112 | + goto out; | |
| 113 | + } | |
| 114 | + | |
| 115 | + switch (buf[0]) { | |
| 116 | + case EKTF2127_REPORT: | |
| 117 | + ektf2127_report_event(ts, buf); | |
| 118 | + break; | |
| 119 | + | |
| 120 | + case EKTF2127_NOISE: | |
| 121 | + if (buf[1] == EKTF2127_ENV_NOISY) | |
| 122 | + dev_dbg(dev, "Environment is electrically noisy\n"); | |
| 123 | + break; | |
| 124 | + | |
| 125 | + case EKTF2127_HELLO: | |
| 126 | + case EKTF2127_CALIB_DONE: | |
| 127 | + break; | |
| 128 | + | |
| 129 | + default: | |
| 130 | + dev_err(dev, "Unexpected packet header byte %#02x\n", buf[0]); | |
| 131 | + break; | |
| 132 | + } | |
| 133 | + | |
| 134 | +out: | |
| 135 | + return IRQ_HANDLED; | |
| 136 | +} | |
| 137 | + | |
| 138 | +static int ektf2127_start(struct input_dev *dev) | |
| 139 | +{ | |
| 140 | + struct ektf2127_ts *ts = input_get_drvdata(dev); | |
| 141 | + | |
| 142 | + enable_irq(ts->client->irq); | |
| 143 | + gpiod_set_value_cansleep(ts->power_gpios, 1); | |
| 144 | + | |
| 145 | + return 0; | |
| 146 | +} | |
| 147 | + | |
| 148 | +static void ektf2127_stop(struct input_dev *dev) | |
| 149 | +{ | |
| 150 | + struct ektf2127_ts *ts = input_get_drvdata(dev); | |
| 151 | + | |
| 152 | + disable_irq(ts->client->irq); | |
| 153 | + gpiod_set_value_cansleep(ts->power_gpios, 0); | |
| 154 | +} | |
| 155 | + | |
| 156 | +static int __maybe_unused ektf2127_suspend(struct device *dev) | |
| 157 | +{ | |
| 158 | + struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); | |
| 159 | + | |
| 160 | + mutex_lock(&ts->input->mutex); | |
| 161 | + if (ts->input->users) | |
| 162 | + ektf2127_stop(ts->input); | |
| 163 | + mutex_unlock(&ts->input->mutex); | |
| 164 | + | |
| 165 | + return 0; | |
| 166 | +} | |
| 167 | + | |
| 168 | +static int __maybe_unused ektf2127_resume(struct device *dev) | |
| 169 | +{ | |
| 170 | + struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev)); | |
| 171 | + | |
| 172 | + mutex_lock(&ts->input->mutex); | |
| 173 | + if (ts->input->users) | |
| 174 | + ektf2127_start(ts->input); | |
| 175 | + mutex_unlock(&ts->input->mutex); | |
| 176 | + | |
| 177 | + return 0; | |
| 178 | +} | |
| 179 | + | |
| 180 | +static SIMPLE_DEV_PM_OPS(ektf2127_pm_ops, ektf2127_suspend, | |
| 181 | + ektf2127_resume); | |
| 182 | + | |
| 183 | +static int ektf2127_query_dimension(struct i2c_client *client, bool width) | |
| 184 | +{ | |
| 185 | + struct device *dev = &client->dev; | |
| 186 | + const char *what = width ? "width" : "height"; | |
| 187 | + u8 what_code = width ? EKTF2127_WIDTH : EKTF2127_HEIGHT; | |
| 188 | + u8 buf[4]; | |
| 189 | + int ret; | |
| 190 | + int error; | |
| 191 | + | |
| 192 | + /* Request dimension */ | |
| 193 | + buf[0] = EKTF2127_REQUEST; | |
| 194 | + buf[1] = width ? EKTF2127_WIDTH : EKTF2127_HEIGHT; | |
| 195 | + buf[2] = 0x00; | |
| 196 | + buf[3] = 0x00; | |
| 197 | + ret = i2c_master_send(client, buf, sizeof(buf)); | |
| 198 | + if (ret != sizeof(buf)) { | |
| 199 | + error = ret < 0 ? ret : -EIO; | |
| 200 | + dev_err(dev, "Failed to request %s: %d\n", what, error); | |
| 201 | + return error; | |
| 202 | + } | |
| 203 | + | |
| 204 | + msleep(20); | |
| 205 | + | |
| 206 | + /* Read response */ | |
| 207 | + ret = i2c_master_recv(client, buf, sizeof(buf)); | |
| 208 | + if (ret != sizeof(buf)) { | |
| 209 | + error = ret < 0 ? ret : -EIO; | |
| 210 | + dev_err(dev, "Failed to receive %s data: %d\n", what, error); | |
| 211 | + return error; | |
| 212 | + } | |
| 213 | + | |
| 214 | + if (buf[0] != EKTF2127_RESPONSE || buf[1] != what_code) { | |
| 215 | + dev_err(dev, "Unexpected %s data: %#02x %#02x\n", | |
| 216 | + what, buf[0], buf[1]); | |
| 217 | + return -EIO; | |
| 218 | + } | |
| 219 | + | |
| 220 | + return (((buf[3] & 0xf0) << 4) | buf[2]) - 1; | |
| 221 | +} | |
| 222 | + | |
| 223 | +static int ektf2127_probe(struct i2c_client *client, | |
| 224 | + const struct i2c_device_id *id) | |
| 225 | +{ | |
| 226 | + struct device *dev = &client->dev; | |
| 227 | + struct ektf2127_ts *ts; | |
| 228 | + struct input_dev *input; | |
| 229 | + u8 buf[4]; | |
| 230 | + int max_x, max_y; | |
| 231 | + int error; | |
| 232 | + | |
| 233 | + if (!client->irq) { | |
| 234 | + dev_err(dev, "Error no irq specified\n"); | |
| 235 | + return -EINVAL; | |
| 236 | + } | |
| 237 | + | |
| 238 | + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); | |
| 239 | + if (!ts) | |
| 240 | + return -ENOMEM; | |
| 241 | + | |
| 242 | + /* This requests the gpio *and* turns on the touchscreen controller */ | |
| 243 | + ts->power_gpios = devm_gpiod_get(dev, "power", GPIOD_OUT_HIGH); | |
| 244 | + if (IS_ERR(ts->power_gpios)) { | |
| 245 | + error = PTR_ERR(ts->power_gpios); | |
| 246 | + if (error != -EPROBE_DEFER) | |
| 247 | + dev_err(dev, "Error getting power gpio: %d\n", error); | |
| 248 | + return error; | |
| 249 | + } | |
| 250 | + | |
| 251 | + input = devm_input_allocate_device(dev); | |
| 252 | + if (!input) | |
| 253 | + return -ENOMEM; | |
| 254 | + | |
| 255 | + input->name = client->name; | |
| 256 | + input->id.bustype = BUS_I2C; | |
| 257 | + input->open = ektf2127_start; | |
| 258 | + input->close = ektf2127_stop; | |
| 259 | + | |
| 260 | + ts->client = client; | |
| 261 | + | |
| 262 | + /* Read hello (ignore result, depends on initial power state) */ | |
| 263 | + msleep(20); | |
| 264 | + i2c_master_recv(ts->client, buf, sizeof(buf)); | |
| 265 | + | |
| 266 | + /* Read resolution from chip */ | |
| 267 | + max_x = ektf2127_query_dimension(client, true); | |
| 268 | + if (max_x < 0) | |
| 269 | + return max_x; | |
| 270 | + | |
| 271 | + max_y = ektf2127_query_dimension(client, false); | |
| 272 | + if (max_y < 0) | |
| 273 | + return max_y; | |
| 274 | + | |
| 275 | + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0); | |
| 276 | + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0); | |
| 277 | + touchscreen_parse_properties(input, true, &ts->prop); | |
| 278 | + | |
| 279 | + error = input_mt_init_slots(input, EKTF2127_MAX_TOUCHES, | |
| 280 | + INPUT_MT_DIRECT | | |
| 281 | + INPUT_MT_DROP_UNUSED | | |
| 282 | + INPUT_MT_TRACK); | |
| 283 | + if (error) | |
| 284 | + return error; | |
| 285 | + | |
| 286 | + ts->input = input; | |
| 287 | + input_set_drvdata(input, ts); | |
| 288 | + | |
| 289 | + error = devm_request_threaded_irq(dev, client->irq, | |
| 290 | + NULL, ektf2127_irq, | |
| 291 | + IRQF_ONESHOT, client->name, ts); | |
| 292 | + if (error) { | |
| 293 | + dev_err(dev, "Error requesting irq: %d\n", error); | |
| 294 | + return error; | |
| 295 | + } | |
| 296 | + | |
| 297 | + /* Stop device till opened */ | |
| 298 | + ektf2127_stop(ts->input); | |
| 299 | + | |
| 300 | + error = input_register_device(input); | |
| 301 | + if (error) | |
| 302 | + return error; | |
| 303 | + | |
| 304 | + i2c_set_clientdata(client, ts); | |
| 305 | + | |
| 306 | + return 0; | |
| 307 | +} | |
| 308 | + | |
| 309 | +#ifdef CONFIG_OF | |
| 310 | +static const struct of_device_id ektf2127_of_match[] = { | |
| 311 | + { .compatible = "elan,ektf2127" }, | |
| 312 | + {} | |
| 313 | +}; | |
| 314 | +MODULE_DEVICE_TABLE(of, ektf2127_of_match); | |
| 315 | +#endif | |
| 316 | + | |
| 317 | +static const struct i2c_device_id ektf2127_i2c_id[] = { | |
| 318 | + { "ektf2127", 0 }, | |
| 319 | + {} | |
| 320 | +}; | |
| 321 | +MODULE_DEVICE_TABLE(i2c, ektf2127_i2c_id); | |
| 322 | + | |
| 323 | +static struct i2c_driver ektf2127_driver = { | |
| 324 | + .driver = { | |
| 325 | + .name = "elan_ektf2127", | |
| 326 | + .pm = &ektf2127_pm_ops, | |
| 327 | + .of_match_table = of_match_ptr(ektf2127_of_match), | |
| 328 | + }, | |
| 329 | + .probe = ektf2127_probe, | |
| 330 | + .id_table = ektf2127_i2c_id, | |
| 331 | +}; | |
| 332 | +module_i2c_driver(ektf2127_driver); | |
| 333 | + | |
| 334 | +MODULE_DESCRIPTION("ELAN eKTF2127 I2C Touchscreen Driver"); | |
| 335 | +MODULE_AUTHOR("Michel Verlaan, Siebren Vroegindeweij"); | |
| 336 | +MODULE_LICENSE("GPL"); | |
| @@ -298,7 +298,7 @@ static u16 elants_i2c_parse_version(u8 *buf) · 297 unmodified lines | ||
| 298 | 298 | return get_unaligned_be32(buf) >> 4; |
| 299 | 299 | } |
| 300 | 300 | |
| 301 | −static int elants_i2c_query_fw_id(struct elants_data *ts) | |
| 301 | +static int elants_i2c_query_hw_version(struct elants_data *ts) | |
| 302 | 302 | { |
| 303 | 303 | struct i2c_client *client = ts->client; |
| 304 | 304 | int error, retry_cnt; |
| @@ -318,8 +318,13 @@ static int elants_i2c_query_fw_id(struct elants_data *ts) · 13 unmodified lines | ||
| 318 | 318 | error, (int)sizeof(resp), resp); |
| 319 | 319 | } |
| 320 | 320 | |
| 321 | − dev_err(&client->dev, | |
| 322 | − "Failed to read fw id or fw id is invalid\n"); | |
| 321 | + if (error) { | |
| 322 | + dev_err(&client->dev, | |
| 323 | + "Failed to read fw id: %d\n", error); | |
| 324 | + return error; | |
| 325 | + } | |
| 326 | + | |
| 327 | + dev_err(&client->dev, "Invalid fw id: %#04x\n", ts->hw_version); | |
| 323 | 328 | |
| 324 | 329 | return -EINVAL; |
| 325 | 330 | } |
| @@ -508,7 +513,7 @@ static int elants_i2c_fastboot(struct i2c_client *client) · 182 unmodified lines | ||
| 508 | 513 | static int elants_i2c_initialize(struct elants_data *ts) |
| 509 | 514 | { |
| 510 | 515 | struct i2c_client *client = ts->client; |
| 511 | − int error, retry_cnt; | |
| 516 | + int error, error2, retry_cnt; | |
| 512 | 517 | const u8 hello_packet[] = { 0x55, 0x55, 0x55, 0x55 }; |
| 513 | 518 | const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 }; |
| 514 | 519 | u8 buf[HEADER_SIZE]; |
| @@ -553,18 +558,22 @@ static int elants_i2c_initialize(struct elants_data *ts) · 38 unmodified lines | ||
| 553 | 558 | } |
| 554 | 559 | } |
| 555 | 560 | |
| 561 | + /* hw version is available even if device in recovery state */ | |
| 562 | + error2 = elants_i2c_query_hw_version(ts); | |
| 556 | 563 | if (!error) |
| 557 | − error = elants_i2c_query_fw_id(ts); | |
| 564 | + error = error2; | |
| 565 | + | |
| 558 | 566 | if (!error) |
| 559 | 567 | error = elants_i2c_query_fw_version(ts); |
| 568 | + if (!error) | |
| 569 | + error = elants_i2c_query_test_version(ts); | |
| 570 | + if (!error) | |
| 571 | + error = elants_i2c_query_bc_version(ts); | |
| 572 | + if (!error) | |
| 573 | + error = elants_i2c_query_ts_info(ts); | |
| 560 | 574 | |
| 561 | − if (error) { | |
| 575 | + if (error) | |
| 562 | 576 | ts->iap_mode = ELAN_IAP_RECOVERY; |
| 563 | − } else { | |
| 564 | − elants_i2c_query_test_version(ts); | |
| 565 | − elants_i2c_query_bc_version(ts); | |
| 566 | − elants_i2c_query_ts_info(ts); | |
| 567 | − } | |
| 568 | 577 | |
| 569 | 578 | return 0; |
| 570 | 579 | } |
| @@ -1,326 +0,0 @@ | ||
| 1 | −/* | |
| 2 | − * FocalTech FT6236 TouchScreen driver. | |
| 3 | − * | |
| 4 | − * Copyright (c) 2010 Focal tech Ltd. | |
| 5 | − * | |
| 6 | − * This software is licensed under the terms of the GNU General Public | |
| 7 | − * License version 2, as published by the Free Software Foundation, and | |
| 8 | − * may be copied, distributed, and modified under those terms. | |
| 9 | − * | |
| 10 | − * This program is distributed in the hope that it will be useful, | |
| 11 | − * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | − * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | − * GNU General Public License for more details. | |
| 14 | − */ | |
| 15 | − | |
| 16 | −#include <linux/delay.h> | |
| 17 | −#include <linux/gpio/consumer.h> | |
| 18 | −#include <linux/i2c.h> | |
| 19 | −#include <linux/input.h> | |
| 20 | −#include <linux/input/mt.h> | |
| 21 | −#include <linux/interrupt.h> | |
| 22 | −#include <linux/module.h> | |
| 23 | −#include <linux/property.h> | |
| 24 | − | |
| 25 | −#define FT6236_MAX_TOUCH_POINTS 2 | |
| 26 | − | |
| 27 | −#define FT6236_REG_TH_GROUP 0x80 | |
| 28 | −#define FT6236_REG_PERIODACTIVE 0x88 | |
| 29 | −#define FT6236_REG_LIB_VER_H 0xa1 | |
| 30 | −#define FT6236_REG_LIB_VER_L 0xa2 | |
| 31 | −#define FT6236_REG_CIPHER 0xa3 | |
| 32 | −#define FT6236_REG_FIRMID 0xa6 | |
| 33 | −#define FT6236_REG_FOCALTECH_ID 0xa8 | |
| 34 | −#define FT6236_REG_RELEASE_CODE_ID 0xaf | |
| 35 | − | |
| 36 | −#define FT6236_EVENT_PRESS_DOWN 0 | |
| 37 | −#define FT6236_EVENT_LIFT_UP 1 | |
| 38 | −#define FT6236_EVENT_CONTACT 2 | |
| 39 | −#define FT6236_EVENT_NO_EVENT 3 | |
| 40 | − | |
| 41 | −struct ft6236_data { | |
| 42 | − struct i2c_client *client; | |
| 43 | − struct input_dev *input; | |
| 44 | − struct gpio_desc *reset_gpio; | |
| 45 | − u32 max_x; | |
| 46 | − u32 max_y; | |
| 47 | − bool invert_x; | |
| 48 | − bool invert_y; | |
| 49 | − bool swap_xy; | |
| 50 | −}; | |
| 51 | − | |
| 52 | −/* | |
| 53 | − * This struct is a touchpoint as stored in hardware. Note that the id, | |
| 54 | − * as well as the event, are stored in the upper nybble of the hi byte. | |
| 55 | − */ | |
| 56 | −struct ft6236_touchpoint { | |
| 57 | − union { | |
| 58 | − u8 xhi; | |
| 59 | − u8 event; | |
| 60 | − }; | |
| 61 | − u8 xlo; | |
| 62 | − union { | |
| 63 | − u8 yhi; | |
| 64 | − u8 id; | |
| 65 | − }; | |
| 66 | − u8 ylo; | |
| 67 | − u8 weight; | |
| 68 | − u8 misc; | |
| 69 | −} __packed; | |
| 70 | − | |
| 71 | −/* This packet represents the register map as read from offset 0 */ | |
| 72 | −struct ft6236_packet { | |
| 73 | − u8 dev_mode; | |
| 74 | − u8 gest_id; | |
| 75 | − u8 touches; | |
| 76 | − struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS]; | |
| 77 | −} __packed; | |
| 78 | − | |
| 79 | −static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data) | |
| 80 | −{ | |
| 81 | − int error; | |
| 82 | − | |
| 83 | − error = i2c_smbus_read_i2c_block_data(client, reg, len, data); | |
| 84 | − if (error < 0) | |
| 85 | − return error; | |
| 86 | − | |
| 87 | − if (error != len) | |
| 88 | − return -EIO; | |
| 89 | − | |
| 90 | − return 0; | |
| 91 | −} | |
| 92 | − | |
| 93 | −static irqreturn_t ft6236_interrupt(int irq, void *dev_id) | |
| 94 | −{ | |
| 95 | − struct ft6236_data *ft6236 = dev_id; | |
| 96 | − struct device *dev = &ft6236->client->dev; | |
| 97 | − struct input_dev *input = ft6236->input; | |
| 98 | − struct ft6236_packet buf; | |
| 99 | − u8 touches; | |
| 100 | − int i, error; | |
| 101 | − | |
| 102 | − error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf); | |
| 103 | − if (error) { | |
| 104 | − dev_err(dev, "read touchdata failed %d\n", error); | |
| 105 | − return IRQ_HANDLED; | |
| 106 | − } | |
| 107 | − | |
| 108 | − touches = buf.touches & 0xf; | |
| 109 | − if (touches > FT6236_MAX_TOUCH_POINTS) { | |
| 110 | − dev_dbg(dev, | |
| 111 | − "%d touch points reported, only %d are supported\n", | |
| 112 | − touches, FT6236_MAX_TOUCH_POINTS); | |
| 113 | − touches = FT6236_MAX_TOUCH_POINTS; | |
| 114 | − } | |
| 115 | − | |
| 116 | − for (i = 0; i < touches; i++) { | |
| 117 | − struct ft6236_touchpoint *point = &buf.points[i]; | |
| 118 | − u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo; | |
| 119 | − u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo; | |
| 120 | − u8 event = point->event >> 6; | |
| 121 | − u8 id = point->id >> 4; | |
| 122 | − bool act = (event == FT6236_EVENT_PRESS_DOWN || | |
| 123 | − event == FT6236_EVENT_CONTACT); | |
| 124 | − | |
| 125 | − input_mt_slot(input, id); | |
| 126 | − input_mt_report_slot_state(input, MT_TOOL_FINGER, act); | |
| 127 | − if (!act) | |
| 128 | − continue; | |
| 129 | − | |
| 130 | − if (ft6236->invert_x) | |
| 131 | − x = ft6236->max_x - x; | |
| 132 | − | |
| 133 | − if (ft6236->invert_y) | |
| 134 | − y = ft6236->max_y - y; | |
| 135 | − | |
| 136 | − if (ft6236->swap_xy) { | |
| 137 | − input_report_abs(input, ABS_MT_POSITION_X, y); | |
| 138 | − input_report_abs(input, ABS_MT_POSITION_Y, x); | |
| 139 | − } else { | |
| 140 | − input_report_abs(input, ABS_MT_POSITION_X, x); | |
| 141 | − input_report_abs(input, ABS_MT_POSITION_Y, y); | |
| 142 | − } | |
| 143 | − } | |
| 144 | − | |
| 145 | − input_mt_sync_frame(input); | |
| 146 | − input_sync(input); | |
| 147 | − | |
| 148 | − return IRQ_HANDLED; | |
| 149 | −} | |
| 150 | − | |
| 151 | −static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg) | |
| 152 | −{ | |
| 153 | − struct i2c_client *client = ft6236->client; | |
| 154 | − u8 val = 0; | |
| 155 | − int error; | |
| 156 | − | |
| 157 | − error = ft6236_read(client, reg, 1, &val); | |
| 158 | − if (error) | |
| 159 | − dev_dbg(&client->dev, | |
| 160 | − "error reading register 0x%02x: %d\n", reg, error); | |
| 161 | − | |
| 162 | − return val; | |
| 163 | −} | |
| 164 | − | |
| 165 | −static void ft6236_debug_info(struct ft6236_data *ft6236) | |
| 166 | −{ | |
| 167 | − struct device *dev = &ft6236->client->dev; | |
| 168 | − | |
| 169 | − dev_dbg(dev, "Touch threshold is %d\n", | |
| 170 | − ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4); | |
| 171 | − dev_dbg(dev, "Report rate is %dHz\n", | |
| 172 | − ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10); | |
| 173 | − dev_dbg(dev, "Firmware library version 0x%02x%02x\n", | |
| 174 | − ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H), | |
| 175 | − ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L)); | |
| 176 | − dev_dbg(dev, "Firmware version 0x%02x\n", | |
| 177 | − ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID)); | |
| 178 | − dev_dbg(dev, "Chip vendor ID 0x%02x\n", | |
| 179 | − ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER)); | |
| 180 | − dev_dbg(dev, "CTPM vendor ID 0x%02x\n", | |
| 181 | − ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID)); | |
| 182 | − dev_dbg(dev, "Release code version 0x%02x\n", | |
| 183 | − ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID)); | |
| 184 | −} | |
| 185 | − | |
| 186 | −static void ft6236_reset(struct ft6236_data *ft6236) | |
| 187 | −{ | |
| 188 | − if (!ft6236->reset_gpio) | |
| 189 | − return; | |
| 190 | − | |
| 191 | − gpiod_set_value_cansleep(ft6236->reset_gpio, 1); | |
| 192 | − usleep_range(5000, 20000); | |
| 193 | − gpiod_set_value_cansleep(ft6236->reset_gpio, 0); | |
| 194 | − msleep(300); | |
| 195 | −} | |
| 196 | − | |
| 197 | −static int ft6236_probe(struct i2c_client *client, | |
| 198 | − const struct i2c_device_id *id) | |
| 199 | −{ | |
| 200 | − struct device *dev = &client->dev; | |
| 201 | − struct ft6236_data *ft6236; | |
| 202 | − struct input_dev *input; | |
| 203 | − u32 fuzz_x = 0, fuzz_y = 0; | |
| 204 | − u8 val; | |
| 205 | − int error; | |
| 206 | − | |
| 207 | − if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | |
| 208 | − return -ENXIO; | |
| 209 | − | |
| 210 | − if (!client->irq) { | |
| 211 | − dev_err(dev, "irq is missing\n"); | |
| 212 | − return -EINVAL; | |
| 213 | − } | |
| 214 | − | |
| 215 | − ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL); | |
| 216 | − if (!ft6236) | |
| 217 | − return -ENOMEM; | |
| 218 | − | |
| 219 | − ft6236->client = client; | |
| 220 | − ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset", | |
| 221 | − GPIOD_OUT_LOW); | |
| 222 | − if (IS_ERR(ft6236->reset_gpio)) { | |
| 223 | − error = PTR_ERR(ft6236->reset_gpio); | |
| 224 | − if (error != -EPROBE_DEFER) | |
| 225 | − dev_err(dev, "error getting reset gpio: %d\n", error); | |
| 226 | − return error; | |
| 227 | − } | |
| 228 | − | |
| 229 | − ft6236_reset(ft6236); | |
| 230 | − | |
| 231 | − /* verify that the controller is present */ | |
| 232 | − error = ft6236_read(client, 0x00, 1, &val); | |
| 233 | − if (error) { | |
| 234 | − dev_err(dev, "failed to read from controller: %d\n", error); | |
| 235 | − return error; | |
| 236 | − } | |
| 237 | − | |
| 238 | − ft6236_debug_info(ft6236); | |
| 239 | − | |
| 240 | − input = devm_input_allocate_device(dev); | |
| 241 | − if (!input) | |
| 242 | − return -ENOMEM; | |
| 243 | − | |
| 244 | − ft6236->input = input; | |
| 245 | − input->name = client->name; | |
| 246 | − input->id.bustype = BUS_I2C; | |
| 247 | − | |
| 248 | − if (device_property_read_u32(dev, "touchscreen-size-x", | |
| 249 | − &ft6236->max_x) || | |
| 250 | − device_property_read_u32(dev, "touchscreen-size-y", | |
| 251 | − &ft6236->max_y)) { | |
| 252 | − dev_err(dev, "touchscreen-size-x and/or -y missing\n"); | |
| 253 | − return -EINVAL; | |
| 254 | − } | |
| 255 | − | |
| 256 | − device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x); | |
| 257 | − device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y); | |
| 258 | − ft6236->invert_x = device_property_read_bool(dev, | |
| 259 | − "touchscreen-inverted-x"); | |
| 260 | − ft6236->invert_y = device_property_read_bool(dev, | |
| 261 | − "touchscreen-inverted-y"); | |
| 262 | − ft6236->swap_xy = device_property_read_bool(dev, | |
| 263 | − "touchscreen-swapped-x-y"); | |
| 264 | − | |
| 265 | − if (ft6236->swap_xy) { | |
| 266 | − input_set_abs_params(input, ABS_MT_POSITION_X, 0, | |
| 267 | − ft6236->max_y, fuzz_y, 0); | |
| 268 | − input_set_abs_params(input, ABS_MT_POSITION_Y, 0, | |
| 269 | − ft6236->max_x, fuzz_x, 0); | |
| 270 | − } else { | |
| 271 | − input_set_abs_params(input, ABS_MT_POSITION_X, 0, | |
| 272 | − ft6236->max_x, fuzz_x, 0); | |
| 273 | − input_set_abs_params(input, ABS_MT_POSITION_Y, 0, | |
| 274 | − ft6236->max_y, fuzz_y, 0); | |
| 275 | − } | |
| 276 | − | |
| 277 | − error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS, | |
| 278 | − INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); | |
| 279 | − if (error) | |
| 280 | − return error; | |
| 281 | − | |
| 282 | − error = devm_request_threaded_irq(dev, client->irq, NULL, | |
| 283 | − ft6236_interrupt, IRQF_ONESHOT, | |
| 284 | − client->name, ft6236); | |
| 285 | − if (error) { | |
| 286 | − dev_err(dev, "request irq %d failed: %d\n", client->irq, error); | |
| 287 | − return error; | |
| 288 | − } | |
| 289 | − | |
| 290 | − error = input_register_device(input); | |
| 291 | − if (error) { | |
| 292 | − dev_err(dev, "failed to register input device: %d\n", error); | |
| 293 | − return error; | |
| 294 | − } | |
| 295 | − | |
| 296 | − return 0; | |
| 297 | −} | |
| 298 | − | |
| 299 | −#ifdef CONFIG_OF | |
| 300 | −static const struct of_device_id ft6236_of_match[] = { | |
| 301 | − { .compatible = "focaltech,ft6236", }, | |
| 302 | − { } | |
| 303 | −}; | |
| 304 | −MODULE_DEVICE_TABLE(of, ft6236_of_match); | |
| 305 | −#endif | |
| 306 | − | |
| 307 | −static const struct i2c_device_id ft6236_id[] = { | |
| 308 | − { "ft6236", }, | |
| 309 | − { } | |
| 310 | −}; | |
| 311 | −MODULE_DEVICE_TABLE(i2c, ft6236_id); | |
| 312 | − | |
| 313 | −static struct i2c_driver ft6236_driver = { | |
| 314 | − .driver = { | |
| 315 | − .name = "ft6236", | |
| 316 | − .of_match_table = of_match_ptr(ft6236_of_match), | |
| 317 | − }, | |
| 318 | − .probe = ft6236_probe, | |
| 319 | − .id_table = ft6236_id, | |
| 320 | −}; | |
| 321 | −module_i2c_driver(ft6236_driver); | |
| 322 | − | |
| 323 | −MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>"); | |
| 324 | −MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>"); | |
| 325 | −MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver"); | |
| 326 | −MODULE_LICENSE("GPL v2"); | |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | * HP Jornada 710/720/729 Touchscreen Driver |
| 14 | 14 | */ |
| 15 | 15 | |
| 16 | +#include <linux/gpio/consumer.h> | |
| 16 | 17 | #include <linux/platform_device.h> |
| 17 | 18 | #include <linux/input.h> |
| 18 | 19 | #include <linux/interrupt.h> |
| @@ -20,9 +21,7 @@ · 1 unmodified line | ||
| 20 | 21 | #include <linux/slab.h> |
| 21 | 22 | #include <linux/io.h> |
| 22 | 23 | |
| 23 | −#include <mach/hardware.h> | |
| 24 | 24 | #include <mach/jornada720.h> |
| 25 | −#include <mach/irqs.h> | |
| 26 | 25 | |
| 27 | 26 | MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); |
| 28 | 27 | MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); |
| @@ -30,6 +29,7 @@ MODULE_LICENSE("GPL v2"); · 1 unmodified line | ||
| 30 | 29 | |
| 31 | 30 | struct jornada_ts { |
| 32 | 31 | struct input_dev *dev; |
| 32 | + struct gpio_desc *gpio; | |
| 33 | 33 | int x_data[4]; /* X sample values */ |
| 34 | 34 | int y_data[4]; /* Y sample values */ |
| 35 | 35 | }; |
| @@ -71,8 +71,8 @@ static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id) · 35 unmodified lines | ||
| 71 | 71 | struct input_dev *input = jornada_ts->dev; |
| 72 | 72 | int x, y; |
| 73 | 73 | |
| 74 | − /* If GPIO_GPIO9 is set to high then report pen up */ | |
| 75 | − if (GPLR & GPIO_GPIO(9)) { | |
| 74 | + /* If gpio is high then report pen up */ | |
| 75 | + if (gpiod_get_value(jornada_ts->gpio)) { | |
| 76 | 76 | input_report_key(input, BTN_TOUCH, 0); |
| 77 | 77 | input_sync(input); |
| 78 | 78 | } else { |
| @@ -101,7 +101,7 @@ static int jornada720_ts_probe(struct platform_device *pdev) · 22 unmodified lines | ||
| 101 | 101 | { |
| 102 | 102 | struct jornada_ts *jornada_ts; |
| 103 | 103 | struct input_dev *input_dev; |
| 104 | − int error; | |
| 104 | + int error, irq; | |
| 105 | 105 | |
| 106 | 106 | jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL); |
| 107 | 107 | if (!jornada_ts) |
| @@ -113,6 +113,14 @@ static int jornada720_ts_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 113 | 113 | |
| 114 | 114 | platform_set_drvdata(pdev, jornada_ts); |
| 115 | 115 | |
| 116 | + jornada_ts->gpio = devm_gpiod_get(&pdev->dev, "penup", GPIOD_IN); | |
| 117 | + if (IS_ERR(jornada_ts->gpio)) | |
| 118 | + return PTR_ERR(jornada_ts->gpio); | |
| 119 | + | |
| 120 | + irq = gpiod_to_irq(jornada_ts->gpio); | |
| 121 | + if (irq <= 0) | |
| 122 | + return irq < 0 ? irq : -EINVAL; | |
| 123 | + | |
| 116 | 124 | jornada_ts->dev = input_dev; |
| 117 | 125 | |
| 118 | 126 | input_dev->name = "HP Jornada 7xx Touchscreen"; |
| @@ -125,8 +133,7 @@ static int jornada720_ts_probe(struct platform_device *pdev) · 6 unmodified lines | ||
| 125 | 133 | input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0); |
| 126 | 134 | input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0); |
| 127 | 135 | |
| 128 | − error = devm_request_irq(&pdev->dev, IRQ_GPIO9, | |
| 129 | − jornada720_ts_interrupt, | |
| 136 | + error = devm_request_irq(&pdev->dev, irq, jornada720_ts_interrupt, | |
| 130 | 137 | IRQF_TRIGGER_RISING, |
| 131 | 138 | "HP7XX Touchscreen driver", pdev); |
| 132 | 139 | if (error) { |
| @@ -37,7 +37,6 @@ struct mc13783_ts_priv { · 36 unmodified lines | ||
| 37 | 37 | struct input_dev *idev; |
| 38 | 38 | struct mc13xxx *mc13xxx; |
| 39 | 39 | struct delayed_work work; |
| 40 | − struct workqueue_struct *workq; | |
| 41 | 40 | unsigned int sample[4]; |
| 42 | 41 | struct mc13xxx_ts_platform_data *touch; |
| 43 | 42 | }; |
| @@ -54,7 +53,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data) · 10 unmodified lines | ||
| 54 | 53 | * be rescheduled for immediate execution here. However the rearm |
| 55 | 54 | * delay is HZ / 50 which is acceptable. |
| 56 | 55 | */ |
| 57 | − queue_delayed_work(priv->workq, &priv->work, 0); | |
| 56 | + schedule_delayed_work(&priv->work, 0); | |
| 58 | 57 | |
| 59 | 58 | return IRQ_HANDLED; |
| 60 | 59 | } |
| @@ -106,16 +105,18 @@ static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv) · 45 unmodified lines | ||
| 106 | 105 | |
| 107 | 106 | dev_dbg(&idev->dev, "report (%d, %d, %d)\n", |
| 108 | 107 | x1, y1, 0x1000 - cr0); |
| 109 | − queue_delayed_work(priv->workq, &priv->work, HZ / 50); | |
| 110 | − } else | |
| 108 | + schedule_delayed_work(&priv->work, HZ / 50); | |
| 109 | + } else { | |
| 111 | 110 | dev_dbg(&idev->dev, "report release\n"); |
| 111 | + } | |
| 112 | 112 | |
| 113 | 113 | input_report_abs(idev, ABS_PRESSURE, |
| 114 | 114 | cr0 ? 0x1000 - cr0 : cr0); |
| 115 | 115 | input_report_key(idev, BTN_TOUCH, cr0); |
| 116 | 116 | input_sync(idev); |
| 117 | − } else | |
| 117 | + } else { | |
| 118 | 118 | dev_dbg(&idev->dev, "discard event\n"); |
| 119 | + } | |
| 119 | 120 | } |
| 120 | 121 | |
| 121 | 122 | static void mc13783_ts_work(struct work_struct *work) |
| @@ -189,14 +190,6 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) · 67 unmodified lines | ||
| 189 | 190 | goto err_free_mem; |
| 190 | 191 | } |
| 191 | 192 | |
| 192 | − /* | |
| 193 | − * We need separate workqueue because mc13783_adc_do_conversion | |
| 194 | − * uses keventd and thus would deadlock. | |
| 195 | − */ | |
| 196 | − priv->workq = create_singlethread_workqueue("mc13783_ts"); | |
| 197 | − if (!priv->workq) | |
| 198 | − goto err_free_mem; | |
| 199 | − | |
| 200 | 193 | idev->name = MC13783_TS_NAME; |
| 201 | 194 | idev->dev.parent = &pdev->dev; |
| 202 | 195 | |
| @@ -215,14 +208,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) · 12 unmodified lines | ||
| 215 | 208 | if (ret) { |
| 216 | 209 | dev_err(&pdev->dev, |
| 217 | 210 | "register input device failed with %d\n", ret); |
| 218 | − goto err_destroy_wq; | |
| 211 | + goto err_free_mem; | |
| 219 | 212 | } |
| 220 | 213 | |
| 221 | 214 | platform_set_drvdata(pdev, priv); |
| 222 | 215 | return 0; |
| 223 | 216 | |
| 224 | −err_destroy_wq: | |
| 225 | − destroy_workqueue(priv->workq); | |
| 226 | 217 | err_free_mem: |
| 227 | 218 | input_free_device(idev); |
| 228 | 219 | kfree(priv); |
| @@ -233,7 +224,6 @@ static int mc13783_ts_remove(struct platform_device *pdev) · 4 unmodified lines | ||
| 233 | 224 | { |
| 234 | 225 | struct mc13783_ts_priv *priv = platform_get_drvdata(pdev); |
| 235 | 226 | |
| 236 | − destroy_workqueue(priv->workq); | |
| 237 | 227 | input_unregister_device(priv->idev); |
| 238 | 228 | kfree(priv); |
| 239 | 229 | |
| @@ -11,10 +11,6 @@ · 10 unmodified lines | ||
| 11 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | 13 | * GNU General Public License for more details. |
| 14 | − * | |
| 15 | − * You should have received a copy of the GNU General Public | |
| 16 | − * License along with this library; if not, write to the Free Software | |
| 17 | − * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 18 | 14 | */ |
| 19 | 15 | |
| 20 | 16 | #include <linux/delay.h> |
| @@ -404,7 +400,6 @@ static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev) · 383 unmodified lines | ||
| 404 | 400 | mutex_lock(&input->mutex); |
| 405 | 401 | |
| 406 | 402 | if (device_may_wakeup(&client->dev)) { |
| 407 | − | |
| 408 | 403 | if (!input->users) { |
| 409 | 404 | ret = pixcir_stop(ts); |
| 410 | 405 | if (ret) { |
| @@ -431,13 +426,7 @@ static const struct of_device_id pixcir_of_match[]; · 20 unmodified lines | ||
| 431 | 426 | static int pixcir_parse_dt(struct device *dev, |
| 432 | 427 | struct pixcir_i2c_ts_data *tsdata) |
| 433 | 428 | { |
| 434 | − const struct of_device_id *match; | |
| 435 | − | |
| 436 | − match = of_match_device(of_match_ptr(pixcir_of_match), dev); | |
| 437 | − if (!match) | |
| 438 | − return -EINVAL; | |
| 439 | − | |
| 440 | − tsdata->chip = (const struct pixcir_i2c_chip_data *)match->data; | |
| 429 | + tsdata->chip = of_device_get_match_data(dev); | |
| 441 | 430 | if (!tsdata->chip) |
| 442 | 431 | return -EINVAL; |
| 443 | 432 | |
| @@ -23,7 +23,7 @@ · 22 unmodified lines | ||
| 23 | 23 | #include <asm/unaligned.h> |
| 24 | 24 | |
| 25 | 25 | #define WDT87XX_NAME "wdt87xx_i2c" |
| 26 | −#define WDT87XX_DRV_VER "0.9.7" | |
| 26 | +#define WDT87XX_DRV_VER "0.9.8" | |
| 27 | 27 | #define WDT87XX_FW_NAME "wdt87xx_fw.bin" |
| 28 | 28 | #define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" |
| 29 | 29 | |
| @@ -157,6 +157,7 @@ · 127 unmodified lines | ||
| 157 | 157 | /* Controller requires minimum 300us between commands */ |
| 158 | 158 | #define WDT_COMMAND_DELAY_MS 2 |
| 159 | 159 | #define WDT_FLASH_WRITE_DELAY_MS 4 |
| 160 | +#define WDT_FLASH_ERASE_DELAY_MS 200 | |
| 160 | 161 | #define WDT_FW_RESET_TIME 2500 |
| 161 | 162 | |
| 162 | 163 | struct wdt87xx_sys_param { |
| @@ -726,7 +727,7 @@ static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) · 563 unmodified lines | ||
| 726 | 727 | break; |
| 727 | 728 | } |
| 728 | 729 | |
| 729 | − msleep(50); | |
| 730 | + msleep(WDT_FLASH_ERASE_DELAY_MS); | |
| 730 | 731 | |
| 731 | 732 | error = wdt87xx_write_data(client, data, start_addr, |
| 732 | 733 | page_size); |
| @@ -500,7 +500,7 @@ static int wm97xx_ts_input_open(struct input_dev *idev) · 499 unmodified lines | ||
| 500 | 500 | { |
| 501 | 501 | struct wm97xx *wm = input_get_drvdata(idev); |
| 502 | 502 | |
| 503 | − wm->ts_workq = create_singlethread_workqueue("kwm97xx"); | |
| 503 | + wm->ts_workq = alloc_ordered_workqueue("kwm97xx", 0); | |
| 504 | 504 | if (wm->ts_workq == NULL) { |
| 505 | 505 | dev_err(wm->dev, |
| 506 | 506 | "Failed to create workqueue\n"); |