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@xxxxxxxxx> > --- > 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@xxxxxxxxx> > + * > + * 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@xxxxxxxxx>"); > 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@xxxxxxxxx> > + * > + * 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@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html