linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
@ 2010-06-23 13:16 Joonyoung Shim
  2010-06-23 17:34 ` Dmitry Torokhov
  0 siblings, 1 reply; 9+ messages in thread
From: Joonyoung Shim @ 2010-06-23 13:16 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input, kyungmin.park

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>
---
 drivers/input/touchscreen/Kconfig       |   12 +
 drivers/input/touchscreen/Makefile      |    1 +
 drivers/input/touchscreen/qt602240_ts.c | 1438 +++++++++++++++++++++++++++++++
 include/linux/i2c/qt602240_ts.h         |   40 +
 4 files changed, 1491 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..1adc0b0
--- /dev/null
+++ b/drivers/input/touchscreen/qt602240_ts.c
@@ -0,0 +1,1438 @@
+/*
+ * 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_ID			9
+#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) */
+	0x07, 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) */
+	0x07, 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 work_struct ts_event_work;
+	struct work_struct ts_disable_work;
+	struct qt602240_platform_data *pdata;
+	struct qt602240_info *info;
+	struct qt602240_object *object_table;
+	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)
+{
+	u8 buf[2];
+	u8 val;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+
+	if (i2c_master_send(client, buf, 2) != 2) {
+		dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+		return -EIO;
+	}
+
+	if (i2c_master_recv(client, &val, 1) != 1) {
+		dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+		return -EIO;
+	}
+
+	return 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)
+{
+	u8 buf[2];
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+
+	if (i2c_master_send(client, buf, 2) != 2) {
+		dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+		return -EIO;
+	}
+
+	if (i2c_master_recv(client, object_buf, 6) != 6) {
+		dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+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;
+	u8 buf[2];
+
+	object = qt602240_get_object(data, QT602240_GEN_MESSAGE);
+	if (!object)
+		return -EINVAL;
+
+	reg = object->start_address;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+
+	if (i2c_master_send(client, buf, 2) != 2) {
+		dev_err(&client->dev, "%s: i2c send failed\n", __func__);
+		return -EIO;
+	}
+
+	if (i2c_master_recv(client, (u8 *)message, 9) != 9) {
+		dev_err(&client->dev, "%s: i2c recv failed\n", __func__);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int qt602240_read_object(struct qt602240_data *data, u8 type, u8 offset)
+{
+	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);
+}
+
+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_check_config_error(struct qt602240_data *data,
+		struct qt602240_message *message, u8 reportid)
+{
+	struct qt602240_object *object;
+	u8 min_reportid;
+
+	object = qt602240_get_object(data, QT602240_GEN_COMMAND);
+	if (!object)
+		return;
+
+	min_reportid = object->max_reportid - object->num_report_ids + 1;
+
+	/* Check if configration error */
+	if ((reportid == min_reportid) && (message->message[0] & 0x8)) {
+		/* Touch disable */
+		qt602240_write_object(data, QT602240_TOUCH_MULTI,
+				QT602240_TOUCH_CTRL, 0);
+
+		/* 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);
+
+		/* Touch enable */
+		qt602240_write_object(data, QT602240_TOUCH_MULTI,
+				QT602240_TOUCH_CTRL, 0x83);
+	}
+}
+
+static void qt602240_input_read(struct qt602240_data *data)
+{
+	struct qt602240_message message;
+	struct qt602240_object *object;
+	struct device *dev = &data->client->dev;
+	struct input_dev *input_dev = data->input_dev;
+	u8 reportid;
+	u8 max_reportid;
+	u8 min_reportid;
+
+repeat:
+	if (qt602240_read_message(data, &message)) {
+		dev_err(dev, "Failed to read message\n");
+		return;
+	}
+
+	reportid = message.reportid;
+
+	/* Check it remains the message to process */
+	if (reportid == 0xff)
+		return;
+
+	/* whether reportid is thing of QT602240_TOUCH_MULTI */
+	object = qt602240_get_object(data, QT602240_TOUCH_MULTI);
+	if (!object)
+		return;
+
+	max_reportid = object->max_reportid;
+	min_reportid = max_reportid - object->num_report_ids + 1;
+
+	if ((reportid >= min_reportid) && (reportid <= max_reportid)) {
+		u8 id;
+		u8 status;
+		int x;
+		int y;
+		int area;
+		int finger_num = 0;
+
+		id = reportid - min_reportid;
+		status = message.message[0];
+
+		/* Check the touch is present on the screen */
+		if (!(status & QT602240_DETECT))
+			goto release;
+
+		/* Check only AMP detection */
+		if (!(status & (QT602240_PRESS | QT602240_MOVE)))
+			goto repeat;
+
+		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);
+
+		data->finger[id].status = status & QT602240_MOVE ?
+			QT602240_MOVE : QT602240_PRESS;
+		data->finger[id].x = x;
+		data->finger[id].y = y;
+		data->finger[id].area = area;
+
+		input_report_key(input_dev, BTN_TOUCH, 1);
+		input_report_abs(input_dev, ABS_X, x);
+		input_report_abs(input_dev, ABS_Y, y);
+
+		goto mt_report;
+
+release:
+		if (status & QT602240_RELEASE) {
+			dev_dbg(dev, "[%d] released\n", id);
+
+			data->finger[id].status = QT602240_RELEASE;
+			data->finger[id].area = 0;
+		}
+
+mt_report:
+		for (id = 0; id < QT602240_MAX_FINGER; id++) {
+			if (!data->finger[id].status)
+				continue;
+
+			input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
+			input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+					data->finger[id].area);
+
+			if (data->finger[id].status == QT602240_RELEASE)
+				data->finger[id].status = 0;
+			else {
+				input_report_abs(input_dev, ABS_MT_POSITION_X,
+						data->finger[id].x);
+				input_report_abs(input_dev, ABS_MT_POSITION_Y,
+						data->finger[id].y);
+				finger_num++;
+			}
+
+			input_mt_sync(input_dev);
+		}
+
+		if (!finger_num)
+			input_report_key(input_dev, BTN_TOUCH, 0);
+		input_sync(input_dev);
+	} else {
+		qt602240_dump_message(dev, &message);
+		qt602240_check_config_error(data, &message, reportid);
+	}
+
+	goto repeat;
+}
+
+static void qt602240_irq_worker(struct work_struct *work)
+{
+	struct qt602240_data *data = container_of(work,
+			struct qt602240_data, ts_event_work);
+
+	qt602240_input_read(data);
+}
+
+static void qt602240_disable_worker(struct work_struct *work)
+{
+	struct qt602240_data *data = container_of(work,
+			struct qt602240_data, ts_disable_work);
+
+	/* Soft reset */
+	qt602240_write_object(data, QT602240_GEN_COMMAND,
+			QT602240_COMMAND_RESET, 1);
+
+	msleep(QT602240_RESET_TIME);
+
+	/* Touch enable */
+	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL,
+			0x83);
+}
+
+static irqreturn_t qt602240_interrupt(int irq, void *dev_id)
+{
+	struct qt602240_data *data = dev_id;
+
+	if (!work_pending(&data->ts_event_work))
+		schedule_work(&data->ts_event_work);
+
+	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 val;
+	int mode = 0;
+
+	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;
+	}
+
+	val = qt602240_read_object(data, QT602240_SPT_CTECONFIG,
+			QT602240_CTE_MODE);
+	if (val < 0)
+		return -EIO;
+
+	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 detect integration */
+	if (pdata->tchdi)
+		qt602240_write_object(data, QT602240_TOUCH_MULTI,
+				QT602240_TOUCH_TCHDI, pdata->tchdi);
+
+	/* 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_info_get(struct qt602240_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct qt602240_info *info = data->info;
+	int val;
+
+	val = qt602240_read_reg(client, QT602240_FAMILY_ID);
+	if (val < 0)
+		return -EIO;
+	info->family_id = val;
+
+	val = qt602240_read_reg(client, QT602240_VARIANT_ID);
+	if (val < 0)
+		return -EIO;
+	info->variant_id = val;
+
+	val = qt602240_read_reg(client, QT602240_VERSION);
+	if (val < 0)
+		return -EIO;
+	info->version = val;
+
+	val = qt602240_read_reg(client, QT602240_BUILD);
+	if (val < 0)
+		return -EIO;
+	info->build = val;
+
+	val = qt602240_read_reg(client, QT602240_OBJECT_NUM);
+	if (val < 0)
+		return -EIO;
+	info->object_num = val;
+
+	return 0;
+}
+
+static int qt602240_initialize(struct qt602240_data *data)
+{
+	struct i2c_client *client = data->client;
+	struct qt602240_info *info;
+	int i;
+	int ret;
+	u16 reg;
+	u8 buf[QT602240_OBJECT_SIZE];
+	u8 reportid = 0;
+
+	info = data->info = kzalloc(sizeof(struct qt602240_info), GFP_KERNEL);
+
+	if (!info) {
+		dev_err(&data->client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	ret = qt602240_info_get(data);
+	if (ret < 0)
+		return ret;
+
+	data->object_table =
+		kzalloc(sizeof(struct qt602240_data) * info->object_num,
+				GFP_KERNEL);
+	if (!data->object_table) {
+		dev_err(&data->client->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* Get object table information */
+	for (i = 0; i < 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(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;
+		}
+	}
+
+	/* Check register init values */
+	ret = qt602240_check_reg_init(data);
+	if (ret < 0)
+		return ret;
+
+	/* Check X/Y matrix size */
+	ret = qt602240_check_matrix_size(data);
+	if (ret < 0)
+		return ret;
+
+	/* Read dummy message to make high CHG pin */
+	do {
+		ret = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0);
+		if (ret < 0)
+			return ret;
+	} while (ret != 0xff);
+
+	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);
+
+	/* Touch enable */
+	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL,
+			0x83);
+
+	/* Update matrix size at info struct */
+	ret = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE);
+	if (ret < 0)
+		return ret;
+	info->matrix_xsize = ret;
+
+	ret = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE);
+	if (ret < 0)
+		return ret;
+	info->matrix_ysize = ret;
+
+	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 val;
+	int count = 0;
+	int i, j;
+
+	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++) {
+			val = qt602240_read_object(data, object->type, j);
+			if (val < 0)
+				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);
+	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 < 0) {
+		dev_err(dev, "Unable to open firmware %s\n", fn);
+		return -ENOMEM;
+	}
+
+	/* 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 (data->client->addr == QT602240_APP_LOW)
+		data->client->addr = QT602240_BOOT_LOW;
+	else
+		data->client->addr = QT602240_BOOT_HIGH;
+
+	ret = qt602240_check_bootloader(data->client,
+			QT602240_WAITING_BOOTLOAD_CMD);
+	if (ret < 0)
+		goto err_fw;
+
+	/* Unlock bootloader */
+	qt602240_unlock_bootloader(data->client);
+
+	while (pos < fw->size) {
+		ret = qt602240_check_bootloader(data->client,
+				QT602240_WAITING_FRAME_DATA);
+		if (ret < 0)
+			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(data->client, fw->data + pos, frame_size);
+
+		ret = qt602240_check_bootloader(data->client,
+				QT602240_FRAME_CRC_PASS);
+		if (ret < 0)
+			goto err_fw;
+
+		pos += frame_size;
+
+		dev_info(dev, "Updated %zd bytes / %zd bytes\n", pos, fw->size);
+	}
+
+err_fw:
+	/* Change to slave address of application */
+	if (data->client->addr == QT602240_BOOT_LOW)
+		data->client->addr = QT602240_APP_LOW;
+	else
+		data->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;
+	}
+
+	/* Firmware update supports from version 21 */
+	if ((data->info->version < QT602240_VER_21) ||
+			(version < QT602240_VER_21)) {
+		dev_err(dev, "Firmware update supports from version 21\n");
+		return -EINVAL;
+	}
+
+	/* Interrupt disable */
+	disable_irq(data->irq);
+
+	ret = qt602240_load_fw(dev, QT602240_FW_NAME);
+	if (ret < 0)
+		dev_err(dev, "The firmware update failed(%d)\n", ret);
+	else {
+		dev_info(dev, "The firmware update successed\n");
+
+		/* Wait for reset */
+		msleep(QT602240_FWRESET_TIME);
+
+		kfree(data->object_table);
+		kfree(data->info);
+
+		qt602240_initialize(data);
+	}
+
+	/* Interrupt enable */
+	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 int __devinit qt602240_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct qt602240_data *data;
+	struct input_dev *input_dev = NULL;
+	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;
+
+	__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_TRACKING_ID, 0,
+			QT602240_MAX_ID, 0, 0);
+	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);
+
+	INIT_WORK(&data->ts_event_work, qt602240_irq_worker);
+	INIT_WORK(&data->ts_disable_work, qt602240_disable_worker);
+	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 < 0)
+		goto err_free_object;
+
+	ret = request_irq(client->irq, qt602240_interrupt, IRQF_TRIGGER_FALLING,
+			client->dev.driver->name, data);
+
+	if (ret < 0) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto err_free_object;
+	}
+
+	ret = input_register_device(input_dev);
+	if (ret < 0)
+		goto err_free_irq;
+
+	ret = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group);
+	if (ret)
+		goto err_free_irq;
+
+	return 0;
+
+err_free_irq:
+	free_irq(client->irq, data);
+err_free_object:
+	kfree(data->object_table);
+	kfree(data->info);
+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);
+	cancel_work_sync(&data->ts_event_work);
+	cancel_work_sync(&data->ts_disable_work);
+	input_unregister_device(data->input_dev);
+	kfree(data->object_table);
+	kfree(data->info);
+	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);
+
+	/* Touch disable */
+	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL,
+			0);
+
+	qt602240_write_object(data, QT602240_GEN_POWER,
+			QT602240_POWER_IDLEACQINT, 0);
+	qt602240_write_object(data, QT602240_GEN_POWER,
+			QT602240_POWER_ACTVACQINT, 0);
+
+	return 0;
+}
+
+static int qt602240_resume(struct i2c_client *client)
+{
+	struct qt602240_data *data = i2c_get_clientdata(client);
+
+	schedule_work(&data->ts_disable_work);
+
+	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..3a8bf19
--- /dev/null
+++ b/include/linux/i2c/qt602240_ts.h
@@ -0,0 +1,40 @@
+/*
+ * 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 tchdi;
+	unsigned int voltage;
+	unsigned char orient;
+};
+
+#endif /* __LINUX_QT602240_TS_H */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-23 13:16 [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver Joonyoung Shim
@ 2010-06-23 17:34 ` Dmitry Torokhov
  2010-06-23 20:06   ` Henrik Rydberg
                     ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Dmitry Torokhov @ 2010-06-23 17:34 UTC (permalink / raw)
  To: Joonyoung Shim; +Cc: linux-input, kyungmin.park, Henrik Rydberg

Hi Joonyoung,

On Wed, Jun 23, 2010 at 10:16:07PM +0900, Joonyoung Shim wrote:
> +
> +static void qt602240_input_read(struct qt602240_data *data)
> +{
> +	struct qt602240_message message;
> +	struct qt602240_object *object;
> +	struct device *dev = &data->client->dev;
> +	struct input_dev *input_dev = data->input_dev;
> +	u8 reportid;
> +	u8 max_reportid;
> +	u8 min_reportid;
> +
> +repeat:
> +	if (qt602240_read_message(data, &message)) {
> +		dev_err(dev, "Failed to read message\n");
> +		return;
> +	}
> +
> +	reportid = message.reportid;
> +
> +	/* Check it remains the message to process */
> +	if (reportid == 0xff)
> +		return;
> +
> +	/* whether reportid is thing of QT602240_TOUCH_MULTI */
> +	object = qt602240_get_object(data, QT602240_TOUCH_MULTI);
> +	if (!object)
> +		return;
> +
> +	max_reportid = object->max_reportid;
> +	min_reportid = max_reportid - object->num_report_ids + 1;
> +
> +	if ((reportid >= min_reportid) && (reportid <= max_reportid)) {
> +		u8 id;
> +		u8 status;
> +		int x;
> +		int y;
> +		int area;
> +		int finger_num = 0;
> +
> +		id = reportid - min_reportid;
> +		status = message.message[0];
> +
> +		/* Check the touch is present on the screen */
> +		if (!(status & QT602240_DETECT))
> +			goto release;
> +
> +		/* Check only AMP detection */
> +		if (!(status & (QT602240_PRESS | QT602240_MOVE)))
> +			goto repeat;
> +
> +		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);
> +
> +		data->finger[id].status = status & QT602240_MOVE ?
> +			QT602240_MOVE : QT602240_PRESS;
> +		data->finger[id].x = x;
> +		data->finger[id].y = y;
> +		data->finger[id].area = area;
> +
> +		input_report_key(input_dev, BTN_TOUCH, 1);
> +		input_report_abs(input_dev, ABS_X, x);
> +		input_report_abs(input_dev, ABS_Y, y);
> +
> +		goto mt_report;
> +
> +release:
> +		if (status & QT602240_RELEASE) {
> +			dev_dbg(dev, "[%d] released\n", id);
> +
> +			data->finger[id].status = QT602240_RELEASE;
> +			data->finger[id].area = 0;
> +		}
> +
> +mt_report:
> +		for (id = 0; id < QT602240_MAX_FINGER; id++) {
> +			if (!data->finger[id].status)
> +				continue;
> +
> +			input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
> +			input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
> +					data->finger[id].area);
> +
> +			if (data->finger[id].status == QT602240_RELEASE)
> +				data->finger[id].status = 0;
> +			else {
> +				input_report_abs(input_dev, ABS_MT_POSITION_X,
> +						data->finger[id].x);
> +				input_report_abs(input_dev, ABS_MT_POSITION_Y,
> +						data->finger[id].y);
> +				finger_num++;
> +			}
> +
> +			input_mt_sync(input_dev);
> +		}
> +
> +		if (!finger_num)
> +			input_report_key(input_dev, BTN_TOUCH, 0);
> +		input_sync(input_dev);
> +	} else {
> +		qt602240_dump_message(dev, &message);
> +		qt602240_check_config_error(data, &message, reportid);
> +	}
> +
> +	goto repeat;

I really dislike gotos that implement logic flow control (i.e. for me
gotos are acceptable in error path and in one-off cases when you restart
processing, like the CRC error in one of the functions above). Please do
not use old Fortran stylein kernel.

> +}
> +
> +static void qt602240_irq_worker(struct work_struct *work)
> +{
> +	struct qt602240_data *data = container_of(work,
> +			struct qt602240_data, ts_event_work);
> +
> +	qt602240_input_read(data);
> +}
> +
> +static void qt602240_disable_worker(struct work_struct *work)
> +{
> +	struct qt602240_data *data = container_of(work,
> +			struct qt602240_data, ts_disable_work);
> +
> +	/* Soft reset */
> +	qt602240_write_object(data, QT602240_GEN_COMMAND,
> +			QT602240_COMMAND_RESET, 1);
> +
> +	msleep(QT602240_RESET_TIME);
> +
> +	/* Touch enable */
> +	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL,
> +			0x83);
> +}
> +
> +static irqreturn_t qt602240_interrupt(int irq, void *dev_id)
> +{
> +	struct qt602240_data *data = dev_id;
> +
> +	if (!work_pending(&data->ts_event_work))
> +		schedule_work(&data->ts_event_work);
> +

Thios begs for use of threaded IRQs.

> +	return IRQ_HANDLED;
> +}
> +
> +
> +static int qt602240_initialize(struct qt602240_data *data)
> +{
> +	struct i2c_client *client = data->client;
> +	struct qt602240_info *info;
> +	int i;
> +	int ret;
> +	u16 reg;
> +	u8 buf[QT602240_OBJECT_SIZE];
> +	u8 reportid = 0;
> +
> +	info = data->info = kzalloc(sizeof(struct qt602240_info), GFP_KERNEL);

Why do you allocate it separately instead of embedding (entire
structure) into qt602240_data?

> +
> +	if (!info) {
> +		dev_err(&data->client->dev, "Failed to allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +
> +		pos += frame_size;
> +
> +		dev_info(dev, "Updated %zd bytes / %zd bytes\n", pos, fw->size);

dev_dbg() maybe?

> +	}
> +
> +err_fw:
> +	/* Change to slave address of application */
> +	if (data->client->addr == QT602240_BOOT_LOW)
> +		data->client->addr = QT602240_APP_LOW;
> +	else
> +		data->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;
> +	}
> +
> +	/* Firmware update supports from version 21 */
> +	if ((data->info->version < QT602240_VER_21) ||
> +			(version < QT602240_VER_21)) {
> +		dev_err(dev, "Firmware update supports from version 21\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Interrupt disable */
> +	disable_irq(data->irq);
> +
> +	ret = qt602240_load_fw(dev, QT602240_FW_NAME);
> +	if (ret < 0)
> +		dev_err(dev, "The firmware update failed(%d)\n", ret);
> +	else {
> +		dev_info(dev, "The firmware update successed\n");

dev_dbg() as well.

> +
> +		/* Wait for reset */
> +		msleep(QT602240_FWRESET_TIME);
> +
> +		kfree(data->object_table);
> +		kfree(data->info);
> +
> +		qt602240_initialize(data);
> +	}
> +
> +	/* Interrupt enable */
> +	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 int __devinit qt602240_probe(struct i2c_client *client,
> +		const struct i2c_device_id *id)
> +{
> +	struct qt602240_data *data;
> +	struct input_dev *input_dev = NULL;

No need to initialize to NULL.

> +	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;
> +
> +	__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_TRACKING_ID, 0,
> +			QT602240_MAX_ID, 0, 0);
> +	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);
> +
> +	INIT_WORK(&data->ts_event_work, qt602240_irq_worker);
> +	INIT_WORK(&data->ts_disable_work, qt602240_disable_worker);
> +	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 < 0)
> +		goto err_free_object;
> +
> +	ret = request_irq(client->irq, qt602240_interrupt, IRQF_TRIGGER_FALLING,
> +			client->dev.driver->name, data);
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev, "Failed to register interrupt\n");
> +		goto err_free_object;
> +	}
> +
> +	ret = input_register_device(input_dev);
> +	if (ret < 0)
> +		goto err_free_irq;
> +
> +	ret = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group);
> +	if (ret)
> +		goto err_free_irq;

After input_register_device() succeeded you need to call
input_unregister_device().

> +
> +	return 0;
> +
> +err_free_irq:
> +	free_irq(client->irq, data);
> +err_free_object:
> +	kfree(data->object_table);
> +	kfree(data->info);
> +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);
> +	cancel_work_sync(&data->ts_event_work);
> +	cancel_work_sync(&data->ts_disable_work);
> +	input_unregister_device(data->input_dev);
> +	kfree(data->object_table);
> +	kfree(data->info);
> +	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);
> +
> +	/* Touch disable */
> +	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL,
> +			0);
> +
> +	qt602240_write_object(data, QT602240_GEN_POWER,
> +			QT602240_POWER_IDLEACQINT, 0);
> +	qt602240_write_object(data, QT602240_GEN_POWER,
> +			QT602240_POWER_ACTVACQINT, 0);
> +
> +	return 0;
> +}
> +
> +static int qt602240_resume(struct i2c_client *client)
> +{
> +	struct qt602240_data *data = i2c_get_clientdata(client);
> +
> +	schedule_work(&data->ts_disable_work);

Hmm, what happens if you go through suspend and resume process very
quickly? Won't your suspend fight with pending resume work? Also, name
of the work is pretty confusing.. Why is it ts_disable_work?

Also, what about open() and close() methods for the input device?

Andplease CC Henrik Rydberg <rydberg@euromail.se> so he can take a look
at the detail of MT protocol.

Thanks!

-- 
Dmitry

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-23 17:34 ` Dmitry Torokhov
@ 2010-06-23 20:06   ` Henrik Rydberg
  2010-06-24  1:41     ` Joonyoung Shim
  2010-06-23 20:18   ` Henrik Rydberg
  2010-06-24  0:58   ` Joonyoung Shim
  2 siblings, 1 reply; 9+ messages in thread
