projects/imx6/patches/linux: fix driver for st1232 touchscreen controller it can work as singletouch or multitouch in singletouch mode added touch button event

This commit is contained in:
vpeter4 2015-06-04 08:55:01 +02:00 committed by Stephan Raue
parent c67d2a2502
commit 02a76526f0

View File

@ -0,0 +1,640 @@
From 8a7ac9c15a3e6e25f8a3238c26a2584d84c6b38f Mon Sep 17 00:00:00 2001
From: vpeter4 <peter.vicman@gmail.com>
Date: Wed, 3 Jun 2015 18:15:31 +0200
Subject: [PATCH] update driver for st1232 touchscreen controller
Original driver is multitouch only and Kodi doesnt understand
this input events. Driver was modified to start as singletouch
and also supports touch button in this mode.
---
drivers/input/touchscreen/st1232.c | 554 ++++++++++++++++++++++---------------
1 file changed, 331 insertions(+), 223 deletions(-)
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index 5c342b3..e1254a1 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -1,12 +1,13 @@
/*
- * ST1232 Touchscreen Controller Driver
+ * ST1232/ST1332 Touchscreen Controller Driver
*
* Copyright (C) 2010 Renesas Solutions Corp.
- * Tony SIM <chinyeow.sim.xt@renesas.com>
+ * Tony SIM <chinyeow.sim.xt@renesas.com>
+ * Copyright (C) 2015 Peter Vicman <peter.vicman@gmail.com>
*
* Using code from:
* - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c
- * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2007 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -30,282 +31,383 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/platform_data/st1232_pdata.h>
-
-#define ST1232_TS_NAME "st1232-ts"
-
-#define MIN_X 0x00
-#define MIN_Y 0x00
-#define MAX_X 0x31f /* (800 - 1) */
-#define MAX_Y 0x1df /* (480 - 1) */
-#define MAX_AREA 0xff
-#define MAX_FINGERS 2
+#include <linux/workqueue.h>
+
+#define ST1232_TS_NAME "st1232-ts"
+#define MIN_X 0
+#define MIN_Y 0
+#define MAX_X (800 - 1)
+#define MAX_Y (480 - 1)
+#define MAX_AREA 0xff
+#define MAX_FINGERS 2
+#define SAMPLE_DELAY 20 /* msecs */
+#define REVERSE_X(x) if (reverse_x == true) { x = MAX_X - (x); } else {}
+#define REVERSE_Y(y) if (reverse_y == true) { y = MAX_Y - (y); } else {}
struct st1232_ts_finger {
- u16 x;
- u16 y;
- u8 t;
- bool is_valid;
+ u16 x;
+ u16 y;
+ u8 t;
+ bool is_valid;
};
struct st1232_ts_data {
- struct i2c_client *client;
- struct input_dev *input_dev;
- struct st1232_ts_finger finger[MAX_FINGERS];
- struct dev_pm_qos_request low_latency_req;
- int reset_gpio;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct st1232_ts_finger finger[MAX_FINGERS];
+ struct dev_pm_qos_request low_latency_req;
+ int reset_gpio;
+ struct delayed_work work;
};
+static bool multitouch = false;
+module_param(multitouch, bool, 0);
+MODULE_PARM_DESC(multitouch, " If multitouch is set to 1 ts acts as multitouch");
+
+static bool reverse_x = false;
+module_param(reverse_x, bool, 0600);
+MODULE_PARM_DESC(reverse_x, " If reverse_x is set to 1 x coordinates are reversed");
+
+static bool reverse_y = false;
+module_param(reverse_y, bool, 0600);
+MODULE_PARM_DESC(reverse_y, " If reverse_y is set to 1 y coordinates are reversed");
+
+static int offset_x = 0;
+module_param(offset_x, int, 0600);
+MODULE_PARM_DESC(offset_x, " Offset value for x axis");
+
+static int offset_y = 0;
+module_param(offset_y, int, 0600);
+MODULE_PARM_DESC(offset_y, " Offset value for y axis");
+
static int st1232_ts_read_data(struct st1232_ts_data *ts)
{
- struct st1232_ts_finger *finger = ts->finger;
- struct i2c_client *client = ts->client;
- struct i2c_msg msg[2];
- int error;
- u8 start_reg;
- u8 buf[10];
-
- /* read touchscreen data from ST1232 */
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 1;
- msg[0].buf = &start_reg;
- start_reg = 0x10;
-
- msg[1].addr = ts->client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = sizeof(buf);
- msg[1].buf = buf;
-
- error = i2c_transfer(client->adapter, msg, 2);
- if (error < 0)
- return error;
-
- /* get "valid" bits */
- finger[0].is_valid = buf[2] >> 7;
- finger[1].is_valid = buf[5] >> 7;
-
- /* get xy coordinate */
- if (finger[0].is_valid) {
- finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
- finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
- finger[0].t = buf[8];
- }
-
- if (finger[1].is_valid) {
- finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
- finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
- finger[1].t = buf[9];
- }
-
- return 0;
+ struct st1232_ts_finger *finger = ts->finger;
+ struct i2c_client *client = ts->client;
+ struct i2c_msg msg[2];
+ int error;
+ u8 start_reg;
+ u8 buf[10];
+
+ /* read touchscreen data from ST1232 */
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &start_reg;
+ start_reg = 0x10;
+
+ msg[1].addr = ts->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = sizeof(buf);
+ msg[1].buf = buf;
+
+ error = i2c_transfer(client->adapter, msg, 2);
+ if (error < 0)
+ return error;
+
+ memset(finger, 0x0, sizeof(struct st1232_ts_finger) * MAX_FINGERS);
+
+ /* get "valid" bits from fingers
+ and combine with "valid" bits from coordinates */
+ finger[0].is_valid = (buf[0] & 0x07); /* only 3 bits on st1332 */
+ finger[0].is_valid &= (buf[2] >> 7);
+ finger[1].is_valid = (buf[0] & 0x07);
+ finger[1].is_valid &= (buf[5] >> 7);
+
+ /* get xy coordinates and strength */
+ if (finger[0].is_valid) {
+ finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
+ finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
+ finger[0].t = buf[8];
+
+ REVERSE_X(finger[0].x)
+ REVERSE_Y(finger[0].y)
+
+ finger[0].x += offset_x;
+ finger[0].y += offset_y;
+ }
+
+ if (finger[1].is_valid) {
+ finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
+ finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
+ finger[1].t = buf[9];
+
+ REVERSE_X(finger[1].x)
+ REVERSE_Y(finger[1].y)
+ }
+
+ return 0;
+}
+
+static void st1232_ts_finger_released(struct work_struct *work)
+{
+ struct st1232_ts_data *ts = container_of(work, struct st1232_ts_data, work.work);
+ struct st1232_ts_finger *finger = ts->finger;
+ struct input_dev *input_dev = ts->input_dev;
+ int ret;
+
+ ret = st1232_ts_read_data(ts);
+ if (ret < 0)
+ goto end;
+
+ /* finger is a pointer to finger[0] */
+ if (finger->is_valid)
+ goto end; /* finger (still) touched */
+
+ /* finger released */
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_sync(input_dev);
+
+end:
+ return;
}
static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
{
- struct st1232_ts_data *ts = dev_id;
- struct st1232_ts_finger *finger = ts->finger;
- struct input_dev *input_dev = ts->input_dev;
- int count = 0;
- int i, ret;
-
- ret = st1232_ts_read_data(ts);
- if (ret < 0)
- goto end;
-
- /* multi touch protocol */
- for (i = 0; i < MAX_FINGERS; i++) {
- if (!finger[i].is_valid)
- continue;
-
- input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
- input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
- input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
- input_mt_sync(input_dev);
- count++;
- }
-
- /* SYN_MT_REPORT only if no contact */
- if (!count) {
- input_mt_sync(input_dev);
- if (ts->low_latency_req.dev) {
- dev_pm_qos_remove_request(&ts->low_latency_req);
- ts->low_latency_req.dev = NULL;
- }
- } else if (!ts->low_latency_req.dev) {
- /* First contact, request 100 us latency. */
- dev_pm_qos_add_ancestor_request(&ts->client->dev,
- &ts->low_latency_req, 100);
- }
-
- /* SYN_REPORT */
- input_sync(input_dev);
+ struct st1232_ts_data *ts = dev_id;
+ struct st1232_ts_finger *finger = ts->finger;
+ struct input_dev *input_dev = ts->input_dev;
+ int count = 0;
+ int i, ret;
+
+ if (multitouch == false) {
+ /*
+ * Cancel scheduled polling for release if we have new value
+ * available. Wait if the polling is already running.
+ */
+ cancel_delayed_work_sync(&ts->work);
+ }
+
+ ret = st1232_ts_read_data(ts);
+ if (ret < 0)
+ goto end;
+
+ if (multitouch == false) {
+ if (finger->is_valid) {
+ input_report_abs(input_dev, ABS_X, finger->x);
+ input_report_abs(input_dev, ABS_Y, finger->y);
+ input_report_abs(input_dev, ABS_PRESSURE, finger->t);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_sync(input_dev);
+ }
+ } else {
+ /* multi touch protocol */
+ for (i = 0; i < MAX_FINGERS; i++) {
+ if (!finger[i].is_valid)
+ continue;
+
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
+ input_mt_sync(input_dev);
+ count++;
+ }
+
+ /* SYN_MT_REPORT only if no contact */
+ if (!count) {
+ input_mt_sync(input_dev);
+ if (ts->low_latency_req.dev) {
+ dev_pm_qos_remove_request(&ts->low_latency_req);
+ ts->low_latency_req.dev = NULL;
+ }
+ } else if (!ts->low_latency_req.dev) {
+ /* First contact, request 100 us latency. */
+ dev_pm_qos_add_ancestor_request(&ts->client->dev, &ts->low_latency_req, 100);
+ }
+
+ /* SYN_REPORT */
+ input_sync(input_dev);
+ }
end:
- return IRQ_HANDLED;
+ if (multitouch == false) {
+ /* start polling for st1232_ts_read_data to detect release */
+ schedule_delayed_work(&ts->work, msecs_to_jiffies(SAMPLE_DELAY));
+ }
+
+ return IRQ_HANDLED;
}
static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
{
- if (gpio_is_valid(ts->reset_gpio))
- gpio_direction_output(ts->reset_gpio, poweron);
+ if (gpio_is_valid(ts->reset_gpio))
+ gpio_direction_output(ts->reset_gpio, poweron);
}
static int st1232_ts_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
- struct st1232_ts_data *ts;
- struct st1232_pdata *pdata = dev_get_platdata(&client->dev);
- struct input_dev *input_dev;
- int error;
-
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- dev_err(&client->dev, "need I2C_FUNC_I2C\n");
- return -EIO;
- }
-
- if (!client->irq) {
- dev_err(&client->dev, "no IRQ?\n");
- return -EINVAL;
- }
-
- ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
- if (!ts)
- return -ENOMEM;
-
- input_dev = devm_input_allocate_device(&client->dev);
- if (!input_dev)
- return -ENOMEM;
-
- ts->client = client;
- ts->input_dev = input_dev;
-
- if (pdata)
- ts->reset_gpio = pdata->reset_gpio;
- else if (client->dev.of_node)
- ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
- else
- ts->reset_gpio = -ENODEV;
-
- if (gpio_is_valid(ts->reset_gpio)) {
- error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
- if (error) {
- dev_err(&client->dev,
- "Unable to request GPIO pin %d.\n",
- ts->reset_gpio);
- return error;
- }
- }
-
- st1232_ts_power(ts, true);
-
- input_dev->name = "st1232-touchscreen";
- input_dev->id.bustype = BUS_I2C;
- input_dev->dev.parent = &client->dev;
-
- __set_bit(EV_SYN, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(EV_ABS, input_dev->evbit);
-
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
-
- error = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, st1232_ts_irq_handler,
- IRQF_ONESHOT,
- client->name, ts);
- if (error) {
- dev_err(&client->dev, "Failed to register interrupt\n");
- return error;
- }
-
- error = input_register_device(ts->input_dev);
- if (error) {
- dev_err(&client->dev, "Unable to register %s input device\n",
- input_dev->name);
- return error;
- }
-
- i2c_set_clientdata(client, ts);
- device_init_wakeup(&client->dev, 1);
-
- return 0;
+ struct st1232_ts_data *ts;
+ struct st1232_pdata *pdata = dev_get_platdata(&client->dev);
+ struct input_dev *input_dev;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C\n");
+ return -EIO;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "no IRQ?\n");
+ return -EINVAL;
+ }
+
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&client->dev);
+ if (!input_dev)
+ return -ENOMEM;
+
+ ts->client = client;
+ ts->input_dev = input_dev;
+
+ if (pdata)
+ ts->reset_gpio = pdata->reset_gpio;
+ else if (client->dev.of_node)
+ ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
+ else
+ ts->reset_gpio = -ENODEV;
+
+ if (gpio_is_valid(ts->reset_gpio)) {
+ error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
+ if (error) {
+ dev_err(&client->dev, "Unable to request GPIO pin %d.\n", ts->reset_gpio);
+ return error;
+ }
+ }
+
+ st1232_ts_power(ts, true);
+
+ input_dev->name = ST1232_TS_NAME;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ if (multitouch == false) {
+ input_dev->phys = ST1232_TS_NAME"/input0";
+
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_PRESSURE, input_dev->absbit);
+
+ input_set_abs_params(input_dev, ABS_X, MIN_X, MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, MIN_Y, MAX_Y, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0x0, 0xff, 0, 0);
+
+ INIT_DELAYED_WORK(&ts->work, st1232_ts_finger_released);
+ } else {
+ __set_bit(EV_SYN, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
+ }
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, st1232_ts_irq_handler,
+ IRQF_ONESHOT,
+ client->name, ts);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ return error;
+ }
+
+ error = input_register_device(ts->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Unable to register %s input device\n",
+ input_dev->name);
+ return error;
+ }
+
+ i2c_set_clientdata(client, ts);
+ device_init_wakeup(&client->dev, 1);
+
+ return 0;
}
static int st1232_ts_remove(struct i2c_client *client)
{
- struct st1232_ts_data *ts = i2c_get_clientdata(client);
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
- device_init_wakeup(&client->dev, 0);
- st1232_ts_power(ts, false);
+ device_init_wakeup(&client->dev, 0);
+ st1232_ts_power(ts, false);
+ cancel_delayed_work_sync(&ts->work);
- return 0;
+ return 0;
}
#ifdef CONFIG_PM_SLEEP
static int st1232_ts_suspend(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct st1232_ts_data *ts = i2c_get_clientdata(client);
-
- if (device_may_wakeup(&client->dev)) {
- enable_irq_wake(client->irq);
- } else {
- disable_irq(client->irq);
- st1232_ts_power(ts, false);
- }
-
- return 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev)) {
+ enable_irq_wake(client->irq);
+ } else {
+ disable_irq(client->irq);
+ cancel_delayed_work_sync(&ts->work);
+ st1232_ts_power(ts, false);
+ }
+
+ return 0;
}
static int st1232_ts_resume(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct st1232_ts_data *ts = i2c_get_clientdata(client);
-
- if (device_may_wakeup(&client->dev)) {
- disable_irq_wake(client->irq);
- } else {
- st1232_ts_power(ts, true);
- enable_irq(client->irq);
- }
-
- return 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct st1232_ts_data *ts = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev)) {
+ disable_irq_wake(client->irq);
+ } else {
+ st1232_ts_power(ts, true);
+ schedule_delayed_work(&ts->work, HZ / 50);
+ enable_irq(client->irq);
+ }
+
+ return 0;
}
-
#endif
static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
- st1232_ts_suspend, st1232_ts_resume);
+ st1232_ts_suspend, st1232_ts_resume);
static const struct i2c_device_id st1232_ts_id[] = {
- { ST1232_TS_NAME, 0 },
- { }
+ { ST1232_TS_NAME, 0 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
#ifdef CONFIG_OF
static const struct of_device_id st1232_ts_dt_ids[] = {
- { .compatible = "sitronix,st1232", },
- { }
+ { .compatible = "sitronix,st1232", },
+ { }
};
MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
#endif
static struct i2c_driver st1232_ts_driver = {
- .probe = st1232_ts_probe,
- .remove = st1232_ts_remove,
- .id_table = st1232_ts_id,
- .driver = {
- .name = ST1232_TS_NAME,
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(st1232_ts_dt_ids),
- .pm = &st1232_ts_pm_ops,
- },
+ .probe = st1232_ts_probe,
+ .remove = st1232_ts_remove,
+ .id_table = st1232_ts_id,
+ .driver = {
+ .name = ST1232_TS_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(st1232_ts_dt_ids),
+ .pm = &st1232_ts_pm_ops,
+ },
};
module_i2c_driver(st1232_ts_driver);
-MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
-MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
+MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>, Peter Vicman <peter.vicman@gmail.com>");
+MODULE_DESCRIPTION("SITRONIX ST1232/ST1332 Touchscreen Controller Driver");
MODULE_LICENSE("GPL");
--
1.8.1.2