Message ID | 20190103012938.7442-11-marex@denx.de (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | input: touchscreen: ili210x: Add ILI2511 support | expand |
On Thu, Jan 03, 2019 at 02:29:38AM +0100, Marek Vasut wrote: > Add support for ILI251x touch controller. This controller is similar > to the ILI210x, except for the following differences: > - Does not support I2C R-W transfer, Read must be followed by an > obscenely long delay, and then followed by Write > - Does support 10 simultaneous touch inputs. > - Touch data format is slightly different, pressure reporting does not > work although the touch data contain such information. > > Signed-off-by: Marek Vasut <marex@denx.de> > Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> > Cc: Henrik Rydberg <rydberg@bitmath.org> > Cc: Olivier Sobrie <olivier@sobrie.be> > Cc: Philipp Puschmann <pp@emlix.com> > To: linux-input@vger.kernel.org > --- > V2: - Implement delayed work for ILI251x > - Fix operation with >6 fingers in ili210x_work > V3: - Use get_unaligned_be16() > --- > drivers/input/touchscreen/ili210x.c | 119 ++++++++++++++++++++++++---- > 1 file changed, 104 insertions(+), 15 deletions(-) > > diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c > index bc327d75c046..7da46c293a74 100644 > --- a/drivers/input/touchscreen/ili210x.c > +++ b/drivers/input/touchscreen/ili210x.c > @@ -6,9 +6,11 @@ > #include <linux/input/mt.h> > #include <linux/delay.h> > #include <linux/gpio/consumer.h> > +#include <linux/of_device.h> > #include <asm/unaligned.h> > > -#define MAX_TOUCHES 2 > +#define ILI210X_TOUCHES 2 > +#define ILI251X_TOUCHES 10 > #define DEFAULT_POLL_PERIOD 20 > > /* Touchscreen commands */ > @@ -32,17 +34,25 @@ struct firmware_version { > u8 minor; > } __packed; > > +enum ili2xxx_model { > + MODEL_ILI210X, > + MODEL_ILI251X, > +}; > + > struct ili210x { > struct i2c_client *client; > struct input_dev *input; > unsigned int poll_period; > struct delayed_work dwork; > struct gpio_desc *reset_gpio; > + enum ili2xxx_model model; > + unsigned int max_touches; > }; > > 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] = { > { > .addr = client->addr, > @@ -58,7 +68,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, > } > }; > > - if (i2c_transfer(client->adapter, msg, 2) != 2) { > + if (priv->model == MODEL_ILI251X) { > + if (i2c_transfer(client->adapter, msg, 1) != 1) { > + dev_err(&client->dev, "i2c transfer failed\n"); > + return -EIO; > + } > + > + mdelay(5); 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; > + } > + } > + > + return 0; > +} > + > +static int ili210x_read(struct i2c_client *client, void *buf, size_t len) > +{ > + 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; > } > @@ -70,7 +111,7 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, > unsigned int finger, > unsigned int *x, unsigned int *y) > { > - if (finger >= MAX_TOUCHES) > + if (finger >= ILI210X_TOUCHES) > return false; > > if (touchdata[0] & BIT(finger)) > @@ -82,16 +123,43 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, > return true; > } > > +static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, > + unsigned int finger, > + unsigned int *x, unsigned int *y) > +{ > + if (finger >= ILI251X_TOUCHES) > + return false; > + > + *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); > + if (!(*x & BIT(15))) /* Touch indication */ > + return false; > + > + *x &= 0x3fff; > + *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); > + > + return true; > +} > + > static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) > { > + struct input_dev *input = priv->input; > int i; > - bool touch; > - unsigned int x, y; > + bool contact = false, touch = false; > + unsigned int x = 0, y = 0; > > - for (i = 0; i < MAX_TOUCHES; i++) { > + for (i = 0; i < priv->max_touches; i++) { > input_mt_slot(input, i); > > - touch = ili210x_touchdata_to_coords(priv, touchdata, i, &x, &y); > + if (priv->model == MODEL_ILI210X) { > + touch = ili210x_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; > + } > + > input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); > if (touch) { > input_report_abs(input, ABS_MT_POSITION_X, x); > @@ -102,7 +170,10 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) > input_mt_report_pointer_emulation(input, false); > input_sync(input); > > - return touchdata[0] & 0xf3; > + if (priv->model == MODEL_ILI210X) > + contact = touchdata[0] & 0xf3; > + > + return contact; > } > > static void ili210x_work(struct work_struct *work) > @@ -110,12 +181,20 @@ static void ili210x_work(struct work_struct *work) > struct ili210x *priv = container_of(work, struct ili210x, > dwork.work); > struct i2c_client *client = priv->client; > - u8 touchdata[1 + 4 * MAX_TOUCHES]; > + u8 touchdata[64] = { 0 }; > bool touch; > - int error; > + int error = -EINVAL; > + > + if (priv->model == MODEL_ILI210X) { > + error = ili210x_read_reg(client, REG_TOUCHDATA, > + touchdata, sizeof(touchdata)); > + } 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 = ili210x_read_reg(client, REG_TOUCHDATA, > - touchdata, sizeof(touchdata)); > if (error) { > dev_err(&client->dev, > "Unable to get touchdata, err = %d\n", error); > @@ -189,9 +268,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, > struct input_dev *input; > struct panel_info panel; > struct firmware_version firmware; > + enum ili2xxx_model model; > int xmax, ymax; > int error; > > + model = (enum ili2xxx_model)id->driver_data; > + > dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); > > if (client->irq <= 0) { > @@ -228,6 +310,11 @@ static int ili210x_i2c_probe(struct i2c_client *client, > 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) > + priv->max_touches = ILI210X_TOUCHES; > + if (model == MODEL_ILI251X) > + priv->max_touches = ILI251X_TOUCHES; > > i2c_set_clientdata(client, priv); > > @@ -266,7 +353,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, > input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); > > /* Multi touch */ > - input_mt_init_slots(input, MAX_TOUCHES, 0); > + input_mt_init_slots(input, priv->max_touches, 0); > input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); > input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); > > @@ -338,13 +425,15 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, > ili210x_i2c_suspend, ili210x_i2c_resume); > > static const struct i2c_device_id ili210x_i2c_id[] = { > - { "ili210x", 0 }, > + { "ili210x", MODEL_ILI210X }, > + { "ili251x", MODEL_ILI251X }, > { } > }; > MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); > > static const struct of_device_id ili210x_dt_ids[] = { > - { .compatible = "ilitek,ili210x", }, > + { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, > + { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, > { }, > }; > MODULE_DEVICE_TABLE(of, ili210x_dt_ids); > -- > 2.19.2 >
On 1/15/19 1:38 AM, Dmitry Torokhov wrote: > On Thu, Jan 03, 2019 at 02:29:38AM +0100, Marek Vasut wrote: >> Add support for ILI251x touch controller. This controller is similar >> to the ILI210x, except for the following differences: >> - Does not support I2C R-W transfer, Read must be followed by an >> obscenely long delay, and then followed by Write >> - Does support 10 simultaneous touch inputs. >> - Touch data format is slightly different, pressure reporting does not >> work although the touch data contain such information. >> >> Signed-off-by: Marek Vasut <marex@denx.de> >> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> >> Cc: Henrik Rydberg <rydberg@bitmath.org> >> Cc: Olivier Sobrie <olivier@sobrie.be> >> Cc: Philipp Puschmann <pp@emlix.com> >> To: linux-input@vger.kernel.org >> --- >> V2: - Implement delayed work for ILI251x >> - Fix operation with >6 fingers in ili210x_work >> V3: - Use get_unaligned_be16() >> --- >> drivers/input/touchscreen/ili210x.c | 119 ++++++++++++++++++++++++---- >> 1 file changed, 104 insertions(+), 15 deletions(-) >> >> diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c >> index bc327d75c046..7da46c293a74 100644 >> --- a/drivers/input/touchscreen/ili210x.c >> +++ b/drivers/input/touchscreen/ili210x.c >> @@ -6,9 +6,11 @@ >> #include <linux/input/mt.h> >> #include <linux/delay.h> >> #include <linux/gpio/consumer.h> >> +#include <linux/of_device.h> >> #include <asm/unaligned.h> >> >> -#define MAX_TOUCHES 2 >> +#define ILI210X_TOUCHES 2 >> +#define ILI251X_TOUCHES 10 >> #define DEFAULT_POLL_PERIOD 20 >> >> /* Touchscreen commands */ >> @@ -32,17 +34,25 @@ struct firmware_version { >> u8 minor; >> } __packed; >> >> +enum ili2xxx_model { >> + MODEL_ILI210X, >> + MODEL_ILI251X, >> +}; >> + >> struct ili210x { >> struct i2c_client *client; >> struct input_dev *input; >> unsigned int poll_period; >> struct delayed_work dwork; >> struct gpio_desc *reset_gpio; >> + enum ili2xxx_model model; >> + unsigned int max_touches; >> }; >> >> 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] = { >> { >> .addr = client->addr, >> @@ -58,7 +68,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, >> } >> }; >> >> - if (i2c_transfer(client->adapter, msg, 2) != 2) { >> + if (priv->model == MODEL_ILI251X) { >> + if (i2c_transfer(client->adapter, msg, 1) != 1) { >> + dev_err(&client->dev, "i2c transfer failed\n"); >> + return -EIO; >> + } >> + >> + mdelay(5); > > usleep_range(5000, 5500) ? Fixed
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index bc327d75c046..7da46c293a74 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -6,9 +6,11 @@ #include <linux/input/mt.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/of_device.h> #include <asm/unaligned.h> -#define MAX_TOUCHES 2 +#define ILI210X_TOUCHES 2 +#define ILI251X_TOUCHES 10 #define DEFAULT_POLL_PERIOD 20 /* Touchscreen commands */ @@ -32,17 +34,25 @@ struct firmware_version { u8 minor; } __packed; +enum ili2xxx_model { + MODEL_ILI210X, + MODEL_ILI251X, +}; + struct ili210x { struct i2c_client *client; struct input_dev *input; unsigned int poll_period; struct delayed_work dwork; struct gpio_desc *reset_gpio; + enum ili2xxx_model model; + unsigned int max_touches; }; 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] = { { .addr = client->addr, @@ -58,7 +68,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, } }; - if (i2c_transfer(client->adapter, msg, 2) != 2) { + if (priv->model == MODEL_ILI251X) { + if (i2c_transfer(client->adapter, msg, 1) != 1) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + mdelay(5); + + 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; + } + } + + return 0; +} + +static int ili210x_read(struct i2c_client *client, void *buf, size_t len) +{ + 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; } @@ -70,7 +111,7 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) { - if (finger >= MAX_TOUCHES) + if (finger >= ILI210X_TOUCHES) return false; if (touchdata[0] & BIT(finger)) @@ -82,16 +123,43 @@ static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, return true; } +static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, + unsigned int finger, + unsigned int *x, unsigned int *y) +{ + if (finger >= ILI251X_TOUCHES) + return false; + + *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); + if (!(*x & BIT(15))) /* Touch indication */ + return false; + + *x &= 0x3fff; + *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); + + return true; +} + static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) { + struct input_dev *input = priv->input; int i; - bool touch; - unsigned int x, y; + bool contact = false, touch = false; + unsigned int x = 0, y = 0; - for (i = 0; i < MAX_TOUCHES; i++) { + for (i = 0; i < priv->max_touches; i++) { input_mt_slot(input, i); - touch = ili210x_touchdata_to_coords(priv, touchdata, i, &x, &y); + if (priv->model == MODEL_ILI210X) { + touch = ili210x_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; + } + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); if (touch) { input_report_abs(input, ABS_MT_POSITION_X, x); @@ -102,7 +170,10 @@ static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) input_mt_report_pointer_emulation(input, false); input_sync(input); - return touchdata[0] & 0xf3; + if (priv->model == MODEL_ILI210X) + contact = touchdata[0] & 0xf3; + + return contact; } static void ili210x_work(struct work_struct *work) @@ -110,12 +181,20 @@ static void ili210x_work(struct work_struct *work) struct ili210x *priv = container_of(work, struct ili210x, dwork.work); struct i2c_client *client = priv->client; - u8 touchdata[1 + 4 * MAX_TOUCHES]; + u8 touchdata[64] = { 0 }; bool touch; - int error; + int error = -EINVAL; + + if (priv->model == MODEL_ILI210X) { + error = ili210x_read_reg(client, REG_TOUCHDATA, + touchdata, sizeof(touchdata)); + } 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 = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, sizeof(touchdata)); if (error) { dev_err(&client->dev, "Unable to get touchdata, err = %d\n", error); @@ -189,9 +268,12 @@ static int ili210x_i2c_probe(struct i2c_client *client, struct input_dev *input; struct panel_info panel; struct firmware_version firmware; + enum ili2xxx_model model; int xmax, ymax; int error; + model = (enum ili2xxx_model)id->driver_data; + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); if (client->irq <= 0) { @@ -228,6 +310,11 @@ static int ili210x_i2c_probe(struct i2c_client *client, 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) + priv->max_touches = ILI210X_TOUCHES; + if (model == MODEL_ILI251X) + priv->max_touches = ILI251X_TOUCHES; i2c_set_clientdata(client, priv); @@ -266,7 +353,7 @@ static int ili210x_i2c_probe(struct i2c_client *client, input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); /* Multi touch */ - input_mt_init_slots(input, MAX_TOUCHES, 0); + input_mt_init_slots(input, priv->max_touches, 0); input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); @@ -338,13 +425,15 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, ili210x_i2c_suspend, ili210x_i2c_resume); static const struct i2c_device_id ili210x_i2c_id[] = { - { "ili210x", 0 }, + { "ili210x", MODEL_ILI210X }, + { "ili251x", MODEL_ILI251X }, { } }; MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); static const struct of_device_id ili210x_dt_ids[] = { - { .compatible = "ilitek,ili210x", }, + { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, + { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, { }, }; MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
Add support for ILI251x touch controller. This controller is similar to the ILI210x, except for the following differences: - Does not support I2C R-W transfer, Read must be followed by an obscenely long delay, and then followed by Write - Does support 10 simultaneous touch inputs. - Touch data format is slightly different, pressure reporting does not work although the touch data contain such information. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Henrik Rydberg <rydberg@bitmath.org> Cc: Olivier Sobrie <olivier@sobrie.be> Cc: Philipp Puschmann <pp@emlix.com> To: linux-input@vger.kernel.org --- V2: - Implement delayed work for ILI251x - Fix operation with >6 fingers in ili210x_work V3: - Use get_unaligned_be16() --- drivers/input/touchscreen/ili210x.c | 119 ++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 15 deletions(-)