From: Henrik Rydberg @ 2010-06-23 20:06 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Joonyoung Shim, linux-input, kyungmin.park

Hi Joonyoung,
[...]
>> +static void qt602240_input_read(struct qt602240_data *data)
>> +{
>> +	struct qt602240_message message;
>> +	struct qt602240_object *object;
>> +	struct device *dev = &data->client->dev;
>> +	struct input_dev *input_dev = data->input_dev;
>> +	u8 reportid;
>> +	u8 max_reportid;
>> +	u8 min_reportid;
>> +
>> +repeat:
>> +	if (qt602240_read_message(data, &message)) {
>> +		dev_err(dev, "Failed to read message\n");
>> +		return;
>> +	}
>> +
>> +	reportid = message.reportid;
>> +
>> +	/* Check it remains the message to process */
>> +	if (reportid == 0xff)
>> +		return;
>> +
>> +	/* whether reportid is thing of QT602240_TOUCH_MULTI */
>> +	object = qt602240_get_object(data, QT602240_TOUCH_MULTI);
>> +	if (!object)
>> +		return;
>> +
>> +	max_reportid = object->max_reportid;
>> +	min_reportid = max_reportid - object->num_report_ids + 1;
>> +
>> +	if ((reportid >= min_reportid) && (reportid <= max_reportid)) {
>> +		u8 id;
>> +		u8 status;
>> +		int x;
>> +		int y;
>> +		int area;
>> +		int finger_num = 0;
>> +
>> +		id = reportid - min_reportid;
>> +		status = message.message[0];
>> +
>> +		/* Check the touch is present on the screen */
>> +		if (!(status & QT602240_DETECT))
>> +			goto release;
>> +
>> +		/* Check only AMP detection */
>> +		if (!(status & (QT602240_PRESS | QT602240_MOVE)))
>> +			goto repeat;
>> +
>> +		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);
>> +
>> +		data->finger[id].status = status & QT602240_MOVE ?
>> +			QT602240_MOVE : QT602240_PRESS;
>> +		data->finger[id].x = x;
>> +		data->finger[id].y = y;
>> +		data->finger[id].area = area;
>> +
>> +		input_report_key(input_dev, BTN_TOUCH, 1);
>> +		input_report_abs(input_dev, ABS_X, x);
>> +		input_report_abs(input_dev, ABS_Y, y);
>> +
>> +		goto mt_report;
>> +
>> +release:
>> +		if (status & QT602240_RELEASE) {
>> +			dev_dbg(dev, "[%d] released\n", id);
>> +
>> +			data->finger[id].status = QT602240_RELEASE;
>> +			data->finger[id].area = 0;
>> +		}
>> +
>> +mt_report:
>> +		for (id = 0; id < QT602240_MAX_FINGER; id++) {
>> +			if (!data->finger[id].status)
>> +				continue;
>> +
>> +			input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
>> +			input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
>> +					data->finger[id].area);
>> +
>> +			if (data->finger[id].status == QT602240_RELEASE)
>> +				data->finger[id].status = 0;
>> +			else {
>> +				input_report_abs(input_dev, ABS_MT_POSITION_X,
>> +						data->finger[id].x);
>> +				input_report_abs(input_dev, ABS_MT_POSITION_Y,
>> +						data->finger[id].y);
>> +				finger_num++;
>> +			}

The finger[id].status is checked twice in this block, is there any particular
reason for it? Either way, reporting only a part of the finger properties before
input_mt_sync() is wrong. Perhaps one can move the second test up together with
the first one?

>> +
>> +			input_mt_sync(input_dev);
>> +		}
>> +
>> +		if (!finger_num)
>> +			input_report_key(input_dev, BTN_TOUCH, 0);

