Merge commit 'v3.2-rc3' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 29 Nov 2011 09:51:07 +0000 (01:51 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 29 Nov 2011 09:51:07 +0000 (01:51 -0800)
40 files changed:
Documentation/input/alps.txt [new file with mode: 0644]
arch/arm/plat-samsung/include/plat/keypad.h
drivers/input/input-polldev.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/lm8323.c
drivers/input/keyboard/samsung-keypad.c
drivers/input/keyboard/tca8418_keypad.c [new file with mode: 0644]
drivers/input/misc/adxl34x.c
drivers/input/misc/ati_remote2.c
drivers/input/mouse/alps.c
drivers/input/mouse/alps.h
drivers/input/mouse/elantech.c
drivers/input/mouse/elantech.h
drivers/input/mouse/hgpk.c
drivers/input/mouse/logips2pp.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/sentelic.c
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/mouse/trackpoint.c
drivers/input/serio/i8042.c
drivers/input/tablet/aiptek.c
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ad7877.c
drivers/input/touchscreen/ad7879-i2c.c
drivers/input/touchscreen/ad7879-spi.c
drivers/input/touchscreen/ad7879.c
drivers/input/touchscreen/ad7879.h
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/egalax_ts.c [new file with mode: 0644]
drivers/input/touchscreen/usbtouchscreen.c
include/linux/input/samsung-keypad.h [new file with mode: 0644]
include/linux/input/tca8418_keypad.h [new file with mode: 0644]

diff --git a/Documentation/input/alps.txt b/Documentation/input/alps.txt
new file mode 100644 (file)
index 0000000..f274c28
--- /dev/null
@@ -0,0 +1,188 @@
+ALPS Touchpad Protocol
+----------------------
+
+Introduction
+------------
+
+Currently the ALPS touchpad driver supports four protocol versions in use by
+ALPS touchpads, called versions 1, 2, 3, and 4. Information about the various
+protocol versions is contained in the following sections.
+
+Detection
+---------
+
+All ALPS touchpads should respond to the "E6 report" command sequence:
+E8-E6-E6-E6-E9. An ALPS touchpad should respond with either 00-00-0A or
+00-00-64.
+
+If the E6 report is successful, the touchpad model is identified using the "E7
+report" sequence: E8-E7-E7-E7-E9. The response is the model signature and is
+matched against known models in the alps_model_data_array.
+
+With protocol versions 3 and 4, the E7 report model signature is always
+73-02-64. To differentiate between these versions, the response from the
+"Enter Command Mode" sequence must be inspected as described below.
+
+Command Mode
+------------
+
+Protocol versions 3 and 4 have a command mode that is used to read and write
+one-byte device registers in a 16-bit address space. The command sequence
+EC-EC-EC-E9 places the device in command mode, and the device will respond
+with 88-07 followed by a third byte. This third byte can be used to determine
+whether the devices uses the version 3 or 4 protocol.
+
+To exit command mode, PSMOUSE_CMD_SETSTREAM (EA) is sent to the touchpad.
+
+While in command mode, register addresses can be set by first sending a
+specific command, either EC for v3 devices or F5 for v4 devices. Then the
+address is sent one nibble at a time, where each nibble is encoded as a
+command with optional data. This enoding differs slightly between the v3 and
+v4 protocols.
+
+Once an address has been set, the addressed register can be read by sending
+PSMOUSE_CMD_GETINFO (E9). The first two bytes of the response contains the
+address of the register being read, and the third contains the value of the
+register. Registers are written by writing the value one nibble at a time
+using the same encoding used for addresses.
+
+Packet Format
+-------------
+
+In the following tables, the following notation is used.
+
+ CAPITALS = stick, miniscules = touchpad
+
+?'s can have different meanings on different models, such as wheel rotation,
+extra buttons, stick buttons on a dualpoint, etc.
+
+PS/2 packet format
+------------------
+
+ byte 0:  0    0 YSGN XSGN    1    M    R    L
+ byte 1: X7   X6   X5   X4   X3   X2   X1   X0
+ byte 2: Y7   Y6   Y5   Y4   Y3   Y2   Y1   Y0
+
+Note that the device never signals overflow condition.
+
+ALPS Absolute Mode - Protocol Verion 1
+--------------------------------------
+
+ byte 0:  1    0    0    0    1   x9   x8   x7
+ byte 1:  0   x6   x5   x4   x3   x2   x1   x0
+ byte 2:  0    ?    ?    l    r    ?  fin  ges
+ byte 3:  0    ?    ?    ?    ?   y9   y8   y7
+ byte 4:  0   y6   y5   y4   y3   y2   y1   y0
+ byte 5:  0   z6   z5   z4   z3   z2   z1   z0
+
+ALPS Absolute Mode - Protocol Version 2
+---------------------------------------
+
+ byte 0:  1    ?    ?    ?    1    ?    ?    ?
+ byte 1:  0   x6   x5   x4   x3   x2   x1   x0
+ byte 2:  0  x10   x9   x8   x7    ?  fin  ges
+ byte 3:  0   y9   y8   y7    1    M    R    L
+ byte 4:  0   y6   y5   y4   y3   y2   y1   y0
+ byte 5:  0   z6   z5   z4   z3   z2   z1   z0
+
+Dualpoint device -- interleaved packet format
+---------------------------------------------
+
+ byte 0:    1    1    0    0    1    1    1    1
+ byte 1:    0   x6   x5   x4   x3   x2   x1   x0
+ byte 2:    0  x10   x9   x8   x7    0  fin  ges
+ byte 3:    0    0 YSGN XSGN    1    1    1    1
+ byte 4:   X7   X6   X5   X4   X3   X2   X1   X0
+ byte 5:   Y7   Y6   Y5   Y4   Y3   Y2   Y1   Y0
+ byte 6:    0   y9   y8   y7    1    m    r    l
+ byte 7:    0   y6   y5   y4   y3   y2   y1   y0
+ byte 8:    0   z6   z5   z4   z3   z2   z1   z0
+
+ALPS Absolute Mode - Protocol Version 3
+---------------------------------------
+
+ALPS protocol version 3 has three different packet formats. The first two are
+associated with touchpad events, and the third is associatd with trackstick
+events.
+
+The first type is the touchpad position packet.
+
+ byte 0:    1    ?   x1   x0    1    1    1    1
+ byte 1:    0  x10   x9   x8   x7   x6   x5   x4
+ byte 2:    0  y10   y9   y8   y7   y6   y5   y4
+ byte 3:    0    M    R    L    1    m    r    l
+ byte 4:    0   mt   x3   x2   y3   y2   y1   y0
+ byte 5:    0   z6   z5   z4   z3   z2   z1   z0
+
+Note that for some devices the trackstick buttons are reported in this packet,
+and on others it is reported in the trackstick packets.
+
+The second packet type contains bitmaps representing the x and y axes. In the
+bitmaps a given bit is set if there is a finger covering that position on the
+given axis. Thus the bitmap packet can be used for low-resolution multi-touch
+data, although finger tracking is not possible.  This packet also encodes the
+number of contacts (f1 and f0 in the table below).
+
+ byte 0:    1    1   x1   x0    1    1    1    1
+ byte 1:    0   x8   x7   x6   x5   x4   x3   x2
+ byte 2:    0   y7   y6   y5   y4   y3   y2   y1
+ byte 3:    0  y10   y9   y8    1    1    1    1
+ byte 4:    0  x14  x13  x12  x11  x10   x9   y0
+ byte 5:    0    1    ?    ?    ?    ?   f1   f0
+
+This packet only appears after a position packet with the mt bit set, and
+ususally only appears when there are two or more contacts (although
+ocassionally it's seen with only a single contact).
+
+The final v3 packet type is the trackstick packet.
+
+ byte 0:    1    1   x7   y7    1    1    1    1
+ byte 1:    0   x6   x5   x4   x3   x2   x1   x0
+ byte 2:    0   y6   y5   y4   y3   y2   y1   y0
+ byte 3:    0    1    0    0    1    0    0    0
+ byte 4:    0   z4   z3   z2   z1   z0    ?    ?
+ byte 5:    0    0    1    1    1    1    1    1
+
+ALPS Absolute Mode - Protocol Version 4
+---------------------------------------
+
+Protocol version 4 has an 8-byte packet format.
+
+ byte 0:    1    ?   x1   x0    1    1    1    1
+ byte 1:    0  x10   x9   x8   x7   x6   x5   x4
+ byte 2:    0  y10   y9   y8   y7   y6   y5   y4
+ byte 3:    0    1   x3   x2   y3   y2   y1   y0
+ byte 4:    0    ?    ?    ?    1    ?    r    l
+ byte 5:    0   z6   z5   z4   z3   z2   z1   z0
+ byte 6:    bitmap data (described below)
+ byte 7:    bitmap data (described below)
+
+The last two bytes represent a partial bitmap packet, with 3 full packets
+required to construct a complete bitmap packet.  Once assembled, the 6-byte
+bitmap packet has the following format:
+
+ byte 0:    0    1   x7   x6   x5   x4   x3   x2
+ byte 1:    0   x1   x0   y4   y3   y2   y1   y0
+ byte 2:    0    0    ?  x14  x13  x12  x11  x10
+ byte 3:    0   x9   x8   y9   y8   y7   y6   y5
+ byte 4:    0    0    0    0    0    0    0    0
+ byte 5:    0    0    0    0    0    0    0  y10
+
+There are several things worth noting here.
+
+ 1) In the bitmap data, bit 6 of byte 0 serves as a sync byte to
+    identify the first fragment of a bitmap packet.
+
+ 2) The bitmaps represent the same data as in the v3 bitmap packets, although
+    the packet layout is different.
+
+ 3) There doesn't seem to be a count of the contact points anywhere in the v4
+    protocol packets. Deriving a count of contact points must be done by
+    analyzing the bitmaps.
+
+ 4) There is a 3 to 1 ratio of position packets to bitmap packets. Therefore
+    MT position can only be updated for every third ST position update, and
+    the count of contact points can only be updated every third packet as
+    well.
+
+So far no v4 devices with tracksticks have been encountered.
index b59a6483cd8a85fa3c5b8253f69a3a22c7753df5..c81ace332a1ea102721e50a3ba2162381193c9ef 100644 (file)
 #ifndef __PLAT_SAMSUNG_KEYPAD_H
 #define __PLAT_SAMSUNG_KEYPAD_H
 
-#include <linux/input/matrix_keypad.h>
-
-#define SAMSUNG_MAX_ROWS       8
-#define SAMSUNG_MAX_COLS       8
-
-/**
- * struct samsung_keypad_platdata - Platform device data for Samsung Keypad.
- * @keymap_data: pointer to &matrix_keymap_data.
- * @rows: number of keypad row supported.
- * @cols: number of keypad col supported.
- * @no_autorepeat: disable key autorepeat.
- * @wakeup: controls whether the device should be set up as wakeup source.
- * @cfg_gpio: configure the GPIO.
- *
- * Initialisation data specific to either the machine or the platform
- * for the device driver to use or call-back when configuring gpio.
- */
-struct samsung_keypad_platdata {
-       const struct matrix_keymap_data *keymap_data;
-       unsigned int rows;
-       unsigned int cols;
-       bool no_autorepeat;
-       bool wakeup;
-
-       void (*cfg_gpio)(unsigned int rows, unsigned int cols);
-};
+#include <linux/input/samsung-keypad.h>
 
 /**
  * samsung_keypad_set_platdata - Set platform data for Samsung Keypad device.
index 7dfe1009fae09b6101133d8e471a0d4b1ef039cd..7f161d93203c09a7fbb31a6522ed81c00d47ca13 100644 (file)
@@ -84,10 +84,12 @@ static ssize_t input_polldev_set_poll(struct device *dev,
 {
        struct input_polled_dev *polldev = dev_get_drvdata(dev);
        struct input_dev *input = polldev->input;
-       unsigned long interval;
+       unsigned int interval;
+       int err;
 
-       if (strict_strtoul(buf, 0, &interval))
-               return -EINVAL;
+       err = kstrtouint(buf, 0, &interval);
+       if (err)
+               return err;
 
        if (interval < polldev->poll_interval_min)
                return -EINVAL;
index 615c21f2a5539d786ec3188c501558a2084a2b46..cdc385b2cf7d2c2941e87b799f0f3ea50e9e7d5a 100644 (file)
@@ -221,6 +221,22 @@ config KEYBOARD_TCA6416
          To compile this driver as a module, choose M here: the
          module will be called tca6416_keypad.
 
+config KEYBOARD_TCA8418
+       tristate "TCA8418 Keypad Support"
+       depends on I2C
+       help
+         This driver implements basic keypad functionality
+         for keys connected through TCA8418 keypad decoder.
+
+         Say Y here if your device has keys connected to
+         TCA8418 keypad decoder.
+
+         If enabled the complete TCA8418 device will be managed through
+         this driver.
+
+         To compile this driver as a module, choose M here: the
+         module will be called tca8418_keypad.
+
 config KEYBOARD_MATRIX
        tristate "GPIO driven matrix keypad support"
        depends on GENERIC_GPIO
@@ -425,9 +441,10 @@ config KEYBOARD_PMIC8XXX
 
 config KEYBOARD_SAMSUNG
        tristate "Samsung keypad support"
-       depends on SAMSUNG_DEV_KEYPAD
+       depends on HAVE_CLK
        help
-         Say Y here if you want to use the Samsung keypad.
+         Say Y here if you want to use the keypad on your Samsung mobile
+         device.
 
          To compile this driver as a module, choose M here: the
          module will be called samsung-keypad.
index ddde0fd476f744c2aa2cd250362f809c4469310b..df7061f129184f4c5dfb6265229529fe1387dcf7 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_KEYBOARD_EP93XX)         += ep93xx_keypad.o
 obj-$(CONFIG_KEYBOARD_GPIO)            += gpio_keys.o
 obj-$(CONFIG_KEYBOARD_GPIO_POLLED)     += gpio_keys_polled.o
 obj-$(CONFIG_KEYBOARD_TCA6416)         += tca6416-keypad.o
+obj-$(CONFIG_KEYBOARD_TCA8418)         += tca8418_keypad.o
 obj-$(CONFIG_KEYBOARD_HIL)             += hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)         += hilkbd.o
 obj-$(CONFIG_KEYBOARD_IMX)             += imx_keypad.o
index 19cfc0cf558c99b18875ca0fd0b6aac26134e356..e05a2e7073c6f5424cb4adeaf1ae76728f16ac35 100644 (file)
@@ -1305,7 +1305,7 @@ static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
 static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
 {
        struct input_dev *old_dev, *new_dev;
-       unsigned long value;
+       unsigned int value;
        int err;
        bool old_extra;
        unsigned char old_set;
@@ -1313,7 +1313,11 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun
        if (!atkbd->write)
                return -EIO;
 
-       if (strict_strtoul(buf, 10, &value) || value > 1)
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
                return -EINVAL;
 
        if (atkbd->extra != value) {
@@ -1389,11 +1393,15 @@ static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
 static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
 {
        struct input_dev *old_dev, *new_dev;
-       unsigned long value;
+       unsigned int value;
        int err;
        bool old_scroll;
 
-       if (strict_strtoul(buf, 10, &value) || value > 1)
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
                return -EINVAL;
 
        if (atkbd->scroll != value) {
@@ -1433,7 +1441,7 @@ static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf)
 static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
 {
        struct input_dev *old_dev, *new_dev;
-       unsigned long value;
+       unsigned int value;
        int err;
        unsigned char old_set;
        bool old_extra;
@@ -1441,7 +1449,11 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
        if (!atkbd->write)
                return -EIO;
 
-       if (strict_strtoul(buf, 10, &value) || (value != 2 && value != 3))
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value != 2 && value != 3)
                return -EINVAL;
 
        if (atkbd->set != value) {
@@ -1484,14 +1496,18 @@ static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf)
 static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
 {
        struct input_dev *old_dev, *new_dev;
-       unsigned long value;
+       unsigned int value;
        int err;
        bool old_softrepeat, old_softraw;
 
        if (!atkbd->write)
                return -EIO;
 
-       if (strict_strtoul(buf, 10, &value) || value > 1)
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
                return -EINVAL;
 
        if (atkbd->softrepeat != value) {
@@ -1534,11 +1550,15 @@ static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf)
 static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
 {
        struct input_dev *old_dev, *new_dev;
-       unsigned long value;
+       unsigned int value;
        int err;
        bool old_softraw;
 
-       if (strict_strtoul(buf, 10, &value) || value > 1)
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
                return -EINVAL;
 
        if (atkbd->softraw != value) {
index 82d1dc8badd5472df8ca2cfd6d90c82a06076be7..21823bfc7911b5284426e17568f25a82b56ad1de 100644 (file)
@@ -545,13 +545,12 @@ static ssize_t lm8323_pwm_store_time(struct device *dev,
 {
        struct led_classdev *led_cdev = dev_get_drvdata(dev);
        struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
-       int ret;
-       unsigned long time;
+       int ret, time;
 
-       ret = strict_strtoul(buf, 10, &time);
+       ret = kstrtoint(buf, 10, &time);
        /* Numbers only, please. */
        if (ret)
