On Wed, Jul 07, 2010 at 04:40:23PM +0900, Joonyoung Shim wrote: > This chip full name is AT42QT602240 or ATMXT224. This is the capacitive > touchscreen supporting 10 multitouch and uses I2C interface. > > Signed-off-by: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> Henrik, Jean, Have your concerns with this patch been addressed? If I get your acked-by I am ready to apply it. Thanks. > --- > Changes since v1: > - Splitting long functions up > - Use threaded IRQs > - Add open() and close() methods for the input device > - Code cleanup from patch v1 feedback > > Changes since v2: > - Fix MT events reporting > - Add mutex on resume function > > Changes since v3: > - Code cleanup > - Add qt602240_start/_stop functions > - Remove resume work > - Use i2c_transfer() instead of i2c_master_send() + i2c_master_recv() > > drivers/input/touchscreen/Kconfig | 12 + > drivers/input/touchscreen/Makefile | 1 + > drivers/input/touchscreen/qt602240_ts.c | 1395 +++++++++++++++++++++++++++++++ > include/linux/i2c/qt602240_ts.h | 39 + > 4 files changed, 1447 insertions(+), 0 deletions(-) > create mode 100644 drivers/input/touchscreen/qt602240_ts.c > create mode 100644 include/linux/i2c/qt602240_ts.h > > diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig > index 3b9d5e2..99c8c7e 100644 > --- a/drivers/input/touchscreen/Kconfig > +++ b/drivers/input/touchscreen/Kconfig > @@ -603,4 +603,16 @@ config TOUCHSCREEN_TPS6507X > To compile this driver as a module, choose M here: the > module will be called tps6507x_ts. > > +config TOUCHSCREEN_QT602240 > + tristate "QT602240 I2C Touchscreen" > + depends on I2C > + help > + Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen > + connected to your system. > + > + If unsure, say N. > + > + To compile this driver as a module, choose M here: the > + module will be called qt602240_ts. > + > endif > diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile > index 497964a..4f3760b 100644 > --- a/drivers/input/touchscreen/Makefile > +++ b/drivers/input/touchscreen/Makefile > @@ -47,3 +47,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o > obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o > obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o > obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o > +obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o > diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c > new file mode 100644 > index 0000000..1ef79f8 > --- /dev/null > +++ b/drivers/input/touchscreen/qt602240_ts.c > @@ -0,0 +1,1395 @@ > +/* > + * qt602240_ts.c - AT42QT602240/ATMXT224 Touchscreen driver > + * > + * Copyright (C) 2010 Samsung Electronics Co.Ltd > + * Author: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2 of the License, or (at your > + * option) any later version. > + * > + */ > + > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/delay.h> > +#include <linux/firmware.h> > +#include <linux/i2c.h> > +#include <linux/i2c/qt602240_ts.h> > +#include <linux/input.h> > +#include <linux/interrupt.h> > +#include <linux/slab.h> > + > +/* Version */ > +#define QT602240_VER_20 20 > +#define QT602240_VER_21 21 > +#define QT602240_VER_22 22 > + > +/* Slave addresses */ > +#define QT602240_APP_LOW 0x4a > +#define QT602240_APP_HIGH 0x4b > +#define QT602240_BOOT_LOW 0x24 > +#define QT602240_BOOT_HIGH 0x25 > + > +/* Firmware */ > +#define QT602240_FW_NAME "qt602240.fw" > + > +/* Registers */ > +#define QT602240_FAMILY_ID 0x00 > +#define QT602240_VARIANT_ID 0x01 > +#define QT602240_VERSION 0x02 > +#define QT602240_BUILD 0x03 > +#define QT602240_MATRIX_X_SIZE 0x04 > +#define QT602240_MATRIX_Y_SIZE 0x05 > +#define QT602240_OBJECT_NUM 0x06 > +#define QT602240_OBJECT_START 0x07 > + > +#define QT602240_OBJECT_SIZE 6 > + > +/* Object types */ > +#define QT602240_DEBUG_DIAGNOSTIC 37 > +#define QT602240_GEN_MESSAGE 5 > +#define QT602240_GEN_COMMAND 6 > +#define QT602240_GEN_POWER 7 > +#define QT602240_GEN_ACQUIRE 8 > +#define QT602240_TOUCH_MULTI 9 > +#define QT602240_TOUCH_KEYARRAY 15 > +#define QT602240_TOUCH_PROXIMITY 23 > +#define QT602240_PROCI_GRIPFACE 20 > +#define QT602240_PROCG_NOISE 22 > +#define QT602240_PROCI_ONETOUCH 24 > +#define QT602240_PROCI_TWOTOUCH 27 > +#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ > +#define QT602240_SPT_GPIOPWM 19 > +#define QT602240_SPT_SELFTEST 25 > +#define QT602240_SPT_CTECONFIG 28 > +#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */ > + > +/* QT602240_GEN_COMMAND field */ > +#define QT602240_COMMAND_RESET 0 > +#define QT602240_COMMAND_BACKUPNV 1 > +#define QT602240_COMMAND_CALIBRATE 2 > +#define QT602240_COMMAND_REPORTALL 3 > +#define QT602240_COMMAND_DIAGNOSTIC 5 > + > +/* QT602240_GEN_POWER field */ > +#define QT602240_POWER_IDLEACQINT 0 > +#define QT602240_POWER_ACTVACQINT 1 > +#define QT602240_POWER_ACTV2IDLETO 2 > + > +/* QT602240_GEN_ACQUIRE field */ > +#define QT602240_ACQUIRE_CHRGTIME 0 > +#define QT602240_ACQUIRE_TCHDRIFT 2 > +#define QT602240_ACQUIRE_DRIFTST 3 > +#define QT602240_ACQUIRE_TCHAUTOCAL 4 > +#define QT602240_ACQUIRE_SYNC 5 > +#define QT602240_ACQUIRE_ATCHCALST 6 > +#define QT602240_ACQUIRE_ATCHCALSTHR 7 > + > +/* QT602240_TOUCH_MULTI field */ > +#define QT602240_TOUCH_CTRL 0 > +#define QT602240_TOUCH_XORIGIN 1 > +#define QT602240_TOUCH_YORIGIN 2 > +#define QT602240_TOUCH_XSIZE 3 > +#define QT602240_TOUCH_YSIZE 4 > +#define QT602240_TOUCH_BLEN 6 > +#define QT602240_TOUCH_TCHTHR 7 > +#define QT602240_TOUCH_TCHDI 8 > +#define QT602240_TOUCH_ORIENT 9 > +#define QT602240_TOUCH_MOVHYSTI 11 > +#define QT602240_TOUCH_MOVHYSTN 12 > +#define QT602240_TOUCH_NUMTOUCH 14 > +#define QT602240_TOUCH_MRGHYST 15 > +#define QT602240_TOUCH_MRGTHR 16 > +#define QT602240_TOUCH_AMPHYST 17 > +#define QT602240_TOUCH_XRANGE_LSB 18 > +#define QT602240_TOUCH_XRANGE_MSB 19 > +#define QT602240_TOUCH_YRANGE_LSB 20 > +#define QT602240_TOUCH_YRANGE_MSB 21 > +#define QT602240_TOUCH_XLOCLIP 22 > +#define QT602240_TOUCH_XHICLIP 23 > +#define QT602240_TOUCH_YLOCLIP 24 > +#define QT602240_TOUCH_YHICLIP 25 > +#define QT602240_TOUCH_XEDGECTRL 26 > +#define QT602240_TOUCH_XEDGEDIST 27 > +#define QT602240_TOUCH_YEDGECTRL 28 > +#define QT602240_TOUCH_YEDGEDIST 29 > +#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ > + > +/* QT602240_PROCI_GRIPFACE field */ > +#define QT602240_GRIPFACE_CTRL 0 > +#define QT602240_GRIPFACE_XLOGRIP 1 > +#define QT602240_GRIPFACE_XHIGRIP 2 > +#define QT602240_GRIPFACE_YLOGRIP 3 > +#define QT602240_GRIPFACE_YHIGRIP 4 > +#define QT602240_GRIPFACE_MAXTCHS 5 > +#define QT602240_GRIPFACE_SZTHR1 7 > +#define QT602240_GRIPFACE_SZTHR2 8 > +#define QT602240_GRIPFACE_SHPTHR1 9 > +#define QT602240_GRIPFACE_SHPTHR2 10 > +#define QT602240_GRIPFACE_SUPEXTTO 11 > + > +/* QT602240_PROCI_NOISE field */ > +#define QT602240_NOISE_CTRL 0 > +#define QT602240_NOISE_OUTFLEN 1 > +#define QT602240_NOISE_GCAFUL_LSB 3 > +#define QT602240_NOISE_GCAFUL_MSB 4 > +#define QT602240_NOISE_GCAFLL_LSB 5 > +#define QT602240_NOISE_GCAFLL_MSB 6 > +#define QT602240_NOISE_ACTVGCAFVALID 7 > +#define QT602240_NOISE_NOISETHR 8 > +#define QT602240_NOISE_FREQHOPSCALE 10 > +#define QT602240_NOISE_FREQ0 11 > +#define QT602240_NOISE_FREQ1 12 > +#define QT602240_NOISE_FREQ2 13 > +#define QT602240_NOISE_FREQ3 14 > +#define QT602240_NOISE_FREQ4 15 > +#define QT602240_NOISE_IDLEGCAFVALID 16 > + > +/* QT602240_SPT_COMMSCONFIG */ > +#define QT602240_COMMS_CTRL 0 > +#define QT602240_COMMS_CMD 1 > + > +/* QT602240_SPT_CTECONFIG field */ > +#define QT602240_CTE_CTRL 0 > +#define QT602240_CTE_CMD 1 > +#define QT602240_CTE_MODE 2 > +#define QT602240_CTE_IDLEGCAFDEPTH 3 > +#define QT602240_CTE_ACTVGCAFDEPTH 4 > +#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */ > + > +#define QT602240_VOLTAGE_DEFAULT 2700000 > +#define QT602240_VOLTAGE_STEP 10000 > + > +/* Define for QT602240_GEN_COMMAND */ > +#define QT602240_BOOT_VALUE 0xa5 > +#define QT602240_BACKUP_VALUE 0x55 > +#define QT602240_BACKUP_TIME 25 /* msec */ > +#define QT602240_RESET_TIME 65 /* msec */ > + > +#define QT602240_FWRESET_TIME 175 /* msec */ > + > +/* Command to unlock bootloader */ > +#define QT602240_UNLOCK_CMD_MSB 0xaa > +#define QT602240_UNLOCK_CMD_LSB 0xdc > + > +/* Bootloader mode status */ > +#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ > +#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ > +#define QT602240_FRAME_CRC_CHECK 0x02 > +#define QT602240_FRAME_CRC_FAIL 0x03 > +#define QT602240_FRAME_CRC_PASS 0x04 > +#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ > +#define QT602240_BOOT_STATUS_MASK 0x3f > + > +/* Touch status */ > +#define QT602240_SUPPRESS (1 << 1) > +#define QT602240_AMP (1 << 2) > +#define QT602240_VECTOR (1 << 3) > +#define QT602240_MOVE (1 << 4) > +#define QT602240_RELEASE (1 << 5) > +#define QT602240_PRESS (1 << 6) > +#define QT602240_DETECT (1 << 7) > + > +/* Touchscreen absolute values */ > +#define QT602240_MAX_XC 0x3ff > +#define QT602240_MAX_YC 0x3ff > +#define QT602240_MAX_AREA 0xff > + > +#define QT602240_MAX_FINGER 10 > + > +/* Initial register values recommended from chip vendor */ > +static const u8 init_vals_ver_20[] = { > + /* QT602240_GEN_COMMAND(6) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_GEN_POWER(7) */ > + 0x20, 0xff, 0x32, > + /* QT602240_GEN_ACQUIRE(8) */ > + 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, > + /* QT602240_TOUCH_MULTI(9) */ > + 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, > + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, > + /* QT602240_TOUCH_KEYARRAY(15) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, > + /* QT602240_SPT_GPIOPWM(19) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, > + /* QT602240_PROCI_GRIPFACE(20) */ > + 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, > + 0x1e, 0x00, > + /* QT602240_PROCG_NOISE(22) */ > + 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, > + 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, > + /* QT602240_TOUCH_PROXIMITY(23) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, > + /* QT602240_PROCI_ONETOUCH(24) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_SPT_SELFTEST(25) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, > + /* QT602240_PROCI_TWOTOUCH(27) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_SPT_CTECONFIG(28) */ > + 0x00, 0x00, 0x00, 0x04, 0x08, > +}; > + > +static const u8 init_vals_ver_21[] = { > + /* QT602240_GEN_COMMAND(6) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_GEN_POWER(7) */ > + 0x20, 0xff, 0x32, > + /* QT602240_GEN_ACQUIRE(8) */ > + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, > + /* QT602240_TOUCH_MULTI(9) */ > + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, > + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_TOUCH_KEYARRAY(15) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, > + /* QT602240_SPT_GPIOPWM(19) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_PROCI_GRIPFACE(20) */ > + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, > + 0x0f, 0x0a, > + /* QT602240_PROCG_NOISE(22) */ > + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, > + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, > + /* QT602240_TOUCH_PROXIMITY(23) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, > + /* QT602240_PROCI_ONETOUCH(24) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_SPT_SELFTEST(25) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, > + /* QT602240_PROCI_TWOTOUCH(27) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_SPT_CTECONFIG(28) */ > + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, > +}; > + > +static const u8 init_vals_ver_22[] = { > + /* QT602240_GEN_COMMAND(6) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_GEN_POWER(7) */ > + 0x20, 0xff, 0x32, > + /* QT602240_GEN_ACQUIRE(8) */ > + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, > + /* QT602240_TOUCH_MULTI(9) */ > + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, > + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, > + /* QT602240_TOUCH_KEYARRAY(15) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, > + /* QT602240_SPT_GPIOPWM(19) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_PROCI_GRIPFACE(20) */ > + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, > + 0x0f, 0x0a, > + /* QT602240_PROCG_NOISE(22) */ > + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, > + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, > + /* QT602240_TOUCH_PROXIMITY(23) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_PROCI_ONETOUCH(24) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_SPT_SELFTEST(25) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + 0x00, 0x00, 0x00, 0x00, > + /* QT602240_PROCI_TWOTOUCH(27) */ > + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, > + /* QT602240_SPT_CTECONFIG(28) */ > + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, > +}; > + > +struct qt602240_info { > + u8 family_id; > + u8 variant_id; > + u8 version; > + u8 build; > + u8 matrix_xsize; > + u8 matrix_ysize; > + u8 object_num; > +}; > + > +struct qt602240_object { > + u8 type; > + u16 start_address; > + u8 size; > + u8 instances; > + u8 num_report_ids; > + > + /* to map object and message */ > + u8 max_reportid; > +}; > + > +struct qt602240_message { > + u8 reportid; > + u8 message[7]; > + u8 checksum; > +}; > + > +struct qt602240_finger { > + int status; > + int x; > + int y; > + int area; > +}; > + > +/* Each client has this additional data */ > +struct qt602240_data { > + struct i2c_client *client; > + struct input_dev *input_dev; > + struct qt602240_platform_data *pdata; > + struct qt602240_object *object_table; > + struct qt602240_info info; > + struct qt602240_finger finger[QT602240_MAX_FINGER]; > + unsigned int irq; > +}; > + > +static int qt602240_object_readable(unsigned int type) > +{ > + switch (type) { > + case QT602240_GEN_MESSAGE: > + case QT602240_GEN_COMMAND: > + case QT602240_GEN_POWER: > + case QT602240_GEN_ACQUIRE: > + case QT602240_TOUCH_MULTI: > + case QT602240_TOUCH_KEYARRAY: > + case QT602240_TOUCH_PROXIMITY: > + case QT602240_PROCI_GRIPFACE: > + case QT602240_PROCG_NOISE: > + case QT602240_PROCI_ONETOUCH: > + case QT602240_PROCI_TWOTOUCH: > + case QT602240_SPT_COMMSCONFIG: > + case QT602240_SPT_GPIOPWM: > + case QT602240_SPT_SELFTEST: > + case QT602240_SPT_CTECONFIG: > + case QT602240_SPT_USERDATA: > + return 1; > + default: > + return 0; > + } > +} > + > +static int qt602240_object_writable(unsigned int type) > +{ > + switch (type) { > + case QT602240_GEN_COMMAND: > + case QT602240_GEN_POWER: > + case QT602240_GEN_ACQUIRE: > + case QT602240_TOUCH_MULTI: > + case QT602240_TOUCH_KEYARRAY: > + case QT602240_TOUCH_PROXIMITY: > + case QT602240_PROCI_GRIPFACE: > + case QT602240_PROCG_NOISE: > + case QT602240_PROCI_ONETOUCH: > + case QT602240_PROCI_TWOTOUCH: > + case QT602240_SPT_GPIOPWM: > + case QT602240_SPT_SELFTEST: > + case QT602240_SPT_CTECONFIG: > + return 1; > + default: > + return 0; > + } > +} > + > +static void qt602240_dump_message(struct device *dev, > + struct qt602240_message *message) > +{ > + dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); > + dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); > + dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); > + dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); > + dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); > + dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); > + dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); > + dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); > + dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); > +} > + > +static int qt602240_check_bootloader(struct i2c_client *client, > + unsigned int state) > +{ > + u8 val; > + > +recheck: > + if (i2c_master_recv(client, &val, 1) != 1) { > + dev_err(&client->dev, "%s: i2c recv failed\n", __func__); > + return -EIO; > + } > + > + switch (state) { > + case QT602240_WAITING_BOOTLOAD_CMD: > + case QT602240_WAITING_FRAME_DATA: > + val &= ~QT602240_BOOT_STATUS_MASK; > + break; > + case QT602240_FRAME_CRC_PASS: > + if (val == QT602240_FRAME_CRC_CHECK) > + goto recheck; > + break; > + default: > + return -EINVAL; > + } > + > + if (val != state) { > + dev_err(&client->dev, "Unvalid bootloader mode state\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int qt602240_unlock_bootloader(struct i2c_client *client) > +{ > + u8 buf[2]; > + > + buf[0] = QT602240_UNLOCK_CMD_LSB; > + buf[1] = QT602240_UNLOCK_CMD_MSB; > + > + if (i2c_master_send(client, buf, 2) != 2) { > + dev_err(&client->dev, "%s: i2c send failed\n", __func__); > + return -EIO; > + } > + > + return 0; > +} > + > +static int qt602240_fw_write(struct i2c_client *client, const u8 *data, > + unsigned int frame_size) > +{ > + if (i2c_master_send(client, data, frame_size) != frame_size) { > + dev_err(&client->dev, "%s: i2c send failed\n", __func__); > + return -EIO; > + } > + > + return 0; > +} > + > +static int __qt602240_read_reg(struct i2c_client *client, u16 reg, u16 len, > + u8 *val) > +{ > + struct i2c_msg xfer[2]; > + u8 buf[2]; > + > + buf[0] = reg & 0xff; > + buf[1] = (reg >> 8) & 0xff; > + > + /* Write register */ > + xfer[0].addr = client->addr; > + xfer[0].flags = 0; > + xfer[0].len = 2; > + xfer[0].buf = buf; > + > + /* Read data */ > + xfer[1].addr = client->addr; > + xfer[1].flags = I2C_M_RD; > + xfer[1].len = len; > + xfer[1].buf = val; > + > + if (i2c_transfer(client->adapter, xfer, 2) != 2) { > + dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); > + return -EIO; > + } > + > + return 0; > +} > + > +static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) > +{ > + return __qt602240_read_reg(client, reg, 1, val); > +} > + > +static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) > +{ > + u8 buf[3]; > + > + buf[0] = reg & 0xff; > + buf[1] = (reg >> 8) & 0xff; > + buf[2] = val; > + > + if (i2c_master_send(client, buf, 3) != 3) { > + dev_err(&client->dev, "%s: i2c send failed\n", __func__); > + return -EIO; > + } > + > + return 0; > +} > + > +static int qt602240_read_object_table(struct i2c_client *client, u16 reg, > + u8 *object_buf) > +{ > + return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, > + object_buf); > +} > + > +static struct qt602240_object * > +qt602240_get_object(struct qt602240_data *data, u8 type) > +{ > + struct qt602240_object *object; > + int i; > + > + for (i = 0; i < data->info.object_num; i++) { > + object = data->object_table + i; > + if (object->type == type) > + return object; > + } > + > + dev_err(&data->client->dev, "invalid type\n"); > + return NULL; > +} > + > +static int qt602240_read_message(struct qt602240_data *data, > + struct qt602240_message *message) > +{ > + struct i2c_client *client = data->client; > + struct qt602240_object *object; > + u16 reg; > + > + object = qt602240_get_object(data, QT602240_GEN_MESSAGE); > + if (!object) > + return -EINVAL; > + > + reg = object->start_address; > + return __qt602240_read_reg(client, reg, sizeof(struct qt602240_message), > + (u8 *)message); > +} > + > +static int qt602240_read_object(struct qt602240_data *data, u8 type, u8 offset, > + u8 *val) > +{ > + struct qt602240_object *object; > + u16 reg; > + > + object = qt602240_get_object(data, type); > + if (!object) > + return -EINVAL; > + > + reg = object->start_address; > + return __qt602240_read_reg(data->client, reg + offset, 1, val); > +} > + > +static int qt602240_write_object(struct qt602240_data *data, u8 type, > + u8 offset, u8 val) > +{ > + struct qt602240_object *object; > + u16 reg; > + > + object = qt602240_get_object(data, type); > + if (!object) > + return -EINVAL; > + > + reg = object->start_address; > + return qt602240_write_reg(data->client, reg + offset, val); > +} > + > +static void qt602240_input_report(struct qt602240_data *data, int single_id) > +{ > + struct qt602240_finger *finger = data->finger; > + struct input_dev *input_dev = data->input_dev; > + int status = finger[single_id].status; > + int finger_num = 0; > + int id; > + > + for (id = 0; id < QT602240_MAX_FINGER; id++) { > + if (!finger[id].status) > + continue; > + > + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, > + finger[id].status != QT602240_RELEASE ? > + finger[id].area : 0); > + input_report_abs(input_dev, ABS_MT_POSITION_X, > + finger[id].x); > + input_report_abs(input_dev, ABS_MT_POSITION_Y, > + finger[id].y); > + input_mt_sync(input_dev); > + > + if (finger[id].status == QT602240_RELEASE) > + finger[id].status = 0; > + else > + finger_num++; > + } > + > + input_report_key(input_dev, BTN_TOUCH, finger_num > 0); > + > + if (status != QT602240_RELEASE) { > + input_report_abs(input_dev, ABS_X, finger[single_id].x); > + input_report_abs(input_dev, ABS_Y, finger[single_id].y); > + } > + > + input_sync(input_dev); > +} > + > +static void qt602240_input_touchevent(struct qt602240_data *data, > + struct qt602240_message *message, int id) > +{ > + struct qt602240_finger *finger = data->finger; > + struct device *dev = &data->client->dev; > + u8 status = message->message[0]; > + int x; > + int y; > + int area; > + > + /* Check the touch is present on the screen */ > + if (!(status & QT602240_DETECT)) { > + if (status & QT602240_RELEASE) { > + dev_dbg(dev, "[%d] released\n", id); > + > + finger[id].status = QT602240_RELEASE; > + qt602240_input_report(data, id); > + } > + return; > + } > + > + /* Check only AMP detection */ > + if (!(status & (QT602240_PRESS | QT602240_MOVE))) > + return; > + > + x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); > + y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); > + area = message->message[4]; > + > + dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, > + status & QT602240_MOVE ? "moved" : "pressed", > + x, y, area); > + > + finger[id].status = status & QT602240_MOVE ? > + QT602240_MOVE : QT602240_PRESS; > + finger[id].x = x; > + finger[id].y = y; > + finger[id].area = area; > + > + qt602240_input_report(data, id); > +} > + > +static irqreturn_t qt602240_interrupt(int irq, void *dev_id) > +{ > + struct qt602240_data *data = dev_id; > + struct qt602240_message message; > + struct qt602240_object *object; > + struct device *dev = &data->client->dev; > + int id; > + u8 reportid; > + u8 max_reportid; > + u8 min_reportid; > + > + do { > + if (qt602240_read_message(data, &message)) { > + dev_err(dev, "Failed to read message\n"); > + goto end; > + } > + > + reportid = message.reportid; > + > + /* whether reportid is thing of QT602240_TOUCH_MULTI */ > + object = qt602240_get_object(data, QT602240_TOUCH_MULTI); > + if (!object) > + goto end; > + > + max_reportid = object->max_reportid; > + min_reportid = max_reportid - object->num_report_ids + 1; > + id = reportid - min_reportid; > + > + if ((reportid >= min_reportid) && (reportid <= max_reportid)) > + qt602240_input_touchevent(data, &message, id); > + else > + qt602240_dump_message(dev, &message); > + } while (reportid != 0xff); > + > +end: > + return IRQ_HANDLED; > +} > + > +static int qt602240_check_reg_init(struct qt602240_data *data) > +{ > + struct qt602240_object *object; > + struct device *dev = &data->client->dev; > + int index = 0; > + int i, j; > + u8 version = data->info.version; > + u8 *init_vals; > + > + switch (version) { > + case QT602240_VER_20: > + init_vals = (u8 *)init_vals_ver_20; > + break; > + case QT602240_VER_21: > + init_vals = (u8 *)init_vals_ver_21; > + break; > + case QT602240_VER_22: > + init_vals = (u8 *)init_vals_ver_22; > + break; > + default: > + dev_err(dev, "Firmware version %d doesn't support\n", version); > + return -EINVAL; > + } > + > + for (i = 0; i < data->info.object_num; i++) { > + object = data->object_table + i; > + > + if (!qt602240_object_writable(object->type)) > + continue; > + > + for (j = 0; j < object->size + 1; j++) > + qt602240_write_object(data, object->type, j, > + init_vals[index + j]); > + > + index += object->size + 1; > + } > + > + return 0; > +} > + > +static int qt602240_check_matrix_size(struct qt602240_data *data) > +{ > + const struct qt602240_platform_data *pdata = data->pdata; > + struct device *dev = &data->client->dev; > + int mode = 0; > + int ret; > + u8 val; > + > + dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); > + dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); > + > + switch (pdata->x_line) { > + case 0 ... 15: > + if (pdata->y_line <= 14) > + mode = 0; > + break; > + case 16: > + if (pdata->y_line <= 12) > + mode = 1; > + if (pdata->y_line == 13 || pdata->y_line == 14) > + mode = 0; > + break; > + case 17: > + if (pdata->y_line <= 11) > + mode = 2; > + if (pdata->y_line == 12 || pdata->y_line == 13) > + mode = 1; > + break; > + case 18: > + if (pdata->y_line <= 10) > + mode = 3; > + if (pdata->y_line == 11 || pdata->y_line == 12) > + mode = 2; > + break; > + case 19: > + if (pdata->y_line <= 9) > + mode = 4; > + if (pdata->y_line == 10 || pdata->y_line == 11) > + mode = 3; > + break; > + case 20: > + mode = 4; > + break; > + default: > + dev_err(dev, "Invalid X/Y lines\n"); > + return -EINVAL; > + } > + > + ret = qt602240_read_object(data, QT602240_SPT_CTECONFIG, > + QT602240_CTE_MODE, &val); > + if (ret) > + return ret; > + > + if (mode == val) > + return 0; > + > + /* Change the CTE configuration */ > + qt602240_write_object(data, QT602240_SPT_CTECONFIG, > + QT602240_CTE_CTRL, 1); > + qt602240_write_object(data, QT602240_SPT_CTECONFIG, > + QT602240_CTE_MODE, mode); > + qt602240_write_object(data, QT602240_SPT_CTECONFIG, > + QT602240_CTE_CTRL, 0); > + > + return 0; > +} > + > +static void qt602240_handle_pdata(struct qt602240_data *data) > +{ > + const struct qt602240_platform_data *pdata = data->pdata; > + u8 voltage; > + > + /* Set touchscreen lines */ > + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, > + pdata->x_line); > + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, > + pdata->y_line); > + > + /* Set touchscreen orient */ > + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, > + pdata->orient); > + > + /* Set touchscreen burst length */ > + qt602240_write_object(data, QT602240_TOUCH_MULTI, > + QT602240_TOUCH_BLEN, pdata->blen); > + > + /* Set touchscreen threshold */ > + qt602240_write_object(data, QT602240_TOUCH_MULTI, > + QT602240_TOUCH_TCHTHR, pdata->threshold); > + > + /* Set touchscreen resolution */ > + qt602240_write_object(data, QT602240_TOUCH_MULTI, > + QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); > + qt602240_write_object(data, QT602240_TOUCH_MULTI, > + QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); > + qt602240_write_object(data, QT602240_TOUCH_MULTI, > + QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); > + qt602240_write_object(data, QT602240_TOUCH_MULTI, > + QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); > + > + /* Set touchscreen voltage */ > + if (data->info.version >= QT602240_VER_21 && pdata->voltage) { > + if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { > + voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / > + QT602240_VOLTAGE_STEP; > + voltage = 0xff - voltage + 1; > + } else > + voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / > + QT602240_VOLTAGE_STEP; > + > + qt602240_write_object(data, QT602240_SPT_CTECONFIG, > + QT602240_CTE_VOLTAGE, voltage); > + } > +} > + > +static int qt602240_get_info(struct qt602240_data *data) > +{ > + struct i2c_client *client = data->client; > + struct qt602240_info *info = &data->info; > + int ret; > + u8 val; > + > + ret = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); > + if (ret) > + return ret; > + info->family_id = val; > + > + ret = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); > + if (ret) > + return ret; > + info->variant_id = val; > + > + ret = qt602240_read_reg(client, QT602240_VERSION, &val); > + if (ret) > + return ret; > + info->version = val; > + > + ret = qt602240_read_reg(client, QT602240_BUILD, &val); > + if (ret) > + return ret; > + info->build = val; > + > + ret = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); > + if (ret) > + return ret; > + info->object_num = val; > + > + return 0; > +} > + > +static int qt602240_get_object_table(struct qt602240_data *data) > +{ > + int ret; > + int i; > + u16 reg; > + u8 reportid = 0; > + u8 buf[QT602240_OBJECT_SIZE]; > + > + for (i = 0; i < data->info.object_num; i++) { > + struct qt602240_object *object = data->object_table + i; > + > + reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; > + ret = qt602240_read_object_table(data->client, reg, buf); > + if (ret) > + return ret; > + > + object->type = buf[0]; > + object->start_address = (buf[2] << 8) | buf[1]; > + object->size = buf[3]; > + object->instances = buf[4]; > + object->num_report_ids = buf[5]; > + > + if (object->num_report_ids) { > + reportid += object->num_report_ids * > + (object->instances + 1); > + object->max_reportid = reportid; > + } > + } > + > + return 0; > +} > + > +static int qt602240_initialize(struct qt602240_data *data) > +{ > + struct i2c_client *client = data->client; > + struct qt602240_info *info = &data->info; > + int count = 10; > + int ret; > + u8 val; > + > + ret = qt602240_get_info(data); > + if (ret) > + return ret; > + > + data->object_table = > + kzalloc(sizeof(struct qt602240_data) * info->object_num, > + GFP_KERNEL); > + if (!data->object_table) { > + dev_err(&client->dev, "Failed to allocate memory\n"); > + return -ENOMEM; > + } > + > + /* Get object table information */ > + ret = qt602240_get_object_table(data); > + if (ret) > + return ret; > + > + /* Check register init values */ > + ret = qt602240_check_reg_init(data); > + if (ret) > + return ret; > + > + /* Check X/Y matrix size */ > + ret = qt602240_check_matrix_size(data); > + if (ret) > + return ret; > + > + /* Read dummy message to make high CHG pin */ > + do { > + ret = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); > + if (ret) > + return ret; > + } while ((val != 0xff) && --count); > + > + if (!count) { > + dev_err(&client->dev, "CHG pin isn't cleared\n"); > + return -EBUSY; > + } > + > + qt602240_handle_pdata(data); > + > + /* Backup to memory */ > + qt602240_write_object(data, QT602240_GEN_COMMAND, > + QT602240_COMMAND_BACKUPNV, > + QT602240_BACKUP_VALUE); > + msleep(QT602240_BACKUP_TIME); > + > + /* Soft reset */ > + qt602240_write_object(data, QT602240_GEN_COMMAND, > + QT602240_COMMAND_RESET, 1); > + msleep(QT602240_RESET_TIME); > + > + /* Update matrix size at info struct */ > + ret = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); > + if (ret) > + return ret; > + info->matrix_xsize = val; > + > + ret = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); > + if (ret) > + return ret; > + info->matrix_ysize = val; > + > + dev_info(&client->dev, > + "Family ID: %d Variant ID: %d Version: %d Build: %d\n", > + info->family_id, info->variant_id, info->version, > + info->build); > + > + dev_info(&client->dev, > + "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", > + info->matrix_xsize, info->matrix_ysize, > + info->object_num); > + > + return 0; > +} > + > +static ssize_t qt602240_object_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct qt602240_data *data = dev_get_drvdata(dev); > + struct qt602240_object *object; > + int count = 0; > + int i, j; > + int ret; > + u8 val; > + > + if (!data) { > + dev_err(dev, "The data is NULL\n"); > + return -EFAULT; > + } > + > + for (i = 0; i < data->info.object_num; i++) { > + object = data->object_table + i; > + > + count += sprintf(buf + count, > + "Object Table Element %d(Type %d)\n", > + i + 1, object->type); > + > + if (!qt602240_object_readable(object->type)) { > + count += sprintf(buf + count, "\n"); > + continue; > + } > + > + for (j = 0; j < object->size + 1; j++) { > + ret = qt602240_read_object(data, object->type, j, &val); > + if (ret) > + return count; > + > + count += sprintf(buf + count, > + " Byte %d: 0x%x (%d)\n", j, val, val); > + } > + > + count += sprintf(buf + count, "\n"); > + } > + > + return count; > +} > + > +static int qt602240_load_fw(struct device *dev, const char *fn) > +{ > + struct qt602240_data *data = dev_get_drvdata(dev); > + struct i2c_client *client = data->client; > + const struct firmware *fw = NULL; > + unsigned int frame_size; > + unsigned int pos = 0; > + int ret; > + > + if (!data) { > + dev_err(dev, "The data is NULL\n"); > + return -EFAULT; > + } > + > + ret = request_firmware(&fw, fn, dev); > + if (ret) { > + dev_err(dev, "Unable to open firmware %s\n", fn); > + return ret; > + } > + > + /* Change to the bootloader mode */ > + qt602240_write_object(data, QT602240_GEN_COMMAND, > + QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); > + msleep(QT602240_RESET_TIME); > + > + /* Change to slave address of bootloader */ > + if (client->addr == QT602240_APP_LOW) > + client->addr = QT602240_BOOT_LOW; > + else > + client->addr = QT602240_BOOT_HIGH; > + > + ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); > + if (ret) > + goto err_fw; > + > + /* Unlock bootloader */ > + qt602240_unlock_bootloader(client); > + > + while (pos < fw->size) { > + ret = qt602240_check_bootloader(client, > + QT602240_WAITING_FRAME_DATA); > + if (ret) > + goto err_fw; > + > + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); > + > + /* We should add 2 at frame size as the the firmware data is not > + * included the CRC bytes. > + */ > + frame_size += 2; > + > + /* Write one frame to device */ > + qt602240_fw_write(client, fw->data + pos, frame_size); > + > + ret = qt602240_check_bootloader(client, > + QT602240_FRAME_CRC_PASS); > + if (ret) > + goto err_fw; > + > + pos += frame_size; > + > + dev_dbg(dev, "Updated %zd bytes / %zd bytes\n", pos, fw->size); > + } > + > +err_fw: > + release_firmware(fw); > + > + /* Change to slave address of application */ > + if (client->addr == QT602240_BOOT_LOW) > + client->addr = QT602240_APP_LOW; > + else > + client->addr = QT602240_APP_HIGH; > + > + return ret; > +} > + > +static ssize_t qt602240_update_fw_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t count) > +{ > + struct qt602240_data *data = dev_get_drvdata(dev); > + unsigned int version; > + int ret; > + > + if (sscanf(buf, "%u", &version) != 1) { > + dev_err(dev, "Invalid values\n"); > + return -EINVAL; > + } > + > + if ((data->info.version < QT602240_VER_21) || > + (version < QT602240_VER_21)) { > + dev_err(dev, "FW update supported starting with version 21\n"); > + return -EINVAL; > + } > + > + disable_irq(data->irq); > + > + ret = qt602240_load_fw(dev, QT602240_FW_NAME); > + if (ret) > + dev_err(dev, "The firmware update failed(%d)\n", ret); > + else { > + dev_dbg(dev, "The firmware update succeeded\n"); > + > + /* Wait for reset */ > + msleep(QT602240_FWRESET_TIME); > + > + kfree(data->object_table); > + > + qt602240_initialize(data); > + } > + > + enable_irq(data->irq); > + > + return count; > +} > + > +static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); > +static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); > + > +static struct attribute *qt602240_attrs[] = { > + &dev_attr_object.attr, > + &dev_attr_update_fw.attr, > + NULL > +}; > + > +static const struct attribute_group qt602240_attr_group = { > + .attrs = qt602240_attrs, > +}; > + > +static void qt602240_start(struct qt602240_data *data) > +{ > + /* Touch enable */ > + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, > + 0x83); > +} > + > +static void qt602240_stop(struct qt602240_data *data) > +{ > + /* Touch disable */ > + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, > + 0); > +} > + > +static int qt602240_input_open(struct input_dev *dev) > +{ > + struct qt602240_data *data = input_get_drvdata(dev); > + > + qt602240_start(data); > + > + return 0; > +} > + > +static void qt602240_input_close(struct input_dev *dev) > +{ > + struct qt602240_data *data = input_get_drvdata(dev); > + > + qt602240_stop(data); > +} > + > +static int __devinit qt602240_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct qt602240_data *data; > + struct input_dev *input_dev; > + int ret; > + > + if (!client->dev.platform_data) > + return -EINVAL; > + > + data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); > + > + input_dev = input_allocate_device(); > + if (!data || !input_dev) { > + dev_err(&client->dev, "Failed to allocate memory\n"); > + ret = -ENOMEM; > + goto err_free_mem; > + } > + > + input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; > + input_dev->id.bustype = BUS_I2C; > + input_dev->dev.parent = &client->dev; > + input_dev->open = qt602240_input_open; > + input_dev->close = qt602240_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, QT602240_MAX_XC, 0, > + 0); > + input_set_abs_params(input_dev, ABS_Y, 0, QT602240_MAX_YC, 0, > + 0); > + > + /* For multi touch */ > + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, > + QT602240_MAX_AREA, 0, 0); > + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, > + QT602240_MAX_XC, 0, 0); > + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, > + QT602240_MAX_YC, 0, 0); > + > + input_set_drvdata(input_dev, data); > + > + data->client = client; > + data->input_dev = input_dev; > + data->pdata = client->dev.platform_data; > + data->irq = client->irq; > + > + i2c_set_clientdata(client, data); > + > + ret = qt602240_initialize(data); > + if (ret) > + goto err_free_object; > + > + ret = request_threaded_irq(client->irq, NULL, qt602240_interrupt, > + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); > + if (ret) { > + dev_err(&client->dev, "Failed to register interrupt\n"); > + goto err_free_object; > + } > + > + ret = input_register_device(input_dev); > + if (ret) > + goto err_free_irq; > + > + ret = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); > + if (ret) > + goto err_unregister_device; > + > + return 0; > + > +err_unregister_device: > + input_unregister_device(input_dev); > +err_free_irq: > + free_irq(client->irq, data); > +err_free_object: > + kfree(data->object_table); > +err_free_mem: > + input_free_device(input_dev); > + kfree(data); > + return ret; > +} > + > +static int __devexit qt602240_remove(struct i2c_client *client) > +{ > + struct qt602240_data *data = i2c_get_clientdata(client); > + > + free_irq(data->irq, data); > + input_unregister_device(data->input_dev); > + kfree(data->object_table); > + kfree(data); > + > + sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg) > +{ > + struct qt602240_data *data = i2c_get_clientdata(client); > + struct input_dev *input_dev = data->input_dev; > + > + mutex_lock(&input_dev->mutex); > + > + if (input_dev->users) > + qt602240_stop(data); > + > + mutex_unlock(&input_dev->mutex); > + > + return 0; > +} > + > +static int qt602240_resume(struct i2c_client *client) > +{ > + struct qt602240_data *data = i2c_get_clientdata(client); > + struct input_dev *input_dev = data->input_dev; > + > + /* Soft reset */ > + qt602240_write_object(data, QT602240_GEN_COMMAND, > + QT602240_COMMAND_RESET, 1); > + > + msleep(QT602240_RESET_TIME); > + > + mutex_lock(&input_dev->mutex); > + > + if (input_dev->users) > + qt602240_start(data); > + > + mutex_unlock(&input_dev->mutex); > + > + return 0; > +} > +#else > +#define qt602240_suspend NULL > +#define qt602240_resume NULL > +#endif > + > +static const struct i2c_device_id qt602240_id[] = { > + { "qt602240_ts", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, qt602240_id); > + > +static struct i2c_driver qt602240_driver = { > + .driver = { > + .name = "qt602240_ts", > + }, > + .probe = qt602240_probe, > + .remove = __devexit_p(qt602240_remove), > + .suspend = qt602240_suspend, > + .resume = qt602240_resume, > + .id_table = qt602240_id, > +}; > + > +static int __init qt602240_init(void) > +{ > + return i2c_add_driver(&qt602240_driver); > +} > + > +static void __exit qt602240_exit(void) > +{ > + i2c_del_driver(&qt602240_driver); > +} > + > +module_init(qt602240_init); > +module_exit(qt602240_exit); > + > +/* Module information */ > +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@xxxxxxxxxxx>"); > +MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/i2c/qt602240_ts.h b/include/linux/i2c/qt602240_ts.h > new file mode 100644 > index 0000000..09b55f9 > --- /dev/null > +++ b/include/linux/i2c/qt602240_ts.h > @@ -0,0 +1,39 @@ > +/* > + * qt602240_ts.h - AT42QT602240/ATMXT224 Touchscreen driver > + * > + * Copyright (C) 2010 Samsung Electronics Co.Ltd > + * Author: Joonyoung Shim <jy0922.shim@xxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2 of the License, or (at your > + * option) any later version. > + * > + */ > + > +#ifndef __LINUX_QT602240_TS_H > +#define __LINUX_QT602240_TS_H > + > +/* Orient */ > +#define QT602240_NORMAL 0x0 > +#define QT602240_DIAGONAL 0x1 > +#define QT602240_HORIZONTAL_FLIP 0x2 > +#define QT602240_ROTATED_90_COUNTER 0x3 > +#define QT602240_VERTICAL_FLIP 0x4 > +#define QT602240_ROTATED_90 0x5 > +#define QT602240_ROTATED_180 0x6 > +#define QT602240_DIAGONAL_COUNTER 0x7 > + > +/* The platform data for the AT42QT602240/ATMXT224 touchscreen driver */ > +struct qt602240_platform_data { > + unsigned int x_line; > + unsigned int y_line; > + unsigned int x_size; > + unsigned int y_size; > + unsigned int blen; > + unsigned int threshold; > + unsigned int voltage; > + unsigned char orient; > +}; > + > +#endif /* __LINUX_QT602240_TS_H */ > -- > 1.7.0.4 > -- 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