linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices
       [not found] <Ferruh Yigit <fery@cypress.com>
@ 2012-08-07 13:09 ` Ferruh Yigit
  2012-08-07 20:05   ` Javier Martinez Canillas
  2012-08-24 13:06   ` Ferruh Yigit
  2012-08-07 13:09 ` [PATCH 2/4] Input: cyttsp4 - core " Ferruh Yigit
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-08-07 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Ferruh YIGIT, Javier Martinez Canillas,
	Henrik Rydberg, Shawn Landden, Ashish Jangam, Olivier Sobrie,
	linux-input, linux-kernel

From: Ferruh YIGIT <fery@cypress.com>

This driver is for Cypress TrueTouch(tm) Standard Product controllers,
Generation4 devices.

Driver consist of four main modules:

Bus driver: Linux bus driver implementation, binds other modules.
Core driver: Core module that communicate with TTSP controller.
MT driver: MultiTouch driver, converts touch information to host specific
touch events
Adapter driver: Communication adapter between host and controller, like
I2C or SPI.

This is Cyttsp4 TTSP Bus Driver,
Provides binding between Adapter, Core, and TTSP Modules.

A complete set of corresponding Adapter, Core, and TTSP module
devices and drivers must be registered with the TTSP Bus handler

Signed-off-by: Ferruh YIGIT <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig       |    9 +
 drivers/input/touchscreen/Makefile      |    1 +
 drivers/input/touchscreen/cyttsp4_bus.c |  608 +++++++++++++++++++++++++++++++
 include/linux/cyttsp4_bus.h             |  271 ++++++++++++++
 4 files changed, 889 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_bus.c
 create mode 100644 include/linux/cyttsp4_bus.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ba232c..4a65736 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -179,6 +179,15 @@ config TOUCHSCREEN_CYTTSP_SPI
          To compile this driver as a module, choose M here: the
          module will be called cyttsp_spi.

+config CYPRESS_CYTTSP4_BUS
+       bool "Cypress TTSP core bus"
+       default n
+       help
+         This option enables support Cypress TTSP core bus.
+         This support is needed for various device and drivers
+         using Cypress TrueTouch(TM) Standard Product
+         protocol.
+
 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 178eb12..ab84aec 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)    += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