-               return -EINVAL;
+               return ret;
 
        pwm->fade_time = time;
 
@@ -613,9 +612,9 @@ static ssize_t lm8323_set_disable(struct device *dev,
 {
        struct lm8323_chip *lm = dev_get_drvdata(dev);
        int ret;
-       unsigned long i;
+       unsigned int i;
 
-       ret = strict_strtoul(buf, 10, &i);
+       ret = kstrtouint(buf, 10, &i);
 
        mutex_lock(&lm->lock);
        lm->kp_enabled = !i;
index f689f49e31093d406a8a93e78827d797f9d9aa4f..1a2b755564f26dfd6dee704e18dfb9bec982ae84 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
-#include <plat/keypad.h>
+#include <linux/input/samsung-keypad.h>
 
 #define SAMSUNG_KEYIFCON                       0x00
 #define SAMSUNG_KEYIFSTSCLR                    0x04
@@ -381,7 +381,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
                                         bool enable)
 {
@@ -440,13 +440,11 @@ static int samsung_keypad_resume(struct device *dev)
 
        return 0;
 }
-
-static const struct dev_pm_ops samsung_keypad_pm_ops = {
-       .suspend        = samsung_keypad_suspend,
-       .resume         = samsung_keypad_resume,
-};
 #endif
 
+static SIMPLE_DEV_PM_OPS(samsung_keypad_pm_ops,
+                        samsung_keypad_suspend, samsung_keypad_resume);
+
 static struct platform_device_id samsung_keypad_driver_ids[] = {
        {
                .name           = "samsung-keypad",
@@ -465,9 +463,7 @@ static struct platform_driver samsung_keypad_driver = {
        .driver         = {
                .name   = "samsung-keypad",
                .owner  = THIS_MODULE,
-#ifdef CONFIG_PM
                .pm     = &samsung_keypad_pm_ops,
-#endif
        },
        .id_table       = samsung_keypad_driver_ids,
 };
diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c
new file mode 100644 (file)
index 0000000..958ec10
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Driver for TCA8418 I2C keyboard
+ *
+ * Copyright (C) 2011 Fuel7, Inc.  All rights reserved.
+ *
+ * Author: Kyle Manna <kyle.manna@fuel7.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ * If you can't comply with GPLv2, alternative licensing terms may be
+ * arranged. Please contact Fuel7, Inc. (http://fuel7.com/) for proprietary
+ * alternative licensing inquiries.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/tca8418_keypad.h>
+
+/* TCA8418 hardware limits */
+#define TCA8418_MAX_ROWS       8
+#define TCA8418_MAX_COLS       10
+
+/* TCA8418 register offsets */
+#define REG_CFG                        0x01
+#define REG_INT_STAT           0x02
+#define REG_KEY_LCK_EC         0x03
+#define REG_KEY_EVENT_A                0x04
+#define REG_KEY_EVENT_B                0x05
+#define REG_KEY_EVENT_C                0x06
+#define REG_KEY_EVENT_D                0x07
+#define REG_KEY_EVENT_E                0x08
+#define REG_KEY_EVENT_F                0x09
+#define REG_KEY_EVENT_G                0x0A
+#define REG_KEY_EVENT_H                0x0B
+#define REG_KEY_EVENT_I                0x0C
+#define REG_KEY_EVENT_J                0x0D
+#define REG_KP_LCK_TIMER       0x0E
+#define REG_UNLOCK1            0x0F
+#define REG_UNLOCK2            0x10
+#define REG_GPIO_INT_STAT1     0x11
+#define REG_GPIO_INT_STAT2     0x12
+#define REG_GPIO_INT_STAT3     0x13
+#define REG_GPIO_DAT_STAT1     0x14
+#define REG_GPIO_DAT_STAT2     0x15
+#define REG_GPIO_DAT_STAT3     0x16
+#define REG_GPIO_DAT_OUT1      0x17
+#define REG_GPIO_DAT_OUT2      0x18
+#define REG_GPIO_DAT_OUT3      0x19
+#define REG_GPIO_INT_EN1       0x1A
+#define REG_GPIO_INT_EN2       0x1B
+#define REG_GPIO_INT_EN3       0x1C
+#define REG_KP_GPIO1           0x1D
+#define REG_KP_GPIO2           0x1E
+#define REG_KP_GPIO3           0x1F
+#define REG_GPI_EM1            0x20
+#define REG_GPI_EM2            0x21
+#define REG_GPI_EM3            0x22
+#define REG_GPIO_DIR1          0x23
+#define REG_GPIO_DIR2          0x24
+#define REG_GPIO_DIR3          0x25
+#define REG_GPIO_INT_LVL1      0x26
+#define REG_GPIO_INT_LVL2      0x27
+#define REG_GPIO_INT_LVL3      0x28
+#define REG_DEBOUNCE_DIS1      0x29
+#define REG_DEBOUNCE_DIS2      0x2A
+#define REG_DEBOUNCE_DIS3      0x2B
+#define REG_GPIO_PULL1         0x2C
+#define REG_GPIO_PULL2         0x2D
+#define REG_GPIO_PULL3         0x2E
+
+/* TCA8418 bit definitions */
+#define CFG_AI                 BIT(7)
+#define CFG_GPI_E_CFG          BIT(6)
+#define CFG_OVR_FLOW_M         BIT(5)
+#define CFG_INT_CFG            BIT(4)
+#define CFG_OVR_FLOW_IEN       BIT(3)
+#define CFG_K_LCK_IEN          BIT(2)
+#define CFG_GPI_IEN            BIT(1)
+#define CFG_KE_IEN             BIT(0)
+
+#define INT_STAT_CAD_INT       BIT(4)
+#define INT_STAT_OVR_FLOW_INT  BIT(3)
+#define INT_STAT_K_LCK_INT     BIT(2)
+#define INT_STAT_GPI_INT       BIT(1)
+#define INT_STAT_K_INT         BIT(0)
+
+/* TCA8418 register masks */
+#define KEY_LCK_EC_KEC         0x7
+#define KEY_EVENT_CODE         0x7f
+#define KEY_EVENT_VALUE                0x80
+
+
+static const struct i2c_device_id tca8418_id[] = {
+       { TCA8418_NAME, 8418, },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tca8418_id);
+
+struct tca8418_keypad {
+       unsigned int rows;
+       unsigned int cols;
+       unsigned int keypad_mask; /* Mask for keypad col/rol regs */
+       unsigned int irq;
+       unsigned int row_shift;
+
+       struct i2c_client *client;
+       struct input_dev *input;
+
+       /* Flexible array member, must be at end of struct */
+       unsigned short keymap[];
+};
+
+/*
+ * Write a byte to the TCA8418
+ */
+static int tca8418_write_byte(struct tca8418_keypad *keypad_data,
+                             int reg, u8 val)
+{
+       int error;
+
+       error = i2c_smbus_write_byte_data(keypad_data->client, reg, val);
+       if (error < 0) {
+               dev_err(&keypad_data->client->dev,
+                       "%s failed, reg: %d, val: %d, error: %d\n",
+                       __func__, reg, val, error);
+               return error;
+       }
+
+       return 0;
+}
+
+/*
+ * Read a byte from the TCA8418
+ */
+static int tca8418_read_byte(struct tca8418_keypad *keypad_data,
+                            int reg, u8 *val)
+{
+       int error;
+
+       error = i2c_smbus_read_byte_data(keypad_data->client, reg);
+       if (error < 0) {
+               dev_err(&keypad_data->client->dev,
+                               "%s failed, reg: %d, error: %d\n",
+                               __func__, reg, error);
+               return error;
+       }
+
+       *val = (u8)error;
+
+       return 0;
+}
+
+static void tca8418_read_keypad(struct tca8418_keypad *keypad_data)
+{
+       int error, col, row;
+       u8 reg, state, code;
+
+       /* Initial read of the key event FIFO */
+       error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
+
+       /* Assume that key code 0 signifies empty FIFO */
+       while (error >= 0 && reg > 0) {
+               state = reg & KEY_EVENT_VALUE;
+               code  = reg & KEY_EVENT_CODE;
+
+               row = code / TCA8418_MAX_COLS;
+               col = code % TCA8418_MAX_COLS;
+
+               row = (col) ? row : row - 1;
+               col = (col) ? col - 1 : TCA8418_MAX_COLS - 1;
+
+               code = MATRIX_SCAN_CODE(row, col, keypad_data->row_shift);
+               input_event(keypad_data->input, EV_MSC, MSC_SCAN, code);
+               input_report_key(keypad_data->input,
+                               keypad_data->keymap[code], state);
+
+               /* Read for next loop */
+               error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
+       }
+
+       if (error < 0)
+               dev_err(&keypad_data->client->dev,
+                       "unable to read REG_KEY_EVENT_A\n");
+
+       input_sync(keypad_data->input);
+}
+
+/*
+ * Threaded IRQ handler and this can (and will) sleep.
+ */
+static irqreturn_t tca8418_irq_handler(int irq, void *dev_id)
+{
+       struct tca8418_keypad *keypad_data = dev_id;
+       u8 reg;
+       int error;
+
+       error = tca8418_read_byte(keypad_data, REG_INT_STAT, &reg);
+       if (error) {
+               dev_err(&keypad_data->client->dev,
+                       "unable to read REG_INT_STAT\n");
+               goto exit;
+       }
+
+       if (reg & INT_STAT_OVR_FLOW_INT)
+               dev_warn(&keypad_data->client->dev, "overflow occurred\n");
+
+       if (reg & INT_STAT_K_INT)
+               tca8418_read_keypad(keypad_data);
+
+exit:
+       /* Clear all interrupts, even IRQs we didn't check (GPI, CAD, LCK) */
+       reg = 0xff;
+       error = tca8418_write_byte(keypad_data, REG_INT_STAT, reg);
+       if (error)
+               dev_err(&keypad_data->client->dev,
+                       "unable to clear REG_INT_STAT\n");
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Configure the TCA8418 for keypad operation
+ */
+static int __devinit tca8418_configure(struct tca8418_keypad *keypad_data)
+{
+       int reg, error;
+
+       /* Write config register, if this fails assume device not present */
+       error = tca8418_write_byte(keypad_data, REG_CFG,
+                               CFG_INT_CFG | CFG_OVR_FLOW_IEN | CFG_KE_IEN);
+       if (error < 0)
+               return -ENODEV;
+
+
+       /* Assemble a mask for row and column registers */
+       reg  =  ~(~0 << keypad_data->rows);
+       reg += (~(~0 << keypad_data->cols)) << 8;
+       keypad_data->keypad_mask = reg;
+
+       /* Set registers to keypad mode */
+       error |= tca8418_write_byte(keypad_data, REG_KP_GPIO1, reg);
+       error |= tca8418_write_byte(keypad_data, REG_KP_GPIO2, reg >> 8);
+       error |= tca8418_write_byte(keypad_data, REG_KP_GPIO3, reg >> 16);
+
+       /* Enable column debouncing */
+       error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS1, reg);
+       error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS2, reg >> 8);
+       error |= tca8418_write_byte(keypad_data, REG_DEBOUNCE_DIS3, reg >> 16);
+
+       return error;
+}
+
+static int __devinit tca8418_keypad_probe(struct i2c_client *client,
+                                         const struct i2c_device_id *id)
+{
+       const struct tca8418_keypad_platform_data *pdata =
+                                               client->dev.platform_data;
+       struct tca8418_keypad *keypad_data;
+       struct input_dev *input;
+       int error, row_shift, max_keys;
+
+       /* Copy the platform data */
+       if (!pdata) {
+               dev_dbg(&client->dev, "no platform data\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->keymap_data) {
+               dev_err(&client->dev, "no keymap data defined\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->rows || pdata->rows > TCA8418_MAX_ROWS) {
+               dev_err(&client->dev, "invalid rows\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->cols || pdata->cols > TCA8418_MAX_COLS) {
+               dev_err(&client->dev, "invalid columns\n");
+               return -EINVAL;
+       }
+
+       /* Check i2c driver capabilities */
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) {
+               dev_err(&client->dev, "%s adapter not supported\n",
+                       dev_driver_string(&client->adapter->dev));
+               return -ENODEV;
+       }
+
+       row_shift = get_count_order(pdata->cols);
+       max_keys = pdata->rows << row_shift;
+
+       /* Allocate memory for keypad_data, keymap and input device */
+       keypad_data = kzalloc(sizeof(*keypad_data) +
+                       max_keys * sizeof(keypad_data->keymap[0]), GFP_KERNEL);
+       if (!keypad_data)
+               return -ENOMEM;
+
+       keypad_data->rows = pdata->rows;
+       keypad_data->cols = pdata->cols;
+       keypad_data->client = client;
+       keypad_data->row_shift = row_shift;
+
+       /* Initialize the chip or fail if chip isn't present */
+       error = tca8418_configure(keypad_data);
+       if (error < 0)
+               goto fail1;
+
+       /* Configure input device */
+       input = input_allocate_device();
+       if (!input) {
+               error = -ENOMEM;
+               goto fail1;
+       }
+       keypad_data->input = input;
+
+       input->name = client->name;
+       input->dev.parent = &client->dev;
+
+       input->id.bustype = BUS_I2C;
+       input->id.vendor  = 0x0001;
+       input->id.product = 0x001;
+       input->id.version = 0x0001;
+
+       input->keycode     = keypad_data->keymap;
+       input->keycodesize = sizeof(keypad_data->keymap[0]);
+       input->keycodemax  = max_keys;
+
+       __set_bit(EV_KEY, input->evbit);
+       if (pdata->rep)
+               __set_bit(EV_REP, input->evbit);
+
+       input_set_capability(input, EV_MSC, MSC_SCAN);
+
+       input_set_drvdata(input, keypad_data);
+
+       matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
+                       input->keycode, input->keybit);
+
+       if (pdata->irq_is_gpio)
+               client->irq = gpio_to_irq(client->irq);
+
+       error = request_threaded_irq(client->irq, NULL, tca8418_irq_handler,
+                                    IRQF_TRIGGER_FALLING,
+                                    client->name, keypad_data);
+       if (error) {
+               dev_dbg(&client->dev,
+                       "Unable to claim irq %d; error %d\n",
+                       client->irq, error);
+               goto fail2;
+       }
+
+       error = input_register_device(input);
+       if (error) {
+               dev_dbg(&client->dev,
+                       "Unable to register input device, error: %d\n", error);
+               goto fail3;
+       }
+
+       i2c_set_clientdata(client, keypad_data);
+       return 0;
+
+fail3:
+       free_irq(client->irq, keypad_data);
+fail2:
+       input_free_device(input);
+fail1:
+       kfree(keypad_data);
+       return error;
+}
+
+static int __devexit tca8418_keypad_remove(struct i2c_client *client)
+{
+       struct tca8418_keypad *keypad_data = i2c_get_clientdata(client);
+
+       free_irq(keypad_data->client->irq, keypad_data);
+
+       input_unregister_device(keypad_data->input);
+
+       kfree(keypad_data);
+
+       return 0;
+}
+
+
+static struct i2c_driver tca8418_keypad_driver = {
+       .driver = {
+               .name   = TCA8418_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = tca8418_keypad_probe,
+       .remove         = __devexit_p(tca8418_keypad_remove),
+       .id_table       = tca8418_id,
+};
+
+static int __init tca8418_keypad_init(void)
+{
+       return i2c_add_driver(&tca8418_keypad_driver);
+}
+subsys_initcall(tca8418_keypad_init);
+
+static void __exit tca8418_keypad_exit(void)
+{
+       i2c_del_driver(&tca8418_keypad_driver);
+}
+module_exit(tca8418_keypad_exit);
+
+MODULE_AUTHOR("Kyle Manna <kyle.manna@fuel7.com>");
+MODULE_DESCRIPTION("Keypad driver for TCA8418");
+MODULE_LICENSE("GPL");
index 09244804fb97674adfd9d39b28fc94c05bf651a3..1cf72fe513e6934af7fb4ab0d5742c3155537146 100644 (file)
@@ -452,10 +452,10 @@ static ssize_t adxl34x_disable_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct adxl34x *ac = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtouint(buf, 10, &val);
        if (error)
                return error;
 
@@ -541,10 +541,10 @@ static ssize_t adxl34x_rate_store(struct device *dev,
                                  const char *buf, size_t count)
 {
        struct adxl34x *ac = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned char val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtou8(buf, 10, &val);
        if (error)
                return error;
 
@@ -576,10 +576,10 @@ static ssize_t adxl34x_autosleep_store(struct device *dev,
                                  const char *buf, size_t count)
 {
        struct adxl34x *ac = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtouint(buf, 10, &val);
        if (error)
                return error;
 
@@ -623,13 +623,13 @@ static ssize_t adxl34x_write_store(struct device *dev,
                                   const char *buf, size_t count)
 {
        struct adxl34x *ac = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
        /*
         * This allows basic ADXL register write access for debug purposes.
         */
-       error = strict_strtoul(buf, 16, &val);
+       error = kstrtouint(buf, 16, &val);
        if (error)
                return error;
 
index 8d345e87075e6b8704ef72231f59e4d477374d73..a34896ed7ed3a53ac31a7b05d420285d9503ac28 100644 (file)
@@ -42,13 +42,13 @@ static int ati_remote2_set_mask(const char *val,
                                const struct kernel_param *kp,
                                unsigned int max)
 {
-       unsigned long mask;
+       unsigned int mask;
        int ret;
 
        if (!val)
                return -EINVAL;
 
-       ret = strict_strtoul(val, 0, &mask);
+       ret = kstrtouint(val, 0, &mask);
        if (ret)
                return ret;
 
@@ -720,11 +720,12 @@ static ssize_t ati_remote2_store_channel_mask(struct device *dev,
        struct usb_device *udev = to_usb_device(dev);
        struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
        struct ati_remote2 *ar2 = usb_get_intfdata(intf);
-       unsigned long mask;
+       unsigned int mask;
        int r;
 
-       if (strict_strtoul(buf, 0, &mask))
-               return -EINVAL;
+       r = kstrtouint(buf, 0, &mask);
+       if (r)
+               return r;
 
        if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK)
                return -EINVAL;
@@ -769,10 +770,12 @@ static ssize_t ati_remote2_store_mode_mask(struct device *dev,
        struct usb_device *udev = to_usb_device(dev);
        struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
        struct ati_remote2 *ar2 = usb_get_intfdata(intf);
-       unsigned long mask;
+       unsigned int mask;
+       int err;
 
-       if (strict_strtoul(buf, 0, &mask))
-               return -EINVAL;
+       err = kstrtouint(buf, 0, &mask);
+       if (err)
+               return err;
 
        if (mask & ~ATI_REMOTE2_MAX_MODE_MASK)
                return -EINVAL;
index 003587c71f43edd8d6ad315d5ffaf514ad3705d7..bd87380bd879edcfd9a278d53c887c444a14b9d6 100644 (file)
 
 #include <linux/slab.h>
 #include <linux/input.h>
+#include <linux/input/mt.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
 
 #include "psmouse.h"
 #include "alps.h"
 
-#define ALPS_OLDPROTO          0x01    /* old style input */
+/*
+ * 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
+
+static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
+       { PSMOUSE_CMD_SETPOLL,          0x00 }, /* 0 */
+       { PSMOUSE_CMD_RESET_DIS,        0x00 }, /* 1 */
+       { PSMOUSE_CMD_SETSCALE21,       0x00 }, /* 2 */
+       { PSMOUSE_CMD_SETRATE,          0x0a }, /* 3 */
+       { PSMOUSE_CMD_SETRATE,          0x14 }, /* 4 */
+       { PSMOUSE_CMD_SETRATE,          0x28 }, /* 5 */
+       { PSMOUSE_CMD_SETRATE,          0x3c }, /* 6 */
+       { PSMOUSE_CMD_SETRATE,          0x50 }, /* 7 */
+       { PSMOUSE_CMD_SETRATE,          0x64 }, /* 8 */
+       { PSMOUSE_CMD_SETRATE,          0xc8 }, /* 9 */
+       { ALPS_CMD_NIBBLE_10,           0x00 }, /* a */
+       { PSMOUSE_CMD_SETRES,           0x00 }, /* b */
+       { PSMOUSE_CMD_SETRES,           0x01 }, /* c */
+       { PSMOUSE_CMD_SETRES,           0x02 }, /* d */
+       { PSMOUSE_CMD_SETRES,           0x03 }, /* e */
+       { PSMOUSE_CMD_SETSCALE11,       0x00 }, /* f */
+};
+
+static const struct alps_nibble_commands alps_v4_nibble_commands[] = {
+       { PSMOUSE_CMD_ENABLE,           0x00 }, /* 0 */
+       { PSMOUSE_CMD_RESET_DIS,        0x00 }, /* 1 */
+       { PSMOUSE_CMD_SETSCALE21,       0x00 }, /* 2 */
+       { PSMOUSE_CMD_SETRATE,          0x0a }, /* 3 */
+       { PSMOUSE_CMD_SETRATE,          0x14 }, /* 4 */
+       { PSMOUSE_CMD_SETRATE,          0x28 }, /* 5 */
+       { PSMOUSE_CMD_SETRATE,          0x3c }, /* 6 */
+       { PSMOUSE_CMD_SETRATE,          0x50 }, /* 7 */
+       { PSMOUSE_CMD_SETRATE,          0x64 }, /* 8 */
+       { PSMOUSE_CMD_SETRATE,          0xc8 }, /* 9 */
+       { ALPS_CMD_NIBBLE_10,           0x00 }, /* a */
+       { PSMOUSE_CMD_SETRES,           0x00 }, /* b */
+       { PSMOUSE_CMD_SETRES,           0x01 }, /* c */
+       { PSMOUSE_CMD_SETRES,           0x02 }, /* d */
+       { PSMOUSE_CMD_SETRES,           0x03 }, /* e */
+       { PSMOUSE_CMD_SETSCALE11,       0x00 }, /* f */
+};
+
+
 #define ALPS_DUALPOINT         0x02    /* touchpad has trackstick */
 #define ALPS_PASS              0x04    /* device has a pass-through port */
 
                                           6-byte ALPS packet */
 
 static const struct alps_model_info alps_model_data[] = {
-       { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
-       { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO },              /* UMAX-530T */
-       { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
-       { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
-       { { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 },                          /* HP ze1115 */
-       { { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
-       { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
-       { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, ALPS_FW_BK_2 },               /* Fujitsu Siemens S6010 */
-       { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL },                 /* Toshiba Satellite S2400-103 */
-       { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 },               /* NEC Versa L320 */
-       { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 },
-       { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */
-       { { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT },             /* ThinkPad R61 8918-5QG */
-       { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
-       { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 },               /* Ahtec Laptop */
-       { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
-       { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
-       { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
+       { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },  /* Toshiba Salellite Pro M10 */
+       { { 0x33, 0x02, 0x0a }, 0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 },                           /* UMAX-530T */
+       { { 0x53, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
+       { { 0x53, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
+       { { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },                           /* HP ze1115 */
+       { { 0x63, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
+       { { 0x63, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
+       { { 0x63, 0x02, 0x28 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 },                /* Fujitsu Siemens S6010 */
+       { { 0x63, 0x02, 0x3c }, 0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL },                  /* Toshiba Satellite S2400-103 */
+       { { 0x63, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 },                /* NEC Versa L320 */
+       { { 0x63, 0x02, 0x64 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
+       { { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },  /* Dell Latitude D800 */
+       { { 0x73, 0x00, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT },              /* ThinkPad R61 8918-5QG */
+       { { 0x73, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 },
+       { { 0x73, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 },                /* Ahtec Laptop */
+       { { 0x20, 0x02, 0x0e }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },  /* XXX */
+       { { 0x22, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
+       { { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT },  /* Dell Latitude D600 */
        /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
-       { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf,
+       { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },
-       { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },          /* Dell Vostro 1400 */
-       { { 0x52, 0x01, 0x14 }, 0xff, 0xff,
-               ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED },      /* Toshiba Tecra A11-11L */
+       { { 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 },
 };
 
 /*
@@ -67,42 +120,7 @@ static const struct alps_model_info alps_model_data[] = {
  * isn't valid per PS/2 spec.
  */
 
-/*
- * PS/2 packet format
- *
- * byte 0:  0    0 YSGN XSGN    1    M    R    L
- * byte 1: X7   X6   X5   X4   X3   X2   X1   X0
- * byte 2: Y7   Y6   Y5   Y4   Y3   Y2   Y1   Y0
- *
- * Note that the device never signals overflow condition.
- *
- * ALPS absolute Mode - new format
- *
- * byte 0:  1    ?    ?    ?    1    ?    ?    ?
- * byte 1:  0   x6   x5   x4   x3   x2   x1   x0
- * byte 2:  0  x10   x9   x8   x7    ?  fin  ges
- * byte 3:  0   y9   y8   y7    1    M    R    L
- * byte 4:  0   y6   y5   y4   y3   y2   y1   y0
- * byte 5:  0   z6   z5   z4   z3   z2   z1   z0
- *
- * Dualpoint device -- interleaved packet format
- *
- * byte 0:    1    1    0    0    1    1    1    1
- * byte 1:    0   x6   x5   x4   x3   x2   x1   x0
- * byte 2:    0  x10   x9   x8   x7    0  fin  ges
- * byte 3:    0    0 YSGN XSGN    1    1    1    1
- * byte 4:   X7   X6   X5   X4   X3   X2   X1   X0
- * byte 5:   Y7   Y6   Y5   Y4   Y3   Y2   Y1   Y0
- * byte 6:    0   y9   y8   y7    1    m    r    l
- * byte 7:    0   y6   y5   y4   y3   y2   y1   y0
- * byte 8:    0   z6   z5   z4   z3   z2   z1   z0
- *
- * CAPITALS = stick, miniscules = touchpad
- *
- * ?'s can have different meanings on different models,
- * such as wheel rotation, extra buttons, stick buttons
- * on a dualpoint, etc.
- */
+/* Packet formats are described in Documentation/input/alps.txt */
 
 static bool alps_is_valid_first_byte(const struct alps_model_info *model,
                                     unsigned char data)
@@ -137,7 +155,7 @@ static void alps_report_buttons(struct psmouse *psmouse,
        input_sync(dev2);
 }
 
-static void alps_process_packet(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;
@@ -147,7 +165,7 @@ static void alps_process_packet(struct psmouse *psmouse)
        int x, y, z, ges, fin, left, right, middle;
        int back = 0, forward = 0;
 
-       if (model->flags & ALPS_OLDPROTO) {
+       if (model->proto_version == ALPS_PROTO_V1) {
                left = packet[2] & 0x10;
                right = packet[2] & 0x08;
                middle = 0;
@@ -239,6 +257,403 @@ static void alps_process_packet(struct psmouse *psmouse)
        input_sync(dev);
 }
 
+/*
+ * Process bitmap data from v3 and v4 protocols. Returns the number of
+ * fingers detected. A return value of 0 means at least one of the
+ * bitmaps was empty.
+ *
+ * The bitmaps don't have enough data to track fingers, so this function
+ * only generates points representing a bounding box of all contacts.
+ * 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,
+                              int *x1, int *y1, int *x2, int *y2)
+{
+       struct alps_bitmap_point {
+               int start_bit;
+               int num_bits;
+       };
+
+       int fingers_x = 0, fingers_y = 0, fingers;
+       int i, bit, prev_bit;
+       struct alps_bitmap_point x_low = {0,}, x_high = {0,};
+       struct alps_bitmap_point y_low = {0,}, y_high = {0,};
+       struct alps_bitmap_point *point;
+
+       if (!x_map || !y_map)
+               return 0;
+
+       *x1 = *y1 = *x2 = *y2 = 0;
+
+       prev_bit = 0;
+       point = &x_low;
+       for (i = 0; x_map != 0; i++, x_map >>= 1) {
+               bit = x_map & 1;
+               if (bit) {
+                       if (!prev_bit) {
+                               point->start_bit = i;
+                               fingers_x++;
+                       }
+                       point->num_bits++;
+               } else {
+                       if (prev_bit)
+                               point = &x_high;
+                       else
+                               point->num_bits = 0;
+               }
+               prev_bit = bit;
+       }
+
+       /*
+        * 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);
+       prev_bit = 0;
+       point = &y_low;
+       for (i = 0; y_map != 0; i++, y_map <<= 1) {
+               bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1));
+               if (bit) {
+                       if (!prev_bit) {
+                               point->start_bit = i;
+                               fingers_y++;
+                       }
+                       point->num_bits++;
+               } else {
+                       if (prev_bit)
+                               point = &y_high;
+                       else
+                               point->num_bits = 0;
+               }
+               prev_bit = bit;
+       }
+
+       /*
+        * Fingers can overlap, so we use the maximum count of fingers
+        * on either axis as the finger count.
+        */
+       fingers = max(fingers_x, fingers_y);
+
+       /*
+        * If total fingers is > 1 but either axis reports only a single
+        * contact, we have overlapping or adjacent fingers. For the
+        * purposes of creating a bounding box, divide the single contact
+        * (roughly) equally between the two points.
+        */
+       if (fingers > 1) {
+               if (fingers_x == 1) {
+                       i = x_low.num_bits / 2;
+                       x_low.num_bits = x_low.num_bits - i;
+                       x_high.start_bit = x_low.start_bit + i;
+                       x_high.num_bits = max(i, 1);
+               } else if (fingers_y == 1) {
+                       i = y_low.num_bits / 2;
+                       y_low.num_bits = y_low.num_bits - i;
+                       y_high.start_bit = y_low.start_bit + i;
+                       y_high.num_bits = max(i, 1);
+               }
+       }
+
+       *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));
+
+       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));
+       }
+
+       return fingers;
+}
+
+static void alps_set_slot(struct input_dev *dev, int slot, bool active,
+                         int x, int y)
+{
+       input_mt_slot(dev, slot);
+       input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+       if (active) {
+               input_report_abs(dev, ABS_MT_POSITION_X, x);
+               input_report_abs(dev, ABS_MT_POSITION_Y, y);
+       }
+}
+
+static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
+                                    int x1, int y1, int x2, int y2)
+{
+       alps_set_slot(dev, 0, num_fingers != 0, x1, y1);
+       alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
+}
+
+static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
+{
+       struct alps_data *priv = psmouse->private;
+       unsigned char *packet = psmouse->packet;
+       struct input_dev *dev = priv->dev2;
+       int x, y, z, left, right, middle;
+
+       /* Sanity check packet */
+       if (!(packet[0] & 0x40)) {
+               psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n");
+               return;
+       }
+
+       /*
+        * There's a special packet that seems to indicate the end
+        * of a stream of trackstick data. Filter these out.
+        */
+       if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f)
+               return;
+
+       x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f));
+       y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f));
+       z = (packet[4] & 0x7c) >> 2;
+
+       /*
+        * The x and y values tend to be quite large, and when used
+        * alone the trackstick is difficult to use. Scale them down
+        * to compensate.
+        */
+       x /= 8;
+       y /= 8;
+
+       input_report_rel(dev, REL_X, x);
+       input_report_rel(dev, REL_Y, -y);
+
+       /*
+        * Most ALPS models report the trackstick buttons in the touchpad
+        * packets, but a few report them here. No reliable way has been
+        * found to differentiate between the models upfront, so we enable
+        * the quirk in response to seeing a button press in the trackstick
+        * packet.
+        */
+       left = packet[3] & 0x01;
+       right = packet[3] & 0x02;
+       middle = packet[3] & 0x04;
+
+       if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) &&
+           (left || right || middle))
+               priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS;
+
+       if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) {
+               input_report_key(dev, BTN_LEFT, left);
+               input_report_key(dev, BTN_RIGHT, right);
+               input_report_key(dev, BTN_MIDDLE, middle);
+       }
+
+       input_sync(dev);
+       return;
+}
+
+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;
+
+       /*
+        * There's no single feature of touchpad position and bitmap packets
+        * that can be used to distinguish between them. We rely on the fact
+        * that a bitmap packet should always follow a position packet with
+        * bit 6 of packet[4] set.
+        */
+       if (priv->multi_packet) {
+               /*
+                * Sometimes a position packet will indicate a multi-packet
+                * sequence, but then what follows is another position
+                * 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,
+                                                          &x1, &y1, &x2, &y2);
+
+                       /*
+                        * We shouldn't report more than one finger if
+                        * we don't have two coordinates.
+                        */
+                       if (fingers > 1 && bmap_fingers < 2)
+                               fingers = bmap_fingers;
+
+                       /* Now process position packet */
+                       packet = priv->multi_data;
+               } else {
+                       priv->multi_packet = 0;
+               }
+       }
+
+       /*
+        * Bit 6 of byte 0 is not usually set in position packets. The only
+        * times it seems to be set is in situations where the data is
+        * suspect anyway, e.g. a palm resting flat on the touchpad. Given
+        * this combined with the fact that this bit is useful for filtering
+        * out misidentified bitmap packets, we reject anything with this
+        * bit set.
+        */
+       if (packet[0] & 0x40)
+               return;
+
+       if (!priv->multi_packet && (packet[4] & 0x40)) {
+               priv->multi_packet = 1;
+               memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
+               return;
+       }
+
+       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)
+               return;
+
+       /*
+        * If we don't have MT data or the bitmaps were empty, we have
+        * to rely on ST data.
+        */
+       if (!fingers) {
+               x1 = x;
+               y1 = y;
+               fingers = z > 0 ? 1 : 0;
+       }
+
+       if (z >= 64)
+               input_report_key(dev, BTN_TOUCH, 1);
+       else
+               input_report_key(dev, BTN_TOUCH, 0);
+
+       alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
+
+       input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+       input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+       input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+       input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
+
+       input_report_key(dev, BTN_LEFT, left);
+       input_report_key(dev, BTN_RIGHT, right);
+       input_report_key(dev, BTN_MIDDLE, middle);
+
+       if (z > 0) {
+               input_report_abs(dev, ABS_X, x);
+               input_report_abs(dev, ABS_Y, y);
+       }
+       input_report_abs(dev, ABS_PRESSURE, 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_sync(dev2);
+       }
+}
+
+static void alps_process_packet_v3(struct psmouse *psmouse)
+{
+       unsigned char *packet = psmouse->packet;
+
+       /*
+        * v3 protocol packets come in three types, two representing
+        * touchpad data and one representing trackstick data.
+        * Trackstick packets seem to be distinguished by always
+        * having 0x3f in the last byte. This value has never been
+        * observed in the last byte of either of the other types
+        * of packets.
+        */
+       if (packet[5] == 0x3f) {
+               alps_process_trackstick_packet_v3(psmouse);
+               return;
+       }
+
+       alps_process_touchpad_packet_v3(psmouse);
+}
+
+static void alps_process_packet_v4(struct psmouse *psmouse)
+{
+       unsigned char *packet = psmouse->packet;
+       struct input_dev *dev = psmouse->dev;
+       int x, y, z;
+       int left, right;
+
+       left = packet[4] & 0x01;
+       right = packet[4] & 0x02;
+
+       x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
+           ((packet[0] & 0x30) >> 4);
+       y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
+       z = packet[5] & 0x7f;
+
+       if (z >= 64)
+               input_report_key(dev, BTN_TOUCH, 1);
+       else
+               input_report_key(dev, BTN_TOUCH, 0);
+
+       if (z > 0) {
+               input_report_abs(dev, ABS_X, x);
+               input_report_abs(dev, ABS_Y, y);
+       }
+       input_report_abs(dev, ABS_PRESSURE, z);
+
+       input_report_key(dev, BTN_TOOL_FINGER, z > 0);
+       input_report_key(dev, BTN_LEFT, left);
+       input_report_key(dev, BTN_RIGHT, right);
+
+       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)
@@ -344,7 +759,7 @@ static void alps_flush_packet(unsigned long data)
 
        serio_pause_rx(psmouse->ps2dev.serio);
 
-       if (psmouse->pktcnt == 6) {
+       if (psmouse->pktcnt == psmouse->pktsize) {
 
                /*
                 * We did not any more data in reasonable amount of time.
@@ -395,8 +810,8 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
                return PSMOUSE_BAD_DATA;
        }
 
-       /* Bytes 2 - 6 should have 0 in the highest bit */
-       if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
+       /* Bytes 2 - pktsize should have 0 in the highest bit */
+       if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
            (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
                psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
                            psmouse->pktcnt - 1,
@@ -404,7 +819,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
                return PSMOUSE_BAD_DATA;
        }
 
-       if (psmouse->pktcnt == 6) {
+       if (psmouse->pktcnt == psmouse->pktsize) {
                alps_process_packet(psmouse);
                return PSMOUSE_FULL_PACKET;
        }
@@ -412,11 +827,127 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
        return PSMOUSE_GOOD_DATA;
 }
 
+static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       struct alps_data *priv = psmouse->private;
+       int command;
+       unsigned char *param;
+       unsigned char dummy[4];
+
+       BUG_ON(nibble > 0xf);
+
+       command = priv->nibble_commands[nibble].command;
+       param = (command & 0x0f00) ?
+               dummy : (unsigned char *)&priv->nibble_commands[nibble].data;
+
+       if (ps2_command(ps2dev, param, command))
+               return -1;
+
+       return 0;
+}
+
+static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       struct alps_data *priv = psmouse->private;
+       int i, nibble;
+
+       if (ps2_command(ps2dev, NULL, priv->addr_command))
+               return -1;
+
+       for (i = 12; i >= 0; i -= 4) {
+               nibble = (addr >> i) & 0xf;
+               if (alps_command_mode_send_nibble(psmouse, nibble))
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       unsigned char param[4];
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+               return -1;
+
+       /*
+        * The address being read is returned in the first two bytes
+        * of the result. Check that this address matches the expected
+        * address.
+        */
+       if (addr != ((param[0] << 8) | param[1]))
+               return -1;
+
+       return param[2];
+}
+
+static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr)
+{
+       if (alps_command_mode_set_addr(psmouse, addr))
+               return -1;
+       return __alps_command_mode_read_reg(psmouse, addr);
+}
+
+static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value)
+{
+       if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf))
+               return -1;
+       if (alps_command_mode_send_nibble(psmouse, value & 0xf))
+               return -1;
+       return 0;
+}
+
+static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr,
+                                      u8 value)
+{
+       if (alps_command_mode_set_addr(psmouse, addr))
+               return -1;
+       return __alps_command_mode_write_reg(psmouse, value);
+}
+
+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)) {
+               psmouse_err(psmouse, "failed to enter command mode\n");
+               return -1;
+       }
+
+       if (param[0] != 0x88 && param[1] != 0x07) {
+               psmouse_dbg(psmouse,
+                           "unknown response while entering command mode: %2.2x %2.2x %2.2x\n",
+                           param[0], param[1], param[2]);
+               return -1;
+       }
+
+       if (resp)
+               *resp = param[2];
+       return 0;
+}
+
+static inline int alps_exit_command_mode(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM))
+               return -1;
+       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;
 
        /*
@@ -464,12 +995,41 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int
                *version = (param[0] << 8) | (param[1] << 4) | i;
        }
 
-       for (i = 0; i < ARRAY_SIZE(alps_model_data); 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)))
-                       return alps_model_data + i;
+                           sizeof(alps_model_data[i].signature))) {
+                       model = alps_model_data + i;
+                       break;
+               }
+       }
 
-       return NULL;
+       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;
 }
 
 /*
@@ -477,7 +1037,7 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int
  * subsequent commands. It looks like glidepad is behind stickpointer,
  * I'd thought it would be other way around...
  */
-static int alps_passthrough_mode(struct psmouse *psmouse, bool enable)
+static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
@@ -494,7 +1054,7 @@ static int alps_passthrough_mode(struct psmouse *psmouse, bool enable)
        return 0;
 }
 
-static int alps_absolute_mode(struct psmouse *psmouse)
+static int alps_absolute_mode_v1_v2(struct psmouse *psmouse)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
 
@@ -565,17 +1125,17 @@ static int alps_tap_mode(struct psmouse *psmouse, int enable)
 static int alps_poll(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
-       unsigned char buf[6];
+       unsigned char buf[sizeof(psmouse->packet)];
        bool poll_failed;
 
        if (priv->i->flags & ALPS_PASS)
-               alps_passthrough_mode(psmouse, true);
+               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)
-               alps_passthrough_mode(psmouse, false);
+               alps_passthrough_mode_v2(psmouse, false);
 
        if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0)
                return -1;
@@ -592,13 +1152,13 @@ static int alps_poll(struct psmouse *psmouse)
        return 0;
 }
 
-static int alps_hw_init(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) &&
-           alps_passthrough_mode(psmouse, true)) {
+           alps_passthrough_mode_v2(psmouse, true)) {
                return -1;
        }
 
@@ -607,13 +1167,13 @@ static int alps_hw_init(struct psmouse *psmouse)
                return -1;
        }
 
-       if (alps_absolute_mode(psmouse)) {
+       if (alps_absolute_mode_v1_v2(psmouse)) {
                psmouse_err(psmouse, "Failed to enable absolute mode\n");
                return -1;
        }
 
        if ((model->flags & ALPS_PASS) &&
-           alps_passthrough_mode(psmouse, false)) {
+           alps_passthrough_mode_v2(psmouse, false)) {
                return -1;
        }
 
@@ -626,6 +1186,297 @@ static int alps_hw_init(struct psmouse *psmouse)
        return 0;
 }
 
+/*
+ * Enable or disable passthrough mode to the trackstick. Must be in
+ * command mode when calling this function.
+ */
+static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable)
+{
+       int reg_val;
+
+       reg_val = alps_command_mode_read_reg(psmouse, 0x0008);
+       if (reg_val == -1)
+               return -1;
+
+       if (enable)
+               reg_val |= 0x01;
+       else
+               reg_val &= ~0x01;
+
+       if (__alps_command_mode_write_reg(psmouse, reg_val))
+               return -1;
+
+       return 0;
+}
+
+/* Must be in command mode when calling this function */
+static int alps_absolute_mode_v3(struct psmouse *psmouse)
+{
+       int reg_val;
+
+       reg_val = alps_command_mode_read_reg(psmouse, 0x0004);
+       if (reg_val == -1)
+               return -1;
+
+       reg_val |= 0x06;
+       if (__alps_command_mode_write_reg(psmouse, reg_val))
+               return -1;
+
+       return 0;
+}
+
+static int alps_hw_init_v3(struct psmouse *psmouse)
+{
+       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;
+
+       if (alps_enter_command_mode(psmouse, NULL))
+               goto error;
+
+       /* Check for trackstick */
+       reg_val = alps_command_mode_read_reg(psmouse, 0x0008);
+       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;
+
+               /*
+                * 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.
+                */
+               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 (alps_enter_command_mode(psmouse, NULL))
+                       goto error_passthrough;
+               if (alps_passthrough_mode_v3(psmouse, false))
+                       goto error;
+       }
+
+       if (alps_absolute_mode_v3(psmouse)) {
+               psmouse_err(psmouse, "Failed to enter absolute mode\n");
+               goto error;
+       }
+
+       reg_val = alps_command_mode_read_reg(psmouse, 0x0006);
+       if (reg_val == -1)
+               goto error;
+       if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01))
+               goto error;
+
+       reg_val = alps_command_mode_read_reg(psmouse, 0x0007);
+       if (reg_val == -1)
+               goto error;
+       if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01))
+               goto error;
+
+       if (alps_command_mode_read_reg(psmouse, 0x0144) == -1)
+               goto error;
+       if (__alps_command_mode_write_reg(psmouse, 0x04))
+               goto error;
+
+       if (alps_command_mode_read_reg(psmouse, 0x0159) == -1)
+               goto error;
+       if (__alps_command_mode_write_reg(psmouse, 0x03))
+               goto error;
+
+       if (alps_command_mode_read_reg(psmouse, 0x0163) == -1)
+               goto error;
+       if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03))
+               goto error;
+
+       if (alps_command_mode_read_reg(psmouse, 0x0162) == -1)
+               goto error;
+       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 */
+       param[0] = 0x64;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+               psmouse_err(psmouse, "Failed to enable data reporting\n");
+               return -1;
+       }
+
+       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
+        * it unusable until the machine reboots, so exit it here just
+        * to be safe
+        */
+       alps_exit_command_mode(psmouse);
+       return -1;
+}
+
+/* Must be in command mode when calling this function */
+static int alps_absolute_mode_v4(struct psmouse *psmouse)
+{
+       int reg_val;
+
+       reg_val = alps_command_mode_read_reg(psmouse, 0x0004);
+       if (reg_val == -1)
+               return -1;
+
+       reg_val |= 0x02;
+       if (__alps_command_mode_write_reg(psmouse, reg_val))
+               return -1;
+
+       return 0;
+}
+
+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;
+
+       if (alps_absolute_mode_v4(psmouse)) {
+               psmouse_err(psmouse, "Failed to enter absolute mode\n");
+               goto error;
+       }
+
+       if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03))
+               goto error;
+
+       if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03))
+               goto error;
+
+       alps_exit_command_mode(psmouse);
+
+       /*
+        * This sequence changes the output from a 9-byte to an
+        * 8-byte format. All the same data seems to be present,
+        * just in a more compact format.
+        */
+       param[0] = 0xc8;
+       param[1] = 0x64;
+       param[2] = 0x50;
+       if (ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+           ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE) ||
+           ps2_command(ps2dev, &param[2], PSMOUSE_CMD_SETRATE) ||
+           ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
+               return -1;
+
+       /* Set rate and enable data reporting */
+       param[0] = 0x64;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+               psmouse_err(psmouse, "Failed to enable data reporting\n");
+               return -1;
+       }
+
+       return 0;
+
+error:
+       /*
+        * Leaving the touchpad in command mode will essentially render
+        * it unusable until the machine reboots, so exit it here just
+        * to be safe
+        */
+       alps_exit_command_mode(psmouse);
+       return -1;
+}
+
+static int alps_hw_init(struct psmouse *psmouse)
+{
+       struct alps_data *priv = psmouse->private;
+       const struct alps_model_info *model = priv->i;
+       int ret = -1;
+
+       switch (model->proto_version) {
+       case ALPS_PROTO_V1:
+       case ALPS_PROTO_V2:
+               ret = alps_hw_init_v1_v2(psmouse);
+               break;
+       case ALPS_PROTO_V3:
+               ret = alps_hw_init_v3(psmouse);
+               break;
+       case ALPS_PROTO_V4:
+               ret = alps_hw_init_v4(psmouse);
+               break;
+       }
+
+       return ret;
+}
+
 static int alps_reconnect(struct psmouse *psmouse)
 {
        const struct alps_model_info *model;
@@ -666,6 +1517,8 @@ int alps_init(struct psmouse *psmouse)
 
        psmouse->private = priv;
 
+       psmouse_reset(psmouse);
+
        model = alps_get_model(psmouse, &version);
        if (!model)
                goto init_fail;
@@ -693,8 +1546,29 @@ int alps_init(struct psmouse *psmouse)
                BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
 
        dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
-       input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
-       input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
+
+       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:
+               set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
+               input_mt_init_slots(dev1, 2);
+               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);
+               /* fall through */
+       case ALPS_PROTO_V4:
+               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;
+       }
+
        input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
 
        if (model->flags & ALPS_WHEEL) {
@@ -737,7 +1611,7 @@ int alps_init(struct psmouse *psmouse)
        psmouse->poll = alps_poll;
        psmouse->disconnect = alps_disconnect;
        psmouse->reconnect = alps_reconnect;
-       psmouse->pktsize = 6;
+       psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6;
 
        /* We are having trouble resyncing ALPS touchpads so disable it for now */
        psmouse->resync_time = 0;
index 904ed8b3c8becf2de0dd41518658ee8888b452f1..a00a4ab92a0f7ca2050fa5a3d8b5c85fb142c233 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
+
 struct alps_model_info {
         unsigned char signature[3];
+       unsigned char command_mode_resp; /* v3/v4 only */
+       unsigned char proto_version;
         unsigned char byte0, mask0;
         unsigned char flags;
 };
 
+struct alps_nibble_commands {
+       int command;
+       unsigned char data;
+};
+
 struct alps_data {
        struct input_dev *dev2;         /* Relative device */
        char phys[32];                  /* Phys */
        const struct alps_model_info *i;/* Info */
+       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 */
+       u8 quirks;
        struct timer_list timer;
 };
 
+#define ALPS_QUIRK_TRACKSTICK_BUTTONS  1 /* trakcstick buttons in trackstick packet */
+
 #ifdef CONFIG_MOUSE_PS2_ALPS
 int alps_detect(struct psmouse *psmouse, bool set_properties);
 int alps_init(struct psmouse *psmouse);
index e2a9867c19d52fce53cac578bdaf265d3211bba1..d2c0db159b18dfc364c2ff7fc036863ef2de9614 100644 (file)
@@ -42,6 +42,24 @@ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
        return 0;
 }
 