The lines above can be combined with the first BTN_TOUCH instance to something
like this:

	input_report_key(input_dev, BTN_TOUCH, finger_num > 0);

The input core will not emit the key unless it actually changes.

>> +		input_sync(input_dev);
>> +	} else {
>> +		qt602240_dump_message(dev, &message);
>> +		qt602240_check_config_error(data, &message, reportid);
>> +	}
>> +
>> +	goto repeat;
[...]
>> +	__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_TRACKING_ID, 0,
>> +			QT602240_MAX_ID, 0, 0);

What is a normal value for QT602240_MAX_ID? Is it modified every time there is a
new 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);
[...]

In general, I think the functions of this driver are too long. Splitting them up
might do good.

Thanks!
Henrik


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-23 17:34 ` Dmitry Torokhov
  2010-06-23 20:06   ` Henrik Rydberg
@ 2010-06-23 20:18   ` Henrik Rydberg
  2010-06-24  0:58   ` Joonyoung Shim
  2 siblings, 0 replies; 9+ messages in thread
From: Henrik Rydberg @ 2010-06-23 20:18 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Joonyoung Shim, linux-input, kyungmin.park, Henrik Rydberg

Dmitry Torokhov wrote:
[...]
> Andplease CC Henrik Rydberg <rydberg@euromail.se> so he can take a look
> at the detail of MT protocol.

