Commit c758f96a authored by Dmitry Torokhov's avatar Dmitry Torokhov

Merge branch 'next' into for-linus

Prepare first round of input updates for 4.9 merge window.
parents 9fb6de1b 265d426d
ADC attached resistor ladder buttons
------------------------------------
Required properties:
- compatible: "adc-keys"
- io-channels: Phandle to an ADC channel
- io-channel-names = "buttons";
- keyup-threshold-microvolt: Voltage at which all the keys are considered up.
Optional properties:
- poll-interval: Poll interval time in milliseconds
- autorepeat: Boolean, Enable auto repeat feature of Linux input
subsystem.
Each button (key) is represented as a sub-node of "adc-keys":
Required subnode-properties:
- label: Descriptive name of the key.
- linux,code: Keycode to emit.
- press-threshold-microvolt: Voltage ADC input when this key is pressed.
Example:
#include <dt-bindings/input/input.h>
adc-keys {
compatible = "adc-keys";
io-channels = <&lradc 0>;
io-channel-names = "buttons";
keyup-threshold-microvolt = <2000000>;
button-up {
label = "Volume Up";
linux,code = <KEY_VOLUMEUP>;
press-threshold-microvolt = <1500000>;
};
button-down {
label = "Volume Down";
linux,code = <KEY_VOLUMEDOWN>;
press-threshold-microvolt = <1000000>;
};
button-enter {
label = "Enter";
linux,code = <KEY_ENTER>;
press-threshold-microvolt = <500000>;
};
};
* GPIO Decoder DT bindings
Required Properties:
- compatible: should be "gpio-decoder"
- gpios: a spec of gpios (at least two) to be decoded to a number with
first entry representing the MSB.
Optional Properties:
- decoder-max-value: Maximum possible value that can be reported by
the gpios.
- linux,axis: the input subsystem axis to map to (ABS_X/ABS_Y).
Defaults to 0 (ABS_X).
Example:
gpio-decoder0 {
compatible = "gpio-decoder";
gpios = <&pca9536 3 GPIO_ACTIVE_HIGH>,
<&pca9536 2 GPIO_ACTIVE_HIGH>,
<&pca9536 1 GPIO_ACTIVE_HIGH>,
<&pca9536 0 GPIO_ACTIVE_HIGH>;
linux,axis = <0>; /* ABS_X */
decoder-max-value = <9>;
};
...@@ -34,11 +34,10 @@ Example nodes: ...@@ -34,11 +34,10 @@ Example nodes:
gpio_keys_polled { gpio_keys_polled {
compatible = "gpio-keys-polled"; compatible = "gpio-keys-polled";
#address-cells = <1>;
#size-cells = <0>;
poll-interval = <100>; poll-interval = <100>;
autorepeat; autorepeat;
button@21 {
button21 {
label = "GPIO Key UP"; label = "GPIO Key UP";
linux,code = <103>; linux,code = <103>;
gpios = <&gpio1 0 1>; gpios = <&gpio1 0 1>;
......
...@@ -19,6 +19,7 @@ Required properties: ...@@ -19,6 +19,7 @@ Required properties:
or: "edt,edt-ft5306" or: "edt,edt-ft5306"
or: "edt,edt-ft5406" or: "edt,edt-ft5406"
or: "edt,edt-ft5506" or: "edt,edt-ft5506"
or: "focaltech,ft6236"
- reg: I2C slave address of the chip (0x38) - reg: I2C slave address of the chip (0x38)
- interrupt-parent: a phandle pointing to the interrupt controller - interrupt-parent: a phandle pointing to the interrupt controller
...@@ -43,6 +44,13 @@ Optional properties: ...@@ -43,6 +44,13 @@ Optional properties:
- offset: allows setting the edge compensation in the range from - offset: allows setting the edge compensation in the range from
0 to 31. 0 to 31.
- touchscreen-size-x : See touchscreen.txt
- touchscreen-size-y : See touchscreen.txt
- touchscreen-fuzz-x : See touchscreen.txt
- touchscreen-fuzz-y : See touchscreen.txt
- touchscreen-inverted-x : See touchscreen.txt
- touchscreen-inverted-y : See touchscreen.txt
- touchscreen-swapped-x-y : See touchscreen.txt
Example: Example:
polytouch: edt-ft5x06@38 { polytouch: edt-ft5x06@38 {
......
* Elan eKTF2127 I2C touchscreen controller
Required properties:
- compatible : "elan,ektf2127"
- reg : I2C slave address of the chip (0x40)
- interrupt-parent : a phandle pointing to the interrupt controller
serving the interrupt for this chip
- interrupts : interrupt specification for the ektf2127 interrupt
- power-gpios : GPIO specification for the pin connected to the
ektf2127's wake input. This needs to be driven high
to take ektf2127 out of it's low power state
For additional optional properties see: touchscreen.txt
Example:
i2c@00000000 {
ektf2127: touchscreen@15 {
compatible = "elan,ektf2127";
reg = <0x15>;
interrupt-parent = <&pio>;
interrupts = <6 11 IRQ_TYPE_EDGE_FALLING>
power-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>;
touchscreen-inverted-x;
touchscreen-swapped-x-y;
};
};
* FocalTech FT6236 I2C touchscreen controller
Required properties:
- compatible : "focaltech,ft6236"
- reg : I2C slave address of the chip (0x38)
- interrupt-parent : a phandle pointing to the interrupt controller
serving the interrupt for this chip
- interrupts : interrupt specification for the touch controller
interrupt
- reset-gpios : GPIO specification for the RSTN input
- touchscreen-size-x : horizontal resolution of touchscreen (in pixels)
- touchscreen-size-y : vertical resolution of touchscreen (in pixels)
Optional properties:
- touchscreen-fuzz-x : horizontal noise value of the absolute input
device (in pixels)
- touchscreen-fuzz-y : vertical noise value of the absolute input
device (in pixels)
- touchscreen-inverted-x : X axis is inverted (boolean)
- touchscreen-inverted-y : Y axis is inverted (boolean)
- touchscreen-swapped-x-y: X and Y axis are swapped (boolean)
Swapping is done after inverting the axis
Example:
ft6x06@38 {
compatible = "focaltech,ft6236";
reg = <0x38>;
interrupt-parent = <&gpio>;
interrupts = <23 2>;
touchscreen-size-x = <320>;
touchscreen-size-y = <480>;
touchscreen-inverted-x;
touchscreen-swapped-x-y;
};
Texas Instruments TPS65218 power button Texas Instruments TPS65217 and TPS65218 power button
This module is part of the TPS65217/TPS65218. For more details about the whole
TPS65217 chip see Documentation/devicetree/bindings/regulator/tps65217.txt.
This driver provides a simple power button event via an Interrupt. This driver provides a simple power button event via an Interrupt.
Required properties: Required properties:
- compatible: should be "ti,tps65218-pwrbutton" - compatible: should be "ti,tps65217-pwrbutton" or "ti,tps65218-pwrbutton"
Required properties for TPS65218:
- interrupts: should be one of the following - interrupts: should be one of the following
- <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218 - <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218
Example: Examples:
&tps {
tps65217-pwrbutton {
compatible = "ti,tps65217-pwrbutton";
};
};
&tps { &tps {
power-button { power-button {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio/machine.h>
#include <linux/platform_data/sa11x0-serial.h> #include <linux/platform_data/sa11x0-serial.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/ioport.h> #include <linux/ioport.h>
...@@ -217,9 +218,22 @@ static struct platform_device jornada_ssp_device = { ...@@ -217,9 +218,22 @@ static struct platform_device jornada_ssp_device = {
.id = -1, .id = -1,
}; };
static struct resource jornada_kbd_resources[] = {
DEFINE_RES_IRQ(IRQ_GPIO0),
};
static struct platform_device jornada_kbd_device = { static struct platform_device jornada_kbd_device = {
.name = "jornada720_kbd", .name = "jornada720_kbd",
.id = -1, .id = -1,
.num_resources = ARRAY_SIZE(jornada_kbd_resources),
.resource = jornada_kbd_resources,
};
static struct gpiod_lookup_table jornada_ts_gpiod_table = {
.dev_id = "jornada_ts",
.table = {
GPIO_LOOKUP("gpio", 9, "penup", GPIO_ACTIVE_HIGH),
},
}; };
static struct platform_device jornada_ts_device = { static struct platform_device jornada_ts_device = {
...@@ -250,6 +264,8 @@ static int __init jornada720_init(void) ...@@ -250,6 +264,8 @@ static int __init jornada720_init(void)
GPSR = GPIO_GPIO20; /* restart gpio20 */ GPSR = GPIO_GPIO20; /* restart gpio20 */
udelay(20); /* give it some time to restart */ udelay(20); /* give it some time to restart */
gpiod_add_lookup_table(&jornada_ts_gpiod_table);
ret = platform_add_devices(devices, ARRAY_SIZE(devices)); ret = platform_add_devices(devices, ARRAY_SIZE(devices));
} }
......
...@@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD ...@@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD
if 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 config KEYBOARD_ADP5520
tristate "Keypad Support for ADP5520 PMIC" tristate "Keypad Support for ADP5520 PMIC"
depends on PMIC_ADP5520 depends on PMIC_ADP5520
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
# Each configuration option enables a list of files. # 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_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
......
/*
* 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");
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <mach/jornada720.h> #include <mach/jornada720.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>"); MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
...@@ -66,10 +64,8 @@ static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id) ...@@ -66,10 +64,8 @@ static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id)
jornada_ssp_start(); jornada_ssp_start();
if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) { if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) {
printk(KERN_DEBUG dev_dbg(&pdev->dev,
"jornada720_kbd: " "GetKeycode command failed with ETIMEDOUT, flushed bus\n");
"GetKeycode command failed with ETIMEDOUT, "
"flushed bus\n");
} else { } else {
/* How many keycodes are waiting for us? */ /* How many keycodes are waiting for us? */
count = jornada_ssp_byte(TXDUMMY); count = jornada_ssp_byte(TXDUMMY);
...@@ -97,14 +93,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev) ...@@ -97,14 +93,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev)
{ {
struct jornadakbd *jornadakbd; struct jornadakbd *jornadakbd;
struct input_dev *input_dev; struct input_dev *input_dev;
int i, err; int i, err, irq;
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL); irq = platform_get_irq(pdev, 0);
input_dev = input_allocate_device(); if (irq <= 0)
if (!jornadakbd || !input_dev) { return irq < 0 ? irq : -EINVAL;
err = -ENOMEM;
goto fail1; 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); platform_set_drvdata(pdev, jornadakbd);
...@@ -127,40 +125,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev) ...@@ -127,40 +125,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev)
input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_capability(input_dev, EV_MSC, MSC_SCAN);
err = request_irq(IRQ_GPIO0, err = devm_request_irq(&pdev->dev, irq, jornada720_kbd_interrupt,
jornada720_kbd_interrupt, IRQF_TRIGGER_FALLING, "jornadakbd", pdev);
IRQF_TRIGGER_FALLING,
"jornadakbd", pdev);
if (err) { if (err) {
printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n"); dev_err(&pdev->dev, "unable to grab IRQ%d: %d\n", irq, err);
goto fail1; return err;
} }
err = input_register_device(jornadakbd->input); return 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;
}; };
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 */ /* work with hotplug and coldplug */
MODULE_ALIAS("platform:jornada720_kbd"); MODULE_ALIAS("platform:jornada720_kbd");
...@@ -169,6 +143,5 @@ static struct platform_driver jornada720_kbd_driver = { ...@@ -169,6 +143,5 @@ static struct platform_driver jornada720_kbd_driver = {
.name = "jornada720_kbd", .name = "jornada720_kbd",
}, },
.probe = jornada720_kbd_probe, .probe = jornada720_kbd_probe,
.remove = jornada720_kbd_remove,
}; };
module_platform_driver(jornada720_kbd_driver); module_platform_driver(jornada720_kbd_driver);
...@@ -168,7 +168,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) ...@@ -168,7 +168,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
error = input_register_device(input); error = input_register_device(input);
if (error < 0) { if (error < 0) {
dev_err(&pdev->dev, "failed to register input device\n"); dev_err(&pdev->dev, "failed to register input device\n");
input_free_device(input);
return error; return error;
} }
......
...@@ -292,6 +292,18 @@ config INPUT_GPIO_TILT_POLLED ...@@ -292,6 +292,18 @@ config INPUT_GPIO_TILT_POLLED
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called gpio_tilt_polled. 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 config INPUT_IXP4XX_BEEPER
tristate "IXP4XX Beeper support" tristate "IXP4XX Beeper support"
depends on ARCH_IXP4XX depends on ARCH_IXP4XX
...@@ -454,10 +466,10 @@ config INPUT_RETU_PWRBUTTON ...@@ -454,10 +466,10 @@ config INPUT_RETU_PWRBUTTON
config INPUT_TPS65218_PWRBUTTON config INPUT_TPS65218_PWRBUTTON
tristate "TPS65218 Power button driver" tristate "TPS65218 Power button driver"
depends on MFD_TPS65218 depends on (MFD_TPS65217 || MFD_TPS65218)
help help
Say Y here if you want to enable power buttong reporting for 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 To compile this driver as a module, choose M here. The module will
be called tps65218-pwrbutton. be called tps65218-pwrbutton.
......
...@@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o ...@@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.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_INPUT_HISI_POWERKEY) += hisi_powerkey.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
......
/*
* 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");
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (C) 2014,2015 Samsung Electronics * Copyright (C) 2014,2015 Samsung Electronics
* Jaewon Kim <jaewon02.kim@samsung.com> * 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. * This program is not provided / owned by Maxim Integrated Products.
* *
...@@ -415,7 +415,7 @@ static struct platform_driver max77693_haptic_driver = { ...@@ -415,7 +415,7 @@ static struct platform_driver max77693_haptic_driver = {
module_platform_driver(max77693_haptic_driver); module_platform_driver(max77693_haptic_driver);
MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); 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_DESCRIPTION("MAXIM 77693/77843 Haptic driver");
MODULE_ALIAS("platform:max77693-haptic"); MODULE_ALIAS("platform:max77693-haptic");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* /*
* 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/ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
* Author: Felipe Balbi <balbi@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 * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -18,31 +19,61 @@ ...@@ -18,31 +19,61 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mfd/tps65217.h>
#include <linux/mfd/tps65218.h> #include <linux/mfd/tps65218.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.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 device *dev;
struct tps65218 *tps; struct regmap *regmap;
struct input_dev *idev; 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; unsigned int reg;
int error; int error;
error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, &reg); error = regmap_read(pwr->regmap, tps_data->reg_status, &reg);
if (error) { if (error) {
dev_err(pwr->dev, "can't read register: %d\n", error); dev_err(pwr->dev, "can't read register: %d\n", error);
goto out; goto out;
} }
if (reg & TPS65218_STATUS_PB_STATE) { if (reg & tps_data->pb_mask) {
input_report_key(pwr->idev, KEY_POWER, 1); input_report_key(pwr->idev, KEY_POWER, 1);
pm_wakeup_event(pwr->dev, 0); pm_wakeup_event(pwr->dev, 0);
} else { } else {
...@@ -55,42 +86,55 @@ static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr) ...@@ -55,42 +86,55 @@ static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr)
return IRQ_HANDLED; 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 device *dev = &pdev->dev;
struct tps65218_pwrbutton *pwr; struct tps6521x_pwrbutton *pwr;
struct input_dev *idev; struct input_dev *idev;
const struct of_device_id *match;
int error; int error;
int irq; 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); pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
if (!pwr) if (!pwr)
return -ENOMEM; return -ENOMEM;
pwr->data = match->data;
idev = devm_input_allocate_device(dev); idev = devm_input_allocate_device(dev);
if (!idev) if (!idev)
return -ENOMEM; return -ENOMEM;
idev->name = "tps65218_pwrbutton"; idev->name = pwr->data->name;
idev->phys = "tps65218_pwrbutton/input0"; snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0",
pwr->data->name);
idev->phys = pwr->phys;
idev->dev.parent = dev; idev->dev.parent = dev;
idev->id.bustype = BUS_I2C; idev->id.bustype = BUS_I2C;
input_set_capability(idev, EV_KEY, KEY_POWER); input_set_capability(idev, EV_KEY, KEY_POWER);
pwr->tps = tps; pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL);
pwr->dev = dev; pwr->dev = dev;
pwr->idev = idev; pwr->idev = idev;
platform_set_drvdata(pdev, pwr); platform_set_drvdata(pdev, pwr);
device_init_wakeup(dev, true); device_init_wakeup(dev, true);
irq = platform_get_irq(pdev, 0); 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_RISING |
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT, IRQF_ONESHOT,
"tps65218-pwrbutton", pwr); pwr->data->name, pwr);
if (error) { if (error) {
dev_err(dev, "failed to request IRQ #%d: %d\n", dev_err(dev, "failed to request IRQ #%d: %d\n",
irq, error); irq, error);
...@@ -106,21 +150,15 @@ static int tps65218_pwron_probe(struct platform_device *pdev) ...@@ -106,21 +150,15 @@ static int tps65218_pwron_probe(struct platform_device *pdev)
return 0; return 0;
} }
static const struct of_device_id of_tps65218_pwr_match[] = { static struct platform_driver tps6521x_pb_driver = {
{ .compatible = "ti,tps65218-pwrbutton" }, .probe = tps6521x_pb_probe,
{ },
};
MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match);
static struct platform_driver tps65218_pwron_driver = {
.probe = tps65218_pwron_probe,
.driver = { .driver = {
.name = "tps65218_pwrbutton", .name = "tps6521x_pwrbutton",
.of_match_table = of_tps65218_pwr_match, .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_LICENSE("GPL v2");
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
...@@ -222,11 +222,13 @@ static int elan_smbus_get_checksum(struct i2c_client *client, ...@@ -222,11 +222,13 @@ static int elan_smbus_get_checksum(struct i2c_client *client,
static int elan_smbus_get_max(struct i2c_client *client, static int elan_smbus_get_max(struct i2c_client *client,
unsigned int *max_x, unsigned int *max_y) unsigned int *max_x, unsigned int *max_y)
{ {
int ret;
int error; int error;
u8 val[3]; u8 val[3];
error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val); ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
if (error) { if (ret != 3) {
error = ret < 0 ? ret : -EIO;
dev_err(&client->dev, "failed to get dimensions: %d\n", error); dev_err(&client->dev, "failed to get dimensions: %d\n", error);
return error; return error;
} }
...@@ -240,12 +242,13 @@ static int elan_smbus_get_max(struct i2c_client *client, ...@@ -240,12 +242,13 @@ static int elan_smbus_get_max(struct i2c_client *client,
static int elan_smbus_get_resolution(struct i2c_client *client, static int elan_smbus_get_resolution(struct i2c_client *client,
u8 *hw_res_x, u8 *hw_res_y) u8 *hw_res_x, u8 *hw_res_y)
{ {
int ret;
int error; int error;
u8 val[3]; u8 val[3];
error = i2c_smbus_read_block_data(client, ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val);
ETP_SMBUS_RESOLUTION_CMD, val); if (ret != 3) {
if (error) { error = ret < 0 ? ret : -EIO;
dev_err(&client->dev, "failed to get resolution: %d\n", error); dev_err(&client->dev, "failed to get resolution: %d\n", error);
return error; return error;
} }
...@@ -260,12 +263,13 @@ static int elan_smbus_get_num_traces(struct i2c_client *client, ...@@ -260,12 +263,13 @@ static int elan_smbus_get_num_traces(struct i2c_client *client,
unsigned int *x_traces, unsigned int *x_traces,
unsigned int *y_traces) unsigned int *y_traces)
{ {
int ret;
int error; int error;
u8 val[3]; u8 val[3];
error = i2c_smbus_read_block_data(client, ret = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val);
ETP_SMBUS_XY_TRACENUM_CMD, val); if (ret != 3) {
if (error) { error = ret < 0 ? ret : -EIO;
dev_err(&client->dev, "failed to get trace info: %d\n", error); dev_err(&client->dev, "failed to get trace info: %d\n", error);
return error; return error;
} }
......
...@@ -390,7 +390,8 @@ static int focaltech_read_size(struct psmouse *psmouse) ...@@ -390,7 +390,8 @@ static int focaltech_read_size(struct psmouse *psmouse)
return 0; 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 */ /* not supported yet */
} }
......
...@@ -1916,7 +1916,7 @@ static int __init psmouse_init(void) ...@@ -1916,7 +1916,7 @@ static int __init psmouse_init(void)
synaptics_module_init(); synaptics_module_init();
hgpk_module_init(); hgpk_module_init();
kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0);
if (!kpsmoused_wq) { if (!kpsmoused_wq) {
pr_err("failed to create kpsmoused workqueue\n"); pr_err("failed to create kpsmoused workqueue\n");
return -ENOMEM; return -ENOMEM;
......
...@@ -71,10 +71,7 @@ static void serport_serio_close(struct serio *serio) ...@@ -71,10 +71,7 @@ static void serport_serio_close(struct serio *serio)
spin_lock_irqsave(&serport->lock, flags); spin_lock_irqsave(&serport->lock, flags);
clear_bit(SERPORT_ACTIVE, &serport->flags); clear_bit(SERPORT_ACTIVE, &serport->flags);
set_bit(SERPORT_DEAD, &serport->flags);
spin_unlock_irqrestore(&serport->lock, 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, ...@@ -248,6 +245,19 @@ static long serport_ldisc_compat_ioctl(struct tty_struct *tty,
} }
#endif #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) static void serport_ldisc_write_wakeup(struct tty_struct * tty)
{ {
struct serport *serport = (struct serport *) tty->disc_data; struct serport *serport = (struct serport *) tty->disc_data;
...@@ -274,6 +284,7 @@ static struct tty_ldisc_ops serport_ldisc = { ...@@ -274,6 +284,7 @@ static struct tty_ldisc_ops serport_ldisc = {
.compat_ioctl = serport_ldisc_compat_ioctl, .compat_ioctl = serport_ldisc_compat_ioctl,
#endif #endif
.receive_buf = serport_ldisc_receive, .receive_buf = serport_ldisc_receive,
.hangup = serport_ldisc_hangup,
.write_wakeup = serport_ldisc_write_wakeup .write_wakeup = serport_ldisc_write_wakeup
}; };
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/usb/input.h> #include <linux/usb/input.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
/* USB HID defines */ /* USB HID defines */
#define USB_REQ_GET_REPORT 0x01 #define USB_REQ_GET_REPORT 0x01
......
...@@ -305,19 +305,6 @@ config TOUCHSCREEN_EGALAX_SERIAL ...@@ -305,19 +305,6 @@ config TOUCHSCREEN_EGALAX_SERIAL
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called egalax_ts_serial. 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 config TOUCHSCREEN_FUJITSU
tristate "Fujitsu serial touchscreen" tristate "Fujitsu serial touchscreen"
select SERIO select SERIO
...@@ -397,6 +384,18 @@ config TOUCHSCREEN_GUNZE ...@@ -397,6 +384,18 @@ config TOUCHSCREEN_GUNZE
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called gunze. 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 config TOUCHSCREEN_ELAN
tristate "Elan eKTH I2C touchscreen" tristate "Elan eKTH I2C touchscreen"
depends on I2C depends on I2C
......
...@@ -32,11 +32,11 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o ...@@ -32,11 +32,11 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_EKTF2127) += ektf2127.o
obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.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_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
......
...@@ -1063,9 +1063,15 @@ static const struct edt_i2c_chip_data edt_ft5506_data = { ...@@ -1063,9 +1063,15 @@ static const struct edt_i2c_chip_data edt_ft5506_data = {
.max_support_points = 10, .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[] = { static const struct i2c_device_id edt_ft5x06_ts_id[] = {
{ .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
{ .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_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 */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
...@@ -1076,6 +1082,8 @@ static const struct of_device_id edt_ft5x06_of_match[] = { ...@@ -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-ft5306", .data = &edt_ft5x06_data },
{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_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 */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
......
/*
* 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");
...@@ -298,7 +298,7 @@ static u16 elants_i2c_parse_version(u8 *buf) ...@@ -298,7 +298,7 @@ static u16 elants_i2c_parse_version(u8 *buf)
return get_unaligned_be32(buf) >> 4; 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; struct i2c_client *client = ts->client;
int error, retry_cnt; int error, retry_cnt;
...@@ -318,8 +318,13 @@ static int elants_i2c_query_fw_id(struct elants_data *ts) ...@@ -318,8 +318,13 @@ static int elants_i2c_query_fw_id(struct elants_data *ts)
error, (int)sizeof(resp), resp); error, (int)sizeof(resp), resp);
} }
dev_err(&client->dev, if (error) {
"Failed to read fw id or fw id is invalid\n"); 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; return -EINVAL;
} }
...@@ -508,7 +513,7 @@ static int elants_i2c_fastboot(struct i2c_client *client) ...@@ -508,7 +513,7 @@ static int elants_i2c_fastboot(struct i2c_client *client)
static int elants_i2c_initialize(struct elants_data *ts) static int elants_i2c_initialize(struct elants_data *ts)
{ {
struct i2c_client *client = ts->client; 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 hello_packet[] = { 0x55, 0x55, 0x55, 0x55 };
const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 }; const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 };
u8 buf[HEADER_SIZE]; u8 buf[HEADER_SIZE];
...@@ -553,18 +558,22 @@ static int elants_i2c_initialize(struct elants_data *ts) ...@@ -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) if (!error)
error = elants_i2c_query_fw_id(ts); error = error2;
if (!error) if (!error)
error = elants_i2c_query_fw_version(ts); 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; 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; return 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");
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
* HP Jornada 710/720/729 Touchscreen Driver * HP Jornada 710/720/729 Touchscreen Driver
*/ */
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -20,9 +21,7 @@ ...@@ -20,9 +21,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/io.h> #include <linux/io.h>
#include <mach/hardware.h>
#include <mach/jornada720.h> #include <mach/jornada720.h>
#include <mach/irqs.h>
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
...@@ -30,6 +29,7 @@ MODULE_LICENSE("GPL v2"); ...@@ -30,6 +29,7 @@ MODULE_LICENSE("GPL v2");
struct jornada_ts { struct jornada_ts {
struct input_dev *dev; struct input_dev *dev;
struct gpio_desc *gpio;
int x_data[4]; /* X sample values */ int x_data[4]; /* X sample values */
int y_data[4]; /* Y sample values */ int y_data[4]; /* Y sample values */
}; };
...@@ -71,8 +71,8 @@ static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id) ...@@ -71,8 +71,8 @@ static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)
struct input_dev *input = jornada_ts->dev; struct input_dev *input = jornada_ts->dev;
int x, y; int x, y;
/* If GPIO_GPIO9 is set to high then report pen up */ /* If gpio is high then report pen up */
if (GPLR & GPIO_GPIO(9)) { if (gpiod_get_value(jornada_ts->gpio)) {
input_report_key(input, BTN_TOUCH, 0); input_report_key(input, BTN_TOUCH, 0);
input_sync(input); input_sync(input);
} else { } else {
...@@ -101,7 +101,7 @@ static int jornada720_ts_probe(struct platform_device *pdev) ...@@ -101,7 +101,7 @@ static int jornada720_ts_probe(struct platform_device *pdev)
{ {
struct jornada_ts *jornada_ts; struct jornada_ts *jornada_ts;
struct input_dev *input_dev; struct input_dev *input_dev;
int error; int error, irq;
jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL); jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
if (!jornada_ts) if (!jornada_ts)
...@@ -113,6 +113,14 @@ static int jornada720_ts_probe(struct platform_device *pdev) ...@@ -113,6 +113,14 @@ static int jornada720_ts_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, jornada_ts); 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; jornada_ts->dev = input_dev;
input_dev->name = "HP Jornada 7xx Touchscreen"; input_dev->name = "HP Jornada 7xx Touchscreen";
...@@ -125,8 +133,7 @@ static int jornada720_ts_probe(struct platform_device *pdev) ...@@ -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_X, 270, 3900, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0); input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
error = devm_request_irq(&pdev->dev, IRQ_GPIO9, error = devm_request_irq(&pdev->dev, irq, jornada720_ts_interrupt,
jornada720_ts_interrupt,
IRQF_TRIGGER_RISING, IRQF_TRIGGER_RISING,
"HP7XX Touchscreen driver", pdev); "HP7XX Touchscreen driver", pdev);
if (error) { if (error) {
......
...@@ -37,7 +37,6 @@ struct mc13783_ts_priv { ...@@ -37,7 +37,6 @@ struct mc13783_ts_priv {
struct input_dev *idev; struct input_dev *idev;
struct mc13xxx *mc13xxx; struct mc13xxx *mc13xxx;
struct delayed_work work; struct delayed_work work;
struct workqueue_struct *workq;
unsigned int sample[4]; unsigned int sample[4];
struct mc13xxx_ts_platform_data *touch; struct mc13xxx_ts_platform_data *touch;
}; };
...@@ -54,7 +53,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data) ...@@ -54,7 +53,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data)
* be rescheduled for immediate execution here. However the rearm * be rescheduled for immediate execution here. However the rearm
* delay is HZ / 50 which is acceptable. * delay is HZ / 50 which is acceptable.
*/ */
queue_delayed_work(priv->workq, &priv->work, 0); schedule_delayed_work(&priv->work, 0);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -106,16 +105,18 @@ static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv) ...@@ -106,16 +105,18 @@ static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)
dev_dbg(&idev->dev, "report (%d, %d, %d)\n", dev_dbg(&idev->dev, "report (%d, %d, %d)\n",
x1, y1, 0x1000 - cr0); x1, y1, 0x1000 - cr0);
queue_delayed_work(priv->workq, &priv->work, HZ / 50); schedule_delayed_work(&priv->work, HZ / 50);
} else } else {
dev_dbg(&idev->dev, "report release\n"); dev_dbg(&idev->dev, "report release\n");
}
input_report_abs(idev, ABS_PRESSURE, input_report_abs(idev, ABS_PRESSURE,
cr0 ? 0x1000 - cr0 : cr0); cr0 ? 0x1000 - cr0 : cr0);
input_report_key(idev, BTN_TOUCH, cr0); input_report_key(idev, BTN_TOUCH, cr0);
input_sync(idev); input_sync(idev);
} else } else {
dev_dbg(&idev->dev, "discard event\n"); dev_dbg(&idev->dev, "discard event\n");
}
} }
static void mc13783_ts_work(struct work_struct *work) static void mc13783_ts_work(struct work_struct *work)
...@@ -189,14 +190,6 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) ...@@ -189,14 +190,6 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
goto err_free_mem; 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->name = MC13783_TS_NAME;
idev->dev.parent = &pdev->dev; idev->dev.parent = &pdev->dev;
...@@ -215,14 +208,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev) ...@@ -215,14 +208,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
if (ret) { if (ret) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"register input device failed with %d\n", ret); "register input device failed with %d\n", ret);
goto err_destroy_wq; goto err_free_mem;
} }
platform_set_drvdata(pdev, priv); platform_set_drvdata(pdev, priv);
return 0; return 0;
err_destroy_wq:
destroy_workqueue(priv->workq);
err_free_mem: err_free_mem:
input_free_device(idev); input_free_device(idev);
kfree(priv); kfree(priv);
...@@ -233,7 +224,6 @@ static int mc13783_ts_remove(struct platform_device *pdev) ...@@ -233,7 +224,6 @@ static int mc13783_ts_remove(struct platform_device *pdev)
{ {
struct mc13783_ts_priv *priv = platform_get_drvdata(pdev); struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
destroy_workqueue(priv->workq);
input_unregister_device(priv->idev); input_unregister_device(priv->idev);
kfree(priv); kfree(priv);
......
...@@ -11,10 +11,6 @@ ...@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * 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> #include <linux/delay.h>
...@@ -404,7 +400,6 @@ static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev) ...@@ -404,7 +400,6 @@ static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev)
mutex_lock(&input->mutex); mutex_lock(&input->mutex);
if (device_may_wakeup(&client->dev)) { if (device_may_wakeup(&client->dev)) {
if (!input->users) { if (!input->users) {
ret = pixcir_stop(ts); ret = pixcir_stop(ts);
if (ret) { if (ret) {
...@@ -431,13 +426,7 @@ static const struct of_device_id pixcir_of_match[]; ...@@ -431,13 +426,7 @@ static const struct of_device_id pixcir_of_match[];
static int pixcir_parse_dt(struct device *dev, static int pixcir_parse_dt(struct device *dev,
struct pixcir_i2c_ts_data *tsdata) struct pixcir_i2c_ts_data *tsdata)
{ {
const struct of_device_id *match; tsdata->chip = of_device_get_match_data(dev);
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;
if (!tsdata->chip) if (!tsdata->chip)
return -EINVAL; return -EINVAL;
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#define WDT87XX_NAME "wdt87xx_i2c" #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_FW_NAME "wdt87xx_fw.bin"
#define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" #define WDT87XX_CFG_NAME "wdt87xx_cfg.bin"
...@@ -157,6 +157,7 @@ ...@@ -157,6 +157,7 @@
/* Controller requires minimum 300us between commands */ /* Controller requires minimum 300us between commands */
#define WDT_COMMAND_DELAY_MS 2 #define WDT_COMMAND_DELAY_MS 2
#define WDT_FLASH_WRITE_DELAY_MS 4 #define WDT_FLASH_WRITE_DELAY_MS 4
#define WDT_FLASH_ERASE_DELAY_MS 200
#define WDT_FW_RESET_TIME 2500 #define WDT_FW_RESET_TIME 2500
struct wdt87xx_sys_param { struct wdt87xx_sys_param {
...@@ -726,7 +727,7 @@ static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) ...@@ -726,7 +727,7 @@ static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk)
break; break;
} }
msleep(50); msleep(WDT_FLASH_ERASE_DELAY_MS);
error = wdt87xx_write_data(client, data, start_addr, error = wdt87xx_write_data(client, data, start_addr,
page_size); page_size);
......
...@@ -500,7 +500,7 @@ static int wm97xx_ts_input_open(struct input_dev *idev) ...@@ -500,7 +500,7 @@ static int wm97xx_ts_input_open(struct input_dev *idev)
{ {
struct wm97xx *wm = input_get_drvdata(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) { if (wm->ts_workq == NULL) {
dev_err(wm->dev, dev_err(wm->dev,
"Failed to create workqueue\n"); "Failed to create workqueue\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment