2015-12-07 07:26:19

by Priit Laes

[permalink] [raw]
Subject: [PATCH 0/2] input: Driver for Goodix GT801 2+1 touchscreen

This series adds support for Goodix GT801 2+1 touchscreen controller
and hooks it up on Gemei G9 tablet.

Now about GT801 2+1 - I initially tried to implement this inside the
existing Goodix driver, but unfortunately there are too many small
bits and pieces that would make the otherwise simple driver a
complicated mess:
- endianness differences of coordinate readouts
- totally different touch protocol
- configuration layout and version information
- I2C register differences (2 bytes vs single byte)

To apply the patchset, you need both linux-input/next and
linux-sunxi/next merged.

Patch 1 (Gemei G9 devicetree bits) should go via linux-sunxi tree.
Patch 2 (GT801 2+1 driver implementation) via linux-input

Priit Laes (2):
ARM: dts: sun4i: gemei-g9: Add touchscreen (Goodix gt801x2) support
input: gt801_2plus1 - Add initial support for Goodix GT801 2+1

.../bindings/input/touchscreen/gt801_2plus1.txt | 23 ++
MAINTAINERS | 6 +
arch/arm/boot/dts/sun4i-a10-gemei-g9.dts | 26 +-
drivers/input/touchscreen/Kconfig | 19 +-
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/gt801_2plus1.c | 375 +++++++++++++++++++++
6 files changed, 445 insertions(+), 5 deletions(-)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt
create mode 100644 drivers/input/touchscreen/gt801_2plus1.c

--
2.6.3


2015-12-07 07:26:48

by Priit Laes

[permalink] [raw]
Subject: [PATCH 1/2] ARM: dts: sun4i: gemei-g9: Add touchscreen (Goodix gt801x2) support

Goodix GT801 2+1 is a touchscreen controller supporting up to
10 touches.

Enable pin and wakeup pin support is currently not implemented:
- enable pin (PI16, specified in FEX) seems to be wrong
- wakeup pin needs some additional reverse engineering work

Signed-off-by: Priit Laes <[email protected]>
---
arch/arm/boot/dts/sun4i-a10-gemei-g9.dts | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts b/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts
index 1d73a98..52e6275 100644
--- a/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts
+++ b/arch/arm/boot/dts/sun4i-a10-gemei-g9.dts
@@ -48,6 +48,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/pinctrl/sun4i-a10.h>

/ {
model = "Gemei G9 Tablet";
@@ -65,11 +66,11 @@
/*
* TODO:
* 2x cameras via CSI
- * audio input
+ * audio input (+ ext. amplifier enable GPIO)
* AXP battery management
* NAND
* OTG
- * Touchscreen - gt801_2plus1 @ i2c adapter 2 @ 0x48
+ * Touchscreen enable and wakeup pins
*/
&codec {
status = "okay";
@@ -114,6 +115,27 @@
};
};

