diff --git a/projects/imx6/patches/linux/linux-332-udoo-fix-st1232-driver.patch b/projects/imx6/patches/linux/linux-332-udoo-fix-st1232-driver.patch new file mode 100644 index 0000000000..fc6f91be5b --- /dev/null +++ b/projects/imx6/patches/linux/linux-332-udoo-fix-st1232-driver.patch @@ -0,0 +1,640 @@ +From 8a7ac9c15a3e6e25f8a3238c26a2584d84c6b38f Mon Sep 17 00:00:00 2001 +From: vpeter4 +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 ++ * Tony SIM ++ * Copyright (C) 2015 Peter Vicman + * + * 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 + #include + #include +- +-#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 ++ ++#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 "); +-MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); ++MODULE_AUTHOR("Tony SIM , Peter Vicman "); ++MODULE_DESCRIPTION("SITRONIX ST1232/ST1332 Touchscreen Controller Driver"); + MODULE_LICENSE("GPL"); +-- +1.8.1.2