aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.14/950-0162-Input-add-I2C-attached-EETI-EXC3000-multi-touch-driv.patch
diff options
context:
space:
mode:
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.patch320
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");