All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] Driver for AUO In-Cell touchscreens using pixcir ICs
@ 2011-12-05 20:05 Heiko Stübner
  2011-12-06  5:56 ` Dmitry Torokhov
  0 siblings, 1 reply; 4+ messages in thread
From: Heiko Stübner @ 2011-12-05 20:05 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input

Some displays from AUO have a so called in-cell touchscreen, meaning it
is built directly into the display unit.

Touchdata is gathered through PIXCIR Tango-ICs and processed in an
Atmel ATmega168P with custom firmware. Communication between the host
system and ATmega is done via I2C.

Devices using this touch solution include the Dell Streak5 and the family
of Qisda ebook readers.

The driver reports single- and multi-touch events including touch area
values.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
changes since v1: address comments from Christoph Fritz
(wrong type for error checks, wrong call to input_free_device)

 drivers/input/touchscreen/Kconfig         |   14 +
 drivers/input/touchscreen/Makefile        |    1 +
 drivers/input/touchscreen/auo-pixcir-ts.c |  656 +++++++++++++++++++++++++++++
 include/linux/input/auo-pixcir-ts.h       |   57 +++
 4 files changed, 728 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/auo-pixcir-ts.c
 create mode 100644 include/linux/input/auo-pixcir-ts.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 2b456a9..7da474e 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -98,6 +98,20 @@ config TOUCHSCREEN_ATMEL_MXT
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_mxt_ts.
 
+config TOUCHSCREEN_AUO_PIXCIR
+	tristate "AUO in-cell touchscreen using Pixcir ICs"
+	depends on I2C
+	depends on GPIOLIB
+	default n
+	help
+	  Say Y here if you have a AUO display with in-cell touchscreen
+	  using Pixcir ICs.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called auo-pixcir-ts.
+
 config TOUCHSCREEN_BITSY
 	tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
 	depends on SA1100_BITSY
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index a09c546..f0b4d16 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)	+= atmel_mxt_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o
+obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)	+= auo-pixcir-ts.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_BU21013)       += bu21013_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)	+= cy8ctmg110_ts.o
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
new file mode 100644
index 0000000..91a06d2
--- /dev/null
+++ b/drivers/input/touchscreen/auo-pixcir-ts.c
@@ -0,0 +1,656 @@
+/* drivers/input/touchscreen/auo-pixcir-ts.c
+ *
+ * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * loosely based on auo_touch.c from Dell Streak vendor-kernel
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/i2c.h>
+#include <linux/semaphore.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input/auo-pixcir-ts.h>
+
+/*
+ * Coordinate calculation:
+ * X1 = X1_LSB + X1_MSB*256
+ * Y1 = Y1_LSB + Y1_MSB*256
+ * X2 = X2_LSB + X2_MSB*256
+ * Y2 = Y2_LSB + Y2_MSB*256
+ */
+#define AUO_PIXCIR_REG_X1_LSB		0x00
+#define AUO_PIXCIR_REG_X1_MSB		0x01
+#define AUO_PIXCIR_REG_Y1_LSB		0x02
+#define AUO_PIXCIR_REG_Y1_MSB		0x03
+#define AUO_PIXCIR_REG_X2_LSB		0x04
+#define AUO_PIXCIR_REG_X2_MSB		0x05
+#define AUO_PIXCIR_REG_Y2_LSB		0x06
+#define AUO_PIXCIR_REG_Y2_MSB		0x07
+
+#define AUO_PIXCIR_REG_STRENGTH		0x0d
+#define AUO_PIXCIR_REG_STRENGTH_X1_LSB	0x0e
+#define AUO_PIXCIR_REG_STRENGTH_X1_MSB	0x0f
+
+#define AUO_PIXCIR_REG_RAW_DATA_X	0x2b
+#define AUO_PIXCIR_REG_RAW_DATA_Y	0x4f
+
+#define AUO_PIXCIR_REG_X_SENSITIVITY	0x6f
+#define AUO_PIXCIR_REG_Y_SENSITIVITY	0x70
+#define AUO_PIXCIR_REG_INT_SETTING	0x71
+#define AUO_PIXCIR_REG_INT_WIDTH	0x72
+#define AUO_PIXCIR_REG_POWER_MODE	0x73
+
+#define AUO_PIXCIR_REG_VERSION		0x77
+#define AUO_PIXCIR_REG_CALIBRATE	0x78
+
+#define AUO_PIXCIR_REG_TOUCHAREA_X1	0x1e
+#define AUO_PIXCIR_REG_TOUCHAREA_Y1	0x1f
+#define AUO_PIXCIR_REG_TOUCHAREA_X2	0x20
+#define AUO_PIXCIR_REG_TOUCHAREA_Y2	0x21
+
+#define AUO_PIXCIR_REG_EEPROM_CALIB_X	0x42
+#define AUO_PIXCIR_REG_EEPROM_CALIB_Y	0xad
+
+#define AUO_PIXCIR_INT_TPNUM_MASK	0xe0
+#define AUO_PIXCIR_INT_TPNUM_SHIFT	5
+#define AUO_PIXCIR_INT_RELEASE		(1 << 4)
+#define AUO_PIXCIR_INT_ENABLE		(1 << 3)
+#define AUO_PIXCIR_INT_POL_HIGH		(1 << 2)
+#define AUO_PIXCIR_INT_MODE_MASK	0x03
+
+/*
+ * Power modes:
+ * active:	scan speed 60Hz
+ * sleep:	scan speed 10Hz can be auto-activated, wakeup on 1st touch
+ * deep sleep:	scan speed 1Hz can only be entered or left manually.
+ */
+#define AUO_PIXCIR_POWER_ACTIVE		0x00
+#define AUO_PIXCIR_POWER_SLEEP		0x01
+#define AUO_PIXCIR_POWER_DEEP_SLEEP	0x02
+#define AUO_PIXCIR_POWER_MASK		0x03
+
+#define AUO_PIXCIR_POWER_ALLOW_SLEEP	(1 << 2)
+#define AUO_PIXCIR_POWER_IDLE_TIME(ms)	((ms & 0xf) << 4)
+
+#define AUO_PIXCIR_CALIBRATE		0x03
+
+#define AUO_PIXCIR_EEPROM_CALIB_X_LEN	62
+#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN	36
+
+#define AUO_PIXCIR_RAW_DATA_X_LEN	18
+#define AUO_PIXCIR_RAW_DATA_Y_LEN	11
+
+#define AUO_PIXCIR_STRENGTH_ENABLE	(1 << 0)
+
+/* Touchscreen absolute values */
+#define AUO_PIXCIR_REPORT_POINTS	2
+#define AUO_PIXCIR_MAX_AREA		0xff
+#define AUO_PIXCIR_PENUP_TIMEOUT_MS	10
+
+struct auo_pixcir_ts {
+	struct i2c_client	*client;
+	struct input_dev	*input;
+	char			phys[32];
+
+	/* special handling for touch_indicate interupt mode */
+	bool			touch_ind_mode;
+
+	wait_queue_head_t	wait;
+	bool			stopped;
+	bool			is_open;
+};
+
+struct auo_point_t {
+	int	coord_x;
+	int	coord_y;
+	int	area_major;
+	int	area_minor;
+	int	orientation;
+};
+
+static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
+				   struct auo_point_t *point)
+{
+	struct i2c_client *client = ts->client;
+	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+	uint8_t raw_coord[8];
+	uint8_t raw_area[4];
+	int i, ret;
+
+	/* touch coordinates */
+	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB,
+					    8, raw_coord);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to read coordinate, %d\n", ret);
+		return ret;
+	}
+
+	/* touch area */
+	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1,
+					    4, raw_area);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not read touch area, %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+		point[i].coord_x = raw_coord[4*i+1] << 8 | raw_coord[4*i];
+		point[i].coord_y = raw_coord[4*i+3] << 8 | raw_coord[4*i+2];
+
+		if (point[i].coord_x > pdata->x_max ||
+		    point[i].coord_y > pdata->y_max) {
+			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
+				point[i].coord_x, point[i].coord_y);
+			point[i].coord_x = point[i].coord_y = 0;
+		}
+
+		/* determine touch major, minor and orientation */
+		point[i].area_major = max(raw_area[2*i], raw_area[2*i+1]);
+		point[i].area_minor = min(raw_area[2*i], raw_area[2*i+1]);
+		point[i].orientation = (raw_area[2*i] > raw_area[2*i+1]);
+	}
+
+	return 0;
+}
+
+static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
+{
+	struct auo_pixcir_ts *ts = dev_id;
+	struct i2c_client *client = ts->client;
+	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+	struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
+	int i;
+	int ret;
+	int fingers = 0;
+	int abs = -1;
+
+	while (!ts->stopped) {
+
+		/* check for up event in touch touch_ind_mode */
+		if (ts->touch_ind_mode) {
+			if (gpio_get_value(pdata->gpio_int) == 0) {
+				input_mt_sync(ts->input);
+				input_report_key(ts->input, BTN_TOUCH, 0);
+				input_sync(ts->input);
+				break;
+			}
+		}
+
+		ret = auo_pixcir_collect_data(ts, point);
+		if (ret < 0) {
+			/* we want to loop only in touch_ind_mode */
+			if (!ts->touch_ind_mode)
+				break;
+
+			wait_event_timeout(ts->wait, ts->stopped,
+				msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+			continue;
+		}
+
+		for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
+			if (point[i].coord_x > 0 || point[i].coord_y > 0) {
+				input_report_abs(ts->input, ABS_MT_POSITION_X,
+						 point[i].coord_x);
+				input_report_abs(ts->input, ABS_MT_POSITION_Y,
+						 point[i].coord_y);
+				input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
+						 point[i].area_major);
+				input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
+						 point[i].area_minor);
+				input_report_abs(ts->input, ABS_MT_ORIENTATION,
+						 point[i].orientation);
+				input_mt_sync(ts->input);
+
+				/* use first finger as source for singletouch */
+				if (fingers == 0)
+					abs = i;
+
+				/* number of touch points could also be queried
+				 * via i2c but would require an additional call
+				 */
+				fingers++;
+			}
+		}
+
+		input_report_key(ts->input, BTN_TOUCH, fingers > 0);
+
+		if (abs > -1) {
+			input_report_abs(ts->input, ABS_X, point[abs].coord_x);
+			input_report_abs(ts->input, ABS_Y, point[abs].coord_y);
+		}
+
+		input_sync(ts->input);
+
+		/* we want to loop only in touch_ind_mode */
+		if (!ts->touch_ind_mode)
+			break;
+
+		wait_event_timeout(ts->wait, ts->stopped,
+				 msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Set the power mode of the device.
+ * Valid modes are
+ * - AUO_PIXCIR_POWER_ACTIVE
+ * - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch
+ * - AUO_PIXCIR_POWER_DEEP_SLEEP
+ */
+static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+			AUO_PIXCIR_REG_POWER_MODE, ret);
+		return ret;
+	}
+
+	ret &= (~AUO_PIXCIR_POWER_MASK);
+	ret |= mode;
+
+	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret);
+	if (ret) {
+		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+			AUO_PIXCIR_REG_POWER_MODE, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int auo_pixcir_int_config(struct auo_pixcir_ts *ts, int int_setting)
+{
+	struct i2c_client *client = ts->client;
+	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		return ret;
+	}
+
+	ret &= (~AUO_PIXCIR_INT_MODE_MASK);
+	ret |= int_setting;
+	ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */
+
+	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+					ret);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		return ret;
+	}
+
+	if (pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND)
+		ts->touch_ind_mode = 1;
+	else
+		ts->touch_ind_mode = 0;
+
+	return 0;
+}
+
+/* controll the generation of interrupts on the device side */
+static int auo_pixcir_int_enable(struct auo_pixcir_ts *ts, int enable)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		disable_irq(client->irq);
+		return ret;
+	}
+
+	if (enable)
+		ret |= AUO_PIXCIR_INT_ENABLE;
+	else
+		ret &= ~AUO_PIXCIR_INT_ENABLE;
+
+	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
+					ret);
+	if (ret < 0) {
+		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
+			AUO_PIXCIR_REG_INT_SETTING, ret);
+		disable_irq(client->irq);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int auo_pixcir_start(struct auo_pixcir_ts *ts)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not set power mode, %d\n",
+			ret);
+		return ret;
+	}
+
+	ts->stopped = false;
+	mb();
+	enable_irq(client->irq);
+
+	ret = auo_pixcir_int_enable(ts, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not enable interrupt, %d\n",
+			ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int auo_pixcir_stop(struct auo_pixcir_ts *ts)
+{
+	struct i2c_client *client = ts->client;
+	int ret;
+
+	ret = auo_pixcir_int_enable(ts, 0);
+	if (ret < 0) {
+		dev_err(&client->dev, "could not disable interrupt, %d\n",
+			ret);
+		return ret;
+	}
+
+	/* disable receiving of interrupts */
+	disable_irq(client->irq);
+	ts->stopped = true;
+	mb();
+	wake_up(&ts->wait);
+
+	return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP);
+}
+
+static int auo_pixcir_input_open(struct input_dev *dev)
+{
+	struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+	int ret;
+
+	ret = auo_pixcir_start(ts);
+	if (ret)
+		return ret;
+
+	ts->is_open = true;
+
+	return 0;
+}
+
+static void auo_pixcir_input_close(struct input_dev *dev)
+{
+	struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+
+	ts->is_open = false;
+
+	auo_pixcir_stop(ts);
+
+	return;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int auo_pixcir_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct auo_pixcir_ts *ts = (struct auo_pixcir_ts *)
+						i2c_get_clientdata(client);
+
+	/* if device isn't open it's stopped already */
+	if (ts->is_open) {
+		if (device_may_wakeup(&client->dev)) {
+			enable_irq_wake(client->irq);
+			return auo_pixcir_power_mode(ts,
+						     AUO_PIXCIR_POWER_SLEEP);
+		} else {
+			auo_pixcir_stop(ts);
+		}
+	}
+
+	return 0;
+}
+
+static int auo_pixcir_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct auo_pixcir_ts *ts = (struct auo_pixcir_ts *)
+						i2c_get_clientdata(client);
+
+	/* if device isn't open it was stopped and should stay that way */
+	if (ts->is_open) {
+		if (device_may_wakeup(&client->dev)) {
+			disable_irq_wake(client->irq);
+			/* automatic wakeup on first-touch */
+		} else if (ts->is_open) {
+			return auo_pixcir_start(ts);
+		}
+	}
+
+	return 0;
+}
+
+SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend, auo_pixcir_resume);
+#endif
+
+static int __devinit auo_pixcir_probe(struct i2c_client *client,
+				      const struct i2c_device_id *id)
+{
+	struct auo_pixcir_ts *ts;
+	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+	struct input_dev *input_dev;
+	int ret;
+
+	if (!pdata)
+		return -EINVAL;
+
+	ts = kzalloc(sizeof(struct auo_pixcir_ts), GFP_KERNEL);
+	if (!ts)
+		return -ENOMEM;
+
+	ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int");
+	if (ret) {
+		dev_err(&client->dev, "request of gpio %d failed, %d\n",
+			pdata->gpio_int, ret);
+		goto err_gpio_int;
+	}
+
+	ret = gpio_request(pdata->gpio_rst, "auo_pixcir_ts_rst");
+	if (ret) {
+		dev_err(&client->dev, "request of gpio %d failed, %d\n",
+			pdata->gpio_int, ret);
+		goto err_gpio_rst;
+	}
+
+	if (pdata->init_hw)
+		pdata->init_hw();
+
+	ts->client = client;
+	ts->touch_ind_mode = 0;
+	init_waitqueue_head(&ts->wait);
+
+	snprintf(ts->phys, sizeof(ts->phys),
+		 "%s/input0", dev_name(&client->dev));
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&client->dev, "could not allocate input device\n");
+		goto err_input_alloc;
+	}
+
+	ts->input = input_dev;
+
+	input_dev->name = "auo_pixcir_ts";
+	input_dev->phys = ts->phys;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = &client->dev;
+
+	input_dev->open = auo_pixcir_input_open;
+	input_dev->close = auo_pixcir_input_close;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+
+	/* For single touch */
+	input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0);
+
+	/* For multi touch */
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+			     pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+			     pdata->y_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
+			     AUO_PIXCIR_MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
+			     AUO_PIXCIR_MAX_AREA, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
+	if (ret < 0)
+		goto err_fw_vers;
+	dev_info(&client->dev, "firmware version 0x%X\n", ret);
+
+	ret = auo_pixcir_int_config(ts, pdata->int_setting);
+	if (ret)
+		goto err_fw_vers;
+
+	/* disable irqs on the device side before irq-request */
+	auo_pixcir_int_enable(ts, 0);
+	ts->stopped = true;
+	ts->is_open = false;
+
+	ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt,
+				   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				   input_dev->name, ts);
+	if (ret) {
+		dev_err(&client->dev, "irq %d requested failed\n", client->irq);
+		goto err_fw_vers;
+	}
+
+	/* stop device and put it into deep sleep until it is opened */
+	ret = auo_pixcir_stop(ts);
+	if (ret < 0)
+		goto err_input_register;
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		dev_err(&client->dev, "could not register input device\n");
+		goto err_input_register;
+	}
+
+	input_set_drvdata(ts->input, ts);
+
+	i2c_set_clientdata(client, ts);
+
+	return 0;
+
+err_input_register:
+	free_irq(client->irq, ts);
+err_fw_vers:
+	input_free_device(input_dev);
+err_input_alloc:
+	if (pdata->exit_hw)
+		pdata->exit_hw();
+	gpio_free(pdata->gpio_rst);
+err_gpio_rst:
+	gpio_free(pdata->gpio_int);
+err_gpio_int:
+	kfree(ts);
+
+	return ret;
+}
+
+static int __devexit auo_pixcir_remove(struct i2c_client *client)
+{
+	struct auo_pixcir_ts *ts = (struct auo_pixcir_ts *)
+						i2c_get_clientdata(client);
+	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+
+	auo_pixcir_stop(ts);
+
+	free_irq(client->irq, ts);
+
+	input_unregister_device(ts->input);
+
+	if (pdata->exit_hw)
+		pdata->exit_hw();
+
+	gpio_free(pdata->gpio_int);
+	gpio_free(pdata->gpio_rst);
+
+	kfree(ts);
+
+	return 0;
+}
+
+static const struct i2c_device_id auo_pixcir_idtable[] = {
+	{ "auo_pixcir_ts", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable);
+
+static struct i2c_driver auo_pixcir_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "auo_pixcir_ts",
+#ifdef CONFIG_PM
+		.pm	= &auo_pixcir_pm_ops,
+#endif
+	},
+	.probe		= auo_pixcir_probe,
+	.remove		= __devexit_p(auo_pixcir_remove),
+	.id_table	= auo_pixcir_idtable,
+};
+
+static int __init auo_pixcir_init(void)
+{
+	return i2c_add_driver(&auo_pixcir_driver);
+}
+module_init(auo_pixcir_init);
+
+static void __exit auo_pixcir_exit(void)
+{
+	i2c_del_driver(&auo_pixcir_driver);
+}
+module_exit(auo_pixcir_exit);
+
+MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
diff --git a/include/linux/input/auo-pixcir-ts.h b/include/linux/input/auo-pixcir-ts.h
new file mode 100644
index 0000000..d4221be
--- /dev/null
+++ b/include/linux/input/auo-pixcir-ts.h
@@ -0,0 +1,57 @@
+/* drivers/input/touchscreen/auo-pixcir-ts.h
+ *
+ * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on auo_touch.h from Dell Streak kernel
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __AUO_PIXCIR_TS_H__
+#define __AUO_PIXCIR_TS_H__
+
+/*
+ * Interrupt modes:
+ * periodical:		interrupt is asserted periodicaly
+ * compare coordinates:	interrupt is asserted when coordinates change
+ * indicate touch:	interrupt is asserted during touch
+ */
+#define AUO_PIXCIR_INT_PERIODICAL	0x00
+#define AUO_PIXCIR_INT_COMP_COORD	0x01
+#define AUO_PIXCIR_INT_TOUCH_IND	0x02
+
+/*
+ * @gpio_int		interrupt gpio
+ * @gpio_rst		reset gpio, used in init_hw and exit_hw
+ * @int_setting		one of AUO_PIXCIR_INT_*
+ * @init_hw		hardwarespecific init
+ * @exit_hw		hardwarespecific shutdown
+ * @x_max		x-resolution
+ * @y_max		y-resolution
+ */
+struct auo_pixcir_ts_platdata {
+	int gpio_int;
+	int gpio_rst;
+
+	int int_setting;
+
+	void (*init_hw)(void);
+	void (*exit_hw)(void);
+
+	unsigned int x_max;
+	unsigned int y_max;
+};
+
+#endif
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] Driver for AUO In-Cell touchscreens using pixcir ICs
  2011-12-05 20:05 [PATCH v2] Driver for AUO In-Cell touchscreens using pixcir ICs Heiko Stübner
@ 2011-12-06  5:56 ` Dmitry Torokhov
  2011-12-07 14:31   ` Heiko Stübner
  0 siblings, 1 reply; 4+ messages in thread
From: Dmitry Torokhov @ 2011-12-06  5:56 UTC (permalink / raw)
  To: Heiko Stübner; +Cc: linux-input, Henrik Rydberg

Hi Heiko,

On Mon, Dec 05, 2011 at 09:05:30PM +0100, Heiko Stübner wrote:
> Some displays from AUO have a so called in-cell touchscreen, meaning it
> is built directly into the display unit.
> 
> Touchdata is gathered through PIXCIR Tango-ICs and processed in an
> Atmel ATmega168P with custom firmware. Communication between the host
> system and ATmega is done via I2C.
> 
> Devices using this touch solution include the Dell Streak5 and the family
> of Qisda ebook readers.
> 
> The driver reports single- and multi-touch events including touch area
> values.
> 
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
> changes since v1: address comments from Christoph Fritz
> (wrong type for error checks, wrong call to input_free_device)
> 
>  drivers/input/touchscreen/Kconfig         |   14 +
>  drivers/input/touchscreen/Makefile        |    1 +
>  drivers/input/touchscreen/auo-pixcir-ts.c |  656 +++++++++++++++++++++++++++++
>  include/linux/input/auo-pixcir-ts.h       |   57 +++
>  4 files changed, 728 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/touchscreen/auo-pixcir-ts.c
>  create mode 100644 include/linux/input/auo-pixcir-ts.h
> 
> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 2b456a9..7da474e 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -98,6 +98,20 @@ config TOUCHSCREEN_ATMEL_MXT
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called atmel_mxt_ts.
>  
> +config TOUCHSCREEN_AUO_PIXCIR
> +	tristate "AUO in-cell touchscreen using Pixcir ICs"
> +	depends on I2C
> +	depends on GPIOLIB
> +	default n

No need for "default n"

> +	help
> +	  Say Y here if you have a AUO display with in-cell touchscreen
> +	  using Pixcir ICs.
> +
> +	  If unsure, say N.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called auo-pixcir-ts.
> +
>  config TOUCHSCREEN_BITSY
>  	tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
>  	depends on SA1100_BITSY
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index a09c546..f0b4d16 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)	+= ad7879-spi.o
>  obj-$(CONFIG_TOUCHSCREEN_ADS7846)	+= ads7846.o
>  obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)	+= atmel_mxt_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC)	+= atmel_tsadcc.o
> +obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)	+= auo-pixcir-ts.o
>  obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
>  obj-$(CONFIG_TOUCHSCREEN_BU21013)       += bu21013_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)	+= cy8ctmg110_ts.o
> diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
> new file mode 100644
> index 0000000..91a06d2
> --- /dev/null
> +++ b/drivers/input/touchscreen/auo-pixcir-ts.c
> @@ -0,0 +1,656 @@
> +/* drivers/input/touchscreen/auo-pixcir-ts.c

Please no file names in files - this way you don't need to change it
here if you rename it later.

> + *
> + * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
> + *
> + * loosely based on auo_touch.c from Dell Streak vendor-kernel
> + *
> + * Copyright (c) 2008 QUALCOMM Incorporated.
> + * Copyright (c) 2008 QUALCOMM USA, INC.
> + *
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/input.h>
> +#include <linux/platform_device.h>
> +#include <linux/jiffies.h>
> +#include <linux/io.h>
> +#include <linux/uaccess.h>

Do you need this?

> +#include <linux/miscdevice.h>

And this?

> +#include <linux/i2c.h>
> +#include <linux/semaphore.h>

I think you want mutex.h instead.

> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/input/auo-pixcir-ts.h>
> +
> +/*
> + * Coordinate calculation:
> + * X1 = X1_LSB + X1_MSB*256
> + * Y1 = Y1_LSB + Y1_MSB*256
> + * X2 = X2_LSB + X2_MSB*256
> + * Y2 = Y2_LSB + Y2_MSB*256
> + */
> +#define AUO_PIXCIR_REG_X1_LSB		0x00
> +#define AUO_PIXCIR_REG_X1_MSB		0x01
> +#define AUO_PIXCIR_REG_Y1_LSB		0x02
> +#define AUO_PIXCIR_REG_Y1_MSB		0x03
> +#define AUO_PIXCIR_REG_X2_LSB		0x04
> +#define AUO_PIXCIR_REG_X2_MSB		0x05
> +#define AUO_PIXCIR_REG_Y2_LSB		0x06
> +#define AUO_PIXCIR_REG_Y2_MSB		0x07
> +
> +#define AUO_PIXCIR_REG_STRENGTH		0x0d
> +#define AUO_PIXCIR_REG_STRENGTH_X1_LSB	0x0e
> +#define AUO_PIXCIR_REG_STRENGTH_X1_MSB	0x0f
> +
> +#define AUO_PIXCIR_REG_RAW_DATA_X	0x2b
> +#define AUO_PIXCIR_REG_RAW_DATA_Y	0x4f
> +
> +#define AUO_PIXCIR_REG_X_SENSITIVITY	0x6f
> +#define AUO_PIXCIR_REG_Y_SENSITIVITY	0x70
> +#define AUO_PIXCIR_REG_INT_SETTING	0x71
> +#define AUO_PIXCIR_REG_INT_WIDTH	0x72
> +#define AUO_PIXCIR_REG_POWER_MODE	0x73
> +
> +#define AUO_PIXCIR_REG_VERSION		0x77
> +#define AUO_PIXCIR_REG_CALIBRATE	0x78
> +
> +#define AUO_PIXCIR_REG_TOUCHAREA_X1	0x1e
> +#define AUO_PIXCIR_REG_TOUCHAREA_Y1	0x1f
> +#define AUO_PIXCIR_REG_TOUCHAREA_X2	0x20
> +#define AUO_PIXCIR_REG_TOUCHAREA_Y2	0x21
> +
> +#define AUO_PIXCIR_REG_EEPROM_CALIB_X	0x42
> +#define AUO_PIXCIR_REG_EEPROM_CALIB_Y	0xad
> +
> +#define AUO_PIXCIR_INT_TPNUM_MASK	0xe0
> +#define AUO_PIXCIR_INT_TPNUM_SHIFT	5
> +#define AUO_PIXCIR_INT_RELEASE		(1 << 4)
> +#define AUO_PIXCIR_INT_ENABLE		(1 << 3)
> +#define AUO_PIXCIR_INT_POL_HIGH		(1 << 2)
> +#define AUO_PIXCIR_INT_MODE_MASK	0x03
> +
> +/*
> + * Power modes:
> + * active:	scan speed 60Hz
> + * sleep:	scan speed 10Hz can be auto-activated, wakeup on 1st touch
> + * deep sleep:	scan speed 1Hz can only be entered or left manually.
> + */
> +#define AUO_PIXCIR_POWER_ACTIVE		0x00
> +#define AUO_PIXCIR_POWER_SLEEP		0x01
> +#define AUO_PIXCIR_POWER_DEEP_SLEEP	0x02
> +#define AUO_PIXCIR_POWER_MASK		0x03
> +
> +#define AUO_PIXCIR_POWER_ALLOW_SLEEP	(1 << 2)
> +#define AUO_PIXCIR_POWER_IDLE_TIME(ms)	((ms & 0xf) << 4)
> +
> +#define AUO_PIXCIR_CALIBRATE		0x03
> +
> +#define AUO_PIXCIR_EEPROM_CALIB_X_LEN	62
> +#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN	36
> +
> +#define AUO_PIXCIR_RAW_DATA_X_LEN	18
> +#define AUO_PIXCIR_RAW_DATA_Y_LEN	11
> +
> +#define AUO_PIXCIR_STRENGTH_ENABLE	(1 << 0)
> +
> +/* Touchscreen absolute values */
> +#define AUO_PIXCIR_REPORT_POINTS	2
> +#define AUO_PIXCIR_MAX_AREA		0xff
> +#define AUO_PIXCIR_PENUP_TIMEOUT_MS	10
> +
> +struct auo_pixcir_ts {
> +	struct i2c_client	*client;
> +	struct input_dev	*input;
> +	char			phys[32];
> +
> +	/* special handling for touch_indicate interupt mode */
> +	bool			touch_ind_mode;
> +
> +	wait_queue_head_t	wait;
> +	bool			stopped;
> +	bool			is_open;
> +};
> +
> +struct auo_point_t {
> +	int	coord_x;
> +	int	coord_y;
> +	int	area_major;
> +	int	area_minor;
> +	int	orientation;
> +};
> +
> +static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
> +				   struct auo_point_t *point)
> +{
> +	struct i2c_client *client = ts->client;
> +	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
> +	uint8_t raw_coord[8];
> +	uint8_t raw_area[4];
> +	int i, ret;
> +
> +	/* touch coordinates */
> +	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB,
> +					    8, raw_coord);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "failed to read coordinate, %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* touch area */
> +	ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1,
> +					    4, raw_area);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "could not read touch area, %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
> +		point[i].coord_x = raw_coord[4*i+1] << 8 | raw_coord[4*i];
> +		point[i].coord_y = raw_coord[4*i+3] << 8 | raw_coord[4*i+2];
> +
> +		if (point[i].coord_x > pdata->x_max ||
> +		    point[i].coord_y > pdata->y_max) {
> +			dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
> +				point[i].coord_x, point[i].coord_y);
> +			point[i].coord_x = point[i].coord_y = 0;
> +		}
> +
> +		/* determine touch major, minor and orientation */
> +		point[i].area_major = max(raw_area[2*i], raw_area[2*i+1]);
> +		point[i].area_minor = min(raw_area[2*i], raw_area[2*i+1]);
> +		point[i].orientation = (raw_area[2*i] > raw_area[2*i+1]);
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
> +{
> +	struct auo_pixcir_ts *ts = dev_id;
> +	struct i2c_client *client = ts->client;
> +	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
> +	struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
> +	int i;
> +	int ret;
> +	int fingers = 0;
> +	int abs = -1;
> +
> +	while (!ts->stopped) {
> +
> +		/* check for up event in touch touch_ind_mode */
> +		if (ts->touch_ind_mode) {
> +			if (gpio_get_value(pdata->gpio_int) == 0) {
> +				input_mt_sync(ts->input);
> +				input_report_key(ts->input, BTN_TOUCH, 0);
> +				input_sync(ts->input);
> +				break;
> +			}
> +		}
> +
> +		ret = auo_pixcir_collect_data(ts, point);
> +		if (ret < 0) {
> +			/* we want to loop only in touch_ind_mode */
> +			if (!ts->touch_ind_mode)
> +				break;
> +
> +			wait_event_timeout(ts->wait, ts->stopped,
> +				msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
> +			continue;
> +		}
> +
> +		for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
> +			if (point[i].coord_x > 0 || point[i].coord_y > 0) {
> +				input_report_abs(ts->input, ABS_MT_POSITION_X,
> +						 point[i].coord_x);
> +				input_report_abs(ts->input, ABS_MT_POSITION_Y,
> +						 point[i].coord_y);
> +				input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
> +						 point[i].area_major);
> +				input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
> +						 point[i].area_minor);
> +				input_report_abs(ts->input, ABS_MT_ORIENTATION,
> +						 point[i].orientation);
> +				input_mt_sync(ts->input);

Is there a way to track the contacts and switch to protocol B by any
chance?

> +
> +				/* use first finger as source for singletouch */
> +				if (fingers == 0)
> +					abs = i;
> +
> +				/* number of touch points could also be queried
> +				 * via i2c but would require an additional call
> +				 */
> +				fingers++;
> +			}
> +		}
> +
> +		input_report_key(ts->input, BTN_TOUCH, fingers > 0);
> +
> +		if (abs > -1) {
> +			input_report_abs(ts->input, ABS_X, point[abs].coord_x);
> +			input_report_abs(ts->input, ABS_Y, point[abs].coord_y);
> +		}
> +
> +		input_sync(ts->input);
> +
> +		/* we want to loop only in touch_ind_mode */
> +		if (!ts->touch_ind_mode)
> +			break;
> +
> +		wait_event_timeout(ts->wait, ts->stopped,
> +				 msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Set the power mode of the device.
> + * Valid modes are
> + * - AUO_PIXCIR_POWER_ACTIVE
> + * - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch
> + * - AUO_PIXCIR_POWER_DEEP_SLEEP
> + */
> +static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode)
> +{
> +	struct i2c_client *client = ts->client;
> +	int ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
> +			AUO_PIXCIR_REG_POWER_MODE, ret);
> +		return ret;
> +	}
> +
> +	ret &= (~AUO_PIXCIR_POWER_MASK);
> +	ret |= mode;
> +
> +	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret);
> +	if (ret) {
> +		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
> +			AUO_PIXCIR_REG_POWER_MODE, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int auo_pixcir_int_config(struct auo_pixcir_ts *ts, int int_setting)

__devinit?

> +{
> +	struct i2c_client *client = ts->client;
> +	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
> +	int ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
> +			AUO_PIXCIR_REG_INT_SETTING, ret);
> +		return ret;
> +	}
> +
> +	ret &= (~AUO_PIXCIR_INT_MODE_MASK);
> +	ret |= int_setting;
> +	ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */
> +
> +	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
> +					ret);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
> +			AUO_PIXCIR_REG_INT_SETTING, ret);
> +		return ret;
> +	}
> +
> +	if (pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND)
> +		ts->touch_ind_mode = 1;
> +	else
> +		ts->touch_ind_mode = 0;

	ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND;
> +
> +	return 0;
> +}
> +
> +/* controll the generation of interrupts on the device side */

Typo: control

> +static int auo_pixcir_int_enable(struct auo_pixcir_ts *ts, int enable)

bool enable

Probably call it "auo_pixcir_int_toggle".

> +{
> +	struct i2c_client *client = ts->client;
> +	int ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "unable to read reg %Xh, %d\n",
> +			AUO_PIXCIR_REG_INT_SETTING, ret);
> +		disable_irq(client->irq);

This looks like wrong place for disable_irq(). Caller should do it IMO.

> +		return ret;
> +	}
> +
> +	if (enable)
> +		ret |= AUO_PIXCIR_INT_ENABLE;
> +	else
> +		ret &= ~AUO_PIXCIR_INT_ENABLE;
> +
> +	ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
> +					ret);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "unable to write reg %Xh, %d\n",
> +			AUO_PIXCIR_REG_INT_SETTING, ret);
> +		disable_irq(client->irq);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int auo_pixcir_start(struct auo_pixcir_ts *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int ret;
> +
> +	ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "could not set power mode, %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	ts->stopped = false;
> +	mb();
> +	enable_irq(client->irq);
> +
> +	ret = auo_pixcir_int_enable(ts, 1);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "could not enable interrupt, %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int auo_pixcir_stop(struct auo_pixcir_ts *ts)
> +{
> +	struct i2c_client *client = ts->client;
> +	int ret;
> +
> +	ret = auo_pixcir_int_enable(ts, 0);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "could not disable interrupt, %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	/* disable receiving of interrupts */
> +	disable_irq(client->irq);
> +	ts->stopped = true;
> +	mb();
> +	wake_up(&ts->wait);
> +
> +	return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP);
> +}
> +
> +static int auo_pixcir_input_open(struct input_dev *dev)
> +{
> +	struct auo_pixcir_ts *ts = input_get_drvdata(dev);
> +	int ret;
> +
> +	ret = auo_pixcir_start(ts);
> +	if (ret)
> +		return ret;
> +
> +	ts->is_open = true;
> +
> +	return 0;
> +}
> +
> +static void auo_pixcir_input_close(struct input_dev *dev)
> +{
> +	struct auo_pixcir_ts *ts = input_get_drvdata(dev);
> +
> +	ts->is_open = false;
> +
> +	auo_pixcir_stop(ts);
> +
> +	return;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int auo_pixcir_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct auo_pixcir_ts *ts = (struct auo_pixcir_ts *)

The cast is not needed.

> +						i2c_get_clientdata(client);
> +
> +	/* if device isn't open it's stopped already */
> +	if (ts->is_open) {
> +		if (device_may_wakeup(&client->dev)) {
> +			enable_irq_wake(client->irq);
> +			return auo_pixcir_power_mode(ts,
> +						     AUO_PIXCIR_POWER_SLEEP);
> +		} else {
> +			auo_pixcir_stop(ts);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int auo_pixcir_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct auo_pixcir_ts *ts = (struct auo_pixcir_ts *)

The cast is not needed.

> +						i2c_get_clientdata(client);
> +
> +	/* if device isn't open it was stopped and should stay that way */

If it is configured as wakeup source it should still wake up the system.

> +	if (ts->is_open) {
> +		if (device_may_wakeup(&client->dev)) {
> +			disable_irq_wake(client->irq);
> +			/* automatic wakeup on first-touch */
> +		} else if (ts->is_open) {

This needs locking WRT open/close. You can use input->users and
input->mutex instead of ts->is_open.

> +			return auo_pixcir_start(ts);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend, auo_pixcir_resume);

static and pull it out of #ifdef and you can drop #ifdef in driver
definition as well.

> +#endif
> +
> +static int __devinit auo_pixcir_probe(struct i2c_client *client,
> +				      const struct i2c_device_id *id)
> +{
> +	struct auo_pixcir_ts *ts;
> +	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
> +	struct input_dev *input_dev;
> +	int ret;
> +
> +	if (!pdata)
> +		return -EINVAL;
> +
> +	ts = kzalloc(sizeof(struct auo_pixcir_ts), GFP_KERNEL);
> +	if (!ts)
> +		return -ENOMEM;
> +
> +	ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int");
> +	if (ret) {
> +		dev_err(&client->dev, "request of gpio %d failed, %d\n",
> +			pdata->gpio_int, ret);
> +		goto err_gpio_int;
> +	}
> +
> +	ret = gpio_request(pdata->gpio_rst, "auo_pixcir_ts_rst");
> +	if (ret) {
> +		dev_err(&client->dev, "request of gpio %d failed, %d\n",
> +			pdata->gpio_int, ret);
> +		goto err_gpio_rst;
> +	}

I do not see it actually being used?

> +
> +	if (pdata->init_hw)
> +		pdata->init_hw();
> +
> +	ts->client = client;
> +	ts->touch_ind_mode = 0;
> +	init_waitqueue_head(&ts->wait);
> +
> +	snprintf(ts->phys, sizeof(ts->phys),
> +		 "%s/input0", dev_name(&client->dev));
> +
> +	input_dev = input_allocate_device();
> +	if (!input_dev) {
> +		dev_err(&client->dev, "could not allocate input device\n");
> +		goto err_input_alloc;
> +	}
> +
> +	ts->input = input_dev;
> +
> +	input_dev->name = "auo_pixcir_ts";

Could do with a better name I think. It does not need to resemble file
name.

> +	input_dev->phys = ts->phys;
> +	input_dev->id.bustype = BUS_I2C;
> +	input_dev->dev.parent = &client->dev;
> +
> +	input_dev->open = auo_pixcir_input_open;
> +	input_dev->close = auo_pixcir_input_close;
> +
> +	__set_bit(EV_ABS, input_dev->evbit);
> +	__set_bit(EV_KEY, input_dev->evbit);
> +
> +	__set_bit(BTN_TOUCH, input_dev->keybit);
> +
> +	/* For single touch */
> +	input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0);
> +	input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0);
> +
> +	/* For multi touch */
> +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
> +			     pdata->x_max, 0, 0);
> +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
> +			     pdata->y_max, 0, 0);
> +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
> +			     AUO_PIXCIR_MAX_AREA, 0, 0);
> +	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
> +			     AUO_PIXCIR_MAX_AREA, 0, 0);
> +	input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
> +
> +	ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
> +	if (ret < 0)
> +		goto err_fw_vers;
> +	dev_info(&client->dev, "firmware version 0x%X\n", ret);
> +
> +	ret = auo_pixcir_int_config(ts, pdata->int_setting);
> +	if (ret)
> +		goto err_fw_vers;
> +
> +	/* disable irqs on the device side before irq-request */
> +	auo_pixcir_int_enable(ts, 0);
> +	ts->stopped = true;
> +	ts->is_open = false;
> +
> +	ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt,
> +				   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +				   input_dev->name, ts);
> +	if (ret) {
> +		dev_err(&client->dev, "irq %d requested failed\n", client->irq);
> +		goto err_fw_vers;
> +	}
> +
> +	/* stop device and put it into deep sleep until it is opened */
> +	ret = auo_pixcir_stop(ts);
> +	if (ret < 0)
> +		goto err_input_register;
> +
> +	ret = input_register_device(input_dev);
> +	if (ret) {
> +		dev_err(&client->dev, "could not register input device\n");
> +		goto err_input_register;
> +	}
> +
> +	input_set_drvdata(ts->input, ts);

Just do this earlier and then auo_pixcir_stop() is enough, you won't
need to call auo_pixcir_int_enable() before registering IRQ.

> +
> +	i2c_set_clientdata(client, ts);
> +
> +	return 0;
> +
> +err_input_register:
> +	free_irq(client->irq, ts);
> +err_fw_vers:
> +	input_free_device(input_dev);
> +err_input_alloc:
> +	if (pdata->exit_hw)
> +		pdata->exit_hw();
> +	gpio_free(pdata->gpio_rst);
> +err_gpio_rst:
> +	gpio_free(pdata->gpio_int);
> +err_gpio_int:
> +	kfree(ts);
> +
> +	return ret;
> +}
> +
> +static int __devexit auo_pixcir_remove(struct i2c_client *client)
> +{
> +	struct auo_pixcir_ts *ts = (struct auo_pixcir_ts *)

No need to cast.

> +						i2c_get_clientdata(client);
> +	struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;

const.

> +
> +	auo_pixcir_stop(ts);

This should not be needed as the device should be closed at this point.

> +
> +	free_irq(client->irq, ts);
> +
> +	input_unregister_device(ts->input);
> +
> +	if (pdata->exit_hw)
> +		pdata->exit_hw();
> +
> +	gpio_free(pdata->gpio_int);
> +	gpio_free(pdata->gpio_rst);
> +
> +	kfree(ts);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id auo_pixcir_idtable[] = {
> +	{ "auo_pixcir_ts", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable);
> +
> +static struct i2c_driver auo_pixcir_driver = {
> +	.driver = {
> +		.owner	= THIS_MODULE,
> +		.name	= "auo_pixcir_ts",
> +#ifdef CONFIG_PM
> +		.pm	= &auo_pixcir_pm_ops,
> +#endif
> +	},
> +	.probe		= auo_pixcir_probe,
> +	.remove		= __devexit_p(auo_pixcir_remove),
> +	.id_table	= auo_pixcir_idtable,
> +};
> +
> +static int __init auo_pixcir_init(void)
> +{
> +	return i2c_add_driver(&auo_pixcir_driver);
> +}
> +module_init(auo_pixcir_init);
> +
> +static void __exit auo_pixcir_exit(void)
> +{
> +	i2c_del_driver(&auo_pixcir_driver);
> +}
> +module_exit(auo_pixcir_exit);
> +
> +MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
> diff --git a/include/linux/input/auo-pixcir-ts.h b/include/linux/input/auo-pixcir-ts.h
> new file mode 100644
> index 0000000..d4221be
> --- /dev/null
> +++ b/include/linux/input/auo-pixcir-ts.h
> @@ -0,0 +1,57 @@
> +/* drivers/input/touchscreen/auo-pixcir-ts.h
> + *
> + * Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
> + *
> + * based on auo_touch.h from Dell Streak kernel
> + *
> + * Copyright (c) 2008 QUALCOMM Incorporated.
> + * Copyright (c) 2008 QUALCOMM USA, INC.
> + *
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef __AUO_PIXCIR_TS_H__
> +#define __AUO_PIXCIR_TS_H__
> +
> +/*
> + * Interrupt modes:
> + * periodical:		interrupt is asserted periodicaly
> + * compare coordinates:	interrupt is asserted when coordinates change
> + * indicate touch:	interrupt is asserted during touch
> + */
> +#define AUO_PIXCIR_INT_PERIODICAL	0x00
> +#define AUO_PIXCIR_INT_COMP_COORD	0x01
> +#define AUO_PIXCIR_INT_TOUCH_IND	0x02
> +
> +/*
> + * @gpio_int		interrupt gpio
> + * @gpio_rst		reset gpio, used in init_hw and exit_hw

OK, if it is used by platform code only then it should not be here.

> + * @int_setting		one of AUO_PIXCIR_INT_*
> + * @init_hw		hardwarespecific init
> + * @exit_hw		hardwarespecific shutdown
> + * @x_max		x-resolution
> + * @y_max		y-resolution
> + */
> +struct auo_pixcir_ts_platdata {
> +	int gpio_int;
> +	int gpio_rst;
> +
> +	int int_setting;
> +
> +	void (*init_hw)(void);

Maybe pass client or device so that we could theoretically handle
multiple devices?

> +	void (*exit_hw)(void);
> +
> +	unsigned int x_max;
> +	unsigned int y_max;
> +};
> +
> +#endif
> -- 
> 1.7.5.4
> 

Thanks.

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] Driver for AUO In-Cell touchscreens using pixcir ICs
  2011-12-06  5:56 ` Dmitry Torokhov
@ 2011-12-07 14:31   ` Heiko Stübner
  2011-12-07 22:09     ` Heiko Stübner
  0 siblings, 1 reply; 4+ messages in thread
From: Heiko Stübner @ 2011-12-07 14:31 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, Henrik Rydberg

Hi Dmitry,

thanks for the review

Am Dienstag, 6. Dezember 2011, 06:56:37 schrieb Dmitry Torokhov:
> On Mon, Dec 05, 2011 at 09:05:30PM +0100, Heiko Stübner wrote:
> > Some displays from AUO have a so called in-cell touchscreen, meaning it
> > is built directly into the display unit.
> > 
> > Touchdata is gathered through PIXCIR Tango-ICs and processed in an
> > Atmel ATmega168P with custom firmware. Communication between the host
> > system and ATmega is done via I2C.
> > 
> > Devices using this touch solution include the Dell Streak5 and the family
> > of Qisda ebook readers.
> > 
> > The driver reports single- and multi-touch events including touch area
> > values.
> > 
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > ---
> > changes since v1: address comments from Christoph Fritz
> > (wrong type for error checks, wrong call to input_free_device)
> > 
> >  drivers/input/touchscreen/Kconfig         |   14 +
> >  drivers/input/touchscreen/Makefile        |    1 +
> >  drivers/input/touchscreen/auo-pixcir-ts.c |  656
> >  +++++++++++++++++++++++++++++ include/linux/input/auo-pixcir-ts.h      
> >  |   57 +++
> >  4 files changed, 728 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/input/touchscreen/auo-pixcir-ts.c
> >  create mode 100644 include/linux/input/auo-pixcir-ts.h
> > 

