Re: [PATCH] ili210x: Add support for Ilitek ILI210x based touchscreens

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Olivier,

> The driver supports chipsets ILI2102, ILI2102s, ILI2103, ILI2103s and
> ILI2105.
> Such kind of controllers can be found in Amazon Kindle Fire devices.
> 
> Reviewed-by: Jan Paesmans <jan.paesmans@xxxxxxxxx>
> Signed-off-by: Olivier Sobrie <olivier@xxxxxxxxx>
> ---

Looks great now, just a few nit-picks inline.

>  drivers/input/touchscreen/Kconfig   |   15 ++
>  drivers/input/touchscreen/Makefile  |    1 +
>  drivers/input/touchscreen/ili210x.c |  378 +++++++++++++++++++++++++++++++++++
>  include/linux/input/ili210x.h       |    9 +
>  4 files changed, 403 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/touchscreen/ili210x.c
>  create mode 100644 include/linux/input/ili210x.h
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index fc087b3..97b31a0 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -243,6 +243,21 @@ config TOUCHSCREEN_FUJITSU
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called fujitsu-ts.
>  
> +config TOUCHSCREEN_ILI210X
> +	tristate "Ilitek ILI210X based touchscreen"
> +	depends on I2C
> +	help
> +	  Say Y here if you have a ILI210X based touchscreen
> +	  controller. This driver supports models ILI2102,
> +	  ILI2102s, ILI2103, ILI2103s and ILI2105.
> +	  Such kind of chipsets can be found in Amazon Kindle Fire
> +	  touchscreens.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ili210x.
> +
>  config TOUCHSCREEN_S3C2410
>  	tristate "Samsung S3C2410/generic touchscreen input driver"
>  	depends on ARCH_S3C2410 || SAMSUNG_DEV_TS
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index e748fb8..3d5cf8c 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -29,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI)		+= eeti_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ELO)		+= elo.o
>  obj-$(CONFIG_TOUCHSCREEN_EGALAX)	+= egalax_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_FUJITSU)	+= fujitsu_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_ILI210X)	+= ili210x.o
>  obj-$(CONFIG_TOUCHSCREEN_INEXIO)	+= inexio.o
>  obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)	+= intel-mid-touch.o
>  obj-$(CONFIG_TOUCHSCREEN_LPC32XX)	+= lpc32xx_ts.o
> diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
> new file mode 100644
> index 0000000..fd2ea23
> --- /dev/null
> +++ b/drivers/input/touchscreen/ili210x.c
> @@ -0,0 +1,378 @@
> +#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/delay.h>
> +#include <linux/workqueue.h>
> +#include <linux/input/ili210x.h>
> +
> +#define MAX_TOUCHES		2
> +#define POLL_PERIOD		msecs_to_jiffies(1)
> +
> +/* Touchscreen commands */
> +#define REG_TOUCHDATA		0x10
> +#define REG_PANEL_INFO		0x20
> +#define REG_FIRMWARE_VERSION	0x40
> +#define REG_CALIBRATE		0xcc
> +
> +struct finger {
> +	u8 x_low;
> +	u8 x_high;
> +	u8 y_low;
> +	u8 y_high;
> +} __packed;
> +
> +struct touchdata {
> +	u8 status;
> +	struct finger finger[MAX_TOUCHES];
> +} __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 ili210x {
> +	struct i2c_client *client;
> +	struct input_dev *input;
> +	int (*get_pendown_state)(void);
> +	spinlock_t lock;

Not used anymore.

> +	struct delayed_work dwork;
> +};
> +
> +static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
> +			    size_t len)
> +{
> +	struct i2c_msg msg[2];
> +
> +	msg[0].addr = client->addr;
> +	msg[0].flags = 0;
> +	msg[0].len = 1;
> +	msg[0].buf = &reg;
> +
> +	msg[1].addr = client->addr;
> +	msg[1].flags = I2C_M_RD;
> +	msg[1].len = len;
> +	msg[1].buf = buf;
> +
> +	if (i2c_transfer(client->adapter, msg, 2) != 2) {
> +		dev_err(&client->dev, "i2c transfer failed\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static void ili210x_report_events(struct input_dev *input,
> +				  const struct touchdata *touchdata)
> +{
> +	int i;
> +	bool touch;
> +	unsigned int x, y;
> +	const struct finger *finger;
> +
> +	for (i = 0; i < MAX_TOUCHES; i++) {
> +		input_mt_slot(input, i);
> +
> +		finger = &touchdata->finger[i];
> +
> +		touch = touchdata->status & (1 << i);
> +		input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
> +		if (touch) {
> +			x = finger->x_low | (finger->x_high << 8);
> +			y = finger->y_low | (finger->y_high << 8);
> +
> +			input_report_abs(input, ABS_MT_POSITION_X, x);
> +			input_report_abs(input, ABS_MT_POSITION_Y, y);
> +		}
> +	}
> +
> +	input_mt_report_pointer_emulation(input, true);

You probably want to use "false" here, since (correctly, as this is a
touchscreen and not a touchpad) none of the BTN_TOOL keys are defined
in the code.

> +	input_sync(input);
> +}
> +
> +static int get_pendown_state(const struct ili210x *priv)
> +{
> +	int state = 0;
> +
> +	if (priv->get_pendown_state)
> +		state = priv->get_pendown_state();
> +
> +	return state;
> +}
> +
> +static void ili210x_work(struct work_struct *work)
> +{
> +	struct ili210x *priv = container_of(work, struct ili210x,
> +					    dwork.work);
> +	struct input_dev *input = priv->input;
> +	struct i2c_client *client = priv->client;
> +	struct device *dev = &client->dev;
> +	struct touchdata touchdata;
> +	int rc;
> +
> +	rc = ili210x_read_reg(client, REG_TOUCHDATA, &touchdata,
> +			      sizeof(touchdata));
> +	if (rc < 0) {
> +		dev_err(dev, "Unable to get touchdata, err = %d\n",
> +			rc);
> +		return;
> +	}
> +
> +	ili210x_report_events(input, &touchdata);
> +
> +	if ((touchdata.status & 0xf3) || get_pendown_state(priv))
> +		schedule_delayed_work(&priv->dwork, POLL_PERIOD);
> +}
> +
> +static irqreturn_t ili210x_irq(int irq, void *irq_data)
> +{
> +	struct ili210x *priv = irq_data;
> +
> +	schedule_delayed_work(&priv->dwork, 0);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static ssize_t ili210x_calibrate(struct device *dev,
> +				 struct device_attribute *attr,
> +				 const char *buf, size_t count)
> +{
> +	struct ili210x *priv = dev_get_drvdata(dev);
> +	unsigned long calibrate;
> +	int rc;
> +	u8 cmd = REG_CALIBRATE;
> +
> +	if (kstrtoul(buf, 10, &calibrate))
> +		return -EINVAL;
> +
> +	if (calibrate > 1)
> +		return -EINVAL;
> +
> +	if (calibrate) {
> +		rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
> +		if (rc != sizeof(cmd))
> +			return -EIO;
> +	}
> +
> +	return count;
> +}
> +static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate);
> +
> +static struct attribute *ili210x_attributes[] = {
> +	&dev_attr_calibrate.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ili210x_attr_group = {
> +	.attrs = ili210x_attributes,
> +};
> +
> +static int __devinit ili210x_i2c_probe(struct i2c_client *client,
> +				       const struct i2c_device_id *id)
> +{
> +	struct ili210x *priv;
> +	struct input_dev *input;
> +	struct device *dev = &client->dev;
> +	struct ili210x_platform_data *pdata = dev->platform_data;
> +	struct panel_info panel;
> +	struct firmware_version firmware;
> +	int xmax, ymax;
> +	int rc;
> +
> +	dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
> +
> +	if (!pdata) {
> +		dev_err(dev, "No platform data!\n");
> +		return -ENODEV;
> +	}
> +
> +	if (client->irq <= 0) {
> +		dev_err(dev, "No IRQ!\n");
> +		return -ENODEV;
> +	}
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv) {
> +		dev_err(dev, "Failed to allocate driver data!\n");
> +		rc = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	dev_set_drvdata(dev, priv);
> +
> +	input = input_allocate_device();
> +	if (!input) {
> +		dev_err(dev, "Failed to allocate input device\n");
> +		rc = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	/* Get firmware version */
> +	rc = ili210x_read_reg(client, REG_FIRMWARE_VERSION, &firmware,
> +			      sizeof(firmware));
> +	if (rc < 0) {
> +		dev_err(dev, "Failed to get firmware version, err: %d\n",
> +			rc);
> +		goto fail_probe;
> +	}
> +
> +	/* get panel info */
> +	rc = ili210x_read_reg(client, REG_PANEL_INFO, &panel,
> +			      sizeof(panel));
> +	if (rc < 0) {
> +		dev_err(dev, "Failed to get panel informations, err: %d\n",
> +			rc);
> +		goto fail_probe;
> +	}
> +
> +	xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
> +	ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
> +
> +	/* Setup input device */
> +	__set_bit(EV_SYN, input->evbit);
> +	__set_bit(EV_KEY, input->evbit);
> +	__set_bit(EV_ABS, input->evbit);
> +	__set_bit(BTN_TOUCH, input->keybit);
> +
> +	/* Single touch */
> +	input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
> +	input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
> +
> +	/* Multi touch */
> +	input_mt_init_slots(input, MAX_TOUCHES);
> +	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);
> +
> +	input->name = "ILI210x Touchscreen";
> +	input->id.bustype = BUS_I2C;
> +	input->dev.parent = dev;
> +
> +	input_set_drvdata(input, priv);
> +
> +	INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
> +	spin_lock_init(&priv->lock);

The spinlock is not used anymore.

> +	priv->get_pendown_state = pdata->get_pendown_state;
> +	rc = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
> +			 client->name, priv);
> +	if (rc) {
> +		dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
> +			rc);
> +		goto fail_probe;
> +	}
> +
> +	rc = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
> +	if (rc) {
> +		dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
> +			rc);
> +		goto fail_sysfs;
> +	}
> +
> +	rc = input_register_device(input);
> +	if (rc) {
> +		dev_err(dev, "Cannot regiser input device, err: %d\n", rc);
> +		goto fail_input;
> +	}
> +
> +	priv->client = client;
> +	priv->input = input;
> +
> +	device_init_wakeup(&client->dev, 1);
> +
> +	dev_info(dev,
> +		 "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
> +		 client->irq, firmware.id, firmware.major, firmware.minor);
> +
> +	return 0;
> +
> +fail_input:
> +	sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
> +fail_sysfs:
> +	free_irq(client->irq, priv);
> +fail_probe:
> +	input_free_device(input);
> +fail:
> +	kfree(priv);
> +	return rc;
> +}
> +
> +static int __devexit ili210x_i2c_remove(struct i2c_client *client)
> +{
> +	struct ili210x *priv = dev_get_drvdata(&client->dev);
> +	struct device *dev = &priv->client->dev;
> +
> +	sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
> +	cancel_delayed_work_sync(&priv->dwork);
> +	free_irq(priv->client->irq, priv);
> +	input_unregister_device(priv->input);
> +	kfree(priv);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int 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 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;
> +}
> +#endif
> +
> +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 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
> +
> +static struct i2c_driver ili210x_ts_driver = {
> +	.driver = {
> +		.name = "ili210x_i2c",
> +		.owner = THIS_MODULE,
> +		.pm = &ili210x_i2c_pm,
> +	},
> +	.id_table = ili210x_i2c_id,
> +	.probe = ili210x_i2c_probe,
> +	.remove = ili210x_i2c_remove,
> +};
> +
> +static int __init ili210x_ts_init(void)
> +{
> +	return i2c_add_driver(&ili210x_ts_driver);
> +}
> +module_init(ili210x_ts_init);
> +
> +static void __exit ili210x_ts_exit(void)
> +{
> +	i2c_del_driver(&ili210x_ts_driver);
> +}
> +module_exit(ili210x_ts_exit);
> +
> +MODULE_AUTHOR("Olivier Sobrie <olivier@xxxxxxxxx>");
> +MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/input/ili210x.h b/include/linux/input/ili210x.h
> new file mode 100644
> index 0000000..710b3dd2
> --- /dev/null
> +++ b/include/linux/input/ili210x.h
> @@ -0,0 +1,9 @@
> +#ifndef _ILI210X_H
> +#define _ILI210X_H
> +
> +struct ili210x_platform_data {
> +	unsigned long irq_flags;
> +	int (*get_pendown_state)(void);
> +};
> +
> +#endif
> -- 
> 1.7.5.4
> 

Apart from the comments, this looks all good to me.

    Reviewed-by: Henrik Rydberg <rydberg@xxxxxxxxxxx>

Thanks.
Henrik
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux