Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 20 Feb 2013 19:00:43 +0000 (11:00 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 20 Feb 2013 19:00:43 +0000 (11:00 -0800)
Pull input updates from Dmitry Torokhov:
 "Two new touchpad drivers - Cypress APA I2C Trackpad and Cypress PS/2
  touchpad and a big update to ALPS driver from Kevin Cernekee that adds
  support for "Rushmore" touchpads and paves way for adding support for
  "Dolphin" touchpads.

  There is also a new input driver for Goldfish emulator and also
  Android keyreset driver was folded into SysRq code.

  A few more drivers were updated with device tree bindings and others
  got some small cleanups and fixes."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (55 commits)
  Input: cyttsp-spi - remove duplicate MODULE_ALIAS()
  Input: tsc2005 - add MODULE_ALIAS
  Input: tegra-kbc - require CONFIG_OF, remove platform data
  Input: synaptics - initialize pointer emulation usage
  Input: MT - do not apply filtering on emulated events
  Input: bma150 - make some defines public and fix some comments
  Input: bma150 - fix checking pm_runtime_get_sync() return value
  Input: ALPS - enable trackstick on Rushmore touchpads
  Input: ALPS - add support for "Rushmore" touchpads
  Input: ALPS - make the V3 packet field decoder "pluggable"
  Input: ALPS - move pixel and bitmap info into alps_data struct
  Input: ALPS - fix command mode check
  Input: ALPS - rework detection of Pinnacle AGx touchpads
  Input: ALPS - move {addr,nibble}_command settings into alps_set_defaults()
  Input: ALPS - use function pointers for different protocol handlers
  Input: ALPS - rework detection sequence
  Input: ALPS - introduce helper function for repeated commands
  Input: ALPS - move alps_get_model() down below hw_init code
  Input: ALPS - copy "model" info into alps_data struct
  Input: ALPS - document the alps.h data structures
  ...

41 files changed:
Documentation/devicetree/bindings/input/imx-keypad.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt
drivers/input/Kconfig
drivers/input/input-mt.c
drivers/input/joystick/walkera0701.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/goldfish_events.c [new file with mode: 0644]
drivers/input/keyboard/imx_keypad.c
drivers/input/keyboard/qt2160.c
drivers/input/keyboard/tegra-kbc.c
drivers/input/misc/adxl34x.c
drivers/input/misc/bma150.c
drivers/input/misc/twl4030-vibra.c
drivers/input/misc/twl6040-vibra.c
drivers/input/misc/wm831x-on.c
drivers/input/mouse/Kconfig
drivers/input/mouse/Makefile
drivers/input/mouse/alps.c
drivers/input/mouse/alps.h
drivers/input/mouse/cyapa.c [new file with mode: 0644]
drivers/input/mouse/cypress_ps2.c [new file with mode: 0644]
drivers/input/mouse/cypress_ps2.h [new file with mode: 0644]
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/synaptics.c
drivers/input/serio/Kconfig
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/cyttsp_spi.c
drivers/input/touchscreen/mms114.c
drivers/input/touchscreen/stmpe-ts.c
drivers/input/touchscreen/tsc2005.c
drivers/input/touchscreen/wm831x-ts.c
drivers/tty/sysrq.c
include/linux/bma150.h
include/linux/input/adxl34x.h
include/linux/input/tegra_kbc.h [deleted file]
include/linux/libps2.h

diff --git a/Documentation/devicetree/bindings/input/imx-keypad.txt b/Documentation/devicetree/bindings/input/imx-keypad.txt
new file mode 100644 (file)
index 0000000..2ebaf7d
--- /dev/null
@@ -0,0 +1,53 @@
+* Freescale i.MX Keypad Port(KPP) device tree bindings
+
+The KPP is designed to interface with a keypad matrix with 2-point contact
+or 3-point contact keys. The KPP is designed to simplify the software task
+of scanning a keypad matrix. The KPP is capable of detecting, debouncing,
+and decoding one or multiple keys pressed simultaneously on a keypad.
+
+Required SoC Specific Properties:
+- compatible: Should be "fsl,<soc>-kpp".
+
+- reg: Physical base address of the KPP and length of memory mapped
+  region.
+
+- interrupts: The KPP interrupt number to the CPU(s).
+
+- clocks: The clock provided by the SoC to the KPP. Some SoCs use dummy
+clock(The clock for the KPP is provided by the SoCs automatically).
+
+Required Board Specific Properties:
+- pinctrl-names: The definition can be found at
+pinctrl/pinctrl-bindings.txt.
+
+- pinctrl-0: The definition can be found at
+pinctrl/pinctrl-bindings.txt.
+
+- linux,keymap: The definition can be found at
+bindings/input/matrix-keymap.txt.
+
+Example:
+kpp: kpp@73f94000 {
+       compatible = "fsl,imx51-kpp", "fsl,imx21-kpp";
+       reg = <0x73f94000 0x4000>;
+       interrupts = <60>;
+       clocks = <&clks 0>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_kpp_1>;
+       linux,keymap = <0x00000067      /* KEY_UP */
+                       0x0001006c      /* KEY_DOWN */
+                       0x00020072      /* KEY_VOLUMEDOWN */
+                       0x00030066      /* KEY_HOME */
+                       0x0100006a      /* KEY_RIGHT */
+                       0x01010069      /* KEY_LEFT */
+                       0x0102001c      /* KEY_ENTER */
+                       0x01030073      /* KEY_VOLUMEUP */
+                       0x02000040      /* KEY_F6 */
+                       0x02010042      /* KEY_F8 */
+                       0x02020043      /* KEY_F9 */
+                       0x02030044      /* KEY_F10 */
+                       0x0300003b      /* KEY_F1 */
+                       0x0301003c      /* KEY_F2 */
+                       0x0302003d      /* KEY_F3 */
+                       0x03030074>;    /* KEY_POWER */
+};
index 72683be6de35ea18f17f6214e30087003c87c587..2995fae7ee474ce81b1a4fdeca3e195d77154ded 100644 (file)
@@ -1,7 +1,18 @@
 * Tegra keyboard controller
+The key controller has maximum 24 pins to make matrix keypad. Any pin
+can be configured as row or column. The maximum column pin can be 8
+and maximum row pins can be 16 for Tegra20/Tegra30.
 
 Required properties:
 - compatible: "nvidia,tegra20-kbc"
+- reg: Register base address of KBC.
+- interrupts: Interrupt number for the KBC.
+- nvidia,kbc-row-pins: The KBC pins which are configured as row. This is an
+  array of pin numbers which is used as rows.
+- nvidia,kbc-col-pins: The KBC pins which are configured as column. This is an
+  array of pin numbers which is used as column.
+- linux,keymap: The keymap for keys as described in the binding document
+  devicetree/bindings/input/matrix-keymap.txt.
 
 Optional properties, in addition to those specified by the shared
 matrix-keyboard bindings:
@@ -19,5 +30,16 @@ Example:
 keyboard: keyboard {
        compatible = "nvidia,tegra20-kbc";
        reg = <0x7000e200 0x100>;
+       interrupts = <0 85 0x04>;
        nvidia,ghost-filter;
+       nvidia,debounce-delay-ms = <640>;
+       nvidia,kbc-row-pins = <0 1 2>;    /* pin 0, 1, 2 as rows */
+       nvidia,kbc-col-pins = <11 12 13>; /* pin 11, 12, 13 as columns */
+       linux,keymap = <0x00000074
+                       0x00010067
+                       0x00020066
+                       0x01010068
+                       0x02000069
+                       0x02010070
+                       0x02020071>;
 };
index 55f7e57d4e4279ebe089dd687f08d9457bc65cca..38b523a1ece0634e7855b5eae62b94b6fe7fd160 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 menu "Input device support"
-       depends on !S390 && !UML
+       depends on !UML
 
 config INPUT
        tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT
index 47a6009dbf43f04ba6dcd883513127857591a70f..71db1930573f45a4226fcf2ed84ab0b19a4c6aeb 100644 (file)
@@ -18,6 +18,7 @@ static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
 {
        if (dev->absinfo && test_bit(src, dev->absbit)) {
                dev->absinfo[dst] = dev->absinfo[src];
+               dev->absinfo[dst].fuzz = 0;
                dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
        }
 }
index f8f892b076e8e229e45762fd39c5d8bea74f73c5..b76ac580703ce5dc9ef97fac6620adc47ea44273 100644 (file)
@@ -12,7 +12,7 @@
  * the Free Software Foundation.
 */
 
-/* #define WK0701_DEBUG */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #define RESERVE 20000
 #define SYNC_PULSE 1306000
@@ -67,6 +67,7 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
 {
        int i;
        int val1, val2, val3, val4, val5, val6, val7, val8;
+       int magic, magic_bit;
        int crc1, crc2;
 
        for (crc1 = crc2 = i = 0; i < 10; i++) {
@@ -102,17 +103,12 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w)
        val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
        val8 *= (w->buf[18] & 2) - 1;   /*sign */
 
-#ifdef WK0701_DEBUG
-       {
-               int magic, magic_bit;
-               magic = (w->buf[21] << 4) | w->buf[22];
-               magic_bit = (w->buf[24] & 8) >> 3;
-               printk(KERN_DEBUG
-                      "walkera0701: %4d %4d %4d %4d  %4d %4d %4d %4d (magic %2x %d)\n",
-                      val1, val2, val3, val4, val5, val6, val7, val8, magic,
-                      magic_bit);
-       }
-#endif
+       magic = (w->buf[21] << 4) | w->buf[22];
+       magic_bit = (w->buf[24] & 8) >> 3;
+       pr_debug("%4d %4d %4d %4d  %4d %4d %4d %4d (magic %2x %d)\n",
+                val1, val2, val3, val4, val5, val6, val7, val8,
+                magic, magic_bit);
+
        input_report_abs(w->input_dev, ABS_X, val2);
        input_report_abs(w->input_dev, ABS_Y, val1);
        input_report_abs(w->input_dev, ABS_Z, val6);
@@ -187,6 +183,9 @@ static int walkera0701_open(struct input_dev *dev)
 {
        struct walkera_dev *w = input_get_drvdata(dev);
 
+       if (parport_claim(w->pardevice))
+               return -EBUSY;
+
        parport_enable_irq(w->parport);
        return 0;
 }
@@ -197,40 +196,51 @@ static void walkera0701_close(struct input_dev *dev)
 
        parport_disable_irq(w->parport);
        hrtimer_cancel(&w->timer);
+
+       parport_release(w->pardevice);
 }
 
 static int walkera0701_connect(struct walkera_dev *w, int parport)
 {
-       int err = -ENODEV;
+       int error;
 
        w->parport = parport_find_number(parport);
-       if (w->parport == NULL)
+       if (!w->parport) {
+               pr_err("parport %d does not exist\n", parport);
                return -ENODEV;
+       }
 
        if (w->parport->irq == -1) {
-               printk(KERN_ERR "walkera0701: parport without interrupt\n");
-               goto init_err;
+               pr_err("parport %d does not have interrupt assigned\n",
+                       parport);
+               error = -EINVAL;
+               goto err_put_parport;
        }
 
-       err = -EBUSY;
        w->pardevice = parport_register_device(w->parport, "walkera0701",
                                    NULL, NULL, walkera0701_irq_handler,
                                    PARPORT_DEV_EXCL, w);
-       if (!w->pardevice)
-               goto init_err;
-
-       if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT))
-               goto init_err1;
+       if (!w->pardevice) {
+               pr_err("failed to register parport device\n");
+               error = -EIO;
+               goto err_put_parport;
+       }
 
-       if (parport_claim(w->pardevice))
-               goto init_err1;
+       if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
+               pr_err("failed to negotiate parport mode\n");
+               error = -EIO;
+               goto err_unregister_device;
+       }
 
        hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        w->timer.function = timer_handler;
 
        w->input_dev = input_allocate_device();
-       if (!w->input_dev)
-               goto init_err2;
+       if (!w->input_dev) {
+               pr_err("failed to allocate input device\n");
+               error = -ENOMEM;
+               goto err_unregister_device;
+       }
 
        input_set_drvdata(w->input_dev, w);
        w->input_dev->name = "Walkera WK-0701 TX";
@@ -241,6 +251,7 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
        w->input_dev->id.vendor = 0x0001;
        w->input_dev->id.product = 0x0001;
        w->input_dev->id.version = 0x0100;
+       w->input_dev->dev.parent = w->parport->dev;
        w->input_dev->open = walkera0701_open;
        w->input_dev->close = walkera0701_close;
 
@@ -254,27 +265,26 @@ static int walkera0701_connect(struct walkera_dev *w, int parport)
        input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
        input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
 
-       err = input_register_device(w->input_dev);
-       if (err)
-               goto init_err3;
+       error = input_register_device(w->input_dev);
+       if (error) {
+               pr_err("failed to register input device\n");
+               goto err_free_input_dev;
+       }
 
        return 0;
 
- init_err3:
+err_free_input_dev:
        input_free_device(w->input_dev);
- init_err2:
-       parport_release(w->pardevice);
- init_err1:
+err_unregister_device:
        parport_unregister_device(w->pardevice);
- init_err:
+err_put_parport:
        parport_put_port(w->parport);
-       return err;
+       return error;
 }
 
 static void walkera0701_disconnect(struct walkera_dev *w)
 {
        input_unregister_device(w->input_dev);
-       parport_release(w->pardevice);
        parport_unregister_device(w->pardevice);
        parport_put_port(w->parport);
 }
index 5a240c60342d8a8e7c3381a576103c2949bdce1d..ac050066700086d2477d1e56f459bc6bc6f313d2 100644 (file)
@@ -224,7 +224,7 @@ config KEYBOARD_TCA6416
 
 config KEYBOARD_TCA8418
        tristate "TCA8418 Keypad Support"
-       depends on I2C
+       depends on I2C && GENERIC_HARDIRQS
        select INPUT_MATRIXKMAP
        help
          This driver implements basic keypad functionality
@@ -303,7 +303,7 @@ config KEYBOARD_HP7XX
 
 config KEYBOARD_LM8323
        tristate "LM8323 keypad chip"
-       depends on I2C
+       depends on I2C && GENERIC_HARDIRQS
        depends on LEDS_CLASS
        help
          If you say yes here you get support for the National Semiconductor
@@ -420,7 +420,7 @@ config KEYBOARD_NOMADIK
 
 config KEYBOARD_TEGRA
        tristate "NVIDIA Tegra internal matrix keyboard controller support"
-       depends on ARCH_TEGRA
+       depends on ARCH_TEGRA && OF
        select INPUT_MATRIXKMAP
        help
          Say Y here if you want to use a matrix keyboard connected directly
@@ -479,6 +479,16 @@ config KEYBOARD_SAMSUNG
          To compile this driver as a module, choose M here: the
          module will be called samsung-keypad.
 
+config KEYBOARD_GOLDFISH_EVENTS
+       depends on GOLDFISH
+       tristate "Generic Input Event device for Goldfish"
+       help
+         Say Y here to get an input event device for the Goldfish virtual
+         device emulator.
+
+         To compile this driver as a module, choose M here: the
+         module will be called goldfish-events.
+
 config KEYBOARD_STOWAWAY
        tristate "Stowaway keyboard"
        select SERIO
index 44e76002f54bf3c4d7250c3ab345b70d89195ad4..49b16453d00ef84184d2beddbee95d7c3a3e33ea 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD)          += atkbd.o
 obj-$(CONFIG_KEYBOARD_BFIN)            += bf54x-keys.o
 obj-$(CONFIG_KEYBOARD_DAVINCI)         += davinci_keyscan.o
 obj-$(CONFIG_KEYBOARD_EP93XX)          += ep93xx_keypad.o
+obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
 obj-$(CONFIG_KEYBOARD_GPIO)            += gpio_keys.o
 obj-$(CONFIG_KEYBOARD_GPIO_POLLED)     += gpio_keys_polled.o
 obj-$(CONFIG_KEYBOARD_TCA6416)         += tca6416-keypad.o
index add5ffd9fe26e675daf2548928175cdf2b0bdb73..2626773ff29b956de97d5c62d383e36e13ee4868 100644 (file)
@@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd)
        serio_continue_rx(atkbd->ps2dev.serio);
 }
 
+static int atkbd_activate(struct atkbd *atkbd)
+{
+       struct ps2dev *ps2dev = &atkbd->ps2dev;
+
+/*
+ * Enable the keyboard to receive keystrokes.
+ */
+
+       if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
+               dev_err(&ps2dev->serio->dev,
+                       "Failed to enable keyboard on %s\n",
+                       ps2dev->serio->phys);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * atkbd_deactivate() resets and disables the keyboard from sending
+ * keystrokes.
+ */
+
+static void atkbd_deactivate(struct atkbd *atkbd)
+{
+       struct ps2dev *ps2dev = &atkbd->ps2dev;
+
+       if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS))
+               dev_err(&ps2dev->serio->dev,
+                       "Failed to deactivate keyboard on %s\n",
+                       ps2dev->serio->phys);
+}
+
 /*
  * atkbd_probe() probes for an AT keyboard on a serio port.
  */
@@ -726,11 +759,17 @@ static int atkbd_probe(struct atkbd *atkbd)
 
        if (atkbd->id == 0xaca1 && atkbd->translated) {
                dev_err(&ps2dev->serio->dev,
-                       "NCD terminal keyboards are only supported on non-translating controlelrs. "
+                       "NCD terminal keyboards are only supported on non-translating controllers. "
                        "Use i8042.direct=1 to disable translation.\n");
                return -1;
        }
 
+/*
+ * Make sure nothing is coming from the keyboard and disturbs our
+ * internal state.
+ */
+       atkbd_deactivate(atkbd);
+
        return 0;
 }
 
@@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd)
        return 0;
 }
 
-static int atkbd_activate(struct atkbd *atkbd)
-{
-       struct ps2dev *ps2dev = &atkbd->ps2dev;
-
-/*
- * Enable the keyboard to receive keystrokes.
- */
-
-       if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
-               dev_err(&ps2dev->serio->dev,
-                       "Failed to enable keyboard on %s\n",
-                       ps2dev->serio->phys);
-               return -1;
-       }
-
-       return 0;
-}
-
 /*
  * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
  * reboot.
@@ -1150,7 +1171,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
 
                atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
                atkbd_reset_state(atkbd);
-               atkbd_activate(atkbd);
 
        } else {
                atkbd->set = 2;
@@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
                goto fail3;
 
        atkbd_enable(atkbd);
+       if (serio->write)
+               atkbd_activate(atkbd);
 
        err = input_register_device(atkbd->dev);
        if (err)
@@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio)
                if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
                        goto out;
 
-               atkbd_activate(atkbd);
-
                /*
                 * Restore LED state and repeat rate. While input core
                 * will do this for us at resume time reconnect may happen
@@ -1223,7 +1243,17 @@ static int atkbd_reconnect(struct serio *serio)
 
        }
 
+       /*
+        * Reset our state machine in case reconnect happened in the middle
+        * of multi-byte scancode.
+        */
+       atkbd->xl_bit = 0;
+       atkbd->emul = 0;
+
        atkbd_enable(atkbd);
+       if (atkbd->write)
+               atkbd_activate(atkbd);
+
        retval = 0;
 
  out:
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
new file mode 100644 (file)
index 0000000..9f60a2e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+enum {
+       REG_READ        = 0x00,
+       REG_SET_PAGE    = 0x00,
+       REG_LEN         = 0x04,
+       REG_DATA        = 0x08,
+
+       PAGE_NAME       = 0x00000,
+       PAGE_EVBITS     = 0x10000,
+       PAGE_ABSDATA    = 0x20000 | EV_ABS,
+};
+
+struct event_dev {
+       struct input_dev *input;
+       int irq;
+       void __iomem *addr;
+       char name[0];
+};
+
+static irqreturn_t events_interrupt(int irq, void *dev_id)
+{
+       struct event_dev *edev = dev_id;
+       unsigned type, code, value;
+
+       type = __raw_readl(edev->addr + REG_READ);
+       code = __raw_readl(edev->addr + REG_READ);
+       value = __raw_readl(edev->addr + REG_READ);
+
+       input_event(edev->input, type, code, value);
+       input_sync(edev->input);
+       return IRQ_HANDLED;
+}
+
+static void events_import_bits(struct event_dev *edev,
+                       unsigned long bits[], unsigned type, size_t count)
+{
+       void __iomem *addr = edev->addr;
+       int i, j;
+       size_t size;
+       uint8_t val;
+
+       __raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
+
+       size = __raw_readl(addr + REG_LEN) * 8;
+       if (size < count)
+               count = size;
+
+       addr += REG_DATA;
+       for (i = 0; i < count; i += 8) {
+               val = __raw_readb(addr++);
+               for (j = 0; j < 8; j++)
+                       if (val & 1 << j)
+                               set_bit(i + j, bits);
+       }
+}
+
+static void events_import_abs_params(struct event_dev *edev)
+{
+       struct input_dev *input_dev = edev->input;
+       void __iomem *addr = edev->addr;
+       u32 val[4];
+       int count;
+       int i, j;
+
+       __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
+
+       count = __raw_readl(addr + REG_LEN) / sizeof(val);
+       if (count > ABS_MAX)
+               count = ABS_MAX;
+
+       for (i = 0; i < count; i++) {
+               if (!test_bit(i, input_dev->absbit))
+                       continue;
+
+               for (j = 0; j < ARRAY_SIZE(val); j++) {
+                       int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32);
+                       val[j] = __raw_readl(edev->addr + REG_DATA + offset);
+               }
+
+               input_set_abs_params(input_dev, i,
+                                    val[0], val[1], val[2], val[3]);
+       }
+}
+
+static int events_probe(struct platform_device *pdev)
+{
+       struct input_dev *input_dev;
+       struct event_dev *edev;
+       struct resource *res;
+       unsigned keymapnamelen;
+       void __iomem *addr;
+       int irq;
+       int i;
+       int error;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return -EINVAL;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       addr = devm_ioremap(&pdev->dev, res->start, 4096);
+       if (!addr)
+               return -ENOMEM;
+
+       __raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
+       keymapnamelen = __raw_readl(addr + REG_LEN);
+
+       edev = devm_kzalloc(&pdev->dev,
+                           sizeof(struct event_dev) + keymapnamelen + 1,
+                           GFP_KERNEL);
+       if (!edev)
+               return -ENOMEM;
+
+       input_dev = devm_input_allocate_device(&pdev->dev);
+       if (!input_dev)
+               return -ENOMEM;
+
+       edev->input = input_dev;
+       edev->addr = addr;
+       edev->irq = irq;
+
+       for (i = 0; i < keymapnamelen; i++)
+               edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
+
+       pr_debug("events_probe() keymap=%s\n", edev->name);
+
+       input_dev->name = edev->name;
+       input_dev->id.bustype = BUS_HOST;
+
+       events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
+       events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
+       events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
+       events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
+       events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
+       events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
+       events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
+       events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
+       events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
+
+       events_import_abs_params(edev);
+
+       error = devm_request_irq(&pdev->dev, edev->irq, events_interrupt, 0,
+                                "goldfish-events-keypad", edev);
+       if (error)
+               return error;
+
+       error = input_register_device(input_dev);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+static struct platform_driver events_driver = {
+       .probe  = events_probe,
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "goldfish_events",
+       },
+};
+
+module_platform_driver(events_driver);
+
+MODULE_AUTHOR("Brian Swetland");
+MODULE_DESCRIPTION("Goldfish Event Device");
+MODULE_LICENSE("GPL");
index 6d150e3e1f5594dac7726ea25ec51dc0a61f8137..98f9113251d2bf31bd8763a537c8795306040455 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
@@ -414,15 +415,23 @@ open_err:
        return -EIO;
 }
 
+#ifdef CONFIG_OF
+static struct of_device_id imx_keypad_of_match[] = {
+       { .compatible = "fsl,imx21-kpp", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_keypad_of_match);
+#endif
+
 static int imx_keypad_probe(struct platform_device *pdev)
 {
        const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data;
        struct imx_keypad *keypad;
        struct input_dev *input_dev;
        struct resource *res;
-       int irq, error, i;
+       int irq, error, i, row, col;
 
-       if (keymap_data == NULL) {
+       if (!keymap_data && !pdev->dev.of_node) {
                dev_err(&pdev->dev, "no keymap defined\n");
                return -EINVAL;
        }
@@ -480,22 +489,6 @@ static int imx_keypad_probe(struct platform_device *pdev)
                goto failed_unmap;
        }
 
-       /* Search for rows and cols enabled */
-       for (i = 0; i < keymap_data->keymap_size; i++) {
-               keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]);
-               keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]);
-       }
-
-       if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) ||
-           keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) {
-               dev_err(&pdev->dev,
-                       "invalid key data (too many rows or colums)\n");
-               error = -EINVAL;
-               goto failed_clock_put;
-       }
-       dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
-       dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
-
        /* Init the Input device */
        input_dev->name = pdev->name;
        input_dev->id.bustype = BUS_HOST;
@@ -512,6 +505,19 @@ static int imx_keypad_probe(struct platform_device *pdev)
                goto failed_clock_put;
        }
 
+       /* Search for rows and cols enabled */
+       for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) {
+               for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) {
+                       i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
+                       if (keypad->keycodes[i] != KEY_RESERVED) {
+                               keypad->rows_en_mask |= 1 << row;
+                               keypad->cols_en_mask |= 1 << col;
+                       }
+               }
+       }
+       dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask);
+       dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask);
+
        __set_bit(EV_REP, input_dev->evbit);
        input_set_capability(input_dev, EV_MSC, MSC_SCAN);
        input_set_drvdata(input_dev, keypad);
@@ -631,6 +637,7 @@ static struct platform_driver imx_keypad_driver = {
                .name   = "imx-keypad",
                .owner  = THIS_MODULE,
                .pm     = &imx_kbd_pm_ops,
+               .of_match_table = of_match_ptr(imx_keypad_of_match),
        },
        .probe          = imx_keypad_probe,
        .remove         = imx_keypad_remove,
index 3dc2b0f27b0c48386cb13bd367df67d5b9cbe214..1c0ddad0a1ccdeb6444c80ff3443f7f3d40c079b 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/leds.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/jiffies.h>
 #define QT2160_CMD_GPIOS      6
 #define QT2160_CMD_SUBVER     7
 #define QT2160_CMD_CALIBRATE  10