The get_maintainer.pl does not return my name, I should probably add something
about the MT protocol to MAINTAINERS.

Henrik


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-23 17:34 ` Dmitry Torokhov
  2010-06-23 20:06   ` Henrik Rydberg
  2010-06-23 20:18   ` Henrik Rydberg
@ 2010-06-24  0:58   ` Joonyoung Shim
  2 siblings, 0 replies; 9+ messages in thread
From: Joonyoung Shim @ 2010-06-24  0:58 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, kyungmin.park, Henrik Rydberg

On 6/24/2010 2:34 AM, Dmitry Torokhov wrote:
> Hi Joonyoung,
> 
> On Wed, Jun 23, 2010 at 10:16:07PM +0900, Joonyoung Shim wrote:
>> +
>> +static void qt602240_input_read(struct qt602240_data *data)
>> +{
>> +	struct qt602240_message message;
>> +	struct qt602240_object *object;
>> +	struct device *dev = &data->client->dev;
>> +	struct input_dev *input_dev = data->input_dev;
>> +	u8 reportid;
>> +	u8 max_reportid;
>> +	u8 min_reportid;
>> +
>> +repeat:
>> +	if (qt602240_read_message(data, &message)) {
>> +		dev_err(dev, "Failed to read message\n");
>> +		return;
>> +	}
>> +
>> +	reportid = message.reportid;
>> +
>> +	/* Check it remains the message to process */
>> +	if (reportid == 0xff)
>> +		return;
>> +
>> +	/* whether reportid is thing of QT602240_TOUCH_MULTI */
>> +	object = qt602240_get_object(data, QT602240_TOUCH_MULTI);
>> +	if (!object)
>> +		return;
>> +
>> +	max_reportid = object->max_reportid;
>> +	min_reportid = max_reportid - object->num_report_ids + 1;
>> +
>> +	if ((reportid >= min_reportid) && (reportid <= max_reportid)) {
>> +		u8 id;
>> +		u8 status;
>> +		int x;
>> +		int y;
>> +		int area;
>> +		int finger_num = 0;
>> +
>> +		id = reportid - min_reportid;
>> +		status = message.message[0];
>> +
>> +		/* Check the touch is present on the screen */
>> +		if (!(status & QT602240_DETECT))
>> +			goto release;
>> +
>> +		/* Check only AMP detection */
>> +		if (!(status & (QT602240_PRESS | QT602240_MOVE)))
>> +			goto repeat;
>> +
>> +		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);
>> +
>> +		data->finger[id].status = status & QT602240_MOVE ?
>> +			QT602240_MOVE : QT602240_PRESS;
>> +		data->finger[id].x = x;
>> +		data->finger[id].y = y;
>> +		data->finger[id].area = area;
>> +
>> +		input_report_key(input_dev, BTN_TOUCH, 1);
>> +		input_report_abs(input_dev, ABS_X, x);
>> +		input_report_abs(input_dev, ABS_Y, y);
>> +
>> +		goto mt_report;
>> +
>> +release:
>> +		if (status & QT602240_RELEASE) {
>> +			dev_dbg(dev, "[%d] released\n", id);
>> +
>> +			data->finger[id].status = QT602240_RELEASE;
>> +			data->finger[id].area = 0;
>> +		}
>> +
>> +mt_report:
>> +		for (id = 0; id < QT602240_MAX_FINGER; id++) {
>> +			if (!data->finger[id].status)
>> +				continue;
>> +
>> +			input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
>> +			input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
>> +					data->finger[id].area);
>> +
>> +			if (data->finger[id].status == QT602240_RELEASE)
>> +				data->finger[id].status = 0;
>> +			else {
>> +				input_report_abs(input_dev, ABS_MT_POSITION_X,
>> +						data->finger[id].x);
>> +				input_report_abs(input_dev, ABS_MT_POSITION_Y,
>> +						data->finger[id].y);
>> +				finger_num++;
>> +			}
>> +
>> +			input_mt_sync(input_dev);
>> +		}
>> +
>> +		if (!finger_num)
>> +			input_report_key(input_dev, BTN_TOUCH, 0);
>> +		input_sync(input_dev);
>> +	} else {
>> +		qt602240_dump_message(dev, &message);
>> +		qt602240_check_config_error(data, &message, reportid);
>> +	}
>> +
>> +	goto repeat;
> 
> I really dislike gotos that implement logic flow control (i.e. for me
> gotos are acceptable in error path and in one-off cases when you restart
> processing, like the CRC error in one of the functions above). Please do
> not use old Fortran stylein kernel.
> 

