Input: add driver for AUO In-Cell touchscreens using pixcir ICs
authorHeiko Stübner <heiko@sntech.de>
Wed, 28 Dec 2011 05:21:17 +0000 (21:21 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 28 Dec 2011 05:22:18 +0000 (21:22 -0800)
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>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/auo-pixcir-ts.c [new file with mode: 0644]
include/linux/input/auo-pixcir-ts.h [new file with mode: 0644]

index 2b456a915d77130edd2d3e7decfc1fb148a54fd0..a121e36e5a47fec8a79db8d6475ff2819e2e4192 100644 (file)
@@ -98,6 +98,19 @@ 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
+       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
index a09c546b33b791ef527f975b4a05ecf97bbe3355..f0b4d16d39a6604dba9df51f523f9faa52331feb 100644 (file)
@@ -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 (file)
index 0000000..94fb9fb
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+ * Driver for AUO in-cell touchscreens
+ *
+ * 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/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/mutex.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;
+};
+
+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;
+       const 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;
+       const 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 __devinit 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;
+       }
+
+       ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND;
+
+       return 0;
+}
+
+/* control the generation of interrupts on the device side */
+static int auo_pixcir_int_toggle(struct auo_pixcir_ts *ts, bool 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);
+               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);
+               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_toggle(ts, 1);
+       if (ret < 0) {
+               dev_err(&client->dev, "could not enable interrupt, %d\n",
+                       ret);
+               disable_irq(client->irq);
+               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_toggle(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;
+
+       return 0;
+}
+
+static void auo_pixcir_input_close(struct input_dev *dev)
+{
+       struct auo_pixcir_ts *ts = input_get_drvdata(dev);
+
+       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 = i2c_get_clientdata(client);
+       struct input_dev *input = ts->input;
+       int ret = 0;
+
+       mutex_lock(&input->mutex);
+
+       /* when configured as wakeup source, device should always wake system
+        * therefore start device if necessary
+        */
+       if (device_may_wakeup(&client->dev)) {
+               /* need to start device if not open, to be wakeup source */
+               if (!input->users) {
+                       ret = auo_pixcir_start(ts);
+                       if (ret)
+                               goto unlock;
+               }
+
+               enable_irq_wake(client->irq);
+               ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP);
+       } else if (input->users) {
+               ret = auo_pixcir_stop(ts);
+       }
+
+unlock:
+       mutex_unlock(&input->mutex);
+
+       return ret;
+}
+
+static int auo_pixcir_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
+       struct input_dev *input = ts->input;
+       int ret = 0;
+
+       mutex_lock(&input->mutex);
+
+       if (device_may_wakeup(&client->dev)) {
+               disable_irq_wake(client->irq);
+
+               /* need to stop device if it was not open on suspend */
+               if (!input->users) {
+                       ret = auo_pixcir_stop(ts);
+                       if (ret)
+                               goto unlock;
+               }
+
+               /* device wakes automatically from SLEEP */
+       } else if (input->users) {
+               ret = auo_pixcir_start(ts);
+       }
+
+unlock:
+       mutex_unlock(&input->mutex);
+
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend,
+                        auo_pixcir_resume);
+
+static int __devinit auo_pixcir_probe(struct i2c_client *client,
+                                     const struct i2c_device_id *id)
+{
+       const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+       struct auo_pixcir_ts *ts;
+       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;
+       }
+
+       if (pdata->init_hw)
+               pdata->init_hw(client);
+
+       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 touchscreen";
+       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;
+
+       input_set_drvdata(ts->input, ts);
+       ts->stopped = true;
+
+       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;
+       }
+
+       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(client);
+       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 = i2c_get_clientdata(client);
+       const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
+
+       free_irq(client->irq, ts);
+
+       input_unregister_device(ts->input);
+
+       if (pdata->exit_hw)
+               pdata->exit_hw(client);
+
+       gpio_free(pdata->gpio_int);
+
+       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",
+               .pm     = &auo_pixcir_pm_ops,
+       },
+       .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 (file)
index 0000000..75d4be7
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Driver for AUO in-cell touchscreens
+ *
+ * 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
+ * @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 int_setting;
+
+       void (*init_hw)(struct i2c_client *);
+       void (*exit_hw)(struct i2c_client *);
+
+       unsigned int x_max;
+       unsigned int y_max;
+};
+
+#endif