+#define QT2160_CMD_DRIVE_X    70
+#define QT2160_CMD_PWMEN_X    74
+#define QT2160_CMD_PWM_DUTY   76
+
+#define QT2160_NUM_LEDS_X      8
 
 #define QT2160_CYCLE_INTERVAL  (2*HZ)
 
@@ -49,6 +55,17 @@ static unsigned char qt2160_key2code[] = {
        KEY_C, KEY_D, KEY_E, KEY_F,
 };
 
+#ifdef CONFIG_LEDS_CLASS
+struct qt2160_led {
+       struct qt2160_data *qt2160;
+       struct led_classdev cdev;
+       struct work_struct work;
+       char name[32];
+       int id;
+       enum led_brightness new_brightness;
+};
+#endif
+
 struct qt2160_data {
        struct i2c_client *client;
        struct input_dev *input;
@@ -56,8 +73,61 @@ struct qt2160_data {
        spinlock_t lock;        /* Protects canceling/rescheduling of dwork */
        unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
        u16 key_matrix;
+#ifdef CONFIG_LEDS_CLASS
+       struct qt2160_led leds[QT2160_NUM_LEDS_X];
+       struct mutex led_lock;
+#endif
 };
 
+static int qt2160_read(struct i2c_client *client, u8 reg);
+static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
+
+#ifdef CONFIG_LEDS_CLASS
+
+static void qt2160_led_work(struct work_struct *work)
+{
+       struct qt2160_led *led = container_of(work, struct qt2160_led, work);
+       struct qt2160_data *qt2160 = led->qt2160;
+       struct i2c_client *client = qt2160->client;
+       int value = led->new_brightness;
+       u32 drive, pwmen;
+
+       mutex_lock(&qt2160->led_lock);
+
+       drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
+       pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
+       if (value != LED_OFF) {
+               drive |= (1 << led->id);
+               pwmen |= (1 << led->id);
+
+       } else {
+               drive &= ~(1 << led->id);
+               pwmen &= ~(1 << led->id);
+       }
+       qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
+       qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
+
+       /*
+        * Changing this register will change the brightness
+        * of every LED in the qt2160. It's a HW limitation.
+        */
+       if (value != LED_OFF)
+               qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
+
+       mutex_unlock(&qt2160->led_lock);
+}
+
+static void qt2160_led_set(struct led_classdev *cdev,
+                          enum led_brightness value)
+{
+       struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
+
+       led->new_brightness = value;
+       schedule_work(&led->work);
+}
+
+#endif /* CONFIG_LEDS_CLASS */
+
 static int qt2160_read_block(struct i2c_client *client,
                             u8 inireg, u8 *buffer, unsigned int count)
 {
@@ -216,6 +286,63 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
        return ret;
 }
 
+#ifdef CONFIG_LEDS_CLASS
+
+static int qt2160_register_leds(struct qt2160_data *qt2160)
+{
+       struct i2c_client *client = qt2160->client;
+       int ret;
+       int i;
+
+       mutex_init(&qt2160->led_lock);
+
+       for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
+               struct qt2160_led *led = &qt2160->leds[i];
+
+               snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
+               led->cdev.name = led->name;
+               led->cdev.brightness_set = qt2160_led_set;
+               led->cdev.brightness = LED_OFF;
+               led->id = i;
+               led->qt2160 = qt2160;
+
+               INIT_WORK(&led->work, qt2160_led_work);
+
+               ret = led_classdev_register(&client->dev, &led->cdev);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Tur off LEDs */
+       qt2160_write(client, QT2160_CMD_DRIVE_X, 0);
+       qt2160_write(client, QT2160_CMD_PWMEN_X, 0);
+       qt2160_write(client, QT2160_CMD_PWM_DUTY, 0);
+
+       return 0;
+}
+
+static void qt2160_unregister_leds(struct qt2160_data *qt2160)
+{
+       int i;
+
+       for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
+               led_classdev_unregister(&qt2160->leds[i].cdev);
+               cancel_work_sync(&qt2160->leds[i].work);
+       }
+}
+
+#else
+
+static inline int qt2160_register_leds(struct qt2160_data *qt2160)
+{
+       return 0;
+}
+
+static inline void qt2160_unregister_leds(struct qt2160_data *qt2160)
+{
+}
+
+#endif
 
 static bool qt2160_identify(struct i2c_client *client)
 {
@@ -249,7 +376,7 @@ static bool qt2160_identify(struct i2c_client *client)
 }
 
 static int qt2160_probe(struct i2c_client *client,
-                                 const struct i2c_device_id *id)
+                       const struct i2c_device_id *id)
 {
        struct qt2160_data *qt2160;
        struct input_dev *input;
@@ -314,11 +441,17 @@ static int qt2160_probe(struct i2c_client *client,
                }
        }
 
+       error = qt2160_register_leds(qt2160);
+       if (error) {
+               dev_err(&client->dev, "Failed to register leds\n");
+               goto err_free_irq;
+       }
+
        error = input_register_device(qt2160->input);
        if (error) {
                dev_err(&client->dev,
                        "Failed to register input device\n");
-               goto err_free_irq;
+               goto err_unregister_leds;
        }
 
        i2c_set_clientdata(client, qt2160);
@@ -326,6 +459,8 @@ static int qt2160_probe(struct i2c_client *client,
 
        return 0;
 
+err_unregister_leds:
+       qt2160_unregister_leds(qt2160);
 err_free_irq:
        if (client->irq)
                free_irq(client->irq, qt2160);
@@ -339,6 +474,8 @@ static int qt2160_remove(struct i2c_client *client)
 {
        struct qt2160_data *qt2160 = i2c_get_clientdata(client);
 
+       qt2160_unregister_leds(qt2160);
+
        /* Release IRQ so no queue will be scheduled */
        if (client->irq)
                free_irq(client->irq, qt2160);
index c76f96872d313f558eb9a75e6305a7d14e123d2c..d89e7d392d1ef38131a45e832f0e7596aa77cf68 100644 (file)
 #include <linux/of.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
-#include <linux/input/tegra_kbc.h>
+#include <linux/input/matrix_keypad.h>
 #include <mach/clk.h>
 
+#define KBC_MAX_GPIO   24
+#define KBC_MAX_KPENT  8
+
+#define KBC_MAX_ROW    16
+#define KBC_MAX_COL    8
+#define KBC_MAX_KEY    (KBC_MAX_ROW * KBC_MAX_COL)
+
 #define KBC_MAX_DEBOUNCE_CNT   0x3ffu
 
 /* KBC row scan time and delay for beginning the row scan. */
 
 #define KBC_ROW_SHIFT  3
 
+enum tegra_pin_type {
+       PIN_CFG_IGNORE,
+       PIN_CFG_COL,
+       PIN_CFG_ROW,
+};
+
+struct tegra_kbc_pin_cfg {
+       enum tegra_pin_type type;
+       unsigned char num;
+};
+
 struct tegra_kbc {
+       struct device *dev;
+       unsigned int debounce_cnt;
+       unsigned int repeat_cnt;
+       struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
+       const struct matrix_keymap_data *keymap_data;
+       bool wakeup;
        void __iomem *mmio;
        struct input_dev *idev;
-       unsigned int irq;
+       int irq;
        spinlock_t lock;
        unsigned int repoll_dly;
        unsigned long cp_dly_jiffies;
@@ -78,7 +102,6 @@ struct tegra_kbc {
        bool use_fn_map;
        bool use_ghost_filter;
        bool keypress_caused_wake;
-       const struct tegra_kbc_platform_data *pdata;
        unsigned short keycode[KBC_MAX_KEY * 2];
        unsigned short current_keys[KBC_MAX_KPENT];
        unsigned int num_pressed_keys;
@@ -87,147 +110,6 @@ struct tegra_kbc {
        struct clk *clk;
 };
 
-static const u32 tegra_kbc_default_keymap[] = {
-       KEY(0, 2, KEY_W),
-       KEY(0, 3, KEY_S),
-       KEY(0, 4, KEY_A),
-       KEY(0, 5, KEY_Z),
-       KEY(0, 7, KEY_FN),
-
-       KEY(1, 7, KEY_LEFTMETA),
-
-       KEY(2, 6, KEY_RIGHTALT),
-       KEY(2, 7, KEY_LEFTALT),
-
-       KEY(3, 0, KEY_5),
-       KEY(3, 1, KEY_4),
-       KEY(3, 2, KEY_R),
-       KEY(3, 3, KEY_E),
-       KEY(3, 4, KEY_F),
-       KEY(3, 5, KEY_D),
-       KEY(3, 6, KEY_X),
-
-       KEY(4, 0, KEY_7),
-       KEY(4, 1, KEY_6),
-       KEY(4, 2, KEY_T),
-       KEY(4, 3, KEY_H),
-       KEY(4, 4, KEY_G),
-       KEY(4, 5, KEY_V),
-       KEY(4, 6, KEY_C),
-       KEY(4, 7, KEY_SPACE),
-
-       KEY(5, 0, KEY_9),
-       KEY(5, 1, KEY_8),
-       KEY(5, 2, KEY_U),
-       KEY(5, 3, KEY_Y),
-       KEY(5, 4, KEY_J),
-       KEY(5, 5, KEY_N),
-       KEY(5, 6, KEY_B),
-       KEY(5, 7, KEY_BACKSLASH),
-
-       KEY(6, 0, KEY_MINUS),
-       KEY(6, 1, KEY_0),
-       KEY(6, 2, KEY_O),
-       KEY(6, 3, KEY_I),
-       KEY(6, 4, KEY_L),
-       KEY(6, 5, KEY_K),
-       KEY(6, 6, KEY_COMMA),
-       KEY(6, 7, KEY_M),
-
-       KEY(7, 1, KEY_EQUAL),
-       KEY(7, 2, KEY_RIGHTBRACE),
-       KEY(7, 3, KEY_ENTER),
-       KEY(7, 7, KEY_MENU),
-
-       KEY(8, 4, KEY_RIGHTSHIFT),
-       KEY(8, 5, KEY_LEFTSHIFT),
-
-       KEY(9, 5, KEY_RIGHTCTRL),
-       KEY(9, 7, KEY_LEFTCTRL),
-
-       KEY(11, 0, KEY_LEFTBRACE),
-       KEY(11, 1, KEY_P),
-       KEY(11, 2, KEY_APOSTROPHE),
-       KEY(11, 3, KEY_SEMICOLON),
-       KEY(11, 4, KEY_SLASH),
-       KEY(11, 5, KEY_DOT),
-
-       KEY(12, 0, KEY_F10),
-       KEY(12, 1, KEY_F9),
-       KEY(12, 2, KEY_BACKSPACE),
-       KEY(12, 3, KEY_3),
-       KEY(12, 4, KEY_2),
-       KEY(12, 5, KEY_UP),
-       KEY(12, 6, KEY_PRINT),
-       KEY(12, 7, KEY_PAUSE),
-
-       KEY(13, 0, KEY_INSERT),
-       KEY(13, 1, KEY_DELETE),
-       KEY(13, 3, KEY_PAGEUP),
-       KEY(13, 4, KEY_PAGEDOWN),
-       KEY(13, 5, KEY_RIGHT),
-       KEY(13, 6, KEY_DOWN),
-       KEY(13, 7, KEY_LEFT),
-
-       KEY(14, 0, KEY_F11),
-       KEY(14, 1, KEY_F12),
-       KEY(14, 2, KEY_F8),
-       KEY(14, 3, KEY_Q),
-       KEY(14, 4, KEY_F4),
-       KEY(14, 5, KEY_F3),
-       KEY(14, 6, KEY_1),
-       KEY(14, 7, KEY_F7),
-
-       KEY(15, 0, KEY_ESC),
-       KEY(15, 1, KEY_GRAVE),
-       KEY(15, 2, KEY_F5),
-       KEY(15, 3, KEY_TAB),
-       KEY(15, 4, KEY_F1),
-       KEY(15, 5, KEY_F2),
-       KEY(15, 6, KEY_CAPSLOCK),
-       KEY(15, 7, KEY_F6),
-
-       /* Software Handled Function Keys */
-       KEY(20, 0, KEY_KP7),
-
-       KEY(21, 0, KEY_KP9),
-       KEY(21, 1, KEY_KP8),
-       KEY(21, 2, KEY_KP4),
-       KEY(21, 4, KEY_KP1),
-
-       KEY(22, 1, KEY_KPSLASH),
-       KEY(22, 2, KEY_KP6),
-       KEY(22, 3, KEY_KP5),
-       KEY(22, 4, KEY_KP3),
-       KEY(22, 5, KEY_KP2),
-       KEY(22, 7, KEY_KP0),
-
-       KEY(27, 1, KEY_KPASTERISK),
-       KEY(27, 3, KEY_KPMINUS),
-       KEY(27, 4, KEY_KPPLUS),
-       KEY(27, 5, KEY_KPDOT),
-
-       KEY(28, 5, KEY_VOLUMEUP),
-
-       KEY(29, 3, KEY_HOME),
-       KEY(29, 4, KEY_END),
-       KEY(29, 5, KEY_BRIGHTNESSDOWN),
-       KEY(29, 6, KEY_VOLUMEDOWN),
-       KEY(29, 7, KEY_BRIGHTNESSUP),
-
-       KEY(30, 0, KEY_NUMLOCK),
-       KEY(30, 1, KEY_SCROLLLOCK),
-       KEY(30, 2, KEY_MUTE),
-
-       KEY(31, 4, KEY_HELP),
-};
-
-static const
-struct matrix_keymap_data tegra_kbc_default_keymap_data = {
-       .keymap         = tegra_kbc_default_keymap,
-       .keymap_size    = ARRAY_SIZE(tegra_kbc_default_keymap),
-};
-
 static void tegra_kbc_report_released_keys(struct input_dev *input,
                                           unsigned short old_keycodes[],
                                           unsigned int old_num_keys,
@@ -357,18 +239,6 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
        writel(val, kbc->mmio + KBC_CONTROL_0);
 }
 
-static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
-{
-       u32 val;
-
-       val = readl(kbc->mmio + KBC_CONTROL_0);
-       if (enable)
-               val |= KBC_CONTROL_KEYPRESS_INT_EN;
-       else
-               val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
-       writel(val, kbc->mmio + KBC_CONTROL_0);
-}
-
 static void tegra_kbc_keypress_timer(unsigned long data)
 {
        struct tegra_kbc *kbc = (struct tegra_kbc *)data;
@@ -439,12 +309,11 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args)
 
 static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
 {
-       const struct tegra_kbc_platform_data *pdata = kbc->pdata;
        int i;
        unsigned int rst_val;
 
        /* Either mask all keys or none. */
-       rst_val = (filter && !pdata->wakeup) ? ~0 : 0;
+       rst_val = (filter && !kbc->wakeup) ? ~0 : 0;
 
        for (i = 0; i < KBC_MAX_ROW; i++)
                writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4);
@@ -452,7 +321,6 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter)
 
 static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
 {
-       const struct tegra_kbc_platform_data *pdata = kbc->pdata;
        int i;
 
        for (i = 0; i < KBC_MAX_GPIO; i++) {
@@ -468,13 +336,13 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
                row_cfg &= ~r_mask;
                col_cfg &= ~c_mask;
 
-               switch (pdata->pin_cfg[i].type) {
+               switch (kbc->pin_cfg[i].type) {
                case PIN_CFG_ROW:
-                       row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft;
+                       row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft;
                        break;
 
                case PIN_CFG_COL:
-                       col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft;
+                       col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft;
                        break;
 
                case PIN_CFG_IGNORE:
@@ -488,7 +356,6 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
 
 static int tegra_kbc_start(struct tegra_kbc *kbc)
 {
-       const struct tegra_kbc_platform_data *pdata = kbc->pdata;
        unsigned int debounce_cnt;
        u32 val = 0;
 
@@ -503,10 +370,10 @@ static int tegra_kbc_start(struct tegra_kbc *kbc)
        tegra_kbc_config_pins(kbc);
        tegra_kbc_setup_wakekeys(kbc, false);
 
-       writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
+       writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0);
 
        /* Keyboard debounce count is maximum of 12 bits. */
-       debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+       debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
        val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
        val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
        val |= KBC_CONTROL_FIFO_CNT_INT_EN;  /* interrupt on FIFO threshold */
@@ -573,21 +440,20 @@ static void tegra_kbc_close(struct input_dev *dev)
        return tegra_kbc_stop(kbc);
 }
 
-static bool
-tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
-                       struct device *dev, unsigned int *num_rows)
+static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc,
+                                       unsigned int *num_rows)
 {
        int i;
 
        *num_rows = 0;
 
        for (i = 0; i < KBC_MAX_GPIO; i++) {
-               const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i];
+               const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i];
 
                switch (pin_cfg->type) {
                case PIN_CFG_ROW:
                        if (pin_cfg->num >= KBC_MAX_ROW) {
-                               dev_err(dev,
+                               dev_err(kbc->dev,
                                        "pin_cfg[%d]: invalid row number %d\n",
                                        i, pin_cfg->num);
                                return false;
@@ -597,7 +463,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
 
                case PIN_CFG_COL:
                        if (pin_cfg->num >= KBC_MAX_COL) {
-                               dev_err(dev,
+                               dev_err(kbc->dev,
                                        "pin_cfg[%d]: invalid column number %d\n",
                                        i, pin_cfg->num);
                                return false;
@@ -608,7 +474,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
                        break;
 
                default:
-                       dev_err(dev,
+                       dev_err(kbc->dev,
                                "pin_cfg[%d]: invalid entry type %d\n",
                                pin_cfg->type, pin_cfg->num);
                        return false;
@@ -618,154 +484,140 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
        return true;
 }
 
-#ifdef CONFIG_OF
-static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
-       struct platform_device *pdev)
+static int tegra_kbc_parse_dt(struct tegra_kbc *kbc)
 {
-       struct tegra_kbc_platform_data *pdata;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = kbc->dev->of_node;
        u32 prop;
        int i;
-
-       if (!np)
-               return NULL;
-
-       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return NULL;
+       u32 num_rows = 0;
+       u32 num_cols = 0;
+       u32 cols_cfg[KBC_MAX_GPIO];
+       u32 rows_cfg[KBC_MAX_GPIO];
+       int proplen;
+       int ret;
 
        if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
-               pdata->debounce_cnt = prop;
+               kbc->debounce_cnt = prop;
 
        if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop))
-               pdata->repeat_cnt = prop;
+               kbc->repeat_cnt = prop;
 
        if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
-               pdata->use_ghost_filter = true;
+               kbc->use_ghost_filter = true;
 
        if (of_find_property(np, "nvidia,wakeup-source", NULL))
-               pdata->wakeup = true;
+               kbc->wakeup = true;
 
-       /*
-        * All currently known keymaps with device tree support use the same
-        * pin_cfg, so set it up here.
-        */
-       for (i = 0; i < KBC_MAX_ROW; i++) {
-               pdata->pin_cfg[i].num = i;
-               pdata->pin_cfg[i].type = PIN_CFG_ROW;
+       if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) {
+               dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n");
+               return -ENOENT;
        }
+       num_rows = proplen / sizeof(u32);
 
-       for (i = 0; i < KBC_MAX_COL; i++) {
-               pdata->pin_cfg[KBC_MAX_ROW + i].num = i;
-               pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL;
+       if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) {
+               dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n");
+               return -ENOENT;
        }
+       num_cols = proplen / sizeof(u32);
 
-       return pdata;
-}
-#else
-static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
-       struct platform_device *pdev)
-{
-       return NULL;
-}
-#endif
+       if (!of_get_property(np, "linux,keymap", &proplen)) {
+               dev_err(kbc->dev, "property linux,keymap not found\n");
+               return -ENOENT;
+       }
 
-static int tegra_kbd_setup_keymap(struct tegra_kbc *kbc)
-{
-       const struct tegra_kbc_platform_data *pdata = kbc->pdata;
-       const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
-       unsigned int keymap_rows = KBC_MAX_KEY;
-       int retval;
+       if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) {
+               dev_err(kbc->dev,
+                       "keypad rows/columns not porperly specified\n");
+               return -EINVAL;
+       }
 
-       if (keymap_data && pdata->use_fn_map)
-               keymap_rows *= 2;
+       /* Set all pins as non-configured */
+       for (i = 0; i < KBC_MAX_GPIO; i++)
+               kbc->pin_cfg[i].type = PIN_CFG_IGNORE;
 
-       retval = matrix_keypad_build_keymap(keymap_data, NULL,
-                                           keymap_rows, KBC_MAX_COL,
-                                           kbc->keycode, kbc->idev);
-       if (retval == -ENOSYS || retval == -ENOENT) {
-               /*
-                * If there is no OF support in kernel or keymap
-                * property is missing, use default keymap.
-                */
-               retval = matrix_keypad_build_keymap(
-                                       &tegra_kbc_default_keymap_data, NULL,
-                                       keymap_rows, KBC_MAX_COL,
-                                       kbc->keycode, kbc->idev);
+       ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins",
+                               rows_cfg, num_rows);
+       if (ret < 0) {
+               dev_err(kbc->dev, "Rows configurations are not proper\n");
+               return -EINVAL;
+       }
+
+       ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins",
+                               cols_cfg, num_cols);
+       if (ret < 0) {
+               dev_err(kbc->dev, "Cols configurations are not proper\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < num_rows; i++) {
+               kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW;
+               kbc->pin_cfg[rows_cfg[i]].num = i;
        }
 
-       return retval;
+       for (i = 0; i < num_cols; i++) {
+               kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL;
+               kbc->pin_cfg[cols_cfg[i]].num = i;
+       }
+
+       return 0;
 }
 
 static int tegra_kbc_probe(struct platform_device *pdev)
 {
-       const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data;
        struct tegra_kbc *kbc;
-       struct input_dev *input_dev;
        struct resource *res;
-       int irq;
        int err;
        int num_rows = 0;
        unsigned int debounce_cnt;
        unsigned int scan_time_rows;
+       unsigned int keymap_rows = KBC_MAX_KEY;
 
-       if (!pdata)
-               pdata = tegra_kbc_dt_parse_pdata(pdev);
+       kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL);
+       if (!kbc) {
+               dev_err(&pdev->dev, "failed to alloc memory for kbc\n");
+               return -ENOMEM;
+       }
 
-       if (!pdata)
-               return -EINVAL;
+       kbc->dev = &pdev->dev;
+       spin_lock_init(&kbc->lock);
 
-       if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) {
-               err = -EINVAL;
-               goto err_free_pdata;
-       }
+       err = tegra_kbc_parse_dt(kbc);
+       if (err)
+               return err;
+
+       if (!tegra_kbc_check_pin_cfg(kbc, &num_rows))
+               return -EINVAL;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(&pdev->dev, "failed to get I/O memory\n");
-               err = -ENXIO;
-               goto err_free_pdata;
+               return -ENXIO;
        }
 
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
+       kbc->irq = platform_get_irq(pdev, 0);
+       if (kbc->irq < 0) {
                dev_err(&pdev->dev, "failed to get keyboard IRQ\n");
-               err = -ENXIO;
-               goto err_free_pdata;
+               return -ENXIO;
        }
 
-       kbc = kzalloc(sizeof(*kbc), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!kbc || !input_dev) {
-               err = -ENOMEM;
-               goto err_free_mem;
+       kbc->idev = devm_input_allocate_device(&pdev->dev);
+       if (!kbc->idev) {
+               dev_err(&pdev->dev, "failed to allocate input device\n");
+               return -ENOMEM;
        }
 
-       kbc->pdata = pdata;
-       kbc->idev = input_dev;
-       kbc->irq = irq;
-       spin_lock_init(&kbc->lock);
        setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc);
 
-       res = request_mem_region(res->start, resource_size(res), pdev->name);
-       if (!res) {
-               dev_err(&pdev->dev, "failed to request I/O memory\n");
-               err = -EBUSY;
-               goto err_free_mem;
-       }
-
-       kbc->mmio = ioremap(res->start, resource_size(res));
+       kbc->mmio = devm_request_and_ioremap(&pdev->dev, res);
        if (!kbc->mmio) {
-               dev_err(&pdev->dev, "failed to remap I/O memory\n");
-               err = -ENXIO;
-               goto err_free_mem_region;
+               dev_err(&pdev->dev, "Cannot request memregion/iomap address\n");
+               return -EBUSY;
        }
 
-       kbc->clk = clk_get(&pdev->dev, NULL);
+       kbc->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(kbc->clk)) {
                dev_err(&pdev->dev, "failed to get keyboard clock\n");
-               err = PTR_ERR(kbc->clk);
-               goto err_iounmap;
+               return PTR_ERR(kbc->clk);
        }
 
        /*
@@ -774,37 +626,38 @@ static int tegra_kbc_probe(struct platform_device *pdev)
         * the rows. There is an additional delay before the row scanning
         * starts. The repoll delay is computed in milliseconds.
         */
-       debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
+       debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
        scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
-       kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
+       kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt;
        kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
 
-       kbc->wakeup_key = pdata->wakeup_key;
-       kbc->use_fn_map = pdata->use_fn_map;
-       kbc->use_ghost_filter = pdata->use_ghost_filter;
+       kbc->idev->name = pdev->name;
+       kbc->idev->id.bustype = BUS_HOST;
+       kbc->idev->dev.parent = &pdev->dev;
+       kbc->idev->open = tegra_kbc_open;
+       kbc->idev->close = tegra_kbc_close;
 
-       input_dev->name = pdev->name;
-       input_dev->id.bustype = BUS_HOST;
-       input_dev->dev.parent = &pdev->dev;
-       input_dev->open = tegra_kbc_open;
-       input_dev->close = tegra_kbc_close;
+       if (kbc->keymap_data && kbc->use_fn_map)
+               keymap_rows *= 2;
 
-       err = tegra_kbd_setup_keymap(kbc);
+       err = matrix_keypad_build_keymap(kbc->keymap_data, NULL,
+                                        keymap_rows, KBC_MAX_COL,
+                                        kbc->keycode, kbc->idev);
        if (err) {
                dev_err(&pdev->dev, "failed to setup keymap\n");
-               goto err_put_clk;
+               return err;
        }
 
-       __set_bit(EV_REP, input_dev->evbit);
-       input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+       __set_bit(EV_REP, kbc->idev->evbit);
+       input_set_capability(kbc->idev, EV_MSC, MSC_SCAN);
 
-       input_set_drvdata(input_dev, kbc);
+       input_set_drvdata(kbc->idev, kbc);
 
-       err = request_irq(kbc->irq, tegra_kbc_isr,
+       err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr,
                          IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
        if (err) {
                dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
-               goto err_put_clk;
+               return err;
        }
 
        disable_irq(kbc->irq);
@@ -812,60 +665,28 @@ static int tegra_kbc_probe(struct platform_device *pdev)
        err = input_register_device(kbc->idev);
        if (err) {
                dev_err(&pdev->dev, "failed to register input device\n");
-               goto err_free_irq;
+               return err;
        }
 
        platform_set_drvdata(pdev, kbc);
-       device_init_wakeup(&pdev->dev, pdata->wakeup);
+       device_init_wakeup(&pdev->dev, kbc->wakeup);
 
        return 0;
-
-err_free_irq:
-       free_irq(kbc->irq, pdev);
-err_put_clk:
-       clk_put(kbc->clk);
-err_iounmap:
-       iounmap(kbc->mmio);
-err_free_mem_region:
-       release_mem_region(res->start, resource_size(res));
-err_free_mem:
-       input_free_device(input_dev);
-       kfree(kbc);
-err_free_pdata:
-       if (!pdev->dev.platform_data)
-               kfree(pdata);
-
-       return err;
 }
 
-static int tegra_kbc_remove(struct platform_device *pdev)
+#ifdef CONFIG_PM_SLEEP
+static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
 {
-       struct tegra_kbc *kbc = platform_get_drvdata(pdev);
-       struct resource *res;
-
-       platform_set_drvdata(pdev, NULL);
-
-       free_irq(kbc->irq, pdev);
-       clk_put(kbc->clk);
-
-       input_unregister_device(kbc->idev);
-       iounmap(kbc->mmio);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, resource_size(res));
-
-       /*
-        * If we do not have platform data attached to the device we
-        * allocated it ourselves and thus need to free it.
-        */
-       if (!pdev->dev.platform_data)
-               kfree(kbc->pdata);
-
-       kfree(kbc);
+       u32 val;
 
-       return 0;
+       val = readl(kbc->mmio + KBC_CONTROL_0);
+       if (enable)
+               val |= KBC_CONTROL_KEYPRESS_INT_EN;
+       else
+               val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
+       writel(val, kbc->mmio + KBC_CONTROL_0);
 }
 
