|  | /* | 
|  | * 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, | 
|  | }; | 
|  |  | 
|  | module_i2c_driver(auo_pixcir_driver); | 
|  |  | 
|  | MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver"); | 
|  | MODULE_LICENSE("GPL v2"); | 
|  | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); |