+/*
+ * V3 and later support this fast command
+ */
+static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c,
+                               unsigned char *param)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+       if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+           ps2_command(ps2dev, NULL, c) ||
+           ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+               psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c);
+               return -1;
+       }
+
+       return 0;
+}
+
 /*
  * A retrying version of ps2_command
  */
@@ -863,13 +881,13 @@ static int elantech_set_range(struct psmouse *psmouse,
                        i = (etd->fw_version > 0x020800 &&
                             etd->fw_version < 0x020900) ? 1 : 2;
 
-                       if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+                       if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
                                return -1;
 
                        fixed_dpi = param[1] & 0x10;
 
                        if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
-                               if (synaptics_send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+                               if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
                                        return -1;
 
                                *x_max = (etd->capabilities[1] - i) * param[1] / 2;
@@ -888,7 +906,7 @@ static int elantech_set_range(struct psmouse *psmouse,
                break;
 
        case 3:
-               if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+               if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
                        return -1;
 
                *x_max = (0x0f & param[0]) << 8 | param[1];
@@ -896,7 +914,7 @@ static int elantech_set_range(struct psmouse *psmouse,
                break;
 
        case 4:
-               if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+               if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
                        return -1;
 
                *x_max = (0x0f & param[0]) << 8 | param[1];
@@ -912,6 +930,30 @@ static int elantech_set_range(struct psmouse *psmouse,
        return 0;
 }
 
+/*
+ * (value from firmware) * 10 + 790 = dpi
+ * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
+ */
+static unsigned int elantech_convert_res(unsigned int val)
+{
+       return (val * 10 + 790) * 10 / 254;
+}
+
+static int elantech_get_resolution_v4(struct psmouse *psmouse,
+                                     unsigned int *x_res,
+                                     unsigned int *y_res)
+{
+       unsigned char param[3];
+
+       if (elantech_send_cmd(psmouse, ETP_RESOLUTION_QUERY, param))
+               return -1;
+
+       *x_res = elantech_convert_res(param[1] & 0x0f);
+       *y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
+
+       return 0;
+}
+
 /*
  * Set the appropriate event bits for the input subsystem
  */
@@ -920,6 +962,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
        struct input_dev *dev = psmouse->dev;
        struct elantech_data *etd = psmouse->private;
        unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
+       unsigned int x_res = 0, y_res = 0;
 
        if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
                return -1;
@@ -967,10 +1010,20 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                break;
 
        case 4:
+               if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) {
+                       /*
+                        * if query failed, print a warning and leave the values
+                        * zero to resemble synaptics.c behavior.
+                        */
+                       psmouse_warn(psmouse, "couldn't query resolution data.\n");
+               }
+
                __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
                /* For X to recognize me as touchpad. */
                input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
                input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
+               input_abs_set_res(dev, ABS_X, x_res);
+               input_abs_set_res(dev, ABS_Y, y_res);
                /*
                 * range of pressure and width is the same as v2,
                 * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility.
@@ -983,6 +1036,8 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                input_mt_init_slots(dev, ETP_MAX_FINGERS);
                input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
+               input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
+               input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
                input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2,
                                     ETP_PMAX_V2, 0, 0);
                /*
@@ -1031,16 +1086,13 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
        struct elantech_data *etd = psmouse->private;
        struct elantech_attr_data *attr = data;
        unsigned char *reg = (unsigned char *) etd + attr->field_offset;
-       unsigned long value;
+       unsigned char value;
        int err;
 
-       err = strict_strtoul(buf, 16, &value);
+       err = kstrtou8(buf, 16, &value);
        if (err)
                return err;
 
-       if (value > 0xff)
-               return -EINVAL;
-
        /* Do we need to preserve some bits for version 2 hardware too? */
        if (etd->hw_version == 1) {
                if (attr->reg == 0x10)
@@ -1233,9 +1285,11 @@ static int elantech_set_properties(struct elantech_data *etd)
                }
        }
 
-       /*
-        * Turn on packet checking by default.
-        */
+       /* decide which send_cmd we're gonna use early */
+       etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd :
+                                              synaptics_send_cmd;
+
+       /* Turn on packet checking by default */
        etd->paritycheck = 1;
 
        /*
@@ -1291,7 +1345,7 @@ int elantech_init(struct psmouse *psmouse)
                     "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
                     etd->hw_version, param[0], param[1], param[2]);
 
-       if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+       if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
            etd->capabilities)) {
                psmouse_err(psmouse, "failed to query capabilities.\n");
                goto init_fail;
index 9e5f1aabea7e92c2abc48fe7de380c17279873be..46db3be45ac988710a821450bd37f5127bfc90c3 100644 (file)
@@ -20,6 +20,7 @@
 #define ETP_FW_VERSION_QUERY           0x01
 #define ETP_CAPABILITIES_QUERY         0x02
 #define ETP_SAMPLE_QUERY               0x03
+#define ETP_RESOLUTION_QUERY           0x04
 
 /*
  * Command values for register reading or writing
@@ -135,6 +136,7 @@ struct elantech_data {
        unsigned int width;
        struct finger_pos mt[ETP_MAX_FINGERS];
        unsigned char parity[256];
+       int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
 };
 
 #ifdef CONFIG_MOUSE_PS2_ELANTECH
index 0470dd46b566c99faa8c8aaa94f2795ad25e58dc..1c5d521de600b23c0d44008f057f77ad40e3bc44 100644 (file)
@@ -789,11 +789,14 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
                                const char *buf, size_t count)
 {
        struct hgpk_data *priv = psmouse->private;
-       unsigned long value;
+       unsigned int value;
        int err;
 
-       err = strict_strtoul(buf, 10, &value);
-       if (err || value > 1)
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
                return -EINVAL;
 
        if (value != priv->powered) {
@@ -881,11 +884,14 @@ static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,
                                const char *buf, size_t count)
 {
        struct hgpk_data *priv = psmouse->private;
-       unsigned long value;
+       unsigned int value;
        int err;
 
-       err = strict_strtoul(buf, 10, &value);
-       if (err || value != 1)
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value != 1)
                return -EINVAL;
 
        /*
index faac2c3bef740e739a59e65c3633b5bf9d262399..84de2fc6acc1c4a163a2b5fed4c45a34572fe4cb 100644 (file)
@@ -155,9 +155,14 @@ static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse,
 static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,
                                          const char *buf, size_t count)
 {
-       unsigned long value;
+       unsigned int value;
+       int err;
 
-       if (strict_strtoul(buf, 10, &value) || value > 1)
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
                return -EINVAL;
 
        ps2pp_set_smartscroll(psmouse, value);
index 9f352fbd7b4f8342478b90c5b52c7cfd44be83af..200be9c9dbc73c29b95ee7b7785a0ca25c89faa9 100644 (file)
@@ -127,7 +127,7 @@ struct psmouse_protocol {
  * relevant events to the input module once full packet has arrived.
  */
 
-static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
+psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
        unsigned char *packet = psmouse->packet;
@@ -819,6 +819,13 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .detect         = synaptics_detect,
                .init           = synaptics_init,
        },
+       {
+               .type           = PSMOUSE_SYNAPTICS_RELATIVE,
+               .name           = "SynRelPS/2",
+               .alias          = "synaptics-relative",
+               .detect         = synaptics_detect,
+               .init           = synaptics_init_relative,
+       },
 #endif
 #ifdef CONFIG_MOUSE_PS2_ALPS
        {
@@ -1558,13 +1565,12 @@ static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char
 static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count)
 {
        unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
-       unsigned long value;
-
-       if (strict_strtoul(buf, 10, &value))
-               return -EINVAL;
+       unsigned int value;
+       int err;
 
-       if ((unsigned int)value != value)
-               return -EINVAL;
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
 
        *field = value;
 
@@ -1671,10 +1677,12 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
 
 static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count)
 {
-       unsigned long value;
+       unsigned int value;
+       int err;
 
-       if (strict_strtoul(buf, 10, &value))
-               return -EINVAL;
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
 
        psmouse->set_rate(psmouse, value);
        return count;
@@ -1682,10 +1690,12 @@ static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const
 
 static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count)
 {
-       unsigned long value;
+       unsigned int value;
+       int err;
 
-       if (strict_strtoul(buf, 10, &value))
-               return -EINVAL;
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
 
        psmouse->set_resolution(psmouse, value);
        return count;
index 9b84b0c4e37189fcd9db65599343cfe4f0f0a806..6a417092d010f7afd62f0a95565ad008fb57f6b6 100644 (file)
@@ -8,6 +8,7 @@
 #define PSMOUSE_CMD_SETSTREAM  0x00ea
 #define PSMOUSE_CMD_SETPOLL    0x00f0
 #define PSMOUSE_CMD_POLL       0x00eb  /* caller sets number of bytes to receive */
+#define PSMOUSE_CMD_RESET_WRAP 0x00ec
 #define PSMOUSE_CMD_GETID      0x02f2
 #define PSMOUSE_CMD_SETRATE    0x10f3
 #define PSMOUSE_CMD_ENABLE     0x00f4
@@ -93,6 +94,7 @@ enum psmouse_type {
        PSMOUSE_HGPK,
        PSMOUSE_ELANTECH,
        PSMOUSE_FSP,
+       PSMOUSE_SYNAPTICS_RELATIVE,
        PSMOUSE_AUTO            /* This one should always be last */
 };
 
@@ -102,6 +104,7 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
 int psmouse_reset(struct psmouse *psmouse);
 void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
 void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
+psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse);
 
 struct psmouse_attribute {
        struct device_attribute dattr;
index c5b12d2e955a5cdb8c968c3e31d86bcb99e416b3..5babc47b39aaf87ba837e36fe79bf9f1b2a97605 100644 (file)
@@ -408,7 +408,7 @@ static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable)
 static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
                                   const char *buf, size_t count)
 {
-       unsigned long reg, val;
+       int reg, val;
        char *rest;
        ssize_t retval;
 
@@ -416,7 +416,11 @@ static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
        if (rest == buf || *rest != ' ' || reg > 0xff)
                return -EINVAL;
 
-       if (strict_strtoul(rest + 1, 16, &val) || val > 0xff)
+       retval = kstrtoint(rest + 1, 16, &val);
+       if (retval)
+               return retval;
+
+       if (val > 0xff)
                return -EINVAL;
 
        if (fsp_reg_write_enable(psmouse, true))
@@ -448,10 +452,13 @@ static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,
                                        const char *buf, size_t count)
 {
        struct fsp_data *pad = psmouse->private;
-       unsigned long reg;
-       int val;
+       int reg, val, err;
+
+       err = kstrtoint(buf, 16, &reg);
+       if (err)
+               return err;
 
-       if (strict_strtoul(buf, 16, &reg) || reg > 0xff)
+       if (reg > 0xff)
                return -EINVAL;
 
        if (fsp_reg_read(psmouse, reg, &val))
@@ -480,9 +487,13 @@ static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,
 static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,
                                        const char *buf, size_t count)
 {
-       unsigned long val;
+       int val, err;
 
-       if (strict_strtoul(buf, 16, &val) || val > 0xff)
+       err = kstrtoint(buf, 16, &val);
+       if (err)
+               return err;
+
+       if (val > 0xff)
                return -EINVAL;
 
        if (fsp_page_reg_write(psmouse, val))
@@ -505,9 +516,14 @@ static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse,
 static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data,
                                        const char *buf, size_t count)
 {
-       unsigned long val;
+       unsigned int val;
+       int err;
+
+       err = kstrtouint(buf, 10, &val);
+       if (err)
+               return err;
 
-       if (strict_strtoul(buf, 10, &val) || val > 1)
+       if (val > 1)
                return -EINVAL;
 
        fsp_onpad_vscr(psmouse, val);
@@ -529,9 +545,14 @@ static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse,
 static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data,
                                        const char *buf, size_t count)
 {
-       unsigned long val;
+       unsigned int val;
+       int err;
+
+       err = kstrtouint(buf, 10, &val);
+       if (err)
+               return err;
 
-       if (strict_strtoul(buf, 10, &val) || val > 1)
+       if (val > 1)
                return -EINVAL;
 
        fsp_onpad_hscr(psmouse, val);
index c080b828e5dc5e2f69c8cf859deebe3d6abbae61..06c9ee57951e1d4b121431ff3fa77d2d8a789336 100644 (file)
@@ -268,19 +268,49 @@ static int synaptics_query_hardware(struct psmouse *psmouse)
        return 0;
 }
 