-#ifdef CONFIG_PM_SLEEP
 static int tegra_kbc_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -954,7 +775,6 @@ MODULE_DEVICE_TABLE(of, tegra_kbc_of_match);
 
 static struct platform_driver tegra_kbc_driver = {
        .probe          = tegra_kbc_probe,
-       .remove         = tegra_kbc_remove,
        .driver = {
                .name   = "tegra-kbc",
                .owner  = THIS_MODULE,
index 1cf72fe513e6934af7fb4ab0d5742c3155537146..0735de3a6468f85cad2c08a1dc601d5a67a67a23 100644 (file)
@@ -232,7 +232,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = {
 
        .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
        .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
-       .fifo_mode = FIFO_STREAM,
+       .fifo_mode = ADXL_FIFO_STREAM,
        .watermark = 0,
 };
 
@@ -732,7 +732,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
        mutex_init(&ac->mutex);
 
        input_dev->name = "ADXL34x accelerometer";
-       revid = ac->bops->read(dev, DEVID);
+       revid = AC_READ(ac, DEVID);
 
        switch (revid) {
        case ID_ADXL345:
@@ -809,7 +809,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
        if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
                ac->fifo_delay = false;
 
-       ac->bops->write(dev, POWER_CTL, 0);
+       AC_WRITE(ac, POWER_CTL, 0);
 
        err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
                                   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
@@ -827,7 +827,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
        if (err)
                goto err_remove_attr;
 
-       AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
        AC_WRITE(ac, OFSX, pdata->x_axis_offset);
        ac->hwcal.x = pdata->x_axis_offset;
        AC_WRITE(ac, OFSY, pdata->y_axis_offset);
index 08ffcabd7220f5f1f5e98c16abc9f953963ddd61..865c2f9d25b9ff9cb55f63505852d89c06cf9687 100644 (file)
 #define BMA150_POLL_MAX                200
 #define BMA150_POLL_MIN                0
 
-#define BMA150_BW_25HZ         0
-#define BMA150_BW_50HZ         1
-#define BMA150_BW_100HZ                2
-#define BMA150_BW_190HZ                3
-#define BMA150_BW_375HZ                4
-#define BMA150_BW_750HZ                5
-#define BMA150_BW_1500HZ       6
-
-#define BMA150_RANGE_2G                0
-#define BMA150_RANGE_4G                1
-#define BMA150_RANGE_8G                2
-
 #define BMA150_MODE_NORMAL     0
 #define BMA150_MODE_SLEEP      2
 #define BMA150_MODE_WAKE_UP    3
@@ -372,7 +360,7 @@ static int bma150_open(struct bma150_data *bma150)
        int error;
 
        error = pm_runtime_get_sync(&bma150->client->dev);
-       if (error && error != -ENOSYS)
+       if (error < 0 && error != -ENOSYS)
                return error;
 
        /*
index 78eb6b30580acdf5aa86096ecef01b653ffec3f6..68a5f33152a8cd8914d39245257735d0e5ecae38 100644 (file)
@@ -43,7 +43,6 @@ struct vibra_info {
        struct device           *dev;
        struct input_dev        *input_dev;
 
-       struct workqueue_struct *workqueue;
        struct work_struct      play_work;
 
        bool                    enabled;
@@ -143,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data,
        if (!info->speed)
                info->speed = effect->u.rumble.weak_magnitude >> 9;
        info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
-       queue_work(info->workqueue, &info->play_work);
-       return 0;
-}
-
-static int twl4030_vibra_open(struct input_dev *input)
-{
-       struct vibra_info *info = input_get_drvdata(input);
-
-       info->workqueue = create_singlethread_workqueue("vibra");
-       if (info->workqueue == NULL) {
-               dev_err(&input->dev, "couldn't create workqueue\n");
-               return -ENOMEM;
-       }
+       schedule_work(&info->play_work);
        return 0;
 }
 
@@ -164,9 +151,6 @@ static void twl4030_vibra_close(struct input_dev *input)
        struct vibra_info *info = input_get_drvdata(input);
 
        cancel_work_sync(&info->play_work);
-       INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */
-       destroy_workqueue(info->workqueue);
-       info->workqueue = NULL;
 
        if (info->enabled)
                vibra_disable(info);
@@ -219,7 +203,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
@@ -227,11 +211,10 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
        info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
        INIT_WORK(&info->play_work, vibra_play_work);
 
-       info->input_dev = input_allocate_device();
+       info->input_dev = devm_input_allocate_device(&pdev->dev);
        if (info->input_dev == NULL) {
                dev_err(&pdev->dev, "couldn't allocate input device\n");
-               ret = -ENOMEM;
-               goto err_kzalloc;
+               return -ENOMEM;
        }
 
        input_set_drvdata(info->input_dev, info);
@@ -239,14 +222,13 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
        info->input_dev->name = "twl4030:vibrator";
        info->input_dev->id.version = 1;
        info->input_dev->dev.parent = pdev->dev.parent;
-       info->input_dev->open = twl4030_vibra_open;
        info->input_dev->close = twl4030_vibra_close;
        __set_bit(FF_RUMBLE, info->input_dev->ffbit);
 
        ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
        if (ret < 0) {
                dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
-               goto err_ialloc;
+               return ret;
        }
 
        ret = input_register_device(info->input_dev);
@@ -262,28 +244,11 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
 
 err_iff:
        input_ff_destroy(info->input_dev);
-err_ialloc:
-       input_free_device(info->input_dev);
-err_kzalloc:
-       kfree(info);
        return ret;
 }
 
-static int twl4030_vibra_remove(struct platform_device *pdev)
-{
-       struct vibra_info *info = platform_get_drvdata(pdev);
-
-       /* this also free ff-memless and calls close if needed */
-       input_unregister_device(info->input_dev);
-       kfree(info);
-       platform_set_drvdata(pdev, NULL);
-
-       return 0;
-}
-
 static struct platform_driver twl4030_vibra_driver = {
        .probe          = twl4030_vibra_probe,
-       .remove         = twl4030_vibra_remove,
        .driver         = {
                .name   = "twl4030-vibra",
                .owner  = THIS_MODULE,
index 71a28ee699f3ef52732e7cd601f26d1ed2eb72c0..0c2dfc8e96918c3e022aad1920ece05e1115abd8 100644 (file)
@@ -275,7 +275,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info) {
                dev_err(&pdev->dev, "couldn't allocate memory\n");
                return -ENOMEM;
@@ -309,53 +309,23 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
        if ((!info->vibldrv_res && !info->viblmotor_res) ||
            (!info->vibrdrv_res && !info->vibrmotor_res)) {
                dev_err(info->dev, "invalid vibra driver/motor resistance\n");
-               ret = -EINVAL;
-               goto err_kzalloc;
+               return -EINVAL;
        }
 
        info->irq = platform_get_irq(pdev, 0);
        if (info->irq < 0) {
                dev_err(info->dev, "invalid irq\n");
-               ret = -EINVAL;
-               goto err_kzalloc;
+               return -EINVAL;
        }
 
        mutex_init(&info->mutex);
 
-       info->input_dev = input_allocate_device();
-       if (info->input_dev == NULL) {
-               dev_err(info->dev, "couldn't allocate input device\n");
-               ret = -ENOMEM;
-               goto err_kzalloc;
-       }
-
-       input_set_drvdata(info->input_dev, info);
-
-       info->input_dev->name = "twl6040:vibrator";
-       info->input_dev->id.version = 1;
-       info->input_dev->dev.parent = pdev->dev.parent;
-       info->input_dev->close = twl6040_vibra_close;
-       __set_bit(FF_RUMBLE, info->input_dev->ffbit);
-
-       ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
-       if (ret < 0) {
-               dev_err(info->dev, "couldn't register vibrator to FF\n");
-               goto err_ialloc;
-       }
-
-       ret = input_register_device(info->input_dev);
-       if (ret < 0) {
-               dev_err(info->dev, "couldn't register input device\n");
-               goto err_iff;
-       }
-
-       platform_set_drvdata(pdev, info);
-
-       ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0,
-                                  "twl6040_irq_vib", info);
+       ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+                                       twl6040_vib_irq_handler, 0,
+                                       "twl6040_irq_vib", info);
        if (ret) {
                dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
-               goto err_irq;
+               return ret;
        }
 
        info->supplies[0].supply = "vddvibl";
@@ -368,7 +338,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                                 ARRAY_SIZE(info->supplies), info->supplies);
        if (ret) {
                dev_err(info->dev, "couldn't get regulators %d\n", ret);
-               goto err_regulator;
+               return ret;
        }
 
        if (vddvibl_uV) {
@@ -377,7 +347,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
                                ret);
-                       goto err_voltage;
+                       goto err_regulator;
                }
        }
 
@@ -387,34 +357,49 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
                                ret);
-                       goto err_voltage;
+                       goto err_regulator;
                }
        }
 
-       info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0);
-       if (info->workqueue == NULL) {
-               dev_err(info->dev, "couldn't create workqueue\n");
+       INIT_WORK(&info->play_work, vibra_play_work);
+
+       info->input_dev = input_allocate_device();
+       if (info->input_dev == NULL) {
+               dev_err(info->dev, "couldn't allocate input device\n");
                ret = -ENOMEM;
-               goto err_voltage;
+               goto err_regulator;
        }
-       INIT_WORK(&info->play_work, vibra_play_work);
+
+       input_set_drvdata(info->input_dev, info);
+
+       info->input_dev->name = "twl6040:vibrator";
+       info->input_dev->id.version = 1;
+       info->input_dev->dev.parent = pdev->dev.parent;
+       info->input_dev->close = twl6040_vibra_close;
+       __set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+       ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+       if (ret < 0) {
+               dev_err(info->dev, "couldn't register vibrator to FF\n");
+               goto err_ialloc;
+       }
+
+       ret = input_register_device(info->input_dev);
+       if (ret < 0) {
+               dev_err(info->dev, "couldn't register input device\n");
+               goto err_iff;
+       }
+
+       platform_set_drvdata(pdev, info);
 
        return 0;
 
-err_voltage:
-       regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
-err_regulator:
-       free_irq(info->irq, info);
-err_irq:
-       input_unregister_device(info->input_dev);
-       info->input_dev = NULL;
 err_iff:
-       if (info->input_dev)
-               input_ff_destroy(info->input_dev);
+       input_ff_destroy(info->input_dev);
 err_ialloc:
        input_free_device(info->input_dev);
-err_kzalloc:
-       kfree(info);
+err_regulator:
+       regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
        return ret;
 }
 
@@ -423,10 +408,7 @@ static int twl6040_vibra_remove(struct platform_device *pdev)
        struct vibra_info *info = platform_get_drvdata(pdev);
 
        input_unregister_device(info->input_dev);
-       free_irq(info->irq, info);
        regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
-       destroy_workqueue(info->workqueue);
-       kfree(info);
 
        return 0;
 }
index 558767d8ebf43d501d77cb3277a706a722a92681..caa2c4068f0924f8070c705501f6771e6f0152fe 100644 (file)
@@ -86,7 +86,7 @@ static int wm831x_on_probe(struct platform_device *pdev)
        wm831x_on->wm831x = wm831x;
        INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
 
-       wm831x_on->dev = input_allocate_device();
+       wm831x_on->dev = devm_input_allocate_device(&pdev->dev);
        if (!wm831x_on->dev) {
                dev_err(&pdev->dev, "Can't allocate input dev\n");
                ret = -ENOMEM;
@@ -119,7 +119,6 @@ static int wm831x_on_probe(struct platform_device *pdev)
 err_irq:
        free_irq(irq, wm831x_on);
 err_input_dev:
-       input_free_device(wm831x_on->dev);
 err:
        return ret;
 }
@@ -131,7 +130,6 @@ static int wm831x_on_remove(struct platform_device *pdev)
 
        free_irq(irq, wm831x_on);
        cancel_delayed_work_sync(&wm831x_on->work);
-       input_unregister_device(wm831x_on->dev);
 
        return 0;
 }
index cd6268cf7cd5d2f99115eba326eb65763236507d..802bd6a72d736422ec21e2c4d655b78635d014cf 100644 (file)
@@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS
 
          If unsure, say Y.
 
+config MOUSE_PS2_CYPRESS
+       bool "Cypress PS/2 mouse protocol extension" if EXPERT
+       default y
+       depends on MOUSE_PS2
+       help
+         Say Y here if you have a Cypress PS/2 Trackpad connected to
+         your system.
+
+         If unsure, say Y.
+
 config MOUSE_PS2_LIFEBOOK
        bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
        default y
@@ -193,6 +203,18 @@ config MOUSE_BCM5974
          To compile this driver as a module, choose M here: the
          module will be called bcm5974.
 
+config MOUSE_CYAPA
+       tristate "Cypress APA I2C Trackpad support"
+       depends on I2C
+       help
+         This driver adds support for Cypress All Points Addressable (APA)
+         I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
+
+         Say Y here if you have a Cypress APA I2C Trackpad.
+
+         To compile this driver as a module, choose M here: the module will be
+         called cyapa.
+
 config MOUSE_INPORT
        tristate "InPort/MS/ATIXL busmouse"
        depends on ISA
index 46ba7556fd4fea020f97c7cb6ac290140d5f828d..c25efdb3f288b0284e041c82dca3cbf42b122b9a 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA)               += amimouse.o
 obj-$(CONFIG_MOUSE_APPLETOUCH)         += appletouch.o
 obj-$(CONFIG_MOUSE_ATARI)              += atarimouse.o
 obj-$(CONFIG_MOUSE_BCM5974)            += bcm5974.o
+obj-$(CONFIG_MOUSE_CYAPA)              += cyapa.o
 obj-$(CONFIG_MOUSE_GPIO)               += gpio_mouse.o
 obj-$(CONFIG_MOUSE_INPORT)             += inport.o
 obj-$(CONFIG_MOUSE_LOGIBM)             += logibm.o
@@ -32,3 +33,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK)  += lifebook.o
 psmouse-$(CONFIG_MOUSE_PS2_SENTELIC)   += sentelic.o
 psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
 psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT)   += touchkit_ps2.o
+psmouse-$(CONFIG_MOUSE_PS2_CYPRESS)    += cypress_ps2.o
index e229fa3cad965e7bdbe8ae1b5358f2335be5394f..7b99fc7c9438dbcbcc115248403e4b9704611f29 100644 (file)
 /*
  * Definitions for ALPS version 3 and 4 command mode protocol
  */
-#define ALPS_V3_X_MAX  2000
-#define ALPS_V3_Y_MAX  1400
-
-#define ALPS_BITMAP_X_BITS     15
-#define ALPS_BITMAP_Y_BITS     11
-
 #define ALPS_CMD_NIBBLE_10     0x01f2
 
+#define ALPS_REG_BASE_RUSHMORE 0xc2c0
+#define ALPS_REG_BASE_PINNACLE 0x0000
+
 static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
        { PSMOUSE_CMD_SETPOLL,          0x00 }, /* 0 */
        { PSMOUSE_CMD_RESET_DIS,        0x00 }, /* 1 */
@@ -109,11 +106,14 @@ static const struct alps_model_info alps_model_data[] = {
        { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },           /* Dell Vostro 1400 */
        { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },                            /* Toshiba Tecra A11-11L */
-       { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT },
-       { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT },
        { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 },
 };
 
+static void alps_set_abs_params_st(struct alps_data *priv,
+                                  struct input_dev *dev1);
+static void alps_set_abs_params_mt(struct alps_data *priv,
+                                  struct input_dev *dev1);
+
 /*
  * XXX - this entry is suspicious. First byte has zero lower nibble,
  * which is what a normal mouse would report. Also, the value 0x0e
@@ -122,10 +122,10 @@ static const struct alps_model_info alps_model_data[] = {
 
 /* Packet formats are described in Documentation/input/alps.txt */
 
-static bool alps_is_valid_first_byte(const struct alps_model_info *model,
+static bool alps_is_valid_first_byte(struct alps_data *priv,
                                     unsigned char data)
 {
-       return (data & model->mask0) == model->byte0;
+       return (data & priv->mask0) == priv->byte0;
 }
 
 static void alps_report_buttons(struct psmouse *psmouse,
@@ -158,14 +158,13 @@ static void alps_report_buttons(struct psmouse *psmouse,
 static void alps_process_packet_v1_v2(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
-       const struct alps_model_info *model = priv->i;
        unsigned char *packet = psmouse->packet;
        struct input_dev *dev = psmouse->dev;
        struct input_dev *dev2 = priv->dev2;
        int x, y, z, ges, fin, left, right, middle;
        int back = 0, forward = 0;
 
-       if (model->proto_version == ALPS_PROTO_V1) {
+       if (priv->proto_version == ALPS_PROTO_V1) {
                left = packet[2] & 0x10;
                right = packet[2] & 0x08;
                middle = 0;
@@ -181,12 +180,12 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
                z = packet[5];
        }
 
-       if (model->flags & ALPS_FW_BK_1) {
+       if (priv->flags & ALPS_FW_BK_1) {
                back = packet[0] & 0x10;
                forward = packet[2] & 4;
        }
 
-       if (model->flags & ALPS_FW_BK_2) {
+       if (priv->flags & ALPS_FW_BK_2) {
                back = packet[3] & 4;
                forward = packet[2] & 4;
                if ((middle = forward && back))
@@ -196,7 +195,7 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
        ges = packet[2] & 1;
        fin = packet[2] & 2;
 
-       if ((model->flags & ALPS_DUALPOINT) && z == 127) {
+       if ((priv->flags & ALPS_DUALPOINT) && z == 127) {
                input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));
                input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
 
@@ -239,15 +238,15 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
        input_report_abs(dev, ABS_PRESSURE, z);
        input_report_key(dev, BTN_TOOL_FINGER, z > 0);
 
-       if (model->flags & ALPS_WHEEL)
+       if (priv->flags & ALPS_WHEEL)
                input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
 
-       if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+       if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
                input_report_key(dev, BTN_FORWARD, forward);
                input_report_key(dev, BTN_BACK, back);
        }
 
-       if (model->flags & ALPS_FOUR_BUTTONS) {
+       if (priv->flags & ALPS_FOUR_BUTTONS) {
                input_report_key(dev, BTN_0, packet[2] & 4);
                input_report_key(dev, BTN_1, packet[0] & 0x10);
                input_report_key(dev, BTN_2, packet[3] & 4);
@@ -267,7 +266,8 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
  * These points are returned in x1, y1, x2, and y2 when the return value
  * is greater than 0.
  */
-static int alps_process_bitmap(unsigned int x_map, unsigned int y_map,
+static int alps_process_bitmap(struct alps_data *priv,
+                              unsigned int x_map, unsigned int y_map,
                               int *x1, int *y1, int *x2, int *y2)
 {
        struct alps_bitmap_point {
@@ -309,7 +309,7 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map,
         * y bitmap is reversed for what we need (lower positions are in
         * higher bits), so we process from the top end.
         */
-       y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - ALPS_BITMAP_Y_BITS);
+       y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits);
        prev_bit = 0;
        point = &y_low;
        for (i = 0; y_map != 0; i++, y_map <<= 1) {
@@ -355,16 +355,18 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map,
                }
        }
 
-       *x1 = (ALPS_V3_X_MAX * (2 * x_low.start_bit + x_low.num_bits - 1)) /
-             (2 * (ALPS_BITMAP_X_BITS - 1));
-       *y1 = (ALPS_V3_Y_MAX * (2 * y_low.start_bit + y_low.num_bits - 1)) /
-             (2 * (ALPS_BITMAP_Y_BITS - 1));
+       *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
+             (2 * (priv->x_bits - 1));
+       *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
+             (2 * (priv->y_bits - 1));
 
        if (fingers > 1) {
-               *x2 = (ALPS_V3_X_MAX * (2 * x_high.start_bit + x_high.num_bits - 1)) /
-                     (2 * (ALPS_BITMAP_X_BITS - 1));
-               *y2 = (ALPS_V3_Y_MAX * (2 * y_high.start_bit + y_high.num_bits - 1)) /
-                     (2 * (ALPS_BITMAP_Y_BITS - 1));
+               *x2 = (priv->x_max *
+                      (2 * x_high.start_bit + x_high.num_bits - 1)) /
+                     (2 * (priv->x_bits - 1));
+               *y2 = (priv->y_max *
+                      (2 * y_high.start_bit + y_high.num_bits - 1)) /
+                     (2 * (priv->y_bits - 1));
        }
 
        return fingers;
@@ -448,17 +450,57 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
        return;
 }
 
+static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
+{
+       f->left = !!(p[3] & 0x01);
+       f->right = !!(p[3] & 0x02);
+       f->middle = !!(p[3] & 0x04);
+
+       f->ts_left = !!(p[3] & 0x10);
+       f->ts_right = !!(p[3] & 0x20);
+       f->ts_middle = !!(p[3] & 0x40);
+}
+
+static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p)
+{
+       f->first_mp = !!(p[4] & 0x40);
+       f->is_mp = !!(p[0] & 0x40);
+
+       f->fingers = (p[5] & 0x3) + 1;
+       f->x_map = ((p[4] & 0x7e) << 8) |
+                  ((p[1] & 0x7f) << 2) |
+                  ((p[0] & 0x30) >> 4);
+       f->y_map = ((p[3] & 0x70) << 4) |
+                  ((p[2] & 0x7f) << 1) |
+                  (p[4] & 0x01);
+
+       f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
+              ((p[0] & 0x30) >> 4);
+       f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
+       f->z = p[5] & 0x7f;
+
+       alps_decode_buttons_v3(f, p);
+}
+
+static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p)
+{
+       alps_decode_pinnacle(f, p);
+
+       f->x_map |= (p[5] & 0x10) << 11;
+       f->y_map |= (p[5] & 0x20) << 6;
+}
+
 static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
        unsigned char *packet = psmouse->packet;
        struct input_dev *dev = psmouse->dev;
        struct input_dev *dev2 = priv->dev2;
-       int x, y, z;
-       int left, right, middle;
        int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        int fingers = 0, bmap_fingers;
-       unsigned int x_bitmap, y_bitmap;
+       struct alps_fields f;
+
+       priv->decode_fields(&f, packet);
 
        /*
         * There's no single feature of touchpad position and bitmap packets
@@ -473,16 +515,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
                 * packet. Check for this, and when it happens process the
                 * position packet as usual.
                 */
-               if (packet[0] & 0x40) {
-                       fingers = (packet[5] & 0x3) + 1;
-                       x_bitmap = ((packet[4] & 0x7e) << 8) |
-                                  ((packet[1] & 0x7f) << 2) |
-                                  ((packet[0] & 0x30) >> 4);
-                       y_bitmap = ((packet[3] & 0x70) << 4) |
-                                  ((packet[2] & 0x7f) << 1) |
-                                  (packet[4] & 0x01);
-
-                       bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap,
+               if (f.is_mp) {
+                       fingers = f.fingers;
+                       bmap_fingers = alps_process_bitmap(priv,
+                                                          f.x_map, f.y_map,
                                                           &x1, &y1, &x2, &y2);
 
                        /*
@@ -493,7 +529,7 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
                                fingers = bmap_fingers;
 
                        /* Now process position packet */
-                       packet = priv->multi_data;
+                       priv->decode_fields(&f, priv->multi_data);
                } else {
                        priv->multi_packet = 0;
                }
@@ -507,10 +543,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
         * out misidentified bitmap packets, we reject anything with this
         * bit set.
         */
-       if (packet[0] & 0x40)
+       if (f.is_mp)
                return;
 
-       if (!priv->multi_packet && (packet[4] & 0x40)) {
+       if (!priv->multi_packet && f.first_mp) {
                priv->multi_packet = 1;
                memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
                return;
@@ -518,22 +554,13 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
 
        priv->multi_packet = 0;
 
-       left = packet[3] & 0x01;
-       right = packet[3] & 0x02;
-       middle = packet[3] & 0x04;
-
-       x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) |
-           ((packet[0] & 0x30) >> 4);
-       y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f);
-       z = packet[5] & 0x7f;
-
        /*
         * Sometimes the hardware sends a single packet with z = 0
         * in the middle of a stream. Real releases generate packets
         * with x, y, and z all zero, so these seem to be flukes.
         * Ignore them.
         */
-       if (x && y && !z)
+       if (f.x && f.y && !f.z)
                return;
 
        /*
@@ -541,12 +568,12 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
         * to rely on ST data.
         */
        if (!fingers) {
-               x1 = x;
-               y1 = y;
-               fingers = z > 0 ? 1 : 0;
+               x1 = f.x;
+               y1 = f.y;
+               fingers = f.z > 0 ? 1 : 0;
        }
 
-       if (z >= 64)
+       if (f.z >= 64)
                input_report_key(dev, BTN_TOUCH, 1);
        else
                input_report_key(dev, BTN_TOUCH, 0);
@@ -555,26 +582,22 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
 
        input_mt_report_finger_count(dev, fingers);
 
-       input_report_key(dev, BTN_LEFT, left);
-       input_report_key(dev, BTN_RIGHT, right);
-       input_report_key(dev, BTN_MIDDLE, middle);
+       input_report_key(dev, BTN_LEFT, f.left);
+       input_report_key(dev, BTN_RIGHT, f.right);
+       input_report_key(dev, BTN_MIDDLE, f.middle);
 
-       if (z > 0) {
-               input_report_abs(dev, ABS_X, x);
-               input_report_abs(dev, ABS_Y, y);
+       if (f.z > 0) {
+               input_report_abs(dev, ABS_X, f.x);
+               input_report_abs(dev, ABS_Y, f.y);
        }
-       input_report_abs(dev, ABS_PRESSURE, z);
+       input_report_abs(dev, ABS_PRESSURE, f.z);
 
        input_sync(dev);
 
        if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
-               left = packet[3] & 0x10;
-               right = packet[3] & 0x20;
-               middle = packet[3] & 0x40;
-
-               input_report_key(dev2, BTN_LEFT, left);
-               input_report_key(dev2, BTN_RIGHT, right);
-               input_report_key(dev2, BTN_MIDDLE, middle);
+               input_report_key(dev2, BTN_LEFT, f.ts_left);
+               input_report_key(dev2, BTN_RIGHT, f.ts_right);
+               input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
                input_sync(dev2);
        }
 }
@@ -639,7 +662,7 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
                           ((priv->multi_data[3] & 0x1f) << 5) |
                            (priv->multi_data[1] & 0x1f);
 
-               fingers = alps_process_bitmap(x_bitmap, y_bitmap,
+               fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap,
                                              &x1, &y1, &x2, &y2);
 
                /* Store MT data.*/
@@ -696,25 +719,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
        input_sync(dev);
 }
 
-static void alps_process_packet(struct psmouse *psmouse)
-{
-       struct alps_data *priv = psmouse->private;
-       const struct alps_model_info *model = priv->i;
-
-       switch (model->proto_version) {
-       case ALPS_PROTO_V1:
-       case ALPS_PROTO_V2:
-               alps_process_packet_v1_v2(psmouse);
-               break;
-       case ALPS_PROTO_V3:
-               alps_process_packet_v3(psmouse);
-               break;
-       case ALPS_PROTO_V4:
-               alps_process_packet_v4(psmouse);
-               break;
-       }
-}
-
 static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
                                        unsigned char packet[],
                                        bool report_buttons)
@@ -765,14 +769,14 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
                if (((psmouse->packet[3] |
                      psmouse->packet[4] |
                      psmouse->packet[5]) & 0x80) ||
-                   (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) {
+                   (!alps_is_valid_first_byte(priv, psmouse->packet[6]))) {
                        psmouse_dbg(psmouse,
                                    "refusing packet %4ph (suspected interleaved ps/2)\n",
                                    psmouse->packet + 3);
                        return PSMOUSE_BAD_DATA;
                }
 
-               alps_process_packet(psmouse);
+               priv->process_packet(psmouse);
 
                /* Continue with the next packet */
                psmouse->packet[0] = psmouse->packet[6];
@@ -816,6 +820,7 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse)
 static void alps_flush_packet(unsigned long data)
 {
        struct psmouse *psmouse = (struct psmouse *)data;
+       struct alps_data *priv = psmouse->private;
 
        serio_pause_rx(psmouse->ps2dev.serio);
 
@@ -833,7 +838,7 @@ static void alps_flush_packet(unsigned long data)
                                    "refusing packet %3ph (suspected interleaved ps/2)\n",
                                    psmouse->packet + 3);
                } else {
-                       alps_process_packet(psmouse);
+                       priv->process_packet(psmouse);
                }
                psmouse->pktcnt = 0;
        }
@@ -844,7 +849,6 @@ static void alps_flush_packet(unsigned long data)
 static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
-       const struct alps_model_info *model = priv->i;
 
        if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
                if (psmouse->pktcnt == 3) {
@@ -857,15 +861,15 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
 
        /* Check for PS/2 packet stuffed in the middle of ALPS packet. */
 
-       if ((model->flags & ALPS_PS2_INTERLEAVED) &&
+       if ((priv->flags & ALPS_PS2_INTERLEAVED) &&
            psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) {
                return alps_handle_interleaved_ps2(psmouse);
        }
 
-       if (!alps_is_valid_first_byte(model, psmouse->packet[0])) {
+       if (!alps_is_valid_first_byte(priv, psmouse->packet[0])) {
                psmouse_dbg(psmouse,
                            "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n",
-                           psmouse->packet[0], model->mask0, model->byte0);
+                           psmouse->packet[0], priv->mask0, priv->byte0);
                return PSMOUSE_BAD_DATA;
        }
 
@@ -879,7 +883,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
        }
 
        if (psmouse->pktcnt == psmouse->pktsize) {
-               alps_process_packet(psmouse);
+               priv->process_packet(psmouse);
                return PSMOUSE_FULL_PACKET;
        }
 
@@ -967,24 +971,42 @@ static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr,
        return __alps_command_mode_write_reg(psmouse, value);
 }
 
+static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
+                       int repeated_command, unsigned char *param)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+       param[0] = 0;
+       if (init_command && ps2_command(ps2dev, param, init_command))
+               return -EIO;
+
+       if (ps2_command(ps2dev,  NULL, repeated_command) ||
+           ps2_command(ps2dev,  NULL, repeated_command) ||
+           ps2_command(ps2dev,  NULL, repeated_command))
+               return -EIO;
+
+       param[0] = param[1] = param[2] = 0xff;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+               return -EIO;
+
+       psmouse_dbg(psmouse, "%2.2X report: %2.2x %2.2x %2.2x\n",
+                   repeated_command, param[0], param[1], param[2]);
+       return 0;
+}
+
 static int alps_enter_command_mode(struct psmouse *psmouse,
                                   unsigned char *resp)
 {
        unsigned char param[4];
-       struct ps2dev *ps2dev = &psmouse->ps2dev;
 
-       if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
-           ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
-           ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) ||
-           ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+       if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_RESET_WRAP, param)) {
                psmouse_err(psmouse, "failed to enter command mode\n");
                return -1;
        }
 