OK. I used while statement at first then it gave me deep depth 
statements, but i will change goto to other as your opinion.

>> +}
>> +
>> +static void qt602240_irq_worker(struct work_struct *work)
>> +{
>> +	struct qt602240_data *data = container_of(work,
>> +			struct qt602240_data, ts_event_work);
>> +
>> +	qt602240_input_read(data);
>> +}
>> +
>> +static void qt602240_disable_worker(struct work_struct *work)
>> +{
>> +	struct qt602240_data *data = container_of(work,
>> +			struct qt602240_data, ts_disable_work);
>> +
>> +	/* Soft reset */
>> +	qt602240_write_object(data, QT602240_GEN_COMMAND,
>> +			QT602240_COMMAND_RESET, 1);
>> +
>> +	msleep(QT602240_RESET_TIME);
>> +
>> +	/* Touch enable */
>> +	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL,
>> +			0x83);
>> +}
>> +
>> +static irqreturn_t qt602240_interrupt(int irq, void *dev_id)
>> +{
>> +	struct qt602240_data *data = dev_id;
>> +
>> +	if (!work_pending(&data->ts_event_work))
>> +		schedule_work(&data->ts_event_work);
>> +
> 
> Thios begs for use of threaded IRQs.
> 

OK.

>> +	return IRQ_HANDLED;
>> +}
>> +
>> +
>> +static int qt602240_initialize(struct qt602240_data *data)
>> +{
>> +	struct i2c_client *client = data->client;
>> +	struct qt602240_info *info;
>> +	int i;
>> +	int ret;
>> +	u16 reg;
>> +	u8 buf[QT602240_OBJECT_SIZE];
>> +	u8 reportid = 0;
>> +
>> +	info = data->info = kzalloc(sizeof(struct qt602240_info), GFP_KERNEL);
> 
> Why do you allocate it separately instead of embedding (entire
> structure) into qt602240_data?
> 

Right, it's better.

>> +
>> +	if (!info) {
>> +		dev_err(&data->client->dev, "Failed to allocate memory\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +
>> +		pos += frame_size;
>> +
>> +		dev_info(dev, "Updated %zd bytes / %zd bytes\n", pos, fw->size);
> 
> dev_dbg() maybe?
> 

OK.

>> +	}
>> +
>> +err_fw:
>> +	/* Change to slave address of application */
>> +	if (data->client->addr == QT602240_BOOT_LOW)
>> +		data->client->addr = QT602240_APP_LOW;
>> +	else
>> +		data->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;
>> +	}
>> +
>> +	/* Firmware update supports from version 21 */
>> +	if ((data->info->version < QT602240_VER_21) ||
>> +			(version < QT602240_VER_21)) {
>> +		dev_err(dev, "Firmware update supports from version 21\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Interrupt disable */
>> +	disable_irq(data->irq);
>> +
>> +	ret = qt602240_load_fw(dev, QT602240_FW_NAME);
>> +	if (ret < 0)
>> +		dev_err(dev, "The firmware update failed(%d)\n", ret);
>> +	else {
>> +		dev_info(dev, "The firmware update successed\n");
> 
> dev_dbg() as well.
> 

OK.

>> +
>> +		/* Wait for reset */
>> +		msleep(QT602240_FWRESET_TIME);
>> +
>> +		kfree(data->object_table);
>> +		kfree(data->info);
>> +
>> +		qt602240_initialize(data);
>> +	}
>> +
>> +	/* Interrupt enable */
>> +	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 int __devinit qt602240_probe(struct i2c_client *client,
>> +		const struct i2c_device_id *id)
>> +{
>> +	struct qt602240_data *data;
>> +	struct input_dev *input_dev = NULL;
> 
> No need to initialize to NULL.
> 

OK.

>> +	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;
>> +
>> +	__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_TRACKING_ID, 0,
>> +			QT602240_MAX_ID, 0, 0);
>> +	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);
>> +
>> +	INIT_WORK(&data->ts_event_work, qt602240_irq_worker);
>> +	INIT_WORK(&data->ts_disable_work, qt602240_disable_worker);
>> +	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 < 0)
>> +		goto err_free_object;
>> +
>> +	ret = request_irq(client->irq, qt602240_interrupt, IRQF_TRIGGER_FALLING,
>> +			client->dev.driver->name, data);
>> +
>> +	if (ret < 0) {
>> +		dev_err(&client->dev, "Failed to register interrupt\n");
>> +		goto err_free_object;
>> +	}
>> +
>> +	ret = input_register_device(input_dev);
>> +	if (ret < 0)
>> +		goto err_free_irq;
>> +
>> +	ret = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group);
>> +	if (ret)
>> +		goto err_free_irq;
> 
> After input_register_device() succeeded you need to call
> input_unregister_device().
> 