+obj-$(CONFIG_CYPRESS_CYTTSP4_BUS) += cyttsp4_bus.o
diff --git a/drivers/input/touchscreen/cyttsp4_bus.c b/drivers/input/touchscreen/cyttsp4_bus.c
new file mode 100644
index 0000000..2e97088
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_bus.c
@@ -0,0 +1,608 @@
+/*
+ * cyttsp4_bus.c
+ * Cypress TrueTouch(TM) Standard Product V4 Bus Driver.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * Author: Aleksej Makarov aleksej.makarov@sonyericsson.com
+ * Modified by: Cypress Semiconductor for complete set of TTSP Bus interfaces.
+ *
+ * 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/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/limits.h>
+
+static DEFINE_MUTEX(core_lock);
+static LIST_HEAD(adapter_list);
+static LIST_HEAD(core_dev_list);
+static LIST_HEAD(cyttsp4_dev_list);
+
+struct bus_type cyttsp4_bus_type;
+
+static void cyttsp4_dev_release(struct device *dev)
+{
+       dev_vdbg(dev, "%s: Enter\n", __func__);
+       put_device(dev->parent);
+}
+
+static struct device_type cyttsp4_dev_type = {
+       .release = cyttsp4_dev_release
+};
+
+static struct device_type cyttsp4_core_type = {
+       .release = cyttsp4_dev_release
+};
+
+static int _cyttsp4_register_dev(struct cyttsp4_device *pdev,
+               struct cyttsp4_core *core)
+{
+       int ret;
+
+       if (!pdev->dev.parent)
+               pdev->dev.parent = get_device(&core->dev);
+       /* Assign (new) core */
+       pdev->core = core;
+       /* Check whether this device is registered before */
+       if (pdev->dev.bus == &cyttsp4_bus_type &&
+                       pdev->dev.type == &cyttsp4_dev_type)
+               return 0;
+
+       pdev->dev.bus = &cyttsp4_bus_type;
+       pdev->dev.type = &cyttsp4_dev_type;
+       dev_set_name(&pdev->dev, "%s.%s", pdev->name,  core->id);
+
+       ret = device_register(&pdev->dev);
+       dev_dbg(&pdev->dev,
+               "%s: Registering device '%s'. Parent at '%s', err = %d\n",
+                __func__, dev_name(&pdev->dev),
+                dev_name(pdev->dev.parent), ret);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to register device, err %d\n",
+                       __func__, ret);
+               pdev->dev.bus = NULL;
+               pdev->dev.type = NULL;
+               pdev->core = NULL;
+       }
+       return ret;
+}
+
+static int _cyttsp4_register_core(struct cyttsp4_core *pdev,
+               struct cyttsp4_adapter *adap)
+{
+       int ret;
+
+       if (!pdev->dev.parent)
+               pdev->dev.parent = get_device(adap->dev);
+       /* Assign (new) adapter */
+       pdev->adap = adap;
+       /* Check whether this core is registered before */
+       if (pdev->dev.bus == &cyttsp4_bus_type &&
+                       pdev->dev.type == &cyttsp4_core_type)
+               return 0;
+
+       pdev->dev.bus = &cyttsp4_bus_type;
+       pdev->dev.type = &cyttsp4_core_type;
+       dev_set_name(&pdev->dev, "%s.%s", pdev->id,  adap->id);
+
+       ret = device_register(&pdev->dev);
+       dev_dbg(&pdev->dev,
+               "%s: Registering device '%s'. Parent at '%s', err = %d\n",
+                __func__, dev_name(&pdev->dev),
+                dev_name(pdev->dev.parent), ret);
+       if (ret) {
+               dev_err(&pdev->dev, "%s: failed to register device, err %d\n",
+                       __func__, ret);
+               pdev->dev.bus = NULL;
+               pdev->dev.type = NULL;
+               pdev->adap = NULL;
+       }
+       return ret;
+}
+
+static struct cyttsp4_adapter *find_adapter(char const *adap_id)
+{
+       struct cyttsp4_adapter *a;
+
+       list_for_each_entry(a, &adapter_list, node)
+               if (!strncmp(a->id, adap_id, NAME_MAX))
+                       return a;
+       return NULL;
+}
+
+static struct cyttsp4_core *find_core(char const *core_id)
+{
+       struct cyttsp4_core *d;
+
+       list_for_each_entry(d, &core_dev_list, node)
+               if (!strncmp(d->id, core_id, NAME_MAX) && d->dev.driver)
+                       return d;
+       return NULL;
+}
+
+static void rescan_devices(struct cyttsp4_core *core)
+{
+       struct cyttsp4_device *d;
+
+       list_for_each_entry(d, &cyttsp4_dev_list, node)
+               if (!d->core && !strncmp(core->id, d->core_id, NAME_MAX))
+                       _cyttsp4_register_dev(d, core);
+}
+
+static void rescan_cores(struct cyttsp4_adapter *adap)
+{
+       struct cyttsp4_core *d;
+
+       list_for_each_entry(d, &core_dev_list, node)
+               if (!d->adap && !strncmp(adap->id, d->adap_id, NAME_MAX))
+                       _cyttsp4_register_core(d, adap);
+}
+
+int cyttsp4_register_device(struct cyttsp4_device *pdev)
+{
+       int ret = 0;
+       struct cyttsp4_core *core;
+
+       if (!pdev)
+               return -EINVAL;
+       mutex_lock(&core_lock);
+       list_add(&pdev->node, &cyttsp4_dev_list);
+       pr_debug("%s: '%s' added to cyttsp4_dev_list\n", __func__, pdev->name);
+       core = find_core(pdev->core_id);
+       if (core)
+               ret = _cyttsp4_register_dev(pdev, core);
+       mutex_unlock(&core_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_device);
+
+static int cyttsp4_match_dev(struct device *dev, void *data)
+{
+       return dev == (struct device *)data;
+}
+
+void cyttsp4_unregister_device(struct cyttsp4_device *pdev)
+{
+       if (!pdev)
+               return;
+       mutex_lock(&core_lock);
+       if (bus_find_device(&cyttsp4_bus_type, NULL, &pdev->dev,
+                       cyttsp4_match_dev)) {
+               dev_dbg(&pdev->dev, "%s: Unregistering device '%s'.\n",
+                       __func__, dev_name(&pdev->dev));
+               /* Put reference taken by bus_find_device() */
+               put_device(&pdev->dev);
+               device_unregister(&pdev->dev);
+       }
+       list_del(&pdev->node);
+       pr_debug("%s: '%s' removed from cyttsp4_dev_list\n", __func__,
+               pdev->name);
+       mutex_unlock(&core_lock);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_unregister_device);
+
+int cyttsp4_register_core_device(struct cyttsp4_core *pdev)
+{
+       int ret = 0;
+       struct cyttsp4_adapter *adap;
+
+       if (!pdev)
+               return -EINVAL;
+       mutex_lock(&core_lock);
+       if (find_core(pdev->id)) {
+               pr_debug("%s: core id '%s' already exists\n",
+                               __func__, pdev->id);
+               ret = -EINVAL;
+               goto fail;
+       }
+       list_add(&pdev->node, &core_dev_list);
+       pr_debug("%s: '%s' added to core_dev_list\n", __func__, pdev->name);
+       adap = find_adapter(pdev->adap_id);
+       if (adap) {
+               pr_debug("%s: adapter for '%s' is '%s'\n", __func__,
+                               pdev->id, dev_name(adap->dev));
+               ret = _cyttsp4_register_core(pdev, adap);
+               if (!ret)
+                       rescan_devices(pdev);
+       }
+fail:
+       mutex_unlock(&core_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_core_device);
+
+int cyttsp4_add_adapter(char const *id, struct cyttsp4_ops const *ops,
+               struct device *parent)
+{
+       int rc = 0;
+       struct cyttsp4_adapter *a;
+
+       if (!parent) {
+               dev_err(parent, "%s: need parent for '%s'\n", __func__, id);
+               return -EINVAL;
+       }
+       mutex_lock(&core_lock);
+       if (find_adapter(id)) {
+               dev_err(parent, "%s: adapter '%s' already exists\n",
+                               __func__, id);
+               rc = -EINVAL;
+               goto fail;
+       }
+       a = kzalloc(sizeof(*a), GFP_KERNEL);
+       if (!a) {
+               dev_err(parent, "%s: failed to allocate adapter '%s'\n",
+                               __func__, id);
+               rc = -ENOMEM;
+               goto fail;
+       }
+       memcpy(a->id, id, sizeof(a->id));
+       a->id[sizeof(a->id) - 1] = 0;
+       a->read = ops->read;
+       a->write = ops->write;
+       a->dev = parent;
+       list_add(&a->node, &adapter_list);
+       dev_dbg(parent, "%s: '%s' added to adapter_list\n", __func__, id);
+       rescan_cores(a);
+fail:
+       mutex_unlock(&core_lock);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_add_adapter);
+
+int cyttsp4_del_adapter(char const *id)
+{
+       int rc = 0;
+       struct cyttsp4_adapter *adap;
+       struct cyttsp4_core *core_dev;
+
+       mutex_lock(&core_lock);
+       adap = find_adapter(id);
+       if (!adap) {
+               pr_err("%s: adapter '%s' does not exist\n",
+                       __func__, id);
+               rc = -EINVAL;
+               goto fail;
+       }
+
+       list_for_each_entry(core_dev, &core_dev_list, node)
+               if (core_dev->adap == adap)
+                       core_dev->adap = NULL;
+
+       list_del(&adap->node);
+       kfree(adap);
+       pr_debug("%s: '%s' removed from adapter_list\n", __func__, id);
+fail:
+       mutex_unlock(&core_lock);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_del_adapter);
+
+static struct cyttsp4_device *verify_device_type(struct device *dev)
+{
+       return dev->type == &cyttsp4_dev_type ? to_cyttsp4_device(dev) : NULL;
+}
+
+static struct cyttsp4_core *verify_core_type(struct device *dev)
+{
+       return dev->type == &cyttsp4_core_type ? to_cyttsp4_core(dev) : NULL;
+}
+
+static int cyttsp4_match_device(struct cyttsp4_device *dev, const char *name)
+{
+       return strncmp(dev->name, name, NAME_MAX) == 0;
+}
+
+static int cyttsp4_match_core_device(struct cyttsp4_core *core,
+               const char *name)
+{
+       return strncmp(core->name, name, NAME_MAX) == 0;
+}
+
+static int cyttsp4_device_match(struct device *dev, struct device_driver *drv)
+{
+       struct cyttsp4_device *cyttsp4_dev = verify_device_type(dev);
+       struct cyttsp4_core *cyttsp4_core;
+       int match;
+
+       if (cyttsp4_dev) {
+               match = cyttsp4_match_device(cyttsp4_dev, drv->name);
+               goto exit;
+       }
+       cyttsp4_core = verify_core_type(dev);
+       if (cyttsp4_core) {
+               match = cyttsp4_match_core_device(cyttsp4_core, drv->name);
+               goto exit;
+       }
+       match = 0;
+exit:
+       dev_dbg(dev, "%s: %s matching '%s' driver\n", __func__,
+                       match ? "is" : "isn't", drv->name);
+       return match;
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+                            char *buf)
+{
+       struct cyttsp4_device *cyttsp4_dev = verify_device_type(dev);
+       struct cyttsp4_core *cyttsp4_core;
+
+       char const *name;
+       int len;
+
+       if (cyttsp4_dev) {
+               name = cyttsp4_dev->name;
+               goto exit;
+       }
+       cyttsp4_core = verify_core_type(dev);
+       if (cyttsp4_core) {
+               name = cyttsp4_core->id;
+               goto exit;
+       }
+       name = "none";
+exit:
+       len = snprintf(buf, PAGE_SIZE, "ttsp4:%s\n", name);
+       return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute cyttsp4_dev_attrs[] = {
+       __ATTR_RO(modalias),
+       __ATTR_NULL,
+};
+
+#ifdef CONFIG_SUSPEND
+static int cyttsp4_pm_suspend(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+
+       dev_dbg(dev, "%s\n", __func__);
+       if (drv && drv->pm && drv->pm->suspend)
+               return drv->pm->suspend(dev);
+       return 0;
+}
+
+static int cyttsp4_pm_resume(struct device *dev)
+{
+       struct device_driver *drv = dev->driver;
+
+       dev_dbg(dev, "%s\n", __func__);
+       if (drv && drv->pm && drv->pm->resume)
+               return drv->pm->suspend(dev);
+       return 0;
+}
+#else /* !CONFIG_SUSPEND */
+#define cyttsp4_pm_suspend             NULL
+#define cyttsp4_pm_resume              NULL
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_PM_RUNTIME
+#define cyttsp4_pm_rt_suspend          pm_generic_runtime_suspend
+#define cyytsp4_pm_rt_resume           pm_generic_runtime_resume
+#define cyytsp4_pm_rt_idle             pm_generic_runtime_idle
+#else /* !CONFIG_PM_RUNTIME */
+#define cyttsp4_pm_rt_suspend          NULL
+#define cyytsp4_pm_rt_resume           NULL
+#define cyytsp4_pm_rt_idle             NULL
+#endif /* !CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops cyttsp4_dev_pm_ops = {
+       .suspend = cyttsp4_pm_suspend,
+       .resume = cyttsp4_pm_resume,
+       .runtime_suspend = cyttsp4_pm_rt_suspend,
+       .runtime_resume = cyytsp4_pm_rt_resume,
+       .runtime_idle = cyytsp4_pm_rt_idle,
+};
+
+struct bus_type cyttsp4_bus_type = {
+       .name           = "ttsp4",
+       .dev_attrs      = cyttsp4_dev_attrs,
+       .match          = cyttsp4_device_match,
+       .uevent         = NULL,
+       .pm             = &cyttsp4_dev_pm_ops,
+};
+EXPORT_SYMBOL_GPL(cyttsp4_bus_type);
+
+static int cyttsp4_drv_remove(struct device *_dev)
+{
+       struct cyttsp4_driver *drv = to_cyttsp4_driver(_dev->driver);
+       struct cyttsp4_device *dev = to_cyttsp4_device(_dev);
+       return drv->remove(dev);
+}
+
+static int cyttsp4_core_drv_remove(struct device *_dev)
+{
+       struct cyttsp4_core_driver *drv = to_cyttsp4_core_driver(_dev->driver);
+       struct cyttsp4_core *dev = to_cyttsp4_core(_dev);
+       return drv->remove(dev);
+}
+
+static int cyttsp4_drv_probe(struct device *_dev)
+{
+       int rc = -ENODEV;
+       struct cyttsp4_driver *drv = to_cyttsp4_driver(_dev->driver);
+       struct cyttsp4_device *dev = to_cyttsp4_device(_dev);
+
+       rc = drv->probe(dev);
+       dev_dbg(_dev, "%s: for %s = %d\n", __func__, dev->name, rc);
+       return rc;
+}
+
+static int cyttsp4_core_drv_probe(struct device *_dev)
+{
+       int rc = -ENODEV;
+       struct cyttsp4_core_driver *drv = to_cyttsp4_core_driver(_dev->driver);
+       struct cyttsp4_core *dev = to_cyttsp4_core(_dev);
+
+       rc = drv->probe(dev);
+       dev_dbg(_dev, "%s: for %s = %d\n", __func__, dev->name, rc);
+       if (!rc)
+               rescan_devices(dev);
+       return rc;
+}
+
+int cyttsp4_register_driver(struct cyttsp4_driver *drv)
+{
+       int ret = 0;
+
+#ifdef CONFIG_MODULES
+       struct cyttsp4_device *d;
+
+       /*
+        * We need to ensure that the driver of this device's
+        * core device should exist (dependency)
+        * To do so, we traverse through the device, its core
+        * device and the driver of its core device, which requires
+        * the device itself should be registered with the system
+        */
+       mutex_lock(&core_lock);
+       list_for_each_entry(d, &cyttsp4_dev_list, node) {
+               if (!cyttsp4_match_device(d, drv->driver.name))
+                       continue;
+               if (d->core) {
+                       if (d->core->dev.driver)
+                               ret = ref_module(drv->driver.owner,
+                                       d->core->dev.driver->owner);
+                       else
+                               /* Core device exists but not core driver */
+                               ret = -ENODEV;
+               }
+               break;
+       }
+       mutex_unlock(&core_lock);
+
+       if (ret) {
+               if (ret == -ENODEV)
+                       pr_err("%s: Core driver module does not exist\n",
+                               __func__);
+               else
+                       pr_err("%s: Error getting ref to core driver module\n",
+                               __func__);
+               goto fail;
+       }
+#endif
+
+       drv->driver.bus = &cyttsp4_bus_type;
+       if (drv->probe)
+               drv->driver.probe = cyttsp4_drv_probe;
+       if (drv->remove)
+               drv->driver.remove = cyttsp4_drv_remove;
+       ret = driver_register(&drv->driver);
+fail:
+       pr_debug("%s: '%s' returned %d\n", __func__, drv->driver.name, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_driver);
+
+int cyttsp4_register_core_driver(struct cyttsp4_core_driver *drv)
+{
+       int ret = 0;
+
+#ifdef CONFIG_MODULES
+       struct cyttsp4_core *d;
+
+       /*
+        * We need to ensure that the driver of this core device's
+        * adapter should exist (dependency)
+        * To do so, we traverse through the core device, its adapter
+        * and the driver of its adapter, which requires the core
+        * device itself should be registered with the system
+        */
+       mutex_lock(&core_lock);
+       list_for_each_entry(d, &core_dev_list, node) {
+               if (!cyttsp4_match_core_device(d, drv->driver.name))
+                       continue;
+               if (d->adap) {
+                       if (d->adap->dev && d->adap->dev->driver) {
+                               ret = ref_module(drv->driver.owner,
+                                       d->adap->dev->driver->owner);
+                       } else {
+                               /* Core dev exist but not adap device
+                                * Do not let until adap module inserted */
+                               ret = -ENODEV;
+                       }
+               }
+               break;
+       }
+       mutex_unlock(&core_lock);
+
+       if (ret) {
+               if (ret == -ENODEV)
+                       pr_err("%s: Adapter driver module does not exist\n",
+                               __func__);
+               else
+                       pr_err("%s: Error getting ref to adapter driver module\n",
+                               __func__);
+               goto fail;
+       }
+#endif
+
+       drv->driver.bus = &cyttsp4_bus_type;
+       if (drv->probe)
+               drv->driver.probe = cyttsp4_core_drv_probe;
+       if (drv->remove)
+               drv->driver.remove = cyttsp4_core_drv_remove;
+       ret = driver_register(&drv->driver);
+fail:
+       pr_debug("%s: '%s' returned %d\n", __func__, drv->driver.name, ret);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_register_core_driver);
+
+void cyttsp4_unregister_driver(struct cyttsp4_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_unregister_driver);
+
+void cyttsp4_unregister_core_driver(struct cyttsp4_core_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_unregister_core_driver);
+
+int __init cyttsp4_bus_init(void)
+{
+       int error;
+       error =  bus_register(&cyttsp4_bus_type);
+       if (error)
+               pr_err("%s: error %d\n", __func__, error);
+       else
+               pr_debug("%s: ok\n", __func__);
+       return error;
+}
+
+static void __exit cyttsp4_bus_exit(void)
+{
+       pr_debug("%s: ok\n", __func__);
+}
+
+subsys_initcall(cyttsp4_bus_init);
+module_exit(cyttsp4_bus_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Aleksej Makarov <aleksej.makarov@sonyericsson.com>");
diff --git a/include/linux/cyttsp4_bus.h b/include/linux/cyttsp4_bus.h
new file mode 100644
index 0000000..b1f64ef
--- /dev/null
+++ b/include/linux/cyttsp4_bus.h
@@ -0,0 +1,271 @@
+/*
+ * cyttsp4_bus.h
+ * Cypress TrueTouch(TM) Standard Product V4 Bus Driver.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * Author: Aleksej Makarov <aleksej.makarov@sonyericsson.com>
+ * Modified by: Cypress Semiconductor to add device functions
+ *
+ * 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_BUS_H
+#define _LINUX_CYTTSP4_BUS_H
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/limits.h>
+
+
+extern struct bus_type cyttsp4_bus_type;
+
+struct cyttsp4_driver;
+struct cyttsp4_device;
+struct cyttsp4_adapter;
+
+enum cyttsp4_atten_type {
+       CY_ATTEN_IRQ,
+       CY_ATTEN_STARTUP,
+       CY_ATTEN_EXCLUSIVE,
+       CY_ATTEN_NUM_ATTEN,
+};
+
+typedef int (*cyttsp4_atten_func) (struct cyttsp4_device *);
+
+struct cyttsp4_ops {
+       int (*write)(struct cyttsp4_adapter *dev, u8 addr,
+               const void *buf, int size);
+       int (*read)(struct cyttsp4_adapter *dev, u8 addr, void *buf, int size);
+};
+
+struct cyttsp4_adapter {
+       struct list_head node;
+       char id[NAME_MAX];
+       struct device *dev;
+       int (*write)(struct cyttsp4_adapter *dev, u8 addr,
+               const void *buf, int size);
+       int (*read)(struct cyttsp4_adapter *dev, u8 addr, void *buf, int size);
+};
+#define to_cyttsp4_adapter(d) container_of(d, struct cyttsp4_adapter, dev)
+
+struct cyttsp4_core {
+       struct list_head node;
+       char const *name;
+       char const *id;
+       char const *adap_id;
+       struct device dev;
+       struct cyttsp4_adapter *adap;
+};
+#define to_cyttsp4_core(d) container_of(d, struct cyttsp4_core, dev)
+
+struct cyttsp4_device {
+       struct list_head node;
+       char const *name;
+       char const *core_id;
+       struct device dev;
+       struct cyttsp4_core *core;
+};
+#define to_cyttsp4_device(d) container_of(d, struct cyttsp4_device, dev)
+
+struct cyttsp4_core_driver {
+       struct device_driver driver;
+       int (*probe)(struct cyttsp4_core *core);
+       int (*remove)(struct cyttsp4_core *core);
+       int (*subscribe_attention)(struct cyttsp4_device *ttsp,
+                               enum cyttsp4_atten_type type,
+                               cyttsp4_atten_func func,
+                               int flags);
+       int (*unsubscribe_attention)(struct cyttsp4_device *ttsp,
+                               enum cyttsp4_atten_type type,
+                               cyttsp4_atten_func func,
+                               int flags);
+       int (*request_exclusive)(struct cyttsp4_device *ttsp, int t);
+       int (*release_exclusive)(struct cyttsp4_device *ttsp);
+       int (*request_reset)(struct cyttsp4_device *ttsp);
+       int (*request_restart)(struct cyttsp4_device *ttsp);
+       int (*request_set_mode)(struct cyttsp4_device *ttsp, int mode);
+       struct cyttsp4_sysinfo *(*request_sysinfo)(struct cyttsp4_device *ttsp);
+       int (*request_handshake)(struct cyttsp4_device *ttsp, u8 mode);
+       int (*request_exec_cmd)(struct cyttsp4_device *ttsp, u8 mode,
+                       u8 *cmd_buf, size_t cmd_size, u8 *return_buf,
+                       size_t return_buf_size, int timeout);
+       int (*request_stop_wd)(struct cyttsp4_device *ttsp);
+       int (*request_toggle_lowpower)(struct cyttsp4_device *ttsp, u8 mode);
+       int (*write)(struct cyttsp4_device *ttsp, int mode,
+               u8 addr, const void *buf, int size);
+       int (*read)(struct cyttsp4_device *ttsp, int mode,
+               u8 addr, void *buf, int size);
+};
+#define to_cyttsp4_core_driver(d) \
+       container_of(d, struct cyttsp4_core_driver, driver)
+
+struct cyttsp4_driver {
+       struct device_driver driver;
+       int (*probe)(struct cyttsp4_device *dev);
+       int (*remove)(struct cyttsp4_device *fev);
+};
+#define to_cyttsp4_driver(d) container_of(d, struct cyttsp4_driver, driver)
+
+extern int cyttsp4_register_driver(struct cyttsp4_driver *drv);
+extern void cyttsp4_unregister_driver(struct cyttsp4_driver *drv);
+
+extern int cyttsp4_register_core_driver(struct cyttsp4_core_driver *drv);
+extern void cyttsp4_unregister_core_driver(struct cyttsp4_core_driver *drv);
+
+extern int cyttsp4_register_device(struct cyttsp4_device *pdev);
+extern void cyttsp4_unregister_device(struct cyttsp4_device *pdev);
+
+extern int cyttsp4_register_core_device(struct cyttsp4_core *pdev);
+
+extern int cyttsp4_add_adapter(char const *id, struct cyttsp4_ops const *ops,
+               struct device *parent);
+
+extern int cyttsp4_del_adapter(char const *id);
+
+static inline int cyttsp4_read(struct cyttsp4_device *ttsp, int mode, u8 addr,
+               void *buf, int size)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->read(ttsp, mode, addr, buf, size);
+}
+
+static inline int cyttsp4_write(struct cyttsp4_device *ttsp, int mode, u8 addr,
+               const void *buf, int size)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->write(ttsp, mode, addr, buf, size);
+}
+
+static inline int cyttsp4_adap_read(struct cyttsp4_adapter *adap, u8 addr,
+               void *buf, int size)
+{
+       return adap->read(adap, addr, buf, size);
+}
+
+static inline int cyttsp4_adap_write(struct cyttsp4_adapter *adap, u8 addr,
+               const void *buf, int size)
+{
+       return adap->write(adap, addr, buf, size);
+}
+
+static inline int cyttsp4_subscribe_attention(struct cyttsp4_device *ttsp,
+               enum cyttsp4_atten_type type, cyttsp4_atten_func func,
+               int flags)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->subscribe_attention(ttsp, type, func, flags);
+}
+
+static inline int cyttsp4_unsubscribe_attention(struct cyttsp4_device *ttsp,
+               enum cyttsp4_atten_type type, cyttsp4_atten_func func,
+               int flags)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->unsubscribe_attention(ttsp, type, func, flags);
+}
+
+static inline int cyttsp4_request_exclusive(struct cyttsp4_device *ttsp, int t)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_exclusive(ttsp, t);
+}
+
+static inline int cyttsp4_release_exclusive(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->release_exclusive(ttsp);
+}
+
+static inline int cyttsp4_request_reset(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_reset(ttsp);
+}
+
+static inline int cyttsp4_request_restart(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_restart(ttsp);
+}
+
+static inline int cyttsp4_request_set_mode(struct cyttsp4_device *ttsp,
+               int mode)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_set_mode(ttsp, mode);
+}
+
+static inline struct cyttsp4_sysinfo *cyttsp4_request_sysinfo(
+               struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_sysinfo(ttsp);
+}
+
+static inline int cyttsp4_request_handshake(struct cyttsp4_device *ttsp,
+               u8 mode)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_handshake(ttsp, mode);
+}
+
+static inline int cyttsp4_request_exec_cmd(struct cyttsp4_device *ttsp,
+               u8 mode, u8 *cmd_buf, size_t cmd_size, u8 *return_buf,
+               size_t return_buf_size, int timeout)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_exec_cmd(ttsp, mode, cmd_buf, cmd_size, return_buf,
+                       return_buf_size, timeout);
+}
+
+static inline int cyttsp4_request_stop_wd(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_stop_wd(ttsp);
+}
+
+static inline int cyttsp4_request_toggle_lowpower(struct cyttsp4_device *ttsp,
+               u8 mode)
+{
+       struct cyttsp4_core *cd = ttsp->core;
+       struct cyttsp4_core_driver *d = to_cyttsp4_core_driver(cd->dev.driver);
+       return d->request_toggle_lowpower(ttsp, mode);
+}
+
+#endif /* _LINUX_CYTTSP4_BUS_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.

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

* [PATCH 2/4] Input: cyttsp4 - core driver for Cypress TMA4XX touchscreen devices
       [not found] <Ferruh Yigit <fery@cypress.com>
  2012-08-07 13:09 ` [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices Ferruh Yigit
@ 2012-08-07 13:09 ` Ferruh Yigit
  2012-08-24 14:21   ` Michal Marek
  2012-08-07 13:10 ` [PATCH 3/4] Input: cyttsp4 - MultiTouch " Ferruh Yigit
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 20+ messages in thread
From: Ferruh Yigit @ 2012-08-07 13:09 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Ferruh YIGIT, Javier Martinez Canillas,
	Henrik Rydberg, Shawn Landden, Ashish Jangam, Olivier Sobrie,
	linux-input, linux-kernel

From: Ferruh YIGIT <fery@cypress.com>

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

Core driver is interface between host and TTSP controller and processes
data sent by controller.

Responsible from IRQ handling, reading system information registers
and supplying protected read/write calls to subscribed modules.

Signed-off-by: Ferruh YIGIT <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig        |   34 +
 drivers/input/touchscreen/Makefile       |   10 +
 drivers/input/touchscreen/cyttsp4_core.c | 2294 ++++++++++++++++++++++++++++++
 drivers/input/touchscreen/cyttsp4_regs.h |  445 ++++++
 include/linux/cyttsp4_core.h             |   97 ++
 5 files changed, 2880 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_core.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_regs.h
 create mode 100644 include/linux/cyttsp4_core.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 4a65736..6fa7278 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -188,6 +188,40 @@ config CYPRESS_CYTTSP4_BUS
          using Cypress TrueTouch(TM) Standard Product
          protocol.

+config TOUCHSCREEN_CYPRESS_CYTTSP4
+       tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
+       default m
+       select CYPRESS_CYTTSP4_BUS
+       help
+         Core driver for Cypress TrueTouch(tm) Standard Product
+         Geneartion4 touchscreen controllers.
+
+         Say Y here if you have a Cypress Gen4 touchscreen.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here.
+
+config TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG
+       bool "Enable debug output"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4
+       default y
+       help
+         Enable Debug output for Cypress TrueTouch(tm)
+         Standard Product Generation4 drivers set.
+
+         Say Y here to enable debug output.
+
+config TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG
+       bool "Enable verbose debug output"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG
+       default n
+       help
+         Enable Verbose Debug output for Cypress TrueTouch(tm)
+         Standard Product Generation4 drivers set.
+
+         Say Y here to enable verbose debug output.
+
 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 ab84aec..d12965b 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -74,3 +74,13 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)    += zylonite-wm97xx.o
 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
+ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG),y)
+CFLAGS_cyttsp4_core.o += -DDEBUG
+CFLAGS_cyttsp4_bus.o += -DDEBUG
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG),y)
+CFLAGS_cyttsp4_core.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_bus.o += -DVERBOSE_DEBUG
+endif
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
new file mode 100644
index 0000000..98391fa
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -0,0 +1,2294 @@
+/*
+ * cyttsp4_core.c
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver 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.
+ *
+ * Author: Aleksej Makarov <aleksej.makarov@sonyericsson.com>
+ * Modified by: Cypress Semiconductor to add device functions
+ *
+ * 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/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/cyttsp4_core.h>
+#include "cyttsp4_regs.h"
+
+#define CY_CORE_MODE_CHANGE_TIMEOUT            1000
+#define CY_CORE_RESET_AND_WAIT_TIMEOUT         500
+#define CY_CORE_WAKEUP_TIMEOUT                 500
+
+MODULE_FIRMWARE(CY_FW_FILE_NAME);
+
+const char *cy_driver_core_name = CYTTSP4_CORE_NAME;
+const char *cy_driver_core_version = CY_DRIVER_VERSION;
+const char *cy_driver_core_date = CY_DRIVER_DATE;
+
+enum cyttsp4_sleep_state {
+       SS_SLEEP_OFF,
+       SS_SLEEP_ON,
+       SS_SLEEPING,
+       SS_WAKING,
+};
+
+struct cyttsp4_core_data {
+       struct device *dev;
+       struct cyttsp4_core *core;
+       struct list_head atten_list[CY_ATTEN_NUM_ATTEN];
+       struct mutex system_lock;
+       struct mutex adap_lock;
+       enum cyttsp4_mode mode;
+       enum cyttsp4_sleep_state sleep_state;
+       int int_status;
+       int cmd_toggle;
+       spinlock_t spinlock;
+       struct cyttsp4_core_platform_data *pdata;
+       wait_queue_head_t wait_q;
+       wait_queue_head_t sleep_q;
+       int irq;
+       struct workqueue_struct *startup_work_q;
+       struct work_struct startup_work;
+       struct workqueue_struct *mode_change_work_q;
+       struct work_struct mode_change_work;
+       struct cyttsp4_sysinfo sysinfo;
+       void *exclusive_dev;
+       atomic_t ignore_irq;
+       bool irq_enabled;
+#ifdef VERBOSE_DEBUG
+       u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+       struct work_struct work;
+       struct timer_list timer;
+};
+
+struct atten_node {
+       struct list_head node;
+       int (*func)(struct cyttsp4_device *);
+       struct cyttsp4_device *ttsp;
+       int mode;
+};
+
+static inline size_t merge_bytes(u8 high, u8 low)
+{
+       return (high << 8) + low;
+}
+
+#ifdef VERBOSE_DEBUG
+void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size,
+               const char *data_name)
+{
+       int i, k;
+       const char fmt[] = "%02X ";
+       int max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
+
+       pr_buf[0] = 0;
+       for (i = k = 0; i < size && k < max; i++, k += 3)
+               scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]);
+
+       dev_vdbg(dev, "%s:  %s[0..%d]=%s%s\n", __func__, data_name, size - 1,
+                       pr_buf, size <= max ? "" : CY_PR_TRUNCATED);
+}
+EXPORT_SYMBOL(cyttsp4_pr_buf);
+#endif
+
+static int cyttsp4_load_status_regs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       struct device *dev = cd->dev;
+       int rc;
+
+       if (!si->xy_mode) {
+               dev_err(cd->dev, "%s: NULL xy_mode pointer\n", __func__);
+               return -EINVAL;
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, CY_REG_BASE,
+               si->xy_mode, si->si_ofs.mode_size);
+       if (rc < 0)
+               dev_err(dev, "%s: fail read mode regs r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode,
+                       si->si_ofs.mode_size, "xy_mode");
+
+       return rc;
+}
+
+static int cyttsp4_handshake(struct cyttsp4_core_data *cd, u8 mode)
+{
+       u8 cmd = mode ^ CY_HST_TOGGLE;
+       int rc = cyttsp4_adap_write(cd->core->adap, CY_REG_BASE, &cmd,
+                       sizeof(cmd));
+
+       if (rc < 0)
+               dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n",
+                               __func__, rc);
+
+       return rc;
+}
+
+static int cyttsp4_toggle_low_power(struct cyttsp4_core_data *cd, u8 mode)
+{
+       u8 cmd = mode ^ CY_HST_LOWPOW;
+       int rc = cyttsp4_adap_write(cd->core->adap, CY_REG_BASE, &cmd,
+                       sizeof(cmd));
+       if (rc < 0)
+               dev_err(cd->dev,
+                       "%s: bus write fail on toggle low power (ret=%d)\n",
+                       __func__, rc);
+       return rc;
+}
+
+static int cyttsp4_hw_soft_reset(struct cyttsp4_core_data *cd)
+{
+       u8 cmd = CY_HST_RESET | CY_HST_MODE_CHANGE;
+       int rc = cyttsp4_adap_write(cd->core->adap, CY_REG_BASE, &cmd,
+                       sizeof(cmd));
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n",
+                               __func__);
+               return rc;
+       }
+       dev_dbg(cd->dev, "%s: execute SOFT reset\n", __func__);
+       return 0;
+}
+
+static int cyttsp4_hw_hard_reset(struct cyttsp4_core_data *cd)
+{
+       if (cd->pdata->xres) {
+               cd->pdata->xres(cd->pdata, cd->dev);
+               dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__);
+               return 0;
+       }
+       dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__);
+       return -ENOSYS;
+}
+
+static int cyttsp4_hw_reset(struct cyttsp4_core_data *cd)
+{
+       int rc = cyttsp4_hw_hard_reset(cd);
+       if (rc == -ENOSYS)
+               rc = cyttsp4_hw_soft_reset(cd);
+       return rc;
+}
+
+static inline int cyttsp4_bits_2_bytes(int nbits, int *max)
+{
+       *max = 1 << nbits;
+       return (nbits + 7) / 8;
+}
+
+static int cyttsp4_si_data_offsets(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc = cyttsp4_adap_read(cd->core->adap, CY_REG_BASE, &si->si_data,
+                                  sizeof(si->si_data));
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       /* Print sysinfo data offsets */
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data,
+                      sizeof(si->si_data), "sysinfo_data_offsets");
+
+       /* convert sysinfo data offset bytes into integers */
+
+       si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh,
+                       si->si_data.map_szl);
+       si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh,
+                       si->si_data.map_szl);
+       si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh,
+                       si->si_data.cydata_ofsl);
+       si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh,
+                       si->si_data.test_ofsl);
+       si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh,
+                       si->si_data.pcfg_ofsl);
+       si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh,
+                       si->si_data.opcfg_ofsl);
+       si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh,
+                       si->si_data.ddata_ofsl);
+       si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh,
+                       si->si_data.mdata_ofsl);
+       return rc;
+}
+
+static int cyttsp4_si_get_cydata(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs;
+       if (si->si_ptrs.cydata == NULL)
+               si->si_ptrs.cydata = kzalloc(si->si_ofs.cydata_size,
+                       GFP_KERNEL);
+       if (si->si_ptrs.cydata == NULL) {
+               dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, si->si_ofs.cydata_ofs,
+                              si->si_ptrs.cydata, si->si_ofs.cydata_size);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+                              (u8 *)si->si_ptrs.cydata,
+                              si->si_ofs.cydata_size, "sysinfo_cydata");
+       return rc;
+}
+
+static int cyttsp4_si_get_test_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs;
+       if (si->si_ptrs.test == NULL)
+               si->si_ptrs.test = kzalloc(si->si_ofs.test_size, GFP_KERNEL);
+       if (si->si_ptrs.test == NULL) {
+               dev_err(cd->dev, "%s: fail alloc test memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, si->si_ofs.test_ofs,
+               si->si_ptrs.test, si->si_ofs.test_size);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read test data r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+                      (u8 *)si->si_ptrs.test, si->si_ofs.test_size,
+                      "sysinfo_test_data");
+       if (si->si_ptrs.test->post_codel &
+           CY_POST_CODEL_WDG_RST)
+               dev_info(cd->dev, "%s: %s codel=%02X\n",
+                        __func__, "Reset was a WATCHDOG RESET",
+                        si->si_ptrs.test->post_codel);
+
+       if (!(si->si_ptrs.test->post_codel &
+             CY_POST_CODEL_CFG_DATA_CRC_FAIL))
+               dev_info(cd->dev, "%s: %s codel=%02X\n", __func__,
+                        "Config Data CRC FAIL",
+                        si->si_ptrs.test->post_codel);
+
+       if (!(si->si_ptrs.test->post_codel &
+             CY_POST_CODEL_PANEL_TEST_FAIL))
+               dev_info(cd->dev, "%s: %s codel=%02X\n",
+                        __func__, "PANEL TEST FAIL",
+                        si->si_ptrs.test->post_codel);
+
+       dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n",
+                __func__, si->si_ptrs.test->post_codel & 0x08 ?
+                "ENABLED" : "DISABLED",
+                si->si_ptrs.test->post_codel);
+       return rc;
+}
+
+static int cyttsp4_si_get_pcfg_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get pcfg data\n", __func__);
+       si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs;
+       if (si->si_ptrs.pcfg == NULL)
+               si->si_ptrs.pcfg = kzalloc(si->si_ofs.pcfg_size, GFP_KERNEL);
+       if (si->si_ptrs.pcfg == NULL) {
+               rc = -ENOMEM;
+               dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+       rc = cyttsp4_adap_read(cd->core->adap, si->si_ofs.pcfg_ofs,
+                              si->si_ptrs.pcfg, si->si_ofs.pcfg_size);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read pcfg data r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh
+                       & CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl);
+       si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh
+                       & CY_PCFG_ORIGIN_X_MASK);
+       si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh
+                       & CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl);
+       si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh
+                       & CY_PCFG_ORIGIN_Y_MASK);
+       si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh,
+                       si->si_ptrs.pcfg->max_zl);
+
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+                      (u8 *)si->si_ptrs.pcfg,
+                      si->si_ofs.pcfg_size, "sysinfo_pcfg_data");
+       return rc;
+}
+
+static int cyttsp4_si_get_opcfg_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int i;
+       enum cyttsp4_tch_abs abs;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get opcfg data\n", __func__);
+       si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs;
+       if (si->si_ptrs.opcfg == NULL)
+               si->si_ptrs.opcfg = kzalloc(si->si_ofs.opcfg_size, GFP_KERNEL);
+       if (si->si_ptrs.opcfg == NULL) {
+               dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__);
+               rc = -ENOMEM;
+               goto cyttsp4_si_get_opcfg_data_exit;
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, si->si_ofs.opcfg_ofs,
+               si->si_ptrs.opcfg, si->si_ofs.opcfg_size);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read opcfg data r=%d\n",
+                       __func__, rc);
+               goto cyttsp4_si_get_opcfg_data_exit;
+       }
+       si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs;
+       si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs;
+       si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) +
+               si->si_ptrs.opcfg->rep_szl;
+       si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns;
+       si->si_ofs.num_btn_regs = (si->si_ofs.num_btns +
+               CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG;
+       si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs;
+       si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0;
+       si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs &
+               CY_BYTE_OFS_MASK;
+       si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size &
+               CY_BYTE_OFS_MASK;
+
+       /* Get the old touch fields */
+       for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) {
+               si->si_ofs.tch_abs[abs].ofs =
+                       si->si_ptrs.opcfg->tch_rec_old[abs].loc &
+                       CY_BYTE_OFS_MASK;
+               si->si_ofs.tch_abs[abs].size =
+                       cyttsp4_bits_2_bytes
+                       (si->si_ptrs.opcfg->tch_rec_old[abs].size,
+                       &si->si_ofs.tch_abs[abs].max);
+               si->si_ofs.tch_abs[abs].bofs =
+                       (si->si_ptrs.opcfg->tch_rec_old[abs].loc &
+                       CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+       }
+
+       /* button fields */
+       si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size;
+       si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs;
+       si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size;
+
+       if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+               /* Get the extended touch fields */
+               for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) {
+                       si->si_ofs.tch_abs[abs].ofs =
+                               si->si_ptrs.opcfg->tch_rec_new[i].loc &
+                               CY_BYTE_OFS_MASK;
+                       si->si_ofs.tch_abs[abs].size =
+                               cyttsp4_bits_2_bytes
+                               (si->si_ptrs.opcfg->tch_rec_new[i].size,
+                               &si->si_ofs.tch_abs[abs].max);
+                       si->si_ofs.tch_abs[abs].bofs =
+                               (si->si_ptrs.opcfg->tch_rec_new[i].loc
+                               & CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+               }
+       }
+
+       for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) {
+               dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__,
+                       cyttsp4_tch_abs_string[abs]);
+               dev_dbg(cd->dev, "%s:     ofs =%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].ofs);
+               dev_dbg(cd->dev, "%s:     siz =%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].size);
+               dev_dbg(cd->dev, "%s:     max =%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].max);
+               dev_dbg(cd->dev, "%s:     bofs=%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].bofs);
+       }
+
+       si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1;
+       si->si_ofs.data_size = si->si_ofs.max_tchs *
+               si->si_ptrs.opcfg->tch_rec_size;
+
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg,
+               si->si_ofs.opcfg_size, "sysinfo_opcfg_data");
+
+cyttsp4_si_get_opcfg_data_exit:
+       return rc;
+}
+
+static int cyttsp4_si_get_ddata(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get ddata data\n", __func__);
+       si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs;
+       if (si->si_ptrs.ddata == NULL)
+               si->si_ptrs.ddata = kzalloc(si->si_ofs.ddata_size, GFP_KERNEL);
+       if (si->si_ptrs.ddata == NULL) {
+               dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, si->si_ofs.ddata_ofs,
+                              si->si_ptrs.ddata, si->si_ofs.ddata_size);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: fail read ddata data r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+                              (u8 *)si->si_ptrs.ddata,
+                              si->si_ofs.ddata_size, "sysinfo_ddata");
+       return rc;
+}
+
+static int cyttsp4_si_get_mdata(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get mdata data\n", __func__);
+       si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs;
+       if (si->si_ptrs.mdata == NULL)
+               si->si_ptrs.mdata = kzalloc(si->si_ofs.mdata_size, GFP_KERNEL);
+       if (si->si_ptrs.mdata == NULL) {
+               dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, si->si_ofs.mdata_ofs,
+                              si->si_ptrs.mdata, si->si_ofs.mdata_size);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: fail read mdata data r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(cd->dev, cd->pr_buf,
+                              (u8 *)si->si_ptrs.mdata,
+                              si->si_ofs.mdata_size, "sysinfo_mdata");
+       return rc;
+}
+
+static int cyttsp4_si_get_btn_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int btn;
+       int num_defined_keys;
+       u16 *key_table;
+       int rc = 0;
+
+       dev_vdbg(cd->dev, "%s: get btn data\n", __func__);
+       if (si->si_ofs.num_btns) {
+               si->si_ofs.btn_keys_size = si->si_ofs.num_btns *
+                       sizeof(struct cyttsp4_btn);
+               if (si->btn == NULL)
+                       si->btn = kzalloc(si->si_ofs.btn_keys_size, GFP_KERNEL);
+               if (si->btn == NULL) {
+                       dev_err(cd->dev, "%s: %s\n", __func__,
+                               "fail alloc btn_keys memory");
+                       return -ENOMEM;
+               }
+               if (cd->pdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL)
+                       num_defined_keys = 0;
+               else if (cd->pdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL)
+                       num_defined_keys = 0;
+               else
+                       num_defined_keys = cd->pdata->sett
+                               [CY_IC_GRPNUM_BTN_KEYS]->size;
+
+               for (btn = 0; btn < si->si_ofs.num_btns &&
+                       btn < num_defined_keys; btn++) {
+                       key_table = (u16 *)cd->pdata->sett
+                               [CY_IC_GRPNUM_BTN_KEYS]->data;
+                       si->btn[btn].key_code = key_table[btn];
+                       si->btn[btn].enabled = true;
+               }
+               for (; btn < si->si_ofs.num_btns; btn++) {
+                       si->btn[btn].key_code = KEY_RESERVED;
+                       si->btn[btn].enabled = true;
+               }
+
+               return rc;
+       }
+
+       si->si_ofs.btn_keys_size = 0;
+       kfree(si->btn);
+       si->btn = NULL;
+       return rc;
+}
+
+static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       if (si->xy_mode == NULL) {
+               si->xy_mode = kzalloc(si->si_ofs.mode_size, GFP_KERNEL);
+               if (si->xy_mode == NULL)
+                       return -ENOMEM;
+       }
+
+       if (si->xy_data == NULL) {
+               si->xy_data = kzalloc(si->si_ofs.data_size, GFP_KERNEL);
+               if (si->xy_data == NULL)
+                       return -ENOMEM;
+       }
+
+       if (si->btn_rec_data == NULL) {
+               si->btn_rec_data = kzalloc(si->si_ofs.btn_rec_size *
+                                          si->si_ofs.num_btns, GFP_KERNEL);
+               if (si->btn_rec_data == NULL)
+                       return -ENOMEM;
+       }
+       return 0;
+}
+
+static void cyttsp4_si_put_log_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       dev_dbg(cd->dev, "%s: cydata_ofs =%4d siz=%4d\n", __func__,
+               si->si_ofs.cydata_ofs, si->si_ofs.cydata_size);
+       dev_dbg(cd->dev, "%s: test_ofs   =%4d siz=%4d\n", __func__,
+               si->si_ofs.test_ofs, si->si_ofs.test_size);
+       dev_dbg(cd->dev, "%s: pcfg_ofs   =%4d siz=%4d\n", __func__,
+               si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size);
+       dev_dbg(cd->dev, "%s: opcfg_ofs  =%4d siz=%4d\n", __func__,
+               si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size);
+       dev_dbg(cd->dev, "%s: ddata_ofs  =%4d siz=%4d\n", __func__,
+               si->si_ofs.ddata_ofs, si->si_ofs.ddata_size);
+       dev_dbg(cd->dev, "%s: mdata_ofs  =%4d siz=%4d\n", __func__,
+               si->si_ofs.mdata_ofs, si->si_ofs.mdata_size);
+
+       dev_dbg(cd->dev, "%s: cmd_ofs       =%4d\n", __func__,
+               si->si_ofs.cmd_ofs);
+       dev_dbg(cd->dev, "%s: rep_ofs       =%4d\n", __func__,
+               si->si_ofs.rep_ofs);
+       dev_dbg(cd->dev, "%s: rep_sz        =%4d\n", __func__,
+               si->si_ofs.rep_sz);
+       dev_dbg(cd->dev, "%s: num_btns      =%4d\n", __func__,
+               si->si_ofs.num_btns);
+       dev_dbg(cd->dev, "%s: num_btn_regs  =%4d\n", __func__,
+               si->si_ofs.num_btn_regs);
+       dev_dbg(cd->dev, "%s: tt_stat_ofs   =%4d\n", __func__,
+               si->si_ofs.tt_stat_ofs);
+       dev_dbg(cd->dev, "%s: tch_rec_size   =%4d\n", __func__,
+               si->si_ofs.tch_rec_size);
+       dev_dbg(cd->dev, "%s: max_tchs      =%4d\n", __func__,
+               si->si_ofs.max_tchs);
+       dev_dbg(cd->dev, "%s: mode_size     =%4d\n", __func__,
+               si->si_ofs.mode_size);
+       dev_dbg(cd->dev, "%s: data_size     =%4d\n", __func__,
+               si->si_ofs.data_size);
+       dev_dbg(cd->dev, "%s: map_sz        =%4d\n", __func__,
+               si->si_ofs.map_sz);
+
+       dev_dbg(cd->dev, "%s: btn_rec_size   =%2d\n", __func__,
+               si->si_ofs.btn_rec_size);
+       dev_dbg(cd->dev, "%s: btn_diff_ofs  =%2d\n", __func__,
+               si->si_ofs.btn_diff_ofs);
+       dev_dbg(cd->dev, "%s: btn_diff_size  =%2d\n", __func__,
+               si->si_ofs.btn_diff_size);
+
+       dev_dbg(cd->dev, "%s: max_x    = 0x%04X (%d)\n", __func__,
+               si->si_ofs.max_x, si->si_ofs.max_x);
+       dev_dbg(cd->dev, "%s: x_origin = %d (%s)\n", __func__,
+               si->si_ofs.x_origin,
+               si->si_ofs.x_origin == CY_NORMAL_ORIGIN ?
+               "left corner" : "right corner");
+       dev_dbg(cd->dev, "%s: max_y    = 0x%04X (%d)\n", __func__,
+               si->si_ofs.max_y, si->si_ofs.max_y);
+       dev_dbg(cd->dev, "%s: y_origin = %d (%s)\n", __func__,
+               si->si_ofs.y_origin,
+               si->si_ofs.y_origin == CY_NORMAL_ORIGIN ?
+               "upper corner" : "lower corner");
+       dev_dbg(cd->dev, "%s: max_p    = 0x%04X (%d)\n", __func__,
+               si->si_ofs.max_p, si->si_ofs.max_p);
+
+       dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__,
+               si->xy_mode, si->xy_data);
+}
+
+static int cyttsp4_get_sysinfo_regs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       rc = cyttsp4_si_data_offsets(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_cydata(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_test_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_pcfg_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_opcfg_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_ddata(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_mdata(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_btn_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_op_data_ptrs(cd);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: failed to get_op_data\n",
+                       __func__);
+               return rc;
+       }
+       cyttsp4_si_put_log_data(cd);
+
+       /* provide flow control handshake */
+       rc = cyttsp4_handshake(cd, si->si_data.hst_mode);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n",
+                       __func__);
+
+       si->ready = true;
+       return rc;
+}
+
+static void cyttsp4_queue_startup(struct cyttsp4_core_data *cd)
+{
+       queue_work(cd->startup_work_q, &cd->startup_work);
+       dev_info(cd->dev, "%s: cyttsp4_startup queued\n", __func__);
+}
+
+void cyttsp4_queue_startup_and_wait(struct cyttsp4_core_data *cd)
+{
+       /* Check if there is a pending startup request */
+       if (flush_work_sync(&cd->startup_work)) {
+               dev_vdbg(cd->dev, "%s: Work pending 1\n", __func__);
+               return;
+       }
+
+       /* Queue one and wait until it finishes */
+       cyttsp4_queue_startup(cd);
+
+       flush_work_sync(&cd->startup_work);
+       dev_vdbg(cd->dev, "%s: Work pending 2\n", __func__);
+}
+
+static void call_atten_cb(struct cyttsp4_core_data *cd, int mode)
+{
+       struct atten_node *atten, *atten_n;
+
+       dev_vdbg(cd->dev, "%s: check list mode=%d\n", __func__, mode);
+       spin_lock(&cd->spinlock);
+       list_for_each_entry_safe(atten, atten_n,
+                       &cd->atten_list[CY_ATTEN_IRQ], node) {
+               if (atten->mode & mode) {
+                       spin_unlock(&cd->spinlock);
+                       dev_vdbg(cd->dev, "%s: attention for mode=%d",
+                                       __func__, atten->mode);
+                       atten->func(atten->ttsp);
+                       spin_lock(&cd->spinlock);
+               }
+       }
+       spin_unlock(&cd->spinlock);
+}
+
+static irqreturn_t cyttsp4_irq(int irq, void *handle)
+{
+       struct cyttsp4_core_data *cd = handle;
+       struct device *dev = cd->dev;
+       enum cyttsp4_mode cur_mode;
+       u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs;
+       u8 mode[3];
+       int rc;
+       u8 cat_masked_cmd;
+
+       /*
+        * Check whether this IRQ should be ignored (external)
+        * This should be the very first thing to check since
+        * ignore_irq may be set for a very short period of time
+        */
+       if (atomic_read(&cd->ignore_irq)) {
+               dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+               return IRQ_HANDLED;
+       }
+
+       dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status);
+
+       mutex_lock(&cd->system_lock);
+
+       /* Just to debug */
+       if (cd->sleep_state == SS_SLEEP_ON) {
+               dev_vdbg(dev, "%s: Received IRQ while in sleep\n",
+                       __func__);
+       } else if (cd->sleep_state == SS_SLEEPING) {
+               dev_vdbg(dev, "%s: Received IRQ while sleeping\n",
+                       __func__);
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, CY_REG_BASE, mode, sizeof(mode));
+       if (rc) {
+               dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+               goto cyttsp4_irq_exit;
+       }
+       dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__,
+                       mode[0], mode[1], mode[2]);
+
+       if (IS_BOOTLOADER(mode[0])) {
+               cur_mode = CY_MODE_BOOTLOADER;
+               dev_vdbg(dev, "%s: bl running\n", __func__);
+               call_atten_cb(cd, cur_mode);
+               if (cd->mode == CY_MODE_BOOTLOADER) {
+                       /* Signal bootloader heartbeat heard */
+                       wake_up(&cd->wait_q);
+                       goto cyttsp4_irq_exit;
+               }
+
+               /* switch to bootloader */
+               dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n",
+                       __func__, cd->mode, cur_mode);
+
+               /* catch operation->bl glitch */
+               if (cd->mode != CY_MODE_UNKNOWN) {
+                       cyttsp4_queue_startup(cd);
+                       goto cyttsp4_irq_exit;
+               }
+
+               /*
+                * do not wake thread on this switch since
+                * it is possible to get an early heartbeat
+                * prior to performing the reset
+                */
+               cd->mode = cur_mode;
+
+               goto cyttsp4_irq_exit;
+       }
+
+       switch (mode[0] & CY_HST_MODE) {
+       case CY_HST_OPERATE:
+               cur_mode = CY_MODE_OPERATIONAL;
+               dev_vdbg(dev, "%s: operational\n", __func__);
+               break;
+       case CY_HST_CAT:
+               cur_mode = CY_MODE_CAT;
+               /* set the start sensor mode state. */
+               cat_masked_cmd = mode[2] & CY_CMD_MASK;
+
+               /* Get the Debug info for the interrupt. */
+               if (cat_masked_cmd != CY_CMD_CAT_RETRIEVE_PANEL_SCAN &&
+                               cat_masked_cmd != CY_CMD_CAT_EXEC_PANEL_SCAN)
+                       dev_info(cd->dev,
+                               "%s: cyttsp4_CaT_IRQ=%02X %02X %02X\n",
+                               __func__, mode[0], mode[1], mode[2]);
+               dev_vdbg(dev, "%s: CaT\n", __func__);
+               break;
+       case CY_HST_SYSINFO:
+               cur_mode = CY_MODE_SYSINFO;
+               dev_vdbg(dev, "%s: sysinfo\n", __func__);
+               break;
+       default:
+               cur_mode = CY_MODE_UNKNOWN;
+               dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__,
+                       mode[0]);
+               break;
+       }
+
+       /* Check whether this IRQ should be ignored (internal) */
+       if (cd->int_status & CY_INT_IGNORE) {
+               dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+               goto cyttsp4_irq_exit;
+       }
+
+       /* Check for wake up interrupt */
+       if (cd->int_status & CY_INT_AWAKE) {
+               cd->int_status &= ~CY_INT_AWAKE;
+               wake_up(&cd->sleep_q);
+               dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__);
+               goto cyttsp4_irq_handshake;
+       }
+
+       /* Expecting mode change interrupt */
+       if ((cd->int_status & CY_INT_MODE_CHANGE)
+                       && (mode[0] & CY_HST_MODE_CHANGE) == 0) {
+               cd->int_status &= ~CY_INT_MODE_CHANGE;
+               dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n",
+                               __func__, cd->mode, cur_mode);
+               cd->mode = cur_mode;
+               wake_up(&cd->wait_q);
+               goto cyttsp4_irq_handshake;
+       }
+
+       /* compare current core mode to current device mode */
+       dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n",
+                       __func__, cd->mode, cur_mode);
+       if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) {
+               /* Unexpected mode change occurred */
+               dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode,
+                               cur_mode, cd->int_status);
+               dev_vdbg(dev, "%s: Unexpected mode change, startup\n",
+                               __func__);
+               cyttsp4_queue_startup(cd);
+               goto cyttsp4_irq_exit;
+       }
+
+       /* Expecting command complete interrupt */
+       dev_vdbg(dev, "%s: command byte:0x%x, toggle:0x%x\n",
+                       __func__, mode[cmd_ofs], cd->cmd_toggle);
+       if ((cd->int_status & CY_INT_EXEC_CMD)
+                       && mode[cmd_ofs] & CY_CMD_COMPLETE) {
+               cd->int_status &= ~CY_INT_EXEC_CMD;
+               dev_vdbg(dev, "%s: Received command complete interrupt\n",
+                               __func__);
+               wake_up(&cd->wait_q);
+               goto cyttsp4_irq_handshake;
+       }
+
+       /* This should be status report, read status regs */
+       if (cd->mode == CY_MODE_OPERATIONAL) {
+               dev_vdbg(dev, "%s: Read status registers\n", __func__);
+               rc = cyttsp4_load_status_regs(cd);
+               if (rc < 0)
+                       dev_err(dev, "%s: fail read mode regs r=%d\n",
+                               __func__, rc);
+       }
+
+       /* attention IRQ */
+       call_atten_cb(cd, cd->mode);
+
+cyttsp4_irq_handshake:
+       /* handshake the event */
+       dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n",
+                       __func__, mode[0], rc);
+       rc = cyttsp4_handshake(cd, mode[0]);
+       if (rc < 0)
+               dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n",
+                               __func__, mode[0], rc);
+
+       /*
+        * a non-zero udelay period is required for using
+        * IRQF_TRIGGER_LOW in order to delay until the
+        * device completes isr deassert
+        */
+       udelay(cd->pdata->level_irq_udelay);
+
+cyttsp4_irq_exit:
+       mutex_unlock(&cd->system_lock);
+       dev_vdbg(dev, "%s: irq done\n", __func__);
+       return IRQ_HANDLED;
+}
+
+static void cyttsp4_start_wd_timer(struct cyttsp4_core_data *cd)
+{
+       if (!CY_WATCHDOG_TIMEOUT)
+               return;
+
+       mod_timer(&cd->timer, jiffies + CY_WATCHDOG_TIMEOUT);
+       return;
+}
+
+static void cyttsp4_stop_wd_timer(struct cyttsp4_core_data *cd)
+{
+       if (!CY_WATCHDOG_TIMEOUT)
+               return;
+
+       del_timer(&cd->timer);
+       cancel_work_sync(&cd->work);
+       return;
+}
+
+static void cyttsp4_timer_watchdog(struct work_struct *work)
+{
+       struct cyttsp4_core_data *cd =
+                       container_of(work, struct cyttsp4_core_data, work);
+       int rep_stat;
+       int mode;
+       int retval;
+
+       if (cd == NULL) {
+               dev_err(cd->dev, "%s: NULL context pointer\n", __func__);
+               return;
+       }
+
+       mutex_lock(&cd->system_lock);
+       retval = cyttsp4_load_status_regs(cd);
+       if (retval < 0) {
+               dev_err(cd->dev,
+                       "%s: failed to access device in watchdog timer r=%d\n",
+                       __func__, retval);
+               cyttsp4_queue_startup(cd);
+               goto cyttsp4_timer_watchdog_exit_error;
+       }
+       mode = cd->sysinfo.xy_mode[CY_REG_BASE];
+       rep_stat = cd->sysinfo.xy_mode[cd->sysinfo.si_ofs.rep_ofs];
+       if (IS_BOOTLOADER(mode) && IS_BOOTLOADER(rep_stat)) {
+               dev_err(cd->dev,
+                       "%s: device found in bootloader mode when operational mode rep_stat=0x%02X\n",
+                       __func__, rep_stat);
+               cyttsp4_queue_startup(cd);
+               goto cyttsp4_timer_watchdog_exit_error;
+       }
+
+       cyttsp4_start_wd_timer(cd);
+ cyttsp4_timer_watchdog_exit_error:
+       mutex_unlock(&cd->system_lock);
+       return;
+}
+
+static void cyttsp4_timer(unsigned long handle)
+{
+       struct cyttsp4_core_data *cd = (struct cyttsp4_core_data *)handle;
+
+       dev_vdbg(cd->dev, "%s: Timer triggered\n", __func__);
+
+       if (!cd)
+               return;
+
+       if (!work_pending(&cd->work))
+               schedule_work(&cd->work);
+
+       return;
+}
+
+static int cyttsp4_write_(struct cyttsp4_device *ttsp, int mode, u8 addr,
+       const void *buf, int size)
+{
+       struct device *dev = &ttsp->dev;
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       int rc = 0;
+
+       mutex_lock(&cd->adap_lock);
+       if (mode != cd->mode) {
+               dev_dbg(dev, "%s: %s (having %x while %x requested)\n",
+                       __func__, "attempt to write in missing mode",
+                       cd->mode, mode);
+               rc = -EACCES;
+               goto exit;
+       }
+       rc = cyttsp4_adap_write(core->adap, addr, buf, size);
+exit:
+       mutex_unlock(&cd->adap_lock);
+       return rc;
+}
+
+static int cyttsp4_read_(struct cyttsp4_device *ttsp, int mode, u8 addr,
+       void *buf, int size)
+{
+       struct device *dev = &ttsp->dev;
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       int rc = 0;
+
+       mutex_lock(&cd->adap_lock);
+       if (mode != cd->mode) {
+               dev_dbg(dev, "%s: %s (having %x while %x requested)\n",
+                       __func__, "attempt to read in missing mode",
+                       cd->mode, mode);
+               rc = -EACCES;
+               goto exit;
+       }
+       rc = cyttsp4_adap_read(core->adap, addr, buf, size);
+exit:
+       mutex_unlock(&cd->adap_lock);
+       return rc;
+}
+
+static int cyttsp4_subscribe_attention_(struct cyttsp4_device *ttsp,
+       enum cyttsp4_atten_type type,
+       int (*func)(struct cyttsp4_device *), int mode)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       unsigned long flags;
+       struct atten_node *atten, *atten_new;
+
+       atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL);
+       if (!atten_new) {
+               dev_err(cd->dev, "%s: Fail alloc atten node\n", __func__);
+               return -ENOMEM;
+       }
+
+       dev_dbg(cd->dev, "%s from '%s'\n", __func__, dev_name(cd->dev));
+
+       spin_lock_irqsave(&cd->spinlock, flags);
+       list_for_each_entry(atten, &cd->atten_list[type], node) {
+               if (atten->ttsp == ttsp && atten->mode == mode) {
+                       spin_unlock_irqrestore(&cd->spinlock, flags);
+                       dev_vdbg(cd->dev, "%s: %s=%p %s=%d\n",
+                                __func__,
+                                "already subscribed attention",
+                                ttsp, "mode", mode);
+
+                       return 0;
+               }
+       }
+
+       atten_new->ttsp = ttsp;
+       atten_new->mode = mode;
+       atten_new->func = func;
+
+       list_add(&atten_new->node, &cd->atten_list[type]);
+       spin_unlock_irqrestore(&cd->spinlock, flags);
+
+       return 0;
+}
+
+static int cyttsp4_unsubscribe_attention_(struct cyttsp4_device *ttsp,
+       enum cyttsp4_atten_type type, int (*func)(struct cyttsp4_device *),
+       int mode)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       struct atten_node *atten, *atten_n;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cd->spinlock, flags);
+       list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) {
+               if (atten->ttsp == ttsp && atten->mode == mode) {
+                       list_del(&atten->node);
+                       spin_unlock_irqrestore(&cd->spinlock, flags);
+                       kfree(atten);
+                       dev_vdbg(cd->dev, "%s: %s=%p %s=%d\n",
+                               __func__,
+                               "unsub for atten->ttsp", atten->ttsp,
+                               "atten->mode", atten->mode);
+                       return 0;
+               }
+       }
+       spin_unlock_irqrestore(&cd->spinlock, flags);
+
+       return -ENODEV;
+}
+
+static int request_exclusive(struct cyttsp4_core_data *cd, void *ownptr, int t)
+{
+       int rc = 0;
+
+       mutex_lock(&cd->system_lock);
+       if (cd->exclusive_dev != NULL) {
+               mutex_unlock(&cd->system_lock);
+               if (t)
+                       t = wait_event_timeout(cd->wait_q,
+                               cd->exclusive_dev == NULL, t);
+               else {
+                       wait_event(cd->wait_q,
+                               cd->exclusive_dev == NULL);
+                       t = 1;
+               }
+               mutex_lock(&cd->system_lock);
+               if (IS_TMO(t)) {
+                       dev_err(cd->dev, "%s: %s\n", __func__,
+                               "tmo waiting exclusive access");
+                       rc = -ETIME;
+               } else {
+                       cd->exclusive_dev = ownptr;
+                       dev_vdbg(cd->dev, "%s: 1 %s ok =%p\n", __func__,
+                               "request_exclusive", cd->exclusive_dev);
+               }
+       } else {
+               cd->exclusive_dev = ownptr;
+               dev_vdbg(cd->dev, "%s: 2 %s ok =%p\n", __func__,
+                       "request_exclusive", cd->exclusive_dev);
+       }
+       mutex_unlock(&cd->system_lock);
+
+       return rc;
+}
+
+static int cyttsp4_request_exclusive_(struct cyttsp4_device *ttsp, int t)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       return request_exclusive(cd, (void *)ttsp, t);
+}
+
+/*
+ * returns error if was not owned
+ */
+static int release_exclusive(struct cyttsp4_core_data *cd, void *ownptr)
+{
+       mutex_lock(&cd->system_lock);
+       if (cd->exclusive_dev != ownptr) {
+               mutex_unlock(&cd->system_lock);
+               return -EINVAL;
+       }
+
+       dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n",
+               __func__, cd->exclusive_dev);
+       cd->exclusive_dev = NULL;
+       wake_up(&cd->wait_q);
+       mutex_unlock(&cd->system_lock);
+       return 0;
+}
+
+static int cyttsp4_release_exclusive_(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       return release_exclusive(cd, (void *)ttsp);
+}
+
+static int cyttsp4_reset_and_wait(struct cyttsp4_core_data *cd)
+{
+       long t;
+       int rc;
+
+       /* reset hardware */
+       mutex_lock(&cd->system_lock);
+       dev_dbg(cd->dev, "%s: reset hw...\n", __func__);
+       rc = cyttsp4_hw_reset(cd);
+       cd->mode = CY_MODE_UNKNOWN;
+       mutex_unlock(&cd->system_lock);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: %s adap='%s' r=%d\n", __func__,
+                       "Fail hw reset", cd->core->adap->id, rc);
+
+       /* wait heartbeat */
+       dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__);
+       t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER,
+               msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT));
+       if  (IS_TMO(t)) {
+               dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n",
+                               __func__, cd->mode);
+               rc = -ETIME;
+       } else
+               rc = 0;
+
+       return rc;
+}
+
+/*
+ * returns err if refused or timeout(core uses fixed timeout period) occurs;
+ * blocks until ISR occurs
+ */
+static int cyttsp4_request_reset_(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       int rc;
+
+       rc = cyttsp4_reset_and_wait(cd);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Error on h/w reset r=%d\n",
+                       __func__, rc);
+
+       return rc;
+}
+
+/*
+ * returns err if refused ; if no error then restart has completed
+ * and system is in normal operating mode
+ */
+static int cyttsp4_request_restart_(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+
+       cyttsp4_queue_startup(cd);
+
+       return 0;
+}
+
+/*
+ * returns err if refused or timeout; block until mode change complete
+ * bit is set (mode change interrupt)
+ */
+static int set_mode(struct cyttsp4_core_data *cd, void *ownptr,
+               u8 new_dev_mode, int new_op_mode)
+{
+       long t;
+       int rc;
+
+       /* change mode */
+       dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n",
+                       __func__, "have exclusive", cd->exclusive_dev,
+                       new_dev_mode, new_op_mode);
+       new_dev_mode |= CY_HST_MODE_CHANGE;
+
+       mutex_lock(&cd->system_lock);
+       cd->int_status |= CY_INT_MODE_CHANGE;
+       rc = cyttsp4_adap_write(cd->core->adap, CY_REG_BASE,
+                       &new_dev_mode, sizeof(new_dev_mode));
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Fail write mode change r=%d\n",
+                               __func__, rc);
+       mutex_unlock(&cd->system_lock);
+
+       /* wait for mode change done interrupt */
+       t = wait_event_timeout(cd->wait_q,
+                       (cd->int_status & CY_INT_MODE_CHANGE) == 0,
+                       msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+       dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n",
+                       __func__, t, cd->mode);
+
+       if (IS_TMO(t)) {
+               dev_err(cd->dev, "%s: %s\n", __func__,
+                               "tmo waiting mode change");
+               mutex_lock(&cd->system_lock);
+               cd->int_status &= ~CY_INT_MODE_CHANGE;
+               mutex_unlock(&cd->system_lock);
+               rc = -EINVAL;
+       }
+
+       return rc;
+}
+
+static int cyttsp4_request_set_mode_(struct cyttsp4_device *ttsp, int mode)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       int rc;
+       enum cyttsp4_hst_mode_bits mode_bits;
+       switch (mode) {
+       case CY_MODE_OPERATIONAL:
+               mode_bits = CY_HST_OPERATE;
+               break;
+       case CY_MODE_SYSINFO:
+               mode_bits = CY_HST_SYSINFO;
+               break;
+       case CY_MODE_CAT:
+               mode_bits = CY_HST_CAT;
+               break;
+       default:
+               dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n",
+                       __func__, mode, mode);
+               return -EINVAL;
+       }
+
+       rc = set_mode(cd, (void *)ttsp, mode_bits, mode);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: fail set_mode=%02X(%d)\n",
+                       __func__, cd->mode, cd->mode);
+
+       return rc;
+}
+
+/*
+ * returns NULL if sysinfo has not been acquired from the device yet
+ */
+static struct cyttsp4_sysinfo *cyttsp4_request_sysinfo_(
+               struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+
+       if (cd->sysinfo.ready)
+               return &cd->sysinfo;
+
+       return NULL;
+}
+
+static int cyttsp4_request_handshake_(struct cyttsp4_device *ttsp, u8 mode)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       int rc;
+
+       rc = cyttsp4_handshake(cd, mode);
+       if (rc < 0)
+               dev_err(&core->dev, "%s: Fail handshake r=%d\n", __func__, rc);
+
+       return rc;
+}
+
+static int cyttsp4_request_toggle_lowpower_(struct cyttsp4_device *ttsp,
+               u8 mode)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       int rc = cyttsp4_toggle_low_power(cd, mode);
+       if (rc < 0)
+               dev_err(&core->dev, "%s: Fail toggle low power r=%d\n",
+                               __func__, rc);
+       return rc;
+}
+
+static const u8 ldr_exit[] = {
+       0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17
+};
+
+/*
+ * Send command to device for CAT and OP modes
+ * return negative value on error, 0 on success
+ */
+static int cyttsp4_request_exec_cmd_(struct cyttsp4_device *ttsp, u8 mode,
+               u8 *cmd_buf, size_t cmd_size, u8 *return_buf,
+               size_t return_buf_size, int timeout)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       struct device *dev = cd->dev;
+       int cmd_ofs;
+       u8 command;
+       int rc;
+
+       mutex_lock(&cd->system_lock);
+       if (mode != cd->mode) {
+               dev_err(dev, "%s: %s (having %x while %x requested)\n",
+                               __func__, "attempt to exec cmd in missing mode",
+                               cd->mode, mode);
+               mutex_unlock(&cd->system_lock);
+               return -EACCES;
+       }
+
+       switch (mode) {
+       case CY_MODE_CAT:
+               cmd_ofs = CY_REG_CAT_CMD;
+               break;
+       case CY_MODE_OPERATIONAL:
+               cmd_ofs = si->si_ofs.cmd_ofs;
+               break;
+       default:
+               dev_err(dev, "%s: Unsupported mode %x for exec cmd\n",
+                               __func__, mode);
+               mutex_unlock(&cd->system_lock);
+               return -EACCES;
+       }
+
+       /* Check if complete is set, so write new command */
+       rc = cyttsp4_adap_read(cd->core->adap, cmd_ofs, &command, 1);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error on read r=%d\n", __func__, rc);
+               mutex_unlock(&cd->system_lock);
+               return rc;
+       }
+
+       cd->cmd_toggle = GET_TOGGLE(command);
+       cd->int_status |= CY_INT_EXEC_CMD;
+
+       if ((command & CY_CMD_COMPLETE_MASK) == 0) {
+               /* Let irq handler run */
+               mutex_unlock(&cd->system_lock);
+               rc = wait_event_timeout(cd->wait_q,
+                               (cd->int_status & CY_INT_EXEC_CMD) == 0,
+                               msecs_to_jiffies(timeout));
+               if (IS_TMO(rc)) {
+                       dev_err(dev, "%s: Command execution timed out\n",
+                                       __func__);
+                       cd->int_status &= ~CY_INT_EXEC_CMD;
+                       return -EINVAL;
+               }
+
+               /* For next command */
+               mutex_lock(&cd->system_lock);
+               rc = cyttsp4_adap_read(cd->core->adap, cmd_ofs, &command, 1);
+               if (rc < 0) {
+                       dev_err(dev, "%s: Error on read r=%d\n", __func__, rc);
+                       mutex_unlock(&cd->system_lock);
+                       return rc;
+               }
+               cd->cmd_toggle = GET_TOGGLE(command);
+               cd->int_status |= CY_INT_EXEC_CMD;
+       }
+
+       /*
+        * Write new command
+        * Only update command bits 0:5
+        */
+       cmd_buf[0] = (command & ~CY_CMD_MASK)
+                       | (cmd_buf[0] & CY_CMD_MASK);
+       /* Clear command complete bit */
+       cmd_buf[0] &= ~CY_CMD_COMPLETE_MASK;
+       rc = cyttsp4_adap_write(cd->core->adap, cmd_ofs, cmd_buf, cmd_size);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error on write command r=%d\n",
+                               __func__, rc);
+               mutex_unlock(&cd->system_lock);
+               return rc;
+       }
+
+       /*
+        * Wait command to be completed
+        */
+       mutex_unlock(&cd->system_lock);
+       rc = wait_event_timeout(cd->wait_q,
+                       (cd->int_status & CY_INT_EXEC_CMD) == 0,
+                       msecs_to_jiffies(timeout));
+       if (IS_TMO(rc)) {
+               dev_err(dev, "%s: Command execution timed out\n", __func__);
+               cd->int_status &= ~CY_INT_EXEC_CMD;
+               return -EINVAL;
+       }
+
+       rc = cyttsp4_adap_read(cd->core->adap, cmd_ofs + 1, return_buf,
+                       return_buf_size);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error on read 3 r=%d\n", __func__, rc);
+               return rc;
+       }
+
+       return 0;
+}
+
+static int cyttsp4_request_stop_wd_(struct cyttsp4_device *ttsp)
+{
+       struct cyttsp4_core *core = ttsp->core;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(&core->dev);
+       cyttsp4_stop_wd_timer(cd);
+       return 0;
+}
+
+static int cyttsp4_core_sleep_(struct cyttsp4_core_data *cd)
+{
+       enum cyttsp4_sleep_state ss = SS_SLEEP_ON;
+       int rc = 0;
+       u8 mode;
+
+       dev_vdbg(cd->dev, "%s: enter...\n", __func__);
+
+       /* Already in sleep mode? */
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_ON) {
+               mutex_unlock(&cd->system_lock);
+               return 0;
+       }
+       cd->sleep_state = SS_SLEEPING;
+       mutex_unlock(&cd->system_lock);
+
+       /* Wait until currently running IRQ handler exits and disable IRQ */
+       disable_irq(cd->irq);
+
+       dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__);
+
+       rc = cyttsp4_adap_read(cd->core->adap, CY_REG_BASE, &mode,
+                       sizeof(mode));
+       if (rc) {
+               dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+               ss = SS_SLEEP_OFF;
+               goto exit;
+       }
+
+       if (IS_BOOTLOADER(mode)) {
+               dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__);
+               rc = -EINVAL;
+               ss = SS_SLEEP_OFF;
+               goto exit;
+       }
+       mode |= CY_HST_SLEEP;
+
+       rc = cyttsp4_adap_write(cd->core->adap, CY_REG_BASE, &mode,
+                       sizeof(mode));
+       if (rc) {
+               dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc);
+               ss = SS_SLEEP_OFF;
+               goto exit;
+       }
+
+       dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__);
+
+       if (cd->pdata->power) {
+               dev_dbg(cd->dev, "%s: Power down HW\n", __func__);
+               rc = cd->pdata->power(cd->pdata, 0, cd->dev, &cd->ignore_irq);
+       } else {
+               dev_dbg(cd->dev, "%s: No power function\n", __func__);
+               rc = 0;
+       }
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: HW Power down fails r=%d\n",
+                               __func__, rc);
+               ss = SS_SLEEP_OFF;
+               goto exit;
+       }
+
+       cyttsp4_stop_wd_timer(cd);
+
+       /* Give time to FW to sleep */
+       mdelay(10);
+
+exit:
+       mutex_lock(&cd->system_lock);
+       if (ss == SS_SLEEP_ON)
+               cd->int_status |= CY_INT_IGNORE;
+       cd->sleep_state = ss;
+       mutex_unlock(&cd->system_lock);
+
+       enable_irq(cd->irq);
+
+       return rc;
+}
+
+static int cyttsp4_core_sleep(struct cyttsp4_core_data *cd)
+{
+       int rc;
+
+       rc = request_exclusive(cd, cd->core, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+                               __func__, cd->exclusive_dev, cd->core);
+               return 0;
+       }
+
+       rc = cyttsp4_core_sleep_(cd);
+
+       if (release_exclusive(cd, cd->core) < 0)
+               dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+       else
+               dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+       return rc;
+}
+
+static int cyttsp4_core_wake_(struct cyttsp4_core_data *cd)
+{
+       struct device *dev = cd->dev;
+       int rc;
+       u8 mode;
+       int t;
+
+       dev_vdbg(cd->dev, "%s: enter...\n", __func__);
+
+       /* Already woken? */
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_OFF) {
+               mutex_unlock(&cd->system_lock);
+               return 0;
+       }
+       cd->int_status &= ~CY_INT_IGNORE;
+       cd->int_status |= CY_INT_AWAKE;
+       cd->sleep_state = SS_WAKING;
+
+       /* Do not start watchdog in already woken state */
+       cyttsp4_start_wd_timer(cd);
+
+       if (cd->pdata->power) {
+               dev_dbg(dev, "%s: Power up HW\n", __func__);
+               rc = cd->pdata->power(cd->pdata, 1, dev, &cd->ignore_irq);
+       } else {
+               dev_dbg(dev, "%s: No power function\n", __func__);
+               rc = -ENOSYS;
+       }
+       if (rc < 0) {
+               dev_err(dev, "%s: HW Power up fails r=%d\n",
+                               __func__, rc);
+
+               /* Initiate a read transaction to wake up */
+               cyttsp4_adap_read(cd->core->adap, CY_REG_BASE, &mode,
+                               sizeof(mode));
+       } else
+               dev_vdbg(cd->dev, "%s: HW power up succeeds\n",
+                       __func__);
+       mutex_unlock(&cd->system_lock);
+
+       t = wait_event_timeout(cd->sleep_q,
+                       (cd->int_status & CY_INT_AWAKE) == 0,
+                       msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT));
+       if (IS_TMO(t)) {
+               dev_err(dev, "%s: TMO waiting for wakeup\n", __func__);
+               mutex_lock(&cd->system_lock);
+               cd->int_status &= ~CY_INT_AWAKE;
+               mutex_unlock(&cd->system_lock);
+               /* Try starting up */
+               cyttsp4_queue_startup(cd);
+       }
+
+       mutex_lock(&cd->system_lock);
+       cd->sleep_state = SS_SLEEP_OFF;
+       mutex_unlock(&cd->system_lock);
+
+       return 0;
+}
+
+static int cyttsp4_core_wake(struct cyttsp4_core_data *cd)
+{
+       int rc;
+
+       rc = request_exclusive(cd, cd->core, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+                               __func__, cd->exclusive_dev, cd->core);
+               return 0;
+       }
+
+       rc = cyttsp4_core_wake_(cd);
+
+       if (release_exclusive(cd, cd->core) < 0)
+               dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+       else
+               dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+       return rc;
+}
+
+static int cyttsp4_startup_(struct cyttsp4_core_data *cd)
+{
+       struct atten_node *atten, *atten_n;
+       unsigned long flags;
+       long t;
+       int rc;
+
+       dev_dbg(cd->dev, "%s: enter...\n", __func__);
+
+       cyttsp4_stop_wd_timer(cd);
+
+       /* reset hardware and wait for heartbeat */
+       rc = cyttsp4_reset_and_wait(cd);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc);
+
+       /* exit bl into sysinfo mode */
+       dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__);
+       mutex_lock(&cd->system_lock);
+       cd->int_status &= ~CY_INT_IGNORE;
+       cd->int_status |= CY_INT_MODE_CHANGE;
+       rc = cyttsp4_adap_write(cd->core->adap, CY_REG_BASE,
+               (u8 *)ldr_exit, sizeof(ldr_exit));
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Fail write adap='%s' r=%d\n",
+                       __func__, cd->core->adap->id, rc);
+       mutex_unlock(&cd->system_lock);
+
+       dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__);
+       t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO,
+               msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+       if (IS_TMO(t)) {
+               dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n",
+                               __func__, cd->mode);
+               mutex_lock(&cd->system_lock);
+               cd->int_status &= ~CY_INT_MODE_CHANGE;
+               mutex_unlock(&cd->system_lock);
+
+               cyttsp4_start_wd_timer(cd);
+
+               /*
+                * Unable to switch to SYSINFO mode,
+                * Corrupted FW may cause crash, exit here.
+                */
+               return 0;
+       }
+
+       /* read sysinfo data */
+       dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__);
+       rc = cyttsp4_get_sysinfo_regs(cd);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n",
+                       __func__, rc);
+
+       cyttsp4_start_wd_timer(cd);
+
+       /* switch to operational mode */
+       dev_vdbg(cd->dev, "%s: set mode cd->core=%p hst_mode=%02X mode=%d...\n",
+               __func__, cd->core, CY_HST_OPERATE, CY_MODE_OPERATIONAL);
+       set_mode(cd, cd->core, CY_HST_OPERATE, CY_MODE_OPERATIONAL);
+
+       /* restore to sleep if was suspended */
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_ON) {
+               cd->sleep_state = SS_SLEEP_OFF;
+               mutex_unlock(&cd->system_lock);
+
+               cyttsp4_core_sleep_(cd);
+       } else {
+               mutex_unlock(&cd->system_lock);
+               /* attention startup */
+               spin_lock_irqsave(&cd->spinlock, flags);
+               list_for_each_entry_safe(atten, atten_n,
+                       &cd->atten_list[CY_ATTEN_STARTUP], node) {
+                       dev_dbg(cd->dev, "%s: attention for '%s'", __func__,
+                               dev_name(&atten->ttsp->dev));
+                       spin_unlock_irqrestore(&cd->spinlock, flags);
+                       atten->func(atten->ttsp);
+                       spin_lock_irqsave(&cd->spinlock, flags);
+               }
+               spin_unlock_irqrestore(&cd->spinlock, flags);
+       }
+       /* Required for signal to the TTHE */
+       dev_info(cd->dev, "%s: cyttsp4_exit startup r=%d...\n", __func__, rc);
+
+       return rc;
+}
+
+static int cyttsp4_startup(struct cyttsp4_core_data *cd)
+{
+       int rc;
+       rc = request_exclusive(cd, cd->core, CY_REQUEST_EXCLUSIVE_TIMEOUT);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n",
+                               __func__, cd->exclusive_dev, cd->core);
+               return rc;
+       }
+
+       rc = cyttsp4_startup_(cd);
+
+       if (release_exclusive(cd, cd->core) < 0)
+               /* Don't return fail code, mode is already changed. */
+               dev_err(cd->dev, "%s: fail to release exclusive\n", __func__);
+       else
+               dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__);
+
+       return rc;
+}
+
+static void cyttsp4_startup_work_function(struct work_struct *work)
+{
+       struct cyttsp4_core_data *cd =  container_of(work,
+               struct cyttsp4_core_data, startup_work);
+       int rc;
+
+       cd->exclusive_dev = NULL;
+       rc = cyttsp4_startup(cd);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Fail queued startup r=%d\n",
+                       __func__, rc);
+}
+
+static void cyttsp4_free_si_ptrs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+
+       kfree(si->si_ptrs.cydata);
+       kfree(si->si_ptrs.test);
+       kfree(si->si_ptrs.pcfg);
+       kfree(si->si_ptrs.opcfg);
+       kfree(si->si_ptrs.ddata);
+       kfree(si->si_ptrs.mdata);
+       kfree(si->btn);
+       kfree(si->xy_mode);
+       kfree(si->xy_data);
+       kfree(si->btn_rec_data);
+}
+
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
+static int cyttsp4_core_suspend(struct device *dev)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int rc;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       rc = cyttsp4_core_sleep(cd);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error on sleep\n", __func__);
+               return -EAGAIN;
+       }
+       return 0;
+}
+
+static int cyttsp4_core_resume(struct device *dev)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int rc;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       rc = cyttsp4_core_wake(cd);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error on wake\n", __func__);
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops cyttsp4_core_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume)
+       SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL)
+};
+
+/*
+ * Show Firmware version via sysfs
+ */
+static ssize_t cyttsp4_ic_ver_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       struct cyttsp4_cydata *cydata = cd->sysinfo.si_ptrs.cydata;
+
+       return sprintf(buf,
+               "%s: 0x%02X 0x%02X\n"
+               "%s: 0x%02X\n"
+               "%s: 0x%02X\n"
+               "%s: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n"
+               "%s: 0x%02X\n"
+               "%s: 0x%02X\n",
+               "TrueTouch Product ID", cydata->ttpidh, cydata->ttpidl,
+               "Firmware Major Version", cydata->fw_ver_major,
+               "Firmware Minor Version", cydata->fw_ver_minor,
+               "Revision Control Number", cydata->revctrl[0],
+               cydata->revctrl[1], cydata->revctrl[2], cydata->revctrl[3],
+               cydata->revctrl[4], cydata->revctrl[5], cydata->revctrl[6],
+               cydata->revctrl[7],
+               "Bootloader Major Version", cydata->blver_major,
+               "Bootloader Minor Version", cydata->blver_minor);
+}
+
+/*
+ * Show Driver version via sysfs
+ */
+static ssize_t cyttsp4_drv_ver_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, CY_MAX_PRBUF_SIZE,
+               "Driver: %s\nVersion: %s\nDate: %s\n",
+               cy_driver_core_name, cy_driver_core_version,
+               cy_driver_core_date);
+}
+
+/*
+ * HW reset via sysfs
+ */
+static ssize_t cyttsp4_hw_reset_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int rc = 0;
+
+       rc = cyttsp4_startup(cd);
+       if (rc < 0)
+               dev_err(dev, "%s: HW reset failed r=%d\n",
+                       __func__, rc);
+
+       return size;
+}
+
+/*
+ * Show IRQ status via sysfs
+ */
+static ssize_t cyttsp4_hw_irq_stat_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int retval;
+
+       if (cd->pdata->irq_stat) {
+               retval = cd->pdata->irq_stat(cd->pdata, dev);
+               switch (retval) {
+               case 0:
+                       return snprintf(buf, CY_MAX_PRBUF_SIZE,
+                               "Interrupt line is LOW.\n");
+               case 1:
+                       return snprintf(buf, CY_MAX_PRBUF_SIZE,
+                               "Interrupt line is HIGH.\n");
+               default:
+                       return snprintf(buf, CY_MAX_PRBUF_SIZE,
+                               "Function irq_stat() returned %d.\n", retval);
+               }
+       }
+
+       return snprintf(buf, CY_MAX_PRBUF_SIZE,
+               "Function irq_stat() undefined.\n");
+}
+
+/*
+ * Show IRQ enable/disable status via sysfs
+ */
+static ssize_t cyttsp4_drv_irq_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       ssize_t ret;
+
+       mutex_lock(&cd->system_lock);
+       if (cd->irq_enabled)
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                       "Driver interrupt is ENABLED\n");
+       else
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                       "Driver interrupt is DISABLED\n");
+       mutex_unlock(&cd->system_lock);
+
+       return ret;
+}
+
+/*
+ * Enable/disable IRQ via sysfs
+ */
+static ssize_t cyttsp4_drv_irq_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       unsigned long value;
+       int retval = 0;
+
+       retval = kstrtoul(buf, 10, &value);
+       if (retval < 0) {
+               dev_err(dev, "%s: Invalid value\n", __func__);
+               goto cyttsp4_drv_irq_store_error_exit;
+       }
+
+       mutex_lock(&cd->system_lock);
+       switch (value) {
+       case 0:
+               if (cd->irq_enabled) {
+                       cd->irq_enabled = false;
+                       /* Disable IRQ */
+                       disable_irq_nosync(cd->irq);
+                       dev_info(dev, "%s: Driver IRQ now disabled\n",
+                               __func__);
+               } else
+                       dev_info(dev, "%s: Driver IRQ already disabled\n",
+                               __func__);
+               break;
+
+       case 1:
+               if (cd->irq_enabled == false) {
+                       cd->irq_enabled = true;
+                       /* Enable IRQ */
+                       enable_irq(cd->irq);
+                       dev_info(dev, "%s: Driver IRQ now enabled\n",
+                               __func__);
+               } else
+                       dev_info(dev, "%s: Driver IRQ already enabled\n",
+                               __func__);
+               break;
+
+       default:
+               dev_err(dev, "%s: Invalid value\n", __func__);
+       }
+       mutex_unlock(&(cd->system_lock));
+
+cyttsp4_drv_irq_store_error_exit:
+
+       return size;
+}
+
+/*
+ * Debugging options via sysfs
+ */
+static ssize_t cyttsp4_drv_debug_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       unsigned long value = 0;
+       int rc = 0;
+
+       rc = kstrtoul(buf, 10, &value);
+       if (rc < 0) {
+               dev_err(dev, "%s: Invalid value\n", __func__);
+               goto cyttsp4_drv_debug_store_exit;
+       }
+
+       switch (value) {
+       case CY_DBG_SUSPEND:
+               dev_info(dev, "%s: SUSPEND (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_core_sleep(cd);
+               if (rc)
+                       dev_err(dev, "%s: Suspend failed rc=%d\n",
+                               __func__, rc);
+               else
+                       dev_info(dev, "%s: Suspend succeeded\n", __func__);
+               break;
+
+       case CY_DBG_RESUME:
+               dev_info(dev, "%s: RESUME (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_core_wake(cd);
+               if (rc)
+                       dev_err(dev, "%s: Resume failed rc=%d\n",
+                               __func__, rc);
+               else
+                       dev_info(dev, "%s: Resume succeeded\n", __func__);
+               break;
+       case CY_DBG_SOFT_RESET:
+               dev_info(dev, "%s: SOFT RESET (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_hw_soft_reset(cd);
+               break;
+       case CY_DBG_RESET:
+               dev_info(dev, "%s: HARD RESET (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_hw_hard_reset(cd);
+               break;
+       default:
+               dev_err(dev, "%s: Invalid value\n", __func__);
+       }
+
+cyttsp4_drv_debug_store_exit:
+       return size;
+}
+
+/*
+ * Show system status on deep sleep status via sysfs
+ */
+static ssize_t cyttsp4_sleep_status_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       ssize_t ret;
+
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_ON)
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                               "Deep Sleep is ENABLED\n");
+       else
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                               "Deep Sleep is DISABLED\n");
+       mutex_unlock(&cd->system_lock);
+
+       return ret;
+}
+
+static struct device_attribute attributes[] = {
+       __ATTR(ic_ver, S_IRUGO, cyttsp4_ic_ver_show, NULL),
+       __ATTR(drv_ver, S_IRUGO, cyttsp4_drv_ver_show, NULL),
+       __ATTR(hw_reset, S_IWUSR, NULL, cyttsp4_hw_reset_store),
+       __ATTR(hw_irq_stat, S_IRUSR, cyttsp4_hw_irq_stat_show, NULL),
+       __ATTR(drv_irq, S_IRUSR | S_IWUSR, cyttsp4_drv_irq_show,
+               cyttsp4_drv_irq_store),
+       __ATTR(drv_debug, S_IWUSR, NULL, cyttsp4_drv_debug_store),
+       __ATTR(sleep_status, S_IRUSR, cyttsp4_sleep_status_show, NULL),
+};
+
+static int add_sysfs_interfaces(struct device *dev)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(attributes); i++)
+               if (device_create_file(dev, attributes + i))
+                       goto undo;
+       return 0;
+undo:
+       for (; i >= 0 ; i--)
+               device_remove_file(dev, attributes + i);
+       dev_err(dev, "%s: failed to create sysfs interface\n", __func__);
+       return -ENODEV;
+}
+
+static void remove_sysfs_interfaces(struct device *dev)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(attributes); i++)
+               device_remove_file(dev, attributes + i);
+}
+
+static int cyttsp4_core_probe(struct cyttsp4_core *core)
+{
+       struct cyttsp4_core_data *cd;
+       struct device *dev = &core->dev;
+       struct cyttsp4_core_platform_data *pdata = dev_get_platdata(dev);
+       enum cyttsp4_atten_type type;
+       unsigned long irq_flags;
+       int rc = 0;
+
+       dev_info(dev, "%s: startup\n", __func__);
+       dev_dbg(dev, "%s: debug on\n", __func__);
+       dev_vdbg(dev, "%s: verbose debug on\n", __func__);
+
+       /* get context and debug print buffers */
+       cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+       if (cd == NULL) {
+               dev_err(dev, "%s: Error, kzalloc\n", __func__);
+               rc = -ENOMEM;
+               goto error_alloc_data_failed;
+       }
+
+       /* point to core device and init lists */
+       cd->core = core;
+       mutex_init(&cd->system_lock);
+       mutex_init(&cd->adap_lock);
+       for (type = 0; type < CY_ATTEN_NUM_ATTEN; type++)
+               INIT_LIST_HEAD(&cd->atten_list[type]);
+       init_waitqueue_head(&cd->wait_q);
+       init_waitqueue_head(&cd->sleep_q);
+       cd->startup_work_q = create_singlethread_workqueue("startup_work_q");
+       if (cd->startup_work_q == NULL) {
+               dev_err(dev, "%s: No memory for %s\n", __func__,
+                       "startup_work_q");
+               goto error_init;
+       }
+
+       dev_dbg(dev, "%s: initialize core data\n", __func__);
+       spin_lock_init(&cd->spinlock);
+       cd->dev = dev;
+       cd->pdata = pdata;
+       cd->irq = gpio_to_irq(pdata->irq_gpio);
+       cd->irq_enabled = true;
+       dev_set_drvdata(dev, cd);
+       if (cd->irq < 0) {
+               rc = -EINVAL;
+               goto error_gpio_irq;
+       }
+
+       if (cd->pdata->init) {
+               dev_info(cd->dev, "%s: Init HW\n", __func__);
+               rc = cd->pdata->init(cd->pdata, 1, cd->dev);
+       } else {
+               dev_info(cd->dev, "%s: No HW INIT function\n", __func__);
+               rc = 0;
+       }
+       if (rc < 0)
+               dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc);
+
+       INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function);
+
+       dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq);
+       if (cd->pdata->level_irq_udelay > 0)
+               /* use level triggered interrupts */
+               irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
+       else
+               /* use edge triggered interrupts */
+               irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+
+       rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags,
+               dev_name(dev), cd);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error, could not request irq\n", __func__);
+               goto error_request_irq;
+       }
+
+       INIT_WORK(&cd->work, cyttsp4_timer_watchdog);
+       setup_timer(&cd->timer, cyttsp4_timer, (unsigned long)cd);
+
+       dev_dbg(dev, "%s: add sysfs interfaces\n", __func__);
+       rc = add_sysfs_interfaces(dev);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error, fail sysfs init\n", __func__);
+               goto error_attr_create;
+       }
+
+       pm_runtime_enable(dev);
+
+       /*
+        * call startup directly to ensure that the device
+        * is tested before leaving the probe
+        */
+       dev_dbg(dev, "%s: call startup\n", __func__);
+
+       pm_runtime_get_sync(dev);
+       rc = cyttsp4_startup(cd);
+       pm_runtime_put(dev);
+
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: Fail initial startup r=%d\n",
+                       __func__, rc);
+               goto error_startup;
+       }
+
+       dev_info(dev, "%s: ok\n", __func__);
+       rc = 0;
+       goto no_error;
+
+error_startup:
+       pm_runtime_disable(dev);
+       cyttsp4_free_si_ptrs(cd);
+error_attr_create:
+       free_irq(cd->irq, cd);
+error_request_irq:
+error_gpio_irq:
+       if (pdata->init)
+               pdata->init(pdata, 0, dev);
+error_init:
+       dev_set_drvdata(dev, NULL);
+       kfree(cd);
+error_alloc_data_failed:
+       dev_err(dev, "%s failed.\n", __func__);
+no_error:
+       return rc;
+}
+
+static int cyttsp4_core_release(struct cyttsp4_core *core)
+{
+       struct device *dev = &core->dev;
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       cyttsp4_stop_wd_timer(cd);
+
+       pm_runtime_suspend(dev);
+       pm_runtime_disable(dev);
+
+       remove_sysfs_interfaces(dev);
+       free_irq(cd->irq, cd);
+       if (cd->pdata->init)
+               cd->pdata->init(cd->pdata, 0, dev);
+       dev_set_drvdata(dev, NULL);
+       cyttsp4_free_si_ptrs(cd);
+       kfree(cd);
+       return 0;
+}
+
+struct cyttsp4_core_driver cyttsp4_core_driver = {
+       .probe = cyttsp4_core_probe,
+       .remove = cyttsp4_core_release,
+       .subscribe_attention = cyttsp4_subscribe_attention_,
+       .unsubscribe_attention = cyttsp4_unsubscribe_attention_,
+       .request_exclusive = cyttsp4_request_exclusive_,
+       .release_exclusive = cyttsp4_release_exclusive_,
+       .request_reset = cyttsp4_request_reset_,
+       .request_restart = cyttsp4_request_restart_,
+       .request_set_mode = cyttsp4_request_set_mode_,
+       .request_sysinfo = cyttsp4_request_sysinfo_,
+       .request_handshake = cyttsp4_request_handshake_,
+       .request_exec_cmd = cyttsp4_request_exec_cmd_,
+       .request_stop_wd = cyttsp4_request_stop_wd_,
+       .request_toggle_lowpower = cyttsp4_request_toggle_lowpower_,
+       .write = cyttsp4_write_,
+       .read = cyttsp4_read_,
+       .driver = {
+               .name = CYTTSP4_CORE_NAME,
+               .bus = &cyttsp4_bus_type,
+               .owner = THIS_MODULE,
+               .pm = &cyttsp4_core_pm_ops,
+       },
+};
+
+static int __init cyttsp4_core_init(void)
+{
+       int rc = 0;
+
+       rc = cyttsp4_register_core_driver(&cyttsp4_core_driver);
+       pr_info("%s: Cypress TTSP v4 core driver (Built %s @ %s) rc=%d\n",
+                __func__, __DATE__, __TIME__, rc);
+       return rc;
+}
+module_init(cyttsp4_core_init);
+
+static void __exit cyttsp4_core_exit(void)
+{
+       cyttsp4_unregister_core_driver(&cyttsp4_core_driver);
+       pr_info("%s: module exit\n", __func__);
+}
+module_exit(cyttsp4_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver");
+MODULE_AUTHOR("Aleksej Makarov <aleksej.makarov@sonyericsson.com>");
diff --git a/drivers/input/touchscreen/cyttsp4_regs.h b/drivers/input/touchscreen/cyttsp4_regs.h
new file mode 100644
index 0000000..a5753e0
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_regs.h
@@ -0,0 +1,445 @@
+/*
+ * cyttsp4_regs.h
+ * Cypress TrueTouch(TM) Standard Product V4 registers.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications AB.
+ *
+ * Author: Aleksej Makarov <aleksej.makarov@sonyericsson.com>
+ * Modified by: Cypress Semiconductor to add test modes and commands
+ *
+ * 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 _CYTTSP4_REGS_H
+#define _CYTTSP4_REGS_H
+
+#define CY_FW_FILE_NAME "cyttsp4_fw.bin"
+
+#define CY_MAX_PRBUF_SIZE           PIPE_BUF
+#define CY_PR_TRUNCATED             " truncated..."
+
+#define CY_TMA1036_TCH_REC_SIZE     6
+#define CY_TMA4XX_TCH_REC_SIZE      9
+#define CY_TMA1036_MAX_TCH          0x0E
+#define CY_TMA4XX_MAX_TCH           0x1E
+
+#define GET_HSTMODE(reg)            ((reg & 0x70) >> 4)
+#define GET_TOGGLE(reg)             ((reg & 0x80) >> 7)
+#define IS_BOOTLOADER(reg)          ((reg) == 0x01)
+#define IS_EXCLUSIVE(dev)           ((dev) != NULL)
+#define IS_TMO(t)                   ((t) == 0)
+
+#define CY_REG_BASE                 0x00
+#define CY_NUM_REVCTRL              8
+#define CY_NUM_MFGID                8
+#define CY_NUM_TCHREC               10
+#define CY_NUM_DDATA                32
+#define CY_NUM_MDATA                64
+
+#define CY_REG_CAT_CMD              2
+#define CY_CMD_COMPLETE_MASK        (1 << 6)
+#define CY_CMD_MASK                 0x3F
+#define CY_EBID                     0
+
+/* touch record system information offset masks and shifts */
+#define CY_BYTE_OFS_MASK            0x1F
+#define CY_BOFS_MASK                0xE0
+#define CY_BOFS_SHIFT               5
+
+#define CY_REQUEST_EXCLUSIVE_TIMEOUT   500
+
+/* maximum number of concurrent tracks */
+#define CY_NUM_TCH_ID               10
+
+#define CY_ACTIVE_STYLUS_ID         10
+
+/* helpers */
+#define GET_NUM_TOUCHES(x)          ((x) & 0x1F)
+#define IS_LARGE_AREA(x)            ((x) & 0x20)
+#define IS_BAD_PKT(x)               ((x) & 0x20)
+
+#define CY_WATCHDOG_TIMEOUT msecs_to_jiffies(1000)
+
+/* drv_debug commands */
+#define CY_DBG_SUSPEND                  4
+#define CY_DBG_RESUME                   5
+#define CY_DBG_SOFT_RESET               97
+#define CY_DBG_RESET                    98
+
+enum cyttsp4_hst_mode_bits {
+       CY_HST_TOGGLE      = (1 << 7),
+       CY_HST_MODE_CHANGE = (1 << 3),
+       CY_HST_MODE        = (7 << 4),
+       CY_HST_OPERATE     = (0 << 4),
+       CY_HST_SYSINFO     = (1 << 4),
+       CY_HST_CAT         = (2 << 4),
+       CY_HST_LOWPOW      = (1 << 2),
+       CY_HST_SLEEP       = (1 << 1),
+       CY_HST_RESET       = (1 << 0),
+};
+
+enum cyttsp_cmd_bits {
+       CY_CMD_COMPLETE    = (1 << 6),
+};
+
+enum cyttsp4_cmd_cat {
+       CY_CMD_CAT_NULL,
+       CY_CMD_CAT_RESERVED_1,
+       CY_CMD_CAT_GET_CFG_BLK_SZ,
+       CY_CMD_CAT_READ_CFG_BLK,
+       CY_CMD_CAT_WRITE_CFG_BLK,
+       CY_CMD_CAT_RESERVED_2,
+       CY_CMD_CAT_LOAD_SELF_TEST_DATA,
+       CY_CMD_CAT_RUN_SELF_TEST,
+       CY_CMD_CAT_GET_SELF_TEST_RESULT,
+       CY_CMD_CAT_CALIBRATE_IDACS,
+       CY_CMD_CAT_INIT_BASELINES,
+       CY_CMD_CAT_EXEC_PANEL_SCAN,
+       CY_CMD_CAT_RETRIEVE_PANEL_SCAN,
+       CY_CMD_CAT_START_SENSOR_DATA_MODE,
+       CY_CMD_CAT_STOP_SENSOR_DATA_MODE,
+       CY_CMD_CAT_INT_PIN_MODE,
+       CY_CMD_CAT_RETRIEVE_DATA_STRUCTURE,
+       CY_CMD_CAT_VERIFY_CFG_BLK_CRC,
+       CY_CMD_CAT_RESERVED_N,
+};
+
+enum cyttsp4_tt_mode_bits {
+       CY_TT_BL     = (1 << 4),
+       CY_TT_INVAL  = (1 << 5),
+       CY_TT_CNTR   = (3 << 6),
+};
+
+enum cyttsp4_bl_status_bits {
+       CY_BL_CS_OK    = (1 << 0),
+       CY_BL_WDOG     = (1 << 1),
+       CY_BL_RUNNING  = (1 << 4),
+       CY_BL_BUSY     = (1 << 7),
+};
+
+/* times */
+#define CY_SCAN_PERIOD              40
+#define CY_BL_ENTER_TIME            100
+
+enum cyttsp4_mode {
+       CY_MODE_UNKNOWN      = 0,
+       CY_MODE_BOOTLOADER   = (1 << 1),
+       CY_MODE_OPERATIONAL  = (1 << 2),
+       CY_MODE_SYSINFO      = (1 << 3),
+       CY_MODE_CAT          = (1 << 4),
+       CY_MODE_STARTUP      = (1 << 5),
+       CY_MODE_LOADER       = (1 << 6),
+       CY_MODE_CHANGE_MODE  = (1 << 7),
+       CY_MODE_CHANGED      = (1 << 8),
+       CY_MODE_CMD_COMPLETE = (1 << 9),
+};
+
+enum cyttsp4_int_state {
+       CY_INT_IGNORE      = (1 << 0),
+       CY_INT_MODE_CHANGE = (1 << 1),
+       CY_INT_EXEC_CMD    = (1 << 2),
+       CY_INT_AWAKE       = (1 << 3),
+};
+
+enum cyttsp4_ic_grpnum {
+       CY_IC_GRPNUM_RESERVED,
+       CY_IC_GRPNUM_CMD_REGS,
+       CY_IC_GRPNUM_TCH_REP,
+       CY_IC_GRPNUM_DATA_REC,
+       CY_IC_GRPNUM_TEST_REC,
+       CY_IC_GRPNUM_PCFG_REC,
+       CY_IC_GRPNUM_TCH_PARM_VAL,
+       CY_IC_GRPNUM_TCH_PARM_SIZE,
+       CY_IC_GRPNUM_RESERVED1,
+       CY_IC_GRPNUM_RESERVED2,
+       CY_IC_GRPNUM_OPCFG_REC,
+       CY_IC_GRPNUM_DDATA_REC,
+       CY_IC_GRPNUM_MDATA_REC,
+       CY_IC_GRPNUM_TEST_REGS,
+       CY_IC_GRPNUM_BTN_KEYS,
+       CY_IC_GRPNUM_TTHE_REGS,
+       CY_IC_GRPNUM_NUM
+};
+
+#define CY_VKEYS_X 720
+#define CY_VKEYS_Y 1280
+
+enum cyttsp4_flags {
+       CY_FLAG_NONE = 0x00,
+       CY_FLAG_HOVER = 0x04,
+       CY_FLAG_FLIP = 0x08,
+       CY_FLAG_INV_X = 0x10,
+       CY_FLAG_INV_Y = 0x20,
+       CY_FLAG_VKEYS = 0x40,
+};
+
+enum cyttsp4_event_id {
+       CY_EV_NO_EVENT,
+       CY_EV_TOUCHDOWN,
+       CY_EV_MOVE,             /* significant displacement (> act dist) */
+       CY_EV_LIFTOFF,          /* record reports last position */
+};
+
+enum cyttsp4_object_id {
+       CY_OBJ_STANDARD_FINGER,
+       CY_OBJ_LARGE_OBJECT,
+       CY_OBJ_STYLUS,
+       CY_OBJ_HOVER,
+};
+
+#define CY_POST_CODEL_WDG_RST           0x01
+#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02
+#define CY_POST_CODEL_PANEL_TEST_FAIL   0x04
+
+#define CY_TEST_CMD_NULL                0
+
+/* test mode NULL command driver codes; D */
+enum cyttsp4_null_test_cmd_code {
+       CY_NULL_CMD_NULL,
+       CY_NULL_CMD_MODE,
+       CY_NULL_CMD_STATUS_SIZE,
+       CY_NULL_CMD_HANDSHAKE,
+       CY_NULL_CMD_LOW_POWER,
+};
+
+enum cyttsp4_test_mode {
+       CY_TEST_MODE_NORMAL_OP,         /* Send touch data to OS; normal op */
+       CY_TEST_MODE_CAT,               /* Configuration and Test */
+       CY_TEST_MODE_SYSINFO,           /* System information mode */
+       CY_TEST_MODE_CLOSED_UNIT,       /* Send scan data to sysfs */
+};
+
+struct cyttsp4_test_mode_params {
+       int cur_mode;
+       int cur_cmd;
+       size_t cur_status_size;
+};
+
+/* GEN4/SOLO Operational interface definitions */
+/* TTSP System Information interface definitions */
+struct cyttsp4_cydata {
+       u8 ttpidh;
+       u8 ttpidl;
+       u8 fw_ver_major;
+       u8 fw_ver_minor;
+       u8 revctrl[CY_NUM_REVCTRL];
+       u8 blver_major;
+       u8 blver_minor;
+       u8 jtag_si_id3;
+       u8 jtag_si_id2;
+       u8 jtag_si_id1;
+       u8 jtag_si_id0;
+       u8 mfgid_sz;
+       u8 mfg_id[CY_NUM_MFGID];
+       u8 cyito_idh;
+       u8 cyito_idl;
+       u8 cyito_verh;
+       u8 cyito_verl;
+       u8 ttsp_ver_major;
+       u8 ttsp_ver_minor;
+       u8 device_info;
+} __packed;
+
+struct cyttsp4_test {
+       u8 post_codeh;
+       u8 post_codel;
+} __packed;
+
+struct cyttsp4_pcfg {
+       u8 electrodes_x;
+       u8 electrodes_y;
+       u8 len_xh;
+       u8 len_xl;
+       u8 len_yh;
+       u8 len_yl;
+       u8 res_xh;
+       u8 res_xl;
+       u8 res_yh;
+       u8 res_yl;
+       u8 max_zh;
+       u8 max_zl;
+} __packed;
+
+enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */
+       CY_TCH_X,       /* X */
+       CY_TCH_Y,       /* Y */
+       CY_TCH_P,       /* P (Z) */
+       CY_TCH_T,       /* TOUCH ID */
+       CY_TCH_E,       /* EVENT ID */
+       CY_TCH_O,       /* OBJECT ID */
+       CY_TCH_W,       /* SIZE */
+       CY_TCH_MAJ,     /* TOUCH_MAJOR */
+       CY_TCH_MIN,     /* TOUCH_MINOR */
+       CY_TCH_OR,      /* ORIENTATION */
+       CY_TCH_NUM_ABS
+};
+
+static const char * const cyttsp4_tch_abs_string[] = {
+       [CY_TCH_X]      = "X",
+       [CY_TCH_Y]      = "Y",
+       [CY_TCH_P]      = "P",
+       [CY_TCH_T]      = "T",
+       [CY_TCH_E]      = "E",
+       [CY_TCH_O]      = "O",
+       [CY_TCH_W]      = "W",
+       [CY_TCH_MAJ]    = "MAJ",
+       [CY_TCH_MIN]    = "MIN",
+       [CY_TCH_OR]     = "OR",
+       [CY_TCH_NUM_ABS] = "INVALID"
+};
+
+#define CY_NUM_TCH_FIELDS       7
+#define CY_NUM_EXT_TCH_FIELDS   3
+
+struct cyttsp4_tch_rec_params {
+       u8 loc;
+       u8 size;
+} __packed;
+
+struct cyttsp4_opcfg {
+       u8 cmd_ofs;
+       u8 rep_ofs;
+       u8 rep_szh;
+       u8 rep_szl;
+       u8 num_btns;
+       u8 tt_stat_ofs;
+       u8 obj_cfg0;
+       u8 max_tchs;
+       u8 tch_rec_size;
+       struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS];
+       u8 btn_rec_size;/* btn record size (in bytes) */
+       u8 btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+       u8 btn_diff_size;/* btn size of diff counts (in bits) */
+       struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS];
+} __packed;
+
+struct cyttsp4_sysinfo_data {
+       u8 hst_mode;
+       u8 reserved;
+       u8 map_szh;
+       u8 map_szl;
+       u8 cydata_ofsh;
+       u8 cydata_ofsl;
+       u8 test_ofsh;
+       u8 test_ofsl;
+       u8 pcfg_ofsh;
+       u8 pcfg_ofsl;
+       u8 opcfg_ofsh;
+       u8 opcfg_ofsl;
+       u8 ddata_ofsh;
+       u8 ddata_ofsl;
+       u8 mdata_ofsh;
+       u8 mdata_ofsl;
+} __packed;
+
+struct cyttsp4_sysinfo_ptr {
+       struct cyttsp4_cydata *cydata;
+       struct cyttsp4_test *test;
+       struct cyttsp4_pcfg *pcfg;
+       struct cyttsp4_opcfg *opcfg;
+       struct cyttsp4_ddata *ddata;
+       struct cyttsp4_mdata *mdata;
+} __packed;
+
+struct cyttsp4_touch {
+       int abs[CY_TCH_NUM_ABS];
+};
+
+struct cyttsp4_tch_abs_params {
+       size_t ofs;     /* abs byte offset */
+       size_t size;    /* size in bits */
+       size_t max;     /* max value */
+       size_t bofs;    /* bit offset */
+};
+
+#define CY_NORMAL_ORIGIN 0     /* upper, left corner */
+#define CY_INVERT_ORIGIN 1     /* lower, right corner */
+
+struct cyttsp4_sysinfo_ofs {
+       size_t chip_type;
+       size_t cmd_ofs;
+       size_t rep_ofs;
+       size_t rep_sz;
+       size_t num_btns;
+       size_t num_btn_regs;    /* ceil(num_btns/4) */
+       size_t tt_stat_ofs;
+       size_t tch_rec_size;
+       size_t obj_cfg0;
+       size_t max_tchs;
+       size_t mode_size;
+       size_t data_size;
+       size_t map_sz;
+       size_t max_x;
+       size_t x_origin;        /* left or right corner */
+       size_t max_y;
+       size_t y_origin;        /* upper or lower corner */
+       size_t max_p;
+       size_t cydata_ofs;
+       size_t test_ofs;
+       size_t pcfg_ofs;
+       size_t opcfg_ofs;
+       size_t ddata_ofs;
+       size_t mdata_ofs;
+       size_t cydata_size;
+       size_t test_size;
+       size_t pcfg_size;
+       size_t opcfg_size;
+       size_t ddata_size;
+       size_t mdata_size;
+       size_t btn_keys_size;
+       struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
+       size_t btn_rec_size; /* btn record size (in bytes) */
+       size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+       size_t btn_diff_size;/* btn size of diff counts (in bits) */
+};
+
+/* button to keycode support */
+#define CY_NUM_BTN_PER_REG     4
+#define CY_NUM_BTN_EVENT_ID    4
+#define CY_BITS_PER_BTN                2
+
+enum cyttsp4_btn_state {
+       CY_BTN_RELEASED = 0,
+       CY_BTN_PRESSED = 1,
+       CY_BTN_NUM_STATE
+};
+
+struct cyttsp4_btn {
+       bool enabled;
+       int state;      /* CY_BTN_PRESSED, CY_BTN_RELEASED */
+       int key_code;
+};
+
+
+struct cyttsp4_sysinfo {
+       bool ready;
+       struct cyttsp4_sysinfo_data si_data;
+       struct cyttsp4_sysinfo_ptr si_ptrs;
+       struct cyttsp4_sysinfo_ofs si_ofs;
+       struct cyttsp4_btn *btn;        /* button states */
+       u8 *btn_rec_data;               /* button diff count data */
+       u8 *xy_mode;                    /* operational mode and status regs */
+       u8 *xy_data;                    /* operational touch regs */
+};
+
+#endif /* _CYTTSP4_REGS_H */
diff --git a/include/linux/cyttsp4_core.h b/include/linux/cyttsp4_core.h
new file mode 100644
index 0000000..17a6fd4
--- /dev/null
+++ b/include/linux/cyttsp4_core.h
@@ -0,0 +1,97 @@
+/*
+ * cyttsp4_core.h
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver 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.
+ *
+ * Author: Aleksej Makarov <aleksej.makarov@sonyericsson.com>
+ * Modifed by: Cypress Semiconductor to add touch settings
+ *
+ * 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_CORE_H
+#define _LINUX_CYTTSP4_CORE_H
+
+#define CYTTSP4_CORE_NAME "cyttsp4_core"
+
+#define CYTTSP4_STR(x) #x
+#define CYTTSP4_STRINGIFY(x) CYTTSP4_STR(x)
+
+#define CY_DRIVER_NAME TTDA
+#define CY_DRIVER_MAJOR 01
+#define CY_DRIVER_MINOR 01
+
+#define CY_DRIVER_REVCTRL 000000
+
+#define CY_DRIVER_VERSION                          \
+CYTTSP4_STRINGIFY(CY_DRIVER_NAME)                  \
+"." CYTTSP4_STRINGIFY(CY_DRIVER_MAJOR)             \
+"." CYTTSP4_STRINGIFY(CY_DRIVER_MINOR)             \
+"." CYTTSP4_STRINGIFY(CY_DRIVER_REVCTRL)
+
+#define CY_DRIVER_DATE "20120705"      /* YYYYMMDD */
+
+/* x-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_X_MASK 0x7F
+
+/* y-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_Y_MASK 0x7F
+
+/* x-axis, 0:origin is on left side of panel, 1: right */
+#define CY_PCFG_ORIGIN_X_MASK 0x80
+
+/* y-axis, 0:origin is on top side of panel, 1: bottom */
+#define CY_PCFG_ORIGIN_Y_MASK 0x80
+
+#define CY_TOUCH_SETTINGS_MAX 32
+
+struct touch_settings {
+       const uint8_t   *data;
+       uint32_t         size;
+       uint8_t         tag;
+} __packed;
+
+struct cyttsp4_core_platform_data {
+       int irq_gpio;
+       int rst_gpio;
+       int level_irq_udelay;
+       int (*xres)(struct cyttsp4_core_platform_data *pdata,
+               struct device *dev);
+       int (*init)(struct cyttsp4_core_platform_data *pdata,
+               int on, struct device *dev);
+       int (*power)(struct cyttsp4_core_platform_data *pdata,
+               int on, struct device *dev, atomic_t *ignore_irq);
+       int (*irq_stat)(struct cyttsp4_core_platform_data *pdata,
+               struct device *dev);
+       struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX];
+};
+
+#ifdef VERBOSE_DEBUG
+extern void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size,
+                          const char *data_name);
+#else
+#define cyttsp4_pr_buf(a, b, c, d, e) do { } while (0)
+#endif
+
+#endif /* _LINUX_CYTTSP4_CORE_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.

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