-       if (param[0] != 0x88 && param[1] != 0x07) {
+       if (param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) {
                psmouse_dbg(psmouse,
-                           "unknown response while entering command mode: %2.2x %2.2x %2.2x\n",
-                           param[0], param[1], param[2]);
+                           "unknown response while entering command mode\n");
                return -1;
        }
 
@@ -1001,99 +1023,6 @@ static inline int alps_exit_command_mode(struct psmouse *psmouse)
        return 0;
 }
 
-static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
-{
-       struct ps2dev *ps2dev = &psmouse->ps2dev;
-       static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
-       unsigned char param[4];
-       const struct alps_model_info *model = NULL;
-       int i;
-
-       /*
-        * First try "E6 report".
-        * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed.
-        * The bits 0-2 of the first byte will be 1s if some buttons are
-        * pressed.
-        */
-       param[0] = 0;
-       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
-           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
-           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
-           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11))
-               return NULL;
-
-       param[0] = param[1] = param[2] = 0xff;
-       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
-               return NULL;
-
-       psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x",
-                   param[0], param[1], param[2]);
-
-       if ((param[0] & 0xf8) != 0 || param[1] != 0 ||
-           (param[2] != 10 && param[2] != 100))
-               return NULL;
-
-       /*
-        * Now try "E7 report". Allowed responses are in
-        * alps_model_data[].signature
-        */
-       param[0] = 0;
-       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
-           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
-           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
-           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21))
-               return NULL;
-
-       param[0] = param[1] = param[2] = 0xff;
-       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
-               return NULL;
-
-       psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x",
-                   param[0], param[1], param[2]);
-
-       if (version) {
-               for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++)
-                       /* empty */;
-               *version = (param[0] << 8) | (param[1] << 4) | i;
-       }
-
-       for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
-               if (!memcmp(param, alps_model_data[i].signature,
-                           sizeof(alps_model_data[i].signature))) {
-                       model = alps_model_data + i;
-                       break;
-               }
-       }
-
-       if (model && model->proto_version > ALPS_PROTO_V2) {
-               /*
-                * Need to check command mode response to identify
-                * model
-                */
-               model = NULL;
-               if (alps_enter_command_mode(psmouse, param)) {
-                       psmouse_warn(psmouse,
-                                    "touchpad failed to enter command mode\n");
-               } else {
-                       for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
-                               if (alps_model_data[i].proto_version > ALPS_PROTO_V2 &&
-                                   alps_model_data[i].command_mode_resp == param[0]) {
-                                       model = alps_model_data + i;
-                                       break;
-                               }
-                       }
-                       alps_exit_command_mode(psmouse);
-
-                       if (!model)
-                               psmouse_dbg(psmouse,
-                                           "Unknown command mode response %2.2x\n",
-                                           param[0]);
-               }
-       }
-
-       return model;
-}
-
 /*
  * For DualPoint devices select the device that should respond to
  * subsequent commands. It looks like glidepad is behind stickpointer,
@@ -1137,18 +1066,10 @@ static int alps_absolute_mode_v1_v2(struct psmouse *psmouse)
 
 static int alps_get_status(struct psmouse *psmouse, char *param)
 {
-       struct ps2dev *ps2dev = &psmouse->ps2dev;
-
        /* Get status: 0xF5 0xF5 0xF5 0xE9 */
-       if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
-           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
-           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
-           ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+       if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_DISABLE, param))
                return -1;
 
-       psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x",
-                   param[0], param[1], param[2]);
-
        return 0;
 }
 
@@ -1190,16 +1111,16 @@ static int alps_poll(struct psmouse *psmouse)
        unsigned char buf[sizeof(psmouse->packet)];
        bool poll_failed;
 
-       if (priv->i->flags & ALPS_PASS)
+       if (priv->flags & ALPS_PASS)
                alps_passthrough_mode_v2(psmouse, true);
 
        poll_failed = ps2_command(&psmouse->ps2dev, buf,
                                  PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0;
 
-       if (priv->i->flags & ALPS_PASS)
+       if (priv->flags & ALPS_PASS)
                alps_passthrough_mode_v2(psmouse, false);
 
-       if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0)
+       if (poll_failed || (buf[0] & priv->mask0) != priv->byte0)
                return -1;
 
        if ((psmouse->badbyte & 0xc8) == 0x08) {
@@ -1217,9 +1138,8 @@ static int alps_poll(struct psmouse *psmouse)
 static int alps_hw_init_v1_v2(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
-       const struct alps_model_info *model = priv->i;
 
-       if ((model->flags & ALPS_PASS) &&
+       if ((priv->flags & ALPS_PASS) &&
            alps_passthrough_mode_v2(psmouse, true)) {
                return -1;
        }
@@ -1234,7 +1154,7 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse)
                return -1;
        }
 
-       if ((model->flags & ALPS_PASS) &&
+       if ((priv->flags & ALPS_PASS) &&
            alps_passthrough_mode_v2(psmouse, false)) {
                return -1;
        }
@@ -1249,26 +1169,31 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse)
 }
 
 /*
- * Enable or disable passthrough mode to the trackstick. Must be in
- * command mode when calling this function.
+ * Enable or disable passthrough mode to the trackstick.
  */
-static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable)
+static int alps_passthrough_mode_v3(struct psmouse *psmouse,
+                                   int reg_base, bool enable)
 {
-       int reg_val;
+       int reg_val, ret = -1;
 
-       reg_val = alps_command_mode_read_reg(psmouse, 0x0008);
-       if (reg_val == -1)
+       if (alps_enter_command_mode(psmouse, NULL))
                return -1;
 
+       reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008);
+       if (reg_val == -1)
+               goto error;
+
        if (enable)
                reg_val |= 0x01;
        else
                reg_val &= ~0x01;
 
-       if (__alps_command_mode_write_reg(psmouse, reg_val))
-               return -1;
+       ret = __alps_command_mode_write_reg(psmouse, reg_val);
 
-       return 0;
+error:
+       if (alps_exit_command_mode(psmouse))
+               ret = -1;
+       return ret;
 }
 
 /* Must be in command mode when calling this function */
@@ -1287,73 +1212,102 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse)
        return 0;
 }
 
-static int alps_hw_init_v3(struct psmouse *psmouse)
+static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
 {
-       struct alps_data *priv = psmouse->private;
-       struct ps2dev *ps2dev = &psmouse->ps2dev;
-       int reg_val;
-       unsigned char param[4];
-
-       priv->nibble_commands = alps_v3_nibble_commands;
-       priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+       int ret = -EIO, reg_val;
 
        if (alps_enter_command_mode(psmouse, NULL))
                goto error;
 
-       /* Check for trackstick */
-       reg_val = alps_command_mode_read_reg(psmouse, 0x0008);
+       reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08);
        if (reg_val == -1)
                goto error;
-       if (reg_val & 0x80) {
-               if (alps_passthrough_mode_v3(psmouse, true))
-                       goto error;
-               if (alps_exit_command_mode(psmouse))
-                       goto error;
+
+       /* bit 7: trackstick is present */
+       ret = reg_val & 0x80 ? 0 : -ENODEV;
+
+error:
+       alps_exit_command_mode(psmouse);
+       return ret;
+}
+
+static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       int ret = 0;
+       unsigned char param[4];
+
+       if (alps_passthrough_mode_v3(psmouse, reg_base, true))
+               return -EIO;
+
+       /*
+        * E7 report for the trackstick
+        *
+        * There have been reports of failures to seem to trace back
+        * to the above trackstick check failing. When these occur
+        * this E7 report fails, so when that happens we continue
+        * with the assumption that there isn't a trackstick after
+        * all.
+        */
+       if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) {
+               psmouse_warn(psmouse, "trackstick E7 report failed\n");
+               ret = -ENODEV;
+       } else {
+               psmouse_dbg(psmouse,
+                           "trackstick E7 report: %2.2x %2.2x %2.2x\n",
+                           param[0], param[1], param[2]);
 
                /*
-                * E7 report for the trackstick
-                *
-                * There have been reports of failures to seem to trace back
-                * to the above trackstick check failing. When these occur
-                * this E7 report fails, so when that happens we continue
-                * with the assumption that there isn't a trackstick after
-                * all.
+                * Not sure what this does, but it is absolutely
+                * essential. Without it, the touchpad does not
+                * work at all and the trackstick just emits normal
+                * PS/2 packets.
                 */
-               param[0] = 0x64;
-               if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
-                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
-                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
-                   ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
-                       psmouse_warn(psmouse, "trackstick E7 report failed\n");
-               } else {
-                       psmouse_dbg(psmouse,
-                                   "trackstick E7 report: %2.2x %2.2x %2.2x\n",
-                                   param[0], param[1], param[2]);
-
-                       /*
-                        * Not sure what this does, but it is absolutely
-                        * essential. Without it, the touchpad does not
-                        * work at all and the trackstick just emits normal
-                        * PS/2 packets.
-                        */
-                       if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
-                           ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
-                           ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
-                           alps_command_mode_send_nibble(psmouse, 0x9) ||
-                           alps_command_mode_send_nibble(psmouse, 0x4)) {
-                               psmouse_err(psmouse,
-                                           "Error sending magic E6 sequence\n");
-                               goto error_passthrough;
-                       }
+               if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+                   alps_command_mode_send_nibble(psmouse, 0x9) ||
+                   alps_command_mode_send_nibble(psmouse, 0x4)) {
+                       psmouse_err(psmouse,
+                                   "Error sending magic E6 sequence\n");
+                       ret = -EIO;
+                       goto error;
                }
 
-               if (alps_enter_command_mode(psmouse, NULL))
-                       goto error_passthrough;
-               if (alps_passthrough_mode_v3(psmouse, false))
-                       goto error;
+               /*
+                * This ensures the trackstick packets are in the format
+                * supported by this driver. If bit 1 isn't set the packet
+                * format is different.
+                */
+               if (alps_enter_command_mode(psmouse, NULL) ||
+                   alps_command_mode_write_reg(psmouse,
+                                               reg_base + 0x08, 0x82) ||
+                   alps_exit_command_mode(psmouse))
+                       ret = -EIO;
        }
 
-       if (alps_absolute_mode_v3(psmouse)) {
+error:
+       if (alps_passthrough_mode_v3(psmouse, reg_base, false))
+               ret = -EIO;
+
+       return ret;
+}
+
+static int alps_hw_init_v3(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       int reg_val;
+       unsigned char param[4];
+
+       reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
+       if (reg_val == -EIO)
+               goto error;
+       if (reg_val == 0 &&
+           alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
+               goto error;
+
+       if (alps_enter_command_mode(psmouse, NULL) ||
+           alps_absolute_mode_v3(psmouse)) {
                psmouse_err(psmouse, "Failed to enter absolute mode\n");
                goto error;
        }
@@ -1390,14 +1344,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse)
        if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04))
                goto error;
 
-       /*
-        * This ensures the trackstick packets are in the format
-        * supported by this driver. If bit 1 isn't set the packet
-        * format is different.
-        */
-       if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82))
-               goto error;
-
        alps_exit_command_mode(psmouse);
 
        /* Set rate and enable data reporting */
@@ -1410,10 +1356,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse)
 
        return 0;
 
-error_passthrough:
-       /* Something failed while in passthrough mode, so try to get out */
-       if (!alps_enter_command_mode(psmouse, NULL))
-               alps_passthrough_mode_v3(psmouse, false);
 error:
        /*
         * Leaving the touchpad in command mode will essentially render
@@ -1424,6 +1366,50 @@ error:
        return -1;
 }
 
+static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
+{
+       struct alps_data *priv = psmouse->private;
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       int reg_val, ret = -1;
+
+       if (priv->flags & ALPS_DUALPOINT) {
+               reg_val = alps_setup_trackstick_v3(psmouse,
+                                                  ALPS_REG_BASE_RUSHMORE);
+               if (reg_val == -EIO)
+                       goto error;
+               if (reg_val == -ENODEV)
+                       priv->flags &= ~ALPS_DUALPOINT;
+       }
+
+       if (alps_enter_command_mode(psmouse, NULL) ||
+           alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 ||
+           alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
+               goto error;
+
+       reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
+       if (reg_val == -1)
+               goto error;
+       if (__alps_command_mode_write_reg(psmouse, reg_val & 0xfd))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
+               goto error;
+
+       /* enter absolute mode */
+       reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
+       if (reg_val == -1)
+               goto error;
+       if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
+               goto error;
+
+       alps_exit_command_mode(psmouse);
+       return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+       alps_exit_command_mode(psmouse);
+       return ret;
+}
+
 /* Must be in command mode when calling this function */
 static int alps_absolute_mode_v4(struct psmouse *psmouse)
 {
@@ -1442,13 +1428,9 @@ static int alps_absolute_mode_v4(struct psmouse *psmouse)
 
 static int alps_hw_init_v4(struct psmouse *psmouse)
 {
-       struct alps_data *priv = psmouse->private;
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char param[4];
 
-       priv->nibble_commands = alps_v4_nibble_commands;
-       priv->addr_command = PSMOUSE_CMD_DISABLE;
-
        if (alps_enter_command_mode(psmouse, NULL))
                goto error;
 
@@ -1517,39 +1499,140 @@ error:
        return -1;
 }
 
-static int alps_hw_init(struct psmouse *psmouse)
+static void alps_set_defaults(struct alps_data *priv)
 {
-       struct alps_data *priv = psmouse->private;
-       const struct alps_model_info *model = priv->i;
-       int ret = -1;
+       priv->byte0 = 0x8f;
+       priv->mask0 = 0x8f;
+       priv->flags = ALPS_DUALPOINT;
+
+       priv->x_max = 2000;
+       priv->y_max = 1400;
+       priv->x_bits = 15;
+       priv->y_bits = 11;
 
-       switch (model->proto_version) {
+       switch (priv->proto_version) {
        case ALPS_PROTO_V1:
        case ALPS_PROTO_V2:
-               ret = alps_hw_init_v1_v2(psmouse);
+               priv->hw_init = alps_hw_init_v1_v2;
+               priv->process_packet = alps_process_packet_v1_v2;
+               priv->set_abs_params = alps_set_abs_params_st;
                break;
        case ALPS_PROTO_V3:
-               ret = alps_hw_init_v3(psmouse);
+               priv->hw_init = alps_hw_init_v3;
+               priv->process_packet = alps_process_packet_v3;
+               priv->set_abs_params = alps_set_abs_params_mt;
+               priv->decode_fields = alps_decode_pinnacle;
+               priv->nibble_commands = alps_v3_nibble_commands;
+               priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
                break;
        case ALPS_PROTO_V4:
-               ret = alps_hw_init_v4(psmouse);
+               priv->hw_init = alps_hw_init_v4;
+               priv->process_packet = alps_process_packet_v4;
+               priv->set_abs_params = alps_set_abs_params_mt;
+               priv->nibble_commands = alps_v4_nibble_commands;
+               priv->addr_command = PSMOUSE_CMD_DISABLE;
                break;
        }
+}
 
-       return ret;
+static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv,
+                           unsigned char *e7, unsigned char *ec)
+{
+       const struct alps_model_info *model;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
+               model = &alps_model_data[i];
+
+               if (!memcmp(e7, model->signature, sizeof(model->signature)) &&
+                   (!model->command_mode_resp ||
+                    model->command_mode_resp == ec[2])) {
+
+                       priv->proto_version = model->proto_version;
+                       alps_set_defaults(priv);
+
+                       priv->flags = model->flags;
+                       priv->byte0 = model->byte0;
+                       priv->mask0 = model->mask0;
+
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
+{
+       unsigned char e6[4], e7[4], ec[4];
+
+       /*
+        * First try "E6 report".
+        * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed.
+        * The bits 0-2 of the first byte will be 1s if some buttons are
+        * pressed.
+        */
+       if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+                        PSMOUSE_CMD_SETSCALE11, e6))
+               return -EIO;
+
+       if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10 && e6[2] != 100))
+               return -EINVAL;
+
+       /*
+        * Now get the "E7" and "EC" reports.  These will uniquely identify
+        * most ALPS touchpads.
+        */
+       if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+                        PSMOUSE_CMD_SETSCALE21, e7) ||
+           alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
+                        PSMOUSE_CMD_RESET_WRAP, ec) ||
+           alps_exit_command_mode(psmouse))
+               return -EIO;
+
+       if (alps_match_table(psmouse, priv, e7, ec) == 0) {
+               return 0;
+       } else if (ec[0] == 0x88 && ec[1] == 0x08) {
+               priv->proto_version = ALPS_PROTO_V3;
+               alps_set_defaults(priv);
+
+               priv->hw_init = alps_hw_init_rushmore_v3;
+               priv->decode_fields = alps_decode_rushmore;
+               priv->x_bits = 16;
+               priv->y_bits = 12;
+
+               /* hack to make addr_command, nibble_command available */
+               psmouse->private = priv;
+
+               if (alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_RUSHMORE))
+                       priv->flags &= ~ALPS_DUALPOINT;
+
+               return 0;
+       } else if (ec[0] == 0x88 && ec[1] == 0x07 &&
+                  ec[2] >= 0x90 && ec[2] <= 0x9d) {
+               priv->proto_version = ALPS_PROTO_V3;
+               alps_set_defaults(priv);
+
+               return 0;
+       }
+
+       psmouse_info(psmouse,
+               "Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n",
+               e7[0], e7[1], e7[2], ec[0], ec[1], ec[2]);
+
+       return -EINVAL;
 }
 
 static int alps_reconnect(struct psmouse *psmouse)
 {
-       const struct alps_model_info *model;
+       struct alps_data *priv = psmouse->private;
 
        psmouse_reset(psmouse);
 
-       model = alps_get_model(psmouse, NULL);
-       if (!model)
+       if (alps_identify(psmouse, priv) < 0)
                return -1;
 
-       return alps_hw_init(psmouse);
+       return priv->hw_init(psmouse);
 }
 
 static void alps_disconnect(struct psmouse *psmouse)
@@ -1562,12 +1645,33 @@ static void alps_disconnect(struct psmouse *psmouse)
        kfree(priv);
 }
 