-static int synaptics_set_absolute_mode(struct psmouse *psmouse)
+static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
+{
+       static unsigned char param = 0xc8;
+       struct synaptics_data *priv = psmouse->private;
+
+       if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+               return 0;
+
+       if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
+               return -1;
+
+       if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
+               return -1;
+
+       /* Advanced gesture mode also sends multi finger data */
+       priv->capabilities |= BIT(1);
+
+       return 0;
+}
+
+static int synaptics_set_mode(struct psmouse *psmouse)
 {
        struct synaptics_data *priv = psmouse->private;
 
-       priv->mode = SYN_BIT_ABSOLUTE_MODE;
-       if (SYN_ID_MAJOR(priv->identity) >= 4)
+       priv->mode = 0;
+       if (priv->absolute_mode)
+               priv->mode |= SYN_BIT_ABSOLUTE_MODE;
+       if (priv->disable_gesture)
                priv->mode |= SYN_BIT_DISABLE_GESTURE;
+       if (psmouse->rate >= 80)
+               priv->mode |= SYN_BIT_HIGH_RATE;
        if (SYN_CAP_EXTENDED(priv->capabilities))
                priv->mode |= SYN_BIT_W_MODE;
 
        if (synaptics_mode_cmd(psmouse, priv->mode))
                return -1;
 
+       if (priv->absolute_mode &&
+           synaptics_set_advanced_gesture_mode(psmouse)) {
+               psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+               return -1;
+       }
+
        return 0;
 }
 
