diff options
author | Adam Williamson <awilliam@redhat.com> | 2014-11-08 14:59:30 -0800 |
---|---|---|
committer | Adam Williamson <awilliam@redhat.com> | 2014-11-08 14:59:30 -0800 |
commit | 89b19396f6c5dd09eaf8f44bcee27d42a2da1502 (patch) | |
tree | 317c2721f3a641d3a9308c512532bfb384a4df06 /Input-add-driver-for-the-Goodix-touchpanel.patch | |
parent | c2e2a0849b034d919e0c2e27c36d77697579eb6b (diff) | |
parent | b1223bfd6a6dd0bec9b3c0df7b286b22bcc35257 (diff) | |
download | kernel-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.patch | 479 |
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 + |