+static void alps_set_abs_params_st(struct alps_data *priv,
+                                  struct input_dev *dev1)
+{
+       input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
+       input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
+}
+
+static void alps_set_abs_params_mt(struct alps_data *priv,
+                                  struct input_dev *dev1)
+{
+       set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
+       input_mt_init_slots(dev1, 2, 0);
+       input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
+       input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
+
+       set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
+       set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
+       set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
+
+       input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
+       input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
+}
+
 int alps_init(struct psmouse *psmouse)
 {
        struct alps_data *priv;
-       const struct alps_model_info *model;
        struct input_dev *dev1 = psmouse->dev, *dev2;
-       int version;
 
        priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
        dev2 = input_allocate_device();
@@ -1581,13 +1685,10 @@ int alps_init(struct psmouse *psmouse)
 
        psmouse_reset(psmouse);
 
-       model = alps_get_model(psmouse, &version);
-       if (!model)
+       if (alps_identify(psmouse, priv) < 0)
                goto init_fail;
 
-       priv->i = model;
-
-       if (alps_hw_init(psmouse))
+       if (priv->hw_init(psmouse))
                goto init_fail;
 
        /*
@@ -1609,41 +1710,20 @@ int alps_init(struct psmouse *psmouse)
 
        dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
 
-       switch (model->proto_version) {
-       case ALPS_PROTO_V1:
-       case ALPS_PROTO_V2:
-               input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
-               input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
-               break;
-       case ALPS_PROTO_V3:
-       case ALPS_PROTO_V4:
-               set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
-               input_mt_init_slots(dev1, 2, 0);
-               input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
-               input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0);
-
-               set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
-               set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
-               set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
-
-               input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
-               input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
-               break;
-       }
-
+       priv->set_abs_params(priv, dev1);
        input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
 
-       if (model->flags & ALPS_WHEEL) {
+       if (priv->flags & ALPS_WHEEL) {
                dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
                dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);
        }
 
-       if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+       if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
                dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);
                dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);
        }
 
-       if (model->flags & ALPS_FOUR_BUTTONS) {
+       if (priv->flags & ALPS_FOUR_BUTTONS) {
                dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);
                dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
                dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
@@ -1654,7 +1734,8 @@ int alps_init(struct psmouse *psmouse)
 
        snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
        dev2->phys = priv->phys;
-       dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
+       dev2->name = (priv->flags & ALPS_DUALPOINT) ?
+                    "DualPoint Stick" : "PS/2 Mouse";
        dev2->id.bustype = BUS_I8042;
        dev2->id.vendor  = 0x0002;
        dev2->id.product = PSMOUSE_ALPS;
@@ -1673,7 +1754,7 @@ int alps_init(struct psmouse *psmouse)
        psmouse->poll = alps_poll;
        psmouse->disconnect = alps_disconnect;
        psmouse->reconnect = alps_reconnect;
-       psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6;
+       psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6;
 
        /* We are having trouble resyncing ALPS touchpads so disable it for now */
        psmouse->resync_time = 0;
@@ -1690,18 +1771,16 @@ init_fail:
 
 int alps_detect(struct psmouse *psmouse, bool set_properties)
 {
-       int version;
-       const struct alps_model_info *model;
+       struct alps_data dummy;
 
-       model = alps_get_model(psmouse, &version);
-       if (!model)
+       if (alps_identify(psmouse, &dummy) < 0)
                return -1;
 
        if (set_properties) {
                psmouse->vendor = "ALPS";
-               psmouse->name = model->flags & ALPS_DUALPOINT ?
+               psmouse->name = dummy.flags & ALPS_DUALPOINT ?
                                "DualPoint TouchPad" : "GlidePoint";
-               psmouse->model = version;
+               psmouse->model = dummy.proto_version << 8;
        }
        return 0;
 }
index ae1ac354c7787a6a9fd2c5430debcfead8b7155b..970480551b6e67e540fdb76b2865e8dd573d550a 100644 (file)
 #ifndef _ALPS_H
 #define _ALPS_H
 
-#define ALPS_PROTO_V1  0
-#define ALPS_PROTO_V2  1
-#define ALPS_PROTO_V3  2
-#define ALPS_PROTO_V4  3
+#define ALPS_PROTO_V1  1
+#define ALPS_PROTO_V2  2
+#define ALPS_PROTO_V3  3
+#define ALPS_PROTO_V4  4
 
+/**
+ * struct alps_model_info - touchpad ID table
+ * @signature: E7 response string to match.
+ * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
+ *   (aka command mode response) identifies the firmware minor version.  This
+ *   can be used to distinguish different hardware models which are not
+ *   uniquely identifiable through their E7 responses.
+ * @proto_version: Indicates V1/V2/V3/...
+ * @byte0: Helps figure out whether a position report packet matches the
+ *   known format for this model.  The first byte of the report, ANDed with
+ *   mask0, should match byte0.
+ * @mask0: The mask used to check the first byte of the report.
+ * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
+ *
+ * Many (but not all) ALPS touchpads can be identified by looking at the
+ * values returned in the "E7 report" and/or the "EC report."  This table
+ * lists a number of such touchpads.
+ */
 struct alps_model_info {
-        unsigned char signature[3];
-       unsigned char command_mode_resp; /* v3/v4 only */
+       unsigned char signature[3];
+       unsigned char command_mode_resp;
        unsigned char proto_version;
-        unsigned char byte0, mask0;
-        unsigned char flags;
+       unsigned char byte0, mask0;
+       unsigned char flags;
 };
 
+/**
+ * struct alps_nibble_commands - encodings for register accesses
+ * @command: PS/2 command used for the nibble
+ * @data: Data supplied as an argument to the PS/2 command, if applicable
+ *
+ * The ALPS protocol uses magic sequences to transmit binary data to the
+ * touchpad, as it is generally not OK to send arbitrary bytes out the
+ * PS/2 port.  Each of the sequences in this table sends one nibble of the
+ * register address or (write) data.  Different versions of the ALPS protocol
+ * use slightly different encodings.
+ */
 struct alps_nibble_commands {
        int command;
        unsigned char data;
 };
 
+/**
+ * struct alps_fields - decoded version of the report packet
+ * @x_map: Bitmap of active X positions for MT.
+ * @y_map: Bitmap of active Y positions for MT.
+ * @fingers: Number of fingers for MT.
+ * @x: X position for ST.
+ * @y: Y position for ST.
+ * @z: Z position for ST.
+ * @first_mp: Packet is the first of a multi-packet report.
+ * @is_mp: Packet is part of a multi-packet report.
+ * @left: Left touchpad button is active.
+ * @right: Right touchpad button is active.
+ * @middle: Middle touchpad button is active.
+ * @ts_left: Left trackstick button is active.
+ * @ts_right: Right trackstick button is active.
+ * @ts_middle: Middle trackstick button is active.
+ */
+struct alps_fields {
+       unsigned int x_map;
+       unsigned int y_map;
+       unsigned int fingers;
+       unsigned int x;
+       unsigned int y;
+       unsigned int z;
+       unsigned int first_mp:1;
+       unsigned int is_mp:1;
+
+       unsigned int left:1;
+       unsigned int right:1;
+       unsigned int middle:1;
+
+       unsigned int ts_left:1;
+       unsigned int ts_right:1;
+       unsigned int ts_middle:1;
+};
+
+/**
+ * struct alps_data - private data structure for the ALPS driver
+ * @dev2: "Relative" device used to report trackstick or mouse activity.
+ * @phys: Physical path for the relative device.
+ * @nibble_commands: Command mapping used for touchpad register accesses.
+ * @addr_command: Command used to tell the touchpad that a register address
+ *   follows.
+ * @proto_version: Indicates V1/V2/V3/...
+ * @byte0: Helps figure out whether a position report packet matches the
+ *   known format for this model.  The first byte of the report, ANDed with
+ *   mask0, should match byte0.
+ * @mask0: The mask used to check the first byte of the report.
+ * @flags: Additional device capabilities (passthrough port, trackstick, etc.).
+ * @x_max: Largest possible X position value.
+ * @y_max: Largest possible Y position value.
+ * @x_bits: Number of X bits in the MT bitmap.
+ * @y_bits: Number of Y bits in the MT bitmap.
+ * @hw_init: Protocol-specific hardware init function.
+ * @process_packet: Protocol-specific function to process a report packet.
+ * @decode_fields: Protocol-specific function to read packet bitfields.
+ * @set_abs_params: Protocol-specific function to configure the input_dev.
+ * @prev_fin: Finger bit from previous packet.
+ * @multi_packet: Multi-packet data in progress.
+ * @multi_data: Saved multi-packet data.
+ * @x1: First X coordinate from last MT report.
+ * @x2: Second X coordinate from last MT report.
+ * @y1: First Y coordinate from last MT report.
+ * @y2: Second Y coordinate from last MT report.
+ * @fingers: Number of fingers from last MT report.
+ * @quirks: Bitmap of ALPS_QUIRK_*.
+ * @timer: Timer for flushing out the final report packet in the stream.
+ */
 struct alps_data {
-       struct input_dev *dev2;         /* Relative device */
-       char phys[32];                  /* Phys */
-       const struct alps_model_info *i;/* Info */
+       struct input_dev *dev2;
+       char phys[32];
+
+       /* these are autodetected when the device is identified */
        const struct alps_nibble_commands *nibble_commands;
-       int addr_command;               /* Command to set register address */
-       int prev_fin;                   /* Finger bit from previous packet */
-       int multi_packet;               /* Multi-packet data in progress */
-       unsigned char multi_data[6];    /* Saved multi-packet data */
-       int x1, x2, y1, y2;             /* Coordinates from last MT report */
-       int fingers;                    /* Number of fingers from MT report */
+       int addr_command;
+       unsigned char proto_version;
+       unsigned char byte0, mask0;
+       unsigned char flags;
+       int x_max;
+       int y_max;
+       int x_bits;
+       int y_bits;
+
+       int (*hw_init)(struct psmouse *psmouse);
+       void (*process_packet)(struct psmouse *psmouse);
+       void (*decode_fields)(struct alps_fields *f, unsigned char *p);
+       void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
+
+       int prev_fin;
+       int multi_packet;
+       unsigned char multi_data[6];
+       int x1, x2, y1, y2;
+       int fingers;
        u8 quirks;
        struct timer_list timer;
 };
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
new file mode 100644 (file)
index 0000000..b409c3d
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+ * Cypress APA trackpad with I2C interface
+ *
+ * Author: Dudley Du <dudl@cypress.com>
+ * Further cleanup and restructuring by:
+ *   Daniel Kurtz <djkurtz@chromium.org>
+ *   Benson Leung <bleung@chromium.org>
+ *
+ * Copyright (C) 2011-2012 Cypress Semiconductor, Inc.
+ * Copyright (C) 2011-2012 Google, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+/* APA trackpad firmware generation */
+#define CYAPA_GEN3   0x03   /* support MT-protocol B with tracking ID. */
+
+#define CYAPA_NAME   "Cypress APA Trackpad (cyapa)"
+
+/* commands for read/write registers of Cypress trackpad */
+#define CYAPA_CMD_SOFT_RESET       0x00
+#define CYAPA_CMD_POWER_MODE       0x01
+#define CYAPA_CMD_DEV_STATUS       0x02
+#define CYAPA_CMD_GROUP_DATA       0x03
+#define CYAPA_CMD_GROUP_CMD        0x04
+#define CYAPA_CMD_GROUP_QUERY      0x05
+#define CYAPA_CMD_BL_STATUS        0x06
+#define CYAPA_CMD_BL_HEAD          0x07
+#define CYAPA_CMD_BL_CMD           0x08
+#define CYAPA_CMD_BL_DATA          0x09
+#define CYAPA_CMD_BL_ALL           0x0a
+#define CYAPA_CMD_BLK_PRODUCT_ID   0x0b
+#define CYAPA_CMD_BLK_HEAD         0x0c
+
+/* report data start reg offset address. */
+#define DATA_REG_START_OFFSET  0x0000
+
+#define BL_HEAD_OFFSET 0x00
+#define BL_DATA_OFFSET 0x10
+
+/*
+ * Operational Device Status Register
+ *
+ * bit 7: Valid interrupt source
+ * bit 6 - 4: Reserved
+ * bit 3 - 2: Power status
+ * bit 1 - 0: Device status
+ */
+#define REG_OP_STATUS     0x00
+#define OP_STATUS_SRC     0x80
+#define OP_STATUS_POWER   0x0c
+#define OP_STATUS_DEV     0x03
+#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV)
+
+/*
+ * Operational Finger Count/Button Flags Register
+ *
+ * bit 7 - 4: Number of touched finger
+ * bit 3: Valid data
+ * bit 2: Middle Physical Button
+ * bit 1: Right Physical Button
+ * bit 0: Left physical Button
+ */
+#define REG_OP_DATA1       0x01
+#define OP_DATA_VALID      0x08
+#define OP_DATA_MIDDLE_BTN 0x04
+#define OP_DATA_RIGHT_BTN  0x02
+#define OP_DATA_LEFT_BTN   0x01
+#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \
+                         OP_DATA_LEFT_BTN)
+
+/*
+ * Bootloader Status Register
+ *
+ * bit 7: Busy
+ * bit 6 - 5: Reserved
+ * bit 4: Bootloader running
+ * bit 3 - 1: Reserved
+ * bit 0: Checksum valid
+ */
+#define REG_BL_STATUS        0x01
+#define BL_STATUS_BUSY       0x80
+#define BL_STATUS_RUNNING    0x10
+#define BL_STATUS_DATA_VALID 0x08
+#define BL_STATUS_CSUM_VALID 0x01
+
+/*
+ * Bootloader Error Register
+ *
+ * bit 7: Invalid
+ * bit 6: Invalid security key
+ * bit 5: Bootloading
+ * bit 4: Command checksum
+ * bit 3: Flash protection error
+ * bit 2: Flash checksum error
+ * bit 1 - 0: Reserved
+ */
+#define REG_BL_ERROR         0x02
+#define BL_ERROR_INVALID     0x80
+#define BL_ERROR_INVALID_KEY 0x40
+#define BL_ERROR_BOOTLOADING 0x20
+#define BL_ERROR_CMD_CSUM    0x10
+#define BL_ERROR_FLASH_PROT  0x08
+#define BL_ERROR_FLASH_CSUM  0x04
+
+#define BL_STATUS_SIZE  3  /* length of bootloader status registers */
+#define BLK_HEAD_BYTES 32
+
+#define PRODUCT_ID_SIZE  16
+#define QUERY_DATA_SIZE  27
+#define REG_PROTOCOL_GEN_QUERY_OFFSET  20
+
+#define REG_OFFSET_DATA_BASE     0x0000
+#define REG_OFFSET_COMMAND_BASE  0x0028
+#define REG_OFFSET_QUERY_BASE    0x002a
+
+#define CAPABILITY_LEFT_BTN_MASK       (0x01 << 3)
+#define CAPABILITY_RIGHT_BTN_MASK      (0x01 << 4)
+#define CAPABILITY_MIDDLE_BTN_MASK     (0x01 << 5)
+#define CAPABILITY_BTN_MASK  (CAPABILITY_LEFT_BTN_MASK | \
+                             CAPABILITY_RIGHT_BTN_MASK | \
+                             CAPABILITY_MIDDLE_BTN_MASK)
+
+#define CYAPA_OFFSET_SOFT_RESET  REG_OFFSET_COMMAND_BASE
+
+#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1)
+
+#define PWR_MODE_MASK   0xfc
+#define PWR_MODE_FULL_ACTIVE (0x3f << 2)
+#define PWR_MODE_IDLE        (0x05 << 2) /* default sleep time is 50 ms. */
+#define PWR_MODE_OFF         (0x00 << 2)
+
+#define PWR_STATUS_MASK      0x0c
+#define PWR_STATUS_ACTIVE    (0x03 << 2)
+#define PWR_STATUS_IDLE      (0x02 << 2)
+#define PWR_STATUS_OFF       (0x00 << 2)
+
+/*
+ * CYAPA trackpad device states.
+ * Used in register 0x00, bit1-0, DeviceStatus field.
+ * Other values indicate device is in an abnormal state and must be reset.
+ */
+#define CYAPA_DEV_NORMAL  0x03
+#define CYAPA_DEV_BUSY    0x01
+
+enum cyapa_state {
+       CYAPA_STATE_OP,
+       CYAPA_STATE_BL_IDLE,
+       CYAPA_STATE_BL_ACTIVE,
+       CYAPA_STATE_BL_BUSY,
+       CYAPA_STATE_NO_DEVICE,
+};
+
+
+struct cyapa_touch {
+       /*
+        * high bits or x/y position value
+        * bit 7 - 4: high 4 bits of x position value
+        * bit 3 - 0: high 4 bits of y position value
+        */
+       u8 xy_hi;
+       u8 x_lo;  /* low 8 bits of x position value. */
+       u8 y_lo;  /* low 8 bits of y position value. */
+       u8 pressure;
+       /* id range is 1 - 15.  It is incremented with every new touch. */
+       u8 id;
+} __packed;
+
+/* The touch.id is used as the MT slot id, thus max MT slot is 15 */
+#define CYAPA_MAX_MT_SLOTS  15
+
+struct cyapa_reg_data {
+       /*
+        * bit 0 - 1: device status
+        * bit 3 - 2: power mode
+        * bit 6 - 4: reserved
+        * bit 7: interrupt valid bit
+        */
+       u8 device_status;
+       /*
+        * bit 7 - 4: number of fingers currently touching pad
+        * bit 3: valid data check bit
+        * bit 2: middle mechanism button state if exists
+        * bit 1: right mechanism button state if exists
+        * bit 0: left mechanism button state if exists
+        */
+       u8 finger_btn;
+       /* CYAPA reports up to 5 touches per packet. */
+       struct cyapa_touch touches[5];
+} __packed;
+
+/* The main device structure */
+struct cyapa {
+       enum cyapa_state state;
+
+       struct i2c_client *client;
+       struct input_dev *input;
+       char phys[32];  /* device physical location */
+       int irq;
+       bool irq_wake;  /* irq wake is enabled */
+       bool smbus;
+
+       /* read from query data region. */
+       char product_id[16];
+       u8 btn_capability;
+       u8 gen;
+       int max_abs_x;
+       int max_abs_y;
+       int physical_size_x;
+       int physical_size_y;
+};
+
+static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03,
+               0x04, 0x05, 0x06, 0x07 };
+static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04,
+               0x05, 0x06, 0x07 };
+
+struct cyapa_cmd_len {
+       u8 cmd;
+       u8 len;
+};
+
+#define CYAPA_ADAPTER_FUNC_NONE   0
+#define CYAPA_ADAPTER_FUNC_I2C    1
+#define CYAPA_ADAPTER_FUNC_SMBUS  2
+#define CYAPA_ADAPTER_FUNC_BOTH   3
+
+/*
+ * macros for SMBus communication
+ */
+#define SMBUS_READ   0x01
+#define SMBUS_WRITE 0x00
+#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1))
+#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01))
+#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80
+#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40
+
+ /* for byte read/write command */
+#define CMD_RESET 0
+#define CMD_POWER_MODE 1
+#define CMD_DEV_STATUS 2
+#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1)
+#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET)
+#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE)
+#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS)
+
+ /* for group registers read/write command */
+#define REG_GROUP_DATA 0
+#define REG_GROUP_CMD 2
+#define REG_GROUP_QUERY 3
+#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3))
+#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA)
+#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD)
+#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY)
+
+ /* for register block read/write command */
+#define CMD_BL_STATUS 0
+#define CMD_BL_HEAD 1
+#define CMD_BL_CMD 2
+#define CMD_BL_DATA 3
+#define CMD_BL_ALL 4
+#define CMD_BLK_PRODUCT_ID 5
+#define CMD_BLK_HEAD 6
+#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1))
+
+/* register block read/write command in bootloader mode */
+#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS)
+#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD)
+#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD)
+#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA)
+#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL)
+
+/* register block read/write command in operational mode */
+#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID)
+#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD)
+
+static const struct cyapa_cmd_len cyapa_i2c_cmds[] = {
+       { CYAPA_OFFSET_SOFT_RESET, 1 },
+       { REG_OFFSET_COMMAND_BASE + 1, 1 },
+       { REG_OFFSET_DATA_BASE, 1 },
+       { REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) },
+       { REG_OFFSET_COMMAND_BASE, 0 },
+       { REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE },
+       { BL_HEAD_OFFSET, 3 },
+       { BL_HEAD_OFFSET, 16 },
+       { BL_HEAD_OFFSET, 16 },
+       { BL_DATA_OFFSET, 16 },
+       { BL_HEAD_OFFSET, 32 },
+       { REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE },
+       { REG_OFFSET_DATA_BASE, 32 }
+};
+
+static const struct cyapa_cmd_len cyapa_smbus_cmds[] = {
+       { CYAPA_SMBUS_RESET, 1 },
+       { CYAPA_SMBUS_POWER_MODE, 1 },
+       { CYAPA_SMBUS_DEV_STATUS, 1 },
+       { CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) },
+       { CYAPA_SMBUS_GROUP_CMD, 2 },
+       { CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE },
+       { CYAPA_SMBUS_BL_STATUS, 3 },
+       { CYAPA_SMBUS_BL_HEAD, 16 },
+       { CYAPA_SMBUS_BL_CMD, 16 },
+       { CYAPA_SMBUS_BL_DATA, 16 },
+       { CYAPA_SMBUS_BL_ALL, 32 },
+       { CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE },
+       { CYAPA_SMBUS_BLK_HEAD, 16 },
+};
+
+static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len,
+                                       u8 *values)
+{
+       return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values);
+}
+
+static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg,
+                                        size_t len, const u8 *values)
+{
+       return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values);
+}
+
+/*
+ * cyapa_smbus_read_block - perform smbus block read command
+ * @cyapa  - private data structure of the driver
+ * @cmd    - the properly encoded smbus command
+ * @len    - expected length of smbus command result
+ * @values - buffer to store smbus command result
+ *
+ * Returns negative errno, else the number of bytes written.
+ *
+ * Note:
+ * In trackpad device, the memory block allocated for I2C register map
+ * is 256 bytes, so the max read block for I2C bus is 256 bytes.
+ */
+static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len,
+                                     u8 *values)
+{
+       ssize_t ret;
+       u8 index;
+       u8 smbus_cmd;
+       u8 *buf;
+       struct i2c_client *client = cyapa->client;
+
+       if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd))
+               return -EINVAL;
+
+       if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) {
+               /* read specific block registers command. */
+               smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
+               ret = i2c_smbus_read_block_data(client, smbus_cmd, values);
+               goto out;
+       }
+
+       ret = 0;
+       for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) {
+               smbus_cmd = SMBUS_ENCODE_IDX(cmd, index);
+               smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ);
+               buf = values + I2C_SMBUS_BLOCK_MAX * index;
+               ret = i2c_smbus_read_block_data(client, smbus_cmd, buf);
+               if (ret < 0)
+                       goto out;
+       }
+
+out:
+       return ret > 0 ? len : ret;
+}
+
+static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx)
+{
+       u8 cmd;
+
+       if (cyapa->smbus) {
+               cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+               cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ);
+       } else {
+               cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+       }
+       return i2c_smbus_read_byte_data(cyapa->client, cmd);
+}
+
+static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value)
+{
+       u8 cmd;
+
+       if (cyapa->smbus) {
+               cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+               cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE);
+       } else {
+               cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+       }
+       return i2c_smbus_write_byte_data(cyapa->client, cmd, value);
+}
+
+static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values)
+{
+       u8 cmd;
+       size_t len;
+
+       if (cyapa->smbus) {
+               cmd = cyapa_smbus_cmds[cmd_idx].cmd;
+               len = cyapa_smbus_cmds[cmd_idx].len;
+               return cyapa_smbus_read_block(cyapa, cmd, len, values);
+       } else {
+               cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+               len = cyapa_i2c_cmds[cmd_idx].len;
+               return cyapa_i2c_reg_read_block(cyapa, cmd, len, values);
+       }
+}
+
+/*
+ * Query device for its current operating state.
+ *
+ */
+static int cyapa_get_state(struct cyapa *cyapa)
+{
+       int ret;
+       u8 status[BL_STATUS_SIZE];
+
+       cyapa->state = CYAPA_STATE_NO_DEVICE;
+
+       /*
+        * Get trackpad status by reading 3 registers starting from 0.
+        * If the device is in the bootloader, this will be BL_HEAD.
+        * If the device is in operation mode, this will be the DATA regs.
+        *
+        */
+       ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE,
+                                      status);
+
+       /*
+        * On smbus systems in OP mode, the i2c_reg_read will fail with
+        * -ETIMEDOUT.  In this case, try again using the smbus equivalent
+        * command.  This should return a BL_HEAD indicating CYAPA_STATE_OP.
+        */
+       if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO))
+               ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status);
+
+       if (ret != BL_STATUS_SIZE)
+               goto error;
+
+       if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) {
+               switch (status[REG_OP_STATUS] & OP_STATUS_DEV) {
+               case CYAPA_DEV_NORMAL:
+               case CYAPA_DEV_BUSY:
+                       cyapa->state = CYAPA_STATE_OP;
+                       break;
+               default:
+                       ret = -EAGAIN;
+                       goto error;
+               }
+       } else {
+               if (status[REG_BL_STATUS] & BL_STATUS_BUSY)
+                       cyapa->state = CYAPA_STATE_BL_BUSY;
+               else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING)
+                       cyapa->state = CYAPA_STATE_BL_ACTIVE;
+               else
+                       cyapa->state = CYAPA_STATE_BL_IDLE;
+       }
+
+       return 0;
+error:
+       return (ret < 0) ? ret : -EAGAIN;
+}
+
+/*
+ * Poll device for its status in a loop, waiting up to timeout for a response.
+ *
+ * When the device switches state, it usually takes ~300 ms.
+ * However, when running a new firmware image, the device must calibrate its
+ * sensors, which can take as long as 2 seconds.
+ *
+ * Note: The timeout has granularity of the polling rate, which is 100 ms.
+ *
+ * Returns:
+ *   0 when the device eventually responds with a valid non-busy state.
+ *   -ETIMEDOUT if device never responds (too many -EAGAIN)
+ *   < 0    other errors
+ */
+static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout)
+{
+       int ret;
+       int tries = timeout / 100;
+
+       ret = cyapa_get_state(cyapa);
+       while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) {
+               msleep(100);
+               ret = cyapa_get_state(cyapa);
+       }
+       return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret;
+}
+
+static int cyapa_bl_deactivate(struct cyapa *cyapa)
+{
+       int ret;
+
+       ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate),
+                                       bl_deactivate);
+       if (ret < 0)
+               return ret;
+
+       /* wait for bootloader to switch to idle state; should take < 100ms */
+       msleep(100);
+       ret = cyapa_poll_state(cyapa, 500);
+       if (ret < 0)
+               return ret;
+       if (cyapa->state != CYAPA_STATE_BL_IDLE)
+               return -EAGAIN;
+       return 0;
+}
+
+/*
+ * Exit bootloader
+ *
+ * Send bl_exit command, then wait 50 - 100 ms to let device transition to
+ * operational mode.  If this is the first time the device's firmware is
+ * running, it can take up to 2 seconds to calibrate its sensors.  So, poll
+ * the device's new state for up to 2 seconds.
+ *
+ * Returns:
+ *   -EIO    failure while reading from device
+ *   -EAGAIN device is stuck in bootloader, b/c it has invalid firmware
+ *   0       device is supported and in operational mode
+ */
+static int cyapa_bl_exit(struct cyapa *cyapa)
+{
+       int ret;
+
+       ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Wait for bootloader to exit, and operation mode to start.
+        * Normally, this takes at least 50 ms.
+        */
+       usleep_range(50000, 100000);
+       /*
+        * In addition, when a device boots for the first time after being
+        * updated to new firmware, it must first calibrate its sensors, which
+        * can take up to an additional 2 seconds.
+        */
+       ret = cyapa_poll_state(cyapa, 2000);
+       if (ret < 0)
+               return ret;
+       if (cyapa->state != CYAPA_STATE_OP)
+               return -EAGAIN;
+
+       return 0;
+}
+
+/*
+ * Set device power mode
+ *
+ */
+static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode)
+{
+       struct device *dev = &cyapa->client->dev;
+       int ret;
+       u8 power;
+
+       if (cyapa->state != CYAPA_STATE_OP)
+               return 0;
+
+       ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE);
+       if (ret < 0)
+               return ret;
+
+       power = ret & ~PWR_MODE_MASK;
+       power |= power_mode & PWR_MODE_MASK;
+       ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power);
+       if (ret < 0)
+               dev_err(dev, "failed to set power_mode 0x%02x err = %d\n",
+                       power_mode, ret);
+       return ret;
+}
+
+static int cyapa_get_query_data(struct cyapa *cyapa)
+{
+       u8 query_data[QUERY_DATA_SIZE];
+       int ret;
+
+       if (cyapa->state != CYAPA_STATE_OP)
+               return -EBUSY;
+
+       ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data);
+       if (ret < 0)
+               return ret;
+       if (ret != QUERY_DATA_SIZE)
+               return -EIO;
+
+       memcpy(&cyapa->product_id[0], &query_data[0], 5);
+       cyapa->product_id[5] = '-';
+       memcpy(&cyapa->product_id[6], &query_data[5], 6);
+       cyapa->product_id[12] = '-';
+       memcpy(&cyapa->product_id[13], &query_data[11], 2);
+       cyapa->product_id[15] = '\0';
+
+       cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK;
+
+       cyapa->gen = query_data[20] & 0x0f;
+
+       cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22];
+       cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23];
+
+       cyapa->physical_size_x =
+               ((query_data[24] & 0xf0) << 4) | query_data[25];
+       cyapa->physical_size_y =
+               ((query_data[24] & 0x0f) << 8) | query_data[26];
+
+       return 0;
+}
+
+/*
+ * Check if device is operational.
+ *
+ * An operational device is responding, has exited bootloader, and has
+ * firmware supported by this driver.
+ *
+ * Returns:
+ *   -EBUSY  no device or in bootloader
+ *   -EIO    failure while reading from device
+ *   -EAGAIN device is still in bootloader
+ *           if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware
+ *   -EINVAL device is in operational mode, but not supported by this driver
+ *   0       device is supported
+ */
+static int cyapa_check_is_operational(struct cyapa *cyapa)
+{
+       struct device *dev = &cyapa->client->dev;
+       static const char unique_str[] = "CYTRA";
+       int ret;
+
+       ret = cyapa_poll_state(cyapa, 2000);
+       if (ret < 0)
+               return ret;
+       switch (cyapa->state) {
+       case CYAPA_STATE_BL_ACTIVE:
+               ret = cyapa_bl_deactivate(cyapa);
+               if (ret)
+                       return ret;
+
+       /* Fallthrough state */
+       case CYAPA_STATE_BL_IDLE:
+               ret = cyapa_bl_exit(cyapa);
+               if (ret)
+                       return ret;
+
+       /* Fallthrough state */
+       case CYAPA_STATE_OP:
+               ret = cyapa_get_query_data(cyapa);
+               if (ret < 0)
+                       return ret;
+
+               /* only support firmware protocol gen3 */
+               if (cyapa->gen != CYAPA_GEN3) {
+                       dev_err(dev, "unsupported protocol version (%d)",
+                               cyapa->gen);
+                       return -EINVAL;
+               }
+
+               /* only support product ID starting with CYTRA */
+               if (memcmp(cyapa->product_id, unique_str,
+                          sizeof(unique_str) - 1) != 0) {
+                       dev_err(dev, "unsupported product ID (%s)\n",
+                               cyapa->product_id);
+                       return -EINVAL;
+               }
+               return 0;
+
+       default:
+               return -EIO;
+       }
+       return 0;
+}
+
+static irqreturn_t cyapa_irq(int irq, void *dev_id)
+{
+       struct cyapa *cyapa = dev_id;
+       struct device *dev = &cyapa->client->dev;
+       struct input_dev *input = cyapa->input;
+       struct cyapa_reg_data data;
+       int i;
+       int ret;
+       int num_fingers;
+
+       if (device_may_wakeup(dev))
+               pm_wakeup_event(dev, 0);
+
+       ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data);
+       if (ret != sizeof(data))
+               goto out;
+
+       if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC ||
+           (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL ||
+           (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) {
+               goto out;
+       }
+
+       num_fingers = (data.finger_btn >> 4) & 0x0f;
+       for (i = 0; i < num_fingers; i++) {
+               const struct cyapa_touch *touch = &data.touches[i];
+               /* Note: touch->id range is 1 to 15; slots are 0 to 14. */
+               int slot = touch->id - 1;
+
+               input_mt_slot(input, slot);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+               input_report_abs(input, ABS_MT_POSITION_X,
+                                ((touch->xy_hi & 0xf0) << 4) | touch->x_lo);
+               input_report_abs(input, ABS_MT_POSITION_Y,
+                                ((touch->xy_hi & 0x0f) << 8) | touch->y_lo);
+               input_report_abs(input, ABS_MT_PRESSURE, touch->pressure);
+       }
+
+       input_mt_sync_frame(input);
+
+       if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
+               input_report_key(input, BTN_LEFT,
+                                data.finger_btn & OP_DATA_LEFT_BTN);
+
+       if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
+               input_report_key(input, BTN_MIDDLE,
+                                data.finger_btn & OP_DATA_MIDDLE_BTN);
+
+       if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
+               input_report_key(input, BTN_RIGHT,
+                                data.finger_btn & OP_DATA_RIGHT_BTN);
+
+       input_sync(input);
+
+out:
+       return IRQ_HANDLED;
+}
+
+static u8 cyapa_check_adapter_functionality(struct i2c_client *client)
+{
+       u8 ret = CYAPA_ADAPTER_FUNC_NONE;
+
+       if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               ret |= CYAPA_ADAPTER_FUNC_I2C;
+       if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_BLOCK_DATA |
+                                    I2C_FUNC_SMBUS_I2C_BLOCK))
+               ret |= CYAPA_ADAPTER_FUNC_SMBUS;
+       return ret;
+}
+
+static int cyapa_create_input_dev(struct cyapa *cyapa)
+{
+       struct device *dev = &cyapa->client->dev;
+       int ret;
+       struct input_dev *input;
+
+       if (!cyapa->physical_size_x || !cyapa->physical_size_y)
+               return -EINVAL;
+
+       input = cyapa->input = input_allocate_device();
+       if (!input) {
+               dev_err(dev, "allocate memory for input device failed\n");
+               return -ENOMEM;
+       }
+
+       input->name = CYAPA_NAME;
+       input->phys = cyapa->phys;
+       input->id.bustype = BUS_I2C;
+       input->id.version = 1;
+       input->id.product = 0;  /* means any product in eventcomm. */
+       input->dev.parent = &cyapa->client->dev;
+
+       input_set_drvdata(input, cyapa);
+
+       __set_bit(EV_ABS, input->evbit);
+
+       /* finger position */
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0,
+                            0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0,
+                            0);
+       input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+       input_abs_set_res(input, ABS_MT_POSITION_X,
+                         cyapa->max_abs_x / cyapa->physical_size_x);
+       input_abs_set_res(input, ABS_MT_POSITION_Y,
+                         cyapa->max_abs_y / cyapa->physical_size_y);
+
+       if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK)
+               __set_bit(BTN_LEFT, input->keybit);
+       if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK)
+               __set_bit(BTN_MIDDLE, input->keybit);
+       if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK)
+               __set_bit(BTN_RIGHT, input->keybit);
+
+       if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK)
+               __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+
+       /* handle pointer emulation and unused slots in core */
+       ret = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS,
+                                 INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+       if (ret) {
+               dev_err(dev, "allocate memory for MT slots failed, %d\n", ret);
+               goto err_free_device;
+       }
+
+       /* Register the device in input subsystem */
+       ret = input_register_device(input);
+       if (ret) {
+               dev_err(dev, "input device register failed, %d\n", ret);
+               goto err_free_device;
+       }
+       return 0;
+
+err_free_device:
+       input_free_device(input);
+       cyapa->input = NULL;
+       return ret;
+}
+
+static int cyapa_probe(struct i2c_client *client,
+                      const struct i2c_device_id *dev_id)
+{
+       int ret;
+       u8 adapter_func;
+       struct cyapa *cyapa;
+       struct device *dev = &client->dev;
+
+       adapter_func = cyapa_check_adapter_functionality(client);
+       if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) {
+               dev_err(dev, "not a supported I2C/SMBus adapter\n");
+               return -EIO;
+       }
+
+       cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL);
+       if (!cyapa) {
+               dev_err(dev, "allocate memory for cyapa failed\n");
+               return -ENOMEM;
+       }
+
+       cyapa->gen = CYAPA_GEN3;
+       cyapa->client = client;
+       i2c_set_clientdata(client, cyapa);
+       sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr,
+               client->addr);
+
+       /* i2c isn't supported, use smbus */
+       if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS)
+               cyapa->smbus = true;
+       cyapa->state = CYAPA_STATE_NO_DEVICE;
+       ret = cyapa_check_is_operational(cyapa);
+       if (ret) {
+               dev_err(dev, "device not operational, %d\n", ret);
+               goto err_mem_free;
+       }
+
+       ret = cyapa_create_input_dev(cyapa);
+       if (ret) {
+               dev_err(dev, "create input_dev instance failed, %d\n", ret);
+               goto err_mem_free;
+       }
+
+       ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
+       if (ret) {
+               dev_err(dev, "set active power failed, %d\n", ret);
+               goto err_unregister_device;
+       }
+
+       cyapa->irq = client->irq;
+       ret = request_threaded_irq(cyapa->irq,
+                                  NULL,
+                                  cyapa_irq,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  "cyapa",
+                                  cyapa);
+       if (ret) {
+               dev_err(dev, "IRQ request failed: %d\n, ", ret);
+               goto err_unregister_device;
+       }
+
+       return 0;
+
+err_unregister_device:
+       input_unregister_device(cyapa->input);
+err_mem_free:
+       kfree(cyapa);
+
+       return ret;
+}
+
+static int cyapa_remove(struct i2c_client *client)
+{
+       struct cyapa *cyapa = i2c_get_clientdata(client);
+
+       free_irq(cyapa->irq, cyapa);
+       input_unregister_device(cyapa->input);
+       cyapa_set_power_mode(cyapa, PWR_MODE_OFF);
+       kfree(cyapa);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cyapa_suspend(struct device *dev)
+{
+       int ret;
+       u8 power_mode;
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+
+       disable_irq(cyapa->irq);
+
+       /*
+        * Set trackpad device to idle mode if wakeup is allowed,
+        * otherwise turn off.
+        */
+       power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE
+                                           : PWR_MODE_OFF;
+       ret = cyapa_set_power_mode(cyapa, power_mode);
+       if (ret < 0)
+               dev_err(dev, "set power mode failed, %d\n", ret);
+
+       if (device_may_wakeup(dev))
+               cyapa->irq_wake = (enable_irq_wake(cyapa->irq) == 0);
+       return 0;
+}
+
+static int cyapa_resume(struct device *dev)
+{
+       int ret;
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev) && cyapa->irq_wake)
+               disable_irq_wake(cyapa->irq);
+
+       ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
+       if (ret)
+               dev_warn(dev, "resume active power failed, %d\n", ret);
+
+       enable_irq(cyapa->irq);
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume);
+
+static const struct i2c_device_id cyapa_id_table[] = {
+       { "cyapa", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, cyapa_id_table);
+
+static struct i2c_driver cyapa_driver = {
+       .driver = {
+               .name = "cyapa",
+               .owner = THIS_MODULE,
+               .pm = &cyapa_pm_ops,
+       },
+
+       .probe = cyapa_probe,
+       .remove = cyapa_remove,
+       .id_table = cyapa_id_table,
+};
+
+module_i2c_driver(cyapa_driver);
+
+MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver");
+MODULE_AUTHOR("Dudley Du <dudl@cypress.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c
new file mode 100644 (file)
index 0000000..1673dc6
--- /dev/null
@@ -0,0 +1,725 @@
+/*
+ * Cypress Trackpad PS/2 mouse driver
+ *
+ * Copyright (c) 2012 Cypress Semiconductor Corporation.
+ *
+ * Author:
+ *   Dudley Du <dudl@cypress.com>
+ *
+ * Additional contributors include:
+ *   Kamal Mostafa <kamal@canonical.com>
+ *   Kyle Fazzari <git@status.e4ward.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "cypress_ps2.h"
+
+#undef CYTP_DEBUG_VERBOSE  /* define this and DEBUG for more verbose dump */
+
+static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n)
+{
+       struct cytp_data *cytp = psmouse->private;
+       cytp->pkt_size = n;
+}
+
+static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
+static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
+
+static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+       if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
+               psmouse_dbg(psmouse,
+                               "sending command 0x%02x failed, resp 0x%02x\n",
+                               value & 0xff, ps2dev->nak);
+               if (ps2dev->nak == CYTP_PS2_RETRY)
+                       return CYTP_PS2_RETRY;
+               else
+                       return CYTP_PS2_ERROR;
+       }
+
+#ifdef CYTP_DEBUG_VERBOSE
+       psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n",
+                       value & 0xff);
+#endif
+
+       return 0;
+}
+
+static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
+                              unsigned char data)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       int tries = CYTP_PS2_CMD_TRIES;
+       int rc;
+
+       ps2_begin_command(ps2dev);
+
+       do {
+               /*
+                * Send extension command byte (0xE8 or 0xF3).
+                * If sending the command fails, send recovery command
+                * to make the device return to the ready state.
+                */
+               rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
+               if (rc == CYTP_PS2_RETRY) {
+                       rc = cypress_ps2_sendbyte(psmouse, 0x00);
+                       if (rc == CYTP_PS2_RETRY)
+                               rc = cypress_ps2_sendbyte(psmouse, 0x0a);
+               }
+               if (rc == CYTP_PS2_ERROR)
+                       continue;
+
+               rc = cypress_ps2_sendbyte(psmouse, data);
+               if (rc == CYTP_PS2_RETRY)
+                       rc = cypress_ps2_sendbyte(psmouse, data);
+               if (rc == CYTP_PS2_ERROR)
+                       continue;
+               else
+                       break;
+       } while (--tries > 0);
+
+       ps2_end_command(ps2dev);
+
+       return rc;
+}
+
+static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
+                                      unsigned char cmd,
+                                      unsigned char *param)
+{
+       int rc;
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       enum psmouse_state old_state;
+       int pktsize;
+
+       ps2_begin_command(&psmouse->ps2dev);
+
+       old_state = psmouse->state;
+       psmouse->state = PSMOUSE_CMD_MODE;
+       psmouse->pktcnt = 0;
+
+       pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3;
+       memset(param, 0, pktsize);
+
+       rc = cypress_ps2_sendbyte(psmouse, 0xe9);
+       if (rc < 0)
+               goto out;
+
+       wait_event_timeout(ps2dev->wait,
+                       (psmouse->pktcnt >= pktsize),
+                       msecs_to_jiffies(CYTP_CMD_TIMEOUT));
+
+       memcpy(param, psmouse->packet, pktsize);
+
+       psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n",
+                       cmd, pktsize, param);
+
+out:
+       psmouse->state = old_state;
+       psmouse->pktcnt = 0;
+
+       ps2_end_command(&psmouse->ps2dev);
+
+       return rc;
+}
+
+static bool cypress_verify_cmd_state(struct psmouse *psmouse,
+                                    unsigned char cmd, unsigned char *param)
+{
+       bool rate_match = false;
+       bool resolution_match = false;
+       int i;
+
+       /* callers will do further checking. */
+       if (cmd == CYTP_CMD_READ_CYPRESS_ID ||
+           cmd == CYTP_CMD_STANDARD_MODE ||
+           cmd == CYTP_CMD_READ_TP_METRICS)
+               return true;
+
+       if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID &&
+           (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) {
+               for (i = 0; i < sizeof(cytp_resolution); i++)
+                       if (cytp_resolution[i] == param[1])
+                               resolution_match = true;
+
+               for (i = 0; i < sizeof(cytp_rate); i++)
+                       if (cytp_rate[i] == param[2])
+                               rate_match = true;
+
+               if (resolution_match && rate_match)
+                       return true;
+       }
+
+       psmouse_dbg(psmouse, "verify cmd state failed.\n");
+       return false;
+}
+
+static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
+                               unsigned char *param)
+{
+       int tries = CYTP_PS2_CMD_TRIES;
+       int rc;
+
+       psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n",
+                cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
+                DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
+
+       do {
+               cypress_ps2_ext_cmd(psmouse,
+                                   PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
+               cypress_ps2_ext_cmd(psmouse,
+                                   PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
+               cypress_ps2_ext_cmd(psmouse,
+                                   PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
+               cypress_ps2_ext_cmd(psmouse,
+                                   PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
+
+               rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
+               if (rc)
+                       continue;
+
+               if (cypress_verify_cmd_state(psmouse, cmd, param))
+                       return 0;
+
+       } while (--tries > 0);
+
+       return -EIO;
+}
+
+int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+       unsigned char param[3];
+
+       if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+               return -ENODEV;
+
+       /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
+       if (param[0] != 0x33 || param[1] != 0xCC)
+               return -ENODEV;
+
+       if (set_properties) {
+               psmouse->vendor = "Cypress";
+               psmouse->name = "Trackpad";
+       }
+
+       return 0;
+}
+
+static int cypress_read_fw_version(struct psmouse *psmouse)
+{
+       struct cytp_data *cytp = psmouse->private;
+       unsigned char param[3];
+
+       if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
+               return -ENODEV;
+
+       /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
+       if (param[0] != 0x33 || param[1] != 0xCC)
+               return -ENODEV;
+
+       cytp->fw_version = param[2] & FW_VERSION_MASX;
+       cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0;
+
+       psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);
+       psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",
+                cytp->tp_metrics_supported);
+
+       return 0;
+}
+
+static int cypress_read_tp_metrics(struct psmouse *psmouse)
+{
+       struct cytp_data *cytp = psmouse->private;
+       unsigned char param[8];
+
+       /* set default values for tp metrics. */
+       cytp->tp_width = CYTP_DEFAULT_WIDTH;
+       cytp->tp_high = CYTP_DEFAULT_HIGH;
+       cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
+       cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
+       cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
+       cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
+       cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+       cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+       memset(param, 0, sizeof(param));
+       if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {
+               /* Update trackpad parameters. */
+               cytp->tp_max_abs_x = (param[1] << 8) | param[0];
+               cytp->tp_max_abs_y = (param[3] << 8) | param[2];
+               cytp->tp_min_pressure = param[4];
+               cytp->tp_max_pressure = param[5];
+       }
+
+       if (!cytp->tp_max_pressure ||
+           cytp->tp_max_pressure < cytp->tp_min_pressure ||
+           !cytp->tp_width || !cytp->tp_high ||
+           !cytp->tp_max_abs_x ||
+           cytp->tp_max_abs_x < cytp->tp_width ||
+           !cytp->tp_max_abs_y ||
+           cytp->tp_max_abs_y < cytp->tp_high)
+               return -EINVAL;
+
+       cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
+       cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
+
+#ifdef CYTP_DEBUG_VERBOSE
+       psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n");
+       psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width);
+       psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high);
+       psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
+       psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
+       psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
+       psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
+       psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x);
+       psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y);
+
+       psmouse_dbg(psmouse, "tp_type_APA = %d\n",
+                       (param[6] & TP_METRICS_BIT_APA) ? 1 : 0);
+       psmouse_dbg(psmouse, "tp_type_MTG = %d\n",
+                       (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0);
+       psmouse_dbg(psmouse, "tp_palm = %d\n",
+                       (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0);
+       psmouse_dbg(psmouse, "tp_stubborn = %d\n",
+                       (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0);
+       psmouse_dbg(psmouse, "tp_1f_jitter = %d\n",
+                       (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2);
+       psmouse_dbg(psmouse, "tp_2f_jitter = %d\n",
+                       (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4);
+       psmouse_dbg(psmouse, "tp_1f_spike = %d\n",
+                       param[7] & TP_METRICS_BIT_1F_SPIKE);
+       psmouse_dbg(psmouse, "tp_2f_spike = %d\n",
+                       (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2);
+       psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n",
+                       (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4);
+#endif
+
+       return 0;
+}
+
+static int cypress_query_hardware(struct psmouse *psmouse)
+{
+       struct cytp_data *cytp = psmouse->private;
+       int ret;
+
+       ret = cypress_read_fw_version(psmouse);
+       if (ret)
+               return ret;
+
+       if (cytp->tp_metrics_supported) {
+               ret = cypress_read_tp_metrics(psmouse);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int cypress_set_absolute_mode(struct psmouse *psmouse)
+{
+       struct cytp_data *cytp = psmouse->private;
+       unsigned char param[3];
+
+       if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
+               return -1;
+
+       cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK)
+                       | CYTP_BIT_ABS_PRESSURE;
+       cypress_set_packet_size(psmouse, 5);
+
+       return 0;
+}
+
+/*
+ * Reset trackpad device.
+ * This is also the default mode when trackpad powered on.
+ */
+static void cypress_reset(struct psmouse *psmouse)
+{
+       struct cytp_data *cytp = psmouse->private;
+
+       cytp->mode = 0;
+
+       psmouse_reset(psmouse);
+}
+
+static int cypress_set_input_params(struct input_dev *input,
+                                   struct cytp_data *cytp)
+{
+       int ret;
+
+       if (!cytp->tp_res_x || !cytp->tp_res_y)
+               return -EINVAL;
+
+       __set_bit(EV_ABS, input->evbit);
+       input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
+       input_set_abs_params(input, ABS_PRESSURE,
+                            cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
+       input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
+
+       /* finger position */
+       input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
+       input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
+       input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
+
+       ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
+                       INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
+       if (ret < 0)
+               return ret;
+
+       __set_bit(INPUT_PROP_SEMI_MT, input->propbit);
+
+       input_abs_set_res(input, ABS_X, cytp->tp_res_x);
+       input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
+
+       input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x);
+       input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y);
+
+       __set_bit(BTN_TOUCH, input->keybit);
+       __set_bit(BTN_TOOL_FINGER, input->keybit);
+       __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+       __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+       __set_bit(BTN_TOOL_QUADTAP, input->keybit);
+       __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
+
+       __clear_bit(EV_REL, input->evbit);
+       __clear_bit(REL_X, input->relbit);
+       __clear_bit(REL_Y, input->relbit);
+
+       __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+       __set_bit(EV_KEY, input->evbit);
+       __set_bit(BTN_LEFT, input->keybit);
+       __set_bit(BTN_RIGHT, input->keybit);
+       __set_bit(BTN_MIDDLE, input->keybit);
+
+       input_set_drvdata(input, cytp);
+
+       return 0;
+}
+
+static int cypress_get_finger_count(unsigned char header_byte)
+{
+       unsigned char bits6_7;
+       int finger_count;
+
+       bits6_7 = header_byte >> 6;
+       finger_count = bits6_7 & 0x03;
+
+       if (finger_count == 1)
+               return 1;
+
+       if (header_byte & ABS_HSCROLL_BIT) {
+               /* HSCROLL gets added on to 0 finger count. */
+               switch (finger_count) {
+                       case 0: return 4;
+                       case 2: return 5;
+                       default:
+                               /* Invalid contact (e.g. palm). Ignore it. */
+                               return -1;
+               }
+       }
+
+       return finger_count;
+}
+
+
+static int cypress_parse_packet(struct psmouse *psmouse,
+                               struct cytp_data *cytp, struct cytp_report_data *report_data)
+{
+       unsigned char *packet = psmouse->packet;
+       unsigned char header_byte = packet[0];
+       int contact_cnt;
+
+       memset(report_data, 0, sizeof(struct cytp_report_data));
+
+       contact_cnt = cypress_get_finger_count(header_byte);
+
+       if (contact_cnt < 0) /* e.g. palm detect */
+               return -EINVAL;
+
+       report_data->contact_cnt = contact_cnt;
+
+       report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
+
+       if (report_data->contact_cnt == 1) {
+               report_data->contacts[0].x =
+                       ((packet[1] & 0x70) << 4) | packet[2];
+               report_data->contacts[0].y =
+                       ((packet[1] & 0x07) << 8) | packet[3];
+               if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+                       report_data->contacts[0].z = packet[4];
+
+       } else if (report_data->contact_cnt >= 2) {
+               report_data->contacts[0].x =
+                       ((packet[1] & 0x70) << 4) | packet[2];
+               report_data->contacts[0].y =
+                       ((packet[1] & 0x07) << 8) | packet[3];
+               if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+                       report_data->contacts[0].z = packet[4];
+
+               report_data->contacts[1].x =
+                       ((packet[5] & 0xf0) << 4) | packet[6];
+               report_data->contacts[1].y =
+                       ((packet[5] & 0x0f) << 8) | packet[7];
+               if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
+                       report_data->contacts[1].z = report_data->contacts[0].z;
+       }
+
+       report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
+       report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
+
+       /*
+        * This is only true if one of the mouse buttons were tapped.  Make
+        * sure it doesn't turn into a click. The regular tap-to-click
+        * functionality will handle that on its own. If we don't do this,
+        * disabling tap-to-click won't affect the mouse button zones.
+        */
+       if (report_data->tap)
+               report_data->left = 0;
+
+#ifdef CYTP_DEBUG_VERBOSE
+       {
+               int i;
+               int n = report_data->contact_cnt;
+               psmouse_dbg(psmouse, "Dump parsed report data as below:\n");
+               psmouse_dbg(psmouse, "contact_cnt = %d\n",
+                       report_data->contact_cnt);
+               if (n > CYTP_MAX_MT_SLOTS)
+                   n = CYTP_MAX_MT_SLOTS;
+               for (i = 0; i < n; i++)
+                       psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i,
+                                       report_data->contacts[i].x,
+                                       report_data->contacts[i].y,
+                                       report_data->contacts[i].z);
+               psmouse_dbg(psmouse, "left = %d\n", report_data->left);
+               psmouse_dbg(psmouse, "right = %d\n", report_data->right);
+               psmouse_dbg(psmouse, "middle = %d\n", report_data->middle);
+       }
+#endif
+
+       return 0;
+}
+
+static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
+{
+       int i;
+       struct input_dev *input = psmouse->dev;
+       struct cytp_data *cytp = psmouse->private;
+       struct cytp_report_data report_data;
+       struct cytp_contact *contact;
+       struct input_mt_pos pos[CYTP_MAX_MT_SLOTS];
+       int slots[CYTP_MAX_MT_SLOTS];
+       int n;
+
+       if (cypress_parse_packet(psmouse, cytp, &report_data))
+               return;
+
+       n = report_data.contact_cnt;
+
+       if (n > CYTP_MAX_MT_SLOTS)
+               n = CYTP_MAX_MT_SLOTS;
+
+       for (i = 0; i < n; i++) {
+               contact = &report_data.contacts[i];
+               pos[i].x = contact->x;
+               pos[i].y = contact->y;
+       }
+
+       input_mt_assign_slots(input, slots, pos, n);
+
+       for (i = 0; i < n; i++) {
+               contact = &report_data.contacts[i];
+               input_mt_slot(input, slots[i]);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+               input_report_abs(input, ABS_MT_POSITION_X, contact->x);
+               input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
+               input_report_abs(input, ABS_MT_PRESSURE, contact->z);
+       }
+
+       input_mt_sync_frame(input);
+
+       input_mt_report_finger_count(input, report_data.contact_cnt);
+
+       input_report_key(input, BTN_LEFT, report_data.left);
+       input_report_key(input, BTN_RIGHT, report_data.right);
+       input_report_key(input, BTN_MIDDLE, report_data.middle);
+
+       input_sync(input);
+}
+
+static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
+{
+       int contact_cnt;
+       int index = psmouse->pktcnt - 1;
+       unsigned char *packet = psmouse->packet;
+       struct cytp_data *cytp = psmouse->private;
+
+       if (index < 0 || index > cytp->pkt_size)
+               return PSMOUSE_BAD_DATA;
+
+       if (index == 0 && (packet[0] & 0xfc) == 0) {
+               /* call packet process for reporting finger leave. */
+               cypress_process_packet(psmouse, 1);
+               return PSMOUSE_FULL_PACKET;
+       }
+
+       /*
+        * Perform validation (and adjust packet size) based only on the
+        * first byte; allow all further bytes through.
+        */
+       if (index != 0)
+               return PSMOUSE_GOOD_DATA;
+
+       /*
+        * If absolute/relative mode bit has not been set yet, just pass
+        * the byte through.
+        */
+       if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0)
+               return PSMOUSE_GOOD_DATA;
+
+       if ((packet[0] & 0x08) == 0x08)
+               return PSMOUSE_BAD_DATA;
+
+       contact_cnt = cypress_get_finger_count(packet[0]);
+
+       if (contact_cnt < 0)
+               return PSMOUSE_BAD_DATA;
+
+       if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
+               cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
+       else
+               cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5);
+
+       return PSMOUSE_GOOD_DATA;
+}
+
+static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
+{
+       struct cytp_data *cytp = psmouse->private;
+
+       if (psmouse->pktcnt >= cytp->pkt_size) {
+               cypress_process_packet(psmouse, 0);
+               return PSMOUSE_FULL_PACKET;
+       }
+
+       return cypress_validate_byte(psmouse);
+}
+
+static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+       struct cytp_data *cytp = psmouse->private;
+
+       if (rate >= 80) {
+               psmouse->rate = 80;
+               cytp->mode |= CYTP_BIT_HIGH_RATE;
+       } else {
+               psmouse->rate = 40;
+               cytp->mode &= ~CYTP_BIT_HIGH_RATE;
+       }
+
+       ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
+                   PSMOUSE_CMD_SETRATE);
+}
+
+static void cypress_disconnect(struct psmouse *psmouse)
+{
+       cypress_reset(psmouse);
+       kfree(psmouse->private);
+       psmouse->private = NULL;
+}
+
+static int cypress_reconnect(struct psmouse *psmouse)
+{
+       int tries = CYTP_PS2_CMD_TRIES;
+       int rc;
+
+       do {
+               cypress_reset(psmouse);
+               rc = cypress_detect(psmouse, false);
+       } while (rc && (--tries > 0));
+
+       if (rc) {
+               psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
+               return -1;
+       }
+
+       if (cypress_set_absolute_mode(psmouse)) {
+               psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+int cypress_init(struct psmouse *psmouse)
+{
+       struct cytp_data *cytp;
+
+       cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
+       psmouse->private = (void *)cytp;
+       if (cytp == NULL)
+               return -ENOMEM;
+
+       cypress_reset(psmouse);
+
+       psmouse->pktsize = 8;
+
+       if (cypress_query_hardware(psmouse)) {
+               psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
+               goto err_exit;
+       }
+
+       if (cypress_set_absolute_mode(psmouse)) {
+               psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
+               goto err_exit;
+       }
+
+       if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
+               psmouse_err(psmouse, "init: Unable to set input params.\n");
+               goto err_exit;
+       }
+
+       psmouse->model = 1;
+       psmouse->protocol_handler = cypress_protocol_handler;
+       psmouse->set_rate = cypress_set_rate;
+       psmouse->disconnect = cypress_disconnect;
+       psmouse->reconnect = cypress_reconnect;
+       psmouse->cleanup = cypress_reset;
+       psmouse->resync_time = 0;
+
+       return 0;
+
+err_exit:
+       /*
+        * Reset Cypress Trackpad as a standard mouse. Then
+        * let psmouse driver commmunicating with it as default PS2 mouse.
+        */
+       cypress_reset(psmouse);
+
+       psmouse->private = NULL;
+       kfree(cytp);
+
+       return -1;
+}
+
+bool cypress_supported(void)
+{
+       return true;
+}
diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h
new file mode 100644 (file)
index 0000000..4720f21
--- /dev/null
@@ -0,0 +1,191 @@
+#ifndef _CYPRESS_PS2_H
+#define _CYPRESS_PS2_H
+
+#include "psmouse.h"
+
+#define CMD_BITS_MASK 0x03
+#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s))
+
+#define ENCODE_CMD(aa, bb, cc, dd) \
+       (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0))
+#define CYTP_CMD_ABS_NO_PRESSURE_MODE       ENCODE_CMD(0, 1, 0, 0)
+#define CYTP_CMD_ABS_WITH_PRESSURE_MODE     ENCODE_CMD(0, 1, 0, 1)
+#define CYTP_CMD_SMBUS_MODE                 ENCODE_CMD(0, 1, 1, 0)
+#define CYTP_CMD_STANDARD_MODE              ENCODE_CMD(0, 2, 0, 0)  /* not implemented yet. */
+#define CYTP_CMD_CYPRESS_REL_MODE           ENCODE_CMD(1, 1, 1, 1)  /* not implemented yet. */
+#define CYTP_CMD_READ_CYPRESS_ID            ENCODE_CMD(0, 0, 0, 0)
+#define CYTP_CMD_READ_TP_METRICS            ENCODE_CMD(0, 0, 0, 1)
+#define CYTP_CMD_SET_HSCROLL_WIDTH(w)       ENCODE_CMD(1, 1, 0, (w))
+#define     CYTP_CMD_SET_HSCROLL_MASK       ENCODE_CMD(1, 1, 0, 0)
+#define CYTP_CMD_SET_VSCROLL_WIDTH(w)       ENCODE_CMD(1, 2, 0, (w))
+#define     CYTP_CMD_SET_VSCROLL_MASK       ENCODE_CMD(1, 2, 0, 0)
+#define CYTP_CMD_SET_PALM_GEOMETRY(e)       ENCODE_CMD(1, 2, 1, (e))
+#define     CYTP_CMD_PALM_GEMMETRY_MASK     ENCODE_CMD(1, 2, 1, 0)
+#define CYTP_CMD_SET_PALM_SENSITIVITY(s)    ENCODE_CMD(1, 2, 2, (s))
+#define     CYTP_CMD_PALM_SENSITIVITY_MASK  ENCODE_CMD(1, 2, 2, 0)
+#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s)   ENCODE_CMD(1, 3, ((s) >> 2), (s))
+#define     CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0)
+#define CYTP_CMD_REQUEST_BASELINE_STATUS    ENCODE_CMD(2, 0, 0, 1)
+#define CYTP_CMD_REQUEST_RECALIBRATION      ENCODE_CMD(2, 0, 0, 3)
+
+#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK)
+#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK)
+#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK)
+#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK)
+
+/* Cypress trackpad working mode. */
+#define CYTP_BIT_ABS_PRESSURE    (1 << 3)
+#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2)
+#define CYTP_BIT_CYPRESS_REL     (1 << 1)
+#define CYTP_BIT_STANDARD_REL    (1 << 0)
+#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL)
+#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE)
+#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK)
+
+#define CYTP_BIT_HIGH_RATE       (1 << 4)
+/*
+ * report mode bit is set, firmware working in Remote Mode.
+ * report mode bit is cleared, firmware working in Stream Mode.
+ */
+#define CYTP_BIT_REPORT_MODE     (1 << 5)
+
+/* scrolling width values for set HSCROLL and VSCROLL width command. */
+#define SCROLL_WIDTH_NARROW 1
+#define SCROLL_WIDTH_NORMAL 2
+#define SCROLL_WIDTH_WIDE   3
+
+#define PALM_GEOMETRY_ENABLE  1
+#define PALM_GEOMETRY_DISABLE 0
+
+#define TP_METRICS_MASK  0x80
+#define FW_VERSION_MASX    0x7f
+#define FW_VER_HIGH_MASK 0x70
+#define FW_VER_LOW_MASK  0x0f
+
+/* Times to retry a ps2_command and millisecond delay between tries. */
+#define CYTP_PS2_CMD_TRIES 3
+#define CYTP_PS2_CMD_DELAY 500
+
+/* time out for PS/2 command only in milliseconds. */
+#define CYTP_CMD_TIMEOUT  200
+#define CYTP_DATA_TIMEOUT 30
+
+#define CYTP_EXT_CMD   0xe8
+#define CYTP_PS2_RETRY 0xfe
+#define CYTP_PS2_ERROR 0xfc
+
+#define CYTP_RESP_RETRY 0x01
+#define CYTP_RESP_ERROR 0xfe
+
+
+#define CYTP_105001_WIDTH  97   /* Dell XPS 13 */
+#define CYTP_105001_HIGH   59
+#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH)
+#define CYTP_DEFAULT_HIGH  (CYTP_105001_HIGH)
+
+#define CYTP_ABS_MAX_X     1600
+#define CYTP_ABS_MAX_Y     900
+#define CYTP_MAX_PRESSURE  255
+#define CYTP_MIN_PRESSURE  0
+
+/* header byte bits of relative package. */
+#define BTN_LEFT_BIT   0x01
+#define BTN_RIGHT_BIT  0x02
+#define BTN_MIDDLE_BIT 0x04
+#define REL_X_SIGN_BIT 0x10
+#define REL_Y_SIGN_BIT 0x20
+
+/* header byte bits of absolute package. */
+#define ABS_VSCROLL_BIT 0x10
+#define ABS_HSCROLL_BIT 0x20
+#define ABS_MULTIFINGER_TAP 0x04
+#define ABS_EDGE_MOTION_MASK 0x80
+
+#define DFLT_RESP_BITS_VALID     0x88  /* SMBus bit should not be set. */
+#define DFLT_RESP_SMBUS_BIT      0x80
+#define   DFLT_SMBUS_MODE        0x80
+#define   DFLT_PS2_MODE          0x00
+#define DFLT_RESP_BIT_MODE       0x40
+#define   DFLT_RESP_REMOTE_MODE  0x40
+#define   DFLT_RESP_STREAM_MODE  0x00
+#define DFLT_RESP_BIT_REPORTING  0x20
+#define DFLT_RESP_BIT_SCALING    0x10
+
+#define TP_METRICS_BIT_PALM               0x80
+#define TP_METRICS_BIT_STUBBORN           0x40
+#define TP_METRICS_BIT_2F_JITTER          0x30
+#define TP_METRICS_BIT_1F_JITTER          0x0c
+#define TP_METRICS_BIT_APA                0x02
+#define TP_METRICS_BIT_MTG                0x01
+#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0
+#define TP_METRICS_BIT_2F_SPIKE           0x0c
+#define TP_METRICS_BIT_1F_SPIKE           0x03
+
+/* bits of first byte response of E9h-Status Request command. */
+#define RESP_BTN_RIGHT_BIT  0x01
+#define RESP_BTN_MIDDLE_BIT 0x02
+#define RESP_BTN_LEFT_BIT   0x04
+#define RESP_SCALING_BIT    0x10
+#define RESP_ENABLE_BIT     0x20
+#define RESP_REMOTE_BIT     0x40
+#define RESP_SMBUS_BIT      0x80
+
+#define CYTP_MAX_MT_SLOTS 2
+
+struct cytp_contact {
+       int x;
+       int y;
+       int z;  /* also named as touch pressure. */
+};
+
+/* The structure of Cypress Trackpad event data. */
+struct cytp_report_data {
+       int contact_cnt;
+       struct cytp_contact contacts[CYTP_MAX_MT_SLOTS];
+       unsigned int left:1;
+       unsigned int right:1;
+       unsigned int middle:1;
+       unsigned int tap:1;  /* multi-finger tap detected. */
+};
+
+/* The structure of Cypress Trackpad device private data. */
+struct cytp_data {
+       int fw_version;
+
+       int pkt_size;
+       int mode;
+
+       int tp_min_pressure;
+       int tp_max_pressure;
+       int tp_width;  /* X direction physical size in mm. */
+       int tp_high;  /* Y direction physical size in mm. */
+       int tp_max_abs_x;  /* Max X absolute units that can be reported. */
+       int tp_max_abs_y;  /* Max Y absolute units that can be reported. */
+
+       int tp_res_x;  /* X resolution in units/mm. */
+       int tp_res_y;  /* Y resolution in units/mm. */
+
+       int tp_metrics_supported;
+};
+
+
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+int cypress_detect(struct psmouse *psmouse, bool set_properties);
+int cypress_init(struct psmouse *psmouse);
+bool cypress_supported(void);
+#else
+inline int cypress_detect(struct psmouse *psmouse, bool set_properties)
+{
+       return -ENOSYS;
+}
+inline int cypress_init(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+inline bool cypress_supported(void)
+{
+       return 0;
+}
+#endif /* CONFIG_MOUSE_PS2_CYPRESS */
+
+#endif  /* _CYPRESS_PS2_H */
index 22fe2547e16991cc45987d33f7c5bfb4436c5265..cff065f6261cf31a3188226f14fe28e9291f6770 100644 (file)
@@ -34,6 +34,7 @@
 #include "touchkit_ps2.h"
 #include "elantech.h"
 #include "sentelic.h"
+#include "cypress_ps2.h"
 
 #define DRIVER_DESC    "PS/2 mouse driver"
 
@@ -758,6 +759,28 @@ static int psmouse_extensions(struct psmouse *psmouse,
                synaptics_reset(psmouse);
        }
 
+/*
+ * Try Cypress Trackpad.
+ * Must try it before Finger Sensing Pad because Finger Sensing Pad probe
+ * upsets some modules of Cypress Trackpads.
+ */
+       if (max_proto > PSMOUSE_IMEX &&
+                       cypress_detect(psmouse, set_properties) == 0) {
+               if (cypress_supported()) {
+                       if (cypress_init(psmouse) == 0)
+                               return PSMOUSE_CYPRESS;
+
+                       /*
+                        * Finger Sensing Pad probe upsets some modules of
+                        * Cypress Trackpad, must avoid Finger Sensing Pad
+                        * probe if Cypress Trackpad device detected.
+                        */
+                       return PSMOUSE_PS2;
+               }
+
+               max_proto = PSMOUSE_IMEX;
+       }
+
 /*
  * Try ALPS TouchPad
  */
@@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .alias          = "thinkps",
                .detect         = thinking_detect,
        },
