summaryrefslogtreecommitdiffstats
path: root/Input-add-driver-for-the-Goodix-touchpanel.patch
diff options
context:
space:
mode:
authorAdam Williamson <awilliam@redhat.com>2014-11-08 14:59:30 -0800
committerAdam Williamson <awilliam@redhat.com>2014-11-08 14:59:30 -0800
commit89b19396f6c5dd09eaf8f44bcee27d42a2da1502 (patch)
tree317c2721f3a641d3a9308c512532bfb384a4df06 /Input-add-driver-for-the-Goodix-touchpanel.patch
parentc2e2a0849b034d919e0c2e27c36d77697579eb6b (diff)
parentb1223bfd6a6dd0bec9b3c0df7b286b22bcc35257 (diff)
downloadkernel-89b19396f6c5dd09eaf8f44bcee27d42a2da1502.tar.gz
kernel-89b19396f6c5dd09eaf8f44bcee27d42a2da1502.tar.xz
kernel-89b19396f6c5dd09eaf8f44bcee27d42a2da1502.zip
Merge branch 'master' into baytrail (3.18 bump)
Diffstat (limited to 'Input-add-driver-for-the-Goodix-touchpanel.patch')
-rw-r--r--Input-add-driver-for-the-Goodix-touchpanel.patch479
1 files changed, 479 insertions, 0 deletions
diff --git a/Input-add-driver-for-the-Goodix-touchpanel.patch b/Input-add-driver-for-the-Goodix-touchpanel.patch
new file mode 100644
index 000000000..33bb3b28d
--- /dev/null
+++ b/Input-add-driver-for-the-Goodix-touchpanel.patch
@@ -0,0 +1,479 @@
+From: Bastien Nocera <hadess@hadess.net>
+Date: Fri, 31 Oct 2014 09:26:16 -0700
+Subject: [PATCH] Input: add driver for the Goodix touchpanel
+
+Add a driver for the Goodix touchscreen panel found in Onda v975w tablets.
+The driver is based off the Android driver gt9xx.c found in some Android
+code dumps, but now bears no resemblance to the original driver.
+
+The driver was tested on the aforementioned tablet.
+
+Signed-off-by: Bastien Nocera <hadess@hadess.net>
+Tested-by: Bastien Nocera <hadess@hadess.net>
+Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+---
+ MAINTAINERS | 6 +
+ drivers/input/touchscreen/Kconfig | 13 ++
+ drivers/input/touchscreen/Makefile | 1 +
+ drivers/input/touchscreen/goodix.c | 395 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 415 insertions(+)
+ create mode 100644 drivers/input/touchscreen/goodix.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 5d6136b8959e..cf7a1a5d2ac9 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -4139,6 +4139,12 @@ L: linux-media@vger.kernel.org
+ S: Maintained
+ F: drivers/media/usb/go7007/
+
++GOODIX TOUCHSCREEN
++M: Bastien Nocera <hadess@hadess.net>
++L: linux-input@vger.kernel.org
++S: Maintained
++F: drivers/input/touchscreen/goodix.c
++
+ GPIO SUBSYSTEM
+ M: Linus Walleij <linus.walleij@linaro.org>
+ M: Alexandre Courbot <gnurou@gmail.com>
+diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+index e1d8003d01f8..568a0200fbc2 100644
+--- a/drivers/input/touchscreen/Kconfig
++++ b/drivers/input/touchscreen/Kconfig
+@@ -295,6 +295,19 @@ config TOUCHSCREEN_FUJITSU
+ To compile this driver as a module, choose M here: the
+ module will be called fujitsu-ts.
+
++config TOUCHSCREEN_GOODIX
++ tristate "Goodix I2C touchscreen"
++ depends on I2C && ACPI
++ help
++ Say Y here if you have the Goodix touchscreen (such as one
++ installed in Onda v975w tablets) connected to your
++ system.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called goodix.
++
+ config TOUCHSCREEN_ILI210X
+ tristate "Ilitek ILI210X based touchscreen"
+ depends on I2C
+diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+index 090e61cc9171..dab4a56ac98e 100644
+--- a/drivers/input/touchscreen/Makefile
++++ b/drivers/input/touchscreen/Makefile
+@@ -34,6 +34,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
++obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
+ obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
+ obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
+ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
+new file mode 100644
+index 000000000000..ca196689f025
+--- /dev/null
++++ b/drivers/input/touchscreen/goodix.c
+@@ -0,0 +1,395 @@
++/*
++ * Driver for Goodix Touchscreens
++ *
++ * Copyright (c) 2014 Red Hat Inc.
++ *
++ * This code is based on gt9xx.c authored by andrew@goodix.com:
++ *
++ * 2010 - 2012 Goodix Technology.
++ */
++
++/*
++ * 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 <linux/kernel.h>
++#include <linux/i2c.h>
++#include <linux/input.h>
++#include <linux/input/mt.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <asm/unaligned.h>
++
++struct goodix_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_CONTACT_SIZE 8
++#define GOODIX_MAX_CONTACTS 10
++
++#define GOODIX_CONFIG_MAX_LENGTH 240
++
++/* Register defines */
++#define GOODIX_READ_COOR_ADDR 0x814E
++#define GOODIX_REG_CONFIG_DATA 0x8047
++#define GOODIX_REG_VERSION 0x8140
++
++#define RESOLUTION_LOC 1
++#define TRIGGER_LOC 6
++
++static const unsigned long goodix_irq_flags[] = {
++ IRQ_TYPE_EDGE_RISING,
++ IRQ_TYPE_EDGE_FALLING,
++ IRQ_TYPE_LEVEL_LOW,
++ IRQ_TYPE_LEVEL_HIGH,
++};
++
++/**
++ * goodix_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 goodix_i2c_read(struct i2c_client *client,
++ u16 reg, u8 *buf, int len)
++{
++ struct i2c_msg msgs[2];
++ u16 wbuf = cpu_to_be16(reg);
++ int ret;
++
++ msgs[0].flags = 0;
++ msgs[0].addr = client->addr;
++ msgs[0].len = 2;
++ msgs[0].buf = (u8 *) &wbuf;
++
++ 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);
++}
++
++static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
++{
++ int touch_num;
++ int error;
++
++ error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, data,
++ GOODIX_CONTACT_SIZE + 1);
++ if (error) {
++ dev_err(&ts->client->dev, "I2C transfer error: %d\n", error);
++ return error;
++ }
++
++ touch_num = data[0] & 0x0f;
++ if (touch_num > GOODIX_MAX_CONTACTS)
++ return -EPROTO;
++
++ if (touch_num > 1) {
++ data += 1 + GOODIX_CONTACT_SIZE;
++ error = goodix_i2c_read(ts->client,
++ GOODIX_READ_COOR_ADDR +
++ 1 + GOODIX_CONTACT_SIZE,
++ data,
++ GOODIX_CONTACT_SIZE * (touch_num - 1));
++ if (error)
++ return error;
++ }
++
++ return touch_num;
++}
++
++static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
++{
++ int id = coor_data[0] & 0x0F;
++ int input_x = get_unaligned_le16(&coor_data[1]);
++ int input_y = get_unaligned_le16(&coor_data[3]);
++ int input_w = get_unaligned_le16(&coor_data[5]);
++
++ input_mt_slot(ts->input_dev, id);
++ 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);
++}
++
++/**
++ * goodix_process_events - Process incoming events
++ *
++ * @ts: our goodix_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 goodix_process_events(struct goodix_ts_data *ts)
++{
++ u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
++ int touch_num;
++ int i;
++
++ touch_num = goodix_ts_read_input_report(ts, point_data);
++ if (touch_num < 0)
++ return;
++
++ for (i = 0; i < touch_num; i++)
++ goodix_ts_report_touch(ts,
++ &point_data[1 + GOODIX_CONTACT_SIZE * i]);
++
++ input_mt_sync_frame(ts->input_dev);
++ input_sync(ts->input_dev);
++}
++
++/**
++ * goodix_ts_irq_handler - The IRQ handler
++ *
++ * @irq: interrupt number.
++ * @dev_id: private data pointer.
++ */
++static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
++{
++ static const u8 end_cmd[] = {
++ GOODIX_READ_COOR_ADDR >> 8,
++ GOODIX_READ_COOR_ADDR & 0xff,
++ 0
++ };
++ struct goodix_ts_data *ts = dev_id;
++
++ goodix_process_events(ts);
++
++ if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
++ dev_err(&ts->client->dev, "I2C write end_cmd error\n");
++
++ return IRQ_HANDLED;
++}
++
++/**
++ * goodix_read_config - Read the embedded configuration of the panel
++ *
++ * @ts: our goodix_ts_data pointer
++ *
++ * Must be called during probe
++ */
++static void goodix_read_config(struct goodix_ts_data *ts)
++{
++ u8 config[GOODIX_CONFIG_MAX_LENGTH];
++ int error;
++
++ error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
++ config,
++ GOODIX_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;
++ return;
++ }
++
++ ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
++ ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
++ ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
++ if (!ts->abs_x_max || !ts->abs_y_max) {
++ dev_err(&ts->client->dev,
++ "Invalid config, using defaults\n");
++ ts->abs_x_max = GOODIX_MAX_WIDTH;
++ ts->abs_y_max = GOODIX_MAX_HEIGHT;
++ }
++}
++
++
++/**
++ * goodix_read_version - Read goodix touchscreen version
++ *
++ * @client: the i2c client
++ * @version: output buffer containing the version on success
++ */
++static int goodix_read_version(struct i2c_client *client, u16 *version)
++{
++ int error;
++ u8 buf[6];
++
++ error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf));
++ if (error) {
++ dev_err(&client->dev, "read version failed: %d\n", error);
++ return error;
++ }
++
++ if (version)
++ *version = get_unaligned_le16(&buf[4]);
++
++ dev_info(&client->dev, "IC VERSION: %6ph\n", buf);
++
++ return 0;
++}
++
++/**
++ * goodix_i2c_test - I2C test function to check if the device answers.
++ *
++ * @client: the i2c client
++ */
++static int goodix_i2c_test(struct i2c_client *client)
++{
++ int retry = 0;
++ int error;
++ u8 test;
++
++ while (retry++ < 2) {
++ error = goodix_i2c_read(client, GOODIX_REG_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;
++}
++
++/**
++ * goodix_request_input_dev - Allocate, populate and register the input device
++ *
++ * @ts: our goodix_ts_data pointer
++ *
++ * Must be called during probe
++ */
++static int goodix_request_input_dev(struct goodix_ts_data *ts)
++{
++ 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;
++ }
++
++ ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) |
++ BIT_MASK(EV_KEY) |
++ BIT_MASK(EV_ABS);
++
++ 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, GOODIX_MAX_CONTACTS,
++ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
++
++ ts->input_dev->name = "Goodix Capacitive TouchScreen";
++ ts->input_dev->phys = "input/ts";
++ ts->input_dev->id.bustype = BUS_I2C;
++ ts->input_dev->id.vendor = 0x0416;
++ ts->input_dev->id.product = 0x1001;
++ ts->input_dev->id.version = 10427;
++
++ 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 goodix_ts_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct goodix_ts_data *ts;
++ unsigned long irq_flags;
++ int error;
++ u16 version_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 = goodix_i2c_test(client);
++ if (error) {
++ dev_err(&client->dev, "I2C communication failure: %d\n", error);
++ return error;
++ }
++
++ error = goodix_read_version(client, &version_info);
++ if (error) {
++ dev_err(&client->dev, "Read version failed.\n");
++ return error;
++ }
++
++ goodix_read_config(ts);
++
++ error = goodix_request_input_dev(ts);
++ 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, goodix_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 goodix_ts_id[] = {
++ { "GDIX1001:00", 0 },
++ { }
++};
++
++static const struct acpi_device_id goodix_acpi_match[] = {
++ { "GDIX1001", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
++
++static struct i2c_driver goodix_ts_driver = {
++ .probe = goodix_ts_probe,
++ .id_table = goodix_ts_id,
++ .driver = {
++ .name = "Goodix-TS",
++ .owner = THIS_MODULE,
++ .acpi_match_table = goodix_acpi_match,
++ },
++};
++module_i2c_driver(goodix_ts_driver);
++
++MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
++MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
++MODULE_DESCRIPTION("Goodix touchscreen driver");
++MODULE_LICENSE("GPL v2");
+--
+1.9.3
+