[ ... small corrections, thanks for finding them ... ]

> > +
> > +		for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
> > +			if (point[i].coord_x > 0 || point[i].coord_y > 0) {
> > +				input_report_abs(ts->input, ABS_MT_POSITION_X,
> > +						 point[i].coord_x);
> > +				input_report_abs(ts->input, ABS_MT_POSITION_Y,
> > +						 point[i].coord_y);
> > +				input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
> > +						 point[i].area_major);
> > +				input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
> > +						 point[i].area_minor);
> > +				input_report_abs(ts->input, ABS_MT_ORIENTATION,
> > +						 point[i].orientation);
> > +				input_mt_sync(ts->input);
> 
> Is there a way to track the contacts and switch to protocol B by any
> chance?
I'm not sure, but will check how the device handles changing the number of 
touching fingers and if it would be possible to switch.

> > +
> > +				/* use first finger as source for singletouch */
> > +				if (fingers == 0)
> > +					abs = i;
> > +
> > +				/* number of touch points could also be queried
> > +				 * via i2c but would require an additional call
> > +				 */
> > +				fingers++;
> > +			}
> > +		}

[ ... more small stuff for me to fix ... ]

> > +
> > +	ret = input_register_device(input_dev);
> > +	if (ret) {
> > +		dev_err(&client->dev, "could not register input device\n");
> > +		goto err_input_register;
> > +	}
> > +
> > +	input_set_drvdata(ts->input, ts);
> 
> Just do this earlier and then auo_pixcir_stop() is enough, you won't
> need to call auo_pixcir_int_enable() before registering IRQ.