@@ -299,26 +329,6 @@ static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
        synaptics_mode_cmd(psmouse, priv->mode);
 }
 
-static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
-{
-       static unsigned char param = 0xc8;
-       struct synaptics_data *priv = psmouse->private;
-
-       if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
-                       SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
-               return 0;
-
-       if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
-               return -1;
-       if (ps2_command(&psmouse->ps2dev, &param, PSMOUSE_CMD_SETRATE))
-               return -1;
-
-       /* Advanced gesture mode also sends multi finger data */
-       priv->capabilities |= BIT(1);
-
-       return 0;
-}
-
 /*****************************************************************************
  *     Synaptics pass-through PS/2 port support
  ****************************************************************************/
@@ -1142,8 +1152,24 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
 {
        int i;
 
+       /* Things that apply to both modes */
        __set_bit(INPUT_PROP_POINTER, dev->propbit);
+       __set_bit(EV_KEY, dev->evbit);
+       __set_bit(BTN_LEFT, dev->keybit);
+       __set_bit(BTN_RIGHT, dev->keybit);
 
+       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+               __set_bit(BTN_MIDDLE, dev->keybit);
+
+       if (!priv->absolute_mode) {
+               /* Relative mode */
+               __set_bit(EV_REL, dev->evbit);
+               __set_bit(REL_X, dev->relbit);
+               __set_bit(REL_Y, dev->relbit);
+               return;
+       }
+
+       /* Absolute mode */
        __set_bit(EV_ABS, dev->evbit);
        set_abs_position_params(dev, priv, ABS_X, ABS_Y);
        input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
@@ -1169,20 +1195,14 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        if (SYN_CAP_PALMDETECT(priv->capabilities))
                input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
 
-       __set_bit(EV_KEY, dev->evbit);
        __set_bit(BTN_TOUCH, dev->keybit);
        __set_bit(BTN_TOOL_FINGER, dev->keybit);
-       __set_bit(BTN_LEFT, dev->keybit);
-       __set_bit(BTN_RIGHT, dev->keybit);
 
        if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
                __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
                __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
        }
 
