input: Add Quantum OBP touchscreen support
authorDima Zavin <dima@android.com>
Thu, 23 Apr 2009 23:06:51 +0000 (16:06 -0700)
committerColin Cross <ccross@android.com>
Wed, 6 Oct 2010 23:32:51 +0000 (16:32 -0700)
This adds a driver for the Quantum capacitive touch controllers
that use the Object Based Protocol (OBP) for communication
between the driver and the on-chip firmware.

Change-Id: Ib469d626d5f0d11562547f2cbeb71a48e11f6072
Cc: Dan Murphy <D.Murphy@motorola.com>
Cc: Mike Chan <mike@android.com>
Cc: Paul Eastham <eastham@google.com>
Signed-off-by: Rebecca Schultz Zavin <rebecca@android.com>
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/qtouch_obp_ts.c [new file with mode: 0644]
include/linux/qtouch_obp_ts.h [new file with mode: 0644]

index d50fd5603604007baf63c47d97561dfc16ea6cad..a390cbad3049427a907dc2553893574ce34116ea 100644 (file)
@@ -657,4 +657,13 @@ config TOUCHSCREEN_STMPE
          To compile this driver as a module, choose M here: the
          module will be called stmpe-ts.
 
+config TOUCHSCREEN_QUANTUM_OBP
+       tristate "Quantum OBP based touchscreens"
+       depends on I2C
+       help
+         Say Y here if you have a Quantum touchscreen that uses
+         the Object Based Protocol based firmware.
+
+         If unsure, say N.
+
 endif
index 93e641bc320e69bcfed60d6769e9abc7fb468d3b..ed2c714805e3f903efa4623f3793cf016d0390e3 100644 (file)
@@ -36,6 +36,7 @@ obj-$(CONFIG_TOUCHSCREEN_PANJIT_I2C)  += panjit_i2c.o
 obj-$(CONFIG_TOUCHSCREEN_PCAP)         += pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)     += penmount.o
 obj-$(CONFIG_TOUCHSCREEN_QT602240)     += qt602240_ts.o
+obj-$(CONFIG_TOUCHSCREEN_QUANTUM_OBP)  += qtouch_obp_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)      += s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)                += stmpe-ts.o
 obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI)    += synaptics_i2c_rmi.o