+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins_a>;
+ status = "okay";
+
+ touchscreen@55 {
+ compatible = "goodix,gt801_2plus1";
+ reg = <0x55>;
+
+ interrupt-parent = <&pio>;
+ interrupts = <7 21 IRQ_TYPE_EDGE_FALLING>; /* EINT21 (PH21) */
+
+ /*
+ * TODO - figure out the wakeup pin:
+ * ctp_wakeup = port:PB13<1><default><default><1>
+ * TODO - find correct GPIO pin, PI16 seems to be incorrect:
+ * ctp_en = port:PI16<1><default><default><0>
+ */
+ };
+};
+
&lradc {
vref-supply = <&reg_ldo2>;

--
2.6.3

2015-12-07 07:26:25

by Priit Laes

[permalink] [raw]
Subject: [PATCH 2/2] input: gt801_2plus1 - Add initial support for Goodix GT801 2+1

This patch adds Goodix GT801 2+1 touchscreen controller support.

GT801 2+1 is a 10-finger touch controller consisting of
ARM controller interfacing two GT801 5-finger controllers.

Signed-off-by: Priit Laes <[email protected]>
---
.../bindings/input/touchscreen/gt801_2plus1.txt | 23 ++
MAINTAINERS | 6 +
drivers/input/touchscreen/Kconfig | 19 +-
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/gt801_2plus1.c | 375 +++++++++++++++++++++
5 files changed, 421 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt
create mode 100644 drivers/input/touchscreen/gt801_2plus1.c

diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt b/Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt
new file mode 100644
index 0000000..070ff5b
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt
@@ -0,0 +1,23 @@
+Device tree bindings for Goodix GT801 2+1 series touchscreen controller
+
+Required properties:
+
+ - compatible : Should be "goodix,gt801_2plus1"
+ - reg : I2C address of the chip. Should be 0x55
+ - interrupt-parent : Interrupt controller to which the chip is connected
+ - interrupts : Interrupt to which the chip is connected
+
+Example:
+
+ i2c@00000000 {
+ /* ... */
+
+ touchscreen@55 {
+ compatible = "goodix,gt801_2plus1";
+ reg = <0x55>;
+ interrupt-parent = <&gpio>;
+ interrupts = <0 0>;
+ };
+
+ /* ... */
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index cba790b..e292126 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4743,6 +4743,12 @@ L: [email protected]
S: Maintained
F: drivers/input/touchscreen/goodix.c

+GOODIX GT801 2PLUS1 TOUCHSCREEN
+M: Priit Laes <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/input/touchscreen/gt801_2plus1.c
+
GPIO SUBSYSTEM
M: Linus Walleij <[email protected]>
M: Alexandre Courbot <[email protected]>
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index ae33da7..c7c3324 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -322,11 +322,11 @@ config TOUCHSCREEN_FUJITSU
module will be called fujitsu-ts.

config TOUCHSCREEN_GOODIX
- tristate "Goodix I2C touchscreen"
+ tristate "Goodix GT9xx I2C touchscreen"
depends on I2C
help
- Say Y here if you have the Goodix touchscreen (such as one
- installed in Onda v975w tablets) connected to your
+ Say Y here if you have one of the Goodix GT9xx touchscreens
+ (such as one installed in Onda v975w tablet) connected to your
system. It also supports 5-finger chip models, which can be
found on ARM tablets, like Wexler TAB7200 and MSI Primo73.

@@ -335,6 +335,19 @@ config TOUCHSCREEN_GOODIX
To compile this driver as a module, choose M here: the
module will be called goodix.

+config TOUCHSCREEN_GT801_2PLUS1
+ tristate "Goodix GT801 2+1 I2C touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have the Goodix GT801 2+1 touchscreen.
+ This controller is found on some older ARM tablets like
+ (Gemei G9 and Zareason Zatab).
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gt801_2plus1.
+
config TOUCHSCREEN_ILI210X
tristate "Ilitek ILI210X based touchscreen"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index cbaa6ab..ff25d1b4 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
+obj-$(CONFIG_TOUCHSCREEN_GT801_2PLUS1) += gt801_2plus1.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
diff --git a/drivers/input/touchscreen/gt801_2plus1.c b/drivers/input/touchscreen/gt801_2plus1.c
new file mode 100644
index 0000000..e2be479
--- /dev/null
+++ b/drivers/input/touchscreen/gt801_2plus1.c
@@ -0,0 +1,375 @@
+/*
+ * Driver for Goodix GT801 2+1 ARM touchscreen controllers
+ *
+ * Copyright (c) 2015 Priit Laes <[email protected]>.
+ *
+ * This code is based on goodix.c driver (c) 2014 Red Hat Inc,
+ * various Android codedumps (c) 2010 - 2012 Goodix Technology
+ * and cleanups done by Emilio López (turl) for linux-sunxi.
+ *
+ * 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 of the License.
+ */
+#include <asm/unaligned.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/input/mt.h>
+
+struct gt801x_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ int abs_x_max;
+ int abs_y_max;
+ unsigned int max_touch_num;
+ unsigned int int_trigger_type;
+};
+
+#define GOODIX_MAX_HEIGHT 4096
+#define GOODIX_MAX_WIDTH 4096
+#define GOODIX_INT_TRIGGER 1
+#define GOODIX_MAX_CONTACTS 10
+#define MAX_CONTACTS_LOC 5
+#define RESOLUTION_LOC 1
+#define TRIGGER_LOC 6
+
+/* Register defines */
+#define GT801X_COOR_ADDR 0x01
+#define GT801X_CONFIG_DATA 0x65
+#define GT801X_REG_ID 0xf0
+
+/* Device specific defines */
+#define GT801X_CONFIG_MAX_LENGTH 7
+#define GT801X_CONTACT_SIZE 5
+
+static const unsigned long goodix_irq_flags[] = {
+ IRQ_TYPE_EDGE_RISING,
+ IRQ_TYPE_EDGE_FALLING,
+ IRQ_TYPE_LEVEL_LOW,
+ IRQ_TYPE_LEVEL_HIGH,
+};
+
+/**
+ * gt801x_i2c_read - read data from a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to read from.
+ * @buf: raw write data buffer.
+ * @len: length of the buffer to write
+ */
+static int gt801x_i2c_read(struct i2c_client *client,
+ u8 reg, u8 *buf, int len)
+{
+ struct i2c_msg msgs[2];
+ int ret;
+
+ msgs[0].flags = 0;
+ msgs[0].addr = client->addr;
+ msgs[0].len = 1;
+ msgs[0].buf = &reg;
+
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].addr = client->addr;
+ msgs[1].len = len;
+ msgs[1].buf = buf;
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
+}
+
+/**
+ * gt801x_process_events - Process incoming events
+ *
+ * @ts: our gt801x_ts_data pointer
+ *
+ * Called when the IRQ is triggered. Read the current device state, and push
+ * the input events to the user space.
+ */
+static void gt801x_process_events(struct gt801x_ts_data *ts)
+{
+ u8 point_data[3 + GT801X_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
+ u8 touch_map[GOODIX_MAX_CONTACTS] = {0};
+ int input_x, input_y, input_w;
+ u8 checksum = 0;
+ u16 touch_raw;
+ u8 touch_num;
+ int error;
+ int loc;
+ int i;
+
+ error = gt801x_i2c_read(ts->client, GT801X_COOR_ADDR,
+ point_data, sizeof(point_data));
+ if (error) {
+ dev_err(&ts->client->dev, "I2C transfer error: %d\n", error);
+ return;
+ }
+
+ /* Fetch touch mapping bits */
+ touch_raw = get_unaligned_le16(&point_data[0]);
+ if (!touch_raw)
+ return;
+
+ /* Build touch map */
+ touch_num = 0;
+ for (i = 0; (touch_raw != 0) && (i < ts->max_touch_num); i++) {
+ if (touch_raw & 1)
+ touch_map[touch_num++] = i;
+ touch_raw >>= 1;
+ }
+
+ /* Calculate checksum */
+ for (i = 0; i < (touch_num*GT801X_CONTACT_SIZE + 3); i++)
+ checksum += point_data[i];
+ if (checksum != 0)
+ return;
+
+ /* Report touches */
+ for (i = 0; i < touch_num; i++) {
+ loc = 2 + GT801X_CONTACT_SIZE * i;
+ input_x = get_unaligned_be16(&point_data[loc]);
+ input_y = get_unaligned_be16(&point_data[loc + 2]);
+ input_w = point_data[loc + 4];
+
+ input_mt_slot(ts->input_dev, i);
+ input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+ input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
+ }
+
+ input_mt_sync_frame(ts->input_dev);
+ input_sync(ts->input_dev);
+}
+
+/**
+ * gt801x_ts_irq_handler - The IRQ handler
+ *
+ * @irq: interrupt number.
+ * @dev_id: private data pointer.
+ */
+static irqreturn_t gt801x_ts_irq_handler(int irq, void *dev_id)
+{
+ gt801x_process_events((struct gt801x_ts_data *)dev_id);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * gt801x_read_config - Read the embedded configuration of the panel
+ *
+ * @ts: our gt801x_ts_data pointer
+ *
+ * Must be called during probe
+ */
+static void gt801x_read_config(struct gt801x_ts_data *ts)
+{
+ u8 config[GT801X_CONFIG_MAX_LENGTH];
+ int error;
+
+ error = gt801x_i2c_read(ts->client, GT801X_CONFIG_DATA,
+ config,
+ GT801X_CONFIG_MAX_LENGTH);
+ if (error) {
+ dev_warn(&ts->client->dev,
+ "Error reading config (%d), using defaults\n",
+ error);
+ ts->abs_x_max = GOODIX_MAX_WIDTH;
+ ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ ts->int_trigger_type = GOODIX_INT_TRIGGER;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
+ return;
+ }
+
+ ts->abs_x_max = get_unaligned_be16(&config[RESOLUTION_LOC]);
+ ts->abs_y_max = get_unaligned_be16(&config[RESOLUTION_LOC + 2]);
+ ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
+ ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
+ if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
+ dev_err(&ts->client->dev,
+ "Invalid config, using defaults\n");
+ ts->abs_x_max = GOODIX_MAX_WIDTH;
+ ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
+ }
+}
+
+/**
+ * gt801x_read_version - Read GT801 2+1 touchscreen version
+ *
+ * @client: the i2c client
+ * @version: output buffer containing the version on success
+ * @id: output buffer containing the id on success
+ */
+static int gt801x_read_version(struct i2c_client *client, u16 *version, u16 *id)
+{
+ int error;
+ u8 buf[16];
+
+ error = gt801x_i2c_read(client, GT801X_REG_ID, buf, sizeof(buf));
+ if (error) {
+ dev_err(&client->dev, "read version failed: %d\n", error);
+ return error;
+ }
+ /* TODO: version info contains 'GT801NI_3R15_1AV' */
+ print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, ARRAY_SIZE(buf));
+ *id = 0x802;
+ *version = 0x15;
+ dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
+ return 0;
+}
+
+/**
+ * gt801x_i2c_test - I2C test function to check if the device answers.
+ *
+ * @client: the i2c client
+ */
+static int gt801x_i2c_test(struct i2c_client *client)
+{
+ int retry = 0;
+ int error;
+ u8 test;
+
+ while (retry++ < 2) {
+ error = gt801x_i2c_read(client, GT801X_CONFIG_DATA,
+ &test, 1);
+ if (!error)
+ return 0;
+
+ dev_err(&client->dev, "i2c test failed attempt %d: %d\n",
+ retry, error);
+ msleep(20);
+ }
+
+ return error;
+}
+
+/**
+ * gt801x_request_input_dev - Allocate, populate and register the input device
+ *
+ * @ts: our gt801x_ts_data pointer
+ * @version: device firmware version
+ * @id: device ID
+ *
+ * Must be called during probe
+ */
+static int gt801x_request_input_dev(struct gt801x_ts_data *ts,
+ u16 version, u16 id)
+{
+ int error;
+
+ ts->input_dev = devm_input_allocate_device(&ts->client->dev);
+ if (!ts->input_dev) {
+ dev_err(&ts->client->dev, "Failed to allocate input device.");
+ return -ENOMEM;
+ }
+
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
+ 0, ts->abs_x_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+ 0, ts->abs_y_max, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+ input_mt_init_slots(ts->input_dev, ts->max_touch_num,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+
+ ts->input_dev->name = "Goodix Capacitive TouchScreen (GT801 2+1)";
+ ts->input_dev->phys = "input/ts";
+ ts->input_dev->id.bustype = BUS_I2C;
+ ts->input_dev->id.vendor = 0x0416;
+ ts->input_dev->id.product = id;
+ ts->input_dev->id.version = version;
+
+ error = input_register_device(ts->input_dev);
+ if (error) {
+ dev_err(&ts->client->dev,
+ "Failed to register input device: %d", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int gt801x_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct gt801x_ts_data *ts;
+ unsigned long irq_flags;
+ int error;
+ u16 version_info, id_info;
+
+ dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C check functionality failed.\n");
+ return -ENXIO;
+ }
+
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ ts->client = client;
+ i2c_set_clientdata(client, ts);
+
+ error = gt801x_i2c_test(client);
+ if (error) {
+ dev_err(&client->dev, "I2C communication failure: %d\n", error);
+ return error;
+ }
+
+ error = gt801x_read_version(client, &version_info, &id_info);
+ if (error) {
+ dev_err(&client->dev, "Read version failed.\n");
+ return error;
+ }
+
+ gt801x_read_config(ts);
+
+ error = gt801x_request_input_dev(ts, version_info, id_info);
+ if (error)
+ return error;
+
+ irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+ error = devm_request_threaded_irq(&ts->client->dev, client->irq,
+ NULL, gt801x_ts_irq_handler,
+ irq_flags, client->name, ts);
+ if (error) {
+ dev_err(&client->dev, "request IRQ failed: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id gt801x_ts_id[] = {
+ { "GDIX1001:00", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, gt801x_ts_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id gt801x_of_match[] = {
+ { .compatible = "goodix,gt801_2plus1" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gt801x_of_match);
+#endif
+
+static struct i2c_driver gt801x_ts_driver = {
+ .probe = gt801x_ts_probe,
+ .id_table = gt801x_ts_id,
+ .driver = {
+ .name = "Goodix-TS",
+ .of_match_table = of_match_ptr(gt801x_of_match),
+ },
+};
+module_i2c_driver(gt801x_ts_driver);
+
+MODULE_AUTHOR("Priit Laes <[email protected]>");
+MODULE_DESCRIPTION("Goodix GT801 2+1 touchscreen driver");
+MODULE_LICENSE("GPL v2");
--
2.6.3

2015-12-07 08:34:47

by Julia Lawall

[permalink] [raw]
Subject: Re: [PATCH 2/2] input: gt801_2plus1 - Add initial support for Goodix GT801 2+1

It looks like braces may be mising. Please check.

julia

On Mon, 7 Dec 2015, kbuild test robot wrote:

> Hi Priit,
>
> [auto build test WARNING on mripard/sunxi/for-next]
> [also build test WARNING on next-20151203]
> [cannot apply to input/next v4.4-rc4]
>
> url: https://github.com/0day-ci/linux/commits/Priit-Laes/input-Driver-for-Goodix-GT801-2-1-touchscreen/20151207-152831
> base: https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux.git sunxi/for-next
> :::::: branch date: 46 minutes ago
> :::::: commit date: 46 minutes ago
>
> >> drivers/input/touchscreen/gt801_2plus1.c:119:3-30: code aligned with following code on line 120
>
> git remote add linux-review https://github.com/0day-ci/linux
> git remote update linux-review
> git checkout d66df313f96e04572821e49cada65cb5c1bea9e2
> vim +119 drivers/input/touchscreen/gt801_2plus1.c
>
> d66df313 Priit Laes 2015-12-07 113 return;
> d66df313 Priit Laes 2015-12-07 114
> d66df313 Priit Laes 2015-12-07 115 /* Build touch map */
> d66df313 Priit Laes 2015-12-07 116 touch_num = 0;
> d66df313 Priit Laes 2015-12-07 117 for (i = 0; (touch_raw != 0) && (i < ts->max_touch_num); i++) {
> d66df313 Priit Laes 2015-12-07 118 if (touch_raw & 1)
> d66df313 Priit Laes 2015-12-07 @119 touch_map[touch_num++] = i;
> d66df313 Priit Laes 2015-12-07 @120 touch_raw >>= 1;
> d66df313 Priit Laes 2015-12-07 121 }
> d66df313 Priit Laes 2015-12-07 122
> d66df313 Priit Laes 2015-12-07 123 /* Calculate checksum */
>
> ---
> 0-DAY kernel test infrastructure Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all Intel Corporation
>

2015-12-07 09:10:27

by Priit Laes

[permalink] [raw]
Subject: Re: [PATCH 2/2] input: gt801_2plus1 - Add initial support for Goodix GT801 2+1

On Mon, 2015-12-07 at 09:34 +0100, Julia Lawall wrote:
> It looks like braces may be mising.  Please check.

Thanks, nice catch!

It's actually messed up indenting. Code works as intended.
>
> julia
>
> On Mon, 7 Dec 2015, kbuild test robot wrote:
>
> > Hi Priit,
> >
> > [auto build test WARNING on mripard/sunxi/for-next]
> > [also build test WARNING on next-20151203]
> > [cannot apply to input/next v4.4-rc4]
> >
> > url:    https://github.com/0day-ci/linux/commits/Priit-Laes/input-D
> river-for-Goodix-GT801-2-1-touchscreen/20151207-152831
> > base:   https://git.kernel.org/pub/scm/linux/kernel/git/mripard/lin
> ux.git sunxi/for-next
> > :::::: branch date: 46 minutes ago
> > :::::: commit date: 46 minutes ago
> >
> > >> drivers/input/touchscreen/gt801_2plus1.c:119:3-30: code aligned
> with following code on line 120
> >
> > git remote add linux-review https://github.com/0day-ci/linux
> > git remote update linux-review
> > git checkout d66df313f96e04572821e49cada65cb5c1bea9e2
> > vim +119 drivers/input/touchscreen/gt801_2plus1.c
> >
> > d66df313 Priit Laes 2015-12-07  113           return;
> > d66df313 Priit Laes 2015-12-07  114 
> > d66df313 Priit Laes 2015-12-07  115   /* Build touch map */
> > d66df313 Priit Laes 2015-12-07  116   touch_num = 0;
> > d66df313 Priit Laes 2015-12-07  117   for (i = 0; (touch_raw != 0)
> && (i < ts->max_touch_num); i++) {
> > d66df313 Priit Laes 2015-12-07  118           if (touch_raw & 1)
> > d66df313 Priit Laes 2015-12-07
> @119                   touch_map[touch_num++] = i;
> > d66df313 Priit Laes 2015-12-07 @120                   touch_raw >>=
> 1;
> > d66df313 Priit Laes 2015-12-07  121   }
> > d66df313 Priit Laes 2015-12-07  122 
> > d66df313 Priit Laes 2015-12-07  123   /* Calculate checksum */
> >
> > ---
> > 0-DAY kernel test infrastructure                Open Source
> Technology Center
> > https://lists.01.org/pipermail/kbuild-all                  ; Intel
> Corporation
> >

2015-12-08 12:15:03

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 0/2] input: Driver for Goodix GT801 2+1 touchscreen

On Mon, 2015-12-07 at 09:25 +0200, Priit Laes wrote:
> This series adds support for Goodix GT801 2+1 touchscreen controller
> and hooks it up on Gemei G9 tablet.
>
> Now about GT801 2+1 - I initially tried to implement this inside the
> existing Goodix driver, but unfortunately there are too many small
> bits and pieces that would make the otherwise simple driver a
> complicated mess:
>   - endianness differences of coordinate readouts
>   - totally different touch protocol
>   - configuration layout and version information
>   - I2C register differences (2 bytes vs single byte)

The goodix driver will get more complicated after we merge Irina's
patchset. Instead of looking at the differences between the protocols,
which don't look insurmountable (a switch statement in a few places,
right?), you should look at the code you could share:
- power management
- ESD support
- ACPI support
- the support for variants in goodix.c (911-variants, and 967-variants)
- ability to read and write config data to the device

Taking this into account (and the fact that I can't read Chinese that
well, so my reading of the specs is cursory), would it make sense to
merge the 2 drivers?

CC'ing Irina about this.

Cheers

2015-12-08 12:15:14

by Bastien Nocera

[permalink] [raw]
Subject: Re: [PATCH 2/2] input: gt801_2plus1 - Add initial support for Goodix GT801 2+1

Hey Priit,

On Mon, 2015-12-07 at 09:26 +0200, Priit Laes wrote:
> This patch adds Goodix GT801 2+1 touchscreen controller support.
>
> GT801 2+1 is a 10-finger touch controller consisting of
> ARM controller interfacing two GT801 5-finger controllers.

This would implement support for the "Guitar" protocol used by a few
more variants of the GT80x devices. Can you rename the driver/docs to that effect?
<snip>
> +
> +static const struct i2c_device_id gt801x_ts_id[] = {
> + { "GDIX1001:00", 0 },

This clashes with the ID used in the goodix.c driver.

Cheers

2015-12-08 15:18:10

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 2/2] input: gt801_2plus1 - Add initial support for Goodix GT801 2+1

On Mon, Dec 07, 2015 at 09:26:01AM +0200, Priit Laes wrote:
> This patch adds Goodix GT801 2+1 touchscreen controller support.
>
> GT801 2+1 is a 10-finger touch controller consisting of
> ARM controller interfacing two GT801 5-finger controllers.
>
> Signed-off-by: Priit Laes <[email protected]>
> ---
> .../bindings/input/touchscreen/gt801_2plus1.txt | 23 ++

For the binding:

Acked-by: Rob Herring <[email protected]>

> MAINTAINERS | 6 +
> drivers/input/touchscreen/Kconfig | 19 +-
> drivers/input/touchscreen/Makefile | 1 +
> drivers/input/touchscreen/gt801_2plus1.c | 375 +++++++++++++++++++++
> 5 files changed, 421 insertions(+), 3 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt
> create mode 100644 drivers/input/touchscreen/gt801_2plus1.c
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt b/Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt
> new file mode 100644
> index 0000000..070ff5b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/gt801_2plus1.txt
> @@ -0,0 +1,23 @@
> +Device tree bindings for Goodix GT801 2+1 series touchscreen controller
> +
> +Required properties:
> +
> + - compatible : Should be "goodix,gt801_2plus1"
> + - reg : I2C address of the chip. Should be 0x55
> + - interrupt-parent : Interrupt controller to which the chip is connected
> + - interrupts : Interrupt to which the chip is connected
> +
> +Example:
> +
> + i2c@00000000 {
> + /* ... */
> +
> + touchscreen@55 {
> + compatible = "goodix,gt801_2plus1";
> + reg = <0x55>;
> + interrupt-parent = <&gpio>;
> + interrupts = <0 0>;
> + };
> +
> + /* ... */
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index cba790b..e292126 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4743,6 +4743,12 @@ L: [email protected]
> S: Maintained
> F: drivers/input/touchscreen/goodix.c
>
> +GOODIX GT801 2PLUS1 TOUCHSCREEN
> +M: Priit Laes <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/input/touchscreen/gt801_2plus1.c
> +
> GPIO SUBSYSTEM
> M: Linus Walleij <[email protected]>
> M: Alexandre Courbot <[email protected]>
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index ae33da7..c7c3324 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -322,11 +322,11 @@ config TOUCHSCREEN_FUJITSU
> module will be called fujitsu-ts.
>
> config TOUCHSCREEN_GOODIX
> - tristate "Goodix I2C touchscreen"
> + tristate "Goodix GT9xx I2C touchscreen"
> depends on I2C
> help
> - Say Y here if you have the Goodix touchscreen (such as one
> - installed in Onda v975w tablets) connected to your
> + Say Y here if you have one of the Goodix GT9xx touchscreens
> + (such as one installed in Onda v975w tablet) connected to your
> system. It also supports 5-finger chip models, which can be
> found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
>
> @@ -335,6 +335,19 @@ config TOUCHSCREEN_GOODIX
> To compile this driver as a module, choose M here: the
> module will be called goodix.
>
> +config TOUCHSCREEN_GT801_2PLUS1
> + tristate "Goodix GT801 2+1 I2C touchscreen"
> + depends on I2C
> + help
> + Say Y here if you have the Goodix GT801 2+1 touchscreen.
> + This controller is found on some older ARM tablets like
> + (Gemei G9 and Zareason Zatab).
> +
> + If unsure, say N.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called gt801_2plus1.
> +
> config TOUCHSCREEN_ILI210X
> tristate "Ilitek ILI210X based touchscreen"
> depends on I2C
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index cbaa6ab..ff25d1b4 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
> obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
> obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
> obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
> +obj-$(CONFIG_TOUCHSCREEN_GT801_2PLUS1) += gt801_2plus1.o
> obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
> obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
> obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
> diff --git a/drivers/input/touchscreen/gt801_2plus1.c b/drivers/input/touchscreen/gt801_2plus1.c
> new file mode 100644
> index 0000000..e2be479
> --- /dev/null
> +++ b/drivers/input/touchscreen/gt801_2plus1.c
> @@ -0,0 +1,375 @@
> +/*
> + * Driver for Goodix GT801 2+1 ARM touchscreen controllers
> + *
> + * Copyright (c) 2015 Priit Laes <[email protected]>.
> + *
> + * This code is based on goodix.c driver (c) 2014 Red Hat Inc,
> + * various Android codedumps (c) 2010 - 2012 Goodix Technology
> + * and cleanups done by Emilio L?pez (turl) for linux-sunxi.
> + *
> + * 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 of the License.
> + */
> +#include <asm/unaligned.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/input/mt.h>
> +
> +struct gt801x_ts_data {
> + struct i2c_client *client;
> + struct input_dev *input_dev;
> + int abs_x_max;
> + int abs_y_max;
> + unsigned int max_touch_num;
> + unsigned int int_trigger_type;
> +};
> +
> +#define GOODIX_MAX_HEIGHT 4096
> +#define GOODIX_MAX_WIDTH 4096
> +#define GOODIX_INT_TRIGGER 1
> +#define GOODIX_MAX_CONTACTS 10
> +#define MAX_CONTACTS_LOC 5
> +#define RESOLUTION_LOC 1
> +#define TRIGGER_LOC 6
> +
> +/* Register defines */
> +#define GT801X_COOR_ADDR 0x01
> +#define GT801X_CONFIG_DATA 0x65
> +#define GT801X_REG_ID 0xf0
> +
> +/* Device specific defines */
> +#define GT801X_CONFIG_MAX_LENGTH 7
> +#define GT801X_CONTACT_SIZE 5
> +
> +static const unsigned long goodix_irq_flags[] = {
> + IRQ_TYPE_EDGE_RISING,
> + IRQ_TYPE_EDGE_FALLING,
> + IRQ_TYPE_LEVEL_LOW,
> + IRQ_TYPE_LEVEL_HIGH,
> +};
> +
> +/**
> + * gt801x_i2c_read - read data from a register of the i2c slave device.
> + *
> + * @client: i2c device.
> + * @reg: the register to read from.
> + * @buf: raw write data buffer.
> + * @len: length of the buffer to write
> + */
> +static int gt801x_i2c_read(struct i2c_client *client,
> + u8 reg, u8 *buf, int len)
> +{
> + struct i2c_msg msgs[2];
> + int ret;
> +
> + msgs[0].flags = 0;
> + msgs[0].addr = client->addr;
> + msgs[0].len = 1;
> + msgs[0].buf = &reg;
> +
> + msgs[1].flags = I2C_M_RD;
> + msgs[1].addr = client->addr;
> + msgs[1].len = len;
> + msgs[1].buf = buf;
> +
> + ret = i2c_transfer(client->adapter, msgs, 2);
> + return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
> +}
> +
> +/**
> + * gt801x_process_events - Process incoming events
> + *
> + * @ts: our gt801x_ts_data pointer
> + *
> + * Called when the IRQ is triggered. Read the current device state, and push
> + * the input events to the user space.
> + */
> +static void gt801x_process_events(struct gt801x_ts_data *ts)
> +{
> + u8 point_data[3 + GT801X_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
> + u8 touch_map[GOODIX_MAX_CONTACTS] = {0};
> + int input_x, input_y, input_w;
> + u8 checksum = 0;
> + u16 touch_raw;
> + u8 touch_num;
> + int error;
> + int loc;
> + int i;
> +
> + error = gt801x_i2c_read(ts->client, GT801X_COOR_ADDR,
> + point_data, sizeof(point_data));
> + if (error) {
> + dev_err(&ts->client->dev, "I2C transfer error: %d\n", error);
> + return;
> + }
> +
> + /* Fetch touch mapping bits */
> + touch_raw = get_unaligned_le16(&point_data[0]);
> + if (!touch_raw)
> + return;
> +
> + /* Build touch map */
> + touch_num = 0;
> + for (i = 0; (touch_raw != 0) && (i < ts->max_touch_num); i++) {
> + if (touch_raw & 1)
> + touch_map[touch_num++] = i;
> + touch_raw >>= 1;
> + }
> +
> + /* Calculate checksum */
> + for (i = 0; i < (touch_num*GT801X_CONTACT_SIZE + 3); i++)
> + checksum += point_data[i];
> + if (checksum != 0)
> + return;
> +
> + /* Report touches */
> + for (i = 0; i < touch_num; i++) {
> + loc = 2 + GT801X_CONTACT_SIZE * i;
> + input_x = get_unaligned_be16(&point_data[loc]);
> + input_y = get_unaligned_be16(&point_data[loc + 2]);
> + input_w = point_data[loc + 4];
> +
> + input_mt_slot(ts->input_dev, i);
> + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
> + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
> + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
> + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
> + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
> + }
> +
> + input_mt_sync_frame(ts->input_dev);
> + input_sync(ts->input_dev);
> +}
> +
> +/**
> + * gt801x_ts_irq_handler - The IRQ handler
> + *
> + * @irq: interrupt number.
> + * @dev_id: private data pointer.
> + */
> +static irqreturn_t gt801x_ts_irq_handler(int irq, void *dev_id)
> +{
> + gt801x_process_events((struct gt801x_ts_data *)dev_id);
> +
> + return IRQ_HANDLED;
> +}
> +
> +/**
> + * gt801x_read_config - Read the embedded configuration of the panel
> + *
> + * @ts: our gt801x_ts_data pointer
> + *
> + * Must be called during probe
> + */
> +static void gt801x_read_config(struct gt801x_ts_data *ts)
> +{
> + u8 config[GT801X_CONFIG_MAX_LENGTH];
> + int error;
> +
> + error = gt801x_i2c_read(ts->client, GT801X_CONFIG_DATA,
> + config,
> + GT801X_CONFIG_MAX_LENGTH);
> + if (error) {
> + dev_warn(&ts->client->dev,
> + "Error reading config (%d), using defaults\n",
> + error);
> + ts->abs_x_max = GOODIX_MAX_WIDTH;
> + ts->abs_y_max = GOODIX_MAX_HEIGHT;
> + ts->int_trigger_type = GOODIX_INT_TRIGGER;
> + ts->max_touch_num = GOODIX_MAX_CONTACTS;
> + return;
> + }
> +
> + ts->abs_x_max = get_unaligned_be16(&config[RESOLUTION_LOC]);
> + ts->abs_y_max = get_unaligned_be16(&config[RESOLUTION_LOC + 2]);
> + ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
> + ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
> + if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
> + dev_err(&ts->client->dev,
> + "Invalid config, using defaults\n");
> + ts->abs_x_max = GOODIX_MAX_WIDTH;
> + ts->abs_y_max = GOODIX_MAX_HEIGHT;
> + ts->max_touch_num = GOODIX_MAX_CONTACTS;
> + }
> +}
> +
> +/**
> + * gt801x_read_version - Read GT801 2+1 touchscreen version
> + *
> + * @client: the i2c client
> + * @version: output buffer containing the version on success
> + * @id: output buffer containing the id on success
> + */
> +static int gt801x_read_version(struct i2c_client *client, u16 *version, u16 *id)
> +{
> + int error;
> + u8 buf[16];
> +
> + error = gt801x_i2c_read(client, GT801X_REG_ID, buf, sizeof(buf));
> + if (error) {
> + dev_err(&client->dev, "read version failed: %d\n", error);
> + return error;
> + }
> + /* TODO: version info contains 'GT801NI_3R15_1AV' */
> + print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, ARRAY_SIZE(buf));
> + *id = 0x802;
> + *version = 0x15;
> + dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
> + return 0;
> +}
> +
> +/**
> + * gt801x_i2c_test - I2C test function to check if the device answers.
> + *
> + * @client: the i2c client
> + */
> +static int gt801x_i2c_test(struct i2c_client *client)
> +{
> + int retry = 0;
> + int error;
> + u8 test;
> +
> + while (retry++ < 2) {
> + error = gt801x_i2c_read(client, GT801X_CONFIG_DATA,
> + &test, 1);
> + if (!error)
> + return 0;
> +
> + dev_err(&client->dev, "i2c test failed attempt %d: %d\n",
> + retry, error);
> + msleep(20);
> + }
> +
> + return error;
> +}
> +
> +/**
> + * gt801x_request_input_dev - Allocate, populate and register the input device
> + *
> + * @ts: our gt801x_ts_data pointer
> + * @version: device firmware version
> + * @id: device ID
> + *
> + * Must be called during probe
> + */
> +static int gt801x_request_input_dev(struct gt801x_ts_data *ts,
> + u16 version, u16 id)
> +{
> + int error;
> +
> + ts->input_dev = devm_input_allocate_device(&ts->client->dev);
> + if (!ts->input_dev) {
> + dev_err(&ts->client->dev, "Failed to allocate input device.");
> + return -ENOMEM;
> + }
> +
> + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
> + 0, ts->abs_x_max, 0, 0);
> + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
> + 0, ts->abs_y_max, 0, 0);
> + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
> + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> +
> + input_mt_init_slots(ts->input_dev, ts->max_touch_num,
> + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> +
> + ts->input_dev->name = "Goodix Capacitive TouchScreen (GT801 2+1)";
> + ts->input_dev->phys = "input/ts";
> + ts->input_dev->id.bustype = BUS_I2C;
> + ts->input_dev->id.vendor = 0x0416;
> + ts->input_dev->id.product = id;
> + ts->input_dev->id.version = version;
> +
> + error = input_register_device(ts->input_dev);
> + if (error) {
> + dev_err(&ts->client->dev,
> + "Failed to register input device: %d", error);
> + return error;
> + }
> +
> + return 0;
> +}
> +
> +static int gt801x_ts_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct gt801x_ts_data *ts;
> + unsigned long irq_flags;
> + int error;
> + u16 version_info, id_info;
> +
> + dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
> + dev_err(&client->dev, "I2C check functionality failed.\n");
> + return -ENXIO;
> + }
> +
> + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
> + if (!ts)
> + return -ENOMEM;
> +
> + ts->client = client;
> + i2c_set_clientdata(client, ts);
> +
> + error = gt801x_i2c_test(client);
> + if (error) {
> + dev_err(&client->dev, "I2C communication failure: %d\n", error);
> + return error;
> + }
> +
> + error = gt801x_read_version(client, &version_info, &id_info);
> + if (error) {
> + dev_err(&client->dev, "Read version failed.\n");
> + return error;
> + }
> +
> + gt801x_read_config(ts);
> +
> + error = gt801x_request_input_dev(ts, version_info, id_info);
> + if (error)
> + return error;
> +
> + irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
> + error = devm_request_threaded_irq(&ts->client->dev, client->irq,
> + NULL, gt801x_ts_irq_handler,
> + irq_flags, client->name, ts);
> + if (error) {
> + dev_err(&client->dev, "request IRQ failed: %d\n", error);
> + return error;
> + }
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id gt801x_ts_id[] = {
> + { "GDIX1001:00", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, gt801x_ts_id);
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id gt801x_of_match[] = {
> + { .compatible = "goodix,gt801_2plus1" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, gt801x_of_match);
> +#endif
> +
> +static struct i2c_driver gt801x_ts_driver = {
> + .probe = gt801x_ts_probe,
> + .id_table = gt801x_ts_id,
> + .driver = {
> + .name = "Goodix-TS",
> + .of_match_table = of_match_ptr(gt801x_of_match),
> + },
> +};
> +module_i2c_driver(gt801x_ts_driver);
> +
> +MODULE_AUTHOR("Priit Laes <[email protected]>");
> +MODULE_DESCRIPTION("Goodix GT801 2+1 touchscreen driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.6.3
>