mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 13:11:40 +00:00
Merge branch 'next' into for-linus
Prepare first round of input updates for 4.9 merge window.
This commit is contained in:
commit
c758f96a8c
49
Documentation/devicetree/bindings/input/adc-keys.txt
Normal file
49
Documentation/devicetree/bindings/input/adc-keys.txt
Normal file
@ -0,0 +1,49 @@
|
||||
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>;
|
||||
};
|
||||
};
|
23
Documentation/devicetree/bindings/input/gpio-decoder.txt
Normal file
23
Documentation/devicetree/bindings/input/gpio-decoder.txt
Normal file
@ -0,0 +1,23 @@
|
||||
* 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:
|
||||
|
||||
gpio_keys_polled {
|
||||
compatible = "gpio-keys-polled";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
poll-interval = <100>;
|
||||
autorepeat;
|
||||
button@21 {
|
||||
|
||||
button21 {
|
||||
label = "GPIO Key UP";
|
||||
linux,code = <103>;
|
||||
gpios = <&gpio1 0 1>;
|
||||
|
@ -19,6 +19,7 @@ Required properties:
|
||||
or: "edt,edt-ft5306"
|
||||
or: "edt,edt-ft5406"
|
||||
or: "edt,edt-ft5506"
|
||||
or: "focaltech,ft6236"
|
||||
|
||||
- reg: I2C slave address of the chip (0x38)
|
||||
- interrupt-parent: a phandle pointing to the interrupt controller
|
||||
@ -43,6 +44,13 @@ Optional properties:
|
||||
|
||||
- offset: allows setting the edge compensation in the range from
|
||||
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:
|
||||
polytouch: edt-ft5x06@38 {
|
||||
|
@ -0,0 +1,27 @@
|
||||
* 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;
|
||||
};
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
* 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;
|
||||
};
|
@ -1,13 +1,24 @@
|
||||
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.
|
||||
|
||||
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
|
||||
- <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
&tps {
|
||||
tps65217-pwrbutton {
|
||||
compatible = "ti,tps65217-pwrbutton";
|
||||
};
|
||||
};
|
||||
|
||||
&tps {
|
||||
power-button {
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/platform_data/sa11x0-serial.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ioport.h>
|
||||
@ -217,9 +218,22 @@ static struct platform_device jornada_ssp_device = {
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static struct resource jornada_kbd_resources[] = {
|
||||
DEFINE_RES_IRQ(IRQ_GPIO0),
|
||||
};
|
||||
|
||||
static struct platform_device jornada_kbd_device = {
|
||||
.name = "jornada720_kbd",
|
||||
.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 = {
|
||||
@ -250,6 +264,8 @@ static int __init jornada720_init(void)
|
||||
GPSR = GPIO_GPIO20; /* restart gpio20 */
|
||||
udelay(20); /* give it some time to restart */
|
||||
|
||||
gpiod_add_lookup_table(&jornada_ts_gpiod_table);
|
||||
|
||||
ret = platform_add_devices(devices, ARRAY_SIZE(devices));
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,21 @@ menuconfig INPUT_KEYBOARD
|
||||
|
||||
if INPUT_KEYBOARD
|
||||
|
||||
config KEYBOARD_ADC
|
||||
tristate "ADC Ladder Buttons"
|
||||
depends on IIO
|
||||
select INPUT_POLLDEV
|
||||
help
|
||||
This driver implements support for buttons connected
|
||||
to an ADC using a resistor ladder.
|
||||
|
||||
Say Y here if your device has such buttons connected to an ADC. Your
|
||||
board-specific setup logic must also provide a configuration data
|
||||
for mapping voltages to buttons.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adc_keys.
|
||||
|
||||
config KEYBOARD_ADP5520
|
||||
tristate "Keypad Support for ADP5520 PMIC"
|
||||
depends on PMIC_ADP5520
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
|
||||
|
210
drivers/input/keyboard/adc-keys.c
Normal file
210
drivers/input/keyboard/adc-keys.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Input driver for resistor ladder connected on ADC
|
||||
*
|
||||
* Copyright (c) 2016 Alexandre Belloni
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input-polldev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct adc_keys_button {
|
||||
u32 voltage;
|
||||
u32 keycode;
|
||||
};
|
||||
|
||||
struct adc_keys_state {
|
||||
struct iio_channel *channel;
|
||||
u32 num_keys;
|
||||
u32 last_key;
|
||||
u32 keyup_voltage;
|
||||
const struct adc_keys_button *map;
|
||||
};
|
||||
|
||||
static void adc_keys_poll(struct input_polled_dev *dev)
|
||||
{
|
||||
struct adc_keys_state *st = dev->private;
|
||||
int i, value, ret;
|
||||
u32 diff, closest = 0xffffffff;
|
||||
int keycode = 0;
|
||||
|
||||
ret = iio_read_channel_processed(st->channel, &value);
|
||||
if (unlikely(ret < 0)) {
|
||||
/* Forcibly release key if any was pressed */
|
||||
value = st->keyup_voltage;
|
||||
} else {
|
||||
for (i = 0; i < st->num_keys; i++) {
|
||||
diff = abs(st->map[i].voltage - value);
|
||||
if (diff < closest) {
|
||||
closest = diff;
|
||||
keycode = st->map[i].keycode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (abs(st->keyup_voltage - value) < closest)
|
||||
keycode = 0;
|
||||
|
||||
if (st->last_key && st->last_key != keycode)
|
||||
input_report_key(dev->input, st->last_key, 0);
|
||||
|
||||
if (keycode)
|
||||
input_report_key(dev->input, keycode, 1);
|
||||
|
||||
input_sync(dev->input);
|
||||
st->last_key = keycode;
|
||||
}
|
||||
|
||||
static int adc_keys_load_keymap(struct device *dev, struct adc_keys_state *st)
|
||||
{
|
||||
struct adc_keys_button *map;
|
||||
struct fwnode_handle *child;
|
||||
int i;
|
||||
|
||||
st->num_keys = device_get_child_node_count(dev);
|
||||
if (st->num_keys == 0) {
|
||||
dev_err(dev, "keymap is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
map = devm_kmalloc_array(dev, st->num_keys, sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
device_for_each_child_node(dev, child) {
|
||||
if (fwnode_property_read_u32(child, "press-threshold-microvolt",
|
||||
&map[i].voltage)) {
|
||||
dev_err(dev, "Key with invalid or missing voltage\n");
|
||||
fwnode_handle_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
map[i].voltage /= 1000;
|
||||
|
||||
if (fwnode_property_read_u32(child, "linux,code",
|
||||
&map[i].keycode)) {
|
||||
dev_err(dev, "Key with invalid or missing linux,code\n");
|
||||
fwnode_handle_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
st->map = map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_keys_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct adc_keys_state *st;
|
||||
struct input_polled_dev *poll_dev;
|
||||
struct input_dev *input;
|
||||
enum iio_chan_type type;
|
||||
int i, value;
|
||||
int error;
|
||||
|
||||
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->channel = devm_iio_channel_get(dev, "buttons");
|
||||
if (IS_ERR(st->channel))
|
||||
return PTR_ERR(st->channel);
|
||||
|
||||
if (!st->channel->indio_dev)
|
||||
return -ENXIO;
|
||||
|
||||
error = iio_get_channel_type(st->channel, &type);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (type != IIO_VOLTAGE) {
|
||||
dev_err(dev, "Incompatible channel type %d\n", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (device_property_read_u32(dev, "keyup-threshold-microvolt",
|
||||
&st->keyup_voltage)) {
|
||||
dev_err(dev, "Invalid or missing keyup voltage\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
st->keyup_voltage /= 1000;
|
||||
|
||||
error = adc_keys_load_keymap(dev, st);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
platform_set_drvdata(pdev, st);
|
||||
|
||||
poll_dev = devm_input_allocate_polled_device(dev);
|
||||
if (!poll_dev) {
|
||||
dev_err(dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!device_property_read_u32(dev, "poll-interval", &value))
|
||||
poll_dev->poll_interval = value;
|
||||
|
||||
poll_dev->poll = adc_keys_poll;
|
||||
poll_dev->private = st;
|
||||
|
||||
input = poll_dev->input;
|
||||
|
||||
input->name = pdev->name;
|
||||
input->phys = "adc-keys/input0";
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->id.vendor = 0x0001;
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = 0x0100;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
for (i = 0; i < st->num_keys; i++)
|
||||
__set_bit(st->map[i].keycode, input->keybit);
|
||||
|
||||
if (device_property_read_bool(dev, "autorepeat"))
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
error = input_register_polled_device(poll_dev);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to register input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id adc_keys_of_match[] = {
|
||||
{ .compatible = "adc-keys", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc_keys_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver __refdata adc_keys_driver = {
|
||||
.driver = {
|
||||
.name = "adc_keys",
|
||||
.of_match_table = of_match_ptr(adc_keys_of_match),
|
||||
},
|
||||
.probe = adc_keys_probe,
|
||||
};
|
||||
module_platform_driver(adc_keys_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Input driver for resistor ladder connected on ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -25,8 +25,6 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <mach/jornada720.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
|
||||
MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
|
||||
@ -66,10 +64,8 @@ static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id)
|
||||
jornada_ssp_start();
|
||||
|
||||
if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) {
|
||||
printk(KERN_DEBUG
|
||||
"jornada720_kbd: "
|
||||
"GetKeycode command failed with ETIMEDOUT, "
|
||||
"flushed bus\n");
|
||||
dev_dbg(&pdev->dev,
|
||||
"GetKeycode command failed with ETIMEDOUT, flushed bus\n");
|
||||
} else {
|
||||
/* How many keycodes are waiting for us? */
|
||||
count = jornada_ssp_byte(TXDUMMY);
|
||||
@ -97,14 +93,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct jornadakbd *jornadakbd;
|
||||
struct input_dev *input_dev;
|
||||
int i, err;
|
||||
int i, err, irq;
|
||||
|
||||
jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!jornadakbd || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0)
|
||||
return irq < 0 ? irq : -EINVAL;
|
||||
|
||||
jornadakbd = devm_kzalloc(&pdev->dev, sizeof(*jornadakbd), GFP_KERNEL);
|
||||
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!jornadakbd || !input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, jornadakbd);
|
||||
|
||||
@ -127,40 +125,16 @@ static int jornada720_kbd_probe(struct platform_device *pdev)
|
||||
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
|
||||
err = request_irq(IRQ_GPIO0,
|
||||
jornada720_kbd_interrupt,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"jornadakbd", pdev);
|
||||
err = devm_request_irq(&pdev->dev, irq, jornada720_kbd_interrupt,
|
||||
IRQF_TRIGGER_FALLING, "jornadakbd", pdev);
|
||||
if (err) {
|
||||
printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n");
|
||||
goto fail1;
|
||||
dev_err(&pdev->dev, "unable to grab IRQ%d: %d\n", irq, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = input_register_device(jornadakbd->input);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2: /* IRQ, DEVICE, MEMORY */
|
||||
free_irq(IRQ_GPIO0, pdev);
|
||||
fail1: /* DEVICE, MEMORY */
|
||||
input_free_device(input_dev);
|
||||
kfree(jornadakbd);
|
||||
return err;
|
||||
return input_register_device(jornadakbd->input);
|
||||
};
|
||||
|
||||
static int jornada720_kbd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(IRQ_GPIO0, pdev);
|
||||
input_unregister_device(jornadakbd->input);
|
||||
kfree(jornadakbd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:jornada720_kbd");
|
||||
|
||||
@ -169,6 +143,5 @@ static struct platform_driver jornada720_kbd_driver = {
|
||||
.name = "jornada720_kbd",
|
||||
},
|
||||
.probe = jornada720_kbd_probe,
|
||||
.remove = jornada720_kbd_remove,
|
||||
};
|
||||
module_platform_driver(jornada720_kbd_driver);
|
||||
|
@ -168,7 +168,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
|
||||
error = input_register_device(input);
|
||||
if (error < 0) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
input_free_device(input);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -292,6 +292,18 @@ config INPUT_GPIO_TILT_POLLED
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gpio_tilt_polled.
|
||||
|
||||
config INPUT_GPIO_DECODER
|
||||
tristate "Polled GPIO Decoder Input driver"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select INPUT_POLLDEV
|
||||
help
|
||||
Say Y here if you want driver to read status of multiple GPIO
|
||||
lines and report the encoded value as an absolute integer to
|
||||
input subsystem.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called gpio_decoder.
|
||||
|
||||
config INPUT_IXP4XX_BEEPER
|
||||
tristate "IXP4XX Beeper support"
|
||||
depends on ARCH_IXP4XX
|
||||
@ -454,10 +466,10 @@ config INPUT_RETU_PWRBUTTON
|
||||
|
||||
config INPUT_TPS65218_PWRBUTTON
|
||||
tristate "TPS65218 Power button driver"
|
||||
depends on MFD_TPS65218
|
||||
depends on (MFD_TPS65217 || MFD_TPS65218)
|
||||
help
|
||||
Say Y here if you want to enable power buttong reporting for
|
||||
the TPS65218 Power Management IC device.
|
||||
TPS65217 and TPS65218 Power Management IC devices.
|
||||
|
||||
To compile this driver as a module, choose M here. The module will
|
||||
be called tps65218-pwrbutton.
|
||||
|
@ -35,6 +35,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
|
||||
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
|
||||
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
|
||||
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
|
||||
obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o
|
||||
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
|
||||
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
|
||||
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
|
||||
|
137
drivers/input/misc/gpio_decoder.c
Normal file
137
drivers/input/misc/gpio_decoder.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* A generic driver to read multiple gpio lines and translate the
|
||||
* encoded numeric value into an input event.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input-polldev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct gpio_decoder {
|
||||
struct input_polled_dev *poll_dev;
|
||||
struct gpio_descs *input_gpios;
|
||||
struct device *dev;
|
||||
u32 axis;
|
||||
u32 last_stable;
|
||||
};
|
||||
|
||||
static int gpio_decoder_get_gpios_state(struct gpio_decoder *decoder)
|
||||
{
|
||||
struct gpio_descs *gpios = decoder->input_gpios;
|
||||
unsigned int ret = 0;
|
||||
int i, val;
|
||||
|
||||
for (i = 0; i < gpios->ndescs; i++) {
|
||||
val = gpiod_get_value_cansleep(gpios->desc[i]);
|
||||
if (val < 0) {
|
||||
dev_err(decoder->dev,
|
||||
"Error reading gpio %d: %d\n",
|
||||
desc_to_gpio(gpios->desc[i]), val);
|
||||
return val;
|
||||
}
|
||||
|
||||
val = !!val;
|
||||
ret = (ret << 1) | val;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gpio_decoder_poll_gpios(struct input_polled_dev *poll_dev)
|
||||
{
|
||||
struct gpio_decoder *decoder = poll_dev->private;
|
||||
int state;
|
||||
|
||||
state = gpio_decoder_get_gpios_state(decoder);
|
||||
if (state >= 0 && state != decoder->last_stable) {
|
||||
input_report_abs(poll_dev->input, decoder->axis, state);
|
||||
input_sync(poll_dev->input);
|
||||
decoder->last_stable = state;
|
||||
}
|
||||
}
|
||||
|
||||
static int gpio_decoder_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct gpio_decoder *decoder;
|
||||
struct input_polled_dev *poll_dev;
|
||||
u32 max;
|
||||
int err;
|
||||
|
||||
decoder = devm_kzalloc(dev, sizeof(struct gpio_decoder), GFP_KERNEL);
|
||||
if (!decoder)
|
||||
return -ENOMEM;
|
||||
|
||||
device_property_read_u32(dev, "linux,axis", &decoder->axis);
|
||||
decoder->input_gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN);
|
||||
if (IS_ERR(decoder->input_gpios)) {
|
||||
dev_err(dev, "unable to acquire input gpios\n");
|
||||
return PTR_ERR(decoder->input_gpios);
|
||||
}
|
||||
if (decoder->input_gpios->ndescs < 2) {
|
||||
dev_err(dev, "not enough gpios found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (device_property_read_u32(dev, "decoder-max-value", &max))
|
||||
max = (1U << decoder->input_gpios->ndescs) - 1;
|
||||
|
||||
decoder->dev = dev;
|
||||
poll_dev = devm_input_allocate_polled_device(decoder->dev);
|
||||
if (!poll_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
poll_dev->private = decoder;
|
||||
poll_dev->poll = gpio_decoder_poll_gpios;
|
||||
decoder->poll_dev = poll_dev;
|
||||
|
||||
poll_dev->input->name = pdev->name;
|
||||
poll_dev->input->id.bustype = BUS_HOST;
|
||||
input_set_abs_params(poll_dev->input, decoder->axis, 0, max, 0, 0);
|
||||
|
||||
err = input_register_polled_device(poll_dev);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to register polled device\n");
|
||||
return err;
|
||||
}
|
||||
platform_set_drvdata(pdev, decoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id gpio_decoder_of_match[] = {
|
||||
{ .compatible = "gpio-decoder", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpio_decoder_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver gpio_decoder_driver = {
|
||||
.probe = gpio_decoder_probe,
|
||||
.driver = {
|
||||
.name = "gpio-decoder",
|
||||
.of_match_table = of_match_ptr(gpio_decoder_of_match),
|
||||
}
|
||||
};
|
||||
module_platform_driver(gpio_decoder_driver);
|
||||
|
||||
MODULE_DESCRIPTION("GPIO decoder input driver");
|
||||
MODULE_AUTHOR("Vignesh R <vigneshr@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2014,2015 Samsung Electronics
|
||||
* Jaewon Kim <jaewon02.kim@samsung.com>
|
||||
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
|
||||
* Krzysztof Kozlowski <krzk@kernel.org>
|
||||
*
|
||||
* This program is not provided / owned by Maxim Integrated Products.
|
||||
*
|
||||
@ -415,7 +415,7 @@ static struct platform_driver max77693_haptic_driver = {
|
||||
module_platform_driver(max77693_haptic_driver);
|
||||
|
||||
MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
|
||||
MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>");
|
||||
MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
|
||||
MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver");
|
||||
MODULE_ALIAS("platform:max77693-haptic");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Texas Instruments' TPS65218 Power Button Input Driver
|
||||
* Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver
|
||||
*
|
||||
* Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Author: Felipe Balbi <balbi@ti.com>
|
||||
* Author: Marcin Niestroj <m.niestroj@grinn-global.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@ -18,31 +19,61 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/tps65217.h>
|
||||
#include <linux/mfd/tps65218.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct tps65218_pwrbutton {
|
||||
struct device *dev;
|
||||
struct tps65218 *tps;
|
||||
struct input_dev *idev;
|
||||
struct tps6521x_data {
|
||||
unsigned int reg_status;
|
||||
unsigned int pb_mask;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr)
|
||||
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 regmap *regmap;
|
||||
struct input_dev *idev;
|
||||
const struct tps6521x_data *data;
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static const struct of_device_id of_tps6521x_pb_match[] = {
|
||||
{ .compatible = "ti,tps65217-pwrbutton", .data = &tps65217_data },
|
||||
{ .compatible = "ti,tps65218-pwrbutton", .data = &tps65218_data },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_tps6521x_pb_match);
|
||||
|
||||
static irqreturn_t tps6521x_pb_irq(int irq, void *_pwr)
|
||||
{
|
||||
struct tps65218_pwrbutton *pwr = _pwr;
|
||||
struct tps6521x_pwrbutton *pwr = _pwr;
|
||||
const struct tps6521x_data *tps_data = pwr->data;
|
||||
unsigned int reg;
|
||||
int error;
|
||||
|
||||
error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, ®);
|
||||
error = regmap_read(pwr->regmap, tps_data->reg_status, ®);
|
||||
if (error) {
|
||||
dev_err(pwr->dev, "can't read register: %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (reg & TPS65218_STATUS_PB_STATE) {
|
||||
if (reg & tps_data->pb_mask) {
|
||||
input_report_key(pwr->idev, KEY_POWER, 1);
|
||||
pm_wakeup_event(pwr->dev, 0);
|
||||
} else {
|
||||
@ -55,42 +86,55 @@ out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tps65218_pwron_probe(struct platform_device *pdev)
|
||||
static int tps6521x_pb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct tps65218_pwrbutton *pwr;
|
||||
struct tps6521x_pwrbutton *pwr;
|
||||
struct input_dev *idev;
|
||||
const struct of_device_id *match;
|
||||
int error;
|
||||
int irq;
|
||||
|
||||
match = of_match_node(of_tps6521x_pb_match, pdev->dev.of_node);
|
||||
if (!match)
|
||||
return -ENXIO;
|
||||
|
||||
pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
|
||||
if (!pwr)
|
||||
return -ENOMEM;
|
||||
|
||||
pwr->data = match->data;
|
||||
|
||||
idev = devm_input_allocate_device(dev);
|
||||
if (!idev)
|
||||
return -ENOMEM;
|
||||
|
||||
idev->name = "tps65218_pwrbutton";
|
||||
idev->phys = "tps65218_pwrbutton/input0";
|
||||
idev->name = pwr->data->name;
|
||||
snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0",
|
||||
pwr->data->name);
|
||||
idev->phys = pwr->phys;
|
||||
idev->dev.parent = dev;
|
||||
idev->id.bustype = BUS_I2C;
|
||||
|
||||
input_set_capability(idev, EV_KEY, KEY_POWER);
|
||||
|
||||
pwr->tps = tps;
|
||||
pwr->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
pwr->dev = dev;
|
||||
pwr->idev = idev;
|
||||
platform_set_drvdata(pdev, pwr);
|
||||
device_init_wakeup(dev, true);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq,
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "No IRQ resource!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(dev, irq, NULL, tps6521x_pb_irq,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
"tps65218-pwrbutton", pwr);
|
||||
pwr->data->name, pwr);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to request IRQ #%d: %d\n",
|
||||
irq, error);
|
||||
@ -106,21 +150,15 @@ static int tps65218_pwron_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_tps65218_pwr_match[] = {
|
||||
{ .compatible = "ti,tps65218-pwrbutton" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match);
|
||||
|
||||
static struct platform_driver tps65218_pwron_driver = {
|
||||
.probe = tps65218_pwron_probe,
|
||||
static struct platform_driver tps6521x_pb_driver = {
|
||||
.probe = tps6521x_pb_probe,
|
||||
.driver = {
|
||||
.name = "tps65218_pwrbutton",
|
||||
.of_match_table = of_tps65218_pwr_match,
|
||||
.name = "tps6521x_pwrbutton",
|
||||
.of_match_table = of_tps6521x_pb_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tps65218_pwron_driver);
|
||||
module_platform_driver(tps6521x_pb_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TPS65218 Power Button");
|
||||
MODULE_DESCRIPTION("TPS6521X Power Button");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
|
@ -222,11 +222,13 @@ static int elan_smbus_get_checksum(struct i2c_client *client,
|
||||
static int elan_smbus_get_max(struct i2c_client *client,
|
||||
unsigned int *max_x, unsigned int *max_y)
|
||||
{
|
||||
int ret;
|
||||
int error;
|
||||
u8 val[3];
|
||||
|
||||
error = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
|
||||
if (error) {
|
||||
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RANGE_CMD, val);
|
||||
if (ret != 3) {
|
||||
error = ret < 0 ? ret : -EIO;
|
||||
dev_err(&client->dev, "failed to get dimensions: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
@ -240,12 +242,13 @@ static int elan_smbus_get_max(struct i2c_client *client,
|
||||
static int elan_smbus_get_resolution(struct i2c_client *client,
|
||||
u8 *hw_res_x, u8 *hw_res_y)
|
||||
{
|
||||
int ret;
|
||||
int error;
|
||||
u8 val[3];
|
||||
|
||||
error = i2c_smbus_read_block_data(client,
|
||||
ETP_SMBUS_RESOLUTION_CMD, val);
|
||||
if (error) {
|
||||
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_RESOLUTION_CMD, val);
|
||||
if (ret != 3) {
|
||||
error = ret < 0 ? ret : -EIO;
|
||||
dev_err(&client->dev, "failed to get resolution: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
@ -260,12 +263,13 @@ static int elan_smbus_get_num_traces(struct i2c_client *client,
|
||||
unsigned int *x_traces,
|
||||
unsigned int *y_traces)
|
||||
{
|
||||
int ret;
|
||||
int error;
|
||||
u8 val[3];
|
||||
|
||||
error = i2c_smbus_read_block_data(client,
|
||||
ETP_SMBUS_XY_TRACENUM_CMD, val);
|
||||
if (error) {
|
||||
ret = i2c_smbus_read_block_data(client, ETP_SMBUS_XY_TRACENUM_CMD, val);
|
||||
if (ret != 3) {
|
||||
error = ret < 0 ? ret : -EIO;
|
||||
dev_err(&client->dev, "failed to get trace info: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
@ -390,7 +390,8 @@ static int focaltech_read_size(struct psmouse *psmouse)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution)
|
||||
static void focaltech_set_resolution(struct psmouse *psmouse,
|
||||
unsigned int resolution)
|
||||
{
|
||||
/* not supported yet */
|
||||
}
|
||||
|
@ -1916,7 +1916,7 @@ static int __init psmouse_init(void)
|
||||
synaptics_module_init();
|
||||
hgpk_module_init();
|
||||
|
||||
kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
|
||||
kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0);
|
||||
if (!kpsmoused_wq) {
|
||||
pr_err("failed to create kpsmoused workqueue\n");
|
||||
return -ENOMEM;
|
||||
|
@ -71,10 +71,7 @@ static void serport_serio_close(struct serio *serio)
|
||||
|
||||
spin_lock_irqsave(&serport->lock, flags);
|
||||
clear_bit(SERPORT_ACTIVE, &serport->flags);
|
||||
set_bit(SERPORT_DEAD, &serport->flags);
|
||||
spin_unlock_irqrestore(&serport->lock, flags);
|
||||
|
||||
wake_up_interruptible(&serport->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -248,6 +245,19 @@ static long serport_ldisc_compat_ioctl(struct tty_struct *tty,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int serport_ldisc_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct serport *serport = (struct serport *) tty->disc_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&serport->lock, flags);
|
||||
set_bit(SERPORT_DEAD, &serport->flags);
|
||||
spin_unlock_irqrestore(&serport->lock, flags);
|
||||
|
||||
wake_up_interruptible(&serport->wait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serport_ldisc_write_wakeup(struct tty_struct * tty)
|
||||
{
|
||||
struct serport *serport = (struct serport *) tty->disc_data;
|
||||
@ -274,6 +284,7 @@ static struct tty_ldisc_ops serport_ldisc = {
|
||||
.compat_ioctl = serport_ldisc_compat_ioctl,
|
||||
#endif
|
||||
.receive_buf = serport_ldisc_receive,
|
||||
.hangup = serport_ldisc_hangup,
|
||||
.write_wakeup = serport_ldisc_write_wakeup
|
||||
};
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
/* USB HID defines */
|
||||
#define USB_REQ_GET_REPORT 0x01
|
||||
|
@ -305,19 +305,6 @@ config TOUCHSCREEN_EGALAX_SERIAL
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called egalax_ts_serial.
|
||||
|
||||
config TOUCHSCREEN_FT6236
|
||||
tristate "FT6236 I2C touchscreen"
|
||||
depends on I2C
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here to enable support for the I2C connected FT6x06 and
|
||||
FT6x36 family of capacitive touchscreen drivers.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ft6236.
|
||||
|
||||
config TOUCHSCREEN_FUJITSU
|
||||
tristate "Fujitsu serial touchscreen"
|
||||
select SERIO
|
||||
@ -397,6 +384,18 @@ config TOUCHSCREEN_GUNZE
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gunze.
|
||||
|
||||
config TOUCHSCREEN_EKTF2127
|
||||
tristate "Elan eKTF2127 I2C touchscreen"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have an Elan eKTF2127 touchscreen
|
||||
connected to your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ektf2127.
|
||||
|
||||
config TOUCHSCREEN_ELAN
|
||||
tristate "Elan eKTH I2C touchscreen"
|
||||
depends on I2C
|
||||
|
@ -32,11 +32,11 @@ obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EKTF2127) += ektf2127.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
|
||||
|
@ -1063,9 +1063,15 @@ static const struct edt_i2c_chip_data edt_ft5506_data = {
|
||||
.max_support_points = 10,
|
||||
};
|
||||
|
||||
static const struct edt_i2c_chip_data edt_ft6236_data = {
|
||||
.max_support_points = 2,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id edt_ft5x06_ts_id[] = {
|
||||
{ .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
|
||||
{ .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
|
||||
/* Note no edt- prefix for compatibility with the ft6236.c driver */
|
||||
{ .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id);
|
||||
@ -1076,6 +1082,8 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
|
||||
{ .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
|
||||
{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
|
||||
{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
|
||||
/* Note focaltech vendor prefix for compatibility with ft6236.c */
|
||||
{ .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
|
||||
|
336
drivers/input/touchscreen/ektf2127.c
Normal file
336
drivers/input/touchscreen/ektf2127.c
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Driver for ELAN eKTF2127 i2c touchscreen controller
|
||||
*
|
||||
* For this driver the layout of the Chipone icn8318 i2c
|
||||
* touchscreencontroller is used.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Author:
|
||||
* Michel Verlaan <michel.verl@gmail.com>
|
||||
* Siebren Vroegindeweij <siebren.vroegindeweij@hotmail.com>
|
||||
*
|
||||
* Original chipone_icn8318 driver:
|
||||
* Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
/* Packet header defines (first byte of data send / received) */
|
||||
#define EKTF2127_NOISE 0x40
|
||||
#define EKTF2127_RESPONSE 0x52
|
||||
#define EKTF2127_REQUEST 0x53
|
||||
#define EKTF2127_HELLO 0x55
|
||||
#define EKTF2127_REPORT 0x5d
|
||||
#define EKTF2127_CALIB_DONE 0x66
|
||||
|
||||
/* Register defines (second byte of data send / received) */
|
||||
#define EKTF2127_ENV_NOISY 0x41
|
||||
#define EKTF2127_HEIGHT 0x60
|
||||
#define EKTF2127_WIDTH 0x63
|
||||
|
||||
/* 2 bytes header + 5 * 3 bytes coordinates + 3 bytes pressure info + footer */
|
||||
#define EKTF2127_TOUCH_REPORT_SIZE 21
|
||||
#define EKTF2127_MAX_TOUCHES 5
|
||||
|
||||
struct ektf2127_ts {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *power_gpios;
|
||||
struct touchscreen_properties prop;
|
||||
};
|
||||
|
||||
static void ektf2127_parse_coordinates(const u8* buf, unsigned int touch_count,
|
||||
struct input_mt_pos *touches)
|
||||
{
|
||||
int index = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < touch_count; i++) {
|
||||
index = 2 + i * 3;
|
||||
|
||||
touches[i].x = (buf[index] & 0x0f);
|
||||
touches[i].x <<= 8;
|
||||
touches[i].x |= buf[index + 2];
|
||||
|
||||
touches[i].y = (buf[index] & 0xf0);
|
||||
touches[i].y <<= 4;
|
||||
touches[i].y |= buf[index + 1];
|
||||
}
|
||||
}
|
||||
|
||||
static void ektf2127_report_event(struct ektf2127_ts *ts, const u8 *buf)
|
||||
{
|
||||
struct input_mt_pos touches[EKTF2127_MAX_TOUCHES];
|
||||
int slots[EKTF2127_MAX_TOUCHES];
|
||||
unsigned int touch_count, i;
|
||||
|
||||
touch_count = buf[1] & 0x07;
|
||||
if (touch_count > EKTF2127_MAX_TOUCHES) {
|
||||
dev_err(&ts->client->dev,
|
||||
"Too many touches %d > %d\n",
|
||||
touch_count, EKTF2127_MAX_TOUCHES);
|
||||
touch_count = EKTF2127_MAX_TOUCHES;
|
||||
}
|
||||
|
||||
ektf2127_parse_coordinates(buf, touch_count, touches);
|
||||
input_mt_assign_slots(ts->input, slots, touches,
|
||||
touch_count, 0);
|
||||
|
||||
for (i = 0; i < touch_count; i++) {
|
||||
input_mt_slot(ts->input, slots[i]);
|
||||
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
|
||||
touchscreen_report_pos(ts->input, &ts->prop,
|
||||
touches[i].x, touches[i].y, true);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(ts->input);
|
||||
input_sync(ts->input);
|
||||
}
|
||||
|
||||
static irqreturn_t ektf2127_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct ektf2127_ts *ts = dev_id;
|
||||
struct device *dev = &ts->client->dev;
|
||||
char buf[EKTF2127_TOUCH_REPORT_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_recv(ts->client, buf, EKTF2127_TOUCH_REPORT_SIZE);
|
||||
if (ret != EKTF2127_TOUCH_REPORT_SIZE) {
|
||||
dev_err(dev, "Error reading touch data: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (buf[0]) {
|
||||
case EKTF2127_REPORT:
|
||||
ektf2127_report_event(ts, buf);
|
||||
break;
|
||||
|
||||
case EKTF2127_NOISE:
|
||||
if (buf[1] == EKTF2127_ENV_NOISY)
|
||||
dev_dbg(dev, "Environment is electrically noisy\n");
|
||||
break;
|
||||
|
||||
case EKTF2127_HELLO:
|
||||
case EKTF2127_CALIB_DONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "Unexpected packet header byte %#02x\n", buf[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ektf2127_start(struct input_dev *dev)
|
||||
{
|
||||
struct ektf2127_ts *ts = input_get_drvdata(dev);
|
||||
|
||||
enable_irq(ts->client->irq);
|
||||
gpiod_set_value_cansleep(ts->power_gpios, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ektf2127_stop(struct input_dev *dev)
|
||||
{
|
||||
struct ektf2127_ts *ts = input_get_drvdata(dev);
|
||||
|
||||
disable_irq(ts->client->irq);
|
||||
gpiod_set_value_cansleep(ts->power_gpios, 0);
|
||||
}
|
||||
|
||||
static int __maybe_unused ektf2127_suspend(struct device *dev)
|
||||
{
|
||||
struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
mutex_lock(&ts->input->mutex);
|
||||
if (ts->input->users)
|
||||
ektf2127_stop(ts->input);
|
||||
mutex_unlock(&ts->input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ektf2127_resume(struct device *dev)
|
||||
{
|
||||
struct ektf2127_ts *ts = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
mutex_lock(&ts->input->mutex);
|
||||
if (ts->input->users)
|
||||
ektf2127_start(ts->input);
|
||||
mutex_unlock(&ts->input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ektf2127_pm_ops, ektf2127_suspend,
|
||||
ektf2127_resume);
|
||||
|
||||
static int ektf2127_query_dimension(struct i2c_client *client, bool width)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
const char *what = width ? "width" : "height";
|
||||
u8 what_code = width ? EKTF2127_WIDTH : EKTF2127_HEIGHT;
|
||||
u8 buf[4];
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
/* Request dimension */
|
||||
buf[0] = EKTF2127_REQUEST;
|
||||
buf[1] = width ? EKTF2127_WIDTH : EKTF2127_HEIGHT;
|
||||
buf[2] = 0x00;
|
||||
buf[3] = 0x00;
|
||||
ret = i2c_master_send(client, buf, sizeof(buf));
|
||||
if (ret != sizeof(buf)) {
|
||||
error = ret < 0 ? ret : -EIO;
|
||||
dev_err(dev, "Failed to request %s: %d\n", what, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
|
||||
/* Read response */
|
||||
ret = i2c_master_recv(client, buf, sizeof(buf));
|
||||
if (ret != sizeof(buf)) {
|
||||
error = ret < 0 ? ret : -EIO;
|
||||
dev_err(dev, "Failed to receive %s data: %d\n", what, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (buf[0] != EKTF2127_RESPONSE || buf[1] != what_code) {
|
||||
dev_err(dev, "Unexpected %s data: %#02x %#02x\n",
|
||||
what, buf[0], buf[1]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return (((buf[3] & 0xf0) << 4) | buf[2]) - 1;
|
||||
}
|
||||
|
||||
static int ektf2127_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct ektf2127_ts *ts;
|
||||
struct input_dev *input;
|
||||
u8 buf[4];
|
||||
int max_x, max_y;
|
||||
int error;
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(dev, "Error no irq specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
/* This requests the gpio *and* turns on the touchscreen controller */
|
||||
ts->power_gpios = devm_gpiod_get(dev, "power", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->power_gpios)) {
|
||||
error = PTR_ERR(ts->power_gpios);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "Error getting power gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->open = ektf2127_start;
|
||||
input->close = ektf2127_stop;
|
||||
|
||||
ts->client = client;
|
||||
|
||||
/* Read hello (ignore result, depends on initial power state) */
|
||||
msleep(20);
|
||||
i2c_master_recv(ts->client, buf, sizeof(buf));
|
||||
|
||||
/* Read resolution from chip */
|
||||
max_x = ektf2127_query_dimension(client, true);
|
||||
if (max_x < 0)
|
||||
return max_x;
|
||||
|
||||
max_y = ektf2127_query_dimension(client, false);
|
||||
if (max_y < 0)
|
||||
return max_y;
|
||||
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_x, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_y, 0, 0);
|
||||
touchscreen_parse_properties(input, true, &ts->prop);
|
||||
|
||||
error = input_mt_init_slots(input, EKTF2127_MAX_TOUCHES,
|
||||
INPUT_MT_DIRECT |
|
||||
INPUT_MT_DROP_UNUSED |
|
||||
INPUT_MT_TRACK);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ts->input = input;
|
||||
input_set_drvdata(input, ts);
|
||||
|
||||
error = devm_request_threaded_irq(dev, client->irq,
|
||||
NULL, ektf2127_irq,
|
||||
IRQF_ONESHOT, client->name, ts);
|
||||
if (error) {
|
||||
dev_err(dev, "Error requesting irq: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Stop device till opened */
|
||||
ektf2127_stop(ts->input);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ektf2127_of_match[] = {
|
||||
{ .compatible = "elan,ektf2127" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ektf2127_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id ektf2127_i2c_id[] = {
|
||||
{ "ektf2127", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ektf2127_i2c_id);
|
||||
|
||||
static struct i2c_driver ektf2127_driver = {
|
||||
.driver = {
|
||||
.name = "elan_ektf2127",
|
||||
.pm = &ektf2127_pm_ops,
|
||||
.of_match_table = of_match_ptr(ektf2127_of_match),
|
||||
},
|
||||
.probe = ektf2127_probe,
|
||||
.id_table = ektf2127_i2c_id,
|
||||
};
|
||||
module_i2c_driver(ektf2127_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ELAN eKTF2127 I2C Touchscreen Driver");
|
||||
MODULE_AUTHOR("Michel Verlaan, Siebren Vroegindeweij");
|
||||
MODULE_LICENSE("GPL");
|
@ -298,7 +298,7 @@ static u16 elants_i2c_parse_version(u8 *buf)
|
||||
return get_unaligned_be32(buf) >> 4;
|
||||
}
|
||||
|
||||
static int elants_i2c_query_fw_id(struct elants_data *ts)
|
||||
static int elants_i2c_query_hw_version(struct elants_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int error, retry_cnt;
|
||||
@ -318,8 +318,13 @@ static int elants_i2c_query_fw_id(struct elants_data *ts)
|
||||
error, (int)sizeof(resp), resp);
|
||||
}
|
||||
|
||||
dev_err(&client->dev,
|
||||
"Failed to read fw id or fw id is invalid\n");
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to read fw id: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
dev_err(&client->dev, "Invalid fw id: %#04x\n", ts->hw_version);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -508,7 +513,7 @@ static int elants_i2c_fastboot(struct i2c_client *client)
|
||||
static int elants_i2c_initialize(struct elants_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int error, retry_cnt;
|
||||
int error, error2, retry_cnt;
|
||||
const u8 hello_packet[] = { 0x55, 0x55, 0x55, 0x55 };
|
||||
const u8 recov_packet[] = { 0x55, 0x55, 0x80, 0x80 };
|
||||
u8 buf[HEADER_SIZE];
|
||||
@ -553,18 +558,22 @@ static int elants_i2c_initialize(struct elants_data *ts)
|
||||
}
|
||||
}
|
||||
|
||||
/* hw version is available even if device in recovery state */
|
||||
error2 = elants_i2c_query_hw_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_fw_id(ts);
|
||||
error = error2;
|
||||
|
||||
if (!error)
|
||||
error = elants_i2c_query_fw_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_test_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_bc_version(ts);
|
||||
if (!error)
|
||||
error = elants_i2c_query_ts_info(ts);
|
||||
|
||||
if (error) {
|
||||
if (error)
|
||||
ts->iap_mode = ELAN_IAP_RECOVERY;
|
||||
} else {
|
||||
elants_i2c_query_test_version(ts);
|
||||
elants_i2c_query_bc_version(ts);
|
||||
elants_i2c_query_ts_info(ts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,326 +0,0 @@
|
||||
/*
|
||||
* FocalTech FT6236 TouchScreen driver.
|
||||
*
|
||||
* Copyright (c) 2010 Focal tech Ltd.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define FT6236_MAX_TOUCH_POINTS 2
|
||||
|
||||
#define FT6236_REG_TH_GROUP 0x80
|
||||
#define FT6236_REG_PERIODACTIVE 0x88
|
||||
#define FT6236_REG_LIB_VER_H 0xa1
|
||||
#define FT6236_REG_LIB_VER_L 0xa2
|
||||
#define FT6236_REG_CIPHER 0xa3
|
||||
#define FT6236_REG_FIRMID 0xa6
|
||||
#define FT6236_REG_FOCALTECH_ID 0xa8
|
||||
#define FT6236_REG_RELEASE_CODE_ID 0xaf
|
||||
|
||||
#define FT6236_EVENT_PRESS_DOWN 0
|
||||
#define FT6236_EVENT_LIFT_UP 1
|
||||
#define FT6236_EVENT_CONTACT 2
|
||||
#define FT6236_EVENT_NO_EVENT 3
|
||||
|
||||
struct ft6236_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *reset_gpio;
|
||||
u32 max_x;
|
||||
u32 max_y;
|
||||
bool invert_x;
|
||||
bool invert_y;
|
||||
bool swap_xy;
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct is a touchpoint as stored in hardware. Note that the id,
|
||||
* as well as the event, are stored in the upper nybble of the hi byte.
|
||||
*/
|
||||
struct ft6236_touchpoint {
|
||||
union {
|
||||
u8 xhi;
|
||||
u8 event;
|
||||
};
|
||||
u8 xlo;
|
||||
union {
|
||||
u8 yhi;
|
||||
u8 id;
|
||||
};
|
||||
u8 ylo;
|
||||
u8 weight;
|
||||
u8 misc;
|
||||
} __packed;
|
||||
|
||||
/* This packet represents the register map as read from offset 0 */
|
||||
struct ft6236_packet {
|
||||
u8 dev_mode;
|
||||
u8 gest_id;
|
||||
u8 touches;
|
||||
struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS];
|
||||
} __packed;
|
||||
|
||||
static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = i2c_smbus_read_i2c_block_data(client, reg, len, data);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (error != len)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct ft6236_data *ft6236 = dev_id;
|
||||
struct device *dev = &ft6236->client->dev;
|
||||
struct input_dev *input = ft6236->input;
|
||||
struct ft6236_packet buf;
|
||||
u8 touches;
|
||||
int i, error;
|
||||
|
||||
error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf);
|
||||
if (error) {
|
||||
dev_err(dev, "read touchdata failed %d\n", error);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
touches = buf.touches & 0xf;
|
||||
if (touches > FT6236_MAX_TOUCH_POINTS) {
|
||||
dev_dbg(dev,
|
||||
"%d touch points reported, only %d are supported\n",
|
||||
touches, FT6236_MAX_TOUCH_POINTS);
|
||||
touches = FT6236_MAX_TOUCH_POINTS;
|
||||
}
|
||||
|
||||
for (i = 0; i < touches; i++) {
|
||||
struct ft6236_touchpoint *point = &buf.points[i];
|
||||
u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;
|
||||
u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;
|
||||
u8 event = point->event >> 6;
|
||||
u8 id = point->id >> 4;
|
||||
bool act = (event == FT6236_EVENT_PRESS_DOWN ||
|
||||
event == FT6236_EVENT_CONTACT);
|
||||
|
||||
input_mt_slot(input, id);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, act);
|
||||
if (!act)
|
||||
continue;
|
||||
|
||||
if (ft6236->invert_x)
|
||||
x = ft6236->max_x - x;
|
||||
|
||||
if (ft6236->invert_y)
|
||||
y = ft6236->max_y - y;
|
||||
|
||||
if (ft6236->swap_xy) {
|
||||
input_report_abs(input, ABS_MT_POSITION_X, y);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, x);
|
||||
} else {
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
}
|
||||
|
||||
input_mt_sync_frame(input);
|
||||
input_sync(input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg)
|
||||
{
|
||||
struct i2c_client *client = ft6236->client;
|
||||
u8 val = 0;
|
||||
int error;
|
||||
|
||||
error = ft6236_read(client, reg, 1, &val);
|
||||
if (error)
|
||||
dev_dbg(&client->dev,
|
||||
"error reading register 0x%02x: %d\n", reg, error);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void ft6236_debug_info(struct ft6236_data *ft6236)
|
||||
{
|
||||
struct device *dev = &ft6236->client->dev;
|
||||
|
||||
dev_dbg(dev, "Touch threshold is %d\n",
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4);
|
||||
dev_dbg(dev, "Report rate is %dHz\n",
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10);
|
||||
dev_dbg(dev, "Firmware library version 0x%02x%02x\n",
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H),
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L));
|
||||
dev_dbg(dev, "Firmware version 0x%02x\n",
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID));
|
||||
dev_dbg(dev, "Chip vendor ID 0x%02x\n",
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER));
|
||||
dev_dbg(dev, "CTPM vendor ID 0x%02x\n",
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID));
|
||||
dev_dbg(dev, "Release code version 0x%02x\n",
|
||||
ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID));
|
||||
}
|
||||
|
||||
static void ft6236_reset(struct ft6236_data *ft6236)
|
||||
{
|
||||
if (!ft6236->reset_gpio)
|
||||
return;
|
||||
|
||||
gpiod_set_value_cansleep(ft6236->reset_gpio, 1);
|
||||
usleep_range(5000, 20000);
|
||||
gpiod_set_value_cansleep(ft6236->reset_gpio, 0);
|
||||
msleep(300);
|
||||
}
|
||||
|
||||
static int ft6236_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct ft6236_data *ft6236;
|
||||
struct input_dev *input;
|
||||
u32 fuzz_x = 0, fuzz_y = 0;
|
||||
u8 val;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENXIO;
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(dev, "irq is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL);
|
||||
if (!ft6236)
|
||||
return -ENOMEM;
|
||||
|
||||
ft6236->client = client;
|
||||
ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ft6236->reset_gpio)) {
|
||||
error = PTR_ERR(ft6236->reset_gpio);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "error getting reset gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ft6236_reset(ft6236);
|
||||
|
||||
/* verify that the controller is present */
|
||||
error = ft6236_read(client, 0x00, 1, &val);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to read from controller: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ft6236_debug_info(ft6236);
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
ft6236->input = input;
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
|
||||
if (device_property_read_u32(dev, "touchscreen-size-x",
|
||||
&ft6236->max_x) ||
|
||||
device_property_read_u32(dev, "touchscreen-size-y",
|
||||
&ft6236->max_y)) {
|
||||
dev_err(dev, "touchscreen-size-x and/or -y missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x);
|
||||
device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y);
|
||||
ft6236->invert_x = device_property_read_bool(dev,
|
||||
"touchscreen-inverted-x");
|
||||
ft6236->invert_y = device_property_read_bool(dev,
|
||||
"touchscreen-inverted-y");
|
||||
ft6236->swap_xy = device_property_read_bool(dev,
|
||||
"touchscreen-swapped-x-y");
|
||||
|
||||
if (ft6236->swap_xy) {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
ft6236->max_y, fuzz_y, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
ft6236->max_x, fuzz_x, 0);
|
||||
} else {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
ft6236->max_x, fuzz_x, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
|
||||
ft6236->max_y, fuzz_y, 0);
|
||||
}
|
||||
|
||||
error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
ft6236_interrupt, IRQF_ONESHOT,
|
||||
client->name, ft6236);
|
||||
if (error) {
|
||||
dev_err(dev, "request irq %d failed: %d\n", client->irq, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to register input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ft6236_of_match[] = {
|
||||
{ .compatible = "focaltech,ft6236", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ft6236_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id ft6236_id[] = {
|
||||
{ "ft6236", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ft6236_id);
|
||||
|
||||
static struct i2c_driver ft6236_driver = {
|
||||
.driver = {
|
||||
.name = "ft6236",
|
||||
.of_match_table = of_match_ptr(ft6236_of_match),
|
||||
},
|
||||
.probe = ft6236_probe,
|
||||
.id_table = ft6236_id,
|
||||
};
|
||||
module_i2c_driver(ft6236_driver);
|
||||
|
||||
MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
|
||||
MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>");
|
||||
MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -13,6 +13,7 @@
|
||||
* HP Jornada 710/720/729 Touchscreen Driver
|
||||
*/
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
@ -20,9 +21,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/jornada720.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
|
||||
MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver");
|
||||
@ -30,6 +29,7 @@ MODULE_LICENSE("GPL v2");
|
||||
|
||||
struct jornada_ts {
|
||||
struct input_dev *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int x_data[4]; /* X sample values */
|
||||
int y_data[4]; /* Y sample values */
|
||||
};
|
||||
@ -71,8 +71,8 @@ static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id)
|
||||
struct input_dev *input = jornada_ts->dev;
|
||||
int x, y;
|
||||
|
||||
/* If GPIO_GPIO9 is set to high then report pen up */
|
||||
if (GPLR & GPIO_GPIO(9)) {
|
||||
/* If gpio is high then report pen up */
|
||||
if (gpiod_get_value(jornada_ts->gpio)) {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_sync(input);
|
||||
} else {
|
||||
@ -101,7 +101,7 @@ static int jornada720_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct jornada_ts *jornada_ts;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
int error, irq;
|
||||
|
||||
jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
|
||||
if (!jornada_ts)
|
||||
@ -113,6 +113,14 @@ static int jornada720_ts_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, jornada_ts);
|
||||
|
||||
jornada_ts->gpio = devm_gpiod_get(&pdev->dev, "penup", GPIOD_IN);
|
||||
if (IS_ERR(jornada_ts->gpio))
|
||||
return PTR_ERR(jornada_ts->gpio);
|
||||
|
||||
irq = gpiod_to_irq(jornada_ts->gpio);
|
||||
if (irq <= 0)
|
||||
return irq < 0 ? irq : -EINVAL;
|
||||
|
||||
jornada_ts->dev = input_dev;
|
||||
|
||||
input_dev->name = "HP Jornada 7xx Touchscreen";
|
||||
@ -125,8 +133,7 @@ static int jornada720_ts_probe(struct platform_device *pdev)
|
||||
input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
|
||||
|
||||
error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
|
||||
jornada720_ts_interrupt,
|
||||
error = devm_request_irq(&pdev->dev, irq, jornada720_ts_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"HP7XX Touchscreen driver", pdev);
|
||||
if (error) {
|
||||
|
@ -37,7 +37,6 @@ struct mc13783_ts_priv {
|
||||
struct input_dev *idev;
|
||||
struct mc13xxx *mc13xxx;
|
||||
struct delayed_work work;
|
||||
struct workqueue_struct *workq;
|
||||
unsigned int sample[4];
|
||||
struct mc13xxx_ts_platform_data *touch;
|
||||
};
|
||||
@ -54,7 +53,7 @@ static irqreturn_t mc13783_ts_handler(int irq, void *data)
|
||||
* be rescheduled for immediate execution here. However the rearm
|
||||
* delay is HZ / 50 which is acceptable.
|
||||
*/
|
||||
queue_delayed_work(priv->workq, &priv->work, 0);
|
||||
schedule_delayed_work(&priv->work, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -106,16 +105,18 @@ static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)
|
||||
|
||||
dev_dbg(&idev->dev, "report (%d, %d, %d)\n",
|
||||
x1, y1, 0x1000 - cr0);
|
||||
queue_delayed_work(priv->workq, &priv->work, HZ / 50);
|
||||
} else
|
||||
schedule_delayed_work(&priv->work, HZ / 50);
|
||||
} else {
|
||||
dev_dbg(&idev->dev, "report release\n");
|
||||
}
|
||||
|
||||
input_report_abs(idev, ABS_PRESSURE,
|
||||
cr0 ? 0x1000 - cr0 : cr0);
|
||||
input_report_key(idev, BTN_TOUCH, cr0);
|
||||
input_sync(idev);
|
||||
} else
|
||||
} else {
|
||||
dev_dbg(&idev->dev, "discard event\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void mc13783_ts_work(struct work_struct *work)
|
||||
@ -189,14 +190,6 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need separate workqueue because mc13783_adc_do_conversion
|
||||
* uses keventd and thus would deadlock.
|
||||
*/
|
||||
priv->workq = create_singlethread_workqueue("mc13783_ts");
|
||||
if (!priv->workq)
|
||||
goto err_free_mem;
|
||||
|
||||
idev->name = MC13783_TS_NAME;
|
||||
idev->dev.parent = &pdev->dev;
|
||||
|
||||
@ -215,14 +208,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"register input device failed with %d\n", ret);
|
||||
goto err_destroy_wq;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
|
||||
err_destroy_wq:
|
||||
destroy_workqueue(priv->workq);
|
||||
err_free_mem:
|
||||
input_free_device(idev);
|
||||
kfree(priv);
|
||||
@ -233,7 +224,6 @@ static int mc13783_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
destroy_workqueue(priv->workq);
|
||||
input_unregister_device(priv->idev);
|
||||
kfree(priv);
|
||||
|
||||
|
@ -11,10 +11,6 @@
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
@ -404,7 +400,6 @@ static int __maybe_unused pixcir_i2c_ts_resume(struct device *dev)
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (device_may_wakeup(&client->dev)) {
|
||||
|
||||
if (!input->users) {
|
||||
ret = pixcir_stop(ts);
|
||||
if (ret) {
|
||||
@ -431,13 +426,7 @@ static const struct of_device_id pixcir_of_match[];
|
||||
static int pixcir_parse_dt(struct device *dev,
|
||||
struct pixcir_i2c_ts_data *tsdata)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(pixcir_of_match), dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
tsdata->chip = (const struct pixcir_i2c_chip_data *)match->data;
|
||||
tsdata->chip = of_device_get_match_data(dev);
|
||||
if (!tsdata->chip)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define WDT87XX_NAME "wdt87xx_i2c"
|
||||
#define WDT87XX_DRV_VER "0.9.7"
|
||||
#define WDT87XX_DRV_VER "0.9.8"
|
||||
#define WDT87XX_FW_NAME "wdt87xx_fw.bin"
|
||||
#define WDT87XX_CFG_NAME "wdt87xx_cfg.bin"
|
||||
|
||||
@ -157,6 +157,7 @@
|
||||
/* Controller requires minimum 300us between commands */
|
||||
#define WDT_COMMAND_DELAY_MS 2
|
||||
#define WDT_FLASH_WRITE_DELAY_MS 4
|
||||
#define WDT_FLASH_ERASE_DELAY_MS 200
|
||||
#define WDT_FW_RESET_TIME 2500
|
||||
|
||||
struct wdt87xx_sys_param {
|
||||
@ -726,7 +727,7 @@ static int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk)
|
||||
break;
|
||||
}
|
||||
|
||||
msleep(50);
|
||||
msleep(WDT_FLASH_ERASE_DELAY_MS);
|
||||
|
||||
error = wdt87xx_write_data(client, data, start_addr,
|
||||
page_size);
|
||||
|
@ -500,7 +500,7 @@ static int wm97xx_ts_input_open(struct input_dev *idev)
|
||||
{
|
||||
struct wm97xx *wm = input_get_drvdata(idev);
|
||||
|
||||
wm->ts_workq = create_singlethread_workqueue("kwm97xx");
|
||||
wm->ts_workq = alloc_ordered_workqueue("kwm97xx", 0);
|
||||
if (wm->ts_workq == NULL) {
|
||||
dev_err(wm->dev,
|
||||
"Failed to create workqueue\n");
|
||||
|
Loading…
Reference in New Issue
Block a user