Ah, i missed.

>> +
>> +	return 0;
>> +
>> +err_free_irq:
>> +	free_irq(client->irq, data);
>> +err_free_object:
>> +	kfree(data->object_table);
>> +	kfree(data->info);
>> +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);
>> +	cancel_work_sync(&data->ts_event_work);
>> +	cancel_work_sync(&data->ts_disable_work);
>> +	input_unregister_device(data->input_dev);
>> +	kfree(data->object_table);
>> +	kfree(data->info);
>> +	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);
>> +
>> +	/* Touch disable */
>> +	qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL,
>> +			0);
>> +
>> +	qt602240_write_object(data, QT602240_GEN_POWER,
>> +			QT602240_POWER_IDLEACQINT, 0);
>> +	qt602240_write_object(data, QT602240_GEN_POWER,
>> +			QT602240_POWER_ACTVACQINT, 0);
>> +
>> +	return 0;
>> +}
>> +
>> +static int qt602240_resume(struct i2c_client *client)
>> +{
>> +	struct qt602240_data *data = i2c_get_clientdata(client);
>> +
>> +	schedule_work(&data->ts_disable_work);
> 
> Hmm, what happens if you go through suspend and resume process very
> quickly? Won't your suspend fight with pending resume work? Also, name
> of the work is pretty confusing.. Why is it ts_disable_work?
> 

When resume the chip needs soft reset and after reset needs long time
delay(65msec), so i used workqueue to reduce resume time. I'm not sure
it is no problem to use workqueue in PM function.
The ts_disable_work is wrong name derived from codes for other purpose
removed now. It should be changed.

> Also, what about open() and close() methods for the input device?
> 

OK, i will consider this methods.

> Andplease CC Henrik Rydberg <rydberg@euromail.se> so he can take a look
> at the detail of MT protocol.
> 
> Thanks!
> 

Thanks.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-23 20:06   ` Henrik Rydberg
@ 2010-06-24  1:41     ` Joonyoung Shim
  2010-06-24 10:27       ` Henrik Rydberg
  0 siblings, 1 reply; 9+ messages in thread
From: Joonyoung Shim @ 2010-06-24  1:41 UTC (permalink / raw)
  To: Henrik Rydberg; +Cc: Dmitry Torokhov, linux-input, kyungmin.park

On 6/24/2010 5:06 AM, Henrik Rydberg wrote:
> Hi Joonyoung,
> [...]
>>> +static void qt602240_input_read(struct qt602240_data *data)
>>> +{
>>> +	struct qt602240_message message;
>>> +	struct qt602240_object *object;
>>> +	struct device *dev = &data->client->dev;
>>> +	struct input_dev *input_dev = data->input_dev;
>>> +	u8 reportid;
>>> +	u8 max_reportid;
>>> +	u8 min_reportid;
>>> +
>>> +repeat:
>>> +	if (qt602240_read_message(data, &message)) {
>>> +		dev_err(dev, "Failed to read message\n");
>>> +		return;
>>> +	}
>>> +
>>> +	reportid = message.reportid;
>>> +
>>> +	/* Check it remains the message to process */
>>> +	if (reportid == 0xff)
>>> +		return;
>>> +
>>> +	/* whether reportid is thing of QT602240_TOUCH_MULTI */
>>> +	object = qt602240_get_object(data, QT602240_TOUCH_MULTI);
>>> +	if (!object)
>>> +		return;
>>> +
>>> +	max_reportid = object->max_reportid;
>>> +	min_reportid = max_reportid - object->num_report_ids + 1;
>>> +
>>> +	if ((reportid >= min_reportid) && (reportid <= max_reportid)) {
>>> +		u8 id;
>>> +		u8 status;
>>> +		int x;
>>> +		int y;
>>> +		int area;
>>> +		int finger_num = 0;
>>> +
>>> +		id = reportid - min_reportid;
>>> +		status = message.message[0];
>>> +
>>> +		/* Check the touch is present on the screen */
>>> +		if (!(status & QT602240_DETECT))
>>> +			goto release;
>>> +
>>> +		/* Check only AMP detection */
>>> +		if (!(status & (QT602240_PRESS | QT602240_MOVE)))
>>> +			goto repeat;
>>> +
>>> +		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);
>>> +
>>> +		data->finger[id].status = status & QT602240_MOVE ?
>>> +			QT602240_MOVE : QT602240_PRESS;
>>> +		data->finger[id].x = x;
>>> +		data->finger[id].y = y;
>>> +		data->finger[id].area = area;
>>> +
>>> +		input_report_key(input_dev, BTN_TOUCH, 1);
>>> +		input_report_abs(input_dev, ABS_X, x);
>>> +		input_report_abs(input_dev, ABS_Y, y);
>>> +
>>> +		goto mt_report;
>>> +
>>> +release:
>>> +		if (status & QT602240_RELEASE) {
>>> +			dev_dbg(dev, "[%d] released\n", id);
>>> +
>>> +			data->finger[id].status = QT602240_RELEASE;
>>> +			data->finger[id].area = 0;
>>> +		}
>>> +
>>> +mt_report:
>>> +		for (id = 0; id < QT602240_MAX_FINGER; id++) {
>>> +			if (!data->finger[id].status)
>>> +				continue;
>>> +
>>> +			input_report_abs(input_dev, ABS_MT_TRACKING_ID, id);
>>> +			input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
>>> +					data->finger[id].area);
>>> +
>>> +			if (data->finger[id].status == QT602240_RELEASE)
>>> +				data->finger[id].status = 0;
>>> +			else {
>>> +				input_report_abs(input_dev, ABS_MT_POSITION_X,
>>> +						data->finger[id].x);
>>> +				input_report_abs(input_dev, ABS_MT_POSITION_Y,
>>> +						data->finger[id].y);
>>> +				finger_num++;
>>> +			}
> 
> The finger[id].status is checked twice in this block, is there any particular
> reason for it?

There is three states press / release / none. The none state means that
the finger weren't be pressed still. First checking is to detect none
state and second checking is to distinguish press and release. Does it
need to report the finger of none state?

> Either way, reporting only a part of the finger properties before
> input_mt_sync() is wrong. Perhaps one can move the second test up together with
> the first one?
>

I don't know well what you mean. Please give me detailed thing.
 
>>> +
>>> +			input_mt_sync(input_dev);
>>> +		}
>>> +
>>> +		if (!finger_num)
>>> +			input_report_key(input_dev, BTN_TOUCH, 0);
> 
> The lines above can be combined with the first BTN_TOUCH instance to something
> like this:
> 
> 	input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
> 
> The input core will not emit the key unless it actually changes.
> 

I have a quick question. If finger_num is more than one, should 
BTN_TOUCH be reported before ABS_XX event reporting?

>>> +		input_sync(input_dev);
>>> +	} else {
>>> +		qt602240_dump_message(dev, &message);
>>> +		qt602240_check_config_error(data, &message, reportid);
>>> +	}
>>> +
>>> +	goto repeat;
> [...]
>>> +	__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_TRACKING_ID, 0,
>>> +			QT602240_MAX_ID, 0, 0);
> 
> What is a normal value for QT602240_MAX_ID? Is it modified every time there is a
> new touch?
> 

The ID value range can differ by chip firmware, but it can be calculated
from 0 to 9. ID is decided by touch order. If i pressed three fingers,
IDs ard 0, 1, 2.

>>> +	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);
> [...]
> 
> In general, I think the functions of this driver are too long. Splitting them up
> might do good.
> 

OK. i will try code cleaning.

Thanks.

> Thanks!
> Henrik
> 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-24  1:41     ` Joonyoung Shim
@ 2010-06-24 10:27       ` Henrik Rydberg
  2010-06-25  1:34         ` Joonyoung Shim
  0 siblings, 1 reply; 9+ messages in thread
From: Henrik Rydberg @ 2010-06-24 10:27 UTC (permalink / raw)
  To: Joonyoung Shim; +Cc: Dmitry Torokhov, linux-input, kyungmin.park

Joonyoung Shim wrote:
[...]
>> The finger[id].status is checked twice in this block, is there any particular
>> reason for it?
> 
> There is three states press / release / none. The none state means that
> the finger weren't be pressed still. First checking is to detect none
> state and second checking is to distinguish press and release. Does it
> need to report the finger of none state?
> 
>> Either way, reporting only a part of the finger properties before
>> input_mt_sync() is wrong. Perhaps one can move the second test up together with
>> the first one?
>>
> 
> I don't know well what you mean. Please give me detailed thing.

The code has one patch where (ABS_MT_TRACKING_ID, ABS_MT_TOUCH_MAJOR) is
reported, and another patch where (ABS_MT_TRACKING_ID, ABS_MT_TOUCH_MAJOR,
ABS_MT_POSITION_X, ABS_MT_POSITION_Y) is reported. This is incorrect. Either
send the release event with ABS_MT_TOUCH_MAJOR set to zero, or do not report the
finger at all.

>>>> +
>>>> +			input_mt_sync(input_dev);
>>>> +		}
>>>> +
>>>> +		if (!finger_num)
>>>> +			input_report_key(input_dev, BTN_TOUCH, 0);
>> The lines above can be combined with the first BTN_TOUCH instance to something
>> like this:
>>
>> 	input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
>>
>> The input core will not emit the key unless it actually changes.
>>
> 
> I have a quick question. If finger_num is more than one, should 
> BTN_TOUCH be reported before ABS_XX event reporting?