+#ifdef CONFIG_MOUSE_PS2_CYPRESS
+       {
+               .type           = PSMOUSE_CYPRESS,
+               .name           = "CyPS/2",
+               .alias          = "cypress",
+               .detect         = cypress_detect,
+               .init           = cypress_init,
+       },
+#endif
        {
                .type           = PSMOUSE_GENPS,
                .name           = "GenPS/2",
index fe1df231ba4c8dfc07488fd6d4fe1031344e0f76..2f0b39d59a9bd51aa9dd3e803f9502a0515c5f61 100644 (file)
@@ -95,6 +95,7 @@ enum psmouse_type {
        PSMOUSE_ELANTECH,
        PSMOUSE_FSP,
        PSMOUSE_SYNAPTICS_RELATIVE,
+       PSMOUSE_CYPRESS,
        PSMOUSE_AUTO            /* This one should always be last */
 };
 
index 12d12ca3fee030e0ab9ced706616c1160a1c584b..2f78538e09d0f9e733be6f862a4de8e9d2c0301c 100644 (file)
@@ -722,11 +722,13 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
        default:
                /*
                 * If the finger slot contained in SGM is valid, and either
-                * hasn't changed, or is new, then report SGM in MTB slot 0.
+                * hasn't changed, or is new, or the old SGM has now moved to
+                * AGM, then report SGM in MTB slot 0.
                 * Otherwise, empty MTB slot 0.
                 */
                if (mt_state->sgm != -1 &&
-                   (mt_state->sgm == old->sgm || old->sgm == -1))
+                   (mt_state->sgm == old->sgm ||
+                    old->sgm == -1 || mt_state->agm == old->sgm))
                        synaptics_report_slot(dev, 0, sgm);
                else
                        synaptics_report_slot(dev, 0, NULL);
