diff mbox

[3/4] Input: cyttsp4 - MultiTouch driver for Cypress TMA4XX touchscreen devices

Message ID 1344345032-30537-1-git-send-email-fery@cypress.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ferruh Yigit Aug. 7, 2012, 1:10 p.m. UTC
From: Ferruh YIGIT <fery@cypress.com>

Cypress TrueTouch(tm) Standard Product controllers, Generetion4
devices, MutliTouch driver.

Subscribes to core driver and converts touch information to OS specific
touch events.

This module is supports multi-touch protocol type B reports.

Signed-off-by: Ferruh YIGIT <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig             |    9 +
 drivers/input/touchscreen/Makefile            |    8 +
 drivers/input/touchscreen/cyttsp4_mt_common.c |  612 +++++++++++++++++++++++++
 drivers/input/touchscreen/cyttsp4_mt_common.h |   76 +++
 drivers/input/touchscreen/cyttsp4_mtb.c       |  107 +++++
 include/linux/cyttsp4_mt.h                    |   72 +++
 6 files changed, 884 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_mt_common.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_mt_common.h
 create mode 100644 drivers/input/touchscreen/cyttsp4_mtb.c
 create mode 100644 include/linux/cyttsp4_mt.h

--
1.7.9.5


This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 6fa7278..57a38f8 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -192,6 +192,7 @@  config TOUCHSCREEN_CYPRESS_CYTTSP4
        tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
        default m
        select CYPRESS_CYTTSP4_BUS
+       select TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B
        help
          Core driver for Cypress TrueTouch(tm) Standard Product
          Geneartion4 touchscreen controllers.
@@ -222,6 +223,14 @@  config TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG

          Say Y here to enable verbose debug output.

+config TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B
+       tristate "Cypress TrueTouch Gen4 MultiTouch Protocol B"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4
+       default m
+       help
+         Cypress TrueTouch(tm) Standard Product Generation4
+         MutliTouch Protocol B support.
+
 config TOUCHSCREEN_DA9034
        tristate "Touchscreen support for Dialog Semiconductor DA9034"
        depends on PMIC_DA903X
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index d12965b..8feae16 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -75,12 +75,20 @@  obj-$(CONFIG_TOUCHSCREEN_W90X900)   += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
 obj-$(CONFIG_CYPRESS_CYTTSP4_BUS) += cyttsp4_bus.o
 obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4)      += cyttsp4_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B) += cyttsp4_mt_b.o
+cyttsp4_mt_b-y := cyttsp4_mtb.o cyttsp4_mt_common.o
 ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG),y)
 CFLAGS_cyttsp4_core.o += -DDEBUG
 CFLAGS_cyttsp4_bus.o += -DDEBUG
+CFLAGS_cyttsp4_mtb.o += -DDEBUG
+CFLAGS_cyttsp4_mt_b.o += -DDEBUG
+CFLAGS_cyttsp4_mt_common.o += -DDEBUG
 endif

 ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG),y)
 CFLAGS_cyttsp4_core.o += -DVERBOSE_DEBUG
 CFLAGS_cyttsp4_bus.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_mtb.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_mt_b.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_mt_common.o += -DVERBOSE_DEBUG
 endif
