d99a023
From: Bastien Nocera <hadess@hadess.net>
d99a023
Date: Fri, 31 Oct 2014 09:26:16 -0700
d99a023
Subject: [PATCH] Input: add driver for the Goodix touchpanel
d99a023
d99a023
Add a driver for the Goodix touchscreen panel found in Onda v975w tablets.
d99a023
The driver is based off the Android driver gt9xx.c found in some Android
d99a023
code dumps, but now bears no resemblance to the original driver.
d99a023
d99a023
The driver was tested on the aforementioned tablet.
d99a023
d99a023
Signed-off-by: Bastien Nocera <hadess@hadess.net>
d99a023
Tested-by: Bastien Nocera <hadess@hadess.net>
d99a023
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
d99a023
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
d99a023
---
d99a023
 MAINTAINERS                        |   6 +
d99a023
 drivers/input/touchscreen/Kconfig  |  13 ++
d99a023
 drivers/input/touchscreen/Makefile |   1 +
d99a023
 drivers/input/touchscreen/goodix.c | 395 +++++++++++++++++++++++++++++++++++++
d99a023
 4 files changed, 415 insertions(+)
d99a023
 create mode 100644 drivers/input/touchscreen/goodix.c
d99a023
d99a023
diff --git a/MAINTAINERS b/MAINTAINERS
56722ef
index c444907ccd69..766848ecf8f5 100644
d99a023
--- a/MAINTAINERS
d99a023
+++ b/MAINTAINERS
56722ef
@@ -4154,6 +4154,12 @@ L:	linux-media@vger.kernel.org
d99a023
 S:	Maintained
d99a023
 F:	drivers/media/usb/go7007/
d99a023
 
d99a023
+GOODIX TOUCHSCREEN
d99a023
+M:	Bastien Nocera <hadess@hadess.net>
d99a023
+L:	linux-input@vger.kernel.org
d99a023
+S:	Maintained
d99a023
+F:	drivers/input/touchscreen/goodix.c
d99a023
+
d99a023
 GPIO SUBSYSTEM
d99a023
 M:	Linus Walleij <linus.walleij@linaro.org>
d99a023
 M:	Alexandre Courbot <gnurou@gmail.com>
d99a023
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
d99a023
index e1d8003d01f8..568a0200fbc2 100644
d99a023
--- a/drivers/input/touchscreen/Kconfig
d99a023
+++ b/drivers/input/touchscreen/Kconfig
d99a023
@@ -295,6 +295,19 @@ config TOUCHSCREEN_FUJITSU
d99a023
 	  To compile this driver as a module, choose M here: the
d99a023
 	  module will be called fujitsu-ts.
d99a023
 
d99a023
+config TOUCHSCREEN_GOODIX
d99a023
+	tristate "Goodix I2C touchscreen"
d99a023
+	depends on I2C && ACPI
d99a023
+	help
d99a023
+	  Say Y here if you have the Goodix touchscreen (such as one
d99a023
+	  installed in Onda v975w tablets) connected to your
d99a023
+	  system.
d99a023
+
d99a023
+	  If unsure, say N.
d99a023
+
d99a023
+	  To compile this driver as a module, choose M here: the
d99a023
+	  module will be called goodix.
d99a023
+
d99a023
 config TOUCHSCREEN_ILI210X
d99a023
 	tristate "Ilitek ILI210X based touchscreen"
d99a023
 	depends on I2C
d99a023
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
d99a023
index 090e61cc9171..dab4a56ac98e 100644
d99a023
--- a/drivers/input/touchscreen/Makefile
d99a023
+++ b/drivers/input/touchscreen/Makefile
d99a023
@@ -34,6 +34,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI)		+= eeti_ts.o
d99a023
 obj-$(CONFIG_TOUCHSCREEN_ELO)		+= elo.o
d99a023
 obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
d99a023
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
d99a023
+obj-$(CONFIG_TOUCHSCREEN_GOODIX)	+= goodix.o
d99a023
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
d99a023
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
d99a023
 obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