@@ -735,9 +737,31 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
                 * If the finger slot contained in AGM is valid, and either
                 * hasn't changed, or is new, then report AGM in MTB slot 1.
                 * Otherwise, empty MTB slot 1.
+                *
+                * However, in the case where the AGM is new, make sure that
+                * that it is either the same as the old SGM, or there was no
+                * SGM.
+                *
+                * Otherwise, if the SGM was just 1, and the new AGM is 2, then
+                * the new AGM will keep the old SGM's tracking ID, which can
+                * cause apparent drumroll.  This happens if in the following
+                * valid finger sequence:
+                *
+                *  Action                 SGM  AGM (MTB slot:Contact)
+                *  1. Touch contact 0    (0:0)
+                *  2. Touch contact 1    (0:0, 1:1)
+                *  3. Lift  contact 0    (1:1)
+                *  4. Touch contacts 2,3 (0:2, 1:3)
+                *
+                * In step 4, contact 3, in AGM must not be given the same
+                * tracking ID as contact 1 had in step 3.  To avoid this,
+                * the first agm with contact 3 is dropped and slot 1 is
+                * invalidated (tracking ID = -1).
                 */
                if (mt_state->agm != -1 &&
-                   (mt_state->agm == old->agm || old->agm == -1))
+                   (mt_state->agm == old->agm ||
+                    (old->agm == -1 &&
+                     (old->sgm == -1 || mt_state->agm == old->sgm))))
                        synaptics_report_slot(dev, 1, agm);
                else
                        synaptics_report_slot(dev, 1, NULL);
@@ -1247,11 +1271,11 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
 
        if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
-               input_mt_init_slots(dev, 2, 0);
                set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
                                        ABS_MT_POSITION_Y);
                /* Image sensors can report per-contact pressure */
                input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+               input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
 
                /* Image sensors can signal 4 and 5 finger clicks */
                __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
index 4a4e182c33e7a0c540a52a566b4eeabe038a5e4a..560c243bfcaf41181e7cfbfc88e0a1cb266fce3d 100644 (file)
@@ -236,6 +236,7 @@ config SERIO_PS2MULT
 
 config SERIO_ARC_PS2
        tristate "ARC PS/2 support"
+       depends on GENERIC_HARDIRQS
        help
          Say Y here if you have an ARC FPGA platform with a PS/2
          controller in it.
index 264138f3217eb9fe2cc32cb23c84ee0b20d5d329..41b6fbf601122b26500c397d24e34f9789286f81 100644 (file)
@@ -359,6 +359,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
                case 0x802: /* Intuos4 General Pen */
                case 0x804: /* Intuos4 Marker Pen */
                case 0x40802: /* Intuos4 Classic Pen */
+               case 0x18803: /* DTH2242 Grip Pen */
                case 0x022:
                        wacom->tool[idx] = BTN_TOOL_PEN;
                        break;
@@ -538,6 +539,13 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
                                input_report_key(input, wacom->tool[1], 0);
                                input_report_abs(input, ABS_MISC, 0);
                        }
+               } else if (features->type == DTK) {
+                       input_report_key(input, BTN_0, (data[6] & 0x01));
+                       input_report_key(input, BTN_1, (data[6] & 0x02));
+                       input_report_key(input, BTN_2, (data[6] & 0x04));
+                       input_report_key(input, BTN_3, (data[6] & 0x08));
+                       input_report_key(input, BTN_4, (data[6] & 0x10));
+                       input_report_key(input, BTN_5, (data[6] & 0x20));
                } else if (features->type == WACOM_24HD) {
                        input_report_key(input, BTN_0, (data[6] & 0x01));
                        input_report_key(input, BTN_1, (data[6] & 0x02));
@@ -785,25 +793,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
        return 1;
 }
 
-static int find_slot_from_contactid(struct wacom_wac *wacom, int contactid)
-{
-       int touch_max = wacom->features.touch_max;
-       int i;
-
-       if (!wacom->slots)
-               return -1;
-
-       for (i = 0; i < touch_max; ++i) {
-               if (wacom->slots[i] == contactid)
-                       return i;
-       }
-       for (i = 0; i < touch_max; ++i) {
-               if (wacom->slots[i] == -1)
-                       return i;
-       }
-       return -1;
-}
-
 static int int_dist(int x1, int y1, int x2, int y2)
 {
        int x = x2 - x1;
@@ -833,8 +822,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
        for (i = 0; i < contacts_to_send; i++) {
                int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
                bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
-               int id = data[offset + 1];
-               int slot = find_slot_from_contactid(wacom, id);
+               int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
 
                if (slot < 0)
                        continue;
@@ -856,9 +844,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
                        input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
                        input_report_abs(input, ABS_MT_ORIENTATION, w > h);
                }
-               wacom->slots[slot] = touch ? id : -1;
        }
-
        input_mt_report_pointer_emulation(input, true);
 
        wacom->num_contacts_left -= contacts_to_send;
@@ -895,7 +881,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
                int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
                bool touch = data[offset] & 0x1;
                int id = le16_to_cpup((__le16 *)&data[offset + 1]);
-               int slot = find_slot_from_contactid(wacom, id);
+               int slot = input_mt_get_slot_by_key(input, id);
 
                if (slot < 0)
                        continue;
@@ -908,9 +894,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
                        input_report_abs(input, ABS_MT_POSITION_X, x);
                        input_report_abs(input, ABS_MT_POSITION_Y, y);
                }
-               wacom->slots[slot] = touch ? id : -1;
        }
-
        input_mt_report_pointer_emulation(input, true);
 
        wacom->num_contacts_left -= contacts_to_send;
@@ -942,12 +926,11 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
                        contact_with_no_pen_down_count++;
                }
        }
+       input_mt_report_pointer_emulation(input, true);
 
        /* keep touch state for pen event */
        wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
 
-       input_mt_report_pointer_emulation(input, true);
-
        return 1;
 }
 
@@ -1104,12 +1087,15 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
 static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
 {
        struct input_dev *input = wacom->input;
-       int slot_id = data[0] - 2;  /* data[0] is between 2 and 17 */
        bool touch = data[1] & 0x80;
+       int slot = input_mt_get_slot_by_key(input, data[0]);
+
+       if (slot < 0)
+               return;
 
        touch = touch && !wacom->shared->stylus_in_proximity;
 
-       input_mt_slot(input, slot_id);
+       input_mt_slot(input, slot);
        input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
 
        if (touch) {
@@ -1162,7 +1148,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
                        wacom_bpt3_button_msg(wacom, data + offset);
 
        }
-
        input_mt_report_pointer_emulation(input, true);
 
        input_sync(input);
@@ -1319,6 +1304,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
        case WACOM_21UX2:
        case WACOM_22HD:
        case WACOM_24HD:
+       case DTK:
                sync = wacom_intuos_irq(wacom_wac);
                break;
 
@@ -1444,39 +1430,64 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
        return (logical_max * 100) / physical_max;
 }
 
-int wacom_setup_input_capabilities(struct input_dev *input_dev,
-                                  struct wacom_wac *wacom_wac)
+static void wacom_abs_set_axis(struct input_dev *input_dev,
+                              struct wacom_wac *wacom_wac)
 {
        struct wacom_features *features = &wacom_wac->features;
-       int i;
-
-       input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-
-       __set_bit(BTN_TOUCH, input_dev->keybit);
-
-       input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
-                            features->x_fuzz, 0);
-       input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
-                            features->y_fuzz, 0);
 
        if (features->device_type == BTN_TOOL_PEN) {
-               input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
-                            features->pressure_fuzz, 0);
+               input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
+                                    features->x_fuzz, 0);
+               input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
+                                    features->y_fuzz, 0);
+               input_set_abs_params(input_dev, ABS_PRESSURE, 0,
+                       features->pressure_max, features->pressure_fuzz, 0);
 
                /* penabled devices have fixed resolution for each model */
                input_abs_set_res(input_dev, ABS_X, features->x_resolution);
                input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
        } else {
-               input_abs_set_res(input_dev, ABS_X,
-                       wacom_calculate_touch_res(features->x_max,
-                                               features->x_phy));
-               input_abs_set_res(input_dev, ABS_Y,
-                       wacom_calculate_touch_res(features->y_max,
-                                               features->y_phy));
+               if (features->touch_max <= 2) {
+                       input_set_abs_params(input_dev, ABS_X, 0,
+                               features->x_max, features->x_fuzz, 0);
+                       input_set_abs_params(input_dev, ABS_Y, 0,
+                               features->y_max, features->y_fuzz, 0);
+                       input_abs_set_res(input_dev, ABS_X,
+                               wacom_calculate_touch_res(features->x_max,
+                                                       features->x_phy));
+                       input_abs_set_res(input_dev, ABS_Y,
+                               wacom_calculate_touch_res(features->y_max,
+                                                       features->y_phy));
+               }
+
+               if (features->touch_max > 1) {
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+                               features->x_max, features->x_fuzz, 0);
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+                               features->y_max, features->y_fuzz, 0);
+                       input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+                               wacom_calculate_touch_res(features->x_max,
+                                                       features->x_phy));
+                       input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+                               wacom_calculate_touch_res(features->y_max,
+                                                       features->y_phy));
+               }
        }
