diff --git a/Documentation/devicetree/bindings/input/touchscreen/exc3000.txt b/Documentation/devicetree/bindings/input/touchscreen/exc3000.txt new file mode 100644 index 00000000000000..1dcff4a43eaa8d --- /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; + }; diff --git a/arch/arm/boot/dts/overlays/Makefile b/arch/arm/boot/dts/overlays/Makefile index 8090fee1d5a49c..bc997af7066578 100644 --- a/arch/arm/boot/dts/overlays/Makefile +++ b/arch/arm/boot/dts/overlays/Makefile @@ -26,6 +26,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \ dwc2.dtbo \ enc28j60.dtbo \ enc28j60-spi2.dtbo \ + exc3000.dtbo \ fe-pi-audio.dtbo \ goodix.dtbo \ googlevoicehat-soundcard.dtbo \ diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README index 1cf89d6423ac01..afc7eea8ced47c 100644 --- a/arch/arm/boot/dts/overlays/README +++ b/arch/arm/boot/dts/overlays/README @@ -468,6 +468,18 @@ Params: int_pin GPIO used for INT (default 39) speed SPI bus speed (default 12000000) +Name: exc3000 +Info: Enables I2C connected EETI EXC3000 multiple touch controller using + GPIO 4 (pin 7 on GPIO header) for interrupt. +Load: dtoverlay=exc3000,= +Params: interrupt GPIO used for interrupt (default 4) + sizex Touchscreen size x (default 4096) + sizey Touchscreen size y (default 4096) + invx Touchscreen inverted x axis + invy Touchscreen inverted y axis + swapxy Touchscreen swapped x y axis + + Name: fe-pi-audio Info: Configures the Fe-Pi Audio Sound Card Load: dtoverlay=fe-pi-audio diff --git a/arch/arm/boot/dts/overlays/exc3000-overlay.dts b/arch/arm/boot/dts/overlays/exc3000-overlay.dts new file mode 100644 index 00000000000000..c5694033d03f31 --- /dev/null +++ b/arch/arm/boot/dts/overlays/exc3000-overlay.dts @@ -0,0 +1,48 @@ +// Device tree overlay for I2C connected EETI EXC3000 multiple touch controller +/dts-v1/; +/plugin/; + +/ { + compatible = "brcm,bcm2708"; + + fragment@0 { + target = <&gpio>; + __overlay__ { + exc3000_pins: exc3000_pins { + brcm,pins = <4>; // interrupt + brcm,function = <0>; // in + brcm,pull = <2>; // pull-up + }; + }; + }; + + fragment@1 { + target = <&i2c1>; + __overlay__ { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + exc3000: exc3000@2a { + compatible = "eeti,exc3000"; + reg = <0x2a>; + pinctrl-names = "default"; + pinctrl-0 = <&exc3000_pins>; + interrupt-parent = <&gpio>; + interrupts = <4 8>; // active low level-sensitive + touchscreen-size-x = <4096>; + touchscreen-size-y = <4096>; + }; + }; + }; + + __overrides__ { + interrupt = <&exc3000_pins>,"brcm,pins:0", + <&exc3000>,"interrupts:0"; + sizex = <&exc3000>,"touchscreen-size-x:0"; + sizey = <&exc3000>,"touchscreen-size-y:0"; + invx = <&exc3000>,"touchscreen-inverted-x?"; + invy = <&exc3000>,"touchscreen-inverted-y?"; + swapxy = <&exc3000>,"touchscreen-swapped-x-y?"; + }; +}; diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig index 8800a1081bb726..aab56f885f3e2c 100644 --- a/arch/arm/configs/bcm2709_defconfig +++ b/arch/arm/configs/bcm2709_defconfig @@ -573,6 +573,7 @@ CONFIG_JOYSTICK_RPISENSE=m CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ADS7846=m CONFIG_TOUCHSCREEN_EGALAX=m +CONFIG_TOUCHSCREEN_EXC3000=m CONFIG_TOUCHSCREEN_GOODIX=m CONFIG_TOUCHSCREEN_EDT_FT5X06=m CONFIG_TOUCHSCREEN_RPI_FT5406=m diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig index 28266427f843a3..ce5f5b66cb38c7 100644 --- a/arch/arm/configs/bcmrpi_defconfig +++ b/arch/arm/configs/bcmrpi_defconfig @@ -569,6 +569,7 @@ CONFIG_JOYSTICK_RPISENSE=m CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ADS7846=m CONFIG_TOUCHSCREEN_EGALAX=m +CONFIG_TOUCHSCREEN_EXC3000=m CONFIG_TOUCHSCREEN_GOODIX=m CONFIG_TOUCHSCREEN_EDT_FT5X06=m CONFIG_TOUCHSCREEN_RPI_FT5406=m diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 0bf78ff1ae888a..9ea2d9287b38cf 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -314,6 +314,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 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f720bc30ada9c2..48118379fee7cc 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -38,6 +38,7 @@ 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_EXC3000) += exc3000.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c new file mode 100644 index 00000000000000..bed0db6999050c --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver"); +MODULE_LICENSE("GPL v2");