BTN_TOUCH should be placed first for the benefit of mousedev (is this correct,
Dmitry?), but there is no restriction on the ordering between MT events and
other events in the package.

>>>> +		input_sync(input_dev);
>>>> +	} else {
>>>> +		qt602240_dump_message(dev, &message);
>>>> +		qt602240_check_config_error(data, &message, reportid);
>>>> +	}
>>>> +
>>>> +	goto repeat;
>> [...]
>>>> +	__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_TRACKING_ID, 0,
>>>> +			QT602240_MAX_ID, 0, 0);
>> What is a normal value for QT602240_MAX_ID? Is it modified every time there is a
>> new touch?
>>
> 
> The ID value range can differ by chip firmware, but it can be calculated
> from 0 to 9. ID is decided by touch order. If i pressed three fingers,
> IDs ard 0, 1, 2.

In the MT protocol lingo, this is actually not a tracking id, but a slot id. A
tracking id increases for every new touch, and can be a much larger number than
the number of slots. Since the MT slot protocol is in the pipe now, perhaps you
would like to become the first driver to use the MT slots protocol? It seems it
would simplify the code of this driver.

>>>> +	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);
>> [...]
>>
>> In general, I think the functions of this driver are too long. Splitting them up
>> might do good.
>>
> 
> OK. i will try code cleaning.
> 
> Thanks.

