All of lore.kernel.org
 help / color / mirror / Atom feed
From: Joonyoung Shim <jy0922.shim@samsung.com>
To: dmitry.torokhov@gmail.com
Cc: linux-input@vger.kernel.org, kyungmin.park@samsung.com,
	rydberg@euromail.se, khali@linux-fr.org
Subject: [PATCH v4] input: qt602240 - Add ATMEL QT602240 touchscreen driver
Date: Wed, 07 Jul 2010 16:40:23 +0900	[thread overview]
Message-ID: <1278488423-2727-1-git-send-email-jy0922.shim@samsung.com> (raw)

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@samsung.com>
---
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@samsung.com>
+ *
+ *  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@samsung.com>");
+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@samsung.com>
+ *
+ *  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


             reply	other threads:[~2010-07-07  7:40 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-07-07  7:40 Joonyoung Shim [this message]
2010-07-13 16:51 ` [PATCH v4] input: qt602240 - Add ATMEL QT602240 touchscreen driver Dmitry Torokhov
2010-07-13 16:54   ` Henrik Rydberg
2010-07-15  1:50   ` Joonyoung Shim
2010-07-19  9:29   ` Jean Delvare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1278488423-2727-1-git-send-email-jy0922.shim@samsung.com \
    --to=jy0922.shim@samsung.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=khali@linux-fr.org \
    --cc=kyungmin.park@samsung.com \
    --cc=linux-input@vger.kernel.org \
    --cc=rydberg@euromail.se \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.