d99a023
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
d99a023
new file mode 100644
d99a023
index 000000000000..ca196689f025
d99a023
--- /dev/null
d99a023
+++ b/drivers/input/touchscreen/goodix.c
d99a023
@@ -0,0 +1,395 @@
d99a023
+/*
d99a023
+ *  Driver for Goodix Touchscreens
d99a023
+ *
d99a023
+ *  Copyright (c) 2014 Red Hat Inc.
d99a023
+ *
d99a023
+ *  This code is based on gt9xx.c authored by andrew@goodix.com:
d99a023
+ *
d99a023
+ *  2010 - 2012 Goodix Technology.
d99a023
+ */
d99a023
+
d99a023
+/*
d99a023
+ * This program is free software; you can redistribute it and/or modify it
d99a023
+ * under the terms of the GNU General Public License as published by the Free
d99a023
+ * Software Foundation; version 2 of the License.
d99a023
+ */
d99a023
+
d99a023
+#include <linux/kernel.h>
d99a023
+#include <linux/i2c.h>
d99a023
+#include <linux/input.h>
d99a023
+#include <linux/input/mt.h>
d99a023
+#include <linux/module.h>
d99a023
+#include <linux/delay.h>
d99a023
+#include <linux/irq.h>
d99a023
+#include <linux/interrupt.h>
d99a023
+#include <linux/slab.h>
d99a023
+#include <asm/unaligned.h>
d99a023
+
d99a023
+struct goodix_ts_data {
d99a023
+	struct i2c_client *client;
d99a023
+	struct input_dev *input_dev;
d99a023
+	int abs_x_max;
d99a023
+	int abs_y_max;
d99a023
+	unsigned int max_touch_num;
d99a023
+	unsigned int int_trigger_type;
d99a023
+};
d99a023
+
d99a023
+#define GOODIX_MAX_HEIGHT		4096
d99a023
+#define GOODIX_MAX_WIDTH		4096
d99a023
+#define GOODIX_INT_TRIGGER		1
d99a023
+#define GOODIX_CONTACT_SIZE		8
d99a023
+#define GOODIX_MAX_CONTACTS		10
d99a023
+
d99a023
+#define GOODIX_CONFIG_MAX_LENGTH	240
d99a023
+
d99a023
+/* Register defines */
d99a023
+#define GOODIX_READ_COOR_ADDR		0x814E
d99a023
+#define GOODIX_REG_CONFIG_DATA		0x8047
d99a023
+#define GOODIX_REG_VERSION		0x8140
d99a023
+
d99a023
+#define RESOLUTION_LOC		1
d99a023
+#define TRIGGER_LOC		6
d99a023
+
d99a023
+static const unsigned long goodix_irq_flags[] = {
d99a023
+	IRQ_TYPE_EDGE_RISING,
d99a023
+	IRQ_TYPE_EDGE_FALLING,
d99a023
+	IRQ_TYPE_LEVEL_LOW,
d99a023
+	IRQ_TYPE_LEVEL_HIGH,
d99a023
+};
d99a023
+
d99a023
+/**
d99a023
+ * goodix_i2c_read - read data from a register of the i2c slave device.
d99a023
+ *
d99a023
+ * @client: i2c device.
d99a023
+ * @reg: the register to read from.
d99a023
+ * @buf: raw write data buffer.
d99a023
+ * @len: length of the buffer to write
d99a023
+ */
d99a023
+static int goodix_i2c_read(struct i2c_client *client,
d99a023
+				u16 reg, u8 *buf, int len)
d99a023
+{
d99a023
+	struct i2c_msg msgs[2];
d99a023
+	u16 wbuf = cpu_to_be16(reg);
d99a023
+	int ret;
d99a023
+
d99a023
+	msgs[0].flags = 0;
d99a023
+	msgs[0].addr  = client->addr;
d99a023
+	msgs[0].len   = 2;
d99a023
+	msgs[0].buf   = (u8 *) &wbuf;
d99a023
+
d99a023
+	msgs[1].flags = I2C_M_RD;
d99a023
+	msgs[1].addr  = client->addr;
d99a023
+	msgs[1].len   = len;
d99a023
+	msgs[1].buf   = buf;
d99a023
+
d99a023
+	ret = i2c_transfer(client->adapter, msgs, 2);
d99a023
+	return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
d99a023
+}
d99a023
+
d99a023
+static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
d99a023
+{
d99a023
+	int touch_num;
d99a023
+	int error;
d99a023
+
d99a023
+	error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, data,
d99a023
+				GOODIX_CONTACT_SIZE + 1);
d99a023
+	if (error) {
d99a023
+		dev_err(&ts->client->dev, "I2C transfer error: %d\n", error);
d99a023
+		return error;
d99a023
+	}
d99a023
+
d99a023
+	touch_num = data[0] & 0x0f;
d99a023
+	if (touch_num > GOODIX_MAX_CONTACTS)
d99a023
+		return -EPROTO;
d99a023
+
d99a023
+	if (touch_num > 1) {
d99a023
+		data += 1 + GOODIX_CONTACT_SIZE;
d99a023
+		error = goodix_i2c_read(ts->client,
d99a023
+					GOODIX_READ_COOR_ADDR +
d99a023
+						1 + GOODIX_CONTACT_SIZE,
d99a023
+					data,
d99a023
+					GOODIX_CONTACT_SIZE * (touch_num - 1));
d99a023
+		if (error)
d99a023
+			return error;
d99a023
+	}
d99a023
+
d99a023
+	return touch_num;
d99a023
+}
d99a023
+
d99a023
+static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
d99a023
+{
d99a023
+	int id = coor_data[0] & 0x0F;
d99a023
+	int input_x = get_unaligned_le16(&coor_data[1]);
d99a023
+	int input_y = get_unaligned_le16(&coor_data[3]);
d99a023
+	int input_w = get_unaligned_le16(&coor_data[5]);
d99a023
+
d99a023
+	input_mt_slot(ts->input_dev, id);
d99a023
+	input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
d99a023
+	input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
d99a023
+	input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
d99a023
+	input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
d99a023
+	input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
d99a023
+}
d99a023
+
d99a023
+/**
d99a023
+ * goodix_process_events - Process incoming events
d99a023
+ *
d99a023
+ * @ts: our goodix_ts_data pointer
d99a023
+ *
d99a023
+ * Called when the IRQ is triggered. Read the current device state, and push
d99a023
+ * the input events to the user space.
d99a023
+ */
d99a023
+static void goodix_process_events(struct goodix_ts_data *ts)
d99a023
+{
d99a023
+	u8  point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
d99a023
+	int touch_num;
d99a023
+	int i;
d99a023
+
d99a023
+	touch_num = goodix_ts_read_input_report(ts, point_data);
d99a023
+	if (touch_num < 0)
d99a023
+		return;
d99a023
+
d99a023
+	for (i = 0; i < touch_num; i++)
d99a023
+		goodix_ts_report_touch(ts,
d99a023
+				&point_data[1 + GOODIX_CONTACT_SIZE * i]);
d99a023
+
d99a023
+	input_mt_sync_frame(ts->input_dev);
d99a023
+	input_sync(ts->input_dev);
d99a023
+}
d99a023
+
d99a023
+/**
d99a023
+ * goodix_ts_irq_handler - The IRQ handler
d99a023
+ *
d99a023
+ * @irq: interrupt number.
d99a023
+ * @dev_id: private data pointer.
d99a023
+ */
d99a023
+static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
d99a023
+{
d99a023
+	static const u8 end_cmd[] = {
d99a023
+		GOODIX_READ_COOR_ADDR >> 8,
d99a023
+		GOODIX_READ_COOR_ADDR & 0xff,
d99a023
+		0
d99a023
+	};
d99a023
+	struct goodix_ts_data *ts = dev_id;
d99a023
+
d99a023
+	goodix_process_events(ts);
d99a023
+
d99a023
+	if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
d99a023
+		dev_err(&ts->client->dev, "I2C write end_cmd error\n");
d99a023
+
d99a023
+	return IRQ_HANDLED;
d99a023
+}
d99a023
+
d99a023
+/**
d99a023
+ * goodix_read_config - Read the embedded configuration of the panel
d99a023
+ *
d99a023
+ * @ts: our goodix_ts_data pointer
d99a023
+ *
d99a023
+ * Must be called during probe
d99a023
+ */
d99a023
+static void goodix_read_config(struct goodix_ts_data *ts)
d99a023
+{
d99a023
+	u8 config[GOODIX_CONFIG_MAX_LENGTH];
d99a023
+	int error;
d99a023
+
d99a023
+	error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
d99a023
+			      config,
d99a023
+			   GOODIX_CONFIG_MAX_LENGTH);
d99a023
+	if (error) {
d99a023
+		dev_warn(&ts->client->dev,
d99a023
+			 "Error reading config (%d), using defaults\n",
d99a023
+			 error);
d99a023
+		ts->abs_x_max = GOODIX_MAX_WIDTH;
d99a023
+		ts->abs_y_max = GOODIX_MAX_HEIGHT;
d99a023
+		ts->int_trigger_type = GOODIX_INT_TRIGGER;
d99a023
+		return;
d99a023
+	}
d99a023
+
d99a023
+	ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
d99a023
+	ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
d99a023
+	ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
d99a023
+	if (!ts->abs_x_max || !ts->abs_y_max) {
d99a023
+		dev_err(&ts->client->dev,
d99a023
+			"Invalid config, using defaults\n");
d99a023
+		ts->abs_x_max = GOODIX_MAX_WIDTH;
d99a023
+		ts->abs_y_max = GOODIX_MAX_HEIGHT;
d99a023
+	}
d99a023
+}
d99a023
+
d99a023
+
d99a023
+/**
d99a023
+ * goodix_read_version - Read goodix touchscreen version
d99a023
+ *
d99a023
+ * @client: the i2c client
d99a023
+ * @version: output buffer containing the version on success
d99a023
+ */
d99a023
+static int goodix_read_version(struct i2c_client *client, u16 *version)
d99a023
+{
d99a023
+	int error;
d99a023
+	u8 buf[6];
d99a023
+
d99a023
+	error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf));
d99a023
+	if (error) {
d99a023
+		dev_err(&client->dev, "read version failed: %d\n", error);
d99a023
+		return error;
d99a023
+	}
d99a023
+
d99a023
+	if (version)
d99a023
+		*version = get_unaligned_le16(&buf[4]);
d99a023
+
d99a023
+	dev_info(&client->dev, "IC VERSION: %6ph\n", buf);
d99a023
+
d99a023
+	return 0;
d99a023
+}
d99a023
+
d99a023
+/**
d99a023
+ * goodix_i2c_test - I2C test function to check if the device answers.
d99a023
+ *
d99a023
+ * @client: the i2c client
d99a023
+ */
d99a023
+static int goodix_i2c_test(struct i2c_client *client)
d99a023
+{
d99a023
+	int retry = 0;
d99a023
+	int error;
d99a023
+	u8 test;
d99a023
+
d99a023
+	while (retry++ < 2) {
d99a023
+		error = goodix_i2c_read(client, GOODIX_REG_CONFIG_DATA,
d99a023
+					&test, 1);
d99a023
+		if (!error)
d99a023
+			return 0;
d99a023
+
d99a023
+		dev_err(&client->dev, "i2c test failed attempt %d: %d\n",
d99a023
+			retry, error);
d99a023
+		msleep(20);
d99a023
+	}
d99a023
+
d99a023
+	return error;
d99a023
+}
d99a023
+
d99a023
+/**
d99a023
+ * goodix_request_input_dev - Allocate, populate and register the input device
d99a023
+ *
d99a023
+ * @ts: our goodix_ts_data pointer
d99a023
+ *
d99a023
+ * Must be called during probe
d99a023
+ */
d99a023
+static int goodix_request_input_dev(struct goodix_ts_data *ts)
d99a023
+{
d99a023
+	int error;
d99a023
+
d99a023
+	ts->input_dev = devm_input_allocate_device(&ts->client->dev);
d99a023
+	if (!ts->input_dev) {
d99a023
+		dev_err(&ts->client->dev, "Failed to allocate input device.");
d99a023
+		return -ENOMEM;
d99a023
+	}
d99a023
+
d99a023
+	ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) |
d99a023
+				  BIT_MASK(EV_KEY) |
d99a023
+				  BIT_MASK(EV_ABS);
d99a023
+
d99a023
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0,
d99a023
+				ts->abs_x_max, 0, 0);
d99a023
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0,
d99a023
+				ts->abs_y_max, 0, 0);
d99a023
+	input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
d99a023
+	input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
d99a023
+
d99a023
+	input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS,
d99a023
+			    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
d99a023
+
d99a023
+	ts->input_dev->name = "Goodix Capacitive TouchScreen";
d99a023
+	ts->input_dev->phys = "input/ts";
d99a023
+	ts->input_dev->id.bustype = BUS_I2C;
d99a023
+	ts->input_dev->id.vendor = 0x0416;
d99a023
+	ts->input_dev->id.product = 0x1001;
d99a023
+	ts->input_dev->id.version = 10427;
d99a023
+
d99a023
+	error = input_register_device(ts->input_dev);
d99a023
+	if (error) {
d99a023
+		dev_err(&ts->client->dev,
d99a023
+			"Failed to register input device: %d", error);
d99a023
+		return error;
d99a023
+	}
d99a023
+
d99a023
+	return 0;
d99a023
+}
d99a023
+
d99a023
+static int goodix_ts_probe(struct i2c_client *client,
d99a023
+			   const struct i2c_device_id *id)
d99a023
+{
d99a023
+	struct goodix_ts_data *ts;
d99a023
+	unsigned long irq_flags;
d99a023
+	int error;
d99a023
+	u16 version_info;
d99a023
+
d99a023
+	dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
d99a023
+
d99a023
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
d99a023
+		dev_err(&client->dev, "I2C check functionality failed.\n");
d99a023
+		return -ENXIO;
d99a023
+	}
d99a023
+
d99a023
+	ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
d99a023
+	if (!ts)
d99a023
+		return -ENOMEM;
d99a023
+
d99a023
+	ts->client = client;
d99a023
+	i2c_set_clientdata(client, ts);
d99a023
+
d99a023
+	error = goodix_i2c_test(client);
d99a023
+	if (error) {
d99a023
+		dev_err(&client->dev, "I2C communication failure: %d\n", error);
d99a023
+		return error;
d99a023
+	}
d99a023
+
d99a023
+	error = goodix_read_version(client, &version_info);
d99a023
+	if (error) {
d99a023
+		dev_err(&client->dev, "Read version failed.\n");
d99a023
+		return error;
d99a023
+	}
d99a023
+
d99a023
+	goodix_read_config(ts);
d99a023
+
d99a023
+	error = goodix_request_input_dev(ts);
d99a023
+	if (error)
d99a023
+		return error;
d99a023
+
d99a023
+	irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
d99a023
+	error = devm_request_threaded_irq(&ts->client->dev, client->irq,
d99a023
+					  NULL, goodix_ts_irq_handler,
d99a023
+					  irq_flags, client->name, ts);
d99a023
+	if (error) {
d99a023
+		dev_err(&client->dev, "request IRQ failed: %d\n", error);
d99a023
+		return error;
d99a023
+	}
d99a023
+
d99a023
+	return 0;
d99a023
+}
d99a023
+
d99a023
+static const struct i2c_device_id goodix_ts_id[] = {
d99a023
+	{ "GDIX1001:00", 0 },
d99a023
+	{ }
d99a023
+};
d99a023
+
d99a023
+static const struct acpi_device_id goodix_acpi_match[] = {
d99a023
+	{ "GDIX1001", 0 },
d99a023
+	{ }
d99a023
+};
d99a023
+MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
d99a023
+
d99a023
+static struct i2c_driver goodix_ts_driver = {
d99a023
+	.probe = goodix_ts_probe,
d99a023
+	.id_table = goodix_ts_id,
d99a023
+	.driver = {
d99a023
+		.name = "Goodix-TS",
d99a023
+		.owner = THIS_MODULE,
d99a023
+		.acpi_match_table = goodix_acpi_match,
d99a023
+	},
d99a023
+};
d99a023
+module_i2c_driver(goodix_ts_driver);
d99a023
+
d99a023
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
d99a023
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
d99a023
+MODULE_DESCRIPTION("Goodix touchscreen driver");
d99a023
+MODULE_LICENSE("GPL v2");
d99a023
-- 
d99a023
1.9.3
d99a023