-       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
-               __set_bit(BTN_MIDDLE, dev->keybit);
-
        if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
            SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
                __set_bit(BTN_FORWARD, dev->keybit);
@@ -1204,10 +1224,58 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        }
 }
 
+static ssize_t synaptics_show_disable_gesture(struct psmouse *psmouse,
+                                             void *data, char *buf)
+{
+       struct synaptics_data *priv = psmouse->private;
+
+       return sprintf(buf, "%c\n", priv->disable_gesture ? '1' : '0');
+}
+
+static ssize_t synaptics_set_disable_gesture(struct psmouse *psmouse,
+                                            void *data, const char *buf,
+                                            size_t len)
+{
+       struct synaptics_data *priv = psmouse->private;
+       unsigned int value;
+       int err;
+
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       if (value > 1)
+               return -EINVAL;
+
+       if (value == priv->disable_gesture)
+               return len;
+
+       priv->disable_gesture = value;
+       if (value)
+               priv->mode |= SYN_BIT_DISABLE_GESTURE;
+       else
+               priv->mode &= ~SYN_BIT_DISABLE_GESTURE;
+
+       if (synaptics_mode_cmd(psmouse, priv->mode))
+               return -EIO;
+
+       return len;
+}
+
+PSMOUSE_DEFINE_ATTR(disable_gesture, S_IWUSR | S_IRUGO, NULL,
+                   synaptics_show_disable_gesture,
+                   synaptics_set_disable_gesture);
+
 static void synaptics_disconnect(struct psmouse *psmouse)
 {
+       struct synaptics_data *priv = psmouse->private;
+
+       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
+               device_remove_file(&psmouse->ps2dev.serio->dev,
+                                  &psmouse_attr_disable_gesture.dattr);
+
        synaptics_reset(psmouse);
-       kfree(psmouse->private);
+       kfree(priv);
        psmouse->private = NULL;
 }
 
@@ -1234,17 +1302,11 @@ static int synaptics_reconnect(struct psmouse *psmouse)
                return -1;
        }
 
