* [PATCH] input: Add Atmel maXTouch touchscreen driver
@ 2010-11-12 14:14 Iiro Valkonen
2010-11-12 20:10 ` Dmitry Torokhov
0 siblings, 1 reply; 6+ messages in thread
From: Iiro Valkonen @ 2010-11-12 14:14 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: kyungmin.park, jy0922.shim, linux-input
Add Atmel maXTouch touchscreen driver (I2C interface).
The MT slots protocol will be implemented later.
Signed-off-by: Iiro Valkonen (iiro.valkonen@atmel.com)
---
drivers/input/touchscreen/Kconfig | 13 +
drivers/input/touchscreen/Makefile | 1 +
drivers/input/touchscreen/atmel_maxtouch.c | 2104 ++++++++++++++++++++++++++++
include/linux/atmel_maxtouch.h | 310 ++++
4 files changed, 2428 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/touchscreen/atmel_maxtouch.c
create mode 100644 include/linux/atmel_maxtouch.h
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 06ea8da..a0cec0d 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -395,6 +395,19 @@ config TOUCHSCREEN_TOUCHWIN
To compile this driver as a module, choose M here: the
module will be called touchwin.
+config TOUCHSCREEN_ATMEL_MAXTOUCH
+ tristate "Atmel maXTouch based touchscreen: I2C Interface"
+ depends on I2C
+ help
+ Say Y here if you have a touchscreen interface using an
+ Amel maXTouch controller, and your board-specific initialization
+ code includes that in its table of I2C devices.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called maXTouch.
+
config TOUCHSCREEN_ATMEL_TSADCC
tristate "Atmel Touchscreen Interface"
depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 7cc1b4f..861941d 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
+obj-$(CONFIG_TOUCHSCREEN_ATMEL_MAXTOUCH)+= atmel_maxtouch.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
diff --git a/drivers/input/touchscreen/atmel_maxtouch.c b/drivers/input/touchscreen/atmel_maxtouch.c
new file mode 100644
index 0000000..cf22f65
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_maxtouch.c
@@ -0,0 +1,2104 @@
+/*
+ * Atmel maXTouch Touchscreen Controller Driver
+ *
+ *
+ * Copyright (C) 2010 Atmel Corporation
+ * Copyright (C) 2010 Ulf Samuelsson (ulf@atmel.com)
+ * Copyright (C) 2009 Raphael Derosso Pereira <raphaelpereira@gmail.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *
+ * Driver for Atmel maXTouch family of touch controllers.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/uaccess.h>
+
+#include <linux/atmel_maxtouch.h>
+
+
+#define DRIVER_VERSION "0.91a"
+
+static int debug = DEBUG_TRACE;
+/* Defaul comms mode = 0 */
+static int comms;
+module_param(debug, int, 0644);
+module_param(comms, int, 0644);
+
+MODULE_PARM_DESC(debug, "Activate debugging output");
+MODULE_PARM_DESC(comms, "Select communications mode");
+
+/* Device Info descriptor */
+/* Parsed from maXTouch "Id information" inside device */
+struct mxt_device_info {
+ u8 family_id;
+ u8 variant_id;
+ u8 major;
+ u8 minor;
+ u8 build;
+ u8 num_objs;
+ u8 x_size;
+ u8 y_size;
+ char family_name[16]; /* Family name */
+ char variant_name[16]; /* Variant name */
+ u16 num_nodes; /* Number of sensor nodes */
+};
+
+/* object descriptor table, parsed from maXTouch "object table" */
+struct mxt_object {
+ u16 chip_addr;
+ u8 type;
+ u8 size;
+ u8 instances;
+ u8 num_report_ids;
+};
+
+
+/* Mapping from report id to object type and instance */
+struct report_id_map {
+ u8 object;
+ u8 instance;
+/*
+ * This is the first report ID belonging to object. It enables us to
+ * find out easily the touch number: each touch has different report
+ * ID (which are assigned to touches in increasing order). By
+ * subtracting the first report ID from current, we get the touch
+ * number.
+ */
+ u8 first_rid;
+};
+
+
+/* Driver datastructure */
+struct mxt_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ char phys_name[32];
+ int irq;
+
+ u16 last_read_addr;
+ bool new_msgs;
+ u8 *last_message;
+
+ int valid_irq_counter;
+ int invalid_irq_counter;
+ int irq_counter;
+ int message_counter;
+ int read_fail_counter;
+
+
+ int bytes_to_read;
+
+ struct delayed_work dwork;
+ u8 xpos_format;
+ u8 ypos_format;
+
+ u8 numtouch;
+
+ struct mxt_device_info device_info;
+
+ u32 info_block_crc;
+ u32 configuration_crc;
+ u16 report_id_count;
+ struct report_id_map *rid_map;
+ struct mxt_object *object_table;
+
+ u16 msg_proc_addr;
+ u8 message_size;
+
+ u16 max_x_val;
+ u16 max_y_val;
+
+ void (*init_hw)(void);
+ void (*exit_hw)(void);
+ u8 (*valid_interrupt)(void);
+ u8 (*read_chg)(void);
+
+ /* debugfs variables */
+ struct dentry *debug_dir;
+ int current_debug_datap;
+
+ struct mutex debug_mutex;
+ u16 *debug_data;
+
+ /* Character device variables */
+ struct cdev cdev;
+ struct cdev cdev_messages; /* 2nd Char dev for messages */
+ dev_t dev_num;
+ struct class *mxt_class;
+
+
+ u16 address_pointer;
+ bool valid_ap;
+
+ /* Message buffer & pointers */
+ char *messages;
+ int msg_buffer_startp, msg_buffer_endp;
+
+ /* Put only non-touch messages to buffer if this is set */
+ char nontouch_msg_only;
+ struct mutex msg_mutex;
+};
+
+#define I2C_RETRY_COUNT 5
+#define I2C_PAYLOAD_SIZE 254
+
+/* Returns the start address of object in mXT memory. */
+#define MXT_BASE_ADDR(object_type, mxt) \
+ get_object_address(object_type, 0, mxt->object_table, \
+ mxt->device_info.num_objs)
+
+/* Maps a report ID to an object type (object type number). */
+#define REPORT_ID_TO_OBJECT(rid, mxt) \
+ (((rid) == 0xff) ? 0 : mxt->rid_map[rid].object)
+
+/* Maps a report ID to an object type (string). */
+#define REPORT_ID_TO_OBJECT_NAME(rid, mxt) \
+ object_type_name[REPORT_ID_TO_OBJECT(rid, mxt)]
+
+/* Returns non-zero if given object is a touch object */
+#define IS_TOUCH_OBJECT(object) \
+ ((object == MXT_TOUCH_MULTITOUCHSCREEN_T9) || \
+ (object == MXT_TOUCH_KEYARRAY_T15) || \
+ (object == MXT_TOUCH_PROXIMITY_T23) || \
+ (object == MXT_TOUCH_SINGLETOUCHSCREEN_T10) || \
+ (object == MXT_TOUCH_XSLIDER_T11) || \
+ (object == MXT_TOUCH_YSLIDER_T12) || \
+ (object == MXT_TOUCH_XWHEEL_T13) || \
+ (object == MXT_TOUCH_YWHEEL_T14) || \
+ (object == MXT_TOUCH_KEYSET_T31) || \
+ (object == MXT_TOUCH_XSLIDERSET_T32) ? 1 : 0)
+
+#define mxt_debug(level, ...) \
+ do { \
+ if (debug >= (level)) \
+ pr_debug(__VA_ARGS__); \
+ } while (0)
+
+
+/*
+ * Check whether we have multi-touch enabled kernel; if not, report just the
+ * first touch (on mXT224, the maximum is 10 simultaneous touches).
+ * Because just the 1st one is reported, it might seem that the screen is not
+ * responding to touch if the first touch is removed while the screen is being
+ * touched by another finger, so beware.
+ *
+ */
+
+#ifdef ABS_MT_TRACKING_ID
+static inline void report_mt(int touch_number, int size, int x, int y,
+ struct mxt_data *mxt) {
+ input_report_abs(mxt->input, ABS_MT_TRACKING_ID, touch_number);
+ input_report_abs(mxt->input, ABS_MT_TOUCH_MAJOR, size);
+ input_report_abs(mxt->input, ABS_MT_POSITION_X, x);
+ input_report_abs(mxt->input, ABS_MT_POSITION_Y, y);
+ input_mt_sync(mxt->input);
+}
+#else
+static inline void report_mt(int touch_number, int size, int x, int y,
+ struct mxt_data *mxt) {
+ if (touch_number == 0) {
+ input_report_abs(mxt->input, ABS_TOOL_WIDTH, size);
+ input_report_abs(mxt->input, ABS_X, x);
+ input_report_abs(mxt->input, ABS_Y, y);
+ }
+}
+#endif
+
+
+static inline void report_gesture(int data, struct mxt_data *mxt)
+{
+ input_event(mxt->input, EV_MSC, MSC_GESTURE, data);
+}
+
+
+static const u8 *object_type_name[] = {
+ [0] = "Reserved",
+ [5] = "GEN_MESSAGEPROCESSOR_T5",
+ [6] = "GEN_COMMANDPROCESSOR_T6",
+ [7] = "GEN_POWERCONFIG_T7",
+ [8] = "GEN_ACQUIRECONFIG_T8",
+ [9] = "TOUCH_MULTITOUCHSCREEN_T9",
+ [15] = "TOUCH_KEYARRAY_T15",
+ [17] = "SPT_COMMSCONFIG_T18",
+ [19] = "SPT_GPIOPWM_T19",
+ [20] = "PROCI_GRIPFACESUPPRESSION_T20",
+ [22] = "PROCG_NOISESUPPRESSION_T22",
+ [23] = "TOUCH_PROXIMITY_T23",
+ [24] = "PROCI_ONETOUCHGESTUREPROCESSOR_T24",
+ [25] = "SPT_SELFTEST_T25",
+ [27] = "PROCI_TWOTOUCHGESTUREPROCESSOR_T27",
+ [28] = "SPT_CTECONFIG_T28",
+ [37] = "DEBUG_DIAGNOSTICS_T37",
+ [38] = "SPT_USER_DATA_T38",
+ [40] = "PROCI_GRIPSUPPRESSION_T40",
+ [41] = "PROCI_PALMSUPPRESSION_T41",
+ [42] = "PROCI_FACESUPPRESSION_T42",
+ [43] = "SPT_DIGITIZER_T43",
+ [44] = "SPT_MESSAGECOUNT_T44",
+};
+
+
+static u16 get_object_address(uint8_t object_type,
+ uint8_t instance,
+ struct mxt_object *object_table,
+ int max_objs);
+
+static int mxt_write_ap(struct mxt_data *mxt, u16 ap);
+
+static int mxt_read_block_wo_addr(struct i2c_client *client,
+ u16 length,
+ u8 *value);
+
+ssize_t debug_data_read(struct mxt_data *mxt, char *buf, size_t count,
+ loff_t *ppos, u8 debug_command)
+{
+ int i;
+ u16 *data;
+ u16 diagnostics_reg;
+ int offset = 0;
+ int size;
+ int read_size;
+ int error;
+ char *buf_start;
+ u16 debug_data_addr;
+ u16 page_address;
+ u8 page;
+ u8 debug_command_reg;
+
+ data = mxt->debug_data;
+ if (data == NULL)
+ return -EIO;
+
+ /* If first read after open, read all data to buffer. */
+ if (mxt->current_debug_datap == 0) {
+ diagnostics_reg =
+ MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+ MXT_ADR_T6_DIAGNOSTIC;
+ if (count > (mxt->device_info.num_nodes * 2))
+ count = mxt->device_info.num_nodes;
+ debug_data_addr =
+ MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) +
+ MXT_ADR_T37_DATA;
+ page_address = MXT_BASE_ADDR(MXT_DEBUG_DIAGNOSTIC_T37, mxt) +
+ MXT_ADR_T37_PAGE;
+ error = mxt_read_block(mxt->client, page_address, 1, &page);
+ if (error < 0)
+ return error;
+ mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);
+ while (page != 0) {
+ error = mxt_write_byte(mxt->client,
+ diagnostics_reg,
+ MXT_CMD_T6_PAGE_DOWN);
+ if (error < 0)
+ return error;
+ /* Wait for command to be handled; when it has, the
+ register will be cleared. */
+ debug_command_reg = 1;
+ while (debug_command_reg != 0) {
+ error = mxt_read_block(mxt->client,
+ diagnostics_reg, 1,
+ &debug_command_reg);
+ if (error < 0)
+ return error;
+ mxt_debug(DEBUG_TRACE,
+ "Waiting for debug diag command "
+ "to propagate...\n");
+
+ }
+ error = mxt_read_block(mxt->client, page_address, 1,
+ &page);
+ if (error < 0)
+ return error;
+ mxt_debug(DEBUG_TRACE, "debug data page = %d\n", page);
+ }
+
+ /*
+ * Lock mutex to prevent writing some unwanted data to debug
+ * command register. User can still write through the char
+ * device interface though. TODO: fix?
+ */
+
+ mutex_lock(&mxt->debug_mutex);
+ /* Configure Debug Diagnostics object to show deltas/refs */
+ error = mxt_write_byte(mxt->client, diagnostics_reg,
+ debug_command);
+
+ /* Wait for command to be handled; when it has, the
+ * register will be cleared. */
+ debug_command_reg = 1;
+ while (debug_command_reg != 0) {
+ error = mxt_read_block(mxt->client,
+ diagnostics_reg, 1,
+ &debug_command_reg);
+ if (error < 0)
+ return error;
+ mxt_debug(DEBUG_TRACE, "Waiting for debug diag command "
+ "to propagate...\n");
+ }
+
+ if (error < 0) {
+ printk(KERN_WARNING
+ "Error writing to maXTouch device!\n");
+ return error;
+ }
+
+ size = mxt->device_info.num_nodes * sizeof(u16);
+
+ while (size > 0) {
+ read_size = size > 128 ? 128 : size;
+ mxt_debug(DEBUG_TRACE,
+ "Debug data read loop, reading %d bytes...\n",
+ read_size);
+ error = mxt_read_block(mxt->client,
+ debug_data_addr,
+ read_size,
+ (u8 *) &data[offset]);
+ if (error < 0) {
+ printk(KERN_WARNING
+ "Error reading debug data\n");
+ goto error;
+ }
+ offset += read_size/2;
+ size -= read_size;
+
+ /* Select next page */
+ error = mxt_write_byte(mxt->client, diagnostics_reg,
+ MXT_CMD_T6_PAGE_UP);
+ if (error < 0) {
+ printk(KERN_WARNING
+ "Error writing to maXTouch device!\n");
+ goto error;
+ }
+ }
+ mutex_unlock(&mxt->debug_mutex);
+ }
+
+ buf_start = buf;
+ i = mxt->current_debug_datap;
+
+ while (((buf - buf_start) < (count - 6)) &&
+ (i < mxt->device_info.num_nodes)) {
+
+ mxt->current_debug_datap++;
+ if (debug_command == MXT_CMD_T6_REFERENCES_MODE)
+ buf += sprintf(buf, "%d: %5d\n", i,
+ (u16) le16_to_cpu(data[i]));
+ else if (debug_command == MXT_CMD_T6_DELTAS_MODE)
+ buf += sprintf(buf, "%d: %5d\n", i,
+ (s16) le16_to_cpu(data[i]));
+ i++;
+ }
+
+ return buf - buf_start;
+error:
+ mutex_unlock(&mxt->debug_mutex);
+ return error;
+}
+
+ssize_t deltas_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ return debug_data_read(file->private_data, buf, count, ppos,
+ MXT_CMD_T6_DELTAS_MODE);
+}
+
+ssize_t refs_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ return debug_data_read(file->private_data, buf, count, ppos,
+ MXT_CMD_T6_REFERENCES_MODE);
+}
+
+int debug_data_open(struct inode *inode, struct file *file)
+{
+ struct mxt_data *mxt;
+ int i;
+ mxt = inode->i_private;
+ if (mxt == NULL)
+ return -EIO;
+ mxt->current_debug_datap = 0;
+ mxt->debug_data = kmalloc(mxt->device_info.num_nodes * sizeof(u16),
+ GFP_KERNEL);
+ if (mxt->debug_data == NULL)
+ return -ENOMEM;
+ for (i = 0; i < mxt->device_info.num_nodes; i++)
+ mxt->debug_data[i] = 7777;
+ file->private_data = mxt;
+ return 0;
+}
+
+int debug_data_release(struct inode *inode, struct file *file)
+{
+ struct mxt_data *mxt;
+ mxt = file->private_data;
+ kfree(mxt->debug_data);
+ return 0;
+}
+
+static const struct file_operations delta_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_data_open,
+ .release = debug_data_release,
+ .read = deltas_read,
+};
+
+static const struct file_operations refs_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_data_open,
+ .release = debug_data_release,
+ .read = refs_read,
+};
+
+
+int mxt_memory_open(struct inode *inode, struct file *file)
+{
+ struct mxt_data *mxt;
+ mxt = container_of(inode->i_cdev, struct mxt_data, cdev);
+ if (mxt == NULL)
+ return -EIO;
+ file->private_data = mxt;
+ return 0;
+}
+
+int mxt_message_open(struct inode *inode, struct file *file)
+{
+ struct mxt_data *mxt;
+ mxt = container_of(inode->i_cdev, struct mxt_data, cdev_messages);
+ if (mxt == NULL)
+ return -EIO;
+ file->private_data = mxt;
+ return 0;
+}
+
+ssize_t mxt_memory_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ int i;
+ struct mxt_data *mxt;
+
+ mxt = file->private_data;
+ if (mxt->valid_ap) {
+ mxt_debug(DEBUG_TRACE, "Reading %d bytes from current ap\n",
+ (int) count);
+ i = mxt_read_block_wo_addr(mxt->client, count, (u8 *) buf);
+ } else {
+ mxt_debug(DEBUG_TRACE, "Address pointer changed since set;"
+ "writing AP (%d) before reading %d bytes",
+ mxt->address_pointer, (int) count);
+ i = mxt_read_block(mxt->client, mxt->address_pointer, count,
+ buf);
+ }
+
+ return i;
+}
+
+ssize_t mxt_memory_write(struct file *file, const char *buf, size_t count,
+ loff_t *ppos)
+{
+ int i;
+ int whole_blocks;
+ int last_block_size;
+ struct mxt_data *mxt;
+ u16 address;
+
+ mxt = file->private_data;
+ address = mxt->address_pointer;
+
+ mxt_debug(DEBUG_TRACE, "mxt_memory_write entered\n");
+ whole_blocks = count / I2C_PAYLOAD_SIZE;
+ last_block_size = count % I2C_PAYLOAD_SIZE;
+
+ for (i = 0; i < whole_blocks; i++) {
+ mxt_debug(DEBUG_TRACE, "About to write to %d...",
+ address);
+ mxt_write_block(mxt->client, address, I2C_PAYLOAD_SIZE,
+ (u8 *) buf);
+ address += I2C_PAYLOAD_SIZE;
+ buf += I2C_PAYLOAD_SIZE;
+ }
+
+ mxt_write_block(mxt->client, address, last_block_size, (u8 *) buf);
+
+ return count;
+}
+
+static int mxt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval;
+ struct mxt_data *mxt;
+
+ retval = 0;
+ mxt = file->private_data;
+
+ switch (cmd) {
+ case MXT_SET_ADDRESS_IOCTL:
+ retval = mxt_write_ap(mxt, (u16) arg);
+ if (retval >= 0) {
+ mxt->address_pointer = (u16) arg;
+ mxt->valid_ap = 1;
+ }
+ break;
+ case MXT_RESET_IOCTL:
+ retval = mxt_write_byte(mxt->client,
+ MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+ MXT_ADR_T6_RESET,
+ 1);
+ break;
+ case MXT_CALIBRATE_IOCTL:
+ retval = mxt_write_byte(mxt->client,
+ MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+ MXT_ADR_T6_CALIBRATE,
+ 1);
+
+ break;
+ case MXT_BACKUP_IOCTL:
+ retval = mxt_write_byte(mxt->client,
+ MXT_BASE_ADDR(MXT_GEN_COMMANDPROCESSOR_T6, mxt) +
+ MXT_ADR_T6_BACKUPNV,
+ MXT_CMD_T6_BACKUP);
+ break;
+ case MXT_NONTOUCH_MSG_IOCTL:
+ mxt->nontouch_msg_only = 1;
+ break;
+ case MXT_ALL_MSG_IOCTL:
+ mxt->nontouch_msg_only = 0;
+ break;
+ default:
+ return -EIO;
+ }
+
+ return retval;
+}
+
+/*
+ * Copies messages from buffer to user space.
+ *
+ * NOTE: if less than (mxt->message_size * 5 + 1) bytes requested,
+ * this will return 0!
+ *
+ */
+ssize_t mxt_message_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ int i;
+ struct mxt_data *mxt;
+ char *buf_start;
+
+ mxt = file->private_data;
+ if (mxt == NULL)
+ return -EIO;
+ buf_start = buf;
+
+ mutex_lock(&mxt->msg_mutex);
+ /* Copy messages until buffer empty, or 'count' bytes written */
+ while ((mxt->msg_buffer_startp != mxt->msg_buffer_endp) &&
+ ((buf - buf_start) < (count - (5 * mxt->message_size) - 1))) {
+
+ for (i = 0; i < mxt->message_size; i++) {
+ buf += sprintf(buf, "[%2X] ",
+ *(mxt->messages + mxt->msg_buffer_endp *
+ mxt->message_size + i));
+ }
+ buf += sprintf(buf, "\n");
+ if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE)
+ mxt->msg_buffer_endp++;
+ else
+ mxt->msg_buffer_endp = 0;
+ }
+ mutex_unlock(&mxt->msg_mutex);
+ return buf - buf_start;
+}
+
+static const struct file_operations mxt_message_fops = {
+ .owner = THIS_MODULE,
+ .open = mxt_message_open,
+ .read = mxt_message_read,
+};
+
+static const struct file_operations mxt_memory_fops = {
+ .owner = THIS_MODULE,
+ .open = mxt_memory_open,
+ .read = mxt_memory_read,
+ .write = mxt_memory_write,
+ .unlocked_ioctl = mxt_ioctl,
+};
+
+
+/* Writes the address pointer (to set up following reads). */
+
+int mxt_write_ap(struct mxt_data *mxt, u16 ap)
+{
+ struct i2c_client *client;
+ __le16 le_ap = cpu_to_le16(ap);
+ client = mxt->client;
+ if (mxt != NULL)
+ mxt->last_read_addr = -1;
+ if (i2c_master_send(client, (u8 *) &le_ap, 2) == 2) {
+ mxt_debug(DEBUG_TRACE, "Address pointer set to %d\n", ap);
+ return 0;
+ } else {
+ mxt_debug(DEBUG_INFO, "Error writing address pointer!\n");
+ return -EIO;
+ }
+}
+
+
+
+/* Calculates the 24-bit CRC sum. */
+static u32 CRC_24(u32 crc, u8 byte1, u8 byte2)
+{
+ static const u32 crcpoly = 0x80001B;
+ u32 result;
+ u32 data_word;
+
+ data_word = ((((u16) byte2) << 8u) | byte1);
+ result = ((crc << 1u) ^ data_word);
+ if (result & 0x1000000)
+ result ^= crcpoly;
+ return result;
+}
+
+/* Returns object address in mXT chip, or zero if object is not found */
+static u16 get_object_address(uint8_t object_type,
+ uint8_t instance,
+ struct mxt_object *object_table,
+ int max_objs)
+{
+ uint8_t object_table_index = 0;
+ uint8_t address_found = 0;
+ uint16_t address = 0;
+ struct mxt_object *obj;
+
+ while ((object_table_index < max_objs) && !address_found) {
+ obj = &object_table[object_table_index];
+ if (obj->type == object_type) {
+ address_found = 1;
+ /* Are there enough instances defined in the FW? */
+ if (obj->instances >= instance) {
+ address = obj->chip_addr +
+ (obj->size + 1) * instance;
+ } else {
+ return 0;
+ }
+ }
+ object_table_index++;
+ }
+ return address;
+}
+
+
+/*
+ * Reads a block of bytes from given address from mXT chip. If we are
+ * reading from message window, and previous read was from message window,
+ * there's no need to write the address pointer: the mXT chip will
+ * automatically set the address pointer back to message window start.
+ */
+
+static int mxt_read_block(struct i2c_client *client,
+ u16 addr,
+ u16 length,
+ u8 *value)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct i2c_msg msg[2];
+ __le16 le_addr;
+ struct mxt_data *mxt;
+
+ mxt = i2c_get_clientdata(client);
+
+ if (mxt != NULL) {
+ if ((mxt->last_read_addr == addr) &&
+ (addr == mxt->msg_proc_addr)) {
+ if (i2c_master_recv(client, value, length) == length)
+ return length;
+ else
+ return -EIO;
+ } else {
+ mxt->last_read_addr = addr;
+ }
+ }
+
+ mxt_debug(DEBUG_TRACE, "Writing address pointer & reading %d bytes "
+ "in on i2c transaction...\n", length);
+
+ le_addr = cpu_to_le16(addr);
+ msg[0].addr = client->addr;
+ msg[0].flags = 0x00;
+ msg[0].len = 2;
+ msg[0].buf = (u8 *) &le_addr;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = length;
+ msg[1].buf = (u8 *) value;
+ if (i2c_transfer(adapter, msg, 2) == 2)
+ return length;
+ else
+ return -EIO;
+
+}
+
+/* Reads a block of bytes from current address from mXT chip. */
+
+static int mxt_read_block_wo_addr(struct i2c_client *client,
+ u16 length,
+ u8 *value)
+{
+
+
+ if (i2c_master_recv(client, value, length) == length) {
+ mxt_debug(DEBUG_TRACE, "I2C block read ok\n");
+ return length;
+ } else {
+ mxt_debug(DEBUG_INFO, "I2C block read failed\n");
+ return -EIO;
+ }
+
+}
+
+
+/* Writes one byte to given address in mXT chip. */
+
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value)
+{
+ struct {
+ __le16 le_addr;
+ u8 data;
+
+ } i2c_byte_transfer;
+
+ struct mxt_data *mxt;
+
+ mxt = i2c_get_clientdata(client);
+ if (mxt != NULL)
+ mxt->last_read_addr = -1;
+ i2c_byte_transfer.le_addr = cpu_to_le16(addr);
+ i2c_byte_transfer.data = value;
+ if (i2c_master_send(client, (u8 *) &i2c_byte_transfer, 3) == 3)
+ return 0;
+ else
+ return -EIO;
+}
+
+
+/* Writes a block of bytes (max 256) to given address in mXT chip. */
+static int mxt_write_block(struct i2c_client *client,
+ u16 addr,
+ u16 length,
+ u8 *value)
+{
+ int i;
+ struct {
+ __le16 le_addr;
+ u8 data[256];
+
+ } i2c_block_transfer;
+
+ struct mxt_data *mxt;
+
+ mxt_debug(DEBUG_TRACE, "Writing %d bytes to %d...", length, addr);
+ if (length > 256)
+ return -EINVAL;
+ mxt = i2c_get_clientdata(client);
+ if (mxt != NULL)
+ mxt->last_read_addr = -1;
+ for (i = 0; i < length; i++)
+ i2c_block_transfer.data[i] = *value++;
+ i2c_block_transfer.le_addr = cpu_to_le16(addr);
+ i = i2c_master_send(client, (u8 *) &i2c_block_transfer, length + 2);
+ if (i == (length + 2))
+ return length;
+ else
+ return -EIO;
+}
+
+/* Calculates the CRC value for mXT infoblock. */
+int calculate_infoblock_crc(u32 *crc_result, u8 *data, int crc_area_size)
+{
+ u32 crc = 0;
+ int i;
+
+ for (i = 0; i < (crc_area_size - 1); i = i + 2)
+ crc = CRC_24(crc, *(data + i), *(data + i + 1));
+ /* If uneven size, pad with zero */
+ if (crc_area_size & 0x0001)
+ crc = CRC_24(crc, *(data + i), 0);
+ /* Return only 24 bits of CRC. */
+ *crc_result = (crc & 0x00FFFFFF);
+
+ return 0;
+}
+
+/* Processes a touchscreen message. */
+void process_T9_message(u8 *message, struct mxt_data *mxt, int last_touch)
+{
+
+ struct input_dev *input;
+ u8 status;
+ u16 xpos = 0xFFFF;
+ u16 ypos = 0xFFFF;
+ u8 touch_size = 255;
+ u8 touch_number;
+ u8 amplitude;
+ u8 report_id;
+
+ static int stored_size[MXT_MAX_NUM_TOUCHES];
+ static int stored_x[MXT_MAX_NUM_TOUCHES];
+ static int stored_y[MXT_MAX_NUM_TOUCHES];
+ int i;
+ int active_touches = 0;
+ /*
+ * If the 'last_touch' flag is set, we have received all the touch
+ * messages there are available in this cycle, so send the events for
+ * touches that are active.
+ */
+ if (last_touch) {
+ /*
+ * For compatibility with single-touch systems, send also
+ * ABS_X & ABS_Y & BTN_TOUCH events.
+ */
+ if (stored_size[0]) {
+ input_report_abs(mxt->input, BTN_TOUCH, 1);
+ input_report_abs(mxt->input, ABS_X, stored_x[0]);
+ input_report_abs(mxt->input, ABS_Y, stored_y[0]);
+ } else {
+ input_report_abs(mxt->input, BTN_TOUCH, 0);
+ }
+
+ for (i = 0; i < mxt->numtouch; i++) {
+ if (stored_size[i]) {
+ active_touches++;
+ input_report_abs(mxt->input,
+ ABS_MT_TRACKING_ID,
+ i);
+ input_report_abs(mxt->input,
+ ABS_MT_TOUCH_MAJOR,
+ stored_size[i]);
+ input_report_abs(mxt->input,
+ ABS_MT_POSITION_X,
+ stored_x[i]);
+ input_report_abs(mxt->input,
+ ABS_MT_POSITION_Y,
+ stored_y[i]);
+ input_mt_sync(mxt->input);
+ }
+ }
+ if (active_touches == 0)
+ input_mt_sync(mxt->input);
+ input_sync(mxt->input);
+ } else {
+ input = mxt->input;
+ status = message[MXT_MSG_T9_STATUS];
+ report_id = message[0];
+
+ if (status & MXT_MSGB_T9_SUPPRESS) {
+ /* Touch has been suppressed by grip/face */
+ /* detection */
+ mxt_debug(DEBUG_TRACE, "SUPRESS");
+ } else {
+ /* Put together the 10-/12-bit coordinate values. */
+ xpos = message[MXT_MSG_T9_XPOSMSB] * 16 +
+ ((message[MXT_MSG_T9_XYPOSLSB] >> 4) & 0xF);
+ ypos = message[MXT_MSG_T9_YPOSMSB] * 16 +
+ ((message[MXT_MSG_T9_XYPOSLSB] >> 0) & 0xF);
+
+ if (mxt->max_x_val < 1024)
+ xpos >>= 2;
+ if (mxt->max_y_val < 1024)
+ ypos >>= 2;
+
+ touch_number = message[MXT_MSG_REPORTID] -
+ mxt->rid_map[report_id].first_rid;
+
+ stored_x[touch_number] = xpos;
+ stored_y[touch_number] = ypos;
+
+ if (status & MXT_MSGB_T9_DETECT) {
+ /*
+ * mXT224 reports the number of touched nodes,
+ * so the exact value for touch ellipse major
+ * axis length in nodes would be
+ * 2*sqrt(touch_size/pi)
+ * (assuming round touch shape), which would
+ * then need to be scaled using information
+ * about how many sensor lines we do have. So
+ * this is very much simplified,
+ * but sufficient for most if not all apps?
+ */
+ touch_size = message[MXT_MSG_T9_TCHAREA];
+ touch_size = touch_size >> 2;
+ if (!touch_size)
+ touch_size = 1;
+ stored_size[touch_number] = touch_size;
+ if (status & MXT_MSGB_T9_AMP)
+ /* Amplitude of touch has changed */
+ amplitude =
+ message[MXT_MSG_T9_TCHAMPLITUDE];
+ }
+
+ if (status & MXT_MSGB_T9_RELEASE) {
+ /* The previously reported touch has been
+ * removed. */
+ stored_size[touch_number] = 0;
+ }
+ }
+
+ if (status & MXT_MSGB_T9_SUPPRESS) {
+ mxt_debug(DEBUG_TRACE, "SUPRESS");
+ } else {
+ if (status & MXT_MSGB_T9_DETECT) {
+ mxt_debug(DEBUG_TRACE, "DETECT:%s%s%s%s",
+ ((status & MXT_MSGB_T9_PRESS) ?
+ " PRESS" : ""),
+ ((status & MXT_MSGB_T9_MOVE) ?
+ " MOVE" : ""),
+ ((status & MXT_MSGB_T9_AMP) ?
+ " AMP" : ""),
+ ((status & MXT_MSGB_T9_VECTOR) ?
+ " VECT" : ""));
+ } else if (status & MXT_MSGB_T9_RELEASE) {
+ mxt_debug(DEBUG_TRACE, "RELEASE");
+ }
+ }
+ mxt_debug(DEBUG_TRACE, "X=%d, Y=%d, TOUCHSIZE=%d",
+ xpos, ypos, touch_size);
+ }
+ return;
+}
+
+
+int process_message(u8 *message, u8 object, struct mxt_data *mxt)
+{
+ struct i2c_client *client;
+ u8 status;
+ u16 xpos = 0xFFFF;
+ u16 ypos = 0xFFFF;
+ u8 event;
+ u8 direction;
+ u16 distance;
+ u8 length;
+ u8 report_id;
+ static u8 error_cond;
+
+ client = mxt->client;
+ length = mxt->message_size;
+ report_id = message[0];
+
+ if ((mxt->nontouch_msg_only == 0) ||
+ (!IS_TOUCH_OBJECT(object))) {
+ mutex_lock(&mxt->msg_mutex);
+ /* Copy the message to buffer */
+ if (mxt->msg_buffer_startp < MXT_MESSAGE_BUFFER_SIZE)
+ mxt->msg_buffer_startp++;
+ else
+ mxt->msg_buffer_startp = 0;
+ if (mxt->msg_buffer_startp == mxt->msg_buffer_endp) {
+ mxt_debug(DEBUG_TRACE,
+ "Message buf full, discarding last entry.\n");
+ if (mxt->msg_buffer_endp < MXT_MESSAGE_BUFFER_SIZE)
+ mxt->msg_buffer_endp++;
+ else
+ mxt->msg_buffer_endp = 0;
+ }
+ memcpy((mxt->messages + mxt->msg_buffer_startp * length),
+ message,
+ length);
+ mutex_unlock(&mxt->msg_mutex);
+ }
+
+ switch (object) {
+ case MXT_GEN_COMMANDPROCESSOR_T6:
+ status = message[1];
+
+ if (status & MXT_MSGB_T6_COMSERR) {
+ if (!(error_cond & MXT_MSGB_T6_COMSERR)) {
+ dev_err(&client->dev,
+ "maXTouch checksum error\n");
+ error_cond |= MXT_MSGB_T6_COMSERR;
+ }
+ }
+ if (status & MXT_MSGB_T6_CFGERR) {
+ /*
+ * Configuration error. A proper configuration
+ * needs to be written to chip and backed up.
+ */
+ if (!(error_cond & MXT_MSGB_T6_CFGERR)) {
+ dev_err(&client->dev,
+ "maXTouch configuration error\n");
+ error_cond |= MXT_MSGB_T6_CFGERR;
+ }
+ }
+ if (status & MXT_MSGB_T6_CAL) {
+ /* Calibration in action, no need to react */
+ dev_info(&client->dev,
+ "maXTouch calibration in progress\n");
+ }
+ if (status & MXT_MSGB_T6_SIGERR) {
+ /*
+ * Signal acquisition error, something is seriously
+ * wrong, not much we can in the driver to correct
+ * this
+ */
+ if (!(error_cond & MXT_MSGB_T6_SIGERR)) {
+ dev_err(&client->dev,
+ "maXTouch acquisition error\n");
+ error_cond |= MXT_MSGB_T6_SIGERR;
+ }
+ }
+ if (status & MXT_MSGB_T6_OFL) {
+ /*
+ * Cycle overflow, the acquisition interval is too
+ * short.
+ */
+ dev_err(&client->dev,
+ "maXTouch cycle overflow\n");
+ }
+ if (status & MXT_MSGB_T6_RESET) {
+ /* Chip has reseted, no need to react. */
+ dev_info(&client->dev,
+ "maXTouch chip reset\n");
+ }
+ if (status == 0) {
+ /* Chip status back to normal. */
+ dev_info(&client->dev,
+ "maXTouch status normal\n");
+ error_cond = 0;
+ }
+ break;
+
+ case MXT_TOUCH_MULTITOUCHSCREEN_T9:
+ process_T9_message(message, mxt, 0);
+ break;
+
+ case MXT_SPT_GPIOPWM_T19:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving GPIO message\n");
+ break;
+
+ case MXT_PROCI_GRIPFACESUPPRESSION_T20:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving face suppression msg\n");
+ break;
+
+ case MXT_PROCG_NOISESUPPRESSION_T22:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving noise suppression msg\n");
+ status = message[MXT_MSG_T22_STATUS];
+ if (status & MXT_MSGB_T22_FHCHG) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "maXTouch: Freq changed\n");
+ }
+ if (status & MXT_MSGB_T22_GCAFERR) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "maXTouch: High noise "
+ "level\n");
+ }
+ if (status & MXT_MSGB_T22_FHERR) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "maXTouch: Freq changed - "
+ "Noise level too high\n");
+ }
+ break;
+
+ case MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving one-touch gesture msg\n");
+
+ event = message[MXT_MSG_T24_STATUS] & 0x0F;
+ xpos = message[MXT_MSG_T24_XPOSMSB] * 16 +
+ ((message[MXT_MSG_T24_XYPOSLSB] >> 4) & 0x0F);
+ ypos = message[MXT_MSG_T24_YPOSMSB] * 16 +
+ ((message[MXT_MSG_T24_XYPOSLSB] >> 0) & 0x0F);
+ if (mxt->max_x_val < 1024)
+ xpos >>= 2;
+ if (mxt->max_y_val < 1024)
+ ypos >>= 2;
+ direction = message[MXT_MSG_T24_DIR];
+ distance = message[MXT_MSG_T24_DIST] +
+ (message[MXT_MSG_T24_DIST + 1] << 16);
+
+ report_gesture((event << 24) | (direction << 16) | distance,
+ mxt);
+ report_gesture((xpos << 16) | ypos, mxt);
+
+ break;
+
+ case MXT_SPT_SELFTEST_T25:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving Self-Test msg\n");
+
+ if (message[MXT_MSG_T25_STATUS] == MXT_MSGR_T25_OK) {
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "maXTouch: Self-Test OK\n");
+
+ } else {
+ dev_err(&client->dev,
+ "maXTouch: Self-Test Failed [%02x]:"
+ "{%02x,%02x,%02x,%02x,%02x}\n",
+ message[MXT_MSG_T25_STATUS],
+ message[MXT_MSG_T25_STATUS + 0],
+ message[MXT_MSG_T25_STATUS + 1],
+ message[MXT_MSG_T25_STATUS + 2],
+ message[MXT_MSG_T25_STATUS + 3],
+ message[MXT_MSG_T25_STATUS + 4]
+ );
+ }
+ break;
+
+ case MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving 2-touch gesture message\n");
+
+ event = message[MXT_MSG_T27_STATUS] & 0xF0;
+ xpos = message[MXT_MSG_T27_XPOSMSB] * 16 +
+ ((message[MXT_MSG_T27_XYPOSLSB] >> 4) & 0x0F);
+ ypos = message[MXT_MSG_T27_YPOSMSB] * 16 +
+ ((message[MXT_MSG_T27_XYPOSLSB] >> 0) & 0x0F);
+ if (mxt->max_x_val < 1024)
+ xpos >>= 2;
+ if (mxt->max_y_val < 1024)
+ ypos >>= 2;
+
+ direction = message[MXT_MSG_T27_ANGLE];
+ distance = message[MXT_MSG_T27_SEPARATION] +
+ (message[MXT_MSG_T27_SEPARATION + 1] << 16);
+
+ report_gesture((event << 24) | (direction << 16) | distance,
+ mxt);
+ report_gesture((xpos << 16) | ypos, mxt);
+
+
+ break;
+
+ case MXT_SPT_CTECONFIG_T28:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "Receiving CTE message...\n");
+ status = message[MXT_MSG_T28_STATUS];
+ if (status & MXT_MSGB_T28_CHKERR)
+ dev_err(&client->dev,
+ "maXTouch: Power-Up CRC failure\n");
+
+ break;
+ default:
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev,
+ "maXTouch: Unknown message!\n");
+
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Processes messages when the interrupt line (CHG) is asserted. Keeps
+ * reading messages until a message with report ID 0xFF is received,
+ * which indicates that there is no more new messages.
+ *
+ */
+
+static void mxt_worker(struct work_struct *work)
+{
+ struct mxt_data *mxt;
+ struct i2c_client *client;
+
+ u8 *message;
+ u16 message_length;
+ u16 message_addr;
+ u8 report_id;
+ u8 object;
+ int error;
+ int i;
+ char *message_string;
+ char *message_start;
+
+ message = NULL;
+ mxt = container_of(work, struct mxt_data, dwork.work);
+ disable_irq(mxt->irq);
+ client = mxt->client;
+ message_addr = mxt->msg_proc_addr;
+ message_length = mxt->message_size;
+
+ if (message_length < 256) {
+ message = kmalloc(message_length, GFP_KERNEL);
+ if (message == NULL) {
+ dev_err(&client->dev, "Error allocating memory\n");
+ return;
+ }
+ } else {
+ dev_err(&client->dev,
+ "Message length larger than 256 bytes not supported\n");
+ return;
+ }
+
+ mxt_debug("maXTouch worker active:\n");
+
+ do {
+ /* Read next message, reread on failure. */
+ /* This driver currently does not request messages with CRC
+ * byte, so we can read one byte less than message_length. */
+ mxt->message_counter++;
+ for (i = 1; i < I2C_RETRY_COUNT; i++) {
+ error = mxt_read_block(client,
+ message_addr,
+ message_length - 1,
+ message);
+ if (error >= 0)
+ break;
+ mxt->read_fail_counter++;
+ dev_err(&client->dev,
+ "Failure reading maxTouch device\n");
+ }
+ if (error < 0) {
+ kfree(message);
+ return;
+ }
+
+ if (mxt->address_pointer != message_addr)
+ mxt->valid_ap = 0;
+ report_id = message[0];
+
+ if (debug >= DEBUG_RAW) {
+ mxt_debug(DEBUG_RAW, "%s message [msg count: %08x]:",
+ REPORT_ID_TO_OBJECT_NAME(report_id, mxt),
+ mxt->message_counter
+ );
+ /* 5 characters per one byte */
+ message_string = kmalloc(message_length * 5,
+ GFP_KERNEL);
+ if (message_string == NULL) {
+ dev_err(&client->dev,
+ "Error allocating memory\n");
+ kfree(message);
+ return;
+ }
+ message_start = message_string;
+ for (i = 0; i < message_length; i++) {
+ message_string +=
+ sprintf(message_string,
+ "0x%02X ", message[i]);
+ }
+ mxt_debug(DEBUG_RAW, "%s", message_start);
+ kfree(message_start);
+ }
+
+ if ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)) {
+ memcpy(mxt->last_message, message, message_length);
+ mxt->new_msgs = 1;
+ smp_wmb();
+ /* Get type of object and process the message */
+ object = mxt->rid_map[report_id].object;
+ process_message(message, object, mxt);
+ }
+
+ mxt_debug(DEBUG_TRACE, "chgline: %d\n", mxt->read_chg());
+ } while (comms ? (mxt->read_chg() == 0) :
+ ((report_id != MXT_END_OF_MESSAGES) && (report_id != 0)));
+
+ /* All messages processed, send the events) */
+ process_T9_message(NULL, mxt, 1);
+
+ kfree(message);
+ enable_irq(mxt->irq);
+ /* Make sure we just didn't miss a interrupt. */
+ if (mxt->read_chg() == 0)
+ schedule_delayed_work(&mxt->dwork, 0);
+}
+
+
+/*
+ * The maXTouch device will signal the host about a new message by asserting
+ * the CHG line. This ISR schedules a worker routine to read the message when
+ * that happens.
+ */
+
+static irqreturn_t mxt_irq_handler(int irq, void *_mxt)
+{
+ struct mxt_data *mxt = _mxt;
+
+ mxt->irq_counter++;
+ if (mxt->valid_interrupt()) {
+ /* Send the signal only if falling edge generated the irq. */
+ cancel_delayed_work(&mxt->dwork);
+ schedule_delayed_work(&mxt->dwork, 0);
+ mxt->valid_irq_counter++;
+ } else {
+ mxt->invalid_irq_counter++;
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+
+/******************************************************************************/
+/* Initialization of driver */
+/******************************************************************************/
+
+static int __devinit mxt_identify(struct i2c_client *client,
+ struct mxt_data *mxt,
+ u8 *id_block_data)
+{
+ u8 buf[MXT_ID_BLOCK_SIZE];
+ int error;
+ int identified;
+
+ identified = 0;
+
+ /* Read Device info to check if chip is valid */
+ error = mxt_read_block(client, MXT_ADDR_INFO_BLOCK, MXT_ID_BLOCK_SIZE,
+ (u8 *) buf);
+
+ if (error < 0) {
+ mxt->read_fail_counter++;
+ dev_err(&client->dev, "Failure accessing maXTouch device\n");
+ return -EIO;
+ }
+
+ memcpy(id_block_data, buf, MXT_ID_BLOCK_SIZE);
+
+ mxt->device_info.family_id = buf[0];
+ mxt->device_info.variant_id = buf[1];
+ mxt->device_info.major = ((buf[2] >> 4) & 0x0F);
+ mxt->device_info.minor = (buf[2] & 0x0F);
+ mxt->device_info.build = buf[3];
+ mxt->device_info.x_size = buf[4];
+ mxt->device_info.y_size = buf[5];
+ mxt->device_info.num_objs = buf[6];
+ mxt->device_info.num_nodes = mxt->device_info.x_size *
+ mxt->device_info.y_size;
+
+ /*
+ * Check Family & Variant Info; warn if not recognized but
+ * still continue.
+ */
+
+ /* MXT224 */
+ if (mxt->device_info.family_id == MXT224_FAMILYID) {
+ strcpy(mxt->device_info.family_name, "mXT224");
+
+ if (mxt->device_info.variant_id == MXT224_CAL_VARIANTID) {
+ strcpy(mxt->device_info.variant_name, "Calibrated");
+ } else if (mxt->device_info.variant_id ==
+ MXT224_UNCAL_VARIANTID) {
+ strcpy(mxt->device_info.variant_name, "Uncalibrated");
+ } else {
+ dev_err(&client->dev,
+ "Warning: maXTouch Variant ID [%d] not "
+ "supported\n",
+ mxt->device_info.variant_id);
+ strcpy(mxt->device_info.variant_name, "UNKNOWN");
+ /* identified = -ENXIO; */
+ }
+
+ /* MXT1386 */
+ } else if (mxt->device_info.family_id == MXT1386_FAMILYID) {
+ strcpy(mxt->device_info.family_name, "mXT1386");
+
+ if (mxt->device_info.variant_id == MXT1386_CAL_VARIANTID) {
+ strcpy(mxt->device_info.variant_name, "Calibrated");
+ } else {
+ dev_err(&client->dev,
+ "Warning: maXTouch Variant ID [%d] not "
+ "supported\n",
+ mxt->device_info.variant_id);
+ strcpy(mxt->device_info.variant_name, "UNKNOWN");
+ /* identified = -ENXIO; */
+ }
+ /* Unknown family ID! */
+ } else {
+ dev_err(&client->dev,
+ "Warning: maXTouch Family ID [%d] not supported\n",
+ mxt->device_info.family_id);
+ strcpy(mxt->device_info.family_name, "UNKNOWN");
+ strcpy(mxt->device_info.variant_name, "UNKNOWN");
+ /* identified = -ENXIO; */
+ }
+
+ dev_info(
+ &client->dev,
+ "Atmel maXTouch (Family %s (%X), Variant %s (%X)) Firmware "
+ "version [%d.%d] Build %d\n",
+ mxt->device_info.family_name,
+ mxt->device_info.family_id,
+ mxt->device_info.variant_name,
+ mxt->device_info.variant_id,
+ mxt->device_info.major,
+ mxt->device_info.minor,
+ mxt->device_info.build
+ );
+ dev_info(
+ &client->dev,
+ "Atmel maXTouch Configuration "
+ "[X: %d] x [Y: %d]\n",
+ mxt->device_info.x_size,
+ mxt->device_info.y_size
+ );
+ return identified;
+}
+
+/*
+ * Reads the object table from maXTouch chip to get object data like
+ * address, size, report id. For Info Block CRC calculation, already read
+ * id data is passed to this function too (Info Block consists of the ID
+ * block and object table).
+ *
+ */
+static int __devinit mxt_read_object_table(struct i2c_client *client,
+ struct mxt_data *mxt,
+ u8 *raw_id_data)
+{
+ u16 report_id_count;
+ u8 buf[MXT_OBJECT_TABLE_ELEMENT_SIZE];
+ u8 *raw_ib_data;
+ u8 object_type;
+ u16 object_address;
+ u16 object_size;
+ u8 object_instances;
+ u8 object_report_ids;
+ u16 object_info_address;
+ u32 crc;
+ u32 calculated_crc;
+ int i;
+ int error;
+
+ u8 object_instance;
+ u8 object_report_id;
+ u8 report_id;
+ int first_report_id;
+ int ib_pointer;
+ struct mxt_object *object_table;
+
+ mxt_debug(DEBUG_TRACE, "maXTouch driver reading configuration\n");
+
+ object_table = kzalloc(sizeof(struct mxt_object) *
+ mxt->device_info.num_objs,
+ GFP_KERNEL);
+ if (object_table == NULL) {
+ printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+ error = -ENOMEM;
+ goto err_object_table_alloc;
+ }
+
+ raw_ib_data = kmalloc(MXT_OBJECT_TABLE_ELEMENT_SIZE *
+ mxt->device_info.num_objs + MXT_ID_BLOCK_SIZE,
+ GFP_KERNEL);
+ if (raw_ib_data == NULL) {
+ printk(KERN_WARNING "maXTouch: Memory allocation failed!\n");
+ error = -ENOMEM;
+ goto err_ib_alloc;
+ }
+
+ /* Copy the ID data for CRC calculation. */
+ memcpy(raw_ib_data, raw_id_data, MXT_ID_BLOCK_SIZE);
+ ib_pointer = MXT_ID_BLOCK_SIZE;
+
+ mxt->object_table = object_table;
+
+ mxt_debug(DEBUG_TRACE, "maXTouch driver Memory allocated\n");
+
+ object_info_address = MXT_ADDR_OBJECT_TABLE;
+
+ report_id_count = 0;
+ for (i = 0; i < mxt->device_info.num_objs; i++) {
+ mxt_debug(DEBUG_TRACE, "Reading maXTouch at [0x%04x]: ",
+ object_info_address);
+
+ error = mxt_read_block(client, object_info_address,
+ MXT_OBJECT_TABLE_ELEMENT_SIZE, buf);
+
+ if (error < 0) {
+ mxt->read_fail_counter++;
+ dev_err(&client->dev,
+ "maXTouch Object %d could not be read\n", i);
+ error = -EIO;
+ goto err_object_read;
+ }
+
+ memcpy(raw_ib_data + ib_pointer, buf,
+ MXT_OBJECT_TABLE_ELEMENT_SIZE);
+ ib_pointer += MXT_OBJECT_TABLE_ELEMENT_SIZE;
+
+ object_type = buf[0];
+ object_address = (buf[2] << 8) + buf[1];
+ object_size = buf[3] + 1;
+ object_instances = buf[4] + 1;
+ object_report_ids = buf[5];
+ mxt_debug(DEBUG_TRACE, "Type=%03d, Address=0x%04x, "
+ "Size=0x%02x, %d instances, %d report id's\n",
+ object_type,
+ object_address,
+ object_size,
+ object_instances,
+ object_report_ids
+ );
+
+ /* TODO: check whether object is known and supported? */
+ /* Save frequently needed info. */
+ if (object_type == MXT_GEN_MESSAGEPROCESSOR_T5) {
+ mxt->msg_proc_addr = object_address;
+ mxt->message_size = object_size;
+ }
+
+ object_table[i].type = object_type;
+ object_table[i].chip_addr = object_address;
+ object_table[i].size = object_size;
+ object_table[i].instances = object_instances;
+ object_table[i].num_report_ids = object_report_ids;
+ report_id_count += object_instances * object_report_ids;
+
+ object_info_address += MXT_OBJECT_TABLE_ELEMENT_SIZE;
+ }
+
+ mxt->rid_map =
+ kzalloc(sizeof(struct report_id_map) * (report_id_count + 1),
+ /* allocate for report_id 0, even if not used */
+ GFP_KERNEL);
+ if (mxt->rid_map == NULL) {
+ printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+ error = -ENOMEM;
+ goto err_rid_map_alloc;
+ }
+
+ mxt->messages = kzalloc(mxt->message_size * MXT_MESSAGE_BUFFER_SIZE,
+ GFP_KERNEL);
+ if (mxt->messages == NULL) {
+ printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+ error = -ENOMEM;
+ goto err_msg_alloc;
+ }
+
+ mxt->last_message = kzalloc(mxt->message_size, GFP_KERNEL);
+ if (mxt->last_message == NULL) {
+ printk(KERN_WARNING "maXTouch: Can't allocate memory!\n");
+ error = -ENOMEM;
+ goto err_msg_alloc;
+ }
+
+ mxt->report_id_count = report_id_count;
+ if (report_id_count > 254) { /* 0 & 255 are reserved */
+ dev_err(&client->dev,
+ "Too many maXTouch report id's [%d]\n",
+ report_id_count);
+ error = -ENXIO;
+ goto err_max_rid;
+ }
+
+ /* Create a mapping from report id to object type */
+ report_id = 1; /* Start from 1, 0 is reserved. */
+
+ /* Create table associating report id's with objects & instances */
+ for (i = 0; i < mxt->device_info.num_objs; i++) {
+ for (object_instance = 0;
+ object_instance < object_table[i].instances;
+ object_instance++){
+ first_report_id = report_id;
+ for (object_report_id = 0;
+ object_report_id < object_table[i].num_report_ids;
+ object_report_id++) {
+ mxt->rid_map[report_id].object =
+ object_table[i].type;
+ mxt->rid_map[report_id].instance =
+ object_instance;
+ mxt->rid_map[report_id].first_rid =
+ first_report_id;
+ report_id++;
+ }
+ }
+ }
+
+ /* Read 3 byte CRC */
+ error = mxt_read_block(client, object_info_address, 3, buf);
+ if (error < 0) {
+ mxt->read_fail_counter++;
+ dev_err(&client->dev, "Error reading CRC\n");
+ }
+
+ crc = (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ if (calculate_infoblock_crc(&calculated_crc, raw_ib_data,
+ ib_pointer)) {
+ printk(KERN_WARNING "Error while calculating CRC!\n");
+ calculated_crc = 0;
+ }
+ kfree(raw_ib_data);
+
+ mxt_debug(DEBUG_TRACE, "\nReported info block CRC = 0x%6X\n", crc);
+ mxt_debug(DEBUG_TRACE, "Calculated info block CRC = 0x%6X\n\n",
+ calculated_crc);
+
+ if (crc == calculated_crc) {
+ mxt->info_block_crc = crc;
+ } else {
+ mxt->info_block_crc = 0;
+ printk(KERN_ALERT "maXTouch: Info block CRC invalid!\n");
+ }
+
+ if (debug >= DEBUG_VERBOSE) {
+
+ dev_info(&client->dev, "maXTouch: %d Objects\n",
+ mxt->device_info.num_objs);
+
+ for (i = 0; i < mxt->device_info.num_objs; i++) {
+ dev_info(&client->dev, "Type:\t\t\t[%d]: %s\n",
+ object_table[i].type,
+ object_type_name[object_table[i].type]);
+ dev_info(&client->dev, "\tAddress:\t0x%04X\n",
+ object_table[i].chip_addr);
+ dev_info(&client->dev, "\tSize:\t\t%d Bytes\n",
+ object_table[i].size);
+ dev_info(&client->dev, "\tInstances:\t%d\n",
+ object_table[i].instances);
+ dev_info(&client->dev, "\tReport Id's:\t%d\n",
+ object_table[i].num_report_ids);
+ }
+ }
+
+ return 0;
+
+
+err_max_rid:
+ kfree(mxt->last_message);
+err_msg_alloc:
+ kfree(mxt->rid_map);
+err_rid_map_alloc:
+err_object_read:
+ kfree(raw_ib_data);
+err_ib_alloc:
+ kfree(object_table);
+err_object_table_alloc:
+ return error;
+}
+
+static int __devinit mxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxt_data *mxt;
+ struct mxt_platform_data *pdata;
+ struct input_dev *input;
+ u8 *id_data;
+ int error;
+
+ mxt_debug(DEBUG_INFO, "mXT224: mxt_probe\n");
+
+ if (client == NULL) {
+ pr_debug("maXTouch: client == NULL\n");
+ return -EINVAL;
+ } else if (client->adapter == NULL) {
+ pr_debug("maXTouch: client->adapter == NULL\n");
+ return -EINVAL;
+ } else if (&client->dev == NULL) {
+ pr_debug("maXTouch: client->dev == NULL\n");
+ return -EINVAL;
+ } else if (&client->adapter->dev == NULL) {
+ pr_debug("maXTouch: client->adapter->dev == NULL\n");
+ return -EINVAL;
+ } else if (id == NULL) {
+ pr_debug("maXTouch: id == NULL\n");
+ return -EINVAL;
+ }
+
+ mxt_debug(DEBUG_INFO, "maXTouch driver v. %s\n", DRIVER_VERSION);
+ mxt_debug(DEBUG_INFO, "\t \"%s\"\n", client->name);
+ mxt_debug(DEBUG_INFO, "\taddr:\t0x%04x\n", client->addr);
+ mxt_debug(DEBUG_INFO, "\tirq:\t%d\n", client->irq);
+ mxt_debug(DEBUG_INFO, "\tflags:\t0x%04x\n", client->flags);
+ mxt_debug(DEBUG_INFO, "\tadapter:\"%s\"\n", client->adapter->name);
+ mxt_debug(DEBUG_INFO, "\tdevice:\t\"%s\"\n", client->dev.init_name);
+
+ mxt_debug(DEBUG_TRACE, "maXTouch driver functionality OK\n");
+
+ /* Allocate structure - we need it to identify device */
+ mxt = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+ if (mxt == NULL) {
+ dev_err(&client->dev, "insufficient memory\n");
+ error = -ENOMEM;
+ goto err_mxt_alloc;
+ }
+
+ id_data = kmalloc(MXT_ID_BLOCK_SIZE, GFP_KERNEL);
+ if (id_data == NULL) {
+ dev_err(&client->dev, "insufficient memory\n");
+ error = -ENOMEM;
+ goto err_id_alloc;
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(&client->dev, "error allocating input device\n");
+ error = -ENOMEM;
+ goto err_input_dev_alloc;
+ }
+
+ /* Initialize Platform data */
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL) {
+ dev_err(&client->dev, "platform data is required!\n");
+ error = -EINVAL;
+ goto err_pdata;
+ }
+ if (debug >= DEBUG_TRACE)
+ printk(KERN_INFO "Platform OK: pdata = 0x%08x\n",
+ (unsigned int) pdata);
+
+ mxt->read_fail_counter = 0;
+ mxt->message_counter = 0;
+ mxt->max_x_val = pdata->max_x;
+ mxt->max_y_val = pdata->max_y;
+ mxt->numtouch = pdata->numtouch;
+
+ /* Get data that is defined in board specific code. */
+ mxt->init_hw = pdata->init_platform_hw;
+ mxt->exit_hw = pdata->exit_platform_hw;
+ mxt->read_chg = pdata->read_chg;
+
+ if (pdata->valid_interrupt != NULL)
+ mxt->valid_interrupt = pdata->valid_interrupt;
+ else
+ mxt->valid_interrupt = mxt_valid_interrupt_dummy;
+
+ if (mxt->init_hw != NULL)
+ mxt->init_hw();
+
+ if (debug >= DEBUG_TRACE)
+ printk(KERN_INFO "maXTouch driver identifying chip\n");
+
+ if (mxt_identify(client, mxt, id_data) < 0) {
+ dev_err(&client->dev, "Chip could not be identified\n");
+ error = -ENODEV;
+ goto err_identify;
+ }
+ /* Chip is valid and active. */
+ if (debug >= DEBUG_TRACE)
+ printk(KERN_INFO "maXTouch driver allocating input device\n");
+
+ mxt->client = client;
+ mxt->input = input;
+
+ INIT_DELAYED_WORK(&mxt->dwork, mxt_worker);
+ mutex_init(&mxt->debug_mutex);
+ mutex_init(&mxt->msg_mutex);
+ mxt_debug(DEBUG_TRACE, "maXTouch driver creating device name\n");
+
+ snprintf(
+ mxt->phys_name,
+ sizeof(mxt->phys_name),
+ "%s/input0",
+ dev_name(&client->dev)
+ );
+ input->name = "Atmel maXTouch Touchscreen controller";
+ input->phys = mxt->phys_name;
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+
+ mxt_debug(DEBUG_INFO, "maXTouch name: \"%s\"\n", input->name);
+ mxt_debug(DEBUG_INFO, "maXTouch phys: \"%s\"\n", input->phys);
+ mxt_debug(DEBUG_INFO, "maXTouch driver setting abs parameters\n");
+
+ set_bit(BTN_TOUCH, input->keybit);
+
+ /* Single touch */
+ input_set_abs_params(input, ABS_X, 0, mxt->max_x_val, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, mxt->max_y_val, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, MXT_MAX_REPORTED_PRESSURE,
+ 0, 0);
+ input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MXT_MAX_REPORTED_WIDTH,
+ 0, 0);
+
+ /* Multitouch */
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, mxt->max_x_val, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, mxt->max_y_val, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MXT_MAX_TOUCH_SIZE,
+ 0, 0);
+ input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, MXT_MAX_NUM_TOUCHES,
+ 0, 0);
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_SYN, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_MSC, input->evbit);
+ input->mscbit[0] = BIT_MASK(MSC_GESTURE);
+
+ mxt_debug(DEBUG_TRACE, "maXTouch driver setting client data\n");
+ i2c_set_clientdata(client, mxt);
+ mxt_debug(DEBUG_TRACE, "maXTouch driver setting drv data\n");
+ input_set_drvdata(input, mxt);
+ mxt_debug(DEBUG_TRACE, "maXTouch driver input register device\n");
+ error = input_register_device(mxt->input);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "Failed to register input device\n");
+ goto err_register_device;
+ }
+
+ error = mxt_read_object_table(client, mxt, id_data);
+ if (error < 0)
+ goto err_read_ot;
+
+
+ /* Create debugfs entries. */
+ mxt->debug_dir = debugfs_create_dir("maXTouch", NULL);
+ if (mxt->debug_dir == -ENODEV) {
+ /* debugfs is not enabled. */
+ printk(KERN_WARNING "debugfs not enabled in kernel\n");
+ } else if (mxt->debug_dir == NULL) {
+ printk(KERN_WARNING "error creating debugfs dir\n");
+ } else {
+ mxt_debug(DEBUG_TRACE, "created \"maXTouch\" debugfs dir\n");
+ debugfs_create_file("deltas", S_IRUSR, mxt->debug_dir, mxt,
+ &delta_fops);
+ debugfs_create_file("refs", S_IRUSR, mxt->debug_dir, mxt,
+ &refs_fops);
+ }
+
+ /* Create character device nodes for reading & writing registers */
+ mxt->mxt_class = class_create(THIS_MODULE, "maXTouch_memory");
+ if (IS_ERR(mxt->mxt_class)) {
+ printk(KERN_WARNING "class create failed! exiting...");
+ goto err_read_ot;
+ }
+ /* 2 numbers; one for memory and one for messages */
+ error = alloc_chrdev_region(&mxt->dev_num, 0, 2,
+ "maXTouch_memory");
+ mxt_debug(DEBUG_VERBOSE,
+ "device number %d allocated!\n", MAJOR(mxt->dev_num));
+ if (error)
+ printk(KERN_WARNING "Error registering device\n");
+
+ cdev_init(&mxt->cdev, &mxt_memory_fops);
+ cdev_init(&mxt->cdev_messages, &mxt_message_fops);
+
+ mxt_debug(DEBUG_VERBOSE, "cdev initialized\n");
+ mxt->cdev.owner = THIS_MODULE;
+ mxt->cdev_messages.owner = THIS_MODULE;
+
+ error = cdev_add(&mxt->cdev, mxt->dev_num, 1);
+ if (error)
+ printk(KERN_WARNING "Bad cdev\n");
+
+ error = cdev_add(&mxt->cdev_messages, mxt->dev_num + 1, 1);
+ if (error)
+ printk(KERN_WARNING "Bad cdev\n");
+
+ mxt_debug(DEBUG_VERBOSE, "cdev added\n");
+
+ device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 0), NULL,
+ "maXTouch");
+
+ device_create(mxt->mxt_class, NULL, MKDEV(MAJOR(mxt->dev_num), 1), NULL,
+ "maXTouch_messages");
+
+ mxt->msg_buffer_startp = 0;
+ mxt->msg_buffer_endp = 0;
+
+ /* Allocate the interrupt */
+ mxt_debug(DEBUG_TRACE, "maXTouch driver allocating interrupt...\n");
+ mxt->irq = client->irq;
+ mxt->valid_irq_counter = 0;
+ mxt->invalid_irq_counter = 0;
+ mxt->irq_counter = 0;
+ if (mxt->irq) {
+ /* Try to request IRQ with falling edge first. This is
+ * not always supported. If it fails, try with any edge. */
+ error = request_irq(mxt->irq,
+ mxt_irq_handler,
+ IRQF_TRIGGER_FALLING,
+ client->dev.driver->name,
+ mxt);
+ if (error < 0) {
+ /* TODO: why only 0 works on STK1000? */
+ error = request_irq(mxt->irq,
+ mxt_irq_handler,
+ 0,
+ client->dev.driver->name,
+ mxt);
+ }
+
+ if (error < 0) {
+ dev_err(&client->dev,
+ "failed to allocate irq %d\n", mxt->irq);
+ goto err_irq;
+ }
+ }
+
+ if (debug > DEBUG_INFO)
+ dev_info(&client->dev, "touchscreen, irq %d\n", mxt->irq);
+
+ /* Schedule a worker routine to read any messages that might have
+ * been sent before interrupts were enabled. */
+ cancel_delayed_work(&mxt->dwork);
+ schedule_delayed_work(&mxt->dwork, 0);
+ kfree(id_data);
+
+ return 0;
+
+
+err_irq:
+ kfree(mxt->rid_map);
+ kfree(mxt->object_table);
+ kfree(mxt->last_message);
+err_read_ot:
+err_register_device:
+err_identify:
+err_pdata:
+ input_free_device(input);
+err_input_dev_alloc:
+ kfree(id_data);
+err_id_alloc:
+ if (mxt->exit_hw != NULL)
+ mxt->exit_hw();
+ kfree(mxt);
+err_mxt_alloc:
+ return error;
+}
+
+static int __devexit mxt_remove(struct i2c_client *client)
+{
+ struct mxt_data *mxt;
+
+ mxt = i2c_get_clientdata(client);
+
+ /* Remove debug dir entries */
+ debugfs_remove_recursive(mxt->debug_dir);
+
+ if (mxt != NULL) {
+ if (mxt->exit_hw != NULL)
+ mxt->exit_hw();
+ if (mxt->irq)
+ free_irq(mxt->irq, mxt);
+ unregister_chrdev_region(mxt->dev_num, 2);
+ device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 0));
+ device_destroy(mxt->mxt_class, MKDEV(MAJOR(mxt->dev_num), 1));
+ cdev_del(&mxt->cdev);
+ cdev_del(&mxt->cdev_messages);
+ cancel_delayed_work_sync(&mxt->dwork);
+ input_unregister_device(mxt->input);
+ class_destroy(mxt->mxt_class);
+ debugfs_remove(mxt->debug_dir);
+
+ kfree(mxt->rid_map);
+ kfree(mxt->object_table);
+ kfree(mxt->last_message);
+ }
+ kfree(mxt);
+
+ i2c_set_clientdata(client, NULL);
+ if (debug >= DEBUG_TRACE)
+ dev_info(&client->dev, "Touchscreen unregistered\n");
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+/*
+ * Allow the touchscreen to resume the system from sleep.
+ * Alternatively we could put the maXTouch to deep sleep mode and use
+ * something else to resume.
+ *
+ * TODO: set ACQINT to some high value, and restore to original at resume?
+ */
+
+static int mxt_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct mxt_data *mxt = i2c_get_clientdata(client);
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(mxt->irq);
+ return 0;
+}
+
+static int mxt_resume(struct i2c_client *client)
+{
+ struct mxt_data *mxt = i2c_get_clientdata(client);
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(mxt->irq);
+ return 0;
+}
+#else
+#define mxt_suspend NULL
+#define mxt_resume NULL
+#endif
+
+static const struct i2c_device_id mxt_idtable[] = {
+ {"maXTouch", 0,},
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, mxt_idtable);
+
+static struct i2c_driver mxt_driver = {
+ .driver = {
+ .name = "maXTouch",
+ .owner = THIS_MODULE,
+ },
+
+ .id_table = mxt_idtable,
+ .probe = mxt_probe,
+ .remove = __devexit_p(mxt_remove),
+ .suspend = mxt_suspend,
+ .resume = mxt_resume,
+
+};
+
+static int __init mxt_init(void)
+{
+ int err;
+ err = i2c_add_driver(&mxt_driver);
+ if (err)
+ printk(KERN_WARNING "Adding maXTouch driver failed "
+ "(errno = %d)\n", err);
+ else
+ mxt_debug(DEBUG_TRACE, "Successfully added driver %s\n",
+ mxt_driver.driver.name);
+ return err;
+}
+
+static void __exit mxt_cleanup(void)
+{
+ i2c_del_driver(&mxt_driver);
+}
+
+
+module_init(mxt_init);
+module_exit(mxt_cleanup);
+
+MODULE_AUTHOR("Iiro Valkonen");
+MODULE_DESCRIPTION("Driver for Atmel maXTouch Touchscreen Controller");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/atmel_maxtouch.h b/include/linux/atmel_maxtouch.h
new file mode 100644
index 0000000..0f2904e
--- /dev/null
+++ b/include/linux/atmel_maxtouch.h
@@ -0,0 +1,310 @@
+/*
+ * Atmel maXTouch header file
+ *
+ * Copyright (c) 2010 Atmel Corporation
+ * Copyright (C) 2010 Ulf Samuelsson (ulf@atmel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or 3 as
+ * published by the Free Software Foundation.
+ * See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#define MXT224_I2C_ADDR1 0x4A
+#define MXT224_I2C_ADDR2 0x4B
+#define MXT1386_I2C_ADDR1 0x4C
+#define MXT1386_I2C_ADDR2 0x4D
+#define MXT1386_I2C_ADDR3 0x5A
+#define MXT1386_I2C_ADDR4 0x5B
+
+/*
+ * Select this address from above depending on what maXTouch
+ * chip you have and how it's address pins are configured;
+ * see datasheet.
+ */
+
+#define MXT_I2C_ADDRESS MXT1386_I2C_ADDR1
+
+#define MXT224_FAMILYID 0x80
+#define MXT1386_FAMILYID 0xA0
+
+#define MXT224_CAL_VARIANTID 0x01
+#define MXT224_UNCAL_VARIANTID 0x00
+#define MXT1386_CAL_VARIANTID 0x00
+
+#define MXT_MAX_REPORTED_WIDTH 255
+#define MXT_MAX_REPORTED_PRESSURE 255
+#define MXT_MAX_TOUCH_SIZE 255
+#define MXT_MAX_NUM_TOUCHES 10
+
+/* Fixed addresses inside maXTouch device */
+#define MXT_ADDR_INFO_BLOCK 0
+#define MXT_ADDR_OBJECT_TABLE 7
+#define MXT_ID_BLOCK_SIZE 7
+#define MXT_OBJECT_TABLE_ELEMENT_SIZE 6
+
+/* Object types */
+#define MXT_DEBUG_DELTAS_T2 2
+#define MXT_DEBUG_REFERENCES_T3 3
+#define MXT_GEN_MESSAGEPROCESSOR_T5 5
+#define MXT_GEN_COMMANDPROCESSOR_T6 6
+#define MXT_GEN_POWERCONFIG_T7 7
+#define MXT_GEN_ACQUIRECONFIG_T8 8
+#define MXT_TOUCH_MULTITOUCHSCREEN_T9 9
+#define MXT_TOUCH_SINGLETOUCHSCREEN_T10 10
+#define MXT_TOUCH_XSLIDER_T11 11
+#define MXT_TOUCH_YSLIDER_T12 12
+#define MXT_TOUCH_XWHEEL_T13 13
+#define MXT_TOUCH_YWHEEL_T14 14
+#define MXT_TOUCH_KEYARRAY_T15 15
+#define MXT_SPT_GPIOPWM_T19 19
+#define MXT_PROCI_GRIPFACESUPPRESSION_T20 20
+#define MXT_PROCG_NOISESUPPRESSION_T22 22
+#define MXT_TOUCH_PROXIMITY_T23 23
+#define MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24 24
+#define MXT_SPT_SELFTEST_T25 25
+#define MXT_DEBUG_CTERANGE_T26 26
+#define MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27 27
+#define MXT_SPT_CTECONFIG_T28 28
+#define MXT_TOUCH_KEYSET_T31 31
+#define MXT_TOUCH_XSLIDERSET_T32 32
+#define MXT_DEBUG_DIAGNOSTIC_T37 37
+#define MXT_USER_INFO_T38 38
+
+
+/*
+ * If a message is read from mXT when there's no new messages available,
+ * the report ID of the message will be 0xFF.
+ */
+#define MXT_END_OF_MESSAGES 0xFF
+
+
+/* GEN_COMMANDPROCESSOR_T6 Register offsets from T6 base address */
+#define MXT_ADR_T6_RESET 0x00
+#define MXT_ADR_T6_BACKUPNV 0x01
+#define MXT_ADR_T6_CALIBRATE 0x02
+#define MXT_ADR_T6_REPORTALL 0x03
+#define MXT_ADR_T6_RESERVED 0x04
+#define MXT_ADR_T6_DIAGNOSTIC 0x05
+
+/* T6 Debug Diagnostics Commands */
+#define MXT_CMD_T6_PAGE_UP 0x01
+#define MXT_CMD_T6_PAGE_DOWN 0x02
+#define MXT_CMD_T6_DELTAS_MODE 0x10
+#define MXT_CMD_T6_REFERENCES_MODE 0x11
+#define MXT_CMD_T6_CTE_MODE 0x31
+
+/* T6 Backup Command */
+#define MXT_CMD_T6_BACKUP 0x55
+
+/* SPT_DEBUG_DIAGNOSTIC_T37 Register offsets from T37 base address */
+#define MXT_ADR_T37_PAGE 0x01
+#define MXT_ADR_T37_DATA 0x02
+
+
+
+/************************************************************************
+ * MESSAGE OBJECTS ADDRESS FIELDS
+ *
+ ************************************************************************/
+#define MXT_MSG_REPORTID 0x00
+
+
+/* MXT_GEN_MESSAGEPROCESSOR_T5 Message address definitions */
+#define MXT_MSG_T5_REPORTID 0x00
+#define MXT_MSG_T5_MESSAGE 0x01
+#define MXT_MSG_T5_CHECKSUM 0x08
+
+/* MXT_GEN_COMMANDPROCESSOR_T6 Message address definitions */
+#define MXT_MSG_T6_STATUS 0x01
+#define MXT_MSGB_T6_COMSERR 0x04
+#define MXT_MSGB_T6_CFGERR 0x08
+#define MXT_MSGB_T6_CAL 0x10
+#define MXT_MSGB_T6_SIGERR 0x20
+#define MXT_MSGB_T6_OFL 0x40
+#define MXT_MSGB_T6_RESET 0x80
+/* Three bytes */
+#define MXT_MSG_T6_CHECKSUM 0x02
+
+/* MXT_GEN_POWERCONFIG_T7 NO Message address definitions */
+/* MXT_GEN_ACQUIRECONFIG_T8 Message address definitions */
+/* MXT_TOUCH_MULTITOUCHSCREEN_T9 Message address definitions */
+
+#define MXT_MSG_T9_STATUS 0x01
+/* Status bit field */
+#define MXT_MSGB_T9_SUPPRESS 0x02
+#define MXT_MSGB_T9_AMP 0x04
+#define MXT_MSGB_T9_VECTOR 0x08
+#define MXT_MSGB_T9_MOVE 0x10
+#define MXT_MSGB_T9_RELEASE 0x20
+#define MXT_MSGB_T9_PRESS 0x40
+#define MXT_MSGB_T9_DETECT 0x80
+
+#define MXT_MSG_T9_XPOSMSB 0x02
+#define MXT_MSG_T9_YPOSMSB 0x03
+#define MXT_MSG_T9_XYPOSLSB 0x04
+#define MXT_MSG_T9_TCHAREA 0x05
+#define MXT_MSG_T9_TCHAMPLITUDE 0x06
+#define MXT_MSG_T9_TCHVECTOR 0x07
+
+
+/* MXT_SPT_GPIOPWM_T19 Message address definitions */
+#define MXT_MSG_T19_STATUS 0x01
+
+/* MXT_PROCI_GRIPFACESUPPRESSION_T20 Message address definitions */
+#define MXT_MSG_T20_STATUS 0x01
+#define MXT_MSGB_T20_FACE_SUPPRESS 0x01
+/* MXT_PROCG_NOISESUPPRESSION_T22 Message address definitions */
+#define MXT_MSG_T22_STATUS 0x01
+#define MXT_MSGB_T22_FHCHG 0x01
+#define MXT_MSGB_T22_GCAFERR 0x04
+#define MXT_MSGB_T22_FHERR 0x08
+#define MXT_MSG_T22_GCAFDEPTH 0x02
+
+/* MXT_TOUCH_PROXIMITY_T23 Message address definitions */
+#define MXT_MSG_T23_STATUS 0x01
+#define MXT_MSGB_T23_FALL 0x20
+#define MXT_MSGB_T23_RISE 0x40
+#define MXT_MSGB_T23_DETECT 0x80
+/* 16 bit */
+#define MXT_MSG_T23_PROXDELTA 0x02
+
+/* MXT_PROCI_ONETOUCHGESTUREPROCESSOR_T24 Message address definitions */
+#define MXT_MSG_T24_STATUS 0x01
+#define MXT_MSG_T24_XPOSMSB 0x02
+#define MXT_MSG_T24_YPOSMSB 0x03
+#define MXT_MSG_T24_XYPOSLSB 0x04
+#define MXT_MSG_T24_DIR 0x05
+/* 16 bit */
+#define MXT_MSG_T24_DIST 0x06
+
+/* MXT_SPT_SELFTEST_T25 Message address definitions */
+#define MXT_MSG_T25_STATUS 0x01
+/* 5 Bytes */
+#define MXT_MSGR_T25_OK 0xFE
+#define MXT_MSGR_T25_INVALID_TEST 0xFD
+#define MXT_MSGR_T25_PIN_FAULT 0x11
+#define MXT_MSGR_T25_SIGNAL_LIMIT_FAULT 0x17
+#define MXT_MSGR_T25_GAIN_ERROR 0x20
+#define MXT_MSG_T25_INFO 0x02
+
+/* MXT_PROCI_TWOTOUCHGESTUREPROCESSOR_T27 Message address definitions */
+#define MXT_MSG_T27_STATUS 0x01
+#define MXT_MSGB_T27_ROTATEDIR 0x10
+#define MXT_MSGB_T27_PINCH 0x20
+#define MXT_MSGB_T27_ROTATE 0x40
+#define MXT_MSGB_T27_STRETCH 0x80
+#define MXT_MSG_T27_XPOSMSB 0x02
+#define MXT_MSG_T27_YPOSMSB 0x03
+#define MXT_MSG_T27_XYPOSLSB 0x04
+#define MXT_MSG_T27_ANGLE 0x05
+
+/* 16 bit */
+#define MXT_MSG_T27_SEPARATION 0x06
+
+/* MXT_SPT_CTECONFIG_T28 Message address definitions */
+#define MXT_MSG_T28_STATUS 0x01
+#define MXT_MSGB_T28_CHKERR 0x01
+
+
+/* One Touch Events */
+#define MXT_GESTURE_RESERVED 0x00
+#define MXT_GESTURE_PRESS 0x01
+#define MXT_GESTURE_RELEASE 0x02
+#define MXT_GESTURE_TAP 0x03
+#define MXT_GESTURE_DOUBLE_TAP 0x04
+#define MXT_GESTURE_FLICK 0x05
+#define MXT_GESTURE_DRAG 0x06
+#define MXT_GESTURE_SHORT_PRESS 0x07
+#define MXT_GESTURE_LONG_PRESS 0x08
+#define MXT_GESTURE_REPEAT_PRESS 0x09
+#define MXT_GESTURE_TAP_AND_PRESS 0x0a
+#define MXT_GESTURE_THROW 0x0b
+
+/* Two-touch events */
+#define MXT_GESTURE_STRETCH (1 << 7)
+#define MXT_GESTURE_ROTATE (1 << 6)
+#define MXT_GESTURE_PINCH (1 << 5)
+#define MXT_GESTURE_ROTATEDIR (1 << 4)
+
+
+
+/* Bootloader states */
+#define WAITING_BOOTLOAD_COMMAND 0xC0
+#define WAITING_FRAME_DATA 0x80
+#define APP_CRC_FAIL 0x40
+#define FRAME_CRC_CHECK 0x02
+#define FRAME_CRC_PASS 0x04
+#define FRAME_CRC_FAIL 0x03
+
+#define MXT_MAX_FRAME_SIZE 276
+
+/* Debug levels */
+#define DEBUG_INFO 1
+#define DEBUG_VERBOSE 2
+#define DEBUG_MESSAGES 5
+#define DEBUG_RAW 8
+#define DEBUG_TRACE 10
+
+/* IOCTL commands */
+#define MXT_SET_ADDRESS_IOCTL _IOW('x', 0x01, int) /* Sets the internal address pointer */
+#define MXT_RESET_IOCTL _IO('x', 0x02) /* Resets the device */
+#define MXT_CALIBRATE_IOCTL _IO('x', 0x03) /* Calibrates the device */
+/* Backups the current state of registers to NVM */
+#define MXT_BACKUP_IOCTL _IO('x', 0x04)
+/*
+ * Only non-touch messages can be read from the message buffer
+ * (/dev/maXTouch_messages)
+ */
+#define MXT_NONTOUCH_MSG_IOCTL _IO('x', 0x05)
+/* All messages can be read from the message buffer */
+#define MXT_ALL_MSG_IOCTL _IO('x', 0x06)
+
+/* Message buffer size. This is a ring buffer, and when full, the oldest entry
+ will be overwritten. */
+#define MXT_MESSAGE_BUFFER_SIZE 128
+
+/* Routines for memory access within a 16 bit address space */
+
+static int mxt_read_block(struct i2c_client *client, u16 addr, u16 length,
+ u8 *value);
+static int mxt_write_byte(struct i2c_client *client, u16 addr, u8 value);
+static int mxt_write_block(struct i2c_client *client, u16 addr, u16 length,
+ u8 *value);
+
+
+/**
+ * struct mxt_platform_data - includes platform specific informatio
+ * related to Atmel maXTouch touchscreen controller.
+ *
+ * @numtouch: Number of simultaneous touches supported
+ * @init_platform_hw(): Initialization function, which can for example
+ * trigger a hardware reset by toggling a GPIO pin
+ * @exit_platform_hw(): Function to run when the driver is unloaded.
+ * @valid_interrupt(): Function that checks the validity of the interrupt -
+ * function that check the validity of a interrupt (by
+ * reading the changeline interrupt pin and checking that
+ * it really is low for example).
+ * @max_x: Reported X range
+ * @max_y: Reported Y range
+ */
+
+struct mxt_platform_data {
+ u8 numtouch; /* Number of touches to report */
+ void (*init_platform_hw)(void);
+ void (*exit_platform_hw)(void);
+ int max_x; /* The default reported X range */
+ int max_y; /* The default reported Y range */
+ u8 (*valid_interrupt) (void);
+ u8 (*read_chg) (void);
+};
+
+
+static u8 mxt_valid_interrupt_dummy(void)
+{
+ return 1;
+}
+
+void mxt_hw_reset(void);
--
1.7.0.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
2010-11-12 14:14 [PATCH] input: Add Atmel maXTouch touchscreen driver Iiro Valkonen
@ 2010-11-12 20:10 ` Dmitry Torokhov
2010-11-17 10:41 ` Iiro Valkonen
0 siblings, 1 reply; 6+ messages in thread
From: Dmitry Torokhov @ 2010-11-12 20:10 UTC (permalink / raw)
To: Iiro Valkonen; +Cc: kyungmin.park, jy0922.shim, linux-input
Hi Iiro,
On Fri, Nov 12, 2010 at 04:14:18PM +0200, Iiro Valkonen wrote:
>
> Add Atmel maXTouch touchscreen driver (I2C interface).
> The MT slots protocol will be implemented later.
>
I believe there is already a driver for this hardware in the tree
(qt602240.c). Could you tell me what additional features your driver
implements and why can't they be added to the existing driver.
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
2010-11-12 20:10 ` Dmitry Torokhov
@ 2010-11-17 10:41 ` Iiro Valkonen
2010-11-17 14:04 ` Murphy, Dan
0 siblings, 1 reply; 6+ messages in thread
From: Iiro Valkonen @ 2010-11-17 10:41 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: kyungmin.park, jy0922.shim, linux-input
On 11/12/2010 10:10 PM, Dmitry Torokhov wrote:
> Hi Iiro,
>
> I believe there is already a driver for this hardware in the tree
> (qt602240.c). Could you tell me what additional features your driver
> implements and why can't they be added to the existing driver.
>
Hi Dmitry,
the current driver is written for one maXTouch chip, mXT224. The driver that I submitted will support all current maXTouch chips, and it is built to be future-proof so it should work with yet to be released chips too, with minor or no changes. This shows in the naming too, part name "qt602240" is deprecated and it shouldn't be used. As the driver should work with all chips in the maXTouch family, a more generic naming convention in the driver name itself and all the code inside the driver would be more appropriate.
Atmel would like to be the maintainer of this driver, and it would be easiest if we could use the code I submitted. Of course we would be happy to implement any improvements like the threaded IRQs.
Best Regards,
--
Iiro Valkonen
Application Engineer / Atmel Corporation
Tel: +358(9)45208250 / Mobile: +358(40)5820012
iiro.valkonen@atmel.com / www.atmel.com
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
2010-11-17 10:41 ` Iiro Valkonen
@ 2010-11-17 14:04 ` Murphy, Dan
2010-11-17 18:23 ` Dmitry Torokhov
0 siblings, 1 reply; 6+ messages in thread
From: Murphy, Dan @ 2010-11-17 14:04 UTC (permalink / raw)
To: Iiro Valkonen; +Cc: Dmitry Torokhov, kyungmin.park, jy0922.shim, linux-input
Hi Dmitry
On Wed, Nov 17, 2010 at 4:41 AM, Iiro Valkonen <iiro.valkonen@atmel.com> wrote:
> On 11/12/2010 10:10 PM, Dmitry Torokhov wrote:
> Hi Dmitry,
>
> the current driver is written for one maXTouch chip, mXT224. The driver that I submitted will support all current >maXTouch chips, and it is built to be future-proof so it should work with yet to be released chips too, with minor or >no changes. This shows in the naming too, part name "qt602240" is deprecated and it shouldn't be used. As the >driver should work with all chips in the maXTouch family, a more generic naming convention in the driver name itself >and all the code inside the driver would be more appropriate.
Having written this same driver for this IC I agree with Iiro that we
should have one driver that would support multiple IC firmware
revisions. Once we had the base driver completed adding functionality
to the driver was a snap.
Can these two drivers co-exist and let the developer decide which driver to use?
Of course the Atmel driver needs to be scrubbed for problems.
Now. I also strongly agree with Dmitry that the driver you are
submitting is IMHO really rough and needs work. Without even
reviewing the complete functionality I have many concerns about the
code.
For instance the header file spacing is really crazy. I am not sure
if the spacing was intended but it is really hard to follow. In the
source it seems like there is more debug code then functional working
code. The driver should be updated to use the request_threaded_irq
call and not the request_irq then there is no need for scheduling work
queues. I can go on and on.... But I won't do a complete review on
this driver unless Dmitry indicates that he will merge it once
complete.
>
> Atmel would like to be the maintainer of this driver, and it would be easiest if we could use the code I submitted. Of course we would be happy to implement any improvements like the threaded IRQs.
I would prefer Atmel be the maintainer as they can expedite future
changes into the driver prior to releasing the support firmware.
Dan
------------------------------------------------------------------------------------
Dan Murphy
Texas Instruments
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
2010-11-17 14:04 ` Murphy, Dan
@ 2010-11-17 18:23 ` Dmitry Torokhov
0 siblings, 0 replies; 6+ messages in thread
From: Dmitry Torokhov @ 2010-11-17 18:23 UTC (permalink / raw)
To: Murphy, Dan; +Cc: Iiro Valkonen, kyungmin.park, jy0922.shim, linux-input
On Wed, Nov 17, 2010 at 08:04:32AM -0600, Murphy, Dan wrote:
> Hi Dmitry
>
> On Wed, Nov 17, 2010 at 4:41 AM, Iiro Valkonen <iiro.valkonen@atmel.com> wrote:
> > On 11/12/2010 10:10 PM, Dmitry Torokhov wrote:
> > Hi Dmitry,
> >
> > the current driver is written for one maXTouch chip, mXT224. The
> > driver that I submitted will support all current >maXTouch chips,
> > and it is built to be future-proof so it should work with yet to be
> > released chips too, with minor or no changes. This shows in the
> > naming too, part name "qt602240" is deprecated and it shouldn't be
> > used. As the driver should work with all chips in the maXTouch
> > family, a more generic naming convention in the driver name itself
> > and all the code inside the driver would be more appropriate.
The name of the driver is not important and can easily be changed, just
send me a patch.
>
> Having written this same driver for this IC I agree with Iiro that we
> should have one driver that would support multiple IC firmware
> revisions. Once we had the base driver completed adding functionality
> to the driver was a snap.
>
> Can these two drivers co-exist and let the developer decide which driver to use?
> Of course the Atmel driver needs to be scrubbed for problems.
No, that would cause duplicate effort. Just witness patches form Intel
submitted for the mainline driver a few days ago.
Development process in Linux kernel is rarely revolutionary but rather
evolutionary. That is why I asked Iiro to enumerate the changes needed
for qt602240 to support entire line of maXTouch chips.
>
> Now. I also strongly agree with Dmitry that the driver you are
> submitting is IMHO really rough and needs work. Without even
> reviewing the complete functionality I have many concerns about the
> code.
>
> For instance the header file spacing is really crazy. I am not sure
> if the spacing was intended but it is really hard to follow. In the
> source it seems like there is more debug code then functional working
> code. The driver should be updated to use the request_threaded_irq
> call and not the request_irq then there is no need for scheduling work
> queues. I can go on and on.... But I won't do a complete review on
> this driver unless Dmitry indicates that he will merge it once
> complete.
Like I said, I will not take the driver wholesale but I will take it
piecemeal, feature by feature transforming qt602240.
BTW, what is up with needing 2 custom character devices?
>
> >
> > Atmel would like to be the maintainer of this driver, and it would
> > be easiest if we could use the code I submitted. Of course we would
> > be happy to implement any improvements like the threaded IRQs.
>
> I would prefer Atmel be the maintainer as they can expedite future
> changes into the driver prior to releasing the support firmware.
>
I will be delighted if Atmel takes over the driver, as long as they
follow the standard kernel practices.
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 6+ messages in thread
[parent not found: <4CE2F692.8040403@atmel.com>]
* Re: [PATCH] input: Add Atmel maXTouch touchscreen driver
[not found] <4CE2F692.8040403@atmel.com>
@ 2010-11-16 22:05 ` Dmitry Torokhov
0 siblings, 0 replies; 6+ messages in thread
From: Dmitry Torokhov @ 2010-11-16 22:05 UTC (permalink / raw)
To: Ulf Samuelsson; +Cc: linux-input, iiro.valkonen
Hi Ulf,
On Tue, Nov 16, 2010 at 10:24:34PM +0100, Ulf Samuelsson wrote:
> Hi Iiro,
>
> On Fri, Nov 12, 2010 at 04:14:18PM +0200, Iiro Valkonen wrote:
> >/ /
> >/ Add Atmel maXTouch touchscreen driver (I2C interface)./
> >/ The MT slots protocol will be implemented later./
> >/ /
>
> I believe there is already a driver for this hardware in the tree
> (qt602240.c). Could you tell me what additional features your driver
> implements and why can't they be added to the existing driver.
>
> Thanks.
>
> --
> Dmitry
>
>
>
> =========
> When we reviewed the Samsung QT602240 driver internally,
> we found 20 significant problems including memory leaks
> and doing I2C transfers within interrupts.
> I have been told that since I2C transfers can sleep, this is not a
> good idea.
It does so in _threaded_ interrupt which is perfectly fine. The need to
access slower buses from the interrupt context was one of the reasons
this facility was added to the kernel.
> Configuration is done inside the driver, instead of using platform
> data. etc. etc.
The platform data is defined in include/linux/i2c/qt602240_ts.h and can
be extended if needed.
The driver passed both mutitouch protocol review process and the general
review and I believe most (all?) memory leaks and other issues presented
in the original version have been fixed.
>
> This is why I started writing a new driver and Iiro has added significant
> improvements/rewrites and is the current maintainer of the maXTouch
> driver within Atmel - the manufacturer of the device in question.
>
> Have not checked if the qt602240.c still does memory leaks,
> but it still seems to make I2C transfers in the interrupt.
>
> My suggestion is that we deprecate the qt602240.c and remove
> it in some future version.
> Instead we should use the official driver from Atmel, that Iiro sent
> to the list.
No. I regret to say but I find the in-tree version of qt602240.c in much
better shape than what was submitted by Iiro. Please identify the
improvements that are needed for the driver and submit separate patches
implementing these improvements.
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-11-17 18:23 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-12 14:14 [PATCH] input: Add Atmel maXTouch touchscreen driver Iiro Valkonen
2010-11-12 20:10 ` Dmitry Torokhov
2010-11-17 10:41 ` Iiro Valkonen
2010-11-17 14:04 ` Murphy, Dan
2010-11-17 18:23 ` Dmitry Torokhov
[not found] <4CE2F692.8040403@atmel.com>
2010-11-16 22:05 ` Dmitry Torokhov
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).