Thanks,
Henrik


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-24 10:27       ` Henrik Rydberg
@ 2010-06-25  1:34         ` Joonyoung Shim
  2010-06-25 13:32           ` Henrik Rydberg
  0 siblings, 1 reply; 9+ messages in thread
From: Joonyoung Shim @ 2010-06-25  1:34 UTC (permalink / raw)
  To: Henrik Rydberg; +Cc: Dmitry Torokhov, linux-input, kyungmin.park

On 6/24/2010 7:27 PM, Henrik Rydberg wrote:
> Joonyoung Shim wrote:
> [...]
>>> The finger[id].status is checked twice in this block, is there any particular
>>> reason for it?
>> There is three states press / release / none. The none state means that
>> the finger weren't be pressed still. First checking is to detect none
>> state and second checking is to distinguish press and release. Does it
>> need to report the finger of none state?
>>
>>> Either way, reporting only a part of the finger properties before
>>> input_mt_sync() is wrong. Perhaps one can move the second test up together with
>>> the first one?
>>>
>> I don't know well what you mean. Please give me detailed thing.
> 
> The code has one patch where (ABS_MT_TRACKING_ID, ABS_MT_TOUCH_MAJOR) is
> reported, and another patch where (ABS_MT_TRACKING_ID, ABS_MT_TOUCH_MAJOR,
> ABS_MT_POSITION_X, ABS_MT_POSITION_Y) is reported. This is incorrect. Either
> send the release event with ABS_MT_TOUCH_MAJOR set to zero, or do not report the
> finger at all.
> 

OK. I had to remove ABS_MT_TRACKING_ID event to use MT protocol A.

>>>>> +
>>>>> +			input_mt_sync(input_dev);
>>>>> +		}
>>>>> +
>>>>> +		if (!finger_num)
>>>>> +			input_report_key(input_dev, BTN_TOUCH, 0);
>>> The lines above can be combined with the first BTN_TOUCH instance to something
>>> like this:
>>>
>>> 	input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
>>>
>>> The input core will not emit the key unless it actually changes.
>>>
>> I have a quick question. If finger_num is more than one, should 
>> BTN_TOUCH be reported before ABS_XX event reporting?
> 
> BTN_TOUCH should be placed first for the benefit of mousedev (is this correct,
> Dmitry?), but there is no restriction on the ordering between MT events and
> other events in the package.
> 

OK.

>>>>> +		input_sync(input_dev);
>>>>> +	} else {
>>>>> +		qt602240_dump_message(dev, &message);
>>>>> +		qt602240_check_config_error(data, &message, reportid);
>>>>> +	}
>>>>> +
>>>>> +	goto repeat;
>>> [...]
>>>>> +	__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_TRACKING_ID, 0,
>>>>> +			QT602240_MAX_ID, 0, 0);
>>> What is a normal value for QT602240_MAX_ID? Is it modified every time there is a
>>> new touch?
>>>
>> The ID value range can differ by chip firmware, but it can be calculated
>> from 0 to 9. ID is decided by touch order. If i pressed three fingers,
>> IDs ard 0, 1, 2.
> 
> In the MT protocol lingo, this is actually not a tracking id, but a slot id. A
> tracking id increases for every new touch, and can be a much larger number than
> the number of slots. Since the MT slot protocol is in the pipe now, perhaps you
> would like to become the first driver to use the MT slots protocol? It seems it
> would simplify the code of this driver.
> 

The ID of this chip is created in order, so i think the slot id and the
tracking id have same relative offset. Now, i will use MT protocol A and
i can change to MT slots protocol later if it is merged.

Thanks.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver
  2010-06-25  1:34         ` Joonyoung Shim
@ 2010-06-25 13:32           ` Henrik Rydberg
  0 siblings, 0 replies; 9+ messages in thread
From: Henrik Rydberg @ 2010-06-25 13:32 UTC (permalink / raw)
  To: Joonyoung Shim; +Cc: Dmitry Torokhov, linux-input, kyungmin.park

Joonyoung Shim wrote:
> On 6/24/2010 7:27 PM, Henrik Rydberg wrote:
>> Joonyoung Shim wrote:
>> [...]
>>>> The finger[id].status is checked twice in this block, is there any particular
>>>> reason for it?
>>> There is three states press / release / none. The none state means that
>>> the finger weren't be pressed still. First checking is to detect none
>>> state and second checking is to distinguish press and release. Does it
>>> need to report the finger of none state?
>>>
>>>> Either way, reporting only a part of the finger properties before
>>>> input_mt_sync() is wrong. Perhaps one can move the second test up together with
>>>> the first one?
>>>>
>>> I don't know well what you mean. Please give me detailed thing.
>> The code has one patch where (ABS_MT_TRACKING_ID, ABS_MT_TOUCH_MAJOR) is
>> reported, and another patch where (ABS_MT_TRACKING_ID, ABS_MT_TOUCH_MAJOR,
>> ABS_MT_POSITION_X, ABS_MT_POSITION_Y) is reported. This is incorrect. Either
>> send the release event with ABS_MT_TOUCH_MAJOR set to zero, or do not report the
>> finger at all.
>>
> 
> OK. I had to remove ABS_MT_TRACKING_ID event to use MT protocol A.

Good idea. It _is_ ok to use the tracking id in protocol A, but it is not really
necessary. There are no bandwidth benefits, and the mtdev library handles the
tracking well in userspace.

[...]
>>> The ID value range can differ by chip firmware, but it can be calculated
>>> from 0 to 9. ID is decided by touch order. If i pressed three fingers,
>>> IDs ard 0, 1, 2.
>> In the MT protocol lingo, this is actually not a tracking id, but a slot id. A
>> tracking id increases for every new touch, and can be a much larger number than
>> the number of slots. Since the MT slot protocol is in the pipe now, perhaps you
>> would like to become the first driver to use the MT slots protocol? It seems it
>> would simplify the code of this driver.
>>
> 
> The ID of this chip is created in order, so i think the slot id and the
> tracking id have same relative offset. Now, i will use MT protocol A and
> i can change to MT slots protocol later if it is merged.

Sounds good.

Thanks,
Henrik


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2010-06-25 13:32 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-06-23 13:16 [PATCH] input: qt602240 - Add ATMEL QT602240 touchscreen driver Joonyoung Shim
2010-06-23 17:34 ` Dmitry Torokhov
2010-06-23 20:06   ` Henrik Rydberg
2010-06-24  1:41     ` Joonyoung Shim
2010-06-24 10:27       ` Henrik Rydberg
2010-06-25  1:34         ` Joonyoung Shim
2010-06-25 13:32           ` Henrik Rydberg
2010-06-23 20:18   ` Henrik Rydberg
2010-06-24  0:58   ` Joonyoung Shim

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).