-       if (synaptics_set_absolute_mode(psmouse)) {
+       if (synaptics_set_mode(psmouse)) {
                psmouse_err(psmouse, "Unable to initialize device.\n");
                return -1;
        }
 
-       if (synaptics_set_advanced_gesture_mode(psmouse)) {
-               psmouse_err(psmouse,
-                           "Advanced gesture mode reconnect failed.\n");
-               return -1;
-       }
-
        if (old_priv.identity != priv->identity ||
            old_priv.model_id != priv->model_id ||
            old_priv.capabilities != priv->capabilities ||
@@ -1321,20 +1383,18 @@ void __init synaptics_module_init(void)
        broken_olpc_ec = dmi_check_system(olpc_dmi_table);
 }
 
-int synaptics_init(struct psmouse *psmouse)
+static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
 {
        struct synaptics_data *priv;
+       int err = -1;
 
        /*
-        * The OLPC XO has issues with Synaptics' absolute mode; similarly to
-        * the HGPK, it quickly degrades and the hardware becomes jumpy and
-        * overly sensitive.  Not only that, but the constant packet spew
-        * (even at a lowered 40pps rate) overloads the EC such that key
-        * presses on the keyboard are missed.  Given all of that, don't
-        * even attempt to use Synaptics mode.  Relative mode seems to work
-        * just fine.
+        * The OLPC XO has issues with Synaptics' absolute mode; the constant
+        * packet spew overloads the EC such that key presses on the keyboard
+        * are missed.  Given that, don't even attempt to use Absolute mode.
+        * Relative mode seems to work just fine.
         */
-       if (broken_olpc_ec) {
+       if (absolute_mode && broken_olpc_ec) {
                psmouse_info(psmouse,
                             "OLPC XO detected, not enabling Synaptics protocol.\n");
                return -ENODEV;
@@ -1351,13 +1411,12 @@ int synaptics_init(struct psmouse *psmouse)
                goto init_fail;
        }
 
-       if (synaptics_set_absolute_mode(psmouse)) {
-               psmouse_err(psmouse, "Unable to initialize device.\n");
-               goto init_fail;
-       }
+       priv->absolute_mode = absolute_mode;
+       if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+               priv->disable_gesture = true;
 
-       if (synaptics_set_advanced_gesture_mode(psmouse)) {
-               psmouse_err(psmouse, "Advanced gesture mode init failed.\n");
+       if (synaptics_set_mode(psmouse)) {
+               psmouse_err(psmouse, "Unable to initialize device.\n");
                goto init_fail;
        }
 
@@ -1382,12 +1441,19 @@ int synaptics_init(struct psmouse *psmouse)
        psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
                          (priv->model_id & 0x000000ff);
 
-       psmouse->protocol_handler = synaptics_process_byte;
+       if (absolute_mode) {
+               psmouse->protocol_handler = synaptics_process_byte;
+               psmouse->pktsize = 6;
+       } else {
+               /* Relative mode follows standard PS/2 mouse protocol */
+               psmouse->protocol_handler = psmouse_process_byte;
+               psmouse->pktsize = 3;
+       }
+
        psmouse->set_rate = synaptics_set_rate;
        psmouse->disconnect = synaptics_disconnect;
        psmouse->reconnect = synaptics_reconnect;
        psmouse->cleanup = synaptics_reset;
-       psmouse->pktsize = 6;
        /* Synaptics can usually stay in sync without extra help */
        psmouse->resync_time = 0;
 
@@ -1406,11 +1472,32 @@ int synaptics_init(struct psmouse *psmouse)
                psmouse->rate = 40;
        }
 
+       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
+               err = device_create_file(&psmouse->ps2dev.serio->dev,
+                                        &psmouse_attr_disable_gesture.dattr);
+               if (err) {
+                       psmouse_err(psmouse,
+                                   "Failed to create disable_gesture attribute (%d)",
+                                   err);
+                       goto init_fail;
+               }
+       }
+
        return 0;
 
  init_fail:
        kfree(priv);
-       return -1;
+       return err;
+}
+
+int synaptics_init(struct psmouse *psmouse)
+{
+       return __synaptics_init(psmouse, true);
+}
+
+int synaptics_init_relative(struct psmouse *psmouse)
+{
+       return __synaptics_init(psmouse, false);
 }
 
 bool synaptics_supported(void)
index 622aea8dd7e09de66a1e573b081ece89f828e632..fd26ccca13d793b225654b21de12a21f6a306ad5 100644 (file)
 #define SYN_ID_MINOR(i)                        (((i) >> 16) & 0xff)
 #define SYN_ID_FULL(i)                 ((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i))
 #define SYN_ID_IS_SYNAPTICS(i)         ((((i) >> 8) & 0xff) == 0x47)
+#define SYN_ID_DISGEST_SUPPORTED(i)    (SYN_ID_MAJOR(i) >= 4)
 
 /* synaptics special commands */
 #define SYN_PS_SET_MODE2               0x14
@@ -159,6 +160,9 @@ struct synaptics_data {
        unsigned char mode;                     /* current mode byte */
        int scroll;
 
+       bool absolute_mode;                     /* run in Absolute mode */
+       bool disable_gesture;                   /* disable gestures */
+
        struct serio *pt_port;                  /* Pass-through serio port */
 
        struct synaptics_mt_state mt_state;     /* Current mt finger state */
@@ -175,6 +179,7 @@ struct synaptics_data {
 void synaptics_module_init(void);
 int synaptics_detect(struct psmouse *psmouse, bool set_properties);
 int synaptics_init(struct psmouse *psmouse);
+int synaptics_init_relative(struct psmouse *psmouse);
 void synaptics_reset(struct psmouse *psmouse);
 bool synaptics_supported(void);
 
index 54b2fa892e1941b76d3d863013b85a4884595770..22b218018137d964fdae5987addd66a1b7272320 100644 (file)
@@ -89,10 +89,12 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
        struct trackpoint_data *tp = psmouse->private;
        struct trackpoint_attr_data *attr = data;
        unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
-       unsigned long value;
+       unsigned char value;
+       int err;
 
-       if (strict_strtoul(buf, 10, &value) || value > 255)
-               return -EINVAL;
+       err = kstrtou8(buf, 10, &value);
+       if (err)
+               return err;
 
        *field = value;
        trackpoint_write(&psmouse->ps2dev, attr->command, value);
@@ -115,9 +117,14 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
        struct trackpoint_data *tp = psmouse->private;
        struct trackpoint_attr_data *attr = data;
        unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
-       unsigned long value;
+       unsigned int value;
+       int err;
+
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
 
-       if (strict_strtoul(buf, 10, &value) || value > 1)
+       if (value > 1)
                return -EINVAL;
 
        if (attr->inverted)
index d37a48e099d0d0c63441f28c6da2a77488797cfd..86564414b75a4535c0f2745bbf2ea812cb5d5069 100644 (file)
@@ -991,7 +991,7 @@ static int i8042_controller_init(void)
  * Reset the controller and reset CRT to the original value set by BIOS.
  */
 
-static void i8042_controller_reset(void)
+static void i8042_controller_reset(bool force_reset)
 {
        i8042_flush();
 
@@ -1016,7 +1016,7 @@ static void i8042_controller_reset(void)
  * Reset the controller if requested.
  */
 
-       if (i8042_reset)
+       if (i8042_reset || force_reset)
                i8042_controller_selftest();
 
 /*
@@ -1139,9 +1139,9 @@ static int i8042_controller_resume(bool force_reset)
  * upsetting it.
  */
 
-static int i8042_pm_reset(struct device *dev)
+static int i8042_pm_suspend(struct device *dev)
 {
-       i8042_controller_reset();
+       i8042_controller_reset(true);
 
        return 0;
 }
@@ -1163,13 +1163,20 @@ static int i8042_pm_thaw(struct device *dev)
        return 0;
 }
 
+static int i8042_pm_reset(struct device *dev)
+{
+       i8042_controller_reset(false);
+
+       return 0;
+}
+
 static int i8042_pm_restore(struct device *dev)
 {
        return i8042_controller_resume(false);
 }
 
 static const struct dev_pm_ops i8042_pm_ops = {
-       .suspend        = i8042_pm_reset,
+       .suspend        = i8042_pm_suspend,
        .resume         = i8042_pm_resume,
        .thaw           = i8042_pm_thaw,
        .poweroff       = i8042_pm_reset,
@@ -1185,7 +1192,7 @@ static const struct dev_pm_ops i8042_pm_ops = {
 
 static void i8042_shutdown(struct platform_device *dev)
 {
-       i8042_controller_reset();
+       i8042_controller_reset(false);
 }
 
 static int __init i8042_create_kbd_port(void)
@@ -1424,7 +1431,7 @@ static int __init i8042_probe(struct platform_device *dev)
  out_fail:
        i8042_free_aux_ports(); /* in case KBD failed but AUX not */
        i8042_free_irqs();
-       i8042_controller_reset();
+       i8042_controller_reset(false);
        i8042_platform_device = NULL;
 
        return error;
@@ -1434,7 +1441,7 @@ static int __devexit i8042_remove(struct platform_device *dev)
 {
        i8042_unregister_ports();
        i8042_free_irqs();
-       i8042_controller_reset();
+       i8042_controller_reset(false);
        i8042_platform_device = NULL;
 
        return 0;
index 6d89fd1842c32a9a577a6d7c424f00be3cfec340..85bace2c8fe8e0c13014b3f0e1a920d9a5810040 100644 (file)
@@ -1198,9 +1198,9 @@ static ssize_t
 store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct aiptek *aiptek = dev_get_drvdata(dev);
-       long x;
+       int x;
 
-       if (strict_strtol(buf, 10, &x)) {
+       if (kstrtoint(buf, 10, &x)) {
                size_t len = buf[count - 1] == '\n' ? count - 1 : count;
 
                if (strncmp(buf, "disable", len))
@@ -1240,9 +1240,9 @@ static ssize_t
 store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct aiptek *aiptek = dev_get_drvdata(dev);
-       long y;
+       int y;
 
-       if (strict_strtol(buf, 10, &y)) {
+       if (kstrtoint(buf, 10, &y)) {
                size_t len = buf[count - 1] == '\n' ? count - 1 : count;
 
                if (strncmp(buf, "disable", len))
@@ -1277,12 +1277,13 @@ static ssize_t
 store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct aiptek *aiptek = dev_get_drvdata(dev);
-       long j;
+       int err, j;
 
-       if (strict_strtol(buf, 10, &j))
-               return -EINVAL;
+       err = kstrtoint(buf, 10, &j);
+       if (err)
+               return err;
 
-       aiptek->newSetting.jitterDelay = (int)j;
+       aiptek->newSetting.jitterDelay = j;
        return count;
 }
 
@@ -1306,12 +1307,13 @@ static ssize_t
 store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct aiptek *aiptek = dev_get_drvdata(dev);
-       long d;
+       int err, d;
 
-       if (strict_strtol(buf, 10, &d))
-               return -EINVAL;
+       err = kstrtoint(buf, 10, &d);
+       if (err)
+               return err;
 
-       aiptek->newSetting.programmableDelay = (int)d;
+       aiptek->newSetting.programmableDelay = d;
        return count;
 }
 
@@ -1557,11 +1559,13 @@ static ssize_t
 store_tabletWheel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct aiptek *aiptek = dev_get_drvdata(dev);
-       long w;
+       int err, w;
 
-       if (strict_strtol(buf, 10, &w)) return -EINVAL;
+       err = kstrtoint(buf, 10, &w);
+       if (err)
+               return err;
 
-       aiptek->newSetting.wheel = (int)w;
+       aiptek->newSetting.wheel = w;
        return count;
 }
 
index 1c1b7b43cf923f728c697514fec2cc5afaa7cd31..2fe21d1a18b7af9ed5b409e484b004af321e5e27 100644 (file)
@@ -28,7 +28,9 @@
 #define HID_USAGE_Y_TILT               0x3e
 #define HID_USAGE_FINGER               0x22
 #define HID_USAGE_STYLUS               0x20
-#define HID_COLLECTION                 0xc0
+#define HID_COLLECTION                 0xa1
+#define HID_COLLECTION_LOGICAL         0x02
+#define HID_COLLECTION_END             0xc0
 
 enum {
        WCM_UNDEFINED = 0,
@@ -66,7 +68,8 @@ static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id,
        do {
                retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
                                USB_REQ_GET_REPORT,
-                               USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                               USB_DIR_IN | USB_TYPE_CLASS |
+                               USB_RECIP_INTERFACE,
                                (type << 8) + id,
                                intf->altsetting[0].desc.bInterfaceNumber,
                                buf, size, 100);
@@ -164,7 +167,70 @@ static void wacom_close(struct input_dev *dev)
                usb_autopm_put_interface(wacom->intf);
 }
 
-static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
+static int wacom_parse_logical_collection(unsigned char *report,
+                                         struct wacom_features *features)
+{
+       int length = 0;
+
+       if (features->type == BAMBOO_PT) {
+
+               /* Logical collection is only used by 3rd gen Bamboo Touch */
+               features->pktlen = WACOM_PKGLEN_BBTOUCH3;
+               features->device_type = BTN_TOOL_DOUBLETAP;
+
+               /*
+                * Stylus and Touch have same active area
+                * so compute physical size based on stylus
+                * data before its overwritten.
+                */
+               features->x_phy =
+                       (features->x_max * features->x_resolution) / 100;
+               features->y_phy =
+                       (features->y_max * features->y_resolution) / 100;
+
+               features->x_max = features->y_max =
+                       get_unaligned_le16(&report[10]);
+
+               length = 11;
+       }
+       return length;
+}
+
+/*
+ * Interface Descriptor of wacom devices can be incomplete and
+ * inconsistent so wacom_features table is used to store stylus
+ * device's packet lengths, various maximum values, and tablet
+ * resolution based on product ID's.
+ *
+ * For devices that contain 2 interfaces, wacom_features table is
+ * inaccurate for the touch interface.  Since the Interface Descriptor
+ * for touch interfaces has pretty complete data, this function exists
+ * to query tablet for this missing information instead of hard coding in
+ * an additional table.
+ *
+ * A typical Interface Descriptor for a stylus will contain a
+ * boot mouse application collection that is not of interest and this
+ * function will ignore it.
+ *
+ * It also contains a digitizer application collection that also is not
+ * of interest since any information it contains would be duplicate
+ * of what is in wacom_features. Usually it defines a report of an array
+ * of bytes that could be used as max length of the stylus packet returned.
+ * If it happens to define a Digitizer-Stylus Physical Collection then
+ * the X and Y logical values contain valid data but it is ignored.
+ *
+ * A typical Interface Descriptor for a touch interface will contain a
+ * Digitizer-Finger Physical Collection which will define both logical
+ * X/Y maximum as well as the physical size of tablet. Since touch
+ * interfaces haven't supported pressure or distance, this is enough
+ * information to override invalid values in the wacom_features table.
+ *
+ * 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical
+ * Collection. Instead they define a Logical Collection with a single
+ * Logical Maximum for both X and Y.
+ */
+static int wacom_parse_hid(struct usb_interface *intf,
+                          struct hid_descriptor *hid_desc,
                           struct wacom_features *features)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
@@ -244,8 +310,6 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                                                /* penabled only accepts exact bytes of data */
                                                if (features->type == TABLETPC2FG)
                                                        features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-                                               if (features->type == BAMBOO_PT)
-                                                       features->pktlen = WACOM_PKGLEN_BBFUN;
                                                features->device_type = BTN_TOOL_PEN;
                                                features->x_max =
                                                        get_unaligned_le16(&report[i + 3]);
@@ -287,8 +351,6 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                                                /* penabled only accepts exact bytes of data */
                                                if (features->type == TABLETPC2FG)
                                                        features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-                                               if (features->type == BAMBOO_PT)
-                                                       features->pktlen = WACOM_PKGLEN_BBFUN;
                                                features->device_type = BTN_TOOL_PEN;
                                                features->y_max =
                                                        get_unaligned_le16(&report[i + 3]);
@@ -302,6 +364,11 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                                i++;
                                break;
 
+                       /*
+                        * Requiring Stylus Usage will ignore boot mouse
+                        * X/Y values and some cases of invalid Digitizer X/Y
+                        * values commonly reported.
+                        */
                        case HID_USAGE_STYLUS:
                                pen = 1;
                                i++;
@@ -309,10 +376,20 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                        }
                        break;
 
-               case HID_COLLECTION:
+               case HID_COLLECTION_END:
                        /* reset UsagePage and Finger */
                        finger = usage = 0;
                        break;
+
+               case HID_COLLECTION:
+                       i++;
+                       switch (report[i]) {
+                       case HID_COLLECTION_LOGICAL:
+                               i += wacom_parse_logical_collection(&report[i],
+                                                                   features);
+                               break;
+                       }
+                       break;
                }
        }
 
@@ -348,7 +425,8 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
                                                WAC_HID_FEATURE_REPORT,
                                                report_id, rep_data, 4, 1);
                } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
-       } else if (features->type != TABLETPC) {
+       } else if (features->type != TABLETPC &&
+                  features->device_type == BTN_TOOL_PEN) {
                do {
                        rep_data[0] = 2;
                        rep_data[1] = 2;
index da0d8761e778cfd8f79e64b26a0ce3acd8cf60c5..ecfcbc8144dca62757b105b5275e71f3b8371b64 100644 (file)
@@ -799,6 +799,9 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
        unsigned char *data = wacom->data;
        int i;
 
+       if (data[0] != 0x02)
+           return 0;
+
        for (i = 0; i < 2; i++) {
                int offset = (data[1] & 0x80) ? (8 * i) : (9 * i);
                bool touch = data[offset + 3] & 0x80;
@@ -837,18 +840,77 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
        return 0;
 }
 
+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;
+
+       touch = touch && !wacom->shared->stylus_in_proximity;
+
+       input_mt_slot(input, slot_id);
+       input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+
+       if (touch) {
+               int x = (data[2] << 4) | (data[4] >> 4);
+               int y = (data[3] << 4) | (data[4] & 0x0f);
+               int w = data[6];
+
+               input_report_abs(input, ABS_MT_POSITION_X, x);
+               input_report_abs(input, ABS_MT_POSITION_Y, y);
+               input_report_abs(input, ABS_MT_TOUCH_MAJOR, w);
+       }
+}
+
+static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
+{
+       struct input_dev *input = wacom->input;
+
+       input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
+       input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
+       input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
+       input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
+}
+
+static int wacom_bpt3_touch(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int count = data[1] & 0x03;
+       int i;
+
+       if (data[0] != 0x02)
+           return 0;
+
+       /* data has up to 7 fixed sized 8-byte messages starting at data[2] */
+       for (i = 0; i < count; i++) {
+               int offset = (8 * i) + 2;
+               int msg_id = data[offset];
+
+               if (msg_id >= 2 && msg_id <= 17)
+                       wacom_bpt3_touch_msg(wacom, data + offset);
+               else if (msg_id == 128)
+                       wacom_bpt3_button_msg(wacom, data + offset);
+
+       }
+
+       input_mt_report_pointer_emulation(input, true);
+
+       input_sync(input);
+
+       return 0;
+}
+
 static int wacom_bpt_pen(struct wacom_wac *wacom)
 {
        struct input_dev *input = wacom->input;
        unsigned char *data = wacom->data;
        int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
 
-       /*
-        * Similar to Graphire protocol, data[1] & 0x20 is proximity and
-        * data[1] & 0x18 is tool ID.  0x30 is safety check to ignore
-        * 2 unused tool ID's.
-        */
-       prox = (data[1] & 0x30) == 0x30;
+       if (data[0] != 0x02)
+           return 0;
+
+       prox = (data[1] & 0x20) == 0x20;
 
        /*
         * All reports shared between PEN and RUBBER tool must be
@@ -912,7 +974,9 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
 {
        if (len == WACOM_PKGLEN_BBTOUCH)
                return wacom_bpt_touch(wacom);
-       else if (len == WACOM_PKGLEN_BBFUN)
+       else if (len == WACOM_PKGLEN_BBTOUCH3)
+               return wacom_bpt3_touch(wacom);
+       else if (len == WACOM_PKGLEN_BBFUN || len == WACOM_PKGLEN_BBPEN)
                return wacom_bpt_pen(wacom);
 
        return 0;
@@ -1031,9 +1095,9 @@ void wacom_setup_device_quirks(struct wacom_features *features)
            features->type == BAMBOO_PT)
                features->quirks |= WACOM_QUIRK_MULTI_INPUT;
 
-       /* quirks for bamboo touch */
+       /* quirk for bamboo touch with 2 low res touches */
        if (features->type == BAMBOO_PT &&
-           features->device_type == BTN_TOOL_DOUBLETAP) {
+           features->pktlen == WACOM_PKGLEN_BBTOUCH) {
                features->x_max <<= 5;
                features->y_max <<= 5;
                features->x_fuzz <<= 5;
@@ -1240,7 +1304,21 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
                        __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
                        __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
 
-                       input_mt_init_slots(input_dev, 2);
+                       if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+                               __set_bit(BTN_TOOL_TRIPLETAP,
+                                         input_dev->keybit);
+                               __set_bit(BTN_TOOL_QUADTAP,
+                                         input_dev->keybit);
+
+                               input_mt_init_slots(input_dev, 16);
+
+                               input_set_abs_params(input_dev,
+                                                    ABS_MT_TOUCH_MAJOR,
+                                                    0, 255, 0, 0);
+                       } else {
+                               input_mt_init_slots(input_dev, 2);
+                       }
+
                        input_set_abs_params(input_dev, ABS_MT_POSITION_X,
                                             0, features->x_max,
                                             features->x_fuzz, 0);
@@ -1506,6 +1584,15 @@ static const struct wacom_features wacom_features_0xDA =
 static struct wacom_features wacom_features_0xDB =
        { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN,  21648, 13700, 1023,
          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xDD =
+        { "Wacom Bamboo Connect", WACOM_PKGLEN_BBPEN,     14720,  9200, 1023,
+          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xDE =
+        { "Wacom Bamboo 16FG 4x5", WACOM_PKGLEN_BBPEN,    14720,  9200, 1023,
+          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xDF =
+        { "Wacom Bamboo 16FG 6x8", WACOM_PKGLEN_BBPEN,    21648, 13700, 1023,
+          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x6004 =
        { "ISD-V4",               WACOM_PKGLEN_GRAPHIRE,  12800,  8000,  255,
          0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@@ -1601,6 +1688,9 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0xD8) },
        { USB_DEVICE_WACOM(0xDA) },
        { USB_DEVICE_WACOM(0xDB) },
+       { USB_DEVICE_WACOM(0xDD) },
+       { USB_DEVICE_WACOM(0xDE) },
+       { USB_DEVICE_WACOM(0xDF) },
        { USB_DEVICE_WACOM(0xF0) },
        { USB_DEVICE_WACOM(0xCC) },
        { USB_DEVICE_WACOM(0x90) },
index 53eb71b6833099ad0a23aed0a3943553231794f1..27f1d1c203a1d2a51eb739ac687ee806bcdaff97 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/types.h>
 
 /* maximum packet length for USB devices */
-#define WACOM_PKGLEN_MAX       32
+#define WACOM_PKGLEN_MAX       64
 
 /* packet length for individual models */
 #define WACOM_PKGLEN_PENPRTN    7
@@ -22,6 +22,8 @@
 #define WACOM_PKGLEN_TPC1FG     5
 #define WACOM_PKGLEN_TPC2FG    14
 #define WACOM_PKGLEN_BBTOUCH   20
+#define WACOM_PKGLEN_BBTOUCH3  64
+#define WACOM_PKGLEN_BBPEN     10
 
 /* device IDs */
 #define STYLUS_DEVICE_ID       0x02
index 3488ffe1fa0a16bc7b5c5a53802607a11fb340fb..2b456a915d77130edd2d3e7decfc1fb148a54fd0 100644 (file)
@@ -177,6 +177,16 @@ config TOUCHSCREEN_EETI
          To compile this driver as a module, choose M here: the
          module will be called eeti_ts.
 
+config TOUCHSCREEN_EGALAX
+       tristate "EETI eGalax multi-touch panel support"
+       depends on I2C
+       help
+         Say Y here to enable support for I2C connected EETI
+         eGalax multi-touch panels.
+
+         To compile this driver as a module, choose M here: the
+         module will be called egalax_ts.
+
 config TOUCHSCREEN_FUJITSU
        tristate "Fujitsu serial touchscreen"
        select SERIO
@@ -541,6 +551,7 @@ config TOUCHSCREEN_USB_COMPOSITE
          - GoTop Super_Q2/GogoPen/PenPower tablets
          - JASTEC USB Touch Controller/DigiTech DTR-02U
          - Zytronic controllers
+         - Elo TouchSystems 2700 IntelliTouch
 
          Have a look at <http://linux.chapter7.ch/touchkit/> for
          a usage description and the required user-space stuff.
@@ -620,6 +631,11 @@ config TOUCHSCREEN_USB_JASTEC
        bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT
        depends on TOUCHSCREEN_USB_COMPOSITE
 
+config TOUCHSCREEN_USB_ELO
+       default y
+       bool "Elo TouchSystems 2700 IntelliTouch controller device support" if EXPERT
+       depends on TOUCHSCREEN_USB_COMPOSITE
+
 config TOUCHSCREEN_USB_E2I
        default y
        bool "e2i Touchscreen controller (e.g. from Mimo 740)"
index f957676035a4e6fb5b17c2b4847a9b5d602d2784..a09c546b33b791ef527f975b4a05ecf97bbe3355 100644 (file)
@@ -23,6 +23,7 @@ obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)   += hampshire.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)                += gunze.o
 obj-$(CONFIG_TOUCHSCREEN_EETI)         += eeti_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)          += elo.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX)       += egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)      += fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)       += inexio.o
 obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)    += intel-mid-touch.o
index 400131df677b424c025682f0a61c97fe4bc85109..2da9f189e4938d8628b5056b292cac75c7522495 100644 (file)
@@ -488,10 +488,10 @@ static ssize_t ad7877_disable_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct ad7877 *ts = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtouint(buf, 10, &val);
        if (error)
                return error;
 
@@ -518,10 +518,10 @@ static ssize_t ad7877_dac_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct ad7877 *ts = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtouint(buf, 10, &val);
        if (error)
                return error;
 
@@ -548,10 +548,10 @@ static ssize_t ad7877_gpio3_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct ad7877 *ts = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtouint(buf, 10, &val);
        if (error)
                return error;
 
@@ -579,10 +579,10 @@ static ssize_t ad7877_gpio4_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct ad7877 *ts = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtouint(buf, 10, &val);
        if (error)
                return error;
 
index c789b974c7958ad7fec36e6f45de9b9b7dc3367d..c4b51170c951e866cd02cb58f8dc67c30466fa1e 100644 (file)
 
 #define AD7879_DEVID           0x79    /* AD7879-1/AD7889-1 */
 
-#ifdef CONFIG_PM_SLEEP
-static int ad7879_i2c_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct ad7879 *ts = i2c_get_clientdata(client);
-
-       ad7879_suspend(ts);
-
-       return 0;
-}
-
-static int ad7879_i2c_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct ad7879 *ts = i2c_get_clientdata(client);
-
-       ad7879_resume(ts);
-
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume);
-
 /* All registers are word-sized.
  * AD7879 uses a high-byte first convention.
  */