+}
 
+int wacom_setup_input_capabilities(struct input_dev *input_dev,
+                                  struct wacom_wac *wacom_wac)
+{
+       struct wacom_features *features = &wacom_wac->features;
+       int i;
+
+       input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+       __set_bit(BTN_TOUCH, input_dev->keybit);
        __set_bit(ABS_MISC, input_dev->absbit);
 
+       wacom_abs_set_axis(input_dev, wacom_wac);
+
        switch (wacom_wac->features.type) {
        case WACOM_MO:
                input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
@@ -1513,12 +1524,17 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                __set_bit(BTN_Y, input_dev->keybit);
                __set_bit(BTN_Z, input_dev->keybit);
 
-               for (i = 0; i < 10; i++)
+               for (i = 6; i < 10; i++)
                        __set_bit(BTN_0 + i, input_dev->keybit);
 
                __set_bit(KEY_PROG1, input_dev->keybit);
                __set_bit(KEY_PROG2, input_dev->keybit);
                __set_bit(KEY_PROG3, input_dev->keybit);
+               /* fall through */
+
+       case DTK:
+               for (i = 0; i < 6; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
 
                input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
                input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
@@ -1614,24 +1630,11 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                } else if (features->device_type == BTN_TOOL_FINGER) {
                        __clear_bit(ABS_MISC, input_dev->absbit);
 
-                       __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
-                       __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
-                       __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
-                       __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
-
-                       input_mt_init_slots(input_dev, features->touch_max, 0);
-
                        input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
                                             0, features->x_max, 0, 0);
                        input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
                                             0, features->y_max, 0, 0);
-
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
-                                            0, features->x_max,
-                                            features->x_fuzz, 0);
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
-                                            0, features->y_max,
-                                            features->y_fuzz, 0);
+                       input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
                }
                break;
 
@@ -1662,27 +1665,14 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        case MTSCREEN:
        case MTTPC:
-               if (features->device_type == BTN_TOOL_FINGER) {
-                       wacom_wac->slots = kmalloc(features->touch_max *
-                                                       sizeof(int),
-                                                  GFP_KERNEL);
-                       if (!wacom_wac->slots)
-                               return -ENOMEM;
-
-                       for (i = 0; i < features->touch_max; i++)
-                               wacom_wac->slots[i] = -1;
-               }
-               /* fall through */
-
        case TABLETPC2FG:
                if (features->device_type == BTN_TOOL_FINGER) {
-                       input_mt_init_slots(input_dev, features->touch_max, 0);
-                       input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
-                                       0, MT_TOOL_MAX, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
-                                       0, features->x_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
-                                       0, features->y_max, 0, 0);
+                       unsigned int flags = INPUT_MT_DIRECT;
+
+                       if (wacom_wac->features.type == TABLETPC2FG)
+                               flags = 0;
+
+                       input_mt_init_slots(input_dev, features->touch_max, flags);
                }
                /* fall through */
 
@@ -1725,35 +1715,26 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 
                if (features->device_type == BTN_TOOL_FINGER) {
+                       unsigned int flags = INPUT_MT_POINTER;
+
                        __set_bit(BTN_LEFT, input_dev->keybit);
                        __set_bit(BTN_FORWARD, input_dev->keybit);
                        __set_bit(BTN_BACK, input_dev->keybit);
                        __set_bit(BTN_RIGHT, input_dev->keybit);
 
-                       __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
-                       __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
-                       input_mt_init_slots(input_dev, features->touch_max, 0);
-
                        if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
-                               __set_bit(BTN_TOOL_TRIPLETAP,
-                                         input_dev->keybit);
-                               __set_bit(BTN_TOOL_QUADTAP,
-                                         input_dev->keybit);
-
                                input_set_abs_params(input_dev,
                                                     ABS_MT_TOUCH_MAJOR,
                                                     0, features->x_max, 0, 0);
                                input_set_abs_params(input_dev,
                                                     ABS_MT_TOUCH_MINOR,
                                                     0, features->y_max, 0, 0);
+                       } else {
+                               __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+                               __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+                               flags = 0;
                        }
-
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
-                                            0, features->x_max,
-                                            features->x_fuzz, 0);
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
-                                            0, features->y_max,
-                                            features->y_fuzz, 0);
+                       input_mt_init_slots(input_dev, features->touch_max, flags);
                } else if (features->device_type == BTN_TOOL_PEN) {
                        __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
                        __set_bit(BTN_TOOL_PEN, input_dev->keybit);
@@ -1978,6 +1959,13 @@ static const struct wacom_features wacom_features_0xCE =
 static const struct wacom_features wacom_features_0xF0 =
        { "Wacom DTU1631",        WACOM_PKGLEN_GRAPHIRE,  34623, 19553,  511,
          0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x59 = /* Pen */
+       { "Wacom DTH2242",        WACOM_PKGLEN_INTUOS,    95840, 54260, 2047,
+         63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
+static const struct wacom_features wacom_features_0x5D = /* Touch */
+       { "Wacom DTH2242",       .type = WACOM_24HDT,
+         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10 };
 static const struct wacom_features wacom_features_0xCC =
        { "Wacom Cintiq 21UX2",   WACOM_PKGLEN_INTUOS,    87200, 65600, 2047,
          63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
@@ -2152,6 +2140,8 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x43) },
        { USB_DEVICE_WACOM(0x44) },
        { USB_DEVICE_WACOM(0x45) },
+       { USB_DEVICE_WACOM(0x59) },
+       { USB_DEVICE_WACOM(0x5D) },
        { USB_DEVICE_WACOM(0xB0) },
        { USB_DEVICE_WACOM(0xB1) },
        { USB_DEVICE_WACOM(0xB2) },
index 9396d7769f86a86930b56f3f2e1fbbae024b82cf..5f9a7721e16cf36a1a92021a03ddb45648292b88 100644 (file)
@@ -78,6 +78,7 @@ enum {
        INTUOS5L,
        WACOM_21UX2,
        WACOM_22HD,
+       DTK,
        WACOM_24HD,
        CINTIQ,
        WACOM_BEE,
@@ -135,7 +136,6 @@ struct wacom_wac {
        int pid;
        int battery_capacity;
        int num_contacts_left;
-       int *slots;
 };
 
 #endif
index 515cfe7905430fb621362231ac7dd468f42a3016..f9a5fd89bc022bd50c19c571441490056d0e9d83 100644 (file)
@@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000
 
 config TOUCHSCREEN_MMS114
        tristate "MELFAS MMS114 touchscreen"
-       depends on I2C
+       depends on I2C && GENERIC_HARDIRQS
        help
          Say Y here if you have the MELFAS MMS114 touchscreen controller
          chip in your system.
index 638e20310f12e1c5a7007a097011827b99dd7fe1..861b7f77605ba9f2c107fc7080457c5a6d8f4916 100644 (file)
@@ -193,7 +193,6 @@ static struct spi_driver cyttsp_spi_driver = {
 
 module_spi_driver(cyttsp_spi_driver);
 
-MODULE_ALIAS("spi:cyttsp");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
 MODULE_AUTHOR("Cypress");
index 98841d8aa635a50540323929e462e49d358fdbc1..4a29ddf6bf1e9a9bfed9a1afcc8cba810e66800c 100644 (file)
@@ -429,12 +429,12 @@ static int mms114_probe(struct i2c_client *client,
                return -ENODEV;
        }
 
-       data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL);
-       input_dev = input_allocate_device();
+       data = devm_kzalloc(&client->dev, sizeof(struct mms114_data),
+                           GFP_KERNEL);
+       input_dev = devm_input_allocate_device(&client->dev);
        if (!data || !input_dev) {
                dev_err(&client->dev, "Failed to allocate memory\n");
-               error = -ENOMEM;
-               goto err_free_mem;
+               return -ENOMEM;
        }
 
        data->client = client;
@@ -466,57 +466,36 @@ static int mms114_probe(struct i2c_client *client,
        input_set_drvdata(input_dev, data);
        i2c_set_clientdata(client, data);
 
-       data->core_reg = regulator_get(&client->dev, "avdd");
+       data->core_reg = devm_regulator_get(&client->dev, "avdd");
        if (IS_ERR(data->core_reg)) {
                error = PTR_ERR(data->core_reg);
                dev_err(&client->dev,
                        "Unable to get the Core regulator (%d)\n", error);
-               goto err_free_mem;
+               return error;
        }
 
-       data->io_reg = regulator_get(&client->dev, "vdd");
+       data->io_reg = devm_regulator_get(&client->dev, "vdd");
        if (IS_ERR(data->io_reg)) {
                error = PTR_ERR(data->io_reg);
                dev_err(&client->dev,
                        "Unable to get the IO regulator (%d)\n", error);
-               goto err_core_reg;
+               return error;
        }
 
-       error = request_threaded_irq(client->irq, NULL, mms114_interrupt,
-                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data);
+       error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+                       mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                       dev_name(&client->dev), data);
        if (error) {
                dev_err(&client->dev, "Failed to register interrupt\n");
-               goto err_io_reg;
+               return error;
        }
        disable_irq(client->irq);
 
        error = input_register_device(data->input_dev);
-       if (error)
-               goto err_free_irq;
-
-       return 0;
-
-err_free_irq:
-       free_irq(client->irq, data);
-err_io_reg:
-       regulator_put(data->io_reg);
-err_core_reg:
-       regulator_put(data->core_reg);
-err_free_mem:
-       input_free_device(input_dev);
-       kfree(data);
-       return error;
-}
-
-static int mms114_remove(struct i2c_client *client)
-{
-       struct mms114_data *data = i2c_get_clientdata(client);
-
-       free_irq(client->irq, data);
-       regulator_put(data->io_reg);
-       regulator_put(data->core_reg);
-       input_unregister_device(data->input_dev);
-       kfree(data);
+       if (error) {
+               dev_err(&client->dev, "Failed to register input device\n");
+               return error;
+       }
 
        return 0;
 }
@@ -590,7 +569,6 @@ static struct i2c_driver mms114_driver = {
                .of_match_table = of_match_ptr(mms114_dt_match),
        },
        .probe          = mms114_probe,
-       .remove         = mms114_remove,
        .id_table       = mms114_id,
 };
 
index 84d884b4ec3ef5b3d757bbff5826ec5bfb438a45..59e81b00f244c1852eca936493538e52d4eb3085 100644 (file)
@@ -120,6 +120,7 @@ static void stmpe_work(struct work_struct *work)
        __stmpe_reset_fifo(ts->stmpe);
 
        input_report_abs(ts->idev, ABS_PRESSURE, 0);
+       input_report_key(ts->idev, BTN_TOUCH, 0);
        input_sync(ts->idev);
 }
 
@@ -153,6 +154,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data)
        input_report_abs(ts->idev, ABS_X, x);
        input_report_abs(ts->idev, ABS_Y, y);
        input_report_abs(ts->idev, ABS_PRESSURE, z);
+       input_report_key(ts->idev, BTN_TOUCH, 1);
        input_sync(ts->idev);
 
        /* flush the FIFO after we have read out our values. */
index 9c0cdc7ea4493d7e9f973c7260baad69d19f74a9..7213e8b07e79bfa48d8b0e5d1f89d5684fee711c 100644 (file)
@@ -753,3 +753,4 @@ module_spi_driver(tsc2005_driver);
 MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
 MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:tsc2005");
index f88fab56178cd5852b5844ea106335a613dc9014..6be2eb6a153a5588180da011fd91611d2ad597ad 100644 (file)
@@ -247,7 +247,7 @@ static int wm831x_ts_probe(struct platform_device *pdev)
 
        wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts),
                                 GFP_KERNEL);
-       input_dev = input_allocate_device();
+       input_dev = devm_input_allocate_device(&pdev->dev);
        if (!wm831x_ts || !input_dev) {
                error = -ENOMEM;
                goto err_alloc;
@@ -376,7 +376,6 @@ err_pd_irq:
 err_data_irq:
        free_irq(wm831x_ts->data_irq, wm831x_ts);
 err_alloc:
-       input_free_device(input_dev);
 
        return error;
 }
@@ -387,7 +386,6 @@ static int wm831x_ts_remove(struct platform_device *pdev)
 
        free_irq(wm831x_ts->pd_irq, wm831x_ts);
        free_irq(wm831x_ts->data_irq, wm831x_ts);
-       input_unregister_device(wm831x_ts->input_dev);
 
        return 0;
 }
index 40e5b3919e279ee771e9c99603b119bf175492c3..814655ee2d6126bace613a3464d81475f2be9362 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/slab.h>
 #include <linux/input.h>
 #include <linux/uaccess.h>
+#include <linux/moduleparam.h>
 
 #include <asm/ptrace.h>
 #include <asm/irq_regs.h>
@@ -578,8 +579,71 @@ struct sysrq_state {
        bool active;
        bool need_reinject;
        bool reinjecting;
+
+       /* reset sequence handling */
+       bool reset_canceled;
+       unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)];
+       int reset_seq_len;
+       int reset_seq_cnt;
+       int reset_seq_version;
 };
 
+#define SYSRQ_KEY_RESET_MAX    20 /* Should be plenty */
+static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX];
+static unsigned int sysrq_reset_seq_len;
+static unsigned int sysrq_reset_seq_version = 1;
+
+static void sysrq_parse_reset_sequence(struct sysrq_state *state)
+{
+       int i;
+       unsigned short key;
+
+       state->reset_seq_cnt = 0;
+
+       for (i = 0; i < sysrq_reset_seq_len; i++) {
+               key = sysrq_reset_seq[i];
+
+               if (key == KEY_RESERVED || key > KEY_MAX)
+                       break;
+
+               __set_bit(key, state->reset_keybit);
+               state->reset_seq_len++;
+
+               if (test_bit(key, state->key_down))
+                       state->reset_seq_cnt++;
+       }
+
+       /* Disable reset until old keys are not released */
+       state->reset_canceled = state->reset_seq_cnt != 0;
+
+       state->reset_seq_version = sysrq_reset_seq_version;
+}
+
+static bool sysrq_detect_reset_sequence(struct sysrq_state *state,
+                                       unsigned int code, int value)
+{
+       if (!test_bit(code, state->reset_keybit)) {
+               /*
+                * Pressing any key _not_ in reset sequence cancels
+                * the reset sequence.
+                */
+               if (value && state->reset_seq_cnt)
+                       state->reset_canceled = true;
+       } else if (value == 0) {
+               /* key release */
+               if (--state->reset_seq_cnt == 0)
+                       state->reset_canceled = false;
+       } else if (value == 1) {
+               /* key press, not autorepeat */
+               if (++state->reset_seq_cnt == state->reset_seq_len &&
+                   !state->reset_canceled) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
 static void sysrq_reinject_alt_sysrq(struct work_struct *work)
 {
        struct sysrq_state *sysrq =
@@ -606,100 +670,121 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work)
        }
 }
 
-static bool sysrq_filter(struct input_handle *handle,
-                        unsigned int type, unsigned int code, int value)
+static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
+                                 unsigned int code, int value)
 {
-       struct sysrq_state *sysrq = handle->private;
        bool was_active = sysrq->active;
        bool suppress;
 
-       /*
-        * Do not filter anything if we are in the process of re-injecting
-        * Alt+SysRq combination.
-        */
-       if (sysrq->reinjecting)
-               return false;
+       switch (code) {
 
-       switch (type) {
+       case KEY_LEFTALT:
+       case KEY_RIGHTALT:
+               if (!value) {
+                       /* One of ALTs is being released */
+                       if (sysrq->active && code == sysrq->alt_use)
+                               sysrq->active = false;
 
-       case EV_SYN:
-               suppress = false;
+                       sysrq->alt = KEY_RESERVED;
+
+               } else if (value != 2) {
+                       sysrq->alt = code;
+                       sysrq->need_reinject = false;
+               }
                break;
 
-       case EV_KEY:
-               switch (code) {
+       case KEY_SYSRQ:
+               if (value == 1 && sysrq->alt != KEY_RESERVED) {
+                       sysrq->active = true;
+                       sysrq->alt_use = sysrq->alt;
+                       /*
+                        * If nothing else will be pressed we'll need
+                        * to re-inject Alt-SysRq keysroke.
+                        */
+                       sysrq->need_reinject = true;
+               }
 
-               case KEY_LEFTALT:
-               case KEY_RIGHTALT:
-                       if (!value) {
-                               /* One of ALTs is being released */
-                               if (sysrq->active && code == sysrq->alt_use)
-                                       sysrq->active = false;
+               /*
+                * Pretend that sysrq was never pressed at all. This
+                * is needed to properly handle KGDB which will try
+                * to release all keys after exiting debugger. If we
+                * do not clear key bit it KGDB will end up sending
+                * release events for Alt and SysRq, potentially
+                * triggering print screen function.
+                */
+               if (sysrq->active)
+                       clear_bit(KEY_SYSRQ, sysrq->handle.dev->key);
 
-                               sysrq->alt = KEY_RESERVED;
+               break;
 
-                       } else if (value != 2) {
-                               sysrq->alt = code;
-                               sysrq->need_reinject = false;
-                       }
-                       break;
+       default:
+               if (sysrq->active && value && value != 2) {
+                       sysrq->need_reinject = false;
+                       __handle_sysrq(sysrq_xlate[code], true);
+               }
+               break;
+       }
 
-               case KEY_SYSRQ:
-                       if (value == 1 && sysrq->alt != KEY_RESERVED) {
-                               sysrq->active = true;
-                               sysrq->alt_use = sysrq->alt;
-                               /*
-                                * If nothing else will be pressed we'll need
-                                * to re-inject Alt-SysRq keysroke.
-                                */
-                               sysrq->need_reinject = true;
-                       }
+       suppress = sysrq->active;
 
-                       /*
-                        * Pretend that sysrq was never pressed at all. This
-                        * is needed to properly handle KGDB which will try
-                        * to release all keys after exiting debugger. If we
-                        * do not clear key bit it KGDB will end up sending
-                        * release events for Alt and SysRq, potentially
-                        * triggering print screen function.
-                        */
-                       if (sysrq->active)
-                               clear_bit(KEY_SYSRQ, handle->dev->key);
+       if (!sysrq->active) {
 
-                       break;
+               /*
+                * See if reset sequence has changed since the last time.
+                */
+               if (sysrq->reset_seq_version != sysrq_reset_seq_version)
+                       sysrq_parse_reset_sequence(sysrq);
 
-               default:
-                       if (sysrq->active && value && value != 2) {
-                               sysrq->need_reinject = false;
-                               __handle_sysrq(sysrq_xlate[code], true);
-                       }
-                       break;
+               /*
+                * If we are not suppressing key presses keep track of
+                * keyboard state so we can release keys that have been
+                * pressed before entering SysRq mode.
+                */
+               if (value)
+                       set_bit(code, sysrq->key_down);
+               else
+                       clear_bit(code, sysrq->key_down);
+
+               if (was_active)
+                       schedule_work(&sysrq->reinject_work);
+
+               if (sysrq_detect_reset_sequence(sysrq, code, value)) {
+                       /* Force emergency reboot */
+                       __handle_sysrq(sysrq_xlate[KEY_B], false);
                }
 
-               suppress = sysrq->active;
+       } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) {
+               /*
+                * Pass on release events for keys that was pressed before
+                * entering SysRq mode.
+                */
+               suppress = false;
+       }
 
-               if (!sysrq->active) {
-                       /*
-                        * If we are not suppressing key presses keep track of
-                        * keyboard state so we can release keys that have been
-                        * pressed before entering SysRq mode.
-                        */
-                       if (value)
-                               set_bit(code, sysrq->key_down);
-                       else
-                               clear_bit(code, sysrq->key_down);
+       return suppress;
+}
 
-                       if (was_active)
-                               schedule_work(&sysrq->reinject_work);
+static bool sysrq_filter(struct input_handle *handle,
+                        unsigned int type, unsigned int code, int value)
+{
+       struct sysrq_state *sysrq = handle->private;
+       bool suppress;
 
-               } else if (value == 0 &&
-                          test_and_clear_bit(code, sysrq->key_down)) {
-                       /*
-                        * Pass on release events for keys that was pressed before
-                        * entering SysRq mode.
-                        */
-                       suppress = false;
-               }
+       /*
+        * Do not filter anything if we are in the process of re-injecting
+        * Alt+SysRq combination.
+        */
+       if (sysrq->reinjecting)
+               return false;
+
+       switch (type) {
+
+       case EV_SYN:
+               suppress = false;
+               break;
+
+       case EV_KEY:
+               suppress = sysrq_handle_keypress(sysrq, code, value);
                break;
 
        default:
@@ -787,7 +872,20 @@ static bool sysrq_handler_registered;
 
 static inline void sysrq_register_handler(void)
 {
+       extern unsigned short platform_sysrq_reset_seq[] __weak;
+       unsigned short key;
        int error;
+       int i;
+
+       if (platform_sysrq_reset_seq) {
+               for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) {
+                       key = platform_sysrq_reset_seq[i];
+                       if (key == KEY_RESERVED || key > KEY_MAX)
+                               break;
+
+                       sysrq_reset_seq[sysrq_reset_seq_len++] = key;
+               }
+       }
 
        error = input_register_handler(&sysrq_handler);
        if (error)
@@ -804,6 +902,36 @@ static inline void sysrq_unregister_handler(void)
        }
 }
 
+static int sysrq_reset_seq_param_set(const char *buffer,
+                                    const struct kernel_param *kp)
+{
+       unsigned long val;
+       int error;
+
+       error = strict_strtoul(buffer, 0, &val);
+       if (error < 0)
+               return error;
+
+       if (val > KEY_MAX)
+               return -EINVAL;
+
+       *((unsigned short *)kp->arg) = val;
+       sysrq_reset_seq_version++;
+
+       return 0;
+}
+
+static struct kernel_param_ops param_ops_sysrq_reset_seq = {
+       .get    = param_get_ushort,
+       .set    = sysrq_reset_seq_param_set,
+};
+
+#define param_check_sysrq_reset_seq(name, p)   \
+       __param_check(name, p, unsigned short)
+
+module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq,
+                        &sysrq_reset_seq_len, 0644);
+
 #else
 
 static inline void sysrq_register_handler(void)
index 7911fda23bb439975d7e9b0b0941b94a2ea3c910..97ade7cdc8708840897de448ccbba138dfcabd99 100644 (file)
 
 #define BMA150_DRIVER          "bma150"
 
+#define BMA150_RANGE_2G                0
+#define BMA150_RANGE_4G                1
+#define BMA150_RANGE_8G                2
+
+#define BMA150_BW_25HZ         0
+#define BMA150_BW_50HZ         1
+#define BMA150_BW_100HZ                2
+#define BMA150_BW_190HZ                3
+#define BMA150_BW_375HZ                4
+#define BMA150_BW_750HZ                5
+#define BMA150_BW_1500HZ       6
+
 struct bma150_cfg {
        bool any_motion_int;            /* Set to enable any-motion interrupt */
        bool hg_int;                    /* Set to enable high-G interrupt */
@@ -34,8 +46,8 @@ struct bma150_cfg {
        unsigned char lg_hyst;          /* Low-G hysterisis */
        unsigned char lg_dur;           /* Low-G duration */
        unsigned char lg_thres;         /* Low-G threshold */
-       unsigned char range;            /* BMA0150_RANGE_xxx (in G) */
-       unsigned char bandwidth;        /* BMA0150_BW_xxx (in Hz) */
+       unsigned char range;            /* one of BMA0150_RANGE_xxx */
+       unsigned char bandwidth;        /* one of BMA0150_BW_xxx */
 };
 
 struct bma150_platform_data {
index 57e01a7cb0060eb5e02f76ca72d18ccf707d2731..010d98175efa78de44e641671cf7a3545aa959d8 100644 (file)
@@ -13,6 +13,8 @@
 #ifndef __LINUX_INPUT_ADXL34X_H__
 #define __LINUX_INPUT_ADXL34X_H__
 
+#include <linux/input.h>
+
 struct adxl34x_platform_data {
 
        /*
diff --git a/include/linux/input/tegra_kbc.h b/include/linux/input/tegra_kbc.h
deleted file mode 100644 (file)
index a130256..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Platform definitions for tegra-kbc keyboard input driver
- *
- * Copyright (c) 2010-2011, NVIDIA Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-#ifndef ASMARM_ARCH_TEGRA_KBC_H
-#define ASMARM_ARCH_TEGRA_KBC_H
-
-#include <linux/types.h>
-#include <linux/input/matrix_keypad.h>
-
-#define KBC_MAX_GPIO   24
-#define KBC_MAX_KPENT  8
-
-#define KBC_MAX_ROW    16
-#define KBC_MAX_COL    8
-#define KBC_MAX_KEY    (KBC_MAX_ROW * KBC_MAX_COL)
-
-enum tegra_pin_type {
-       PIN_CFG_IGNORE,
-       PIN_CFG_COL,
-       PIN_CFG_ROW,
-};
-
-struct tegra_kbc_pin_cfg {
-       enum tegra_pin_type type;
-       unsigned char num;
-};
-
-struct tegra_kbc_wake_key {
-       u8 row:4;
-       u8 col:4;
-};
-
-struct tegra_kbc_platform_data {
-       unsigned int debounce_cnt;
-       unsigned int repeat_cnt;
-
-       struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO];
-       const struct matrix_keymap_data *keymap_data;
-
-       u32 wakeup_key;
-       bool wakeup;
-       bool use_fn_map;
-       bool use_ghost_filter;
-};
-#endif
index 79603a6c356fb5dd28f2bddf27bceafbf263c7d2..4ad06e824f76f797250b7cef01ac5932ffa7452b 100644 (file)
@@ -36,7 +36,7 @@ struct ps2dev {
        wait_queue_head_t wait;
 
        unsigned long flags;
-       unsigned char cmdbuf[6];
+       unsigned char cmdbuf[8];
        unsigned char cmdcnt;
        unsigned char nak;
 };