* [PATCH 3/4] Input: cyttsp4 - MultiTouch driver for Cypress TMA4XX touchscreen devices
       [not found] <Ferruh Yigit <fery@cypress.com>
  2012-08-07 13:09 ` [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices Ferruh Yigit
  2012-08-07 13:09 ` [PATCH 2/4] Input: cyttsp4 - core " Ferruh Yigit
@ 2012-08-07 13:10 ` Ferruh Yigit
  2012-08-07 13:10 ` [PATCH 4/4] Input: cyttsp4 - I2C " Ferruh Yigit
  2012-09-14 17:48 ` [PATCH v2 0/3] Input: cyttsp4 - " Ferruh Yigit
  4 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-08-07 13:10 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Ferruh YIGIT, Javier Martinez Canillas,
	Henrik Rydberg, Shawn Landden, Ashish Jangam, Olivier Sobrie,
	linux-input, linux-kernel

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

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 */
--
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.

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

* [PATCH 4/4] Input: cyttsp4 - I2C driver for Cypress TMA4XX touchscreen devices
       [not found] <Ferruh Yigit <fery@cypress.com>
                   ` (2 preceding siblings ...)
  2012-08-07 13:10 ` [PATCH 3/4] Input: cyttsp4 - MultiTouch " Ferruh Yigit
@ 2012-08-07 13:10 ` Ferruh Yigit
  2012-09-14 17:48 ` [PATCH v2 0/3] Input: cyttsp4 - " Ferruh Yigit
  4 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-08-07 13:10 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Ferruh YIGIT, Javier Martinez Canillas,
	Henrik Rydberg, Shawn Landden, Ashish Jangam, Olivier Sobrie,
	linux-input, linux-kernel

From: Ferruh YIGIT <fery@cypress.com>

Cypress TrueTouch(tm) Standard Product controllers, Generation4 devices,
I2C adapter module.

This driver adds communication support with TTSP controller using I2C
bus.

Signed-off-by: Ferruh YIGIT <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig       |   13 ++
 drivers/input/touchscreen/Makefile      |    3 +
 drivers/input/touchscreen/cyttsp4_i2c.c |  246 +++++++++++++++++++++++++++++++
 drivers/input/touchscreen/cyttsp4_i2c.h |   35 +++++
 4 files changed, 297 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 57a38f8..58f5d0b 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_I2C
        select TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B
        help
          Core driver for Cypress TrueTouch(tm) Standard Product
@@ -223,6 +224,18 @@ config TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG

          Say Y here to enable verbose debug output.

+config TOUCHSCREEN_CYPRESS_CYTTSP4_I2C
+       tristate "Cypress TrueTouch Gen4 I2C"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4
+       select I2C
+       default m
+       help
+         Cypress TrueTouch(tm) Standard Product Generation4
+         I2C bus interface.
+
+         Say Y here to enable I2C bus interface to TTSP
+         touchscreen controller.
+
 config TOUCHSCREEN_CYPRESS_CYTTSP4_MT_B
        tristate "Cypress TrueTouch Gen4 MultiTouch Protocol B"
        depends on TOUCHSCREEN_CYPRESS_CYTTSP4
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 8feae16..c5e5fc6 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -75,11 +75,13 @@ 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_I2C)  += cyttsp4_i2c.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_i2c.o += -DDEBUG
 CFLAGS_cyttsp4_mtb.o += -DDEBUG
 CFLAGS_cyttsp4_mt_b.o += -DDEBUG
 CFLAGS_cyttsp4_mt_common.o += -DDEBUG
@@ -88,6 +90,7 @@ endif
 ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG),y)
 CFLAGS_cyttsp4_core.o += -DVERBOSE_DEBUG
 CFLAGS_cyttsp4_bus.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_i2c.o += -DVERBOSE_DEBUG
 CFLAGS_cyttsp4_mtb.o += -DVERBOSE_DEBUG
 CFLAGS_cyttsp4_mt_b.o += -DVERBOSE_DEBUG
 CFLAGS_cyttsp4_mt_common.o += -DVERBOSE_DEBUG
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c
new file mode 100644
index 0000000..e330219
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_i2c.c
@@ -0,0 +1,246 @@
+/*
+ * cyttsp4_i2c.c
+ * Cypress TrueTouch(TM) Standard Product V4 I2C Driver 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.
+ *
+ * Author: Aleksej Makarov <aleksej.makarov@sonyericsson.com>
+ * Modified by: Cypress Semiconductor for test with device
+ *
+ * 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 "cyttsp4_i2c.h"
+
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#define CY_I2C_DATA_SIZE  (3 * 256)
+
+struct cyttsp4_i2c {
+       struct i2c_client *client;
+       u8 wr_buf[CY_I2C_DATA_SIZE];
+       struct hrtimer timer;
+       struct mutex lock;
+       atomic_t timeout;
+};
+
+static int cyttsp4_i2c_read_block_data(struct cyttsp4_i2c *ts_i2c, u8 addr,
+       size_t length, void *values)
+{
+       int rc;
+
+       /* write addr */
+       rc = i2c_master_send(ts_i2c->client, &addr, sizeof(addr));
+       if (rc < 0)
+               return rc;
+       else if (rc != sizeof(addr))
+               return -EIO;
+
+       /* read data */
+       rc = i2c_master_recv(ts_i2c->client, values, length);
+
+       return (rc < 0) ? rc : rc != length ? -EIO : 0;
+}
+
+static int cyttsp4_i2c_write_block_data(struct cyttsp4_i2c *ts_i2c, u8 addr,
+       size_t length, const void *values)
+{
+       int rc;
+
+       if (sizeof(ts_i2c->wr_buf) < (length + 1))
+               return -ENOMEM;
+
+       ts_i2c->wr_buf[0] = addr;
+       memcpy(&ts_i2c->wr_buf[1], values, length);
+       length += 1;
+
+       /* write data */
+       rc = i2c_master_send(ts_i2c->client, ts_i2c->wr_buf, length);
+
+       return (rc < 0) ? rc : rc != length ? -EIO : 0;
+}
+
+static int cyttsp4_i2c_write(struct cyttsp4_adapter *adap, u8 addr,
+       const void *buf, int size)
+{
+       struct cyttsp4_i2c *ts = dev_get_drvdata(adap->dev);
+       int rc;
+
+       pm_runtime_get_noresume(adap->dev);
+       mutex_lock(&ts->lock);
+       rc = cyttsp4_i2c_write_block_data(ts, addr, size, buf);
+       mutex_unlock(&ts->lock);
+       pm_runtime_put_noidle(adap->dev);
+
+       return rc;
+}
+
+static int cyttsp4_i2c_read(struct cyttsp4_adapter *adap, u8 addr,
+       void *buf, int size)
+{
+       struct cyttsp4_i2c *ts = dev_get_drvdata(adap->dev);
+       int rc;
+
+       pm_runtime_get_noresume(adap->dev);
+       mutex_lock(&ts->lock);
+       rc = cyttsp4_i2c_read_block_data(ts, addr, size, buf);
+       mutex_unlock(&ts->lock);
+       pm_runtime_put_noidle(adap->dev);
+
+       return rc;
+}
+
+static struct cyttsp4_ops ops = {
+       .write = cyttsp4_i2c_write,
+       .read = cyttsp4_i2c_read,
+};
+
+static int __devinit cyttsp4_i2c_probe(struct i2c_client *client,
+       const struct i2c_device_id *i2c_id)
+{
+       struct cyttsp4_i2c *ts_i2c;
+       struct device *dev = &client->dev;
+       char const *adap_id = dev_get_platdata(dev);
+       char const *id;
+       int rc;
+
+       dev_info(dev, "%s: Starting %s probe...\n", __func__, CYTTSP4_I2C_NAME);
+
+       dev_dbg(dev, "%s: debug on\n", __func__);
+       dev_vdbg(dev, "%s: verbose debug on\n", __func__);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(dev, "%s: fail check I2C functionality\n", __func__);
+               rc = -EIO;
+               goto error_alloc_data_failed;
+       }
+
+       ts_i2c = kzalloc(sizeof(struct cyttsp4_i2c), GFP_KERNEL);
+       if (ts_i2c == NULL) {
+               dev_err(dev, "%s: Error, kzalloc.\n", __func__);
+               rc = -ENOMEM;
+               goto error_alloc_data_failed;
+       }
+
+       mutex_init(&ts_i2c->lock);
+       ts_i2c->client = client;
+       client->dev.bus = &i2c_bus_type;
+       i2c_set_clientdata(client, ts_i2c);
+       dev_set_drvdata(&client->dev, ts_i2c);
+
+       if (adap_id)
+               id = adap_id;
+       else
+               id = CYTTSP4_I2C_NAME;
+
+       dev_dbg(dev, "%s: add adap='%s' (CYTTSP4_I2C_NAME=%s)\n", __func__, id,
+               CYTTSP4_I2C_NAME);
+
+       pm_runtime_enable(&client->dev);
+
+       rc = cyttsp4_add_adapter(id, &ops, dev);
+       if (rc) {
+               dev_err(dev, "%s: Error on probe %s\n", __func__,
+                       CYTTSP4_I2C_NAME);
+               goto add_adapter_err;
+       }
+
+       dev_info(dev, "%s: Successful probe %s\n", __func__, CYTTSP4_I2C_NAME);
+
+       return 0;
+
+add_adapter_err:
+       pm_runtime_disable(&client->dev);
+       dev_set_drvdata(&client->dev, NULL);
+       i2c_set_clientdata(client, NULL);
+       kfree(ts_i2c);
+error_alloc_data_failed:
+       return rc;
+}
+
+/* registered in driver struct */
+static int __devexit cyttsp4_i2c_remove(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct cyttsp4_i2c *ts_i2c = dev_get_drvdata(dev);
+       char const *adap_id = dev_get_platdata(dev);
+       char const *id;
+
+       if (adap_id)
+               id = adap_id;
+       else
+               id = CYTTSP4_I2C_NAME;
+
+       dev_info(dev, "%s\n", __func__);
+       cyttsp4_del_adapter(id);
+       pm_runtime_disable(&client->dev);
+       dev_set_drvdata(&client->dev, NULL);
+       i2c_set_clientdata(client, NULL);
+       kfree(ts_i2c);
+       return 0;
+}
+
+static const struct i2c_device_id cyttsp4_i2c_id[] = {
+       { CYTTSP4_I2C_NAME, 0 },  { }
+};
+
+static struct i2c_driver cyttsp4_i2c_driver = {
+       .driver = {
+               .name = CYTTSP4_I2C_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = cyttsp4_i2c_probe,
+       .remove = __devexit_p(cyttsp4_i2c_remove),
+       .id_table = cyttsp4_i2c_id,
+};
+
+static int __init cyttsp4_i2c_init(void)
+{
+       int rc = i2c_add_driver(&cyttsp4_i2c_driver);
+
+       pr_info("%s: Cypress TTSP I2C Touchscreen Driver (Built %s @ %s) rc=%d\n",
+                __func__, __DATE__, __TIME__, rc);
+       return rc;
+}
+module_init(cyttsp4_i2c_init);
+
+static void __exit cyttsp4_i2c_exit(void)
+{
+       i2c_del_driver(&cyttsp4_i2c_driver);
+       pr_info("%s: module exit\n", __func__);
+}
+module_exit(cyttsp4_i2c_exit);
+
+MODULE_ALIAS(CYTTSP4_I2C_NAME);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
+MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id);
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.h b/drivers/input/touchscreen/cyttsp4_i2c.h
new file mode 100644
index 0000000..94fd104
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_i2c.h
@@ -0,0 +1,35 @@
+/*
+ * cyttsp4_i2c.h
+ * Cypress TrueTouch(TM) Standard Product V4 I2C driver 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_I2C_H
+#define _LINUX_CYTTSP4_I2C_H
+
+#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter"
+
+#endif /* _LINUX_CYTTSP4_I2C_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.

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

* Re: [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices
  2012-08-07 13:09 ` [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices Ferruh Yigit
@ 2012-08-07 20:05   ` Javier Martinez Canillas
  2012-08-08  5:53     ` Ferruh Yigit
  2012-08-24 13:06   ` Ferruh Yigit
  1 sibling, 1 reply; 20+ messages in thread
From: Javier Martinez Canillas @ 2012-08-07 20:05 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Dmitry Torokhov, Kevin McNeely, Henrik Rydberg, Shawn Landden,
	Ashish Jangam, Olivier Sobrie, linux-input, linux-kernel

On Tue, Aug 7, 2012 at 3:09 PM, Ferruh Yigit <fery@cypress.com> wrote:
> From: Ferruh YIGIT <fery@cypress.com>
>
> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
> Generation4 devices.
>
> Driver consist of four main modules:
>
> Bus driver: Linux bus driver implementation, binds other modules.
> Core driver: Core module that communicate with TTSP controller.
> MT driver: MultiTouch driver, converts touch information to host specific
> touch events
> Adapter driver: Communication adapter between host and controller, like
> I2C or SPI.
>
> This is Cyttsp4 TTSP Bus Driver,
> Provides binding between Adapter, Core, and TTSP Modules.
>
> A complete set of corresponding Adapter, Core, and TTSP module
> devices and drivers must be registered with the TTSP Bus handler
>

Hi Ferruh,

There is already a driver in the kernel that supports Cypress
TrueTouch(TM) Standard Product (TTSP) controllers Generation3 (Cypress
Txx3xx parts).

The driver has a similar architecture that yours and it has a generic
driver to control the device and a driver for each communication bus
used to communicate with the controller. Drivers for SPI and I2C data
buses are already implemented.

The drivers are:

drivers/input/touchscreen/cyttsp_core.c
drivers/input/touchscreen/cyttsp_i2c.c
drivers/input/touchscreen/cyttsp_spi.c

This driver was original developed by Kevin for Android and used
multi-touch protocol type A. Since the hardware is able to track
contacts by hardware I added protocol type B support and cleaned the
driver to be merged on mainline.

I wonder how big is the delta between cyttsp Gen3 and cyttsp Gen4 and
if both drivers could be merged or at least refactored to reuse some
common code. I don't have the specification for any of the device
families but by looking at your code it seems that this may be
possible.

Thanks a lot and best regards,
Javier

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

* Re: [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices
  2012-08-07 20:05   ` Javier Martinez Canillas
@ 2012-08-08  5:53     ` Ferruh Yigit
  0 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-08-08  5:53 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Dmitry Torokhov, Kevin McNeely, Henrik Rydberg, Shawn Landden,
	Ashish Jangam, Olivier Sobrie, linux-input, linux-kernel

Hello Javier,

We needed a new modular driver mechanism so that we can handle the very
flexible new generation of Cypress multi-touch devices (TMA4XX and TMA5XX).

With this initial set of patches we are providing the primary function
of multi-touch processing, according to protocol B, as the foundation
for future enhancements and future generations of our parts.

We plan to submit patches in the near future for additional modules such
as handling of integrated button codes and firmware class loader
capability.

The Gen3 memory map is very different from the Gen4 and Gen5 products,
however it is not difficult to upgrade the current Gen3 code into a new
set of modules that can fit into this model.

Adding a module patchset for Gen3 also will allow us to add Gen2 support
in that module as well.

Thanks and Best Regards,
ferruh

On 08/07/2012 11:05 PM, Javier Martinez Canillas wrote:
> On Tue, Aug 7, 2012 at 3:09 PM, Ferruh Yigit <fery@cypress.com> wrote:
>> From: Ferruh YIGIT <fery@cypress.com>
>>
>> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
>> Generation4 devices.
>>
>> Driver consist of four main modules:
>>
>> Bus driver: Linux bus driver implementation, binds other modules.
>> Core driver: Core module that communicate with TTSP controller.
>> MT driver: MultiTouch driver, converts touch information to host specific
>> touch events
>> Adapter driver: Communication adapter between host and controller, like
>> I2C or SPI.
>>
>> This is Cyttsp4 TTSP Bus Driver,
>> Provides binding between Adapter, Core, and TTSP Modules.
>>
>> A complete set of corresponding Adapter, Core, and TTSP module
>> devices and drivers must be registered with the TTSP Bus handler
>>
> Hi Ferruh,
>
> There is already a driver in the kernel that supports Cypress
> TrueTouch(TM) Standard Product (TTSP) controllers Generation3 (Cypress
> Txx3xx parts).
>
> The driver has a similar architecture that yours and it has a generic
> driver to control the device and a driver for each communication bus
> used to communicate with the controller. Drivers for SPI and I2C data
> buses are already implemented.
>
> The drivers are:
>
> drivers/input/touchscreen/cyttsp_core.c
> drivers/input/touchscreen/cyttsp_i2c.c
> drivers/input/touchscreen/cyttsp_spi.c
>
> This driver was original developed by Kevin for Android and used
> multi-touch protocol type A. Since the hardware is able to track
> contacts by hardware I added protocol type B support and cleaned the
> driver to be merged on mainline.
>
> I wonder how big is the delta between cyttsp Gen3 and cyttsp Gen4 and
> if both drivers could be merged or at least refactored to reuse some
> common code. I don't have the specification for any of the device
> families but by looking at your code it seems that this may be
> possible.
>
> Thanks a lot and best regards,
> Javier


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.

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

* Re: [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices
  2012-08-07 13:09 ` [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices Ferruh Yigit
  2012-08-07 20:05   ` Javier Martinez Canillas
@ 2012-08-24 13:06   ` Ferruh Yigit
  2012-08-24 17:34     ` Henrik Rydberg
  1 sibling, 1 reply; 20+ messages in thread
From: Ferruh Yigit @ 2012-08-24 13:06 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Ferruh Yigit, Dmitry Torokhov, Kevin McNeely,
	Javier Martinez Canillas, Shawn Landden, Ashish Jangam,
	Olivier Sobrie, linux-input, linux-kernel

On 08/07/2012 04:09 PM, Ferruh Yigit wrote:
> From: Ferruh YIGIT <fery@cypress.com>
>
> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
> Generation4 devices.
>
> Driver consist of four main modules:
>
> Bus driver: Linux bus driver implementation, binds other modules.
> Core driver: Core module that communicate with TTSP controller.
> MT driver: MultiTouch driver, converts touch information to host specific
> touch events
> Adapter driver: Communication adapter between host and controller, like
> I2C or SPI.
>
> This is Cyttsp4 TTSP Bus Driver,
> Provides binding between Adapter, Core, and TTSP Modules.
>
> A complete set of corresponding Adapter, Core, and TTSP module
> devices and drivers must be registered with the TTSP Bus handler

Hi Henrik,

Is driver reviewed and is there anything we can help on it?

--

Thanks and Regards,
ferruh


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.

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

* Re: [PATCH 2/4] Input: cyttsp4 - core driver for Cypress TMA4XX touchscreen devices
  2012-08-07 13:09 ` [PATCH 2/4] Input: cyttsp4 - core " Ferruh Yigit
@ 2012-08-24 14:21   ` Michal Marek
  2012-08-27  5:49     ` Ferruh Yigit
  0 siblings, 1 reply; 20+ messages in thread
From: Michal Marek @ 2012-08-24 14:21 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Dmitry Torokhov, Kevin McNeely, Javier Martinez Canillas,
	Henrik Rydberg, Shawn Landden, Ashish Jangam, Olivier Sobrie,
	linux-input, linux-kernel

Dne 7.8.2012 15:09, Ferruh Yigit napsal(a):
> +static int __init cyttsp4_core_init(void)
> +{
> +       int rc = 0;
> +
> +       rc = cyttsp4_register_core_driver(&cyttsp4_core_driver);
> +       pr_info("%s: Cypress TTSP v4 core driver (Built %s @ %s) rc=%d\n",
> +                __func__, __DATE__, __TIME__, rc);

Hi,

please drop the __DATE__ and __TIME__ macros to keep the build
deterministic.

thanks,
Michal

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

* Re: [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices
  2012-08-24 13:06   ` Ferruh Yigit
@ 2012-08-24 17:34     ` Henrik Rydberg
       [not found]       ` <503B099A.6010807@cypress.com>
  0 siblings, 1 reply; 20+ messages in thread
From: Henrik Rydberg @ 2012-08-24 17:34 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Dmitry Torokhov, Kevin McNeely, Javier Martinez Canillas,
	Shawn Landden, Ashish Jangam, Olivier Sobrie, linux-input,
	linux-kernel

Hi Ferruh,

> Hi Henrik,
> 
> Is driver reviewed and is there anything we can help on it?

As Javier mentioned, there is already a framework for the Cypress
devices in the kernel. Please make sure your patches build on what is
already there. Until then, I have nothing further to add.

Thanks,
Henrik

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

* Re: [PATCH 2/4] Input: cyttsp4 - core driver for Cypress TMA4XX touchscreen devices
  2012-08-24 14:21   ` Michal Marek
@ 2012-08-27  5:49     ` Ferruh Yigit
  0 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-08-27  5:49 UTC (permalink / raw)
  To: Michal Marek
  Cc: Dmitry Torokhov, Kevin McNeely, Javier Martinez Canillas,
	Henrik Rydberg, Shawn Landden, Ashish Jangam, Olivier Sobrie,
	linux-input, linux-kernel

On 08/24/2012 05:21 PM, Michal Marek wrote:
> Dne 7.8.2012 15:09, Ferruh Yigit napsal(a):
>> +static int __init cyttsp4_core_init(void)
>> +{
>> +       int rc = 0;
>> +
>> +       rc = cyttsp4_register_core_driver(&cyttsp4_core_driver);
>> +       pr_info("%s: Cypress TTSP v4 core driver (Built %s @ %s) rc=%d\n",
>> +                __func__, __DATE__, __TIME__, rc);
> Hi,
>
> please drop the __DATE__ and __TIME__ macros to keep the build
> deterministic.
>
> thanks,
> Michal

Thank you Michal,

Will replace them with fixed release date macro.

Regards,
ferruh

--

Regards,
ferruh


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.

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

* Re: [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices
       [not found]       ` <503B099A.6010807@cypress.com>
@ 2012-09-12 13:16         ` Ferruh Yigit
  2012-09-12 13:34           ` Henrik Rydberg
  0 siblings, 1 reply; 20+ messages in thread
From: Ferruh Yigit @ 2012-09-12 13:16 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Dmitry Torokhov, Kevin McNeely, Javier Martinez Canillas,
	Shawn Landden, Ashish Jangam, Olivier Sobrie, linux-input,
	linux-kernel

On 08/27/2012 08:46 AM, Ferruh Yigit wrote:
> On 08/24/2012 08:34 PM, Henrik Rydberg wrote:
>> Hi Ferruh,
>>
>> As Javier mentioned, there is already a framework for the Cypress
>> devices in the kernel. Please make sure your patches build on what is
>> already there. Until then, I have nothing further to add.
>>
>> Thanks,
>> Henrik
>
> Hi Henrik,
>
> Thank you for your response.
>
> We now understand the requirement for our driver and we will submit a
> new patchset as soon as possible.
>
> Best Regards,
> ferruh
>

Hello Henrik,

After Javier's and your comment, we started an effort to update driver
and re-structured it similar to the existing Gen3 driver. We are close
to submit a patch-set for updated code, but this effort showed that this
new patch-set also won't be an incremental commit to existing driver but
will have to introduce new files.

Existing driver in the kernel is for Cypress Gen3 devices and it is not
a framework to cover Gen4 devices, Gen4 devices has different host
interface.

This re-structure made driver simpler with cost of removing some
features like ttsp bus or merging modular layers that work with
subscription mechanism into one file with direct function calls.
The concern here for our side is, the next generation touchscreen
controllers may cause similar problem with this Gen3 structure while
adding support to Linux kernel for them.

Before finalizing the work, we want to learn if introducing new files
for new driver is acceptable and our effort is inline with your request,
can you please comment on it?

Thanks and Regards,
ferruh

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.

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

* Re: [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices
  2012-09-12 13:16         ` Ferruh Yigit
@ 2012-09-12 13:34           ` Henrik Rydberg
  0 siblings, 0 replies; 20+ messages in thread
From: Henrik Rydberg @ 2012-09-12 13:34 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Dmitry Torokhov, Kevin McNeely, Javier Martinez Canillas,
	Shawn Landden, Ashish Jangam, Olivier Sobrie, linux-input,
	linux-kernel

Hi Ferruh,

> Before finalizing the work, we want to learn if introducing new files
> for new driver is acceptable and our effort is inline with your request,
> can you please comment on it?

New files are certainly alright when they are warranted, but of course
it depends on the details. We will know once you have submitted the
new version. :-)

Thanks,
Henrik

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

* [PATCH v2 0/3] Input: cyttsp4 - driver for Cypress TMA4XX touchscreen devices
       [not found] <Ferruh Yigit <fery@cypress.com>
                   ` (3 preceding siblings ...)
  2012-08-07 13:10 ` [PATCH 4/4] Input: cyttsp4 - I2C " Ferruh Yigit
@ 2012-09-14 17:48 ` Ferruh Yigit
  2012-09-14 17:48   ` [PATCH v2 1/3] Input: cyttsp4 - core " Ferruh Yigit
                     ` (3 more replies)
  4 siblings, 4 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-09-14 17:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Phil Warkentin, Ferruh Yigit,
	Javier Martinez Canillas, Henrik Rydberg, Shawn Landden,
	Ashish Jangam, Olivier Sobrie, linux-input, linux-kernel

This driver is for Cypress TrueTouch(tm) Standard Product controllers,
Generation4 devices.

This is second version of driver, code re-structured to match with
existing Generation3 driver code.

Driver consist of three modules:
- Core module: Main module, gets data from TTSP controller,
sent MT events to Linux
- I2C module: Underlying communication with I2C bus
- SPI module: Underlying communication with SPI bus

Ferruh Yigit (3):
  Input: cyttsp4 - core driver for Cypress TMA4XX touchscreen devices
  Input: cyttsp4 - I2C driver for Cypress TMA4XX touchscreen devices
  Input: cyttsp4 - SPI driver for Cypress TMA4XX touchscreen devices

 drivers/input/touchscreen/Kconfig        |   57 +
 drivers/input/touchscreen/Makefile       |   14 +
 drivers/input/touchscreen/cyttsp4_core.c | 2308 ++++++++++++++++++++++++++++++
 drivers/input/touchscreen/cyttsp4_core.h |  525 +++++++
 drivers/input/touchscreen/cyttsp4_i2c.c  |  191 +++
 drivers/input/touchscreen/cyttsp4_i2c.h  |   34 +
 drivers/input/touchscreen/cyttsp4_spi.c  |  318 ++++
 drivers/input/touchscreen/cyttsp4_spi.h  |   34 +
 include/linux/input/cyttsp4.h            |  126 ++
 9 files changed, 3607 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_core.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_core.h
 create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.h
 create mode 100644 drivers/input/touchscreen/cyttsp4_spi.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_spi.h
 create mode 100644 include/linux/input/cyttsp4.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.

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

* [PATCH v2 1/3] Input: cyttsp4 - core driver for Cypress TMA4XX touchscreen devices
  2012-09-14 17:48 ` [PATCH v2 0/3] Input: cyttsp4 - " Ferruh Yigit
@ 2012-09-14 17:48   ` Ferruh Yigit
  2012-09-14 17:48   ` [PATCH v2 2/3] Input: cyttsp4 - I2C " Ferruh Yigit
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-09-14 17:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Phil Warkentin, Ferruh Yigit,
	Javier Martinez Canillas, Henrik Rydberg, Shawn Landden,
	Ashish Jangam, Olivier Sobrie, linux-input, linux-kernel

Cypress TrueTouch(tm) Standard Product controllers, Generation4
devices, Core driver.

Core module has two main functionality:
1- Interface between Host and TTSP controller
2- Process data sent by controller and send MT events to OS

This Core module support Multi Touch protocol Type B

Responsible from IRQ handling, reading system information registers
and sending multi touch events to Linux

Signed-off-by: Ferruh Yigit <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig        |   33 +
 drivers/input/touchscreen/Makefile       |    8 +
 drivers/input/touchscreen/cyttsp4_core.c | 2308 ++++++++++++++++++++++++++++++
 drivers/input/touchscreen/cyttsp4_core.h |  525 +++++++
 include/linux/input/cyttsp4.h            |  126 ++
 5 files changed, 3000 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_core.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_core.h
 create mode 100644 include/linux/input/cyttsp4.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ba232c..d6ebd4c 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -179,6 +179,39 @@ config TOUCHSCREEN_CYTTSP_SPI
          To compile this driver as a module, choose M here: the
          module will be called cyttsp_spi.

+config TOUCHSCREEN_CYPRESS_CYTTSP4
+       tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
+       default n
+       help
+         Core driver for Cypress TrueTouch(tm) Standard Product
+         Generation4 touchscreen controllers.
+
+         Say Y here if you have a Cypress Gen4 touchscreen.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here.
+
+config TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG
+       bool "Enable debug output"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4
+       default n
+       help
+         Enable Debug output for Cypress TrueTouch(tm)
+         Standard Product Generation4 driver set.
+
+         Say Y here to enable debug output.
+
+config TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG
+       bool "Enable verbose debug output"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG
+       default n
+       help
+         Enable Verbose Debug output for Cypress TrueTouch(tm)
+         Standard Product Generation4 driver set.
+
+         Say Y here to enable verbose debug output.
+
 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 178eb12..1cdab28 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -73,3 +73,11 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)   += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4)      += cyttsp4_core.o
+ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG),y)
+CFLAGS_cyttsp4_core.o += -DDEBUG
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG),y)
+CFLAGS_cyttsp4_core.o += -DVERBOSE_DEBUG
+endif
diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c
new file mode 100644
index 0000000..2ec7223
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.c
@@ -0,0 +1,2308 @@
+/*
+ * cyttsp4_core.c
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver module.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <linux/input/cyttsp4.h>
+#include "cyttsp4_core.h"
+
+#define CY_CORE_MODE_CHANGE_TIMEOUT            1000
+#define CY_CORE_RESET_AND_WAIT_TIMEOUT         500
+#define CY_CORE_WAKEUP_TIMEOUT                 500
+
+MODULE_FIRMWARE(CY_FW_FILE_NAME);
+
+const char *cy_driver_core_name = CYTTSP4_CORE_NAME;
+const char *cy_driver_core_version = CY_DRIVER_VERSION;
+const char *cy_driver_core_date = CY_DRIVER_DATE;
+
+static inline size_t merge_bytes(u8 high, u8 low)
+{
+       return (high << 8) + low;
+}
+
+static inline int cyttsp4_bits_2_bytes(int nbits, int *max)
+{
+       *max = 1 << nbits;
+       return (nbits + 7) / 8;
+}
+
+static inline int cyttsp4_read(struct cyttsp4_core_data *cd,
+                u8 addr, void *buf, int size)
+{
+       return cd->ops->read(cd->dev, addr, buf, size);
+}
+
+static inline int cyttsp4_write(struct cyttsp4_core_data *cd,
+               u8 addr, const void *buf, int size)
+{
+       return cd->ops->write(cd->dev, addr, buf, size);
+}
+
+#ifdef VERBOSE_DEBUG
+void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size,
+               const char *data_name)
+{
+       int i, k;
+       const char fmt[] = "%02X ";
+       int max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
+
+       pr_buf[0] = 0;
+       for (i = k = 0; i < size && k < max; i++, k += 3)
+               scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]);
+
+       dev_vdbg(dev, "%s:  %s[0..%d]=%s%s\n", __func__, data_name, size - 1,
+                       pr_buf, size <= max ? "" : CY_PR_TRUNCATED);
+}
+EXPORT_SYMBOL(cyttsp4_pr_buf);
+#endif
+
+static int cyttsp4_xres(struct cyttsp4_core_platform_data *pdata,
+               struct device *dev)
+{
+       int rst_gpio = pdata->rst_gpio;
+       int rc = 0;
+
+       gpio_set_value(rst_gpio, 1);
+       msleep(20);
+       gpio_set_value(rst_gpio, 0);
+       msleep(40);
+       gpio_set_value(rst_gpio, 1);
+       msleep(20);
+       dev_info(dev,
+               "%s: RESET CYTTSP gpio=%d r=%d\n", __func__,
+               pdata->rst_gpio, rc);
+       return rc;
+}
+
+static int cyttsp4_init(struct cyttsp4_core_platform_data *pdata,
+               struct device *dev)
+{
+       int rst_gpio = pdata->rst_gpio;
+       int irq_gpio = pdata->irq_gpio;
+       int rc;
+
+       rc = gpio_request(rst_gpio, NULL);
+       if (rc < 0) {
+               gpio_free(rst_gpio);
+               /* Try once more */
+               rc = gpio_request(rst_gpio, NULL);
+       }
+       if (rc < 0) {
+               dev_err(dev, "%s: Fail request gpio=%d\n", __func__, rst_gpio);
+               goto error;
+       }
+
+       rc = gpio_direction_output(rst_gpio, 1);
+       if (rc < 0) {
+               dev_err(dev, "%s: Fail set output gpio=%d\n", __func__,
+                       rst_gpio);
+               goto error_free_rst_gpio;
+       }
+
+       rc = gpio_request(irq_gpio, NULL);
+       if (rc < 0) {
+               gpio_free(irq_gpio);
+               /* Try once more */
+               rc = gpio_request(irq_gpio, NULL);
+       }
+       if (rc < 0) {
+               dev_err(dev, "%s: Fail request gpio=%d\n", __func__, irq_gpio);
+               goto error_free_rst_gpio;
+       }
+
+       rc = gpio_direction_input(irq_gpio);
+       if (rc < 0) {
+               dev_err(dev, "%s: Fail set input gpio=%d\n", __func__,
+                       irq_gpio);
+               goto error_free_irq_gpio;
+       }
+
+       dev_info(dev, "%s: INIT CYTTSP RST gpio=%d and IRQ gpio=%d\n",
+               __func__, rst_gpio, irq_gpio);
+       return 0;
+
+error_free_irq_gpio:
+       gpio_free(irq_gpio);
+error_free_rst_gpio:
+       gpio_free(rst_gpio);
+error:
+       return rc;
+}
+
+static void cyttsp4_exit(struct cyttsp4_core_platform_data *pdata,
+               struct device *dev)
+{
+       int rst_gpio = pdata->rst_gpio;
+       int irq_gpio = pdata->irq_gpio;
+
+       gpio_free(rst_gpio);
+       gpio_free(irq_gpio);
+
+       dev_info(dev, "%s: EXIT CYTTSP RST gpio=%d and IRQ gpio=%d\n",
+               __func__, rst_gpio, irq_gpio);
+}
+
+static int cyttsp4_irq_stat(struct cyttsp4_core_platform_data *pdata,
+               struct device *dev)
+{
+       return gpio_get_value(pdata->irq_gpio);
+}
+
+static int cyttsp4_wakeup(struct cyttsp4_core_data *cd)
+{
+       atomic_t *ignore_irq = &cd->ignore_irq;
+       int irq_gpio = cd->pdata->irq_gpio;
+       struct device *dev = cd->dev;
+       int rc = 0;
+
+       atomic_set(ignore_irq, 1);
+       rc = gpio_direction_output(irq_gpio, 0);
+       if (rc < 0) {
+               atomic_set(ignore_irq, 0);
+               dev_err(dev,
+                       "%s: Fail set output gpio=%d\n",
+                       __func__, irq_gpio);
+       } else {
+               udelay(2000);
+               rc = gpio_direction_input(irq_gpio);
+               atomic_set(ignore_irq, 0);
+               if (rc < 0) {
+                       dev_err(dev,
+                               "%s: Fail set input gpio=%d\n",
+                               __func__, irq_gpio);
+               }
+       }
+
+       dev_info(dev,
+               "%s: WAKEUP CYTTSP gpio=%d r=%d\n", __func__,
+               irq_gpio, rc);
+       return rc;
+}
+
+static int cyttsp4_load_status_regs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       struct device *dev = cd->dev;
+       int rc;
+
+       if (!si->xy_mode) {
+               dev_err(cd->dev, "%s: NULL xy_mode pointer\n", __func__);
+               return -EINVAL;
+       }
+
+       rc = cyttsp4_read(cd, CY_REG_BASE, si->xy_mode, si->si_ofs.mode_size);
+       if (rc < 0)
+               dev_err(dev, "%s: fail read mode regs r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode,
+                       si->si_ofs.mode_size, "xy_mode");
+
+       return rc;
+}
+
+static int cyttsp4_handshake(struct cyttsp4_core_data *cd, u8 mode)
+{
+       u8 cmd = mode ^ CY_HST_TOGGLE;
+       int rc = cyttsp4_write(cd, CY_REG_BASE, &cmd, sizeof(cmd));
+
+       if (rc < 0)
+               dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n",
+                               __func__, rc);
+
+       return rc;
+}
+
+static int cyttsp4_hw_soft_reset(struct cyttsp4_core_data *cd)
+{
+       u8 cmd = CY_HST_RESET | CY_HST_MODE_CHANGE;
+       int rc = cyttsp4_write(cd, CY_REG_BASE, &cmd, sizeof(cmd));
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n",
+                               __func__);
+               return rc;
+       }
+       dev_dbg(cd->dev, "%s: execute SOFT reset\n", __func__);
+       return 0;
+}
+
+static int cyttsp4_hw_hard_reset(struct cyttsp4_core_data *cd)
+{
+       dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__);
+       return cyttsp4_xres(cd->pdata, cd->dev);
+}
+
+static int cyttsp4_hw_reset(struct cyttsp4_core_data *cd)
+{
+       int rc = cyttsp4_hw_hard_reset(cd);
+       if (rc == -ENOSYS)
+               rc = cyttsp4_hw_soft_reset(cd);
+       return rc;
+}
+
+static int cyttsp4_si_data_offsets(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc = cyttsp4_read(cd, CY_REG_BASE, &si->si_data,
+                       sizeof(si->si_data));
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       /* Print sysinfo data offsets */
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data,
+               sizeof(si->si_data), "sysinfo_data_offsets");
+
+       /* convert sysinfo data offset bytes into integers */
+
+       si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh,
+                       si->si_data.map_szl);
+       si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh,
+                       si->si_data.cydata_ofsl);
+       si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh,
+                       si->si_data.test_ofsl);
+       si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh,
+                       si->si_data.pcfg_ofsl);
+       si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh,
+                       si->si_data.opcfg_ofsl);
+       si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh,
+                       si->si_data.ddata_ofsl);
+       si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh,
+                       si->si_data.mdata_ofsl);
+       return rc;
+}
+
+static int cyttsp4_si_get_cydata(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs;
+       if (si->si_ptrs.cydata == NULL)
+               si->si_ptrs.cydata = kzalloc(si->si_ofs.cydata_size,
+                       GFP_KERNEL);
+       if (si->si_ptrs.cydata == NULL) {
+               dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_read(cd, si->si_ofs.cydata_ofs, si->si_ptrs.cydata,
+                       si->si_ofs.cydata_size);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: fail read cydata r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata,
+                       si->si_ofs.cydata_size, "sysinfo_cydata");
+       return rc;
+}
+
+static int cyttsp4_si_get_test_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs;
+       if (si->si_ptrs.test == NULL)
+               si->si_ptrs.test = kzalloc(si->si_ofs.test_size, GFP_KERNEL);
+       if (si->si_ptrs.test == NULL) {
+               dev_err(cd->dev, "%s: fail alloc test memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_read(cd, si->si_ofs.test_ofs, si->si_ptrs.test,
+                       si->si_ofs.test_size);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read test data r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.test,
+               si->si_ofs.test_size, "sysinfo_test_data");
+
+       if (si->si_ptrs.test->post_codel &
+           CY_POST_CODEL_WDG_RST)
+               dev_info(cd->dev, "%s: %s codel=%02X\n",
+                        __func__, "Reset was a WATCHDOG RESET",
+                        si->si_ptrs.test->post_codel);
+
+       if (!(si->si_ptrs.test->post_codel &
+             CY_POST_CODEL_CFG_DATA_CRC_FAIL))
+               dev_info(cd->dev, "%s: %s codel=%02X\n", __func__,
+                        "Config Data CRC FAIL",
+                        si->si_ptrs.test->post_codel);
+
+       if (!(si->si_ptrs.test->post_codel &
+             CY_POST_CODEL_PANEL_TEST_FAIL))
+               dev_info(cd->dev, "%s: %s codel=%02X\n",
+                        __func__, "PANEL TEST FAIL",
+                        si->si_ptrs.test->post_codel);
+
+       dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n",
+                __func__, si->si_ptrs.test->post_codel & 0x08 ?
+                "ENABLED" : "DISABLED",
+                si->si_ptrs.test->post_codel);
+       return rc;
+}
+
+static int cyttsp4_si_get_pcfg_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get pcfg data\n", __func__);
+       si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs;
+       if (si->si_ptrs.pcfg == NULL)
+               si->si_ptrs.pcfg = kzalloc(si->si_ofs.pcfg_size, GFP_KERNEL);
+       if (si->si_ptrs.pcfg == NULL) {
+               rc = -ENOMEM;
+               dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+       rc = cyttsp4_read(cd, si->si_ofs.pcfg_ofs, si->si_ptrs.pcfg,
+                       si->si_ofs.pcfg_size);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read pcfg data r=%d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh
+                       & CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl);
+       si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh
+                       & CY_PCFG_ORIGIN_X_MASK);
+       si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh
+                       & CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl);
+       si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh
+                       & CY_PCFG_ORIGIN_Y_MASK);
+       si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh,
+                       si->si_ptrs.pcfg->max_zl);
+
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.pcfg,
+               si->si_ofs.pcfg_size, "sysinfo_pcfg_data");
+
+       return rc;
+}
+
+static int cyttsp4_si_get_opcfg_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int i;
+       enum cyttsp4_tch_abs abs;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get opcfg data\n", __func__);
+       si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs;
+       if (si->si_ptrs.opcfg == NULL)
+               si->si_ptrs.opcfg = kzalloc(si->si_ofs.opcfg_size, GFP_KERNEL);
+       if (si->si_ptrs.opcfg == NULL) {
+               dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__);
+               rc = -ENOMEM;
+               goto cyttsp4_si_get_opcfg_data_exit;
+       }
+
+       rc = cyttsp4_read(cd, si->si_ofs.opcfg_ofs, si->si_ptrs.opcfg,
+                       si->si_ofs.opcfg_size);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: fail read opcfg data r=%d\n",
+                       __func__, rc);
+               goto cyttsp4_si_get_opcfg_data_exit;
+       }
+       si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs;
+       si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs;
+       si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) +
+               si->si_ptrs.opcfg->rep_szl;
+       si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns;
+       si->si_ofs.num_btn_regs = (si->si_ofs.num_btns +
+               CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG;
+       si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs;
+       si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0;
+       si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs &
+               CY_BYTE_OFS_MASK;
+       si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size &
+               CY_BYTE_OFS_MASK;
+
+       /* Get the old touch fields */
+       for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) {
+               si->si_ofs.tch_abs[abs].ofs =
+                       si->si_ptrs.opcfg->tch_rec_old[abs].loc &
+                       CY_BYTE_OFS_MASK;
+               si->si_ofs.tch_abs[abs].size =
+                       cyttsp4_bits_2_bytes
+                       (si->si_ptrs.opcfg->tch_rec_old[abs].size,
+                       &si->si_ofs.tch_abs[abs].max);
+               si->si_ofs.tch_abs[abs].bofs =
+                       (si->si_ptrs.opcfg->tch_rec_old[abs].loc &
+                       CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+       }
+
+       /* button fields */
+       si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size;
+       si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs;
+       si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size;
+
+       if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) {
+               /* Get the extended touch fields */
+               for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) {
+                       si->si_ofs.tch_abs[abs].ofs =
+                               si->si_ptrs.opcfg->tch_rec_new[i].loc &
+                               CY_BYTE_OFS_MASK;
+                       si->si_ofs.tch_abs[abs].size =
+                               cyttsp4_bits_2_bytes
+                               (si->si_ptrs.opcfg->tch_rec_new[i].size,
+                               &si->si_ofs.tch_abs[abs].max);
+                       si->si_ofs.tch_abs[abs].bofs =
+                               (si->si_ptrs.opcfg->tch_rec_new[i].loc
+                               & CY_BOFS_MASK) >> CY_BOFS_SHIFT;
+               }
+       }
+
+       for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) {
+               dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__,
+                       cyttsp4_tch_abs_string[abs]);
+               dev_dbg(cd->dev, "%s:     ofs =%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].ofs);
+               dev_dbg(cd->dev, "%s:     siz =%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].size);
+               dev_dbg(cd->dev, "%s:     max =%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].max);
+               dev_dbg(cd->dev, "%s:     bofs=%2d\n", __func__,
+                       si->si_ofs.tch_abs[abs].bofs);
+       }
+
+       si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1;
+       si->si_ofs.data_size = si->si_ofs.max_tchs *
+               si->si_ptrs.opcfg->tch_rec_size;
+
+       cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg,
+               si->si_ofs.opcfg_size, "sysinfo_opcfg_data");
+
+cyttsp4_si_get_opcfg_data_exit:
+       return rc;
+}
+
+static int cyttsp4_si_get_ddata(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get ddata data\n", __func__);
+       si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs;
+       if (si->si_ptrs.ddata == NULL)
+               si->si_ptrs.ddata = kzalloc(si->si_ofs.ddata_size, GFP_KERNEL);
+       if (si->si_ptrs.ddata == NULL) {
+               dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_read(cd, si->si_ofs.ddata_ofs, si->si_ptrs.ddata,
+                       si->si_ofs.ddata_size);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: fail read ddata data r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.ddata,
+                       si->si_ofs.ddata_size, "sysinfo_ddata");
+
+       return rc;
+}
+
+static int cyttsp4_si_get_mdata(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       dev_vdbg(cd->dev, "%s: get mdata data\n", __func__);
+       si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs;
+       if (si->si_ptrs.mdata == NULL)
+               si->si_ptrs.mdata = kzalloc(si->si_ofs.mdata_size, GFP_KERNEL);
+       if (si->si_ptrs.mdata == NULL) {
+               dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       rc = cyttsp4_read(cd, si->si_ofs.mdata_ofs, si->si_ptrs.mdata,
+                       si->si_ofs.mdata_size);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: fail read mdata data r=%d\n",
+                       __func__, rc);
+       else
+               cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.mdata,
+                       si->si_ofs.mdata_size, "sysinfo_mdata");
+
+       return rc;
+}
+
+static int cyttsp4_si_get_btn_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int btn;
+       int num_defined_keys;
+       u16 *key_table;
+       int rc = 0;
+
+       dev_vdbg(cd->dev, "%s: get btn data\n", __func__);
+       if (si->si_ofs.num_btns) {
+               si->si_ofs.btn_keys_size = si->si_ofs.num_btns *
+                       sizeof(struct cyttsp4_btn);
+               if (si->btn == NULL)
+                       si->btn = kzalloc(si->si_ofs.btn_keys_size, GFP_KERNEL);
+               if (si->btn == NULL) {
+                       dev_err(cd->dev, "%s: %s\n", __func__,
+                               "fail alloc btn_keys memory");
+                       return -ENOMEM;
+               }
+               if (cd->pdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL)
+                       num_defined_keys = 0;
+               else if (cd->pdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL)
+                       num_defined_keys = 0;
+               else
+                       num_defined_keys = cd->pdata->sett
+                               [CY_IC_GRPNUM_BTN_KEYS]->size;
+
+               for (btn = 0; btn < si->si_ofs.num_btns &&
+                       btn < num_defined_keys; btn++) {
+                       key_table = (u16 *)cd->pdata->sett
+                               [CY_IC_GRPNUM_BTN_KEYS]->data;
+                       si->btn[btn].key_code = key_table[btn];
+                       si->btn[btn].enabled = true;
+               }
+               for (; btn < si->si_ofs.num_btns; btn++) {
+                       si->btn[btn].key_code = KEY_RESERVED;
+                       si->btn[btn].enabled = true;
+               }
+
+               return rc;
+       }
+
+       si->si_ofs.btn_keys_size = 0;
+       kfree(si->btn);
+       si->btn = NULL;
+       return rc;
+}
+
+static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       if (si->xy_mode == NULL) {
+               si->xy_mode = kzalloc(si->si_ofs.mode_size, GFP_KERNEL);
+               if (si->xy_mode == NULL)
+                       return -ENOMEM;
+       }
+
+       if (si->xy_data == NULL) {
+               si->xy_data = kzalloc(si->si_ofs.data_size, GFP_KERNEL);
+               if (si->xy_data == NULL)
+                       return -ENOMEM;
+       }
+
+       if (si->btn_rec_data == NULL) {
+               si->btn_rec_data = kzalloc(si->si_ofs.btn_rec_size *
+                                          si->si_ofs.num_btns, GFP_KERNEL);
+               if (si->btn_rec_data == NULL)
+                       return -ENOMEM;
+       }
+       return 0;
+}
+
+static void cyttsp4_si_put_log_data(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       dev_dbg(cd->dev, "%s: cydata_ofs =%4d siz=%4d\n", __func__,
+               si->si_ofs.cydata_ofs, si->si_ofs.cydata_size);
+       dev_dbg(cd->dev, "%s: test_ofs   =%4d siz=%4d\n", __func__,
+               si->si_ofs.test_ofs, si->si_ofs.test_size);
+       dev_dbg(cd->dev, "%s: pcfg_ofs   =%4d siz=%4d\n", __func__,
+               si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size);
+       dev_dbg(cd->dev, "%s: opcfg_ofs  =%4d siz=%4d\n", __func__,
+               si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size);
+       dev_dbg(cd->dev, "%s: ddata_ofs  =%4d siz=%4d\n", __func__,
+               si->si_ofs.ddata_ofs, si->si_ofs.ddata_size);
+       dev_dbg(cd->dev, "%s: mdata_ofs  =%4d siz=%4d\n", __func__,
+               si->si_ofs.mdata_ofs, si->si_ofs.mdata_size);
+
+       dev_dbg(cd->dev, "%s: cmd_ofs       =%4d\n", __func__,
+               si->si_ofs.cmd_ofs);
+       dev_dbg(cd->dev, "%s: rep_ofs       =%4d\n", __func__,
+               si->si_ofs.rep_ofs);
+       dev_dbg(cd->dev, "%s: rep_sz        =%4d\n", __func__,
+               si->si_ofs.rep_sz);
+       dev_dbg(cd->dev, "%s: num_btns      =%4d\n", __func__,
+               si->si_ofs.num_btns);
+       dev_dbg(cd->dev, "%s: num_btn_regs  =%4d\n", __func__,
+               si->si_ofs.num_btn_regs);
+       dev_dbg(cd->dev, "%s: tt_stat_ofs   =%4d\n", __func__,
+               si->si_ofs.tt_stat_ofs);
+       dev_dbg(cd->dev, "%s: tch_rec_size   =%4d\n", __func__,
+               si->si_ofs.tch_rec_size);
+       dev_dbg(cd->dev, "%s: max_tchs      =%4d\n", __func__,
+               si->si_ofs.max_tchs);
+       dev_dbg(cd->dev, "%s: mode_size     =%4d\n", __func__,
+               si->si_ofs.mode_size);
+       dev_dbg(cd->dev, "%s: data_size     =%4d\n", __func__,
+               si->si_ofs.data_size);
+       dev_dbg(cd->dev, "%s: map_sz        =%4d\n", __func__,
+               si->si_ofs.map_sz);
+
+       dev_dbg(cd->dev, "%s: btn_rec_size   =%2d\n", __func__,
+               si->si_ofs.btn_rec_size);
+       dev_dbg(cd->dev, "%s: btn_diff_ofs  =%2d\n", __func__,
+               si->si_ofs.btn_diff_ofs);
+       dev_dbg(cd->dev, "%s: btn_diff_size  =%2d\n", __func__,
+               si->si_ofs.btn_diff_size);
+
+       dev_dbg(cd->dev, "%s: max_x    = 0x%04X (%d)\n", __func__,
+               si->si_ofs.max_x, si->si_ofs.max_x);
+       dev_dbg(cd->dev, "%s: x_origin = %d (%s)\n", __func__,
+               si->si_ofs.x_origin,
+               si->si_ofs.x_origin == CY_NORMAL_ORIGIN ?
+               "left corner" : "right corner");
+       dev_dbg(cd->dev, "%s: max_y    = 0x%04X (%d)\n", __func__,
+               si->si_ofs.max_y, si->si_ofs.max_y);
+       dev_dbg(cd->dev, "%s: y_origin = %d (%s)\n", __func__,
+               si->si_ofs.y_origin,
+               si->si_ofs.y_origin == CY_NORMAL_ORIGIN ?
+               "upper corner" : "lower corner");
+       dev_dbg(cd->dev, "%s: max_p    = 0x%04X (%d)\n", __func__,
+               si->si_ofs.max_p, si->si_ofs.max_p);
+
+       dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__,
+               si->xy_mode, si->xy_data);
+}
+
+static int cyttsp4_get_sysinfo_regs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+       int rc;
+
+       rc = cyttsp4_si_data_offsets(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_cydata(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_test_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_pcfg_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_opcfg_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_ddata(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_mdata(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_btn_data(cd);
+       if (rc < 0)
+               return rc;
+
+       rc = cyttsp4_si_get_op_data_ptrs(cd);
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: failed to get_op_data\n",
+                       __func__);
+               return rc;
+       }
+       cyttsp4_si_put_log_data(cd);
+
+       /* provide flow control handshake */
+       rc = cyttsp4_handshake(cd, si->si_data.hst_mode);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n",
+                       __func__);
+
+       si->ready = true;
+       return rc;
+}
+
+static void cyttsp4_queue_startup(struct cyttsp4_core_data *cd)
+{
+       queue_work(cd->startup_work_q, &cd->startup_work);
+       dev_info(cd->dev, "%s: cyttsp4_startup queued\n", __func__);
+}
+
+static void cyttsp4_lift_all(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);
+       }
+
+       /* 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->input->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->input->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->input->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->input->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]);
+                       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;
+                       }
+                       input_mt_slot(md->input, t);
+                       input_mt_report_slot_state(md->input, MT_TOOL_FINGER,
+                                       true);
+                       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]);
+                       }
+               }
+               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]);
+       }
+
+       for (t = 0; t < si->si_ofs.max_tchs + 1; t++) {
+               if (ids[t])
+                       continue;
+               input_mt_slot(md->input, t);
+               input_mt_report_slot_state(md->input, MT_TOOL_FINGER, false);
+       }
+
+       input_sync(md->input);
+
+       md->num_prv_tch = num_cur_tch;
+       md->prv_tch_type = tch.abs[CY_TCH_O];
+
+       return;
+}
+
+/* read xy_data for all current touches */
+int cyttsp4_xy_worker(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_mt_data *md = &cd->md;
+       struct device *dev = &md->input->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(cd, 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 irqreturn_t cyttsp4_irq(int irq, void *handle)
+{
+       struct cyttsp4_core_data *cd = handle;
+       struct device *dev = cd->dev;
+       enum cyttsp4_mode cur_mode;
+       u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs;
+       u8 mode[3];
+       int rc;
+
+       /*
+        * Check whether this IRQ should be ignored (external)
+        * This should be the very first thing to check since
+        * ignore_irq may be set for a very short period of time
+        */
+       if (atomic_read(&cd->ignore_irq)) {
+               dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+               return IRQ_HANDLED;
+       }
+
+       dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status);
+
+       mutex_lock(&cd->system_lock);
+
+       /* Just to debug */
+       if (cd->sleep_state == SS_SLEEP_ON) {
+               dev_vdbg(dev, "%s: Received IRQ while in sleep\n",
+                       __func__);
+       } else if (cd->sleep_state == SS_SLEEPING) {
+               dev_vdbg(dev, "%s: Received IRQ while sleeping\n",
+                       __func__);
+       }
+
+       rc = cyttsp4_read(cd, CY_REG_BASE, mode, sizeof(mode));
+       if (rc) {
+               dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+               goto cyttsp4_irq_exit;
+       }
+       dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__,
+                       mode[0], mode[1], mode[2]);
+
+       if (IS_BOOTLOADER(mode[0])) {
+               cur_mode = CY_MODE_BOOTLOADER;
+               dev_vdbg(dev, "%s: bl running\n", __func__);
+               if (cd->mode == CY_MODE_BOOTLOADER) {
+                       /* Signal bootloader heartbeat heard */
+                       wake_up(&cd->wait_q);
+                       goto cyttsp4_irq_exit;
+               }
+
+               /* switch to bootloader */
+               dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n",
+                       __func__, cd->mode, cur_mode);
+
+               /* catch operation->bl glitch */
+               if (cd->mode != CY_MODE_UNKNOWN) {
+                       cyttsp4_queue_startup(cd);
+                       goto cyttsp4_irq_exit;
+               }
+
+               /*
+                * do not wake thread on this switch since
+                * it is possible to get an early heartbeat
+                * prior to performing the reset
+                */
+               cd->mode = cur_mode;
+
+               goto cyttsp4_irq_exit;
+       }
+
+       switch (mode[0] & CY_HST_MODE) {
+       case CY_HST_OPERATE:
+               cur_mode = CY_MODE_OPERATIONAL;
+               dev_vdbg(dev, "%s: operational\n", __func__);
+               break;
+       case CY_HST_CAT:
+               cur_mode = CY_MODE_CAT;
+               dev_vdbg(dev, "%s: CaT\n", __func__);
+               break;
+       case CY_HST_SYSINFO:
+               cur_mode = CY_MODE_SYSINFO;
+               dev_vdbg(dev, "%s: sysinfo\n", __func__);
+               break;
+       default:
+               cur_mode = CY_MODE_UNKNOWN;
+               dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__,
+                       mode[0]);
+               break;
+       }
+
+       /* Check whether this IRQ should be ignored (internal) */
+       if (cd->int_status & CY_INT_IGNORE) {
+               dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__);
+               goto cyttsp4_irq_exit;
+       }
+
+       /* Check for wake up interrupt */
+       if (cd->int_status & CY_INT_AWAKE) {
+               cd->int_status &= ~CY_INT_AWAKE;
+               wake_up(&cd->sleep_q);
+               dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__);
+               goto cyttsp4_irq_handshake;
+       }
+
+       /* Expecting mode change interrupt */
+       if ((cd->int_status & CY_INT_MODE_CHANGE)
+                       && (mode[0] & CY_HST_MODE_CHANGE) == 0) {
+               cd->int_status &= ~CY_INT_MODE_CHANGE;
+               dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n",
+                               __func__, cd->mode, cur_mode);
+               cd->mode = cur_mode;
+               wake_up(&cd->wait_q);
+               goto cyttsp4_irq_handshake;
+       }
+
+       /* compare current core mode to current device mode */
+       dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n",
+                       __func__, cd->mode, cur_mode);
+       if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) {
+               /* Unexpected mode change occurred */
+               dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode,
+                               cur_mode, cd->int_status);
+               dev_vdbg(dev, "%s: Unexpected mode change, startup\n",
+                               __func__);
+               cyttsp4_queue_startup(cd);
+               goto cyttsp4_irq_exit;
+       }
+
+       /* Expecting command complete interrupt */
+       dev_vdbg(dev, "%s: command byte:0x%x, toggle:0x%x\n",
+                       __func__, mode[cmd_ofs], cd->cmd_toggle);
+       if ((cd->int_status & CY_INT_EXEC_CMD)
+                       && mode[cmd_ofs] & CY_CMD_COMPLETE) {
+               cd->int_status &= ~CY_INT_EXEC_CMD;
+               dev_vdbg(dev, "%s: Received command complete interrupt\n",
+                               __func__);
+               wake_up(&cd->wait_q);
+               goto cyttsp4_irq_handshake;
+       }
+
+       /* This should be status report, read status regs */
+       if (cd->mode == CY_MODE_OPERATIONAL) {
+               dev_vdbg(dev, "%s: Read status registers\n", __func__);
+               rc = cyttsp4_load_status_regs(cd);
+               if (rc < 0) {
+                       dev_err(dev, "%s: fail read mode regs r=%d\n",
+                               __func__, rc);
+               } else {
+                       rc = cyttsp4_xy_worker(cd);
+                       if (rc < 0)
+                               dev_err(dev, "%s: fail xy_worker r=%d\n",
+                                       __func__, rc);
+               }
+       }
+
+cyttsp4_irq_handshake:
+       /* handshake the event */
+       dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n",
+                       __func__, mode[0], rc);
+       rc = cyttsp4_handshake(cd, mode[0]);
+       if (rc < 0)
+               dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n",
+                               __func__, mode[0], rc);
+
+       /*
+        * a non-zero udelay period is required for using
+        * IRQF_TRIGGER_LOW in order to delay until the
+        * device completes isr deassert
+        */
+       udelay(cd->pdata->level_irq_udelay);
+
+cyttsp4_irq_exit:
+       mutex_unlock(&cd->system_lock);
+       dev_vdbg(dev, "%s: irq done\n", __func__);
+       return IRQ_HANDLED;
+}
+
+static void cyttsp4_start_wd_timer(struct cyttsp4_core_data *cd)
+{
+       if (!CY_WATCHDOG_TIMEOUT)
+               return;
+
+       mod_timer(&cd->timer, jiffies + CY_WATCHDOG_TIMEOUT);
+       return;
+}
+
+static void cyttsp4_stop_wd_timer(struct cyttsp4_core_data *cd)
+{
+       if (!CY_WATCHDOG_TIMEOUT)
+               return;
+
+       del_timer(&cd->timer);
+       cancel_work_sync(&cd->work);
+       return;
+}
+
+static void cyttsp4_timer_watchdog(struct work_struct *work)
+{
+       struct cyttsp4_core_data *cd =
+                       container_of(work, struct cyttsp4_core_data, work);
+       int rep_stat;
+       int mode;
+       int retval;
+
+       if (cd == NULL) {
+               dev_err(cd->dev, "%s: NULL context pointer\n", __func__);
+               return;
+       }
+
+       mutex_lock(&cd->system_lock);
+       retval = cyttsp4_load_status_regs(cd);
+       if (retval < 0) {
+               dev_err(cd->dev,
+                       "%s: failed to access device in watchdog timer r=%d\n",
+                       __func__, retval);
+               cyttsp4_queue_startup(cd);
+               goto cyttsp4_timer_watchdog_exit_error;
+       }
+       mode = cd->sysinfo.xy_mode[CY_REG_BASE];
+       rep_stat = cd->sysinfo.xy_mode[cd->sysinfo.si_ofs.rep_ofs];
+       if (IS_BOOTLOADER(mode) && IS_BOOTLOADER(rep_stat)) {
+               dev_err(cd->dev,
+                       "%s: device found in bootloader mode when operational mode rep_stat=0x%02X\n",
+                       __func__, rep_stat);
+               cyttsp4_queue_startup(cd);
+               goto cyttsp4_timer_watchdog_exit_error;
+       }
+
+       cyttsp4_start_wd_timer(cd);
+cyttsp4_timer_watchdog_exit_error:
+       mutex_unlock(&cd->system_lock);
+       return;
+}
+
+static void cyttsp4_timer(unsigned long handle)
+{
+       struct cyttsp4_core_data *cd = (struct cyttsp4_core_data *)handle;
+
+       dev_vdbg(cd->dev, "%s: Timer triggered\n", __func__);
+
+       if (!cd)
+               return;
+
+       if (!work_pending(&cd->work))
+               schedule_work(&cd->work);
+
+       return;
+}
+
+static int cyttsp4_reset_and_wait(struct cyttsp4_core_data *cd)
+{
+       long t;
+       int rc;
+
+       /* reset hardware */
+       mutex_lock(&cd->system_lock);
+       dev_dbg(cd->dev, "%s: reset hw...\n", __func__);
+       rc = cyttsp4_hw_reset(cd);
+       cd->mode = CY_MODE_UNKNOWN;
+       mutex_unlock(&cd->system_lock);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Fail hw reset r=%d\n", __func__, rc);
+
+       /* wait heartbeat */
+       dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__);
+       t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER,
+               msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT));
+       if  (IS_TMO(t)) {
+               dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n",
+                               __func__, cd->mode);
+               rc = -ETIME;
+       } else
+               rc = 0;
+
+       return rc;
+}
+
+/*
+ * returns err if refused or timeout; block until mode change complete
+ * bit is set (mode change interrupt)
+ */
+static int cyttsp4_set_mode(struct cyttsp4_core_data *cd, u8 new_dev_mode,
+               int new_op_mode)
+{
+       long t;
+       int rc;
+
+       /* change mode */
+       dev_dbg(cd->dev, "%s: new_dev_mode=%02X new_mode=%d\n",
+                       __func__, new_dev_mode, new_op_mode);
+       new_dev_mode |= CY_HST_MODE_CHANGE;
+
+       mutex_lock(&cd->system_lock);
+       cd->int_status |= CY_INT_MODE_CHANGE;
+       rc = cyttsp4_write(cd, CY_REG_BASE, &new_dev_mode,
+                       sizeof(new_dev_mode));
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Fail write mode change r=%d\n",
+                               __func__, rc);
+       mutex_unlock(&cd->system_lock);
+
+       /* wait for mode change done interrupt */
+       t = wait_event_timeout(cd->wait_q,
+                       (cd->int_status & CY_INT_MODE_CHANGE) == 0,
+                       msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+       dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n",
+                       __func__, t, cd->mode);
+
+       if (IS_TMO(t)) {
+               dev_err(cd->dev, "%s: %s\n", __func__,
+                               "tmo waiting mode change");
+               mutex_lock(&cd->system_lock);
+               cd->int_status &= ~CY_INT_MODE_CHANGE;
+               mutex_unlock(&cd->system_lock);
+               rc = -EINVAL;
+       }
+
+       return rc;
+}
+
+static const u8 ldr_exit[] = {
+       0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17
+};
+
+static int cyttsp4_core_sleep(struct cyttsp4_core_data *cd)
+{
+       enum cyttsp4_sleep_state ss = SS_SLEEP_ON;
+       int rc = 0;
+       u8 mode;
+
+       dev_vdbg(cd->dev, "%s: enter...\n", __func__);
+
+       /* Already in sleep mode? */
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_ON) {
+               mutex_unlock(&cd->system_lock);
+               return 0;
+       }
+       cd->sleep_state = SS_SLEEPING;
+       mutex_unlock(&cd->system_lock);
+
+       /* Wait until currently running IRQ handler exits and disable IRQ */
+       disable_irq(cd->irq);
+
+       dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__);
+
+       rc = cyttsp4_read(cd, CY_REG_BASE, &mode, sizeof(mode));
+       if (rc) {
+               dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc);
+               ss = SS_SLEEP_OFF;
+               goto exit;
+       }
+
+       if (IS_BOOTLOADER(mode)) {
+               dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__);
+               rc = -EINVAL;
+               ss = SS_SLEEP_OFF;
+               goto exit;
+       }
+       mode |= CY_HST_SLEEP;
+
+       rc = cyttsp4_write(cd, CY_REG_BASE, &mode, sizeof(mode));
+       if (rc) {
+               dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc);
+               ss = SS_SLEEP_OFF;
+               goto exit;
+       }
+
+       dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__);
+
+       if (cd->pdata->power) {
+               dev_dbg(cd->dev, "%s: Power down HW\n", __func__);
+               cd->pdata->power(cd->pdata, 0, cd->dev);
+       } else {
+               dev_vdbg(cd->dev, "%s: No power function\n", __func__);
+       }
+
+       cyttsp4_stop_wd_timer(cd);
+
+       /* Give time to FW to sleep */
+       mdelay(10);
+
+exit:
+       mutex_lock(&cd->system_lock);
+       if (ss == SS_SLEEP_ON)
+               cd->int_status |= CY_INT_IGNORE;
+       cd->sleep_state = ss;
+       mutex_unlock(&cd->system_lock);
+
+       enable_irq(cd->irq);
+
+       return rc;
+}
+
+
+static int cyttsp4_core_wake(struct cyttsp4_core_data *cd)
+{
+       struct device *dev = cd->dev;
+       int rc;
+       u8 mode;
+       int t;
+
+       dev_vdbg(cd->dev, "%s: enter...\n", __func__);
+
+       /* Already woken? */
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_OFF) {
+               mutex_unlock(&cd->system_lock);
+               return 0;
+       }
+       cd->int_status &= ~CY_INT_IGNORE;
+       cd->int_status |= CY_INT_AWAKE;
+       cd->sleep_state = SS_WAKING;
+
+       /* Do not start watchdog in already woken state */
+       cyttsp4_start_wd_timer(cd);
+
+       if (cd->pdata->power) {
+               dev_dbg(dev, "%s: Power up HW\n", __func__);
+               cd->pdata->power(cd->pdata, 1, dev);
+       } else {
+               dev_vdbg(dev, "%s: No power function\n", __func__);
+       }
+
+       rc = cyttsp4_wakeup(cd);
+       if (rc < 0) {
+               dev_err(dev, "%s: HW Power up fails r=%d\n",
+                               __func__, rc);
+
+               /* Initiate a read transaction to wake up */
+               cyttsp4_read(cd, CY_REG_BASE, &mode, sizeof(mode));
+       } else
+               dev_vdbg(cd->dev, "%s: HW power up succeeds\n",
+                       __func__);
+       mutex_unlock(&cd->system_lock);
+
+       t = wait_event_timeout(cd->sleep_q,
+                       (cd->int_status & CY_INT_AWAKE) == 0,
+                       msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT));
+       if (IS_TMO(t)) {
+               dev_err(dev, "%s: TMO waiting for wakeup\n", __func__);
+               mutex_lock(&cd->system_lock);
+               cd->int_status &= ~CY_INT_AWAKE;
+               mutex_unlock(&cd->system_lock);
+               /* Try starting up */
+               cyttsp4_queue_startup(cd);
+       }
+
+       mutex_lock(&cd->system_lock);
+       cd->sleep_state = SS_SLEEP_OFF;
+       mutex_unlock(&cd->system_lock);
+
+       return 0;
+}
+
+static int cyttsp4_startup_to_sysinfo_mode(struct cyttsp4_core_data *cd)
+{
+       long t;
+       int rc;
+
+       dev_dbg(cd->dev, "%s: enter...\n", __func__);
+
+       cyttsp4_stop_wd_timer(cd);
+
+       /* reset hardware and wait for heartbeat */
+       rc = cyttsp4_reset_and_wait(cd);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc);
+
+       /* exit bl into sysinfo mode */
+       dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__);
+       mutex_lock(&cd->system_lock);
+       cd->int_status &= ~CY_INT_IGNORE;
+       cd->int_status |= CY_INT_MODE_CHANGE;
+       rc = cyttsp4_write(cd, CY_REG_BASE, (u8 *)ldr_exit, sizeof(ldr_exit));
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Fail write adap= r=%d\n", __func__, rc);
+       mutex_unlock(&cd->system_lock);
+
+       dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__);
+       t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO,
+               msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT));
+       if (IS_TMO(t)) {
+               dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n",
+                               __func__, cd->mode);
+               mutex_lock(&cd->system_lock);
+               cd->int_status &= ~CY_INT_MODE_CHANGE;
+               mutex_unlock(&cd->system_lock);
+
+               cyttsp4_start_wd_timer(cd);
+
+               /*
+                * Unable to switch to SYSINFO mode,
+                * Corrupted FW may cause crash, exit here.
+                */
+               return 0;
+       }
+
+       /* read sysinfo data */
+       dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__);
+       rc = cyttsp4_get_sysinfo_regs(cd);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n",
+                       __func__, rc);
+       return rc;
+}
+
+static void cyttsp4_switch_to_operating_mode(struct cyttsp4_core_data *cd)
+{
+       bool sleep = false;
+
+       cyttsp4_start_wd_timer(cd);
+
+       /* switch to operational mode */
+       dev_vdbg(cd->dev, "%s: set mode hst_mode=%02X mode=%d...\n",
+               __func__, CY_HST_OPERATE, CY_MODE_OPERATIONAL);
+       cyttsp4_set_mode(cd, CY_HST_OPERATE, CY_MODE_OPERATIONAL);
+
+       /* restore to sleep if was suspended */
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_ON) {
+               cd->sleep_state = SS_SLEEP_OFF;
+               sleep = true;
+       }
+       mutex_unlock(&cd->system_lock);
+
+       if (sleep)
+               cyttsp4_core_sleep(cd);
+}
+
+static int cyttsp4_startup(struct cyttsp4_core_data *cd)
+{
+       int rc;
+
+       rc = cyttsp4_startup_to_sysinfo_mode(cd);
+       if (!rc)
+               cyttsp4_switch_to_operating_mode(cd);
+
+       return rc;
+}
+
+static void cyttsp4_startup_work_function(struct work_struct *work)
+{
+       struct cyttsp4_core_data *cd =  container_of(work,
+               struct cyttsp4_core_data, startup_work);
+       int rc;
+
+       rc = cyttsp4_startup(cd);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: Fail queued startup r=%d\n",
+                       __func__, rc);
+}
+
+static void cyttsp4_free_si_ptrs(struct cyttsp4_core_data *cd)
+{
+       struct cyttsp4_sysinfo *si = &cd->sysinfo;
+
+       kfree(si->si_ptrs.cydata);
+       kfree(si->si_ptrs.test);
+       kfree(si->si_ptrs.pcfg);
+       kfree(si->si_ptrs.opcfg);
+       kfree(si->si_ptrs.ddata);
+       kfree(si->si_ptrs.mdata);
+       kfree(si->btn);
+       kfree(si->xy_mode);
+       kfree(si->xy_data);
+       kfree(si->btn_rec_data);
+}
+
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
+static int cyttsp4_core_suspend(struct device *dev)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int rc;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       rc = cyttsp4_core_sleep(cd);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error on sleep\n", __func__);
+               return -EAGAIN;
+       }
+       return 0;
+}
+
+static int cyttsp4_core_resume(struct device *dev)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int rc;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       rc = cyttsp4_core_wake(cd);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error on wake\n", __func__);
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+#endif
+
+const struct dev_pm_ops cyttsp4_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume)
+       SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(cyttsp4_pm_ops);
+
+/*
+ * Show Firmware version via sysfs
+ */
+static ssize_t cyttsp4_ic_ver_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       struct cyttsp4_cydata *cydata = cd->sysinfo.si_ptrs.cydata;
+
+       return sprintf(buf,
+               "%s: 0x%02X 0x%02X\n"
+               "%s: 0x%02X\n"
+               "%s: 0x%02X\n"
+               "%s: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n"
+               "%s: 0x%02X\n"
+               "%s: 0x%02X\n",
+               "TrueTouch Product ID", cydata->ttpidh, cydata->ttpidl,
+               "Firmware Major Version", cydata->fw_ver_major,
+               "Firmware Minor Version", cydata->fw_ver_minor,
+               "Revision Control Number", cydata->revctrl[0],
+               cydata->revctrl[1], cydata->revctrl[2], cydata->revctrl[3],
+               cydata->revctrl[4], cydata->revctrl[5], cydata->revctrl[6],
+               cydata->revctrl[7],
+               "Bootloader Major Version", cydata->blver_major,
+               "Bootloader Minor Version", cydata->blver_minor);
+}
+
+/*
+ * Show Driver version via sysfs
+ */
+static ssize_t cyttsp4_drv_ver_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, CY_MAX_PRBUF_SIZE,
+               "Driver: %s\nVersion: %s\nDate: %s\n",
+               cy_driver_core_name, cy_driver_core_version,
+               cy_driver_core_date);
+}
+
+/*
+ * HW reset via sysfs
+ */
+static ssize_t cyttsp4_hw_reset_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int rc = 0;
+
+       rc = cyttsp4_startup(cd);
+       if (rc < 0)
+               dev_err(dev, "%s: HW reset failed r=%d\n",
+                       __func__, rc);
+
+       return size;
+}
+
+/*
+ * Show IRQ status via sysfs
+ */
+static ssize_t cyttsp4_hw_irq_stat_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       int retval;
+
+       retval = cyttsp4_irq_stat(cd->pdata, dev);
+       switch (retval) {
+       case 0:
+               return snprintf(buf, CY_MAX_PRBUF_SIZE,
+                       "Interrupt line is LOW.\n");
+       case 1:
+               return snprintf(buf, CY_MAX_PRBUF_SIZE,
+                       "Interrupt line is HIGH.\n");
+       }
+
+       return snprintf(buf, CY_MAX_PRBUF_SIZE,
+               "Function irq_stat() returned %d.\n", retval);
+}
+
+/*
+ * Show IRQ enable/disable status via sysfs
+ */
+static ssize_t cyttsp4_drv_irq_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       ssize_t ret;
+
+       mutex_lock(&cd->system_lock);
+       if (cd->irq_enabled)
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                       "Driver interrupt is ENABLED\n");
+       else
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                       "Driver interrupt is DISABLED\n");
+       mutex_unlock(&cd->system_lock);
+
+       return ret;
+}
+
+/*
+ * Enable/disable IRQ via sysfs
+ */
+static ssize_t cyttsp4_drv_irq_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       unsigned long value;
+       int retval = 0;
+
+       retval = kstrtoul(buf, 10, &value);
+       if (retval < 0) {
+               dev_err(dev, "%s: Invalid value\n", __func__);
+               goto cyttsp4_drv_irq_store_error_exit;
+       }
+
+       mutex_lock(&cd->system_lock);
+       switch (value) {
+       case 0:
+               if (cd->irq_enabled) {
+                       cd->irq_enabled = false;
+                       /* Disable IRQ */
+                       disable_irq_nosync(cd->irq);
+                       dev_info(dev, "%s: Driver IRQ now disabled\n",
+                               __func__);
+               } else
+                       dev_info(dev, "%s: Driver IRQ already disabled\n",
+                               __func__);
+               break;
+
+       case 1:
+               if (cd->irq_enabled == false) {
+                       cd->irq_enabled = true;
+                       /* Enable IRQ */
+                       enable_irq(cd->irq);
+                       dev_info(dev, "%s: Driver IRQ now enabled\n",
+                               __func__);
+               } else
+                       dev_info(dev, "%s: Driver IRQ already enabled\n",
+                               __func__);
+               break;
+
+       default:
+               dev_err(dev, "%s: Invalid value\n", __func__);
+       }
+       mutex_unlock(&(cd->system_lock));
+
+cyttsp4_drv_irq_store_error_exit:
+
+       return size;
+}
+
+/*
+ * Debugging options via sysfs
+ */
+static ssize_t cyttsp4_drv_debug_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       unsigned long value = 0;
+       int rc = 0;
+
+       rc = kstrtoul(buf, 10, &value);
+       if (rc < 0) {
+               dev_err(dev, "%s: Invalid value\n", __func__);
+               goto cyttsp4_drv_debug_store_exit;
+       }
+
+       switch (value) {
+       case CY_DBG_SUSPEND:
+               dev_info(dev, "%s: SUSPEND (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_core_sleep(cd);
+               if (rc)
+                       dev_err(dev, "%s: Suspend failed rc=%d\n",
+                               __func__, rc);
+               else
+                       dev_info(dev, "%s: Suspend succeeded\n", __func__);
+               break;
+
+       case CY_DBG_RESUME:
+               dev_info(dev, "%s: RESUME (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_core_wake(cd);
+               if (rc)
+                       dev_err(dev, "%s: Resume failed rc=%d\n",
+                               __func__, rc);
+               else
+                       dev_info(dev, "%s: Resume succeeded\n", __func__);
+               break;
+       case CY_DBG_SOFT_RESET:
+               dev_info(dev, "%s: SOFT RESET (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_hw_soft_reset(cd);
+               break;
+       case CY_DBG_RESET:
+               dev_info(dev, "%s: HARD RESET (cd=%p)\n", __func__, cd);
+               rc = cyttsp4_hw_hard_reset(cd);
+               break;
+       default:
+               dev_err(dev, "%s: Invalid value\n", __func__);
+       }
+
+cyttsp4_drv_debug_store_exit:
+       return size;
+}
+
+/*
+ * Show system status on deep sleep status via sysfs
+ */
+static ssize_t cyttsp4_sleep_status_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       ssize_t ret;
+
+       mutex_lock(&cd->system_lock);
+       if (cd->sleep_state == SS_SLEEP_ON)
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                               "Deep Sleep is ENABLED\n");
+       else
+               ret = snprintf(buf, CY_MAX_PRBUF_SIZE,
+                               "Deep Sleep is DISABLED\n");
+       mutex_unlock(&cd->system_lock);
+
+       return ret;
+}
+
+static struct device_attribute attributes[] = {
+       __ATTR(ic_ver, S_IRUGO, cyttsp4_ic_ver_show, NULL),
+       __ATTR(drv_ver, S_IRUGO, cyttsp4_drv_ver_show, NULL),
+       __ATTR(hw_reset, S_IWUSR, NULL, cyttsp4_hw_reset_store),
+       __ATTR(hw_irq_stat, S_IRUSR, cyttsp4_hw_irq_stat_show, NULL),
+       __ATTR(drv_irq, S_IRUSR | S_IWUSR, cyttsp4_drv_irq_show,
+               cyttsp4_drv_irq_store),
+       __ATTR(drv_debug, S_IWUSR, NULL, cyttsp4_drv_debug_store),
+       __ATTR(sleep_status, S_IRUSR, cyttsp4_sleep_status_show, NULL),
+};
+
+static int add_sysfs_interfaces(struct device *dev)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(attributes); i++)
+               if (device_create_file(dev, attributes + i))
+                       goto undo;
+       return 0;
+undo:
+       for (; i >= 0 ; i--)
+               device_remove_file(dev, attributes + i);
+       dev_err(dev, "%s: failed to create sysfs interface\n", __func__);
+       return -ENODEV;
+}
+
+static void remove_sysfs_interfaces(struct device *dev)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(attributes); i++)
+               device_remove_file(dev, attributes + i);
+}
+
+void cyttsp4_mt_release(struct cyttsp4_mt_data *md)
+{
+       struct device *dev = md->input->dev.parent;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+
+       input_unregister_device(md->input);
+
+       input_set_drvdata(md->input, NULL);
+}
+
+static int cyttsp4_mt_open(struct input_dev *input)
+{
+       struct device *dev = input->dev.parent;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       pm_runtime_get_sync(dev);
+
+       return 0;
+}
+
+static void cyttsp4_mt_close(struct input_dev *input)
+{
+       struct cyttsp4_mt_data *md = input_get_drvdata(input);
+       struct device *dev = input->dev.parent;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       cyttsp4_lift_all(md);
+
+       pm_runtime_put(dev);
+}
+
+
+int cyttsp4_mt_probe(struct cyttsp4_core_data *cd)
+{
+       struct device *dev = cd->dev;
+       struct cyttsp4_mt_data *md = &cd->md;
+       struct cyttsp4_platform_data *pdata = dev_get_platdata(dev);
+       struct cyttsp4_mt_platform_data *mt_pdata = pdata->mt_pdata;
+       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__);
+
+       if (mt_pdata == NULL) {
+               dev_err(dev, "%s: No MT platform data\n", __func__);
+               rc = -EINVAL;
+               goto error_no_mt_pdata;
+       }
+
+       md->prv_tch_type = CY_OBJ_STANDARD_FINGER;
+       md->pdata = mt_pdata;
+
+       /* 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 = mt_pdata->inp_dev_name;
+       scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev));
+       md->input->phys = md->phys;
+       md->input->id.bustype = cd->ops->bustype;
+       md->input->dev.parent = dev;
+       md->input->open = cyttsp4_mt_open;
+       md->input->close = cyttsp4_mt_close;
+       input_set_drvdata(md->input, md);
+
+       /* get sysinfo */
+       md->si = &cd->sysinfo;
+       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;
+               }
+       }
+
+       /* max num slots equals max touches + 1 for hover */
+       input_mt_init_slots(md->input, md->si->si_ofs.max_tchs + 1);
+       rc = input_register_device(md->input);
+       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:
+       input_set_drvdata(md->input, NULL);
+error_alloc_failed:
+error_no_mt_pdata:
+       dev_err(dev, "%s failed.\n", __func__);
+       return rc;
+}
+
+int cyttsp4_probe(struct device *dev, struct cyttsp4_bus_ops *ops,
+               u8 *xfer_buf)
+{
+       struct cyttsp4_core_data *cd;
+       struct cyttsp4_platform_data *pdata = dev_get_platdata(dev);
+       struct cyttsp4_core_platform_data *core_pdata;
+       unsigned long irq_flags;
+       int rc = 0;
+
+       dev_info(dev, "%s: startup\n", __func__);
+       dev_dbg(dev, "%s: debug on\n", __func__);
+       dev_vdbg(dev, "%s: verbose debug on\n", __func__);
+
+       if (pdata == NULL) {
+               dev_err(dev, "%s: No platform data\n", __func__);
+               rc = -EINVAL;
+               goto error_no_pdata;
+       }
+
+       core_pdata = pdata->core_pdata;
+       if (core_pdata == NULL) {
+               dev_err(dev, "%s: No core platform data\n", __func__);
+               rc = -EINVAL;
+               goto error_no_core_pdata;
+       }
+
+       /* get context and debug print buffers */
+       cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+       if (cd == NULL) {
+               dev_err(dev, "%s: Error, kzalloc\n", __func__);
+               rc = -ENOMEM;
+               goto error_alloc_data_failed;
+       }
+
+       /* init lists */
+       mutex_init(&cd->system_lock);
+       init_waitqueue_head(&cd->wait_q);
+       init_waitqueue_head(&cd->sleep_q);
+       cd->startup_work_q = create_singlethread_workqueue("startup_work_q");
+       if (cd->startup_work_q == NULL) {
+               dev_err(dev, "%s: No memory for %s\n", __func__,
+                       "startup_work_q");
+               goto error_init;
+       }
+
+       dev_dbg(dev, "%s: initialize core data\n", __func__);
+       spin_lock_init(&cd->spinlock);
+       cd->dev = dev;
+       cd->pdata = core_pdata;
+       cd->ops = ops;
+       cd->irq = gpio_to_irq(core_pdata->irq_gpio);
+       cd->irq_enabled = true;
+       cd->xfer_buf = xfer_buf;
+       dev_set_drvdata(dev, cd);
+       if (cd->irq < 0) {
+               rc = -EINVAL;
+               goto error_gpio_irq;
+       }
+
+       dev_info(cd->dev, "%s: Init HW\n", __func__);
+       rc = cyttsp4_init(cd->pdata, cd->dev);
+       if (rc < 0)
+               dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc);
+
+       INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function);
+
+       dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq);
+       if (core_pdata->level_irq_udelay > 0)
+               /* use level triggered interrupts */
+               irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT;
+       else
+               /* use edge triggered interrupts */
+               irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+
+       rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags,
+               dev_name(dev), cd);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error, could not request irq\n", __func__);
+               goto error_request_irq;
+       }
+
+       INIT_WORK(&cd->work, cyttsp4_timer_watchdog);
+       setup_timer(&cd->timer, cyttsp4_timer, (unsigned long)cd);
+
+       dev_dbg(dev, "%s: add sysfs interfaces\n", __func__);
+       rc = add_sysfs_interfaces(dev);
+       if (rc < 0) {
+               dev_err(dev, "%s: Error, fail sysfs init\n", __func__);
+               goto error_attr_create;
+       }
+
+       pm_runtime_enable(dev);
+
+       /*
+        * call startup directly to ensure that the device
+        * is tested before leaving the probe
+        */
+       dev_dbg(dev, "%s: call startup\n", __func__);
+
+       rc = cyttsp4_startup_to_sysinfo_mode(cd);
+
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: Fail initial startup r=%d\n",
+                       __func__, rc);
+               goto error_startup;
+       }
+
+       rc = cyttsp4_mt_probe(cd);
+
+       if (rc < 0) {
+               dev_err(cd->dev, "%s: Fail MT probe r=%d\n",
+                       __func__, rc);
+               goto error_startup;
+       }
+
+       cyttsp4_switch_to_operating_mode(cd);
+
+       dev_info(dev, "%s: ok\n", __func__);
+       rc = 0;
+       goto no_error;
+
+error_startup:
+       cyttsp4_free_si_ptrs(cd);
+       pm_runtime_disable(dev);
+error_attr_create:
+       free_irq(cd->irq, cd);
+error_request_irq:
+error_gpio_irq:
+       cyttsp4_exit(core_pdata, dev);
+error_init:
+       dev_set_drvdata(dev, NULL);
+       kfree(cd);
+error_alloc_data_failed:
+error_no_core_pdata:
+error_no_pdata:
+       dev_err(dev, "%s failed.\n", __func__);
+no_error:
+       return rc;
+}
+EXPORT_SYMBOL_GPL(cyttsp4_probe);
+
+void cyttsp4_remove(struct device *dev)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+
+       cyttsp4_mt_release(&cd->md);
+
+       cyttsp4_stop_wd_timer(cd);
+
+       remove_sysfs_interfaces(dev);
+       free_irq(cd->irq, cd);
+       cyttsp4_exit(cd->pdata, dev);
+       cyttsp4_free_si_ptrs(cd);
+       pm_runtime_disable(dev);
+       dev_set_drvdata(dev, NULL);
+       kfree(cd);
+}
+EXPORT_SYMBOL_GPL(cyttsp4_remove);
+
+static int __init cyttsp4_core_init(void)
+{
+       pr_info("%s: Cypress TTSP v4 core driver (Built %s)\n",
+                __func__, CY_DRIVER_DATE);
+       return 0;
+}
+module_init(cyttsp4_core_init);
+
+static void __exit cyttsp4_core_exit(void)
+{
+       pr_info("%s: module exit\n", __func__);
+}
+module_exit(cyttsp4_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard (TTSP) touchscreen core driver");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h
new file mode 100644
index 0000000..5626c70
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_core.h
@@ -0,0 +1,525 @@
+/*
+ * cyttsp4_core.h
+ * Cypress TrueTouch(TM) Standard Product V4 Core driver module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * 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 _CYTTSP4_CORE_H
+#define _CYTTSP4_CORE_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>
+
+#define CY_FW_FILE_NAME "cyttsp4_fw.bin"
+
+#define CY_MAX_PRBUF_SIZE           PIPE_BUF
+#define CY_PR_TRUNCATED             " truncated..."
+
+#define CY_TMA1036_TCH_REC_SIZE     6
+#define CY_TMA4XX_TCH_REC_SIZE      9
+#define CY_TMA1036_MAX_TCH          0x0E
+#define CY_TMA4XX_MAX_TCH           0x1E
+
+#define GET_HSTMODE(reg)            ((reg & 0x70) >> 4)
+#define GET_TOGGLE(reg)             ((reg & 0x80) >> 7)
+#define IS_BOOTLOADER(reg)          ((reg) == 0x01)
+#define IS_EXCLUSIVE(dev)           ((dev) != NULL)
+#define IS_TMO(t)                   ((t) == 0)
+
+#define CY_REG_BASE                 0x00
+#define CY_NUM_REVCTRL              8
+#define CY_NUM_MFGID                8
+#define CY_NUM_TCHREC               10
+#define CY_NUM_DDATA                32
+#define CY_NUM_MDATA                64
+
+#define CY_REG_CAT_CMD              2
+#define CY_CMD_COMPLETE_MASK        (1 << 6)
+#define CY_CMD_MASK                 0x3F
+#define CY_EBID                     0
+
+/* touch record system information offset masks and shifts */
+#define CY_BYTE_OFS_MASK            0x1F
+#define CY_BOFS_MASK                0xE0
+#define CY_BOFS_SHIFT               5
+
+#define CY_REQUEST_EXCLUSIVE_TIMEOUT   500
+
+/* maximum number of concurrent tracks */
+#define CY_NUM_TCH_ID               10
+
+#define CY_ACTIVE_STYLUS_ID         10
+
+/* helpers */
+#define GET_NUM_TOUCHES(x)          ((x) & 0x1F)
+#define IS_LARGE_AREA(x)            ((x) & 0x20)
+#define IS_BAD_PKT(x)               ((x) & 0x20)
+
+#define CY_WATCHDOG_TIMEOUT msecs_to_jiffies(1000)
+
+/* drv_debug commands */
+#define CY_DBG_SUSPEND                  4
+#define CY_DBG_RESUME                   5
+#define CY_DBG_SOFT_RESET               97
+#define CY_DBG_RESET                    98
+
+enum cyttsp4_hst_mode_bits {
+       CY_HST_TOGGLE      = (1 << 7),
+       CY_HST_MODE_CHANGE = (1 << 3),
+       CY_HST_MODE        = (7 << 4),
+       CY_HST_OPERATE     = (0 << 4),
+       CY_HST_SYSINFO     = (1 << 4),
+       CY_HST_CAT         = (2 << 4),
+       CY_HST_LOWPOW      = (1 << 2),
+       CY_HST_SLEEP       = (1 << 1),
+       CY_HST_RESET       = (1 << 0),
+};
+
+enum cyttsp_cmd_bits {
+       CY_CMD_COMPLETE    = (1 << 6),
+};
+
+enum cyttsp4_cmd_cat {
+       CY_CMD_CAT_NULL,
+       CY_CMD_CAT_RESERVED_1,
+       CY_CMD_CAT_GET_CFG_BLK_SZ,
+       CY_CMD_CAT_READ_CFG_BLK,
+       CY_CMD_CAT_WRITE_CFG_BLK,
+       CY_CMD_CAT_RESERVED_2,
+       CY_CMD_CAT_LOAD_SELF_TEST_DATA,
+       CY_CMD_CAT_RUN_SELF_TEST,
+       CY_CMD_CAT_GET_SELF_TEST_RESULT,
+       CY_CMD_CAT_CALIBRATE_IDACS,
+       CY_CMD_CAT_INIT_BASELINES,
+       CY_CMD_CAT_EXEC_PANEL_SCAN,
+       CY_CMD_CAT_RETRIEVE_PANEL_SCAN,
+       CY_CMD_CAT_START_SENSOR_DATA_MODE,
+       CY_CMD_CAT_STOP_SENSOR_DATA_MODE,
+       CY_CMD_CAT_INT_PIN_MODE,
+       CY_CMD_CAT_RETRIEVE_DATA_STRUCTURE,
+       CY_CMD_CAT_VERIFY_CFG_BLK_CRC,
+       CY_CMD_CAT_RESERVED_N,
+};
+
+enum cyttsp4_tt_mode_bits {
+       CY_TT_BL     = (1 << 4),
+       CY_TT_INVAL  = (1 << 5),
+       CY_TT_CNTR   = (3 << 6),
+};
+
+enum cyttsp4_bl_status_bits {
+       CY_BL_CS_OK    = (1 << 0),
+       CY_BL_WDOG     = (1 << 1),
+       CY_BL_RUNNING  = (1 << 4),
+       CY_BL_BUSY     = (1 << 7),
+};
+
+/* times */
+#define CY_SCAN_PERIOD              40
+#define CY_BL_ENTER_TIME            100
+
+enum cyttsp4_mode {
+       CY_MODE_UNKNOWN      = 0,
+       CY_MODE_BOOTLOADER   = (1 << 1),
+       CY_MODE_OPERATIONAL  = (1 << 2),
+       CY_MODE_SYSINFO      = (1 << 3),
+       CY_MODE_CAT          = (1 << 4),
+       CY_MODE_STARTUP      = (1 << 5),
+       CY_MODE_LOADER       = (1 << 6),
+       CY_MODE_CHANGE_MODE  = (1 << 7),
+       CY_MODE_CHANGED      = (1 << 8),
+       CY_MODE_CMD_COMPLETE = (1 << 9),
+};
+
+enum cyttsp4_int_state {
+       CY_INT_IGNORE      = (1 << 0),
+       CY_INT_MODE_CHANGE = (1 << 1),
+       CY_INT_EXEC_CMD    = (1 << 2),
+       CY_INT_AWAKE       = (1 << 3),
+};
+
+enum cyttsp4_ic_grpnum {
+       CY_IC_GRPNUM_RESERVED,
+       CY_IC_GRPNUM_CMD_REGS,
+       CY_IC_GRPNUM_TCH_REP,
+       CY_IC_GRPNUM_DATA_REC,
+       CY_IC_GRPNUM_TEST_REC,
+       CY_IC_GRPNUM_PCFG_REC,
+       CY_IC_GRPNUM_TCH_PARM_VAL,
+       CY_IC_GRPNUM_TCH_PARM_SIZE,
+       CY_IC_GRPNUM_RESERVED1,
+       CY_IC_GRPNUM_RESERVED2,
+       CY_IC_GRPNUM_OPCFG_REC,
+       CY_IC_GRPNUM_DDATA_REC,
+       CY_IC_GRPNUM_MDATA_REC,
+       CY_IC_GRPNUM_TEST_REGS,
+       CY_IC_GRPNUM_BTN_KEYS,
+       CY_IC_GRPNUM_TTHE_REGS,
+       CY_IC_GRPNUM_NUM
+};
+
+#define CY_VKEYS_X 720
+#define CY_VKEYS_Y 1280
+
+enum cyttsp4_flags {
+       CY_FLAG_NONE = 0x00,
+       CY_FLAG_HOVER = 0x04,
+       CY_FLAG_FLIP = 0x08,
+       CY_FLAG_INV_X = 0x10,
+       CY_FLAG_INV_Y = 0x20,
+       CY_FLAG_VKEYS = 0x40,
+};
+
+enum cyttsp4_event_id {
+       CY_EV_NO_EVENT,
+       CY_EV_TOUCHDOWN,
+       CY_EV_MOVE,             /* significant displacement (> act dist) */
+       CY_EV_LIFTOFF,          /* record reports last position */
+};
+
+enum cyttsp4_object_id {
+       CY_OBJ_STANDARD_FINGER,
+       CY_OBJ_LARGE_OBJECT,
+       CY_OBJ_STYLUS,
+       CY_OBJ_HOVER,
+};
+
+#define CY_POST_CODEL_WDG_RST           0x01
+#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02
+#define CY_POST_CODEL_PANEL_TEST_FAIL   0x04
+
+#define CY_TEST_CMD_NULL                0
+
+/* test mode NULL command driver codes; D */
+enum cyttsp4_null_test_cmd_code {
+       CY_NULL_CMD_NULL,
+       CY_NULL_CMD_MODE,
+       CY_NULL_CMD_STATUS_SIZE,
+       CY_NULL_CMD_HANDSHAKE,
+       CY_NULL_CMD_LOW_POWER,
+};
+
+enum cyttsp4_test_mode {
+       CY_TEST_MODE_NORMAL_OP,         /* Send touch data to OS; normal op */
+       CY_TEST_MODE_CAT,               /* Configuration and Test */
+       CY_TEST_MODE_SYSINFO,           /* System information mode */
+       CY_TEST_MODE_CLOSED_UNIT,       /* Send scan data to sysfs */
+};
+
+struct cyttsp4_test_mode_params {
+       int cur_mode;
+       int cur_cmd;
+       size_t cur_status_size;
+};
+
+/* GEN4/SOLO Operational interface definitions */
+/* TTSP System Information interface definitions */
+struct cyttsp4_cydata {
+       u8 ttpidh;
+       u8 ttpidl;
+       u8 fw_ver_major;
+       u8 fw_ver_minor;
+       u8 revctrl[CY_NUM_REVCTRL];
+       u8 blver_major;
+       u8 blver_minor;
+       u8 jtag_si_id3;
+       u8 jtag_si_id2;
+       u8 jtag_si_id1;
+       u8 jtag_si_id0;
+       u8 mfgid_sz;
+       u8 mfg_id[CY_NUM_MFGID];
+       u8 cyito_idh;
+       u8 cyito_idl;
+       u8 cyito_verh;
+       u8 cyito_verl;
+       u8 ttsp_ver_major;
+       u8 ttsp_ver_minor;
+       u8 device_info;
+} __packed;
+
+struct cyttsp4_test {
+       u8 post_codeh;
+       u8 post_codel;
+} __packed;
+
+struct cyttsp4_pcfg {
+       u8 electrodes_x;
+       u8 electrodes_y;
+       u8 len_xh;
+       u8 len_xl;
+       u8 len_yh;
+       u8 len_yl;
+       u8 res_xh;
+       u8 res_xl;
+       u8 res_yh;
+       u8 res_yl;
+       u8 max_zh;
+       u8 max_zl;
+} __packed;
+
+enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */
+       CY_TCH_X,       /* X */
+       CY_TCH_Y,       /* Y */
+       CY_TCH_P,       /* P (Z) */
+       CY_TCH_T,       /* TOUCH ID */
+       CY_TCH_E,       /* EVENT ID */
+       CY_TCH_O,       /* OBJECT ID */
+       CY_TCH_W,       /* SIZE */
+       CY_TCH_MAJ,     /* TOUCH_MAJOR */
+       CY_TCH_MIN,     /* TOUCH_MINOR */
+       CY_TCH_OR,      /* ORIENTATION */
+       CY_TCH_NUM_ABS
+};
+
+static const char * const cyttsp4_tch_abs_string[] = {
+       [CY_TCH_X]      = "X",
+       [CY_TCH_Y]      = "Y",
+       [CY_TCH_P]      = "P",
+       [CY_TCH_T]      = "T",
+       [CY_TCH_E]      = "E",
+       [CY_TCH_O]      = "O",
+       [CY_TCH_W]      = "W",
+       [CY_TCH_MAJ]    = "MAJ",
+       [CY_TCH_MIN]    = "MIN",
+       [CY_TCH_OR]     = "OR",
+       [CY_TCH_NUM_ABS] = "INVALID"
+};
+
+#define CY_NUM_TCH_FIELDS       7
+#define CY_NUM_EXT_TCH_FIELDS   3
+
+struct cyttsp4_tch_rec_params {
+       u8 loc;
+       u8 size;
+} __packed;
+
+struct cyttsp4_opcfg {
+       u8 cmd_ofs;
+       u8 rep_ofs;
+       u8 rep_szh;
+       u8 rep_szl;
+       u8 num_btns;
+       u8 tt_stat_ofs;
+       u8 obj_cfg0;
+       u8 max_tchs;
+       u8 tch_rec_size;
+       struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS];
+       u8 btn_rec_size;/* btn record size (in bytes) */
+       u8 btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+       u8 btn_diff_size;/* btn size of diff counts (in bits) */
+       struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS];
+} __packed;
+
+struct cyttsp4_sysinfo_data {
+       u8 hst_mode;
+       u8 reserved;
+       u8 map_szh;
+       u8 map_szl;
+       u8 cydata_ofsh;
+       u8 cydata_ofsl;
+       u8 test_ofsh;
+       u8 test_ofsl;
+       u8 pcfg_ofsh;
+       u8 pcfg_ofsl;
+       u8 opcfg_ofsh;
+       u8 opcfg_ofsl;
+       u8 ddata_ofsh;
+       u8 ddata_ofsl;
+       u8 mdata_ofsh;
+       u8 mdata_ofsl;
+} __packed;
+
+struct cyttsp4_sysinfo_ptr {
+       struct cyttsp4_cydata *cydata;
+       struct cyttsp4_test *test;
+       struct cyttsp4_pcfg *pcfg;
+       struct cyttsp4_opcfg *opcfg;
+       struct cyttsp4_ddata *ddata;
+       struct cyttsp4_mdata *mdata;
+} __packed;
+
+struct cyttsp4_touch {
+       int abs[CY_TCH_NUM_ABS];
+};
+
+struct cyttsp4_tch_abs_params {
+       size_t ofs;     /* abs byte offset */
+       size_t size;    /* size in bits */
+       size_t max;     /* max value */
+       size_t bofs;    /* bit offset */
+};
+
+#define CY_NORMAL_ORIGIN 0     /* upper, left corner */
+#define CY_INVERT_ORIGIN 1     /* lower, right corner */
+
+struct cyttsp4_sysinfo_ofs {
+       size_t chip_type;
+       size_t cmd_ofs;
+       size_t rep_ofs;
+       size_t rep_sz;
+       size_t num_btns;
+       size_t num_btn_regs;    /* ceil(num_btns/4) */
+       size_t tt_stat_ofs;
+       size_t tch_rec_size;
+       size_t obj_cfg0;
+       size_t max_tchs;
+       size_t mode_size;
+       size_t data_size;
+       size_t map_sz;
+       size_t max_x;
+       size_t x_origin;        /* left or right corner */
+       size_t max_y;
+       size_t y_origin;        /* upper or lower corner */
+       size_t max_p;
+       size_t cydata_ofs;
+       size_t test_ofs;
+       size_t pcfg_ofs;
+       size_t opcfg_ofs;
+       size_t ddata_ofs;
+       size_t mdata_ofs;
+       size_t cydata_size;
+       size_t test_size;
+       size_t pcfg_size;
+       size_t opcfg_size;
+       size_t ddata_size;
+       size_t mdata_size;
+       size_t btn_keys_size;
+       struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
+       size_t btn_rec_size; /* btn record size (in bytes) */
+       size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
+       size_t btn_diff_size;/* btn size of diff counts (in bits) */
+};
+
+/* button to keycode support */
+#define CY_NUM_BTN_PER_REG     4
+#define CY_NUM_BTN_EVENT_ID    4
+#define CY_BITS_PER_BTN                2
+
+enum cyttsp4_btn_state {
+       CY_BTN_RELEASED = 0,
+       CY_BTN_PRESSED = 1,
+       CY_BTN_NUM_STATE
+};
+
+struct cyttsp4_btn {
+       bool enabled;
+       int state;      /* CY_BTN_PRESSED, CY_BTN_RELEASED */
+       int key_code;
+};
+
+
+struct cyttsp4_sysinfo {
+       bool ready;
+       struct cyttsp4_sysinfo_data si_data;
+       struct cyttsp4_sysinfo_ptr si_ptrs;
+       struct cyttsp4_sysinfo_ofs si_ofs;
+       struct cyttsp4_btn *btn;        /* button states */
+       u8 *btn_rec_data;               /* button diff count data */
+       u8 *xy_mode;                    /* operational mode and status regs */
+       u8 *xy_data;                    /* operational touch regs */
+};
+
+struct cyttsp4_mt_data {
+       struct cyttsp4_mt_platform_data *pdata;
+       struct cyttsp4_sysinfo *si;
+       struct input_dev *input;
+       char phys[NAME_MAX];
+       int num_prv_tch;
+       int prv_tch_type;
+#ifdef VERBOSE_DEBUG
+       u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+};
+
+enum cyttsp4_sleep_state {
+       SS_SLEEP_OFF,
+       SS_SLEEP_ON,
+       SS_SLEEPING,
+       SS_WAKING,
+};
+
+struct cyttsp4_core_data {
+       struct device *dev;
+       struct mutex system_lock;
+       enum cyttsp4_mode mode;
+       enum cyttsp4_sleep_state sleep_state;
+       int int_status;
+       int cmd_toggle;
+       spinlock_t spinlock;
+       struct cyttsp4_core_platform_data *pdata;
+       wait_queue_head_t wait_q;
+       wait_queue_head_t sleep_q;
+       int irq;
+       struct workqueue_struct *startup_work_q;
+       struct work_struct startup_work;
+       struct workqueue_struct *mode_change_work_q;
+       struct work_struct mode_change_work;
+       struct cyttsp4_sysinfo sysinfo;
+       struct cyttsp4_bus_ops *ops;
+       atomic_t ignore_irq;
+       bool irq_enabled;
+       struct work_struct work;
+       struct timer_list timer;
+       struct cyttsp4_mt_data md;
+       u8 *xfer_buf;
+#ifdef VERBOSE_DEBUG
+       u8 pr_buf[CY_MAX_PRBUF_SIZE];
+#endif
+};
+
+struct cyttsp4_bus_ops {
+       u16 bustype;
+       int (*write)(struct device *dev, u8 addr,
+               const void *buf, int size);
+       int (*read)(struct device *dev, u8 addr, void *buf, int size);
+};
+
+#ifdef VERBOSE_DEBUG
+extern void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size,
+                          const char *data_name);
+#else
+#define cyttsp4_pr_buf(a, b, c, d, e) do { } while (0)
+#endif
+
+extern int cyttsp4_probe(struct device *dev, struct cyttsp4_bus_ops *ops,
+               u8 *xfer_buf);
+extern void cyttsp4_remove(struct device *dev);
+extern const struct dev_pm_ops cyttsp4_pm_ops;
+
+static inline u8 *cyttsp4_get_xfer_buf(struct device *dev)
+{
+       struct cyttsp4_core_data *cd = dev_get_drvdata(dev);
+       return cd->xfer_buf;
+}
+
+#endif /* _CYTTSP4_CORE_H */
diff --git a/include/linux/input/cyttsp4.h b/include/linux/input/cyttsp4.h
new file mode 100644
index 0000000..12d200b
--- /dev/null
+++ b/include/linux/input/cyttsp4.h
@@ -0,0 +1,126 @@
+/*
+ * cyttsp4.h
+ * Cypress TrueTouch(TM) Standard Product V4 module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * 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_H
+#define _LINUX_CYTTSP4_H
+
+#define CYTTSP4_CORE_NAME "cyttsp4_core"
+#define CYTTSP4_MT_NAME "cyttsp4_mt"
+
+#define CYTTSP4_STR(x) #x
+#define CYTTSP4_STRINGIFY(x) CYTTSP4_STR(x)
+
+#define CY_DRIVER_NAME TTDA
+#define CY_DRIVER_MAJOR 02
+#define CY_DRIVER_MINOR 01
+
+#define CY_DRIVER_REVCTRL 000004
+
+#define CY_DRIVER_VERSION                          \
+CYTTSP4_STRINGIFY(CY_DRIVER_NAME)                  \
+"." CYTTSP4_STRINGIFY(CY_DRIVER_MAJOR)             \
+"." CYTTSP4_STRINGIFY(CY_DRIVER_MINOR)             \
+"." CYTTSP4_STRINGIFY(CY_DRIVER_REVCTRL)
+
+#define CY_DRIVER_DATE "20120803"      /* YYYYMMDD */
+
+/* x-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_X_MASK 0x7F
+
+/* y-axis resolution of panel in pixels */
+#define CY_PCFG_RESOLUTION_Y_MASK 0x7F
+
+/* x-axis, 0:origin is on left side of panel, 1: right */
+#define CY_PCFG_ORIGIN_X_MASK 0x80
+
+/* y-axis, 0:origin is on top side of panel, 1: bottom */
+#define CY_PCFG_ORIGIN_Y_MASK 0x80
+
+#define CY_TOUCH_SETTINGS_MAX 32
+
+struct touch_settings {
+       const uint8_t   *data;
+       uint32_t         size;
+       uint8_t         tag;
+} __packed;
+
+struct cyttsp4_core_platform_data {
+       int irq_gpio;
+       int rst_gpio;
+       int level_irq_udelay;
+       /* power is called before waking up and after sleeping
+        * to perform any external power related tasks
+        */
+       void (*power)(struct cyttsp4_core_platform_data *pdata,
+               int on, struct device *dev);
+       struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX];
+};
+
+/* 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;
+};
+
+struct cyttsp4_platform_data {
+       struct cyttsp4_core_platform_data *core_pdata;
+       struct cyttsp4_mt_platform_data *mt_pdata;
+};
+#endif /* _LINUX_CYTTSP4_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.

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

* [PATCH v2 2/3] Input: cyttsp4 - I2C driver for Cypress TMA4XX touchscreen devices
  2012-09-14 17:48 ` [PATCH v2 0/3] Input: cyttsp4 - " Ferruh Yigit
  2012-09-14 17:48   ` [PATCH v2 1/3] Input: cyttsp4 - core " Ferruh Yigit
@ 2012-09-14 17:48   ` Ferruh Yigit
  2012-09-14 17:48   ` [PATCH v2 3/3] Input: cyttsp4 - SPI " Ferruh Yigit
  2012-09-14 18:46   ` [PATCH v2 0/3] Input: cyttsp4 - " Henrik Rydberg
  3 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-09-14 17:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Phil Warkentin, Ferruh Yigit,
	Javier Martinez Canillas, Henrik Rydberg, Shawn Landden,
	Ashish Jangam, Olivier Sobrie, linux-input, linux-kernel

Cypress TrueTouch(tm) Standard Product controllers, Generation4
devices, I2C adapter module.

This driver adds communication support with TTSP controller
using I2C bus.

Signed-off-by: Ferruh Yigit <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig       |   12 ++
 drivers/input/touchscreen/Makefile      |    3 +
 drivers/input/touchscreen/cyttsp4_i2c.c |  191 +++++++++++++++++++++++++++++++
 drivers/input/touchscreen/cyttsp4_i2c.h |   34 ++++++
 4 files changed, 240 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index d6ebd4c..fcc1c82 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -212,6 +212,18 @@ config TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG

          Say Y here to enable verbose debug output.

+config TOUCHSCREEN_CYPRESS_CYTTSP4_I2C
+       tristate "Cypress TrueTouch Gen4 I2C"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4
+       depends on I2C
+       default n
+       help
+         Cypress TrueTouch(tm) Standard Product Generation4
+         I2C bus interface.
+
+         Say Y here to enable I2C bus interface to TTSP
+         touchscreen controller.
+
 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 1cdab28..8f5e8ff 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -74,10 +74,13 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)   += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4)      += cyttsp4_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_I2C)  += cyttsp4_i2c.o
 ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG),y)
 CFLAGS_cyttsp4_core.o += -DDEBUG
+CFLAGS_cyttsp4_i2c.o += -DDEBUG
 endif

 ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG),y)
 CFLAGS_cyttsp4_core.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_i2c.o += -DVERBOSE_DEBUG
 endif
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c
new file mode 100644
index 0000000..0faf0f7
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_i2c.c
@@ -0,0 +1,191 @@
+/*
+ * cyttsp4_i2c.c
+ * Cypress TrueTouch(TM) Standard Product V4 I2C Driver module.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * 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/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/input/cyttsp4.h>
+#include "cyttsp4_core.h"
+#include "cyttsp4_i2c.h"
+
+#define CY_I2C_DATA_SIZE       256
+
+static int cyttsp4_i2c_read_block_data(struct i2c_client *client, u8 addr,
+       size_t length, void *values)
+{
+       int rc;
+
+       /* write addr */
+       rc = i2c_master_send(client, &addr, sizeof(addr));
+       if (rc < 0)
+               return rc;
+       else if (rc != sizeof(addr))
+               return -EIO;
+
+       /* read data */
+       rc = i2c_master_recv(client, values, length);
+
+       return (rc < 0) ? rc : rc != length ? -EIO : 0;
+}
+
+static int cyttsp4_i2c_write_block_data(struct i2c_client *client, u8 addr,
+       size_t length, const void *values)
+{
+       int rc;
+       u8 *wr_buf = cyttsp4_get_xfer_buf(&client->dev);
+
+       if (length + 1 > CY_I2C_DATA_SIZE)
+               return -EINVAL;
+
+       wr_buf[0] = addr;
+       memcpy(&wr_buf[1], values, length);
+       length += 1;
+
+       /* write data */
+       rc = i2c_master_send(client, wr_buf, length);
+
+       return (rc < 0) ? rc : rc != length ? -EIO : 0;
+}
+
+static int cyttsp4_i2c_write(struct device *dev, u8 addr,
+       const void *buf, int size)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int rc;
+
+       rc = cyttsp4_i2c_write_block_data(client, addr, size, buf);
+
+       return rc;
+}
+
+static int cyttsp4_i2c_read(struct device *dev, u8 addr,
+               void *buf, int size)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int rc;
+
+       rc = cyttsp4_i2c_read_block_data(client, addr, size, buf);
+
+       return rc;
+}
+
+static struct cyttsp4_bus_ops ops = {
+       .bustype = BUS_I2C,
+       .write = cyttsp4_i2c_write,
+       .read = cyttsp4_i2c_read,
+};
+
+static int __devinit cyttsp4_i2c_probe(struct i2c_client *client,
+       const struct i2c_device_id *i2c_id)
+{
+       struct device *dev = &client->dev;
+       u8 *xfer_buf;
+       int rc;
+
+       dev_info(dev, "%s: Starting %s probe...\n", __func__, CYTTSP4_I2C_NAME);
+
+       dev_dbg(dev, "%s: debug on\n", __func__);
+       dev_vdbg(dev, "%s: verbose debug on\n", __func__);
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(dev, "%s: fail check I2C functionality\n", __func__);
+               return -EIO;
+       }
+
+       xfer_buf = kmalloc(CY_I2C_DATA_SIZE, GFP_KERNEL);
+       if (!xfer_buf) {
+               dev_err(dev, "%s: Error, kmalloc\n", __func__);
+               return -ENOMEM;
+       }
+
+       client->dev.bus = &i2c_bus_type;
+
+       rc = cyttsp4_probe(dev, &ops, xfer_buf);
+       if (rc) {
+               dev_err(dev, "%s: Error on probe %s\n", __func__,
+                       CYTTSP4_I2C_NAME);
+               goto probe_err;
+       }
+
+       dev_info(dev, "%s: Successful probe %s\n", __func__, CYTTSP4_I2C_NAME);
+
+       return 0;
+
+probe_err:
+       kfree(xfer_buf);
+       return rc;
+}
+
+static int __devexit cyttsp4_i2c_remove(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       u8 *xfer_buf = cyttsp4_get_xfer_buf(dev);
+
+       dev_info(dev, "%s\n", __func__);
+       cyttsp4_remove(dev);
+       kfree(xfer_buf);
+       return 0;
+}
+
+static const struct i2c_device_id cyttsp4_i2c_id[] = {
+       { CYTTSP4_I2C_NAME, 0 },  { }
+};
+
+static struct i2c_driver cyttsp4_i2c_driver = {
+       .driver = {
+               .name = CYTTSP4_I2C_NAME,
+               .owner = THIS_MODULE,
+               .pm = &cyttsp4_pm_ops,
+       },
+       .probe = cyttsp4_i2c_probe,
+       .remove = __devexit_p(cyttsp4_i2c_remove),
+       .id_table = cyttsp4_i2c_id,
+};
+
+static int __init cyttsp4_i2c_init(void)
+{
+       int rc = i2c_add_driver(&cyttsp4_i2c_driver);
+
+       pr_info("%s: Cypress TTSP I2C Touchscreen Driver (Built %s) rc=%d\n",
+                __func__, CY_DRIVER_DATE, rc);
+       return rc;
+}
+module_init(cyttsp4_i2c_init);
+
+static void __exit cyttsp4_i2c_exit(void)
+{
+       i2c_del_driver(&cyttsp4_i2c_driver);
+       pr_info("%s: module exit\n", __func__);
+}
+module_exit(cyttsp4_i2c_exit);
+
+MODULE_ALIAS(CYTTSP4_I2C_NAME);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
+MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id);
diff --git a/drivers/input/touchscreen/cyttsp4_i2c.h b/drivers/input/touchscreen/cyttsp4_i2c.h
new file mode 100644
index 0000000..5137102
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_i2c.h
@@ -0,0 +1,34 @@
+/*
+ * cyttsp4_i2c.h
+ * Cypress TrueTouch(TM) Standard Product V4 I2C driver module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * 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_I2C_H
+#define _LINUX_CYTTSP4_I2C_H
+
+#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter"
+
+#endif /* _LINUX_CYTTSP4_I2C_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.

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

* [PATCH v2 3/3] Input: cyttsp4 - SPI driver for Cypress TMA4XX touchscreen devices
  2012-09-14 17:48 ` [PATCH v2 0/3] Input: cyttsp4 - " Ferruh Yigit
  2012-09-14 17:48   ` [PATCH v2 1/3] Input: cyttsp4 - core " Ferruh Yigit
  2012-09-14 17:48   ` [PATCH v2 2/3] Input: cyttsp4 - I2C " Ferruh Yigit
@ 2012-09-14 17:48   ` Ferruh Yigit
  2012-09-14 18:46   ` [PATCH v2 0/3] Input: cyttsp4 - " Henrik Rydberg
  3 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-09-14 17:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Kevin McNeely, Phil Warkentin, Ferruh Yigit,
	Javier Martinez Canillas, Henrik Rydberg, Shawn Landden,
	Ashish Jangam, Olivier Sobrie, linux-input, linux-kernel

Cypress TrueTouch(tm) Standard Product controllers, Generation4
devices, SPI adapter module.

This driver adds communication support with TTSP controller
using SPI bus.

Signed-off-by: Ferruh Yigit <fery@cypress.com>
---
 drivers/input/touchscreen/Kconfig       |   12 ++
 drivers/input/touchscreen/Makefile      |    3 +
 drivers/input/touchscreen/cyttsp4_spi.c |  318 +++++++++++++++++++++++++++++++
 drivers/input/touchscreen/cyttsp4_spi.h |   34 ++++
 4 files changed, 367 insertions(+)
 create mode 100644 drivers/input/touchscreen/cyttsp4_spi.c
 create mode 100644 drivers/input/touchscreen/cyttsp4_spi.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index fcc1c82..59706e8 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -224,6 +224,18 @@ config TOUCHSCREEN_CYPRESS_CYTTSP4_I2C
          Say Y here to enable I2C bus interface to TTSP
          touchscreen controller.

+config TOUCHSCREEN_CYPRESS_CYTTSP4_SPI
+       tristate "Cypress TrueTouch Gen4 SPI"
+       depends on TOUCHSCREEN_CYPRESS_CYTTSP4
+       depends on SPI
+       default n
+       help
+         Cypress TrueTouch(tm) Standard Product Generation4
+         SPI bus interface.
+
+         Say Y here to enable SPI bus interface to TTSP
+         touchscreen controller.
+
 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 8f5e8ff..585be70 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -75,12 +75,15 @@ obj-$(CONFIG_TOUCHSCREEN_W90X900)   += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4)      += cyttsp4_core.o
 obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_I2C)  += cyttsp4_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_SPI)  += cyttsp4_spi.o
 ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_DEBUG),y)
 CFLAGS_cyttsp4_core.o += -DDEBUG
 CFLAGS_cyttsp4_i2c.o += -DDEBUG
+CFLAGS_cyttsp4_spi.o += -DDEBUG
 endif

 ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP4_VDEBUG),y)
 CFLAGS_cyttsp4_core.o += -DVERBOSE_DEBUG
 CFLAGS_cyttsp4_i2c.o += -DVERBOSE_DEBUG
+CFLAGS_cyttsp4_spi.o += -DVERBOSE_DEBUG
 endif
diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c
new file mode 100644
index 0000000..4aa036e
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_spi.c
@@ -0,0 +1,318 @@
+/*
+ * cyttsp4_spi.c
+ * Cypress TrueTouch(TM) Standard Product V4 SPI Driver module.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * 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/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/input/cyttsp4.h>
+#include "cyttsp4_core.h"
+#include "cyttsp4_spi.h"
+
+#define CY_SPI_WR_OP           0x00 /* r/~w */
+#define CY_SPI_RD_OP           0x01
+#define CY_SPI_A8_BIT          0x02
+#define CY_SPI_WR_HEADER_BYTES 2
+#define CY_SPI_RD_HEADER_BYTES 1
+#define CY_SPI_SYNC_BYTE       0
+#define CY_SPI_SYNC_ACK                0x62 /* from TRM *A protocol */
+#define CY_SPI_DATA_SIZE       (3 * 256)
+#define CY_SPI_BITS_PER_WORD   8
+#define CY_SPI_MAX_REG         512
+
+#define CY_SPI_MAX_HEADER_BYTES        \
+               max(CY_SPI_WR_HEADER_BYTES, CY_SPI_RD_HEADER_BYTES)
+
+static int cyttsp4_spi_xfer(u8 op, struct spi_device *spi,
+                       u8 reg, u8 *buf, int length)
+{
+       struct device *dev = &spi->dev;
+       struct spi_message msg;
+       struct spi_transfer xfer[2];
+       u8 wr_hdr_buf[CY_SPI_MAX_HEADER_BYTES];
+       u8 rd_hdr_buf[CY_SPI_MAX_HEADER_BYTES];
+       int rc;
+
+       memset(wr_hdr_buf, 0, CY_SPI_MAX_HEADER_BYTES);
+       memset(rd_hdr_buf, 0, CY_SPI_MAX_HEADER_BYTES);
+       memset(xfer, 0, sizeof(xfer));
+
+       spi_message_init(&msg);
+
+       /* Header buffer */
+       xfer[0].tx_buf = wr_hdr_buf;
+       xfer[0].rx_buf = rd_hdr_buf;
+
+       switch (op) {
+       case CY_SPI_WR_OP:
+               if (length + CY_SPI_WR_HEADER_BYTES > CY_SPI_DATA_SIZE) {
+                       dev_vdbg(dev,
+                               "%s: length+%d=%d is greater than SPI max=%d\n",
+                               __func__, CY_SPI_WR_HEADER_BYTES,
+                               length + CY_SPI_WR_HEADER_BYTES,
+                               CY_SPI_DATA_SIZE);
+                       rc = -EINVAL;
+                       goto cyttsp4_spi_xfer_exit;
+               }
+
+               /* Header byte 0 */
+               if (reg > 255)
+                       wr_hdr_buf[0] = CY_SPI_WR_OP + CY_SPI_A8_BIT;
+               else
+                       wr_hdr_buf[0] = CY_SPI_WR_OP;
+
+               /* Header byte 1 */
+               wr_hdr_buf[1] = reg % 256;
+
+               xfer[0].len = CY_SPI_WR_HEADER_BYTES;
+
+               spi_message_add_tail(&xfer[0], &msg);
+
+               /* Data buffer */
+               if (buf) {
+                       xfer[1].tx_buf = buf;
+                       xfer[1].len = length;
+
+                       spi_message_add_tail(&xfer[1], &msg);
+               }
+               break;
+
+       case CY_SPI_RD_OP:
+               if (!buf) {
+                       dev_err(dev, "%s: No read buffer\n", __func__);
+                       rc = -EINVAL;
+                       goto cyttsp4_spi_xfer_exit;
+               }
+
+               if ((length + CY_SPI_RD_HEADER_BYTES) > CY_SPI_DATA_SIZE) {
+                       dev_vdbg(dev,
+                               "%s: length+%d=%d is greater than SPI max=%d\n",
+                               __func__, CY_SPI_RD_HEADER_BYTES,
+                               length + CY_SPI_RD_HEADER_BYTES,
+                               CY_SPI_DATA_SIZE);
+                       rc = -EINVAL;
+                       goto cyttsp4_spi_xfer_exit;
+               }
+
+               /* Header byte 0 */
+               wr_hdr_buf[0] = CY_SPI_RD_OP;
+
+               xfer[0].len = CY_SPI_RD_HEADER_BYTES;
+
+               spi_message_add_tail(&xfer[0], &msg);
+
+               /* Data buffer */
+               xfer[1].rx_buf = buf;
+               xfer[1].len = length;
+
+               spi_message_add_tail(&xfer[1], &msg);
+               break;
+
+       default:
+               dev_dbg(dev, "%s: bad op code=%d\n", __func__, op);
+               rc = -EINVAL;
+               goto cyttsp4_spi_xfer_exit;
+       }
+
+       rc = spi_sync(spi, &msg);
+       if (rc < 0) {
+               dev_vdbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
+                       __func__, rc, xfer[0].len, op);
+               /*
+                * do not return here since probably a bad ACK sequence
+                * let the following ACK check handle any errors and
+                * allow silent retries
+                */
+       }
+
+       if (rd_hdr_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK)
+               /* signal ACK error so silent retry */
+               rc = 1;
+       else
+               rc = 0;
+
+cyttsp4_spi_xfer_exit:
+       return rc;
+}
+
+static s32 cyttsp4_spi_read_block_data(struct spi_device *spi, u8 addr,
+                               size_t length, void *data)
+{
+       int rc = 0;
+       struct device *dev = &spi->dev;
+
+       dev_vdbg(dev, "%s: Enter\n", __func__);
+
+       /* Write address */
+       rc = cyttsp4_spi_xfer(CY_SPI_WR_OP, spi, addr, NULL, 0);
+       if (rc < 0) {
+               dev_err(dev, "%s: Fail write address r=%d\n", __func__, rc);
+               return rc;
+       }
+
+       /* Read data */
+       rc = cyttsp4_spi_xfer(CY_SPI_RD_OP, spi, addr, data, length);
+       if (rc < 0) {
+               dev_err(dev, "%s: Fail read r=%d\n", __func__, rc);
+               /*
+                * Do not print the above error if the data sync bytes
+                * were not found.
+                * This is a normal condition for the bootloader loader
+                * startup and need to retry until data sync bytes are found.
+                */
+       } else if (rc > 0)
+               /* Now signal fail; so retry can be done */
+               rc = -EIO;
+
+       return rc;
+}
+
+static s32 cyttsp4_spi_write_block_data(struct spi_device *spi, u8 addr,
+                               size_t length, const void *data)
+{
+       int rc = 0;
+       struct device *dev = &spi->dev;
+
+       dev_vdbg(dev, "%s: Enter\n", __func__);
+
+       rc = cyttsp4_spi_xfer(CY_SPI_WR_OP, spi, addr, (void *)data, length);
+       if (rc < 0) {
+               dev_err(dev, "%s: Fail write r=%d\n", __func__, rc);
+               /*
+                * Do not print the above error if the data sync bytes
+                * were not found.
+                * This is a normal condition for the bootloader loader
+                * startup and need to retry until data sync bytes are found.
+                */
+       } else if (rc > 0)
+               /* Now signal fail; so retry can be done */
+               rc = -EIO;
+
+       return rc;
+}
+
+static int cyttsp4_spi_write(struct device *dev, u8 addr,
+               const void *buf, int size)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       int rc;
+
+       rc = cyttsp4_spi_write_block_data(spi, addr, size, buf);
+
+       return rc;
+}
+
+static int cyttsp4_spi_read(struct device *dev, u8 addr,
+               void *buf, int size)
+{
+       struct spi_device *spi = to_spi_device(dev);
+       int rc;
+
+       rc = cyttsp4_spi_read_block_data(spi, addr, size, buf);
+
+       return rc;
+}
+
+static struct cyttsp4_bus_ops ops = {
+       .bustype = BUS_SPI,
+       .write = cyttsp4_spi_write,
+       .read = cyttsp4_spi_read,
+};
+
+static int __devinit cyttsp4_spi_probe(struct spi_device *spi)
+{
+       struct device *dev = &spi->dev;
+       int rc = 0;
+
+       dev_dbg(dev, "%s: Probing ...\n", __func__);
+
+       spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+       spi->mode = SPI_MODE_0;
+
+       rc = spi_setup(spi);
+       if (rc < 0) {
+               dev_err(dev, "%s: SPI setup error %d\n", __func__, rc);
+               return rc;
+       }
+
+       rc = cyttsp4_probe(dev, &ops, NULL);
+       if (rc) {
+               dev_err(dev, "%s: Error on probe %s\n", __func__,
+                       CYTTSP4_SPI_NAME);
+               goto probe_err;
+       }
+
+       dev_info(dev, "%s: Successful prob %s\n", __func__, CYTTSP4_SPI_NAME);
+
+       return 0;
+
+probe_err:
+       return rc;
+}
+
+static int __devexit cyttsp4_spi_remove(struct spi_device *spi)
+{
+       struct device *dev = &spi->dev;
+
+       dev_info(dev, "%s\n", __func__);
+       cyttsp4_remove(dev);
+       return 0;
+}
+
+static struct spi_driver cyttsp4_spi_driver = {
+       .driver = {
+               .name = CYTTSP4_SPI_NAME,
+               .bus = &spi_bus_type,
+               .owner = THIS_MODULE,
+               .pm = &cyttsp4_pm_ops,
+       },
+       .probe = cyttsp4_spi_probe,
+       .remove = __devexit_p(cyttsp4_spi_remove),
+};
+
+static int __init cyttsp4_spi_init(void)
+{
+       int err;
+
+       err = spi_register_driver(&cyttsp4_spi_driver);
+       pr_info("%s: Cypress TTSP SPI Touchscreen Driver (Built %s) returned %d\n",
+                __func__, CY_DRIVER_DATE, err);
+
+       return err;
+}
+module_init(cyttsp4_spi_init);
+
+static void __exit cyttsp4_spi_exit(void)
+{
+       spi_unregister_driver(&cyttsp4_spi_driver);
+       pr_info("%s: module exit\n", __func__);
+}
+module_exit(cyttsp4_spi_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp4_spi.h b/drivers/input/touchscreen/cyttsp4_spi.h
new file mode 100644
index 0000000..82dc065
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp4_spi.h
@@ -0,0 +1,34 @@
+/*
+ * cyttsp4_spi.h
+ * Cypress TrueTouch(TM) Standard Product V4 SPI Driver module.
+ * For use with Cypress Txx4xx parts.
+ * Supported parts include:
+ * TMA4XX
+ * TMA1036
+ *
+ * Copyright (C) 2012 Cypress Semiconductor
+ *
+ * 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_SPI_H
+#define _LINUX_CYTTSP4_SPI_H
+
+#define CYTTSP4_SPI_NAME "cyttsp4_spi_adapter"
+
+#endif /* _LINUX_CYTTSP4_SPI_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.

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

* Re: [PATCH v2 0/3] Input: cyttsp4 - driver for Cypress TMA4XX touchscreen devices
  2012-09-14 17:48 ` [PATCH v2 0/3] Input: cyttsp4 - " Ferruh Yigit
                     ` (2 preceding siblings ...)
  2012-09-14 17:48   ` [PATCH v2 3/3] Input: cyttsp4 - SPI " Ferruh Yigit
@ 2012-09-14 18:46   ` Henrik Rydberg
  2012-09-15 15:42     ` Javier Martinez Canillas
  2012-09-19 11:53     ` Ferruh Yigit
  3 siblings, 2 replies; 20+ messages in thread
From: Henrik Rydberg @ 2012-09-14 18:46 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Dmitry Torokhov, Kevin McNeely, Phil Warkentin,
	Javier Martinez Canillas, Shawn Landden, Ashish Jangam,
	Olivier Sobrie, linux-input, linux-kernel

Hi Ferruh,

> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
> Generation4 devices.
> 
> This is second version of driver, code re-structured to match with
> existing Generation3 driver code.

To integrate with the existing gen3 driver is clearly not the same as
creating a parallel set of files, which obviously share a lot of code
with the gen3 code.

For instance, the i2c transfer layer seems more or less identical to
the existing one; just look at cyttsp_i2c_write_block_data() versus
cyttsp4_i2c_write_block_data(). If different generations of device
data cannot even be transported through the same _generic_ interface,
something is clearly not right.

You can't seriously expect anyone to want to maintain one set of files
for every new version. Please make sure to build on what is already
there.

Thanks.
Henrik

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

* Re: [PATCH v2 0/3] Input: cyttsp4 - driver for Cypress TMA4XX touchscreen devices
  2012-09-14 18:46   ` [PATCH v2 0/3] Input: cyttsp4 - " Henrik Rydberg
@ 2012-09-15 15:42     ` Javier Martinez Canillas
  2012-09-19 12:02       ` Ferruh Yigit
  2012-09-19 11:53     ` Ferruh Yigit
  1 sibling, 1 reply; 20+ messages in thread
From: Javier Martinez Canillas @ 2012-09-15 15:42 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Henrik Rydberg, Dmitry Torokhov, Kevin McNeely, Phil Warkentin,
	Shawn Landden, Ashish Jangam, Olivier Sobrie, linux-input,
	linux-kernel

On Fri, Sep 14, 2012 at 8:46 PM, Henrik Rydberg <rydberg@euromail.se> wrote:
> Hi Ferruh,
>
>> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
>> Generation4 devices.
>>
>> This is second version of driver, code re-structured to match with
>> existing Generation3 driver code.
>
> To integrate with the existing gen3 driver is clearly not the same as
> creating a parallel set of files, which obviously share a lot of code
> with the gen3 code.
>
> For instance, the i2c transfer layer seems more or less identical to
> the existing one; just look at cyttsp_i2c_write_block_data() versus
> cyttsp4_i2c_write_block_data(). If different generations of device
> data cannot even be transported through the same _generic_ interface,
> something is clearly not right.
>
> You can't seriously expect anyone to want to maintain one set of files
> for every new version. Please make sure to build on what is already
> there.
>
> Thanks.
> Henrik
> --

Hi Ferruh,

Thanks a lot for taking the time to try to modify your driver to fit
wit the existent Gen3 driver but I agree with Henrik that it seems
that at least the transport code from the Gen3 driver could be reused.

As he said the i2c layer code is almost identical and even when there
are some differences on the spi transfer functions I think that the
current Gen3 spi transport code could be refactor to support it. These
differences seems to be only minor as far as I understood by looking
at cyttsp_spi_xfer() and cyttsp4_spi_xfer() functions.

Also it seems that you forgot to post your 1/3 patch from your series
that adds the Gen4 core driver. At least I couldn't find neither on my
mailbox nor on linux-input archives the patch:

[PATCH v2 1/3] Input: cyttsp4 - core driver for Cypress TMA4XX
touchscreen devices

So I couldn't take a look to try to figure out if the current Gen3
core driver can be extended to support the new Gen4 family or if new
core driver file is actually needed.

Best regards,
Javier

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

* Re: [PATCH v2 0/3] Input: cyttsp4 - driver for Cypress TMA4XX touchscreen devices
  2012-09-14 18:46   ` [PATCH v2 0/3] Input: cyttsp4 - " Henrik Rydberg
  2012-09-15 15:42     ` Javier Martinez Canillas
@ 2012-09-19 11:53     ` Ferruh Yigit
  1 sibling, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-09-19 11:53 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Dmitry Torokhov, Kevin McNeely, Phil Warkentin,
	Javier Martinez Canillas, Shawn Landden, Ashish Jangam,
	Olivier Sobrie, linux-input, linux-kernel

On 09/14/2012 09:46 PM, Henrik Rydberg wrote:
> Hi Ferruh,
>
>> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
>> Generation4 devices.
>>
>> This is second version of driver, code re-structured to match with
>> existing Generation3 driver code.
> To integrate with the existing gen3 driver is clearly not the same as
> creating a parallel set of files, which obviously share a lot of code
> with the gen3 code.
>
> For instance, the i2c transfer layer seems more or less identical to
> the existing one; just look at cyttsp_i2c_write_block_data() versus
> cyttsp4_i2c_write_block_data(). If different generations of device
> data cannot even be transported through the same _generic_ interface,
> something is clearly not right.
>
> You can't seriously expect anyone to want to maintain one set of files
> for every new version. Please make sure to build on what is already
> there.
>
> Thanks.
> Henrik
Hi Henrik,

Thank you for your review.

We will work on merging transfer layers of drivers and submit a new set
of patches.


Thanks and Regards,
ferruh

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.

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

* Re: [PATCH v2 0/3] Input: cyttsp4 - driver for Cypress TMA4XX touchscreen devices
  2012-09-15 15:42     ` Javier Martinez Canillas
@ 2012-09-19 12:02       ` Ferruh Yigit
  0 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2012-09-19 12:02 UTC (permalink / raw)
  To: Javier Martinez Canillas
  Cc: Henrik Rydberg, Dmitry Torokhov, Kevin McNeely, Phil Warkentin,
	Shawn Landden, Ashish Jangam, Olivier Sobrie, linux-input,
	linux-kernel

On 09/15/2012 06:42 PM, Javier Martinez Canillas wrote:
> On Fri, Sep 14, 2012 at 8:46 PM, Henrik Rydberg <rydberg@euromail.se> wrote:
>> Hi Ferruh,
>>
>>> This driver is for Cypress TrueTouch(tm) Standard Product controllers,
>>> Generation4 devices.
>>>
>>> This is second version of driver, code re-structured to match with
>>> existing Generation3 driver code.
>> To integrate with the existing gen3 driver is clearly not the same as
>> creating a parallel set of files, which obviously share a lot of code
>> with the gen3 code.
>>
>> For instance, the i2c transfer layer seems more or less identical to
>> the existing one; just look at cyttsp_i2c_write_block_data() versus
>> cyttsp4_i2c_write_block_data(). If different generations of device
>> data cannot even be transported through the same _generic_ interface,
>> something is clearly not right.
>>
>> You can't seriously expect anyone to want to maintain one set of files
>> for every new version. Please make sure to build on what is already
>> there.
>>
>> Thanks.
>> Henrik
>> --
> Hi Ferruh,
>
> Thanks a lot for taking the time to try to modify your driver to fit
> wit the existent Gen3 driver but I agree with Henrik that it seems
> that at least the transport code from the Gen3 driver could be reused.
>
> As he said the i2c layer code is almost identical and even when there
> are some differences on the spi transfer functions I think that the
> current Gen3 spi transport code could be refactor to support it. These
> differences seems to be only minor as far as I understood by looking
> at cyttsp_spi_xfer() and cyttsp4_spi_xfer() functions.
>
> Also it seems that you forgot to post your 1/3 patch from your series
> that adds the Gen4 core driver. At least I couldn't find neither on my
> mailbox nor on linux-input archives the patch:
>
> [PATCH v2 1/3] Input: cyttsp4 - core driver for Cypress TMA4XX
> touchscreen devices
>
> So I couldn't take a look to try to figure out if the current Gen3
> core driver can be extended to support the new Gen4 family or if new
> core driver file is actually needed.
>
> Best regards,
> Javier
Hi Javier,

As you mentioned there is a difference on SPI because of protocol
difference.
But agreed that we can merge transport layer with existing driver.

We will at least start on merging transport layer of Gen4 driver with
existing driver.

Thanks & Best Regards,
ferruh


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.

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

end of thread, other threads:[~2012-09-19 12:02 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <Ferruh Yigit <fery@cypress.com>
2012-08-07 13:09 ` [PATCH 1/4] Input: cyttsp4 - bus driver for Cypress TMA4XX touchscreen devices Ferruh Yigit
2012-08-07 20:05   ` Javier Martinez Canillas
2012-08-08  5:53     ` Ferruh Yigit
2012-08-24 13:06   ` Ferruh Yigit
2012-08-24 17:34     ` Henrik Rydberg
     [not found]       ` <503B099A.6010807@cypress.com>
2012-09-12 13:16         ` Ferruh Yigit
2012-09-12 13:34           ` Henrik Rydberg
2012-08-07 13:09 ` [PATCH 2/4] Input: cyttsp4 - core " Ferruh Yigit
2012-08-24 14:21   ` Michal Marek
2012-08-27  5:49     ` Ferruh Yigit
2012-08-07 13:10 ` [PATCH 3/4] Input: cyttsp4 - MultiTouch " Ferruh Yigit
2012-08-07 13:10 ` [PATCH 4/4] Input: cyttsp4 - I2C " Ferruh Yigit
2012-09-14 17:48 ` [PATCH v2 0/3] Input: cyttsp4 - " Ferruh Yigit
2012-09-14 17:48   ` [PATCH v2 1/3] Input: cyttsp4 - core " Ferruh Yigit
2012-09-14 17:48   ` [PATCH v2 2/3] Input: cyttsp4 - I2C " Ferruh Yigit
2012-09-14 17:48   ` [PATCH v2 3/3] Input: cyttsp4 - SPI " Ferruh Yigit
2012-09-14 18:46   ` [PATCH v2 0/3] Input: cyttsp4 - " Henrik Rydberg
2012-09-15 15:42     ` Javier Martinez Canillas
2012-09-19 12:02       ` Ferruh Yigit
2012-09-19 11:53     ` Ferruh Yigit

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