diff options
Diffstat (limited to 'target/linux/brcm2708/patches-4.14/950-0162-Input-add-I2C-attached-EETI-EXC3000-multi-touch-driv.patch')
-rw-r--r-- | target/linux/brcm2708/patches-4.14/950-0162-Input-add-I2C-attached-EETI-EXC3000-multi-touch-driv.patch | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.14/950-0162-Input-add-I2C-attached-EETI-EXC3000-multi-touch-driv.patch b/target/linux/brcm2708/patches-4.14/950-0162-Input-add-I2C-attached-EETI-EXC3000-multi-touch-driv.patch new file mode 100644 index 0000000000..033039e5cb --- /dev/null +++ b/target/linux/brcm2708/patches-4.14/950-0162-Input-add-I2C-attached-EETI-EXC3000-multi-touch-driv.patch @@ -0,0 +1,320 @@ +From 99aaee7d1fde86bd2119d84e90435d89d6d3af07 Mon Sep 17 00:00:00 2001 +From: Ahmet Inan <inan@distec.de> +Date: Sat, 14 Oct 2017 10:10:53 -0700 +Subject: [PATCH 162/454] Input: add I2C attached EETI EXC3000 multi touch + driver + +commit 7e577a17f2eefeef32f1106ebf91e7cd143ba654 upstream. +beware: code adapted to use the old timer API. + +The 3000 series have a new protocol which allows to report up to 5 points +in a single 66 byte frame. One must always read in 66 byte frames. +To support up to 10 points, two consecutive frames need to be read: +The first frame says how many points until sync. +The second frame must say zero points or both frames must be discarded. + +To be able to work with the higher 400KHz I2C bus rate, one must +successfully send a special package prior _each_ read or the controller +will refuse to cooperate. + +This is a minimal implementation based on egalax_i2c.c (which can be found +on the internet) and egalax_ts.c but without the vendor interface and no +power management support. + +Signed-off-by: Ahmet Inan <inan@distec.de> +Acked-by: Rob Herring <robh@kernel.org> +Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> +--- + .../bindings/input/touchscreen/exc3000.txt | 27 +++ + drivers/input/touchscreen/Kconfig | 10 + + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/exc3000.c | 223 ++++++++++++++++++ + 4 files changed, 261 insertions(+) + create mode 100644 Documentation/devicetree/bindings/input/touchscreen/exc3000.txt + create mode 100644 drivers/input/touchscreen/exc3000.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/input/touchscreen/exc3000.txt +@@ -0,0 +1,27 @@ ++* EETI EXC3000 Multiple Touch Controller ++ ++Required properties: ++- compatible: must be "eeti,exc3000" ++- reg: i2c slave address ++- interrupt-parent: the phandle for the interrupt controller ++- interrupts: touch controller interrupt ++- touchscreen-size-x: See touchscreen.txt ++- touchscreen-size-y: See touchscreen.txt ++ ++Optional properties: ++- touchscreen-inverted-x: See touchscreen.txt ++- touchscreen-inverted-y: See touchscreen.txt ++- touchscreen-swapped-x-y: See touchscreen.txt ++ ++Example: ++ ++ touchscreen@2a { ++ compatible = "eeti,exc3000"; ++ reg = <0x2a>; ++ interrupt-parent = <&gpio1>; ++ interrupts = <9 IRQ_TYPE_LEVEL_LOW>; ++ touchscreen-size-x = <4096>; ++ touchscreen-size-y = <4096>; ++ touchscreen-inverted-x; ++ touchscreen-swapped-x-y; ++ }; +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -316,6 +316,16 @@ config TOUCHSCREEN_EGALAX_SERIAL + To compile this driver as a module, choose M here: the + module will be called egalax_ts_serial. + ++config TOUCHSCREEN_EXC3000 ++ tristate "EETI EXC3000 multi-touch panel support" ++ depends on I2C ++ help ++ Say Y here to enable support for I2C connected EETI ++ EXC3000 multi-touch panels. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called exc3000. ++ + config TOUCHSCREEN_FUJITSU + tristate "Fujitsu serial touchscreen" + select SERIO +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_ELAN) += elant + 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_EXC3000) += exc3000.o + obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o + obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o + obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o +--- /dev/null ++++ b/drivers/input/touchscreen/exc3000.c +@@ -0,0 +1,223 @@ ++/* ++ * Driver for I2C connected EETI EXC3000 multiple touch controller ++ * ++ * Copyright (C) 2017 Ahmet Inan <inan@distec.de> ++ * ++ * minimal implementation based on egalax_ts.c and egalax_i2c.c ++ * ++ * 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/bitops.h> ++#include <linux/device.h> ++#include <linux/i2c.h> ++#include <linux/input.h> ++#include <linux/input/mt.h> ++#include <linux/input/touchscreen.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/timer.h> ++#include <asm/unaligned.h> ++ ++#define EXC3000_NUM_SLOTS 10 ++#define EXC3000_SLOTS_PER_FRAME 5 ++#define EXC3000_LEN_FRAME 66 ++#define EXC3000_LEN_POINT 10 ++#define EXC3000_MT_EVENT 6 ++#define EXC3000_TIMEOUT_MS 100 ++ ++struct exc3000_data { ++ struct i2c_client *client; ++ struct input_dev *input; ++ struct touchscreen_properties prop; ++ struct timer_list timer; ++ u8 buf[2 * EXC3000_LEN_FRAME]; ++}; ++ ++static void exc3000_report_slots(struct input_dev *input, ++ struct touchscreen_properties *prop, ++ const u8 *buf, int num) ++{ ++ for (; num--; buf += EXC3000_LEN_POINT) { ++ if (buf[0] & BIT(0)) { ++ input_mt_slot(input, buf[1]); ++ input_mt_report_slot_state(input, MT_TOOL_FINGER, true); ++ touchscreen_report_pos(input, prop, ++ get_unaligned_le16(buf + 2), ++ get_unaligned_le16(buf + 4), ++ true); ++ } ++ } ++} ++ ++static void exc3000_timer(unsigned long d) ++{ ++ struct exc3000_data *data = (void *)d; ++ ++ input_mt_sync_frame(data->input); ++ input_sync(data->input); ++} ++ ++static int exc3000_read_frame(struct i2c_client *client, u8 *buf) ++{ ++ int ret; ++ ++ ret = i2c_master_send(client, "'", 2); ++ if (ret < 0) ++ return ret; ++ ++ if (ret != 2) ++ return -EIO; ++ ++ ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME); ++ if (ret < 0) ++ return ret; ++ ++ if (ret != EXC3000_LEN_FRAME) ++ return -EIO; ++ ++ if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME || ++ buf[2] != EXC3000_MT_EVENT) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int exc3000_read_data(struct i2c_client *client, ++ u8 *buf, int *n_slots) ++{ ++ int error; ++ ++ error = exc3000_read_frame(client, buf); ++ if (error) ++ return error; ++ ++ *n_slots = buf[3]; ++ if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS) ++ return -EINVAL; ++ ++ if (*n_slots > EXC3000_SLOTS_PER_FRAME) { ++ /* Read 2nd frame to get the rest of the contacts. */ ++ error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME); ++ if (error) ++ return error; ++ ++ /* 2nd chunk must have number of contacts set to 0. */ ++ if (buf[EXC3000_LEN_FRAME + 3] != 0) ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static irqreturn_t exc3000_interrupt(int irq, void *dev_id) ++{ ++ struct exc3000_data *data = dev_id; ++ struct input_dev *input = data->input; ++ u8 *buf = data->buf; ++ int slots, total_slots; ++ int error; ++ ++ error = exc3000_read_data(data->client, buf, &total_slots); ++ if (error) { ++ /* Schedule a timer to release "stuck" contacts */ ++ mod_timer(&data->timer, ++ jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS)); ++ goto out; ++ } ++ ++ /* ++ * We read full state successfully, no contacts will be "stuck". ++ */ ++ del_timer_sync(&data->timer); ++ ++ while (total_slots > 0) { ++ slots = min(total_slots, EXC3000_SLOTS_PER_FRAME); ++ exc3000_report_slots(input, &data->prop, buf + 4, slots); ++ total_slots -= slots; ++ buf += EXC3000_LEN_FRAME; ++ } ++ ++ input_mt_sync_frame(input); ++ input_sync(input); ++ ++out: ++ return IRQ_HANDLED; ++} ++ ++static int exc3000_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct exc3000_data *data; ++ struct input_dev *input; ++ int error; ++ ++ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->client = client; ++ setup_timer(&data->timer, exc3000_timer, (unsigned long)data); ++ ++ input = devm_input_allocate_device(&client->dev); ++ if (!input) ++ return -ENOMEM; ++ ++ data->input = input; ++ ++ input->name = "EETI EXC3000 Touch Screen"; ++ input->id.bustype = BUS_I2C; ++ ++ input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0); ++ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); ++ touchscreen_parse_properties(input, true, &data->prop); ++ ++ error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, ++ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); ++ if (error) ++ return error; ++ ++ error = input_register_device(input); ++ if (error) ++ return error; ++ ++ error = devm_request_threaded_irq(&client->dev, client->irq, ++ NULL, exc3000_interrupt, IRQF_ONESHOT, ++ client->name, data); ++ if (error) ++ return error; ++ ++ return 0; ++} ++ ++static const struct i2c_device_id exc3000_id[] = { ++ { "exc3000", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, exc3000_id); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id exc3000_of_match[] = { ++ { .compatible = "eeti,exc3000" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, exc3000_of_match); ++#endif ++ ++static struct i2c_driver exc3000_driver = { ++ .driver = { ++ .name = "exc3000", ++ .of_match_table = of_match_ptr(exc3000_of_match), ++ }, ++ .id_table = exc3000_id, ++ .probe = exc3000_probe, ++}; ++ ++module_i2c_driver(exc3000_driver); ++ ++MODULE_AUTHOR("Ahmet Inan <inan@distec.de>"); ++MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver"); ++MODULE_LICENSE("GPL v2"); |