Just to be sure, you mean moving the input_set_drvdata before the irq-
registration?

> > +
> > +	i2c_set_clientdata(client, ts);
> > +
> > +	return 0;
> > +

[ ... and still more small stuff ]

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] Driver for AUO In-Cell touchscreens using pixcir ICs
  2011-12-07 14:31   ` Heiko Stübner
@ 2011-12-07 22:09     ` Heiko Stübner
  0 siblings, 0 replies; 4+ messages in thread
From: Heiko Stübner @ 2011-12-07 22:09 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, Henrik Rydberg

Am Mittwoch 07 Dezember 2011, 15:31:20 schrieb Heiko Stübner:
> Hi Dmitry,
> 
> thanks for the review
> 
> Am Dienstag, 6. Dezember 2011, 06:56:37 schrieb Dmitry Torokhov:
> > On Mon, Dec 05, 2011 at 09:05:30PM +0100, Heiko Stübner wrote:
> > > Some displays from AUO have a so called in-cell touchscreen, meaning it
> > > is built directly into the display unit.
> > > 
> > > Touchdata is gathered through PIXCIR Tango-ICs and processed in an
> > > Atmel ATmega168P with custom firmware. Communication between the host
> > > system and ATmega is done via I2C.
> > > 
> > > Devices using this touch solution include the Dell Streak5 and the
> > > family of Qisda ebook readers.
> > > 
> > > The driver reports single- and multi-touch events including touch area
> > > values.
> > > 
> > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > ---
> > > changes since v1: address comments from Christoph Fritz
> > > (wrong type for error checks, wrong call to input_free_device)
> > > 
> > >  drivers/input/touchscreen/Kconfig         |   14 +
> > >  drivers/input/touchscreen/Makefile        |    1 +
> > >  drivers/input/touchscreen/auo-pixcir-ts.c |  656
> > >  +++++++++++++++++++++++++++++ include/linux/input/auo-pixcir-ts.h
> > >  
> > >  |   57 +++
> > >  
> > >  4 files changed, 728 insertions(+), 0 deletions(-)
> > >  create mode 100644 drivers/input/touchscreen/auo-pixcir-ts.c
> > >  create mode 100644 include/linux/input/auo-pixcir-ts.h
> 
> [ ... small corrections, thanks for finding them ... ]
> 
> > > +
> > > +		for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
> > > +			if (point[i].coord_x > 0 || point[i].coord_y > 0) {
> > > +				input_report_abs(ts->input, ABS_MT_POSITION_X,
> > > +						 point[i].coord_x);
> > > +				input_report_abs(ts->input, ABS_MT_POSITION_Y,
> > > +						 point[i].coord_y);
> > > +				input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
> > > +						 point[i].area_major);
> > > +				input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
> > > +						 point[i].area_minor);
> > > +				input_report_abs(ts->input, ABS_MT_ORIENTATION,
> > > +						 point[i].orientation);
> > > +				input_mt_sync(ts->input);
> > 
> > Is there a way to track the contacts and switch to protocol B by any
> > chance?
> 
> I'm not sure, but will check how the device handles changing the number of
> touching fingers and if it would be possible to switch.

