Hi Icenowy,

On Tue, Oct 11, 2016 at 08:33:57AM +0800, Icenowy Zheng wrote:
> This driver adds support for Ilitek ili2139 touch IC, which is used in
> several Colorfly tablets (for example, Colorfly E708 Q1, which is an
> Allwinner A31s tablet with mainline kernel support).
> 
> Theortically it may support more Ilitek touch ICs, however, only ili2139
> is used in any mainlined device.
> 
> It supports device tree enumeration, with screen resolution and axis
> quirks configurable.
> 
> Signed-off-by: Icenowy Zheng <icen...@aosc.xyz>

Please extend ili210x.c instead of adding brand new driver, they look
very similar.

Thanks.

> ---
>  drivers/input/touchscreen/Kconfig   |  14 ++
>  drivers/input/touchscreen/Makefile  |   1 +
>  drivers/input/touchscreen/ili2139.c | 320 
> ++++++++++++++++++++++++++++++++++++
>  3 files changed, 335 insertions(+)
>  create mode 100644 drivers/input/touchscreen/ili2139.c
> 
> diff --git a/drivers/input/touchscreen/Kconfig 
> b/drivers/input/touchscreen/Kconfig
> index 5079813..bb4d9d2 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -348,6 +348,20 @@ config TOUCHSCREEN_ILI210X
>         To compile this driver as a module, choose M here: the
>         module will be called ili210x.
>  
> +config TOUCHSCREEN_ILI2139
> +     tristate "Ilitek ILI2139 based touchscreen"
> +     depends on I2C
> +     depends on OF
> +     help
> +       Say Y here if you have a ILI2139 based touchscreen
> +       controller. Such kind of chipsets can be found in several
> +       Colorfly tablets.
> +
> +       If unsure, say N.
> +
> +       To compile this driver as a module, choose M here; the
> +       module will be called ili2139.
> +
>  config TOUCHSCREEN_IPROC
>       tristate "IPROC touch panel driver support"
>       depends on ARCH_BCM_IPROC || COMPILE_TEST
> diff --git a/drivers/input/touchscreen/Makefile 
> b/drivers/input/touchscreen/Makefile
> index 81b8645..930b5e2 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -40,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)     += 
> egalax_ts_serial.o
>  obj-$(CONFIG_TOUCHSCREEN_FUJITSU)    += fujitsu_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_GOODIX)     += goodix.o
>  obj-$(CONFIG_TOUCHSCREEN_ILI210X)    += ili210x.o
> +obj-$(CONFIG_TOUCHSCREEN_ILI2139)    += ili2139.o
>  obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC) += imx6ul_tsc.o
>  obj-$(CONFIG_TOUCHSCREEN_INEXIO)     += inexio.o
>  obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)  += intel-mid-touch.o
> diff --git a/drivers/input/touchscreen/ili2139.c 
> b/drivers/input/touchscreen/ili2139.c
> new file mode 100644
> index 0000000..65c2dea
> --- /dev/null
> +++ b/drivers/input/touchscreen/ili2139.c
> @@ -0,0 +1,320 @@
> +/* -------------------------------------------------------------------------
> + * Copyright (C) 2016, Icenowy Zheng <icen...@aosc.xyz>
> + *
> + * Derived from:
> + *  ili210x.c
> + *  Copyright (C) Olivier Sobrie <oliv...@sobrie.be>
> + *
> + *  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; either version 2 of the License, or
> + *  (at your option) any later version.
> + *
> + *  This program is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + * -------------------------------------------------------------------------
> + */
> +
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/input/touchscreen.h>
> +#include <linux/delay.h>
> +#include <linux/workqueue.h>
> +
> +#define DEFAULT_POLL_PERIOD  20
> +
> +#define MAX_TOUCHES          10
> +#define COMPATIBLE_TOUCHES   2
> +
> +/* Touchscreen commands */
> +#define REG_TOUCHDATA                0x10
> +#define REG_TOUCHSUBDATA     0x11
> +#define REG_PANEL_INFO               0x20
> +#define REG_FIRMWARE_VERSION 0x40
> +#define REG_PROTO_VERSION    0x42
> +
> +#define SUBDATA_STATUS_TOUCH_POINT   0x80
> +#define SUBDATA_STATUS_RELEASE_POINT 0x00
> +
> +struct finger {
> +     u8 x_low;
> +     u8 x_high;
> +     u8 y_low;
> +     u8 y_high;
> +} __packed;
> +
> +struct touchdata {
> +     u8 length;
> +     struct finger finger[COMPATIBLE_TOUCHES];
> +} __packed;
> +
> +struct touch_subdata {
> +     u8 status;
> +     struct finger finger;
> +} __packed;
> +
> +struct panel_info {
> +     struct finger finger_max;
> +     u8 xchannel_num;
> +     u8 ychannel_num;
> +} __packed;
> +
> +struct firmware_version {
> +     u8 id;
> +     u8 major;
> +     u8 minor;
> +} __packed;
> +
> +struct ili2139 {
> +     struct i2c_client *client;
> +     struct input_dev *input;
> +     unsigned int poll_period;
> +     struct delayed_work dwork;
> +     struct touchscreen_properties prop;
> +     int slots[MAX_TOUCHES];
> +     int ids[MAX_TOUCHES];
> +     struct input_mt_pos pos[MAX_TOUCHES];
> +};
> +
> +static int ili2139_read_reg(struct i2c_client *client, u8 reg, void *buf,
> +                         size_t len)
> +{
> +     struct i2c_msg msg[2] = {
> +             {
> +                     .addr   = client->addr,
> +                     .flags  = 0,
> +                     .len    = 1,
> +                     .buf    = &reg,
> +             },
> +             {
> +                     .addr   = client->addr,
> +                     .flags  = I2C_M_RD,
> +                     .len    = len,
> +                     .buf    = buf,
> +             }
> +     };
> +
> +     if (i2c_transfer(client->adapter, msg, 2) != 2) {
> +             dev_err(&client->dev, "i2c transfer failed\n");
> +             return -EIO;
> +     }
> +
> +     return 0;
> +}
> +
> +static void ili2139_work(struct work_struct *work)
> +{
> +     int id;
> +     struct ili2139 *priv = container_of(work, struct ili2139,
> +                                         dwork.work);
> +     struct i2c_client *client = priv->client;
> +     struct touchdata touchdata;
> +     struct touch_subdata subdata;
> +     int error;
> +
> +     error = ili2139_read_reg(client, REG_TOUCHDATA,
> +                              &touchdata, sizeof(touchdata));
> +     if (error) {
> +             dev_err(&client->dev,
> +                     "Unable to get touchdata, err = %d\n", error);
> +             return;
> +     }
> +
> +     for (id = 0; id < touchdata.length; id++) {
> +             error = ili2139_read_reg(client, REG_TOUCHSUBDATA, &subdata,
> +                                      sizeof(subdata));
> +             if (error) {
> +                     dev_err(&client->dev,
> +                             "Unable to get touch subdata, err = %d\n",
> +                             error);
> +                     return;
> +             }
> +
> +             priv->ids[id] = subdata.status & 0x3F;
> +
> +             /* The sequence changed in the v2 subdata protocol. */
> +             touchscreen_set_mt_pos(&priv->pos[id], &priv->prop,
> +                     (subdata.finger.x_high | (subdata.finger.x_low << 8)),
> +                     (subdata.finger.y_high | (subdata.finger.y_low << 8)));
> +     }
> +
> +     input_mt_assign_slots(priv->input, priv->slots, priv->pos,
> +                           touchdata.length, 0);
> +
> +     for (id = 0; id < touchdata.length; id++) {
> +             input_mt_slot(priv->input, priv->slots[id]);
> +             input_mt_report_slot_state(priv->input, MT_TOOL_FINGER,
> +                                        subdata.status &
> +                                        SUBDATA_STATUS_TOUCH_POINT);
> +             input_report_abs(priv->input, ABS_MT_POSITION_X,
> +                              priv->pos[id].x);
> +             input_report_abs(priv->input, ABS_MT_POSITION_Y,
> +                              priv->pos[id].y);
> +     }
> +
> +     input_mt_sync_frame(priv->input);
> +     input_sync(priv->input);
> +
> +     schedule_delayed_work(&priv->dwork,
> +                           msecs_to_jiffies(priv->poll_period));
> +}
> +
> +static irqreturn_t ili2139_irq(int irq, void *irq_data)
> +{
> +     struct ili2139 *priv = irq_data;
> +
> +     schedule_delayed_work(&priv->dwork, 0);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static int ili2139_i2c_probe(struct i2c_client *client,
> +                                    const struct i2c_device_id *id)
> +{
> +     struct device *dev = &client->dev;
> +     struct ili2139 *priv;
> +     struct input_dev *input;
> +     struct panel_info panel;
> +     struct firmware_version firmware;
> +     int xmax, ymax;
> +     int error;
> +
> +     dev_dbg(dev, "Probing for ILI2139 I2C Touschreen driver");
> +
> +     if (client->irq <= 0) {
> +             dev_err(dev, "No IRQ!\n");
> +             return -ENODEV;
> +     }
> +
> +     /* Get firmware version */
> +     error = ili2139_read_reg(client, REG_FIRMWARE_VERSION,
> +                              &firmware, sizeof(firmware));
> +     if (error) {
> +             dev_err(dev, "Failed to get firmware version, err: %d\n",
> +                     error);
> +             return error;
> +     }
> +
> +     /* get panel info */
> +     error = ili2139_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
> +     if (error) {
> +             dev_err(dev, "Failed to get panel information, err: %d\n",
> +                     error);
> +             return error;
> +     }
> +
> +     xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
> +     ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
> +
> +     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +     input = devm_input_allocate_device(dev);
> +     if (!priv || !input)
> +             return -ENOMEM;
> +
> +     priv->client = client;
> +     priv->input = input;
> +     priv->poll_period = DEFAULT_POLL_PERIOD;
> +     INIT_DELAYED_WORK(&priv->dwork, ili2139_work);
> +
> +     /* Setup input device */
> +     input->name = "ILI2139 Touchscreen";
> +     input->id.bustype = BUS_I2C;
> +     input->dev.parent = dev;
> +
> +     __set_bit(EV_SYN, input->evbit);
> +     __set_bit(EV_KEY, input->evbit);
> +     __set_bit(EV_ABS, input->evbit);
> +
> +     /* Multi touch */
> +     input_mt_init_slots(input, MAX_TOUCHES, INPUT_MT_DIRECT |
> +                         INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
> +     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);
> +
> +     touchscreen_parse_properties(input, true, &priv->prop);
> +
> +     input_set_drvdata(input, priv);
> +     i2c_set_clientdata(client, priv);
> +
> +     error = devm_request_irq(dev, client->irq, ili2139_irq,
> +                              IRQF_TRIGGER_FALLING, client->name, priv);
> +     if (error) {
> +             dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
> +                     error);
> +             return error;
> +     }
> +
> +     error = input_register_device(priv->input);
> +     if (error) {
> +             dev_err(dev, "Cannot register input device, err: %d\n", error);
> +             return error;
> +     }
> +
> +     device_init_wakeup(&client->dev, 1);
> +
> +     dev_dbg(dev,
> +             "ILI2139 initialized (IRQ: %d), firmware version %d.%d.%d",
> +             client->irq, firmware.id, firmware.major, firmware.minor);
> +
> +     return 0;
> +}
> +
> +static int ili2139_i2c_remove(struct i2c_client *client)
> +{
> +     struct ili2139 *priv = i2c_get_clientdata(client);
> +
> +     cancel_delayed_work_sync(&priv->dwork);
> +
> +     return 0;
> +}
> +
> +static int __maybe_unused ili2139_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 ili2139_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(ili2139_i2c_pm,
> +                      ili2139_i2c_suspend, ili2139_i2c_resume);
> +
> +static const struct i2c_device_id ili2139_i2c_id[] = {
> +     { "ili2139", 0 },
> +     { }
> +};
> +MODULE_DEVICE_TABLE(i2c, ili2139_i2c_id);
> +
> +static struct i2c_driver ili2139_ts_driver = {
> +     .driver = {
> +             .name = "ili2139_i2c",
> +             .pm = &ili2139_i2c_pm,
> +     },
> +     .id_table = ili2139_i2c_id,
> +     .probe = ili2139_i2c_probe,
> +     .remove = ili2139_i2c_remove,
> +};
> +
> +module_i2c_driver(ili2139_ts_driver);
> +
> +MODULE_AUTHOR("Olivier Sobrie <oliv...@sobrie.be>");
> +MODULE_DESCRIPTION("ILI2139 I2C Touchscreen Driver");
> +MODULE_LICENSE("GPL");
> -- 
> 2.10.1
> 

-- 
Dmitry

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to