From eb91ecc9fc43fbefdfdfe1ea8b3f136ccbc02f66 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 11 Aug 2019 09:16:37 -0700 Subject: [PATCH 1/7] Input: ili210x - add ILI2117 support Add support for ILI2117 touch controller. This controller is similar to the ILI210x and ILI251x, except for the following differences: - Reading out of touch data must happen at most 300 mS after the interrupt line was asserted. No command must be sent, the data are returned upon pure I2C read of 43 bytes long. - Supports 10 simultaneous touch inputs. - Touch data format is slightly different. Signed-off-by: Marek Vasut Reviewed-by: Rob Herring # for DT binding Tested-by: Adam Ford #imx6q-logicpd Tested-by: Sven Van Asbroeck # ILI2118A variant Signed-off-by: Dmitry Torokhov --- .../bindings/input/ilitek,ili2xxx.txt | 3 +- drivers/input/touchscreen/ili210x.c | 46 ++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt b/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt index b2a76301e632..dc194b2c151a 100644 --- a/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt +++ b/Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt @@ -1,8 +1,9 @@ -Ilitek ILI210x/ILI251x touchscreen controller +Ilitek ILI210x/ILI2117/ILI251x touchscreen controller Required properties: - compatible: ilitek,ili210x for ILI210x + ilitek,ili2117 for ILI2117 ilitek,ili251x for ILI251x - reg: The I2C address of the device diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index e9006407c9bc..36491d8ff990 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -13,6 +13,7 @@ #include #define ILI210X_TOUCHES 2 +#define ILI211X_TOUCHES 10 #define ILI251X_TOUCHES 10 #define DEFAULT_POLL_PERIOD 20 @@ -30,6 +31,7 @@ struct firmware_version { enum ili2xxx_model { MODEL_ILI210X, + MODEL_ILI211X, MODEL_ILI251X, }; @@ -118,6 +120,27 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, return true; } +static bool ili211x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, + unsigned int finger, + unsigned int *x, unsigned int *y) +{ + u32 data; + + if (finger >= ILI211X_TOUCHES) + return false; + + data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0); + if (data == 0xffffffff) /* Finger up */ + return false; + + *x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) | + touchdata[1 + (finger * 4) + 1]; + *y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) | + touchdata[1 + (finger * 4) + 2]; + + return true; +} + static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) @@ -146,6 +169,9 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) if (priv->model == MODEL_ILI210X) { touch = ili210x_touchdata_to_coords(priv, touchdata, i, &x, &y); + } else if (priv->model == MODEL_ILI211X) { + touch = ili211x_touchdata_to_coords(priv, touchdata, + i, &x, &y); } else if (priv->model == MODEL_ILI251X) { touch = ili251x_touchdata_to_coords(priv, touchdata, i, &x, &y); @@ -176,12 +202,26 @@ static void ili210x_work(struct work_struct *work) dwork.work); struct i2c_client *client = priv->client; u8 touchdata[64] = { 0 }; + s16 sum = 0; bool touch; - int error = -EINVAL; + int i, error = -EINVAL; if (priv->model == MODEL_ILI210X) { error = ili210x_read_reg(client, REG_TOUCHDATA, touchdata, sizeof(touchdata)); + } else if (priv->model == MODEL_ILI211X) { + error = ili210x_read(client, touchdata, 43); + if (!error) { + /* This chip uses custom checksum at the end of data */ + for (i = 0; i <= 41; i++) + sum = (sum + touchdata[i]) & 0xff; + if ((-sum & 0xff) != touchdata[42]) { + dev_err(&client->dev, + "CRC error (crc=0x%02x expected=0x%02x)\n", + sum, touchdata[42]); + return; + } + } } else if (priv->model == MODEL_ILI251X) { error = ili210x_read_reg(client, REG_TOUCHDATA, touchdata, 31); @@ -311,6 +351,8 @@ static int ili210x_i2c_probe(struct i2c_client *client, priv->model = model; if (model == MODEL_ILI210X) priv->max_touches = ILI210X_TOUCHES; + if (model == MODEL_ILI211X) + priv->max_touches = ILI211X_TOUCHES; if (model == MODEL_ILI251X) priv->max_touches = ILI251X_TOUCHES; @@ -395,6 +437,7 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, static const struct i2c_device_id ili210x_i2c_id[] = { { "ili210x", MODEL_ILI210X }, + { "ili2117", MODEL_ILI211X }, { "ili251x", MODEL_ILI251X }, { } }; @@ -402,6 +445,7 @@ MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); static const struct of_device_id ili210x_dt_ids[] = { { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, + { .compatible = "ilitek,ili2117", .data = (void *)MODEL_ILI211X }, { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, { }, }; From 71f8e38ae635d92e553eec8d7359cb88b539b306 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 10 Aug 2019 23:19:24 -0700 Subject: [PATCH 2/7] Input: ili210x - switch to using threaded IRQ Let's switch the driver to using threaded IRQ so that we do not need to manage the interrupt and work separately, and we do not acknowledge interrupt until we finished handling it completely. Tested-by: Adam Ford #imx6q-logicpd Tested-by: Sven Van Asbroeck # ILI2118A variant Tested-by: Marek Vasut # ILI2117 Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 115 +++++++++++++--------------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 36491d8ff990..6985ca8b6565 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -1,21 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-only -#include +#include +#include #include -#include -#include #include #include #include -#include -#include -#include +#include +#include #include +#include #include #define ILI210X_TOUCHES 2 #define ILI211X_TOUCHES 10 #define ILI251X_TOUCHES 10 -#define DEFAULT_POLL_PERIOD 20 + +#define ILI2XXX_POLL_PERIOD 20 /* Touchscreen commands */ #define REG_TOUCHDATA 0x10 @@ -38,12 +38,11 @@ enum ili2xxx_model { struct ili210x { struct i2c_client *client; struct input_dev *input; - unsigned int poll_period; - struct delayed_work dwork; struct gpio_desc *reset_gpio; struct touchscreen_properties prop; enum ili2xxx_model model; unsigned int max_touches; + bool stop; }; static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, @@ -196,57 +195,54 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) return contact; } -static void ili210x_work(struct work_struct *work) +static irqreturn_t ili210x_irq(int irq, void *irq_data) { - struct ili210x *priv = container_of(work, struct ili210x, - dwork.work); + struct ili210x *priv = irq_data; struct i2c_client *client = priv->client; u8 touchdata[64] = { 0 }; s16 sum = 0; bool touch; - int i, error = -EINVAL; + int i; + int error; - if (priv->model == MODEL_ILI210X) { - error = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, sizeof(touchdata)); - } else if (priv->model == MODEL_ILI211X) { - error = ili210x_read(client, touchdata, 43); - if (!error) { - /* This chip uses custom checksum at the end of data */ - for (i = 0; i <= 41; i++) - sum = (sum + touchdata[i]) & 0xff; - if ((-sum & 0xff) != touchdata[42]) { - dev_err(&client->dev, - "CRC error (crc=0x%02x expected=0x%02x)\n", - sum, touchdata[42]); - return; + do { + if (priv->model == MODEL_ILI210X) { + error = ili210x_read_reg(client, REG_TOUCHDATA, + touchdata, sizeof(touchdata)); + } else if (priv->model == MODEL_ILI211X) { + error = ili210x_read(client, touchdata, 43); + if (!error) { + /* + * This chip uses custom checksum at the end + * of data. + */ + for (i = 0; i <= 41; i++) + sum = (sum + touchdata[i]) & 0xff; + if ((-sum & 0xff) != touchdata[42]) { + dev_err(&client->dev, + "CRC error (crc=0x%02x expected=0x%02x)\n", + sum, touchdata[42]); + break; + } } + } else if (priv->model == MODEL_ILI251X) { + error = ili210x_read_reg(client, REG_TOUCHDATA, + touchdata, 31); + if (!error && touchdata[0] == 2) + error = ili210x_read(client, + &touchdata[31], 20); } - } else if (priv->model == MODEL_ILI251X) { - error = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, 31); - if (!error && touchdata[0] == 2) - error = ili210x_read(client, &touchdata[31], 20); - } - if (error) { - dev_err(&client->dev, - "Unable to get touchdata, err = %d\n", error); - return; - } + if (error) { + dev_err(&client->dev, + "Unable to get touchdata, err = %d\n", error); + break; + } - touch = ili210x_report_events(priv, touchdata); - - if (touch) - schedule_delayed_work(&priv->dwork, - msecs_to_jiffies(priv->poll_period)); -} - -static irqreturn_t ili210x_irq(int irq, void *irq_data) -{ - struct ili210x *priv = irq_data; - - schedule_delayed_work(&priv->dwork, 0); + touch = ili210x_report_events(priv, touchdata); + if (touch) + msleep(ILI2XXX_POLL_PERIOD); + } while (!priv->stop && touch); return IRQ_HANDLED; } @@ -293,11 +289,12 @@ static void ili210x_power_down(void *data) gpiod_set_value_cansleep(reset_gpio, 1); } -static void ili210x_cancel_work(void *data) +static void ili210x_stop(void *data) { struct ili210x *priv = data; - cancel_delayed_work_sync(&priv->dwork); + /* Tell ISR to quit even if there is a contact. */ + priv->stop = true; } static int ili210x_i2c_probe(struct i2c_client *client, @@ -345,8 +342,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, priv->client = client; priv->input = input; - priv->poll_period = DEFAULT_POLL_PERIOD; - INIT_DELAYED_WORK(&priv->dwork, ili210x_work); priv->reset_gpio = reset_gpio; priv->model = model; if (model == MODEL_ILI210X) @@ -378,18 +373,18 @@ static int ili210x_i2c_probe(struct i2c_client *client, touchscreen_parse_properties(input, true, &priv->prop); input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); - error = devm_add_action(dev, ili210x_cancel_work, priv); - if (error) - return error; - - error = devm_request_irq(dev, client->irq, ili210x_irq, 0, - client->name, priv); + error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, + IRQF_ONESHOT, client->name, priv); if (error) { dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error); return error; } + error = devm_add_action_or_reset(dev, ili210x_stop, priv); + if (error) + return error; + error = devm_device_add_group(dev, &ili210x_attr_group); if (error) { dev_err(dev, "Unable to create sysfs attributes, err: %d\n", From 43f06a4c639de8ee89fc348a9a3ecd70320a04dd Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 Nov 2019 10:39:41 -0800 Subject: [PATCH 3/7] Input: ili210x - handle errors from input_mt_init_slots() input_mt_init_slots() may fail and we need to handle such failures. Tested-by: Adam Ford #imx6q-logicpd Tested-by: Sven Van Asbroeck # ILI2118A variant Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 6985ca8b6565..7dee37901b7b 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -371,7 +371,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); touchscreen_parse_properties(input, true, &priv->prop); - input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); + + error = input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); + if (error) { + dev_err(dev, "Unable to set up slots, err: %d\n", error); + return error; + } error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, IRQF_ONESHOT, client->name, priv); From efda86a49553bc7e44b3f7254208577bff2da932 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 Nov 2019 15:20:47 -0800 Subject: [PATCH 4/7] Input: ili210x - do not set parent device explicitly We are using devm_input_allocate_device() that set's up the parent for us, no need to do it ourselves. Tested-by: Adam Ford #imx6q-logicpd Tested-by: Sven Van Asbroeck # ILI2118A variant Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 7dee37901b7b..28e2dd748c5a 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -365,7 +365,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, /* Setup input device */ input->name = "ILI210x Touchscreen"; input->id.bustype = BUS_I2C; - input->dev.parent = dev; /* Multi touch */ input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); From ef536abd3afd1e3cbddf04c724db793f12ab9c44 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 6 Feb 2019 22:33:05 -0800 Subject: [PATCH 5/7] Input: ili210x - define and use chip operations structure Instead of doing if/else if/else on the chip's model number let's define chip operations structure and use it to perform indirect calls. With number of protocols supported by the driver growing, this makes it better maintainable. This change includes fixes to checks whether the driver should continue polling the controller by Sven Van Asbroeck . Tested-by: Adam Ford #imx6q-logicpd Tested-by: Sven Van Asbroeck # ILI2118A variant Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 311 ++++++++++++++++------------ 1 file changed, 174 insertions(+), 137 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 28e2dd748c5a..22811df82044 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -11,12 +11,13 @@ #include #include -#define ILI210X_TOUCHES 2 -#define ILI211X_TOUCHES 10 -#define ILI251X_TOUCHES 10 - #define ILI2XXX_POLL_PERIOD 20 +#define ILI210X_DATA_SIZE 64 +#define ILI211X_DATA_SIZE 43 +#define ILI251X_DATA_SIZE1 31 +#define ILI251X_DATA_SIZE2 20 + /* Touchscreen commands */ #define REG_TOUCHDATA 0x10 #define REG_PANEL_INFO 0x20 @@ -29,10 +30,14 @@ struct firmware_version { u8 minor; } __packed; -enum ili2xxx_model { - MODEL_ILI210X, - MODEL_ILI211X, - MODEL_ILI251X, +struct ili2xxx_chip { + int (*read_reg)(struct i2c_client *client, u8 reg, + void *buf, size_t len); + int (*get_touch_data)(struct i2c_client *client, u8 *data); + bool (*parse_touch_data)(const u8 *data, unsigned int finger, + unsigned int *x, unsigned int *y); + bool (*continue_polling)(const u8 *data, bool touch); + unsigned int max_touches; }; struct ili210x { @@ -40,16 +45,14 @@ struct ili210x { struct input_dev *input; struct gpio_desc *reset_gpio; struct touchscreen_properties prop; - enum ili2xxx_model model; - unsigned int max_touches; + const struct ili2xxx_chip *chip; bool stop; }; -static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, - size_t len) +static int ili210x_read_reg(struct i2c_client *client, + u8 reg, void *buf, size_t len) { - struct ili210x *priv = i2c_get_clientdata(client); - struct i2c_msg msg[2] = { + struct i2c_msg msg[] = { { .addr = client->addr, .flags = 0, @@ -63,53 +66,28 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, .buf = buf, } }; + int error, ret; - if (priv->model == MODEL_ILI251X) { - if (i2c_transfer(client->adapter, msg, 1) != 1) { - dev_err(&client->dev, "i2c transfer failed\n"); - return -EIO; - } - - usleep_range(5000, 5500); - - if (i2c_transfer(client->adapter, msg + 1, 1) != 1) { - dev_err(&client->dev, "i2c transfer failed\n"); - return -EIO; - } - } else { - if (i2c_transfer(client->adapter, msg, 2) != 2) { - dev_err(&client->dev, "i2c transfer failed\n"); - return -EIO; - } + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s failed: %d\n", __func__, error); + return error; } return 0; } -static int ili210x_read(struct i2c_client *client, void *buf, size_t len) +static int ili210x_read_touch_data(struct i2c_client *client, u8 *data) { - struct i2c_msg msg = { - .addr = client->addr, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - }; - - if (i2c_transfer(client->adapter, &msg, 1) != 1) { - dev_err(&client->dev, "i2c transfer failed\n"); - return -EIO; - } - - return 0; + return ili210x_read_reg(client, REG_TOUCHDATA, + data, ILI210X_DATA_SIZE); } -static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, +static bool ili210x_touchdata_to_coords(const u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) { - if (finger >= ILI210X_TOUCHES) - return false; - if (touchdata[0] & BIT(finger)) return false; @@ -119,15 +97,53 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, return true; } -static bool ili211x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, +static bool ili210x_check_continue_polling(const u8 *data, bool touch) +{ + return data[0] & 0xf3; +} + +static const struct ili2xxx_chip ili210x_chip = { + .read_reg = ili210x_read_reg, + .get_touch_data = ili210x_read_touch_data, + .parse_touch_data = ili210x_touchdata_to_coords, + .continue_polling = ili210x_check_continue_polling, + .max_touches = 2, +}; + +static int ili211x_read_touch_data(struct i2c_client *client, u8 *data) +{ + s16 sum = 0; + int error; + int ret; + int i; + + ret = i2c_master_recv(client, data, ILI211X_DATA_SIZE); + if (ret != ILI211X_DATA_SIZE) { + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s failed: %d\n", __func__, error); + return error; + } + + /* This chip uses custom checksum at the end of data */ + for (i = 0; i < ILI211X_DATA_SIZE - 1; i++) + sum = (sum + data[i]) & 0xff; + + if ((-sum & 0xff) != data[ILI211X_DATA_SIZE - 1]) { + dev_err(&client->dev, + "CRC error (crc=0x%02x expected=0x%02x)\n", + sum, data[ILI211X_DATA_SIZE - 1]); + return -EIO; + } + + return 0; +} + +static bool ili211x_touchdata_to_coords(const u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) { u32 data; - if (finger >= ILI211X_TOUCHES) - return false; - data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0); if (data == 0xffffffff) /* Finger up */ return false; @@ -140,58 +156,104 @@ static bool ili211x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, return true; } -static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, +static bool ili211x_decline_polling(const u8 *data, bool touch) +{ + return false; +} + +static const struct ili2xxx_chip ili211x_chip = { + .read_reg = ili210x_read_reg, + .get_touch_data = ili211x_read_touch_data, + .parse_touch_data = ili211x_touchdata_to_coords, + .continue_polling = ili211x_decline_polling, + .max_touches = 10, +}; + +static int ili251x_read_reg(struct i2c_client *client, + u8 reg, void *buf, size_t len) +{ + int error; + int ret; + + ret = i2c_master_send(client, ®, 1); + if (ret == 1) { + usleep_range(5000, 5500); + + ret = i2c_master_recv(client, buf, len); + if (ret == len) + return 0; + } + + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s failed: %d\n", __func__, error); + return ret; +} + +static int ili251x_read_touch_data(struct i2c_client *client, u8 *data) +{ + int error; + + error = ili251x_read_reg(client, REG_TOUCHDATA, + data, ILI251X_DATA_SIZE1); + if (!error && data[0] == 2) { + error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1, + ILI251X_DATA_SIZE2); + if (error >= 0 && error != ILI251X_DATA_SIZE2) + error = -EIO; + } + + return error; +} + +static bool ili251x_touchdata_to_coords(const u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) { - if (finger >= ILI251X_TOUCHES) + u16 val; + + val = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); + if (!(val & BIT(15))) /* Touch indication */ return false; - *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); - if (!(*x & BIT(15))) /* Touch indication */ - return false; - - *x &= 0x3fff; + *x = val & 0x3fff; *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); return true; } +static bool ili251x_check_continue_polling(const u8 *data, bool touch) +{ + return touch; +} + +static const struct ili2xxx_chip ili251x_chip = { + .read_reg = ili251x_read_reg, + .get_touch_data = ili251x_read_touch_data, + .parse_touch_data = ili251x_touchdata_to_coords, + .continue_polling = ili251x_check_continue_polling, + .max_touches = 10, +}; + static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) { struct input_dev *input = priv->input; int i; - bool contact = false, touch = false; + bool contact = false, touch; unsigned int x = 0, y = 0; - for (i = 0; i < priv->max_touches; i++) { - if (priv->model == MODEL_ILI210X) { - touch = ili210x_touchdata_to_coords(priv, touchdata, - i, &x, &y); - } else if (priv->model == MODEL_ILI211X) { - touch = ili211x_touchdata_to_coords(priv, touchdata, - i, &x, &y); - } else if (priv->model == MODEL_ILI251X) { - touch = ili251x_touchdata_to_coords(priv, touchdata, - i, &x, &y); - if (touch) - contact = true; - } + for (i = 0; i < priv->chip->max_touches; i++) { + touch = priv->chip->parse_touch_data(touchdata, i, &x, &y); input_mt_slot(input, i); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - if (!touch) - continue; - touchscreen_report_pos(input, &priv->prop, x, y, - true); + if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) { + touchscreen_report_pos(input, &priv->prop, x, y, true); + contact = true; + } } input_mt_report_pointer_emulation(input, false); input_sync(input); - if (priv->model == MODEL_ILI210X) - contact = touchdata[0] & 0xf3; - return contact; } @@ -199,50 +261,25 @@ static irqreturn_t ili210x_irq(int irq, void *irq_data) { struct ili210x *priv = irq_data; struct i2c_client *client = priv->client; - u8 touchdata[64] = { 0 }; - s16 sum = 0; + const struct ili2xxx_chip *chip = priv->chip; + u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; + bool keep_polling; bool touch; - int i; int error; do { - if (priv->model == MODEL_ILI210X) { - error = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, sizeof(touchdata)); - } else if (priv->model == MODEL_ILI211X) { - error = ili210x_read(client, touchdata, 43); - if (!error) { - /* - * This chip uses custom checksum at the end - * of data. - */ - for (i = 0; i <= 41; i++) - sum = (sum + touchdata[i]) & 0xff; - if ((-sum & 0xff) != touchdata[42]) { - dev_err(&client->dev, - "CRC error (crc=0x%02x expected=0x%02x)\n", - sum, touchdata[42]); - break; - } - } - } else if (priv->model == MODEL_ILI251X) { - error = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, 31); - if (!error && touchdata[0] == 2) - error = ili210x_read(client, - &touchdata[31], 20); - } - + error = chip->get_touch_data(client, touchdata); if (error) { dev_err(&client->dev, - "Unable to get touchdata, err = %d\n", error); + "Unable to get touch data: %d\n", error); break; } touch = ili210x_report_events(priv, touchdata); - if (touch) + keep_polling = chip->continue_polling(touchdata, touch); + if (keep_polling) msleep(ILI2XXX_POLL_PERIOD); - } while (!priv->stop && touch); + } while (!priv->stop && keep_polling); return IRQ_HANDLED; } @@ -298,20 +335,26 @@ static void ili210x_stop(void *data) } static int ili210x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct device *dev = &client->dev; + const struct ili2xxx_chip *chip; struct ili210x *priv; struct gpio_desc *reset_gpio; struct input_dev *input; struct firmware_version firmware; - enum ili2xxx_model model; int error; - model = (enum ili2xxx_model)id->driver_data; - dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + chip = device_get_match_data(dev); + if (!chip && id) + chip = (const struct ili2xxx_chip *)id->driver_data; + if (!chip) { + dev_err(&client->dev, "unknown device model\n"); + return -ENODEV; + } + if (client->irq <= 0) { dev_err(dev, "No IRQ!\n"); return -EINVAL; @@ -343,19 +386,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, priv->client = client; priv->input = input; priv->reset_gpio = reset_gpio; - priv->model = model; - if (model == MODEL_ILI210X) - priv->max_touches = ILI210X_TOUCHES; - if (model == MODEL_ILI211X) - priv->max_touches = ILI211X_TOUCHES; - if (model == MODEL_ILI251X) - priv->max_touches = ILI251X_TOUCHES; - + priv->chip = chip; i2c_set_clientdata(client, priv); /* Get firmware version */ - error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, - &firmware, sizeof(firmware)); + error = chip->read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); if (error) { dev_err(dev, "Failed to get firmware version, err: %d\n", error); @@ -371,7 +407,8 @@ static int ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); touchscreen_parse_properties(input, true, &priv->prop); - error = input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); + error = input_mt_init_slots(input, priv->chip->max_touches, + INPUT_MT_DIRECT); if (error) { dev_err(dev, "Unable to set up slots, err: %d\n", error); return error; @@ -435,18 +472,18 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, ili210x_i2c_suspend, ili210x_i2c_resume); static const struct i2c_device_id ili210x_i2c_id[] = { - { "ili210x", MODEL_ILI210X }, - { "ili2117", MODEL_ILI211X }, - { "ili251x", MODEL_ILI251X }, + { "ili210x", (long)&ili210x_chip }, + { "ili2117", (long)&ili211x_chip }, + { "ili251x", (long)&ili251x_chip }, { } }; MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); static const struct of_device_id ili210x_dt_ids[] = { - { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, - { .compatible = "ilitek,ili2117", .data = (void *)MODEL_ILI211X }, - { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, - { }, + { .compatible = "ilitek,ili210x", .data = &ili210x_chip }, + { .compatible = "ilitek,ili2117", .data = &ili211x_chip }, + { .compatible = "ilitek,ili251x", .data = &ili251x_chip }, + { } }; MODULE_DEVICE_TABLE(of, ili210x_dt_ids); From 172bb5f34c0dd5e94eb877edb3e8721d1ebd5c75 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 Nov 2019 15:27:41 -0800 Subject: [PATCH 6/7] Input: ili210x - do not unconditionally mark touchscreen as wakeup source I2C devices that are supposed to be wakeup sources should be instantiated with I2C_CLIENT_WAKE flag (which can be either set by in board info, or retrieved from "wakeup-source" property); individual drivers should not be marking devices as wakeup sources unconditionally. Tested-by: Adam Ford #imx6q-logicpd Tested-by: Sven Van Asbroeck # ILI2118A variant Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index 22811df82044..ef0da2fc8bd3 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -439,8 +439,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, return error; } - device_init_wakeup(dev, 1); - dev_dbg(dev, "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", client->irq, firmware.id, firmware.major, firmware.minor); From a5fdf7d02656e71071d1a13ab887f94e58651baf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 Nov 2019 15:30:22 -0800 Subject: [PATCH 7/7] Input: ili210x - remove unneeded suspend and resume handlers When I2C client is instantiated with I2C_CLIENT_WAKE flag (either via "wakeup-source" device property, or via board info flag), it will mark the main IRQ line as wakeup IRQ, which will ensure that it will be enabled for wakeup when system transitions to suspend state. Since our suspend/resume handlers were only managing IRQ wakeup state, they are no longer needed, and can be removed. Tested-by: Adam Ford #imx6q-logicpd Tested-by: Sven Van Asbroeck # ILI2118A variant Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ili210x.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index ef0da2fc8bd3..0ed6014af6d7 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -446,29 +446,6 @@ static int ili210x_i2c_probe(struct i2c_client *client, return 0; } -static int __maybe_unused ili210x_i2c_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); - - return 0; -} - -static int __maybe_unused ili210x_i2c_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, - ili210x_i2c_suspend, ili210x_i2c_resume); - static const struct i2c_device_id ili210x_i2c_id[] = { { "ili210x", (long)&ili210x_chip }, { "ili2117", (long)&ili211x_chip }, @@ -488,7 +465,6 @@ MODULE_DEVICE_TABLE(of, ili210x_dt_ids); static struct i2c_driver ili210x_ts_driver = { .driver = { .name = "ili210x_i2c", - .pm = &ili210x_i2c_pm, .of_match_table = ili210x_dt_ids, }, .id_table = ili210x_i2c_id,