Sadly, the device does not seem capable of really tracking the contacts 
individually.
My testcase:
- p0 <-> finger0, p1 <-> finger1
- finger0 is lifted 
in todays test: new mapping finger1 <-> p0
some time ago during another test sometimes it did this switch and sometimes 
not, so the mapping reported point <-> finger is probably not stable.

> > > +
> > > +				/* use first finger as source for singletouch */
> > > +				if (fingers == 0)
> > > +					abs = i;
> > > +
> > > +				/* number of touch points could also be queried
> > > +				 * via i2c but would require an additional call
> > > +				 */
> > > +				fingers++;
> > > +			}
> > > +		}
> 
> [ ... more small stuff for me to fix ... ]
> 
> > > +
> > > +	ret = input_register_device(input_dev);
> > > +	if (ret) {
> > > +		dev_err(&client->dev, "could not register input device\n");
> > > +		goto err_input_register;
> > > +	}
> > > +
> > > +	input_set_drvdata(ts->input, ts);
> > 
> > Just do this earlier and then auo_pixcir_stop() is enough, you won't
> > need to call auo_pixcir_int_enable() before registering IRQ.
> 
> Just to be sure, you mean moving the input_set_drvdata before the irq-
> registration?

seems to work

> > > +
> > > +	i2c_set_clientdata(client, ts);
> > > +
> > > +	return 0;
> > > +
> 
> [ ... and still more small stuff ]
> 
> Thanks
> Heiko

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2011-12-07 22:09 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-05 20:05 [PATCH v2] Driver for AUO In-Cell touchscreens using pixcir ICs Heiko Stübner
2011-12-06  5:56 ` Dmitry Torokhov
2011-12-07 14:31   ` Heiko Stübner
2011-12-07 22:09     ` Heiko Stübner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.