@@ -119,7 +95,7 @@ static struct i2c_driver ad7879_i2c_driver = {
        .driver = {
                .name   = "ad7879",
                .owner  = THIS_MODULE,
-               .pm     = &ad7879_i2c_pm,
+               .pm     = &ad7879_pm_ops,
        },
        .probe          = ad7879_i2c_probe,
        .remove         = __devexit_p(ad7879_i2c_remove),
index b1643c8fa7c9a1bca311e90e2cfc39e7ba836e2f..8f391ffbf441ddc25c21b5a18da9c0bce05a88fe 100644 (file)
 #define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
 #define AD7879_READCMD(reg)  (AD7879_CMD(reg) | AD7879_CMD_READ)
 
-#ifdef CONFIG_PM_SLEEP
-static int ad7879_spi_suspend(struct device *dev)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       struct ad7879 *ts = spi_get_drvdata(spi);
-
-       ad7879_suspend(ts);
-
-       return 0;
-}
-
-static int ad7879_spi_resume(struct device *dev)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       struct ad7879 *ts = spi_get_drvdata(spi);
-
-       ad7879_resume(ts);
-
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(ad7879_spi_pm, ad7879_spi_suspend, ad7879_spi_resume);
-
 /*
  * ad7879_read/write are only used for initial setup and for sysfs controls.
  * The main traffic is done in ad7879_collect().
@@ -176,7 +152,7 @@ static struct spi_driver ad7879_spi_driver = {
                .name   = "ad7879",
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
-               .pm     = &ad7879_spi_pm,
+               .pm     = &ad7879_pm_ops,
        },
        .probe          = ad7879_spi_probe,
        .remove         = __devexit_p(ad7879_spi_remove),
index 3b2e9ed2aeec3482c7ec178b238410fc93424ff2..e2482b40da5198fdb1406e135fff827d049e5895 100644 (file)
@@ -281,8 +281,11 @@ static void ad7879_close(struct input_dev* input)
                __ad7879_disable(ts);
 }
 
-void ad7879_suspend(struct ad7879 *ts)
+#ifdef CONFIG_PM_SLEEP
+static int ad7879_suspend(struct device *dev)
 {
+       struct ad7879 *ts = dev_get_drvdata(dev);
+
        mutex_lock(&ts->input->mutex);
 
        if (!ts->suspended && !ts->disabled && ts->input->users)
@@ -291,11 +294,14 @@ void ad7879_suspend(struct ad7879 *ts)
        ts->suspended = true;
 
        mutex_unlock(&ts->input->mutex);
+
+       return 0;
 }
-EXPORT_SYMBOL(ad7879_suspend);
 
-void ad7879_resume(struct ad7879 *ts)
+static int ad7879_resume(struct device *dev)
 {
+       struct ad7879 *ts = dev_get_drvdata(dev);
+
        mutex_lock(&ts->input->mutex);
 
        if (ts->suspended && !ts->disabled && ts->input->users)
@@ -304,8 +310,13 @@ void ad7879_resume(struct ad7879 *ts)
        ts->suspended = false;
 
        mutex_unlock(&ts->input->mutex);
+
+       return 0;
 }
-EXPORT_SYMBOL(ad7879_resume);
+#endif
+
+SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume);
+EXPORT_SYMBOL(ad7879_pm_ops);
 
 static void ad7879_toggle(struct ad7879 *ts, bool disable)
 {
@@ -340,10 +351,10 @@ static ssize_t ad7879_disable_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct ad7879 *ts = dev_get_drvdata(dev);
-       unsigned long val;
+       unsigned int val;
        int error;
 
-       error = strict_strtoul(buf, 10, &val);
+       error = kstrtouint(buf, 10, &val);
        if (error)
                return error;
 
index 6b45a27236c72ec640ed0e160a1755a12917daba..6fd13c48d373d5d4c8fa46996278376af11aff17 100644 (file)
@@ -21,8 +21,8 @@ struct ad7879_bus_ops {
        int (*write)(struct device *dev, u8 reg, u16 val);
 };
 
-void ad7879_suspend(struct ad7879 *);
-void ad7879_resume(struct ad7879 *);
+extern const struct dev_pm_ops ad7879_pm_ops;
+
 struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
                            const struct ad7879_bus_ops *bops);
 void ad7879_remove(struct ad7879 *);
index de31ec6fe9e47005ba94cc9eae222f922bb9c4b9..4cedae6a36eaa0328abb1831647784a6c29d6747 100644 (file)
@@ -602,10 +602,12 @@ static ssize_t ads7846_disable_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct ads7846 *ts = dev_get_drvdata(dev);
-       unsigned long i;
+       unsigned int i;
+       int err;
 
-       if (strict_strtoul(buf, 10, &i))
-               return -EINVAL;
+       err = kstrtouint(buf, 10, &i);
+       if (err)
+               return err;
 
        if (i)
                ads7846_disable(ts);
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
new file mode 100644 (file)
index 0000000..eadcc2e
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Driver for EETI eGalax Multiple Touch Controller
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * based on max11801_ts.c
+ *
+ * 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.
+ */
+
+/* EETI eGalax serial touch screen controller is a I2C based multiple
+ * touch screen controller, it supports 5 point multiple touch. */
+
+/* TODO:
+  - auto idle mode support
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/input/mt.h>
+
+/*
+ * Mouse Mode: some panel may configure the controller to mouse mode,
+ * which can only report one point at a given time.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_MOUSE              0x1
+/*
+ * Vendor Mode: this mode is used to transfer some vendor specific
+ * messages.
+ * This driver will ignore events in this mode.
+ */
+#define REPORT_MODE_VENDOR             0x3
+/* Multiple Touch Mode */
+#define REPORT_MODE_MTTOUCH            0x4
+
+#define MAX_SUPPORT_POINTS             5
+
+#define EVENT_VALID_OFFSET     7
+#define EVENT_VALID_MASK       (0x1 << EVENT_VALID_OFFSET)
+#define EVENT_ID_OFFSET                2
+#define EVENT_ID_MASK          (0xf << EVENT_ID_OFFSET)
+#define EVENT_IN_RANGE         (0x1 << 1)
+#define EVENT_DOWN_UP          (0X1 << 0)
+
+#define MAX_I2C_DATA_LEN       10
+
+#define EGALAX_MAX_X   32760
+#define EGALAX_MAX_Y   32760
+#define EGALAX_MAX_TRIES 100
+
+struct egalax_ts {
+       struct i2c_client               *client;
+       struct input_dev                *input_dev;
+};
+
+static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
+{
+       struct egalax_ts *ts = dev_id;
+       struct input_dev *input_dev = ts->input_dev;
+       struct i2c_client *client = ts->client;
+       u8 buf[MAX_I2C_DATA_LEN];
+       int id, ret, x, y, z;
+       int tries = 0;
+       bool down, valid;
+       u8 state;
+
+       do {
+               ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
+       } while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
+
+       if (ret < 0)
+               return IRQ_HANDLED;
+
+       if (buf[0] != REPORT_MODE_MTTOUCH) {
+               /* ignore mouse events and vendor events */
+               return IRQ_HANDLED;
+       }
+
+       state = buf[1];
+       x = (buf[3] << 8) | buf[2];
+       y = (buf[5] << 8) | buf[4];
+       z = (buf[7] << 8) | buf[6];
+
+       valid = state & EVENT_VALID_MASK;
+       id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
+       down = state & EVENT_DOWN_UP;
+
+       if (!valid || id > MAX_SUPPORT_POINTS) {
+               dev_dbg(&client->dev, "point invalid\n");
+               return IRQ_HANDLED;
+       }
+
+       input_mt_slot(input_dev, id);
+       input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
+
+       dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d",
+               down ? "down" : "up", id, x, y, z);
+
+       if (down) {
+               input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+               input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+               input_report_abs(input_dev, ABS_MT_PRESSURE, z);
+       }
+
+       input_mt_report_pointer_emulation(input_dev, true);
+       input_sync(input_dev);
+
+       return IRQ_HANDLED;
+}
+
+/* wake up controller by an falling edge of interrupt gpio.  */
+static int egalax_wake_up_device(struct i2c_client *client)
+{
+       int gpio = irq_to_gpio(client->irq);
+       int ret;
+
+       ret = gpio_request(gpio, "egalax_irq");
+       if (ret < 0) {
+               dev_err(&client->dev,
+                       "request gpio failed, cannot wake up controller: %d\n",
+                       ret);
+               return ret;
+       }
+
+       /* wake up controller via an falling edge on IRQ gpio. */
+       gpio_direction_output(gpio, 0);
+       gpio_set_value(gpio, 1);
+
+       /* controller should be waken up, return irq.  */
+       gpio_direction_input(gpio);
+       gpio_free(gpio);
+
+       return 0;
+}
+
+static int __devinit egalax_firmware_version(struct i2c_client *client)
+{
+       static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
+       int ret;
+
+       ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int __devinit egalax_ts_probe(struct i2c_client *client,
+                                      const struct i2c_device_id *id)
+{
+       struct egalax_ts *ts;
+       struct input_dev *input_dev;
+       int ret;
+       int error;
+
+       ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL);
+       if (!ts) {
+               dev_err(&client->dev, "Failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(&client->dev, "Failed to allocate memory\n");
+               error = -ENOMEM;
+               goto err_free_ts;
+       }
+
+       ts->client = client;
+       ts->input_dev = input_dev;
+
+       /* controller may be in sleep, wake it up. */
+       egalax_wake_up_device(client);
+
+       ret = egalax_firmware_version(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to read firmware version\n");
+               error = -EIO;
+               goto err_free_dev;
+       }
+
+       input_dev->name = "EETI eGalax Touch Screen";
+       input_dev->id.bustype = BUS_I2C;
+       input_dev->dev.parent = &client->dev;
+
+       __set_bit(EV_ABS, input_dev->evbit);
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(BTN_TOUCH, input_dev->keybit);
+
+       input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0);
+       input_set_abs_params(input_dev,
+                            ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
+       input_set_abs_params(input_dev,
+                            ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
+       input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
+
+       input_set_drvdata(input_dev, ts);
+
+       error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt,
+                                    IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                    "egalax_ts", ts);
+       if (error < 0) {
+               dev_err(&client->dev, "Failed to register interrupt\n");
+               goto err_free_dev;
+       }
+
+       error = input_register_device(ts->input_dev);
+       if (error)
+               goto err_free_irq;
+
+       i2c_set_clientdata(client, ts);
+       return 0;
+
+err_free_irq:
+       free_irq(client->irq, ts);
+err_free_dev:
+       input_free_device(input_dev);
+err_free_ts:
+       kfree(ts);
+
+       return error;
+}
+
+static __devexit int egalax_ts_remove(struct i2c_client *client)
+{
+       struct egalax_ts *ts = i2c_get_clientdata(client);
+
+       free_irq(client->irq, ts);
+
+       input_unregister_device(ts->input_dev);
+       kfree(ts);
+
+       return 0;
+}
+
+static const struct i2c_device_id egalax_ts_id[] = {
+       { "egalax_ts", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, egalax_ts_id);
+
+#ifdef CONFIG_PM_SLEEP
+static int egalax_ts_suspend(struct device *dev)
+{
+       static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = {
+               0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
+       };
+       struct i2c_client *client = to_i2c_client(dev);
+       int ret;
+
+       ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
+       return ret > 0 ? 0 : ret;
+}
+
+static int egalax_ts_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       return egalax_wake_up_device(client);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
+
+static struct i2c_driver egalax_ts_driver = {
+       .driver = {
+               .name   = "egalax_ts",
+               .owner  = THIS_MODULE,
+               .pm     = &egalax_ts_pm_ops,
+       },
+       .id_table       = egalax_ts_id,
+       .probe          = egalax_ts_probe,
+       .remove         = __devexit_p(egalax_ts_remove),
+};
+
+static int __init egalax_ts_init(void)
+{
+       return i2c_add_driver(&egalax_ts_driver);
+}
+
+static void __exit egalax_ts_exit(void)
+{
+       i2c_del_driver(&egalax_ts_driver);
+}
+
+module_init(egalax_ts_init);
+module_exit(egalax_ts_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
+MODULE_LICENSE("GPL");
index 73fd6642b681b92a074b6e8ab2619b65768292f7..9dbd8b4d9a6e77a8d8393044af2c15fd2a8f731a 100644 (file)
@@ -16,6 +16,7 @@
  *  - JASTEC USB touch controller/DigiTech DTR-02U
  *  - Zytronic capacitive touchscreen
  *  - NEXIO/iNexio
+ *  - Elo TouchSystems 2700 IntelliTouch
  *
  * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
  * Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -138,6 +139,7 @@ enum {
        DEVTYPE_ZYTRONIC,
        DEVTYPE_TC45USB,
        DEVTYPE_NEXIO,
+       DEVTYPE_ELO,
 };
 
 #define USB_DEVICE_HID_CLASS(vend, prod) \
@@ -239,6 +241,10 @@ static const struct usb_device_id usbtouch_devices[] = {
                .driver_info = DEVTYPE_NEXIO},
 #endif
 
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+       {USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO},
+#endif
+
        {}
 };
 
@@ -944,6 +950,24 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
 #endif
 
 
+/*****************************************************************************
+ * ELO part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+
+static int elo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+       dev->x = (pkt[3] << 8) | pkt[2];
+       dev->y = (pkt[5] << 8) | pkt[4];
+       dev->touch = pkt[6] > 0;
+       dev->press = pkt[6];
+
+       return 1;
+}
+#endif
+
+
 /*****************************************************************************
  * the different device descriptors
  */
@@ -953,6 +977,18 @@ static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
 #endif
 
 static struct usbtouch_device_info usbtouch_dev_info[] = {
+#ifdef CONFIG_TOUCHSCREEN_USB_ELO
+       [DEVTYPE_ELO] = {
+               .min_xc         = 0x0,
+               .max_xc         = 0x0fff,
+               .min_yc         = 0x0,
+               .max_yc         = 0x0fff,
+               .max_press      = 0xff,
+               .rept_size      = 8,
+               .read_data      = elo_read_data,
+       },
+#endif
+
 #ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
        [DEVTYPE_EGALAX] = {
                .min_xc         = 0x0,
diff --git a/include/linux/input/samsung-keypad.h b/include/linux/input/samsung-keypad.h
new file mode 100644 (file)
index 0000000..f25619b
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Samsung Keypad platform data definitions
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __SAMSUNG_KEYPAD_H
+#define __SAMSUNG_KEYPAD_H
+
+#include <linux/input/matrix_keypad.h>
+
+#define SAMSUNG_MAX_ROWS       8
+#define SAMSUNG_MAX_COLS       8
+
+/**
+ * struct samsung_keypad_platdata - Platform device data for Samsung Keypad.
+ * @keymap_data: pointer to &matrix_keymap_data.
+ * @rows: number of keypad row supported.
+ * @cols: number of keypad col supported.
+ * @no_autorepeat: disable key autorepeat.
+ * @wakeup: controls whether the device should be set up as wakeup source.
+ * @cfg_gpio: configure the GPIO.
+ *
+ * Initialisation data specific to either the machine or the platform
+ * for the device driver to use or call-back when configuring gpio.
+ */
+struct samsung_keypad_platdata {
+       const struct matrix_keymap_data *keymap_data;
+       unsigned int rows;
+       unsigned int cols;
+       bool no_autorepeat;
+       bool wakeup;
+
+       void (*cfg_gpio)(unsigned int rows, unsigned int cols);
+};
+
+#endif /* __SAMSUNG_KEYPAD_H */
diff --git a/include/linux/input/tca8418_keypad.h b/include/linux/input/tca8418_keypad.h
new file mode 100644 (file)
index 0000000..e71a85d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * TCA8418 keypad platform support
+ *
+ * Copyright (C) 2011 Fuel7, Inc.  All rights reserved.
+ *
+ * Author: Kyle Manna <kyle.manna@fuel7.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ * If you can't comply with GPLv2, alternative licensing terms may be
+ * arranged. Please contact Fuel7, Inc. (http://fuel7.com/) for proprietary
+ * alternative licensing inquiries.
+ */
+
+#ifndef _TCA8418_KEYPAD_H
+#define _TCA8418_KEYPAD_H
+
+#include <linux/types.h>
+#include <linux/input/matrix_keypad.h>
+
+#define TCA8418_I2C_ADDR       0x34
+#define        TCA8418_NAME            "tca8418_keypad"
+
+struct tca8418_keypad_platform_data {
+       const struct matrix_keymap_data *keymap_data;
+       unsigned rows;
+       unsigned cols;
+       bool rep;
+       bool irq_is_gpio;
+};
+
+#endif