diff --git a/drivers/input/touchscreen/qtouch_obp_ts.c b/drivers/input/touchscreen/qtouch_obp_ts.c
new file mode 100644 (file)
index 0000000..77a49f7
--- /dev/null
@@ -0,0 +1,1165 @@
+/*
+ * drivers/input/touchscreen/qtouch_obp_ts.c - driver for Quantum touch IC
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009 Motorola, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Derived from the Motorola OBP touch driver.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/earlysuspend.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/qtouch_obp_ts.h>
+
+#define IGNORE_CHECKSUM_MISMATCH
+
+struct qtm_object {
+       struct qtm_obj_entry            entry;
+       uint8_t                         report_id_min;
+       uint8_t                         report_id_max;
+};
+
+struct axis_map {
+       int     key;
+       int     x;
+       int     y;
+};
+
+struct coordinate_map {
+       int x_data;
+       int y_data;
+       int z_data;
+       int w_data;
+       int down;
+};
+
+#define _BITMAP_LEN                    BITS_TO_LONGS(QTM_OBP_MAX_OBJECT_NUM)
+struct qtouch_ts_data {
+       struct i2c_client               *client;
+       struct input_dev                *input_dev;
+       struct work_struct              init_work;
+       struct work_struct              work;
+       struct qtouch_ts_platform_data  *pdata;
+       struct coordinate_map           finger_data[10];
+       struct early_suspend            early_suspend;
+
+       struct qtm_object               obj_tbl[QTM_OBP_MAX_OBJECT_NUM];
+       unsigned long                   obj_map[_BITMAP_LEN];
+
+       uint32_t                        last_keystate;
+       uint16_t                        eeprom_checksum;
+       uint8_t                     checksum_cnt;
+       int                                     x_delta;
+       int                                     y_delta;
+
+       /* Note: The message buffer is reused for reading different messages.
+        * MUST enforce that there is no concurrent access to msg_buf. */
+       uint8_t                         *msg_buf;
+       int                             msg_size;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void qtouch_ts_early_suspend(struct early_suspend *handler);
+static void qtouch_ts_late_resume(struct early_suspend *handler);
+#endif
+
+static struct workqueue_struct *qtouch_ts_wq;
+
+static uint32_t qtouch_tsdebug;
+module_param_named(tsdebug, qtouch_tsdebug, uint, 0664);
+
+static irqreturn_t qtouch_ts_irq_handler(int irq, void *dev_id)
+{
+       struct qtouch_ts_data *ts = dev_id;
+
+       disable_irq_nosync(ts->client->irq);
+       queue_work(qtouch_ts_wq, &ts->work);
+       return IRQ_HANDLED;
+}
+
+static int qtouch_write(struct qtouch_ts_data *ts, void *buf, int buf_sz)
+{
+       int retries = 10;
+       int ret;
+
+       do {
+               ret = i2c_master_send(ts->client, (char *)buf, buf_sz);
+       } while ((ret < buf_sz) && (--retries > 0));
+
+       if (ret < 0)
+               pr_info("%s: Error while trying to write %d bytes\n", __func__,
+                       buf_sz);
+       else if (ret != buf_sz) {
+               pr_info("%s: Write %d bytes, expected %d\n", __func__,
+                       ret, buf_sz);
+               ret = -EIO;
+       }
+       return ret;
+}
+
+static int qtouch_set_addr(struct qtouch_ts_data *ts, uint16_t addr)
+{
+       int ret;
+
+       /* Note: addr on the wire is LSB first */
+       ret = qtouch_write(ts, (char *)&addr, sizeof(uint16_t));
+       if (ret < 0)
+               pr_info("%s: Can't send obp addr 0x%4x\n", __func__, addr);
+
+       return ret >= 0 ? 0 : ret;
+}
+
+static int qtouch_read(struct qtouch_ts_data *ts, void *buf, int buf_sz)
+{
+       int retries = 10;
+       int ret;
+
+       do {
+               memset(buf, 0, buf_sz);
+               ret = i2c_master_recv(ts->client, (char *)buf, buf_sz);
+       } while ((ret < 0) && (--retries > 0));
+
+       if (ret < 0)
+               pr_info("%s: Error while trying to read %d bytes\n", __func__,
+                       buf_sz);
+       else if (ret != buf_sz) {
+               pr_info("%s: Read %d bytes, expected %d\n", __func__,
+                       ret, buf_sz);
+               ret = -EIO;
+       }
+
+       return ret >= 0 ? 0 : ret;
+}
+
+static int qtouch_read_addr(struct qtouch_ts_data *ts, uint16_t addr,
+                           void *buf, int buf_sz)
+{
+       int ret;
+
+       ret = qtouch_set_addr(ts, addr);
+       if (ret != 0)
+               return ret;
+
+       return qtouch_read(ts, buf, buf_sz);
+}
+
+static struct qtm_obj_message *qtouch_read_msg(struct qtouch_ts_data *ts)
+{
+       int ret;
+
+       ret = qtouch_read(ts, ts->msg_buf, ts->msg_size);
+       if (!ret)
+               return (struct qtm_obj_message *)ts->msg_buf;
+       return NULL;
+}
+
+static int qtouch_write_addr(struct qtouch_ts_data *ts, uint16_t addr,
+                            void *buf, int buf_sz)
+{
+       int ret;
+       uint8_t write_buf[128];
+
+       if (buf_sz + sizeof(uint16_t) > sizeof(write_buf)) {
+               pr_err("%s: Buffer too large (%d)\n", __func__, buf_sz);
+               return -EINVAL;
+       }
+
+       memcpy(write_buf, (void *)&addr, sizeof(addr));
+       memcpy((void *)write_buf + sizeof(addr), buf, buf_sz);
+
+       ret = qtouch_write(ts, write_buf, buf_sz + sizeof(addr));
+
+       if (ret < 0) {
+               pr_err("%s: Could not write %d bytes.\n", __func__, buf_sz);
+               return ret;
+       }
+
+       return 0;
+}
+
+static uint16_t calc_csum(uint16_t curr_sum, void *_buf, int buf_sz)
+{
+       uint8_t *buf = _buf;
+       uint32_t new_sum;
+       int i;
+
+       while (buf_sz-- > 0) {
+               new_sum = (((uint32_t) curr_sum) << 8) | *(buf++);
+               for (i = 0; i < 8; ++i) {
+                       if (new_sum & 0x800000)
+                               new_sum ^= 0x800500;
+                       new_sum <<= 1;
+               }
+               curr_sum = ((uint32_t) new_sum >> 8) & 0xffff;
+       }
+
+       return curr_sum;
+}
+
+static inline struct qtm_object *find_obj(struct qtouch_ts_data *ts, int id)
+{
+       return &ts->obj_tbl[id];
+}
+
+static struct qtm_object *create_obj(struct qtouch_ts_data *ts,
+                                    struct qtm_obj_entry *entry)
+{
+       struct qtm_object *obj;
+
+       obj = &ts->obj_tbl[entry->type];
+       memcpy(&obj->entry, entry, sizeof(*entry));
+       set_bit(entry->type, ts->obj_map);
+
+       return obj;
+}
+
+static struct qtm_object *find_object_rid(struct qtouch_ts_data *ts, int rid)
+{
+       int i;
+
+       for_each_bit(i, ts->obj_map, QTM_OBP_MAX_OBJECT_NUM) {
+               struct qtm_object *obj = &ts->obj_tbl[i];
+
+               if ((rid >= obj->report_id_min) && (rid <= obj->report_id_max))
+                       return obj;
+       }
+
+       return NULL;
+}
+
+static void qtouch_force_reset(struct qtouch_ts_data *ts, uint8_t sw_reset)
+{
+       struct qtm_object *obj;
+       uint16_t addr;
+       uint8_t val;
+       int ret;
+
+       if (ts->pdata->hw_reset && !sw_reset) {
+               pr_info("%s: Forcing HW reset\n", __func__);
+               ts->pdata->hw_reset();
+       } else if (sw_reset) {
+               pr_info("%s: Forcing SW reset\n", __func__);
+               obj = find_obj(ts, QTM_OBJ_GEN_CMD_PROC);
+               addr =
+                   obj->entry.addr + offsetof(struct qtm_gen_cmd_proc, reset);
+               val = 1;
+               ret = qtouch_write_addr(ts, addr, &val, 1);
+               if (ret)
+                       pr_err("%s: Unable to send the reset msg\n", __func__);
+       }
+}
+
+static int qtouch_force_calibration(struct qtouch_ts_data *ts)
+{
+       struct qtm_object *obj;
+       uint16_t addr;
+       uint8_t val;
+       int ret;
+
+       pr_info("%s: Forcing calibration\n", __func__);
+
+       obj = find_obj(ts, QTM_OBJ_GEN_CMD_PROC);
+
+       addr = obj->entry.addr + offsetof(struct qtm_gen_cmd_proc, calibrate);
+       val = 1;
+       ret = qtouch_write_addr(ts, addr, &val, 1);
+       if (ret)
+               pr_err("%s: Unable to send the calibrate message\n", __func__);
+       return ret;
+}
+
+#undef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+static int qtouch_power_config(struct qtouch_ts_data *ts, int on)
+{
+       struct qtm_gen_power_cfg pwr_cfg;
+       struct qtm_object *obj;
+
+       if (!on) {
+               /* go to standby mode */
+               pwr_cfg.idle_acq_int = 0;
+               pwr_cfg.active_acq_int = 0;
+       } else {
+               pwr_cfg.idle_acq_int = ts->pdata->power_cfg.idle_acq_int;
+               pwr_cfg.active_acq_int = ts->pdata->power_cfg.active_acq_int;
+       }
+
+       pwr_cfg.active_idle_to = ts->pdata->power_cfg.active_idle_to;
+
+       obj = find_obj(ts, QTM_OBJ_GEN_PWR_CONF);
+       return qtouch_write_addr(ts, obj->entry.addr, &pwr_cfg,
+                                min(sizeof(pwr_cfg), obj->entry.size));
+}
+
+/* Apply the configuration provided in the platform_data to the hardware */
+static int qtouch_hw_init(struct qtouch_ts_data *ts)
+{
+       struct qtm_object *obj;
+       int i;
+       int ret;
+       uint16_t adj_addr;
+
+       pr_info("%s: Doing hw init\n", __func__);
+
+       /* take the IC out of suspend */
+       qtouch_power_config(ts, 1);
+
+       /* configure the acquisition object. */
+       obj = find_obj(ts, QTM_OBJ_GEN_ACQUIRE_CONF);
+       ret = qtouch_write_addr(ts, obj->entry.addr, &ts->pdata->acquire_cfg,
+                               min(sizeof(ts->pdata->acquire_cfg),
+                                   obj->entry.size));
+       if (ret != 0) {
+               pr_err("%s: Can't write acquisition config\n", __func__);
+               return ret;
+       }
+
+       /* The multitouch and keyarray objects have very similar memory
+        * layout, but are just different enough where we basically have to
+        * repeat the same code */
+
+       /* configure the multi-touch object. */
+       obj = find_obj(ts, QTM_OBJ_TOUCH_MULTI);
+       if (obj && obj->entry.num_inst > 0) {
+               struct qtm_touch_multi_cfg cfg;
+               memcpy(&cfg, &ts->pdata->multi_touch_cfg, sizeof(cfg));
+               if (ts->pdata->flags & QTOUCH_USE_MULTITOUCH)
+                       cfg.ctrl |= (1 << 1) | (1 << 0); /* reporten | enable */
+               else
+                       cfg.ctrl = 0;
+               ret = qtouch_write_addr(ts, obj->entry.addr, &cfg,
+                                       min(sizeof(cfg), obj->entry.size));
+               if (ret != 0) {
+                       pr_err("%s: Can't write multi-touch config\n",
+                              __func__);
+                       return ret;
+               }
+       }
+
+       /* configure the key-array object. */
+       obj = find_obj(ts, QTM_OBJ_TOUCH_KEYARRAY);
+       if (obj && obj->entry.num_inst > 0) {
+               struct qtm_touch_keyarray_cfg cfg;
+               for (i = 0; i < obj->entry.num_inst; i++) {
+                       if (i > (ts->pdata->key_array.num_keys - 1)) {
+                               pr_info("%s: No entry key instance.\n",
+                                       __func__);
+                               memset(&cfg, 0, sizeof(cfg));
+                       } else if (ts->pdata->flags & QTOUCH_USE_KEYARRAY) {
+                               memcpy(&cfg, &ts->pdata->key_array.cfg[i], sizeof(cfg));
+                               cfg.ctrl |= (1 << 1) | (1 << 0); /* reporten | enable */
+                       } else
+                               memset(&cfg, 0, sizeof(cfg));
+
+                       adj_addr = obj->entry.addr +
+                               ((obj->entry.size + 1) * i);
+                       ret = qtouch_write_addr(ts, adj_addr, &cfg,
+                               min(sizeof(cfg), obj->entry.size));
+                       if (ret != 0) {
+                               pr_err("%s: Can't write keyarray config\n",
+                                          __func__);
+                               return ret;
+                       }
+               }
+       }
+
+       /* configure the signal filter */
+       obj = find_obj(ts, QTM_OBJ_PROCG_SIG_FILTER);
+       if (obj && obj->entry.num_inst > 0) {
+               ret = qtouch_write_addr(ts, obj->entry.addr,
+                                       &ts->pdata->sig_filter_cfg,
+                                       min(sizeof(ts->pdata->sig_filter_cfg),
+                                           obj->entry.size));
+               if (ret != 0) {
+                       pr_err("%s: Can't write signal filter config\n",
+                              __func__);
+                       return ret;
+               }
+       }
+
+       /* configure the linearization table */
+       obj = find_obj(ts, QTM_OBJ_PROCI_LINEAR_TBL);
+       if (obj && obj->entry.num_inst > 0) {
+               ret = qtouch_write_addr(ts, obj->entry.addr,
+                                       &ts->pdata->linear_tbl_cfg,
+                                       min(sizeof(ts->pdata->linear_tbl_cfg),
+                                           obj->entry.size));
+               if (ret != 0) {
+                       pr_err("%s: Can't write linear table config\n",
+                              __func__);
+                       return ret;
+               }
+       }
+       /* configure the grip suppression table */
+       obj = find_obj(ts, QTM_OBJ_PROCI_GRIPFACESUPPRESSION);
+       if (obj && obj->entry.num_inst > 0) {
+               ret = qtouch_write_addr(ts, obj->entry.addr,
+                                       &ts->pdata->grip_suppression_cfg,
+                                       min(sizeof(ts->pdata->grip_suppression_cfg),
+                                           obj->entry.size));
+               if (ret != 0) {
+                       pr_err("%s: Can't write the grip suppression config\n",
+                              __func__);
+                       return ret;
+               }
+       }
+
+       /* configure the noise suppression table */
+       obj = find_obj(ts, QTM_OBJ_NOISESUPPRESSION_1);
+       if (obj && obj->entry.num_inst > 0) {
+               ret = qtouch_write_addr(ts, obj->entry.addr,
+                                       &ts->pdata->noise1_suppression_cfg,
+                                       min(sizeof(ts->pdata->noise1_suppression_cfg),
+                                           obj->entry.size));
+               if (ret != 0) {
+                       pr_err("%s: Can't write the noise suppression config\n",
+                              __func__);
+                       return ret;
+               }
+       }
+
+       ret = qtouch_force_calibration(ts);
+       if (ret != 0) {
+               pr_err("%s: Unable to recalibrate after reset\n", __func__);
+               return ret;
+       }
+
+       /* Write the settings into nvram, if needed */
+       if (ts->pdata->flags & QTOUCH_CFG_BACKUPNV) {
+               uint8_t val;
+               uint16_t addr;
+
+               obj = find_obj(ts, QTM_OBJ_GEN_CMD_PROC);
+               addr = obj->entry.addr + offsetof(struct qtm_gen_cmd_proc,
+                                                 backupnv);
+               val = 0x55;
+               ret = qtouch_write_addr(ts, addr, &val, 1);
+               if (ret != 0) {
+                       pr_err("%s: Can't backup nvram settings\n", __func__);
+                       return ret;
+               }
+               /* Since the IC does not indicate that has completed the
+               backup place a hard wait here.  If we communicate with the
+               IC during backup the EEPROM may be corrupted */
+
+               msleep(500);
+       }
+
+       /* reset the address pointer */
+       ret = qtouch_set_addr(ts, ts->obj_tbl[QTM_OBJ_GEN_MSG_PROC].entry.addr);
+       if (ret != 0) {
+               pr_err("%s: Unable to reset address pointer after reset\n",
+                      __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+/* Handles a message from the command processor object. */
+static int do_cmd_proc_msg(struct qtouch_ts_data *ts, struct qtm_object *obj,
+                          void *_msg)
+{
+       struct qtm_cmd_proc_msg *msg = _msg;
+       int ret = 0;
+       int hw_reset = 0;
+
+       if (msg->status & QTM_CMD_PROC_STATUS_RESET) {
+               if (qtouch_tsdebug)
+                       pr_info("%s:EEPROM checksum is 0x%X cnt %i\n",
+                               __func__, msg->checksum, ts->checksum_cnt);
+               if (msg->checksum != ts->eeprom_checksum) {
+                       if (ts->checksum_cnt > 2) {
+                               /* Assume the checksum is what it is, cannot
+                               disable the touch screen so set the checksum*/
+                               ts->eeprom_checksum = msg->checksum;
+                               ts->checksum_cnt = 0;
+                       } else {
+                               ret = qtouch_hw_init(ts);
+                               if (ret != 0)
+                                       pr_err("%s:Cannot init the touch IC\n",
+                                                  __func__);
+                               hw_reset = 1;
+                               ts->checksum_cnt++;
+                       }
+               }
+               pr_info("%s: Reset done.\n", __func__);
+       }
+
+       if (msg->status & QTM_CMD_PROC_STATUS_CAL)
+               pr_info("%s: Self-calibration started.\n", __func__);
+
+       if (msg->status & QTM_CMD_PROC_STATUS_OFL)
+               pr_err("%s: Acquisition cycle length overflow\n", __func__);
+
+       if (msg->status & QTM_CMD_PROC_STATUS_SIGERR)
+               pr_err("%s: Acquisition error\n", __func__);
+
+       if (msg->status & QTM_CMD_PROC_STATUS_CFGERR) {
+               ret = qtouch_hw_init(ts);
+               if (ret != 0)
+                       pr_err("%s:Cannot init the touch IC\n",
+                              __func__);
+
+               pr_err("%s: Configuration error\n", __func__);
+       }
+       /* Check the EEPROM checksum.  An ESD event may cause
+       the checksum to change during operation so we need to
+       reprogram the EEPROM and reset the IC */
+       if (ts->pdata->flags & QTOUCH_EEPROM_CHECKSUM) {
+               if (msg->checksum != ts->eeprom_checksum) {
+                       if (qtouch_tsdebug)
+                               pr_info("%s:EEPROM checksum is 0x%X cnt %i \
+                                               hw_reset %i\n",
+                                       __func__, msg->checksum,
+                                       ts->checksum_cnt, hw_reset);
+                       if (ts->checksum_cnt > 2) {
+                               /* Assume the checksum is what it is, cannot
+                               disable the touch screen so set the checksum*/
+                               ts->eeprom_checksum = msg->checksum;
+                               ts->checksum_cnt = 0;
+                       } else {
+                               if (!hw_reset) {
+                                       ret = qtouch_hw_init(ts);
+                                       if (ret != 0)
+                                               pr_err("%s:Cannot init the touch IC\n",
+                                               __func__);
+                                       qtouch_force_reset(ts, 0);
+                                       ts->checksum_cnt++;
+                               }
+                       }
+               }
+       }
+       return ret;
+}
+
+/* Handles a message from a multi-touch object. */
+static int do_touch_multi_msg(struct qtouch_ts_data *ts, struct qtm_object *obj,
+                             void *_msg)
+{
+       struct qtm_touch_multi_msg *msg = _msg;
+       int i;
+       int x;
+       int y;
+       int pressure;
+       int width;
+       int finger;
+       int down;
+
+       finger = msg->report_id - obj->report_id_min;
+       if (finger >= ts->pdata->multi_touch_cfg.num_touch)
+               return 0;
+
+       /* x/y are 10bit values, with bottom 2 bits inside the xypos_lsb */
+       x = (msg->xpos_msb << 2) | ((msg->xypos_lsb >> 6) & 0x3);
+       y = (msg->ypos_msb << 2) | ((msg->xypos_lsb >> 2) & 0x3);
+       width = msg->touch_area;
+       pressure = msg->touch_amp;
+
+       if (ts->pdata->flags & QTOUCH_SWAP_XY)
+               swap(x, y);
+
+       if (qtouch_tsdebug & 2)
+               pr_info("%s: stat=%02x, f=%d x=%d y=%d p=%d w=%d\n", __func__,
+                       msg->status, finger, x, y, pressure, width);
+
+       down = !(msg->status & QTM_TOUCH_MULTI_STATUS_RELEASE);
+
+       /* The chip may report erroneous points way
+       beyond what a user could possibly perform so we filter
+       these out */
+       if (ts->finger_data[finger].down &&
+                       (abs(ts->finger_data[finger].x_data - x) > ts->x_delta ||
+                       abs(ts->finger_data[finger].y_data - y) > ts->y_delta)) {
+                               down = 0;
+                               if (qtouch_tsdebug & 2)
+                                       pr_info("%s: x0 %i x1 %i y0 %i y1 %i\n",
+                                               __func__,
+                                               ts->finger_data[finger].x_data, x,
+                                               ts->finger_data[finger].y_data, y);
+       } else {
+               ts->finger_data[finger].x_data = x;
+               ts->finger_data[finger].y_data = y;
+               ts->finger_data[finger].w_data = width;
+       }
+
+       /* The touch IC will not give back a pressure of zero
+          so send a 0 when a liftoff is produced */
+       if (!down) {
+               ts->finger_data[finger].z_data = 0;
+       } else {
+               ts->finger_data[finger].z_data = pressure;
+               ts->finger_data[finger].down = down;
+       }
+
+       for (i = 0; i < ts->pdata->multi_touch_cfg.num_touch; i++) {
+               if (ts->finger_data[i].down == 0)
+                       continue;
+               input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+                                ts->finger_data[i].z_data);
+               input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+                                ts->finger_data[i].w_data);
+               input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
+                                ts->finger_data[i].x_data);
+               input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
+                                ts->finger_data[i].y_data);
+               input_mt_sync(ts->input_dev);
+       }
+       input_sync(ts->input_dev);
+
+       if (!down) {
+               memset(&ts->finger_data[finger], 0,
+               sizeof(struct coordinate_map));
+       }
+
+       return 0;
+}
+
+/* Handles a message from a keyarray object. */
+static int do_touch_keyarray_msg(struct qtouch_ts_data *ts,
+                                struct qtm_object *obj, void *_msg)
+{
+       struct qtm_touch_keyarray_msg *msg = _msg;
+       int i;
+
+       /* nothing changed.. odd. */
+       if (ts->last_keystate == msg->keystate)
+               return 0;
+
+       for (i = 0; i < ts->pdata->key_array.num_keys; ++i) {
+               struct qtouch_key *key = &ts->pdata->key_array.keys[i];
+               uint32_t bit = 1 << (key->channel & 0x1f);
+               if ((msg->keystate & bit) != (ts->last_keystate & bit))
+                       input_report_key(ts->input_dev, key->code,
+                                        msg->keystate & bit);
+       }
+       input_sync(ts->input_dev);
+
+       if (qtouch_tsdebug & 2)
+               pr_info("%s: key state changed 0x%08x -> 0x%08x\n", __func__,
+                       ts->last_keystate, msg->keystate);
+
+       /* update our internal state */
+       ts->last_keystate = msg->keystate;
+
+       return 0;
+}
+
+static int qtouch_handle_msg(struct qtouch_ts_data *ts, struct qtm_object *obj,
+                            struct qtm_obj_message *msg)
+{
+       int ret = 0;
+
+       /* These are all the known objects that we know how to handle. */
+       switch (obj->entry.type) {
+       case QTM_OBJ_GEN_CMD_PROC:
+               ret = do_cmd_proc_msg(ts, obj, msg);
+               break;
+
+       case QTM_OBJ_TOUCH_MULTI:
+               ret = do_touch_multi_msg(ts, obj, msg);
+               break;
+
+       case QTM_OBJ_TOUCH_KEYARRAY:
+               ret = do_touch_keyarray_msg(ts, obj, msg);
+               break;
+
+       default:
+               /* probably not fatal? */
+               ret = 0;
+               pr_info("%s: No handler defined for message from object "
+                       "type %d, report_id %d\n", __func__, obj->entry.type,
+                       msg->report_id);
+       }
+
+       return ret;
+}
+
+static void qtouch_ts_work_func(struct work_struct *work)
+{
+       struct qtouch_ts_data *ts =
+               container_of(work, struct qtouch_ts_data, work);
+       struct qtm_obj_message *msg;
+       struct qtm_object *obj;
+       int ret;
+
+       msg = qtouch_read_msg(ts);
+       if (msg == NULL) {
+               pr_err("%s: Cannot read message\n", __func__);
+               goto done;
+       }
+
+       obj = find_object_rid(ts, msg->report_id);
+       if (!obj) {
+               pr_err("%s: Unknown object for report_id %d\n", __func__,
+                      msg->report_id);
+               goto done;
+       }
+
+       ret = qtouch_handle_msg(ts, obj, msg);
+       if (ret != 0) {
+               pr_err("%s: Unable to process message for obj %d, "
+                      "report_id %d\n", __func__, obj->entry.type,
+                      msg->report_id);
+               goto done;
+       }
+
+done:
+       enable_irq(ts->client->irq);
+}
+
+static int qtouch_process_info_block(struct qtouch_ts_data *ts)
+{
+       struct qtm_id_info qtm_info;
+       uint16_t our_csum = 0x0;
+       uint16_t their_csum;
+       uint8_t report_id;
+       uint16_t addr;
+       int err;
+       int i;
+
+       /* query the device and get the info block. */
+       err = qtouch_read_addr(ts, QTM_OBP_ID_INFO_ADDR, &qtm_info,
+                              sizeof(qtm_info));
+       if (err != 0) {
+               pr_err("%s: Cannot read info object block\n", __func__);
+               goto err_read_info_block;
+       }
+       our_csum = calc_csum(our_csum, &qtm_info, sizeof(qtm_info));
+
+       /* TODO: Add a version/family/variant check? */
+       pr_info("%s: Build version is 0x%x\n", __func__, qtm_info.version);
+
+       if (qtm_info.num_objs == 0) {
+               pr_err("%s: Device (0x%x/0x%x/0x%x/0x%x) does not export any "
+                      "objects.\n", __func__, qtm_info.family_id,
+                      qtm_info.variant_id, qtm_info.version, qtm_info.build);
+               err = -ENODEV;
+               goto err_no_objects;
+       }
+
+       addr = QTM_OBP_ID_INFO_ADDR + sizeof(qtm_info);
+       report_id = 1;
+
+       /* read out the object entries table */
+       for (i = 0; i < qtm_info.num_objs; ++i) {
+               struct qtm_object *obj;
+               struct qtm_obj_entry entry;
+
+               err = qtouch_read_addr(ts, addr, &entry, sizeof(entry));
+               if (err != 0) {
+                       pr_err("%s: Can't read object (%d) entry.\n",
+                              __func__, i);
+                       err = -EIO;
+                       goto err_read_entry;
+               }
+               our_csum = calc_csum(our_csum, &entry, sizeof(entry));
+               addr += sizeof(entry);
+
+               entry.size++;
+               entry.num_inst++;
+
+               pr_info("%s: Object %d @ 0x%04x (%d) insts %d rep_ids %d\n",
+                       __func__, entry.type, entry.addr, entry.size,
+                       entry.num_inst, entry.num_rids);
+
+               if (entry.type >= QTM_OBP_MAX_OBJECT_NUM) {
+                       pr_warning("%s: Unknown object type (%d) encountered\n",
+                                  __func__, entry.type);
+                       /* Not fatal */
+                       continue;
+               }
+
+               /* save the message_procesor msg_size for easy reference. */
+               if (entry.type == QTM_OBJ_GEN_MSG_PROC)
+                       ts->msg_size = entry.size;
+
+               obj = create_obj(ts, &entry);
+               /* set the report_id range that the object is responsible for */
+               if ((obj->entry.num_rids * obj->entry.num_inst) != 0) {
+                       obj->report_id_min = report_id;
+                       report_id += obj->entry.num_rids * obj->entry.num_inst;
+                       obj->report_id_max = report_id - 1;
+               }
+       }
+
+       if (!ts->msg_size) {
+               pr_err("%s: Message processing object not found. Bailing.\n",
+                      __func__);
+               err = -ENODEV;
+               goto err_no_msg_proc;
+       }
+
+       /* verify that some basic objects are present. These objects are
+        * assumed to be present by the rest of the driver, so fail out now
+        * if the firmware is busted. */
+       if (!find_obj(ts, QTM_OBJ_GEN_PWR_CONF) ||
+           !find_obj(ts, QTM_OBJ_GEN_ACQUIRE_CONF) ||
+           !find_obj(ts, QTM_OBJ_GEN_MSG_PROC) ||
+           !find_obj(ts, QTM_OBJ_GEN_CMD_PROC)) {
+               pr_err("%s: Required objects are missing\n", __func__);
+               err = -ENOENT;
+               goto err_missing_objs;
+       }
+
+       err = qtouch_read_addr(ts, addr, &their_csum, sizeof(their_csum));
+       if (err != 0) {
+               pr_err("%s: Unable to read remote checksum\n", __func__);
+               err = -ENODEV;
+               goto err_no_checksum;
+       }
+
+       /* FIXME: The algorithm described in the datasheet doesn't seem to
+        * match what the touch firmware is doing on the other side. We
+        * always get mismatches! */
+       if (our_csum != their_csum) {
+               pr_warning("%s: Checksum mismatch (0x%04x != 0x%04x)\n",
+                          __func__, our_csum, their_csum);
+#ifndef IGNORE_CHECKSUM_MISMATCH
+               err = -ENODEV;
+               goto err_bad_checksum;
+#endif
+       }
+
+       pr_info("%s: %s found. family 0x%x, variant 0x%x, ver 0x%x, "
+               "build 0x%x, matrix %dx%d, %d objects.\n", __func__,
+               QTOUCH_TS_NAME, qtm_info.family_id, qtm_info.variant_id,
+               qtm_info.version, qtm_info.build, qtm_info.matrix_x_size,
+               qtm_info.matrix_y_size, qtm_info.num_objs);
+
+       ts->eeprom_checksum = ts->pdata->nv_checksum;
+
+       return 0;
+
+err_no_checksum:
+err_missing_objs:
+err_no_msg_proc:
+err_read_entry:
+err_no_objects:
+err_read_info_block:
+       return err;
+}
+
+static int qtouch_ts_probe(struct i2c_client *client,
+                          const struct i2c_device_id *id)
+{
+       struct qtouch_ts_platform_data *pdata = client->dev.platform_data;
+       struct qtouch_ts_data *ts;
+       struct qtm_object *obj;
+       int err;
+       int i;
+
+       if (pdata == NULL) {
+               pr_err("%s: platform data required\n", __func__);
+               return -ENODEV;
+       } else if (!client->irq) {
+               pr_err("%s: polling mode currently not supported\n", __func__);
+               return -ENODEV;
+       } else if (!pdata->hw_reset) {
+               pr_err("%s: Must supply a hw reset function\n", __func__);
+               return -ENODEV;
+       }
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               pr_err("%s: need I2C_FUNC_I2C\n", __func__);
+               return -ENODEV;
+       }
+
+       ts = kzalloc(sizeof(struct qtouch_ts_data), GFP_KERNEL);
+       if (ts == NULL) {
+               err = -ENOMEM;
+               goto err_alloc_data_failed;
+       }
+
+       INIT_WORK(&ts->work, qtouch_ts_work_func);
+
+       ts->pdata = pdata;
+       ts->client = client;
+       i2c_set_clientdata(client, ts);
+       ts->checksum_cnt = 0;
+       ts->x_delta = ts->pdata->x_delta;
+       ts->y_delta = ts->pdata->y_delta;
+
+       ts->input_dev = input_allocate_device();
+       if (ts->input_dev == NULL) {
+               pr_err("%s: failed to alloc input device\n", __func__);
+               err = -ENOMEM;
+               goto err_alloc_input_dev;
+       }
+       ts->input_dev->name = "qtouch-touchscreen";
+       input_set_drvdata(ts->input_dev, ts);
+
+       qtouch_force_reset(ts, 0);
+
+       err = qtouch_process_info_block(ts);
+       if (err != 0)
+               goto err_process_info_block;
+
+       ts->msg_buf = kmalloc(ts->msg_size, GFP_KERNEL);
+       if (ts->msg_buf == NULL) {
+               pr_err("%s: Cannot allocate msg_buf\n", __func__);
+               err = -ENOMEM;
+               goto err_alloc_msg_buf;
+       }
+
+       /* Point the address pointer to the message processor.
+        * Must do this before enabling interrupts */
+       obj = find_obj(ts, QTM_OBJ_GEN_MSG_PROC);
+       err = qtouch_set_addr(ts, obj->entry.addr);
+       if (err != 0) {
+               pr_err("%s: Can't to set addr to msg processor\n", __func__);
+               goto err_rst_addr_msg_proc;
+       }
+
+       set_bit(EV_SYN, ts->input_dev->evbit);
+
+       /* register the harwdare assisted virtual keys, if any */
+       obj = find_obj(ts, QTM_OBJ_TOUCH_KEYARRAY);
+       if (obj && (obj->entry.num_inst > 0) &&
+           (pdata->flags & QTOUCH_USE_KEYARRAY)) {
+               for (i = 0; i < pdata->key_array.num_keys; ++i)
+                       input_set_capability(ts->input_dev, EV_KEY,
+                                            pdata->key_array.keys[i].code);
+       }
+
+       /* register the software virtual keys, if any are provided */
+       for (i = 0; i < pdata->vkeys.count; ++i)
+               input_set_capability(ts->input_dev, EV_KEY,
+                                    pdata->vkeys.keys[i].code);
+
+       obj = find_obj(ts, QTM_OBJ_TOUCH_MULTI);
+       if (obj && obj->entry.num_inst > 0) {
+               set_bit(EV_ABS, ts->input_dev->evbit);
+               /* Legacy support for testing only */
+               input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH);
+               input_set_capability(ts->input_dev, EV_KEY, BTN_2);
+               input_set_abs_params(ts->input_dev, ABS_X,
+                       pdata->abs_min_x, pdata->abs_max_x,
+                       pdata->fuzz_x, 0);
+               input_set_abs_params(ts->input_dev, ABS_HAT0X,
+                       pdata->abs_min_x, pdata->abs_max_x,
+                       pdata->fuzz_x, 0);
+               input_set_abs_params(ts->input_dev, ABS_Y,
+                       pdata->abs_min_y, pdata->abs_max_y,
+                       pdata->fuzz_y, 0);
+               input_set_abs_params(ts->input_dev, ABS_HAT0Y,
+                       pdata->abs_min_x, pdata->abs_max_x,
+                       pdata->fuzz_x, 0);
+               input_set_abs_params(ts->input_dev, ABS_PRESSURE,
+                       pdata->abs_min_p, pdata->abs_max_p,
+                       pdata->fuzz_p, 0);
+               input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH,
+                       pdata->abs_min_w, pdata->abs_max_w,
+                       pdata->fuzz_w, 0);
+
+               /* multi touch */
+               input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X,
+                       pdata->abs_min_x, pdata->abs_max_x,
+                       pdata->fuzz_x, 0);
+               input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y,
+                       pdata->abs_min_y, pdata->abs_max_y,
+                       pdata->fuzz_y, 0);
+               input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR,
+                       pdata->abs_min_p, pdata->abs_max_p,
+                       pdata->fuzz_p, 0);
+               input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR,
+                       pdata->abs_min_w, pdata->abs_max_w,
+                       pdata->fuzz_w, 0);
+       }
+
+       memset(&ts->finger_data[0], 0,
+               (sizeof(struct coordinate_map) *
+               ts->pdata->multi_touch_cfg.num_touch));
+
+       err = input_register_device(ts->input_dev);
+       if (err != 0) {
+               pr_err("%s: Cannot register input device \"%s\"\n", __func__,
+                      ts->input_dev->name);
+               goto err_input_register_dev;
+       }
+
+       err = request_irq(ts->client->irq, qtouch_ts_irq_handler,
+                         IRQ_DISABLED | pdata->irqflags, "qtouch_ts_int", ts);
+       if (err != 0) {
+               pr_err("%s: request_irq (%d) failed\n", __func__,
+                      ts->client->irq);
+               goto err_request_irq;
+       }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+       ts->early_suspend.suspend = qtouch_ts_early_suspend;
+       ts->early_suspend.resume = qtouch_ts_late_resume;
+       register_early_suspend(&ts->early_suspend);
+#endif
+
+       return 0;
+
+err_request_irq:
+       input_unregister_device(ts->input_dev);
+
+err_input_register_dev:
+err_rst_addr_msg_proc:
+       if (ts->msg_buf)
+               kfree(ts->msg_buf);
+
+err_alloc_msg_buf:
+err_process_info_block:
+       input_free_device(ts->input_dev);
+
+err_alloc_input_dev:
+       i2c_set_clientdata(client, NULL);
+       kfree(ts);
+
+err_alloc_data_failed:
+       return err;
+}
+
+static int qtouch_ts_remove(struct i2c_client *client)
+{
+       struct qtouch_ts_data *ts = i2c_get_clientdata(client);
+
+       unregister_early_suspend(&ts->early_suspend);
+       free_irq(ts->client->irq, ts);
+       input_unregister_device(ts->input_dev);
+       input_free_device(ts->input_dev);
+       i2c_set_clientdata(client, NULL);
+       kfree(ts);
+       return 0;
+}
+
+static int qtouch_ts_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       struct qtouch_ts_data *ts = i2c_get_clientdata(client);
+       int ret;
+       if (qtouch_tsdebug & 4)
+               pr_info("%s: Suspending\n", __func__);
+
+       disable_irq_nosync(ts->client->irq);
+       ret = cancel_work_sync(&ts->work);
+       if (ret) { /* if work was pending disable-count is now 2 */
+               pr_info("%s: Pending work item\n", __func__);
+               enable_irq(ts->client->irq);
+       }
+
+       ret = qtouch_power_config(ts, 0);
+       if (ret < 0)
+               pr_err("%s: Cannot write power config\n", __func__);
+
+       return 0;
+}
+
+static int qtouch_ts_resume(struct i2c_client *client)
+{
+       struct qtouch_ts_data *ts = i2c_get_clientdata(client);
+       int ret;
+       int i;
+
+       if (qtouch_tsdebug & 4)
+               pr_info("%s: Resuming\n", __func__);
+
+       /* If we were suspended while a touch was happening
+          we need to tell the upper layers so they do not hang
+          waiting on the liftoff that will not come. */
+       for (i = 0; i < ts->pdata->multi_touch_cfg.num_touch; i++) {
+               if (qtouch_tsdebug & 4)
+                       pr_info("%s: Finger %i down state %i\n",
+                               __func__, i, ts->finger_data[i].down);
+               if (ts->finger_data[i].down == 0)
+                       continue;
+               input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+               input_mt_sync(ts->input_dev);
+               memset(&ts->finger_data[i], 0,
+               sizeof(struct coordinate_map));
+       }
+       input_sync(ts->input_dev);
+
+       ret = qtouch_power_config(ts, 1);
+       if (ret < 0) {
+               pr_err("%s: Cannot write power config\n", __func__);
+               return -EIO;
+       }
+       qtouch_force_reset(ts, 0);
+
+       enable_irq(ts->client->irq);
+       return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void qtouch_ts_early_suspend(struct early_suspend *handler)
+{
+       struct qtouch_ts_data *ts;
+
+       ts = container_of(handler, struct qtouch_ts_data, early_suspend);
+       qtouch_ts_suspend(ts->client, PMSG_SUSPEND);
+}
+
+static void qtouch_ts_late_resume(struct early_suspend *handler)
+{
+       struct qtouch_ts_data *ts;
+
+       ts = container_of(handler, struct qtouch_ts_data, early_suspend);
+       qtouch_ts_resume(ts->client);
+}
+#endif
+
+/******** init ********/
+static const struct i2c_device_id qtouch_ts_id[] = {
+       { QTOUCH_TS_NAME, 0 },
+       { }
+};
+
+static struct i2c_driver qtouch_ts_driver = {
+       .probe          = qtouch_ts_probe,
+       .remove         = qtouch_ts_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+       .suspend        = qtouch_ts_suspend,
+       .resume         = qtouch_ts_resume,
+#endif
+       .id_table       = qtouch_ts_id,
+       .driver = {
+               .name   = QTOUCH_TS_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __devinit qtouch_ts_init(void)
+{
+       qtouch_ts_wq = create_singlethread_workqueue("qtouch_obp_ts_wq");
+       if (qtouch_ts_wq == NULL) {
+               pr_err("%s: No memory for qtouch_ts_wq\n", __func__);
+               return -ENOMEM;
+       }
+       return i2c_add_driver(&qtouch_ts_driver);
+}
+
+static void __exit qtouch_ts_exit(void)
+{
+       i2c_del_driver(&qtouch_ts_driver);
+       if (qtouch_ts_wq)
+               destroy_workqueue(qtouch_ts_wq);
+}
+
+module_init(qtouch_ts_init);
+module_exit(qtouch_ts_exit);
+
+MODULE_AUTHOR("Dima Zavin <dima@android.com>");
+MODULE_DESCRIPTION("Quantum OBP Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/qtouch_obp_ts.h b/include/linux/qtouch_obp_ts.h
new file mode 100644 (file)
index 0000000..1091b89
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * include/linux/qtouch_obp_ts.h - platform/protocol data for Quantum touch IC
+ *
+ * Copyright (C) 2009 Google, Inc.
+  * Copyright (C) 2009 Motorola, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Derived from the Motorola OBP touch driver.
+ *
+ */
+
+#ifndef _LINUX_QTOUCH_OBP_TS_H
+#define _LINUX_QTOUCH_OBP_TS_H
+
+#define QTOUCH_TS_NAME "qtouch-obp-ts"
+
+#define QTM_OBP_ID_INFO_ADDR           0
+
+enum {
+       QTM_OBJ_RESERVED0               = 0,
+       QTM_OBJ_RESERVED1               = 1,
+       QTM_OBJ_DBG_DELTAS              = 2,
+       QTM_OBJ_DBG_REFS                = 3,
+       QTM_OBJ_DBG_SIGS                = 4,
+       QTM_OBJ_GEN_MSG_PROC            = 5,
+       QTM_OBJ_GEN_CMD_PROC            = 6,
+       QTM_OBJ_GEN_PWR_CONF            = 7,
+       QTM_OBJ_GEN_ACQUIRE_CONF        = 8,
+       QTM_OBJ_TOUCH_MULTI             = 9,
+       QTM_OBJ_TOUCH_SINGLE            = 10,
+       QTM_OBJ_TOUCH_XSLIDER           = 11,
+       QTM_OBJ_TOUCH_SLIDER            = 12,
+       QTM_OBJ_TOUCH_XWHEEL            = 13,
+       QTM_OBJ_TOUCH_YWHEEL            = 14,
+       QTM_OBJ_TOUCH_KEYARRAY          = 15,
+       QTM_OBJ_PROCG_SIG_FILTER        = 16,
+       QTM_OBJ_PROCI_LINEAR_TBL        = 17,
+       QTM_OBJ_PROCI_GESTURES_PROC     = 18,
+       QTM_OBJ_PROCI_GRIPFACESUPPRESSION = 20,
+       QTM_OBJ_NOISESUPPRESSION_1 = 36,
+
+       /* Max number of objects currently defined */
+       QTM_OBP_MAX_OBJECT_NUM = QTM_OBJ_NOISESUPPRESSION_1 + 1,
+};
+
+/* OBP structures as defined by the wire protocol. */
+
+/* Note: Not all the structures below need an explicit packed attribute since
+ * many of them just contain uint8_t's. However, the protocol is defined in
+ * such a way that the structures may expand in the future, with
+ * potential multi-byte fields. Thus, we will mark them all as packed to
+ * minimize silly bugs in the future.
+ */
+
+/* part of the info block */
+struct qtm_id_info {
+       uint8_t                 family_id;
+       uint8_t                 variant_id;
+       uint8_t                 version;
+       uint8_t                 build;
+       uint8_t                 matrix_x_size;
+       uint8_t                 matrix_y_size;
+       uint8_t                 num_objs;
+} __attribute__ ((packed));
+
+/* an entry in the ote table */
+struct qtm_obj_entry {
+       uint8_t                 type;
+       uint16_t                addr;
+       uint8_t                 size;
+       uint8_t                 num_inst;
+       uint8_t                 num_rids;
+} __attribute__ ((packed));
+
+
+/*******************************/
+/*********** messages **********/
+/*******************************/
+
+/* generic message received from the message_processor object. size/buffer
+ * defined at runtime after reading the info block */
+struct qtm_obj_message {
+       uint8_t                 report_id;
+       uint8_t                 msg[0];
+} __attribute__ ((packed));
+
+/* status message sent by the command processor - T6 */
+#define QTM_CMD_PROC_STATUS_RESET      (1 << 7)
+#define QTM_CMD_PROC_STATUS_OFL                (1 << 6)
+#define QTM_CMD_PROC_STATUS_SIGERR     (1 << 5)
+#define QTM_CMD_PROC_STATUS_CAL                (1 << 4)
+#define QTM_CMD_PROC_STATUS_CFGERR     (1 << 3)
+struct qtm_cmd_proc_msg {
+       uint8_t                 report_id;
+       uint8_t                 status;
+       uint16_t                checksum;
+} __attribute__ ((packed));
+
+/* status message sent by the mutlitouch touch object - T9*/
+#define QTM_TOUCH_MULTI_STATUS_TOUCH           (1 << 7)
+#define QTM_TOUCH_MULTI_STATUS_PRESS           (1 << 6)
+#define QTM_TOUCH_MULTI_STATUS_RELEASE         (1 << 5)
+#define QTM_TOUCH_MULTI_STATUS_MOVE            (1 << 4)
+#define QTM_TOUCH_MULTI_STATUS_VECTOR          (1 << 3)
+#define QTM_TOUCH_MULTI_STATUS_AMPLITUDE       (1 << 2)
+struct qtm_touch_multi_msg {
+       uint8_t                 report_id;
+       uint8_t                 status;
+       uint8_t                 xpos_msb;
+       uint8_t                 ypos_msb;
+       uint8_t                 xypos_lsb;
+       uint8_t                 touch_area;
+       uint8_t                 touch_amp;
+       uint8_t                 touch_vect;
+} __attribute__ ((packed));
+
+/* status message sent by the keyarray touch object - T15 */
+#define QTM_TOUCH_KEYARRAY_STATUS_TOUCH                (1 << 7)
+struct qtm_touch_keyarray_msg {
+       uint8_t                 report_id;
+       uint8_t                 status;
+       uint32_t                keystate;
+} __attribute__ ((packed));
+
+
+
+/*******************************/
+/**** configuration objects ****/
+/*******************************/
+
+/* GEN_COMMANDPROCESSOR_T6 */
+struct qtm_gen_cmd_proc {
+       uint8_t                 reset;
+       uint8_t                 backupnv;
+       uint8_t                 calibrate;
+       uint8_t                 reportall;
+       uint8_t                 debugctrl;
+} __attribute__ ((packed));
+
+/* GEN_POWERCONFIG_T7 */
+struct qtm_gen_power_cfg {
+       uint8_t                 idle_acq_int;      /* in ms */
+       uint8_t                 active_acq_int;    /* in ms */
+       uint8_t                 active_idle_to;    /* in 200ms */
+} __attribute__ ((packed));
+
+/* GEN_ACQUIRECONFIG_T8 */
+struct qtm_gen_acquire_cfg {
+       uint8_t                 charge_time;       /* in 250ns */
+       uint8_t                 atouch_drift;      /* in 200ms */
+       uint8_t                 touch_drift;       /* in 200ms */
+       uint8_t                 drift_susp;        /* in 200ms */
+       uint8_t                 touch_autocal;     /* in 200ms */
+       uint8_t                 sync;
+} __attribute__ ((packed));
+
+/* TOUCH_MULTITOUCHSCREEN_T9 */
+struct qtm_touch_multi_cfg {
+       uint8_t                 ctrl;
+       uint8_t                 x_origin;
+       uint8_t                 y_origin;
+       uint8_t                 x_size;
+       uint8_t                 y_size;
+       uint8_t                 aks_cfg;
+       uint8_t                 burst_len;
+       uint8_t                 tch_det_thr;
+       uint8_t                 tch_det_int;
+       uint8_t                 rsvd1;
+       uint8_t                 rsvd2;
+       uint8_t                 mov_hyst_init;
+       uint8_t                 mov_hyst_next;
+       uint8_t                 mov_filter;
+       uint8_t                 num_touch;
+       uint8_t                 merge_hyst;
+       uint8_t                 merge_thresh;
+       uint8_t         amp_hyst;
+       uint16_t                x_res;
+       uint16_t                y_res;
+       uint8_t                 x_low_clip;
+       uint8_t                 x_high_clip;
+       uint8_t                 y_low_clip;
+       uint8_t                 y_high_clip;
+
+} __attribute__ ((packed));
+
+/* TOUCH_KEYARRAY_T15 */
+struct qtm_touch_keyarray_cfg {
+       uint8_t                 ctrl;
+       uint8_t                 x_origin;
+       uint8_t                 y_origin;
+       uint8_t                 x_size;
+       uint8_t                 y_size;
+       uint8_t                 aks_cfg;
+       uint8_t                 burst_len;
+       uint8_t                 tch_det_thr;
+       uint8_t                 tch_det_int;
+       uint8_t                 rsvd1;
+       uint8_t                 rsvd2;
+} __attribute__ ((packed));
+
+/* PROCG_SIGNALFILTER_T16 */
+struct qtm_procg_sig_filter_cfg {
+       uint8_t                 slew;
+       uint8_t                 median;
+       uint8_t                 iir;
+} __attribute__ ((packed));
+
+/* PROCI_LINEARIZATIONTABLE_T17 */
+struct qtm_proci_linear_tbl_cfg {
+       uint8_t                 ctrl;
+       uint16_t                x_offset;
+       uint8_t                 x_segment[16];
+       uint16_t                y_offset;
+       uint8_t                 y_segment[16];
+} __attribute__ ((packed));
+
+/* PROCI_GRIPFACESUPPRESSION_T20 */
+struct qtm_proci_grip_suppression_cfg {
+       uint8_t                 ctrl;
+       uint8_t                 xlogrip;
+       uint8_t                 xhigrip;
+       uint8_t                 ylogrip;
+       uint8_t                 yhigrip;
+       uint8_t                 maxtchs;
+       uint8_t                 reserve0;
+       uint8_t                 szthr1;
+       uint8_t                 szthr2;
+       uint8_t                 shpthr1;
+       uint8_t                 shpthr2;
+} __attribute__ ((packed));
+
+/* QTM_OBJ_NOISESUPPRESSION_1 */
+struct qtm_proci_noise1_suppression_cfg {
+       uint8_t                 ctrl;
+       uint8_t                 reserved;
+       uint8_t                 atchthr;
+       uint8_t                 duty_cycle;
+} __attribute__ ((packed));
+
+/*******************************/
+/******** platform data ********/
+/*******************************/
+
+struct vkey {
+       int     code;
+       int     min;
+       int     max;
+};
+
+struct virt_keys {
+       struct vkey             *keys;
+       int                     count;
+       int                     start;
+};
+
+struct qtouch_key {
+       uint8_t                         channel;
+       int                             code;
+};
+
+struct qtouch_key_array {
+       struct qtm_touch_keyarray_cfg   *cfg;
+       struct qtouch_key               *keys;
+       int                             num_keys;
+};
+
+#define QTOUCH_FLIP_X          (1 << 0)
+#define QTOUCH_FLIP_Y          (1 << 1)
+#define QTOUCH_SWAP_XY         (1 << 2)
+#define QTOUCH_USE_MULTITOUCH  (1 << 3)
+#define QTOUCH_USE_KEYARRAY    (1 << 4)
+#define QTOUCH_CFG_BACKUPNV    (1 << 5)
+#define QTOUCH_EEPROM_CHECKSUM  (1 << 6)
+
+struct qtouch_ts_platform_data {
+       uint32_t                flags;
+       unsigned long           irqflags;
+
+       uint32_t                abs_min_x;
+       uint32_t                abs_max_x;
+       uint32_t                abs_min_y;
+       uint32_t                abs_max_y;
+       uint32_t                abs_min_p;
+       uint32_t                abs_max_p;
+       uint32_t                abs_min_w;
+       uint32_t                abs_max_w;
+
+       uint32_t                x_delta;
+       uint32_t                y_delta;
+
+       uint16_t                nv_checksum;
+
+       uint32_t                fuzz_x;
+       uint32_t                fuzz_y;
+       uint32_t                fuzz_p;
+       uint32_t                fuzz_w;
+
+       int                     (*hw_reset)(void);
+
+       struct virt_keys        vkeys;
+
+       /* TODO: allow multiple key arrays */
+       struct qtouch_key_array                 key_array;
+
+       /* object configuration information from board */
+       struct qtm_gen_power_cfg                power_cfg;
+       struct qtm_gen_acquire_cfg              acquire_cfg;
+       struct qtm_touch_multi_cfg              multi_touch_cfg;
+       struct qtm_touch_keyarray_cfg   key_array_cfg;
+       struct qtm_procg_sig_filter_cfg         sig_filter_cfg;
+       struct qtm_proci_linear_tbl_cfg         linear_tbl_cfg;
+       struct qtm_proci_grip_suppression_cfg   grip_suppression_cfg;
+       struct qtm_proci_noise1_suppression_cfg noise1_suppression_cfg;
+};
+
+#endif /* _LINUX_QTOUCH_OBP_TS_H */
+