diff --git a/drivers/input/touchscreen/cyttsp4_mt_common.c b/drivers/input/touchscreen/cyttsp4_mt_common.c
new file mode 100644
index 0000000..e512f9c
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_mt_common.c
@@ -0,0 +1,612 @@ 
+/*
+ * cyttsp4_mt_common.c
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include "cyttsp4_mt_common.h"
+
+static void cyttsp4_lift_all(struct cyttsp4_mt_data *md)
+{
+       if (md->num_prv_tch != 0) {
+               if (md->mt_function.report_slot_liftoff)
+                       md->mt_function.report_slot_liftoff(md);
+               /* ICS Lift off button release signal and empty mt */
+               if (md->prv_tch_type != CY_OBJ_HOVER)
+                       input_report_key(md->input, BTN_TOUCH, CY_BTN_RELEASED);
+               input_sync(md->input);
+               md->num_prv_tch = 0;
+       }
+}
+
+static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md,
+       int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+       int nbyte;
+       int next;
+
+       for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+               dev_vdbg(&md->ttsp->dev,
+                       "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+                       " xy_data[%d]=%02X(%d) bofs=%d\n",
+                       __func__, *axis, *axis, size, max, xy_data, next,
+                       xy_data[next], xy_data[next], bofs);
+               *axis = (*axis * 256) + (xy_data[next] >> bofs);
+               next++;
+       }
+
+       *axis &= max - 1;
+
+       dev_vdbg(&md->ttsp->dev,
+               "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p"
+               " xy_data[%d]=%02X(%d)\n",
+               __func__, *axis, *axis, size, max, xy_data, next,
+               xy_data[next], xy_data[next]);
+}
+
+static void cyttsp4_get_touch(struct cyttsp4_mt_data *md,
+       struct cyttsp4_touch *touch, u8 *xy_data)
+{
+       struct device *dev = &md->ttsp->dev;
+       struct cyttsp4_sysinfo *si = md->si;
+       enum cyttsp4_tch_abs abs;
+       int tmp;
+       bool flipped;
+
+       for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) {
+               cyttsp4_get_touch_axis(md, &touch->abs[abs],
+                       si->si_ofs.tch_abs[abs].size,
+                       si->si_ofs.tch_abs[abs].max,
+                       xy_data + si->si_ofs.tch_abs[abs].ofs,
+                       si->si_ofs.tch_abs[abs].bofs);
+               dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__,
+                       cyttsp4_tch_abs_string[abs],
+                       touch->abs[abs], touch->abs[abs]);
+       }
+
+       if (md->pdata->flags & CY_FLAG_FLIP) {
+               tmp = touch->abs[CY_TCH_X];
+               touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y];
+               touch->abs[CY_TCH_Y] = tmp;
+               flipped = true;
+       } else
+               flipped = false;
+
+       if (md->pdata->flags & CY_FLAG_INV_X) {
+               if (flipped)
+                       touch->abs[CY_TCH_X] = md->si->si_ofs.max_y -
+                               touch->abs[CY_TCH_X];
+               else
+                       touch->abs[CY_TCH_X] = md->si->si_ofs.max_x -
+                               touch->abs[CY_TCH_X];
+       }
+       if (md->pdata->flags & CY_FLAG_INV_Y) {
+               if (flipped)
+                       touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x -
+                               touch->abs[CY_TCH_Y];
+               else
+                       touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y -
+                               touch->abs[CY_TCH_Y];
+       }
+
+       dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
+               __func__, flipped ? "true" : "false",
+               md->pdata->flags & CY_FLAG_INV_X ? "true" : "false",
+               md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false",
+               touch->abs[CY_TCH_X], touch->abs[CY_TCH_X],
+               touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]);
+}
+
+static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch)
+{
+       struct device *dev = &md->ttsp->dev;
+       struct cyttsp4_sysinfo *si = md->si;
+       struct cyttsp4_touch tch;
+       int sig;
+       int i, j, t = 0;
+       int ids[max(CY_TMA1036_MAX_TCH + 1,
+               CY_TMA4XX_MAX_TCH + 1)]; /* add one for hover */
+       int mt_sync_count = 0;
+
+       memset(ids, 0, (si->si_ofs.max_tchs + 1) * sizeof(int));
+       memset(&tch, 0, sizeof(struct cyttsp4_touch));
+       for (i = 0; i < num_cur_tch; i++) {
+               cyttsp4_get_touch(md, &tch, si->xy_data +
+                       (i * si->si_ofs.tch_rec_size));
+               if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs
+                       [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) ||
+                       (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs
+                       [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) {
+                       dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n",
+                               __func__, i, tch.abs[CY_TCH_T],
+                               md->pdata->frmwrk->abs[(CY_ABS_ID_OST *
+                               CY_NUM_ABS_SET) + CY_MAX_OST]);
+                       if (md->mt_function.input_sync)
+                               md->mt_function.input_sync(md->input);
+                       mt_sync_count++;
+                       continue;
+               }
+
+               /*
+                * if any touch is hover, then there is only one touch
+                * so it is OK to check the first touch for hover condition
+                */
+               if ((md->num_prv_tch == 0 && tch.abs[CY_TCH_O] != CY_OBJ_HOVER)
+                       || (md->prv_tch_type == CY_OBJ_HOVER
+                       && tch.abs[CY_TCH_O] != CY_OBJ_HOVER))
+                       input_report_key(md->input, BTN_TOUCH, CY_BTN_PRESSED);
+
+               /* use 0 based track id's */
+               sig = md->pdata->frmwrk->abs
+                       [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0];
+               if (sig != CY_IGNORE_VALUE) {
+                       t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs
+                               [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST];
+                       if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) {
+                               dev_dbg(dev, "%s: t=%d e=%d lift-off\n",
+                                       __func__, t, tch.abs[CY_TCH_E]);
+                               goto cyttsp4_get_mt_touches_pr_tch;
+                       }
+                       if (md->mt_function.input_report)
+                               md->mt_function.input_report(md->input, sig, t);
+                       ids[t] = true;
+               }
+
+               /* Check if hover on this touch */
+               dev_vdbg(dev, "%s: t=%d z=%d\n", __func__, t,
+                       tch.abs[CY_TCH_P]);
+               if (t == CY_ACTIVE_STYLUS_ID) {
+                       tch.abs[CY_TCH_P] = 0;
+                       dev_dbg(dev, "%s: t=%d z=%d force zero\n", __func__, t,
+                               tch.abs[CY_TCH_P]);
+               }
+
+               /* all devices: position and pressure fields */
+               for (j = 0; j <= CY_ABS_W_OST ; j++) {
+                       sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) *
+                               CY_NUM_ABS_SET) + 0];
+                       if (sig != CY_IGNORE_VALUE)
+                               input_report_abs(md->input, sig,
+                                       tch.abs[CY_TCH_X + j]);
+               }
+               if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+                       /*
+                        * TMA400 size and orientation fields:
+                        * if pressure is non-zero and major touch
+                        * signal is zero, then set major and minor touch
+                        * signals to minimum non-zero value
+                        */
+                       if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0)
+                               tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1;
+
+                       /* Get the extended touch fields */
+                       for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) {
+                               sig = md->pdata->frmwrk->abs
+                                       [((CY_ABS_MAJ_OST + j) *
+                                       CY_NUM_ABS_SET) + 0];
+                               if (sig != CY_IGNORE_VALUE)
+                                       input_report_abs(md->input, sig,
+                                               tch.abs[CY_TCH_MAJ + j]);
+                       }
+               }
+               if (md->mt_function.input_sync)
+                       md->mt_function.input_sync(md->input);
+               mt_sync_count++;
+
+cyttsp4_get_mt_touches_pr_tch:
+               if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE)
+                       dev_dbg(dev,
+                               "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n",
+                               __func__, t,
+                               tch.abs[CY_TCH_X],
+                               tch.abs[CY_TCH_Y],
+                               tch.abs[CY_TCH_P],
+                               tch.abs[CY_TCH_MAJ],
+                               tch.abs[CY_TCH_MIN],
+                               tch.abs[CY_TCH_OR],
+                               tch.abs[CY_TCH_E]);
+               else
+                       dev_dbg(dev,
+                               "%s: t=%d x=%d y=%d z=%d e=%d\n", __func__,
+                               t,
+                               tch.abs[CY_TCH_X],
+                               tch.abs[CY_TCH_Y],
+                               tch.abs[CY_TCH_P],
+                               tch.abs[CY_TCH_E]);
+       }
+
+       if (md->mt_function.final_sync)
+               md->mt_function.final_sync(md->input, si->si_ofs.max_tchs,
+                               mt_sync_count, ids);
+
+       md->num_prv_tch = num_cur_tch;
+       md->prv_tch_type = tch.abs[CY_TCH_O];
+
+       return;
+}
+
+/* read xy_data for all current touches */
+static int cyttsp4_xy_worker(struct cyttsp4_mt_data *md)
+{
+       struct device *dev = &md->ttsp->dev;
+       struct cyttsp4_sysinfo *si = md->si;
+       u8 num_cur_tch;
+       u8 hst_mode;
+       u8 rep_len;
+       u8 rep_stat;
+       u8 tt_stat;
+       int rc = 0;
+
+       /*
+        * Get event data from cyttsp4 device.
+        * The event data includes all data
+        * for all active touches.
+        * Event data also includes button data
+        */
+       /*
+        * Use 2 reads:
+        * 1st read to get mode + button bytes + touch count (core)
+        * 2nd read (optional) to get touch 1 - touch n data
+        */
+       hst_mode = si->xy_mode[CY_REG_BASE];
+       rep_len = si->xy_mode[si->si_ofs.rep_ofs];
+       rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1];
+       tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs];
+       dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__,
+               "hst_mode=", hst_mode, "rep_len=", rep_len,
+               "rep_stat=", rep_stat, "tt_stat=", tt_stat);
+
+       num_cur_tch = GET_NUM_TOUCHES(tt_stat);
+       dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch);
+
+       if (rep_len == 0 && num_cur_tch > 0) {
+               dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n",
+                       __func__, rep_len, num_cur_tch);
+               goto cyttsp4_xy_worker_exit;
+       }
+
+       /* read touches */
+       if (num_cur_tch > 0) {
+               rc = cyttsp4_read(md->ttsp, CY_MODE_OPERATIONAL,
+                       si->si_ofs.tt_stat_ofs + 1, si->xy_data,
+                       num_cur_tch * si->si_ofs.tch_rec_size);
+               if (rc < 0) {
+                       dev_err(dev, "%s: read fail on touch regs r=%d\n",
+                               __func__, rc);
+                       goto cyttsp4_xy_worker_exit;
+               }
+       }
+
+       /* print xy data */
+       cyttsp4_pr_buf(dev, md->pr_buf, si->xy_data, num_cur_tch *
+               si->si_ofs.tch_rec_size, "xy_data");
+
+       /* check any error conditions */
+       if (IS_BAD_PKT(rep_stat)) {
+               dev_dbg(dev, "%s: Invalid buffer detected\n", __func__);
+               rc = 0;
+               goto cyttsp4_xy_worker_exit;
+       } else if (IS_LARGE_AREA(tt_stat)) {
+               /* terminate all active tracks */
+               num_cur_tch = 0;
+               dev_dbg(dev, "%s: Large area detected\n", __func__);
+       } else if (num_cur_tch > si->si_ofs.max_tchs) {
+               if (num_cur_tch > max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)) {
+                       /* terminate all active tracks */
+                       dev_err(dev, "%s: Num touch err detected (n=%d)\n",
+                               __func__, num_cur_tch);
+                       num_cur_tch = 0;
+               } else {
+                       dev_err(dev, "%s: %s (n=%d c=%d)\n", __func__,
+                               "too many tch; set to max tch",
+                               num_cur_tch, si->si_ofs.max_tchs);
+                       num_cur_tch = si->si_ofs.max_tchs;
+               }
+       }
+
+       /* extract xy_data for all currently reported touches */
+       dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__,
+               num_cur_tch);
+       if (num_cur_tch)
+               cyttsp4_get_mt_touches(md, num_cur_tch);
+       else
+               cyttsp4_lift_all(md);
+
+       dev_vdbg(dev, "%s: done\n", __func__);
+       rc = 0;
+
+cyttsp4_xy_worker_exit:
+       return rc;
+}
+
+static int cyttsp4_mt_attention(struct cyttsp4_device *ttsp)
+{
+       struct device *dev = &ttsp->dev;
+       struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+       int rc = 0;
+
+       dev_vdbg(dev, "%s\n", __func__);
+
+       /* core handles handshake */
+       rc = cyttsp4_xy_worker(md);
+       if (rc < 0)
+               dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);
+
+       return rc;
+}
+
+static int cyttsp4_startup_attention(struct cyttsp4_device *ttsp)
+{
+       struct device *dev = &ttsp->dev;
+       struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+       int rc = 0;
+
+       dev_vdbg(dev, "%s\n", __func__);
+
+       cyttsp4_lift_all(md);
+       return rc;
+}
+
+static int cyttsp4_mt_open(struct input_dev *input)
+{
+       struct device *dev = input->dev.parent;
+       struct cyttsp4_device *ttsp =
+               container_of(dev, struct cyttsp4_device, dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       pm_runtime_get_sync(dev);
+
+       dev_vdbg(dev, "%s: setup subscriptions\n", __func__);
+
+       /* set up touch call back */
+       cyttsp4_subscribe_attention(ttsp, CY_ATTEN_IRQ,
+               cyttsp4_mt_attention, CY_MODE_OPERATIONAL);
+
+       /* set up startup call back */
+       cyttsp4_subscribe_attention(ttsp, CY_ATTEN_STARTUP,
+               cyttsp4_startup_attention, 0);
+
+       return 0;
+}
+
+static void cyttsp4_mt_close(struct input_dev *input)
+{
+       struct device *dev = input->dev.parent;
+       struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+       struct cyttsp4_device *ttsp =
+               container_of(dev, struct cyttsp4_device, dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       cyttsp4_lift_all(md);
+
+       cyttsp4_unsubscribe_attention(ttsp, CY_ATTEN_IRQ,
+               cyttsp4_mt_attention, CY_MODE_OPERATIONAL);
+
+       cyttsp4_unsubscribe_attention(ttsp, CY_ATTEN_STARTUP,
+               cyttsp4_startup_attention, 0);
+
+       pm_runtime_put(dev);
+}
+
+
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
+static int cyttsp4_mt_suspend(struct device *dev)
+{
+       struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       cyttsp4_lift_all(md);
+       return 0;
+}
+
+static int cyttsp4_mt_resume(struct device *dev)
+{
+       dev_dbg(dev, "%s\n", __func__);
+
+       return 0;
+}
+#endif
+
+const struct dev_pm_ops cyttsp4_mt_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_mt_suspend, cyttsp4_mt_resume)
+       SET_RUNTIME_PM_OPS(cyttsp4_mt_suspend, cyttsp4_mt_resume, NULL)
+};
+
+int cyttsp4_mt_release(struct cyttsp4_device *ttsp)
+{
+       struct device *dev = &ttsp->dev;
+       struct cyttsp4_mt_data *md = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+
+       input_unregister_device(md->input);
+
+       pm_runtime_suspend(dev);
+       pm_runtime_disable(dev);
+
+       dev_set_drvdata(dev, NULL);
+       kfree(md);
+       return 0;
+}
+
+static int cyttsp4_mt_probe(struct cyttsp4_device *ttsp)
+{
+       struct device *dev = &ttsp->dev;
+       struct cyttsp4_mt_data *md;
+       struct cyttsp4_mt_platform_data *pdata = dev_get_platdata(dev);
+       int signal = CY_IGNORE_VALUE;
+       int max_x, max_y, max_p, min, max;
+       int max_x_tmp, max_y_tmp;
+       int i;
+       int rc;
+
+       dev_info(dev, "%s\n", __func__);
+       dev_dbg(dev, "%s: debug on\n", __func__);
+       dev_vdbg(dev, "%s: verbose debug on\n", __func__);
+
+       md = kzalloc(sizeof(*md), GFP_KERNEL);
+       if (md == NULL) {
+               dev_err(dev, "%s: Error, kzalloc\n", __func__);
+               rc = -ENOMEM;
+               goto error_alloc_data_failed;
+       }
+
+       cyttsp4_init_function_ptrs(md);
+
+       md->prv_tch_type = CY_OBJ_STANDARD_FINGER;
+       md->ttsp = ttsp;
+       md->pdata = pdata;
+       dev_set_drvdata(dev, md);
+       /* Create the input device and register it. */
+       dev_vdbg(dev, "%s: Create the input device and register it\n",
+               __func__);
+       md->input = input_allocate_device();
+       if (md->input == NULL) {
+               dev_err(dev, "%s: Error, failed to allocate input device\n",
+                       __func__);
+               rc = -ENOSYS;
+               goto error_alloc_failed;
+       }
+
+       md->input->name = ttsp->name;
+       scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev));
+       md->input->phys = md->phys;
+       md->input->dev.parent = &md->ttsp->dev;
+       md->input->open = cyttsp4_mt_open;
+       md->input->close = cyttsp4_mt_close;
+       input_set_drvdata(md->input, md);
+
+       pm_runtime_enable(dev);
+
+       pm_runtime_get_sync(dev);
+       /* get sysinfo */
+       md->si = cyttsp4_request_sysinfo(ttsp);
+       pm_runtime_put(dev);
+
+       if (md->si == NULL) {
+               dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
+                       __func__, md->si);
+               rc = -ENODEV;
+               goto error_get_sysinfo;
+       }
+
+       dev_vdbg(dev, "%s: Initialize event signals\n", __func__);
+       __set_bit(EV_ABS, md->input->evbit);
+       __set_bit(EV_REL, md->input->evbit);
+       __set_bit(EV_KEY, md->input->evbit);
+       bitmap_fill(md->input->absbit, ABS_MAX);
+       __set_bit(BTN_TOUCH, md->input->keybit);
+
+       /* If virtualkeys enabled, don't use all screen */
+       if (md->pdata->flags & CY_FLAG_VKEYS) {
+               max_x_tmp = CY_VKEYS_X;
+               max_y_tmp = CY_VKEYS_Y;
+       } else {
+               max_x_tmp = md->si->si_ofs.max_x;
+               max_y_tmp = md->si->si_ofs.max_y;
+       }
+
+       /* get maximum values from the sysinfo data */
+       if (md->pdata->flags & CY_FLAG_FLIP) {
+               max_x = max_y_tmp - 1;
+               max_y = max_x_tmp - 1;
+       } else {
+               max_x = max_x_tmp - 1;
+               max_y = max_y_tmp - 1;
+       }
+       max_p = md->si->si_ofs.max_p;
+
+       /* set event signal capabilities */
+       for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) {
+               signal = md->pdata->frmwrk->abs
+                       [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST];
+               if (signal != CY_IGNORE_VALUE) {
+                       min = md->pdata->frmwrk->abs
+                               [(i * CY_NUM_ABS_SET) + CY_MIN_OST];
+                       max = md->pdata->frmwrk->abs
+                               [(i * CY_NUM_ABS_SET) + CY_MAX_OST];
+                       if (i == CY_ABS_ID_OST) {
+                               /* shift track ids down to start at 0 */
+                               max = max - min;
+                               min = min - min;
+                       } else if (i == CY_ABS_X_OST)
+                               max = max_x;
+                       else if (i == CY_ABS_Y_OST)
+                               max = max_y;
+                       else if (i == CY_ABS_P_OST)
+                               max = max_p;
+                       input_set_abs_params(md->input, signal, min, max,
+                               md->pdata->frmwrk->abs
+                               [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST],
+                               md->pdata->frmwrk->abs
+                               [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]);
+                       dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n",
+                               __func__, signal, min, max);
+                       if ((i == CY_ABS_ID_OST) &&
+                               (md->si->si_ofs.tch_rec_size <
+                               CY_TMA4XX_TCH_REC_SIZE))
+                               break;
+               }
+       }
+
+       rc = md->mt_function.input_register_device(md->input,
+                       md->si->si_ofs.max_tchs);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error, failed register input device r=%d\n",
+                       __func__, rc);
+               goto error_init_input;
+       }
+
+       dev_dbg(dev, "%s: OK\n", __func__);
+       return 0;
+
+error_init_input:
+       input_free_device(md->input);
+error_get_sysinfo:
+       pm_runtime_suspend(dev);
+       pm_runtime_disable(dev);
+       input_set_drvdata(md->input, NULL);
+error_alloc_failed:
+       kfree(md);
+error_alloc_data_failed:
+       dev_err(dev, "%s failed.\n", __func__);
+       return rc;
+}
+
+struct cyttsp4_driver cyttsp4_mt_driver = {
+       .probe = cyttsp4_mt_probe,
+       .remove = cyttsp4_mt_release,
+       .driver = {
+               .name = CYTTSP4_MT_NAME,
+               .bus = &cyttsp4_bus_type,
+               .pm = &cyttsp4_mt_pm_ops,
+       },
+};
+
diff --git a/drivers/input/touchscreen/cyttsp4_mt_common.h b/drivers/input/touchscreen/cyttsp4_mt_common.h
new file mode 100644
index 0000000..d363e9e0
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_mt_common.h
@@ -0,0 +1,76 @@ 
+/*
+ * cyttsp4_mt_common.h
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include <linux/cyttsp4_bus.h>
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/cyttsp4_core.h>
+#include <linux/cyttsp4_mt.h>
+#include "cyttsp4_regs.h"
+
+struct cyttsp4_mt_data;
+struct cyttsp4_mt_function {
+       int (*mt_release)(struct cyttsp4_device *ttsp);
+       int (*mt_probe)(struct cyttsp4_device *ttsp,
+                       struct cyttsp4_mt_data *md);
+       void (*report_slot_liftoff)(struct cyttsp4_mt_data *md);
+       void (*input_sync)(struct input_dev *input);
+       void (*input_report)(struct input_dev *input, int sig, int t);
+       void (*final_sync)(struct input_dev *input, int max_tchs,
+                       int mt_sync_count, int *ids);
+       int (*input_register_device)(struct input_dev *input, int max_tchs);
+};
+
+struct cyttsp4_mt_data {
+       struct cyttsp4_device *ttsp;
+       struct cyttsp4_mt_platform_data *pdata;
+       struct cyttsp4_sysinfo *si;
+       struct input_dev *input;
+       struct cyttsp4_mt_function mt_function;
+       char phys[NAME_MAX];
+       int num_prv_tch;
+       int prv_tch_type;
+#ifdef VERBOSE_DEBUG
+       u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+};
+
+extern void cyttsp4_init_function_ptrs(struct cyttsp4_mt_data *md);
+extern struct cyttsp4_driver cyttsp4_mt_driver;
+
diff --git a/drivers/input/touchscreen/cyttsp4_mtb.c b/drivers/input/touchscreen/cyttsp4_mtb.c
new file mode 100644
index 0000000..7a0b7b1
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_mtb.c
@@ -0,0 +1,107 @@ 
+/*
+ * cyttsp4_mtb.c
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/input/mt.h>
+
+#include "cyttsp4_mt_common.h"
+
+static void cyttsp4_final_sync(struct input_dev *input, int max_tchs,
+               int mt_sync_count, int *ids)
+{
+       int t;
+
+       for (t = 0; t < max_tchs + 1; t++) {
+               if (ids[t])
+                       continue;
+               input_mt_slot(input, t);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+       }
+
+       input_sync(input);
+}
+
+static void cyttsp4_input_report(struct input_dev *input, int sig, int t)
+{
+       input_mt_slot(input, t);
+       input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+}
+
+static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md)
+{
+       struct cyttsp4_sysinfo *si = md->si;
+       int t;
+
+       if (md->num_prv_tch == 0)
+               return;
+
+       for (t = 0; t < si->si_ofs.max_tchs + 1; t++) {
+               input_mt_slot(md->input, t);
+               input_mt_report_slot_state(md->input,
+                       MT_TOOL_FINGER, false);
+       }
+}
+
+static int cyttsp4_input_register_device(struct input_dev *input, int max_tchs)
+{
+       /* max num slots equals max touches + 1 for hover */
+       input_mt_init_slots(input, max_tchs + 1);
+       return input_register_device(input);
+}
+
+void cyttsp4_init_function_ptrs(struct cyttsp4_mt_data *md)
+{
+       md->mt_function.report_slot_liftoff = cyttsp4_report_slot_liftoff;
+       md->mt_function.final_sync = cyttsp4_final_sync;
+       md->mt_function.input_sync = NULL;
+       md->mt_function.input_report = cyttsp4_input_report;
+       md->mt_function.input_register_device = cyttsp4_input_register_device;
+}
+
+static int __init cyttsp4_mt_init(void)
+{
+       int rc;
+       cyttsp4_mt_driver.driver.owner = THIS_MODULE;
+       rc = cyttsp4_register_driver(&cyttsp4_mt_driver);
+       pr_info("%s: Cypress TTSP MT v4 multi-touch (Built %s @ %s), rc=%d\n",
+                __func__, __DATE__, __TIME__, rc);
+       return rc;
+}
+module_init(cyttsp4_mt_init);
+
+static void __exit cyttsp4_mt_exit(void)
+{
+       cyttsp4_unregister_driver(&cyttsp4_mt_driver);
+       pr_info("%s: module exit\n", __func__);
+}
+module_exit(cyttsp4_mt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard 2D multi-touch driver");
+MODULE_AUTHOR("Cypress Semiconductor");
diff --git a/include/linux/cyttsp4_mt.h b/include/linux/cyttsp4_mt.h
new file mode 100644
index 0000000..4339544
--- /dev/null
+++ b/include/linux/cyttsp4_mt.h
@@ -0,0 +1,72 @@ 
+/*
+ * cyttsp4_mt.h
+ * Cypress TrueTouch(TM) Standard Product V4 Multi-touch module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
+ *
+ */
+
+#ifndef _LINUX_CYTTSP4_MT_H
+#define _LINUX_CYTTSP4_MT_H
+
+#define CYTTSP4_MT_NAME "cyttsp4_mt"
+
+/* abs settings */
+#define CY_IGNORE_VALUE             0xFFFF
+/* abs signal capabilities offsets in the frameworks array */
+enum cyttsp4_sig_caps {
+       CY_SIGNAL_OST,
+       CY_MIN_OST,
+       CY_MAX_OST,
+       CY_FUZZ_OST,
+       CY_FLAT_OST,
+       CY_NUM_ABS_SET  /* number of signal capability fields */
+};
+
+/* abs axis signal offsets in the framworks array  */
+enum cyttsp4_sig_ost {
+       CY_ABS_X_OST,
+       CY_ABS_Y_OST,
+       CY_ABS_P_OST,
+       CY_ABS_W_OST,
+       CY_ABS_ID_OST,
+       CY_ABS_MAJ_OST,
+       CY_ABS_MIN_OST,
+       CY_ABS_OR_OST,
+       CY_NUM_ABS_OST  /* number of abs signals */
+};
+
+struct touch_framework {
+       const uint16_t  *abs;
+       uint8_t         size;
+       uint8_t         enable_vkeys;
+} __packed;
+
+struct cyttsp4_mt_platform_data {
+       struct touch_framework *frmwrk;
+       unsigned short flags;
+       char const *inp_dev_name;
+};
+
+#endif /* _LINUX_CYTTSP4_MT_H */