Merge tag 'v3.19-rc4' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 15 Jan 2015 17:46:14 +0000 (09:46 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 15 Jan 2015 17:46:14 +0000 (09:46 -0800)
Merge with mainline to bring in the latest thermal and other changes.

29 files changed:
Documentation/ABI/testing/sysfs-driver-input-axp-pek [new file with mode: 0644]
Documentation/devicetree/bindings/input/e3x0-button.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/regulator-haptic.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
MAINTAINERS
drivers/input/evdev.c
drivers/input/input.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/imx_keypad.c
drivers/input/keyboard/sun4i-lradc-keys.c [new file with mode: 0644]
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/axp20x-pek.c [new file with mode: 0644]
drivers/input/misc/e3x0-button.c [new file with mode: 0644]
drivers/input/misc/regulator-haptic.c [new file with mode: 0644]
drivers/input/misc/tps65218-pwrbutton.c [new file with mode: 0644]
drivers/input/mouse/Kconfig
drivers/input/mouse/alps.c
drivers/input/mouse/focaltech.c
drivers/input/mouse/focaltech.h
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/touchscreen/elants_i2c.c
include/linux/platform_data/regulator-haptic.h [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/sysfs-driver-input-axp-pek b/Documentation/ABI/testing/sysfs-driver-input-axp-pek
new file mode 100644 (file)
index 0000000..a5e671b
--- /dev/null
@@ -0,0 +1,11 @@
+What:          /sys/class/input/input(x)/device/startup
+Date:          March 2014
+Contact:       Carlo Caione <carlo@caione.org>
+Description:   Startup time in us. Board is powered on if the button is pressed
+               for more than <startup_time>
+
+What:          /sys/class/input/input(x)/device/shutdown
+Date:          March 2014
+Contact:       Carlo Caione <carlo@caione.org>
+Description:   Shutdown time in us. Board is powered off if the button is pressed
+               for more than <shutdown_time>
diff --git a/Documentation/devicetree/bindings/input/e3x0-button.txt b/Documentation/devicetree/bindings/input/e3x0-button.txt
new file mode 100644 (file)
index 0000000..751665e
--- /dev/null
@@ -0,0 +1,25 @@
+National Instruments Ettus Research USRP E3x0 button driver
+
+This module is part of the NI Ettus Research USRP E3x0 SDR.
+
+This module provides a simple power button event via two interrupts.
+
+Required properties:
+- compatible: should be one of the following
+  - "ettus,e3x0-button": For devices such as the NI Ettus Research USRP E3x0
+- interrupt-parent:
+  - a phandle to the interrupt controller that it is attached to.
+- interrupts: should be one of the following
+  - <0 30 1>, <0 31 1>: For devices such as the NI Ettus Research USRP E3x0
+- interrupt-names: should be one of the following
+  - "press", "release": For devices such as the NI Ettus Research USRP E3x0
+
+Note: Interrupt numbers might vary depending on the FPGA configuration.
+
+Example:
+       button {
+               compatible = "ettus,e3x0-button";
+               interrupt-parent = <&intc>;
+               interrupts = <0 30 1>, <0 31 1>;
+               interrupt-names = "press", "release";
+       }
diff --git a/Documentation/devicetree/bindings/input/regulator-haptic.txt b/Documentation/devicetree/bindings/input/regulator-haptic.txt
new file mode 100644 (file)
index 0000000..3ed1c7e
--- /dev/null
@@ -0,0 +1,21 @@
+* Regulator Haptic Device Tree Bindings
+
+Required Properties:
+ - compatible : Should be "regulator-haptic"
+ - haptic-supply : Power supply to the haptic motor.
+       [*] refer Documentation/devicetree/bindings/regulator/regulator.txt
+
+ - max-microvolt : The maximum voltage value supplied to the haptic motor.
+               [The unit of the voltage is a micro]
+
+ - min-microvolt : The minimum voltage value supplied to the haptic motor.
+               [The unit of the voltage is a micro]
+
+Example:
+
+       haptics {
+               compatible = "regulator-haptic";
+               haptic-supply = <&motor_regulator>;
+               max-microvolt = <2700000>;
+               min-microvolt = <1100000>;
+       };
diff --git a/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt b/Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
new file mode 100644 (file)
index 0000000..b9c32f6
--- /dev/null
@@ -0,0 +1,62 @@
+Allwinner sun4i low res adc attached tablet keys
+------------------------------------------------
+
+Required properties:
+ - compatible: "allwinner,sun4i-a10-lradc-keys"
+ - reg: mmio address range of the chip
+ - interrupts: interrupt to which the chip is connected
+ - vref-supply: powersupply for the lradc reference voltage
+
+Each key is represented as a sub-node of "allwinner,sun4i-a10-lradc-keys":
+
+Required subnode-properties:
+       - label: Descriptive name of the key.
+       - linux,code: Keycode to emit.
+       - channel: Channel this key is attached to, mut be 0 or 1.
+       - voltage: Voltage in µV at lradc input when this key is pressed.
+
+Example:
+
+#include <dt-bindings/input/input.h>
+
+       lradc: lradc@01c22800 {
+               compatible = "allwinner,sun4i-a10-lradc-keys";
+               reg = <0x01c22800 0x100>;
+               interrupts = <31>;
+               vref-supply = <&reg_vcc3v0>;
+
+               button@191 {
+                       label = "Volume Up";
+                       linux,code = <KEY_VOLUMEUP>;
+                       channel = <0>;
+                       voltage = <191274>;
+               };
+
+               button@392 {
+                       label = "Volume Down";
+                       linux,code = <KEY_VOLUMEDOWN>;
+                       channel = <0>;
+                       voltage = <392644>;
+               };
+
+               button@601 {
+                       label = "Menu";
+                       linux,code = <KEY_MENU>;
+                       channel = <0>;
+                       voltage = <601151>;
+               };
+
+               button@795 {
+                       label = "Enter";
+                       linux,code = <KEY_ENTER>;
+                       channel = <0>;
+                       voltage = <795090>;
+               };
+
+               button@987 {
+                       label = "Home";
+                       linux,code = <KEY_HOMEPAGE>;
+                       channel = <0>;
+                       voltage = <987387>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt b/Documentation/devicetree/bindings/input/tps65218-pwrbutton.txt
new file mode 100644 (file)
index 0000000..e30e0b9
--- /dev/null
@@ -0,0 +1,17 @@
+Texas Instruments TPS65218 power button
+
+This driver provides a simple power button event via an Interrupt.
+
+Required properties:
+- compatible: should be "ti,tps65218-pwrbutton"
+- interrupts: should be one of the following
+   - <3 IRQ_TYPE_EDGE_BOTH>: For controllers compatible with tps65218
+
+Example:
+
+&tps {
+       power-button {
+               compatible = "ti,tps65218-pwrbutton";
+               interrupts = <3 IRQ_TYPE_EDGE_BOTH>;
+       };
+};
index b1df0ad..38c13dc 100644 (file)
@@ -54,6 +54,7 @@ epcos EPCOS AG
 epfl   Ecole Polytechnique Fédérale de Lausanne
 epson  Seiko Epson Corp.
 est    ESTeem Wireless Modems
+ettus  NI Ettus Research
 eukrea  Eukréa Electromatique
 everest        Everest Semiconductor Co. Ltd.
 excito Excito
index 3589d67..f14405a 100644 (file)
@@ -3467,6 +3467,14 @@ M:       "Maciej W. Rozycki" <macro@linux-mips.org>
 S:     Maintained
 F:     drivers/tty/serial/dz.*
 
+E3X0 POWER BUTTON DRIVER
+M:     Moritz Fischer <moritz.fischer@ettus.com>
+L:     usrp-users@lists.ettus.com
+W:     http://www.ettus.com
+S:     Supported
+F:     drivers/input/misc/e3x0-button.c
+F:     Documentation/devicetree/bindings/input/e3x0-button.txt
+
 E4000 MEDIA DRIVER
 M:     Antti Palosaari <crope@iki.fi>
 L:     linux-media@vger.kernel.org
@@ -9205,6 +9213,13 @@ F:       arch/m68k/sun3*/
 F:     arch/m68k/include/asm/sun3*
 F:     drivers/net/ethernet/i825xx/sun3*
 
+SUN4I LOW RES ADC ATTACHED TABLET KEYS DRIVER
+M:     Hans de Goede <hdegoede@redhat.com>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/input/sun4i-lradc-keys.txt
+F:     drivers/input/keyboard/sun4i-lradc-keys.c
+
 SUNDANCE NETWORK DRIVER
 M:     Denis Kirjanov <kda@linux-powerpc.org>
 L:     netdev@vger.kernel.org
index 18d4b2c..e7cee38 100644 (file)
@@ -62,26 +62,6 @@ struct evdev_client {
        struct input_event buffer[];
 };
 
-static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
-{
-       switch (clkid) {
-
-       case CLOCK_REALTIME:
-               client->clk_type = EV_CLK_REAL;
-               break;
-       case CLOCK_MONOTONIC:
-               client->clk_type = EV_CLK_MONO;
-               break;
-       case CLOCK_BOOTTIME:
-               client->clk_type = EV_CLK_BOOT;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 /* flush queued events of type @type, caller must hold client->buffer_lock */
 static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
 {
@@ -128,8 +108,8 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
        client->head = head;
 }
 
-/* queue SYN_DROPPED event */
-static void evdev_queue_syn_dropped(struct evdev_client *client)
+/* queue SYN_DROPPED event and flush queue if flush parameter is true */
+static void evdev_queue_syn_dropped(struct evdev_client *client, bool flush)
 {
        unsigned long flags;
        struct input_event ev;
@@ -148,6 +128,9 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
 
        spin_lock_irqsave(&client->buffer_lock, flags);
 
+       if (flush)
+               client->packet_head = client->head = client->tail;
+
        client->buffer[client->head++] = ev;
        client->head &= client->bufsize - 1;
 
@@ -160,6 +143,32 @@ static void evdev_queue_syn_dropped(struct evdev_client *client)
        spin_unlock_irqrestore(&client->buffer_lock, flags);
 }
 
+static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
+{
+       if (client->clk_type == clkid)
+               return 0;
+
+       switch (clkid) {
+
+       case CLOCK_REALTIME:
+               client->clk_type = EV_CLK_REAL;
+               break;
+       case CLOCK_MONOTONIC:
+               client->clk_type = EV_CLK_MONO;
+               break;
+       case CLOCK_BOOTTIME:
+               client->clk_type = EV_CLK_BOOT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Flush pending events and queue SYN_DROPPED event.*/
+       evdev_queue_syn_dropped(client, true);
+
+       return 0;
+}
+
 static void __pass_event(struct evdev_client *client,
                         const struct input_event *event)
 {
@@ -794,7 +803,7 @@ static int evdev_handle_get_val(struct evdev_client *client,
 
        ret = bits_to_user(mem, maxbit, maxlen, p, compat);
        if (ret < 0)
-               evdev_queue_syn_dropped(client);
+               evdev_queue_syn_dropped(client, false);
 
        kfree(mem);
 
index 213e3a1..cc357f1 100644 (file)
@@ -100,23 +100,24 @@ static unsigned int input_to_handler(struct input_handle *handle,
        struct input_value *end = vals;
        struct input_value *v;
 
-       for (v = vals; v != vals + count; v++) {
-               if (handler->filter &&
-                   handler->filter(handle, v->type, v->code, v->value))
-                       continue;
-               if (end != v)
-                       *end = *v;
-               end++;
+       if (handler->filter) {
+               for (v = vals; v != vals + count; v++) {
+                       if (handler->filter(handle, v->type, v->code, v->value))
+                               continue;
+                       if (end != v)
+                               *end = *v;
+                       end++;
+               }
+               count = end - vals;
        }
 
-       count = end - vals;
        if (!count)
                return 0;
 
        if (handler->events)
                handler->events(handle, vals, count);
        else if (handler->event)
-               for (v = vals; v != end; v++)
+               for (v = vals; v != vals + count; v++)
                        handler->event(handle, v->type, v->code, v->value);
 
        return count;
@@ -143,8 +144,11 @@ static void input_pass_values(struct input_dev *dev,
                count = input_to_handler(handle, vals, count);
        } else {
                list_for_each_entry_rcu(handle, &dev->h_list, d_node)
-                       if (handle->open)
+                       if (handle->open) {
                                count = input_to_handler(handle, vals, count);
+                               if (!count)
+                                       break;
+                       }
        }
 
        rcu_read_unlock();
@@ -152,12 +156,14 @@ static void input_pass_values(struct input_dev *dev,
        add_input_randomness(vals->type, vals->code, vals->value);
 
        /* trigger auto repeat for key events */
-       for (v = vals; v != vals + count; v++) {
-               if (v->type == EV_KEY && v->value != 2) {
-                       if (v->value)
-                               input_start_autorepeat(dev, v->code);
-                       else
-                               input_stop_autorepeat(dev);
+       if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
+               for (v = vals; v != vals + count; v++) {
+                       if (v->type == EV_KEY && v->value != 2) {
+                               if (v->value)
+                                       input_start_autorepeat(dev, v->code);
+                               else
+                                       input_stop_autorepeat(dev);
+                       }
                }
        }
 }
index a5d9b3f..a89ba7c 100644 (file)
@@ -568,6 +568,16 @@ config KEYBOARD_STMPE
          To compile this driver as a module, choose M here: the module will be
          called stmpe-keypad.
 
+config KEYBOARD_SUN4I_LRADC
+       tristate "Allwinner sun4i low res adc attached tablet keys support"
+       depends on ARCH_SUNXI
+       help
+         This selects support for the Allwinner low res adc attached tablet
+         keys found on Allwinner sunxi SoCs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sun4i-lradc-keys.
+
 config KEYBOARD_DAVINCI
        tristate "TI DaVinci Key Scan"
        depends on ARCH_DAVINCI_DM365
index febafa5..4707678 100644 (file)
@@ -53,6 +53,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR)          += spear-keyboard.o
 obj-$(CONFIG_KEYBOARD_STMPE)           += stmpe-keypad.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)                += stowaway.o
 obj-$(CONFIG_KEYBOARD_ST_KEYSCAN)      += st-keyscan.o
+obj-$(CONFIG_KEYBOARD_SUN4I_LRADC)     += sun4i-lradc-keys.o
 obj-$(CONFIG_KEYBOARD_SUNKBD)          += sunkbd.o
 obj-$(CONFIG_KEYBOARD_TC3589X)         += tc3589x-keypad.o
 obj-$(CONFIG_KEYBOARD_TEGRA)           += tegra-kbc.o
index e53f232..2e855e6 100644 (file)
@@ -448,8 +448,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad),
-                             GFP_KERNEL);
+       keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
        if (!keypad) {
                dev_err(&pdev->dev, "not enough memory for driver data\n");
                return -ENOMEM;
diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c
new file mode 100644 (file)
index 0000000..cc8f7dd
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Allwinner sun4i low res adc attached tablet keys driver
+ *
+ * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.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.
+ *
+ * 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.
+ */
+
+/*
+ * Allwinnner sunxi SoCs have a lradc which is specifically designed to have
+ * various (tablet) keys (ie home, back, search, etc). attached to it using
+ * a resistor network. This driver is for the keys on such boards.
+ *
+ * There are 2 channels, currently this driver only supports channel 0 since
+ * there are no boards known to use channel 1.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define LRADC_CTRL             0x00
+#define LRADC_INTC             0x04
+#define LRADC_INTS             0x08
+#define LRADC_DATA0            0x0c
+#define LRADC_DATA1            0x10
+
+/* LRADC_CTRL bits */
+#define FIRST_CONVERT_DLY(x)   ((x) << 24) /* 8 bits */
+#define CHAN_SELECT(x)         ((x) << 22) /* 2 bits */
+#define CONTINUE_TIME_SEL(x)   ((x) << 16) /* 4 bits */
+#define KEY_MODE_SEL(x)                ((x) << 12) /* 2 bits */
+#define LEVELA_B_CNT(x)                ((x) << 8)  /* 4 bits */
+#define HOLD_EN(x)             ((x) << 6)
+#define LEVELB_VOL(x)          ((x) << 4)  /* 2 bits */
+#define SAMPLE_RATE(x)         ((x) << 2)  /* 2 bits */
+#define ENABLE(x)              ((x) << 0)
+
+/* LRADC_INTC and LRADC_INTS bits */
+#define CHAN1_KEYUP_IRQ                BIT(12)
+#define CHAN1_ALRDY_HOLD_IRQ   BIT(11)
+#define CHAN1_HOLD_IRQ         BIT(10)
+#define        CHAN1_KEYDOWN_IRQ       BIT(9)
+#define CHAN1_DATA_IRQ         BIT(8)
+#define CHAN0_KEYUP_IRQ                BIT(4)
+#define CHAN0_ALRDY_HOLD_IRQ   BIT(3)
+#define CHAN0_HOLD_IRQ         BIT(2)
+#define        CHAN0_KEYDOWN_IRQ       BIT(1)
+#define CHAN0_DATA_IRQ         BIT(0)
+
+struct sun4i_lradc_keymap {
+       u32 voltage;
+       u32 keycode;
+};
+
+struct sun4i_lradc_data {
+       struct device *dev;
+       struct input_dev *input;
+       void __iomem *base;
+       struct regulator *vref_supply;
+       struct sun4i_lradc_keymap *chan0_map;
+       u32 chan0_map_count;
+       u32 chan0_keycode;
+       u32 vref;
+};
+
+static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id)
+{
+       struct sun4i_lradc_data *lradc = dev_id;
+       u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff;
+
+       ints  = readl(lradc->base + LRADC_INTS);
+
+       /*
+        * lradc supports only one keypress at a time, release does not give
+        * any info as to which key was released, so we cache the keycode.
+        */
+
+       if (ints & CHAN0_KEYUP_IRQ) {
+               input_report_key(lradc->input, lradc->chan0_keycode, 0);
+               lradc->chan0_keycode = 0;
+       }
+
+       if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) {
+               val = readl(lradc->base + LRADC_DATA0) & 0x3f;
+               voltage = val * lradc->vref / 63;
+
+               for (i = 0; i < lradc->chan0_map_count; i++) {
+                       diff = abs(lradc->chan0_map[i].voltage - voltage);
+                       if (diff < closest) {
+                               closest = diff;
+                               keycode = lradc->chan0_map[i].keycode;
+                       }
+               }
+
+               lradc->chan0_keycode = keycode;
+               input_report_key(lradc->input, lradc->chan0_keycode, 1);
+       }
+
+       input_sync(lradc->input);
+
+       writel(ints, lradc->base + LRADC_INTS);
+
+       return IRQ_HANDLED;
+}
+
+static int sun4i_lradc_open(struct input_dev *dev)
+{
+       struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
+       int error;
+
+       error = regulator_enable(lradc->vref_supply);
+       if (error)
+               return error;
+
+       /* lradc Vref internally is divided by 2/3 */
+       lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3;
+
+       /*
+        * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to
+        * stabilize on press, wait (1 + 1) * 4 ms for key release
+        */
+       writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
+               SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL);
+
+       writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC);
+
+       return 0;
+}
+
+static void sun4i_lradc_close(struct input_dev *dev)
+{
+       struct sun4i_lradc_data *lradc = input_get_drvdata(dev);
+
+       /* Disable lradc, leave other settings unchanged */
+       writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) |
+               SAMPLE_RATE(2), lradc->base + LRADC_CTRL);
+       writel(0, lradc->base + LRADC_INTC);
+
+       regulator_disable(lradc->vref_supply);
+}
+
+static int sun4i_lradc_load_dt_keymap(struct device *dev,
+                                     struct sun4i_lradc_data *lradc)
+{
+       struct device_node *np, *pp;
+       int i;
+       int error;
+
+       np = dev->of_node;
+       if (!np)
+               return -EINVAL;
+
+       lradc->chan0_map_count = of_get_child_count(np);
+       if (lradc->chan0_map_count == 0) {
+               dev_err(dev, "keymap is missing in device tree\n");
+               return -EINVAL;
+       }
+
+       lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count,
+                                             sizeof(struct sun4i_lradc_keymap),
+                                             GFP_KERNEL);
+       if (!lradc->chan0_map)
+               return -ENOMEM;
+
+       i = 0;
+       for_each_child_of_node(np, pp) {
+               struct sun4i_lradc_keymap *map = &lradc->chan0_map[i];
+               u32 channel;
+
+               error = of_property_read_u32(pp, "channel", &channel);
+               if (error || channel != 0) {
+                       dev_err(dev, "%s: Inval channel prop\n", pp->name);
+                       return -EINVAL;
+               }
+
+               error = of_property_read_u32(pp, "voltage", &map->voltage);
+               if (error) {
+                       dev_err(dev, "%s: Inval voltage prop\n", pp->name);
+                       return -EINVAL;
+               }
+
+               error = of_property_read_u32(pp, "linux,code", &map->keycode);
+               if (error) {
+                       dev_err(dev, "%s: Inval linux,code prop\n", pp->name);
+                       return -EINVAL;
+               }
+
+               i++;
+       }
+
+       return 0;
+}
+
+static int sun4i_lradc_probe(struct platform_device *pdev)
+{
+       struct sun4i_lradc_data *lradc;
+       struct device *dev = &pdev->dev;
+       int i;
+       int error;
+
+       lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL);
+       if (!lradc)
+               return -ENOMEM;
+
+       error = sun4i_lradc_load_dt_keymap(dev, lradc);
+       if (error)
+               return error;
+
+       lradc->vref_supply = devm_regulator_get(dev, "vref");
+       if (IS_ERR(lradc->vref_supply))
+               return PTR_ERR(lradc->vref_supply);
+
+       lradc->dev = dev;
+       lradc->input = devm_input_allocate_device(dev);
+       if (!lradc->input)
+               return -ENOMEM;
+
+       lradc->input->name = pdev->name;
+       lradc->input->phys = "sun4i_lradc/input0";
+       lradc->input->open = sun4i_lradc_open;
+       lradc->input->close = sun4i_lradc_close;
+       lradc->input->id.bustype = BUS_HOST;
+       lradc->input->id.vendor = 0x0001;
+       lradc->input->id.product = 0x0001;
+       lradc->input->id.version = 0x0100;
+
+       __set_bit(EV_KEY, lradc->input->evbit);
+       for (i = 0; i < lradc->chan0_map_count; i++)
+               __set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit);
+
+       input_set_drvdata(lradc->input, lradc);
+
+       lradc->base = devm_ioremap_resource(dev,
+                             platform_get_resource(pdev, IORESOURCE_MEM, 0));
+       if (IS_ERR(lradc->base))
+               return PTR_ERR(lradc->base);
+
+       error = devm_request_irq(dev, platform_get_irq(pdev, 0),
+                                sun4i_lradc_irq, 0,
+                                "sun4i-a10-lradc-keys", lradc);
+       if (error)
+               return error;
+
+       error = input_register_device(lradc->input);
+       if (error)
+               return error;
+
+       platform_set_drvdata(pdev, lradc);
+       return 0;
+}
+
+static const struct of_device_id sun4i_lradc_of_match[] = {
+       { .compatible = "allwinner,sun4i-a10-lradc-keys", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
+
+static struct platform_driver sun4i_lradc_driver = {
+       .driver = {
+               .name   = "sun4i-a10-lradc-keys",
+               .of_match_table = of_match_ptr(sun4i_lradc_of_match),
+       },
+       .probe  = sun4i_lradc_probe,
+};
+
+module_platform_driver(sun4i_lradc_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
index 23297ab..6deb8da 100644 (file)
@@ -93,6 +93,16 @@ config INPUT_BMA150
          To compile this driver as a module, choose M here: the
          module will be called bma150.
 
+config INPUT_E3X0_BUTTON
+       tristate "NI Ettus Research USRP E3x0 Button support."
+       default n
+       help
+         Say Y here to enable support for the NI Ettus Research
+         USRP E3x0 Button.
+
+         To compile this driver as a module, choose M here: the
+         module will be called e3x0_button.
+
 config INPUT_PCSPKR
        tristate "PC Speaker support"
        depends on PCSPKR_PLATFORM
@@ -394,6 +404,18 @@ config INPUT_CM109
          To compile this driver as a module, choose M here: the module will be
          called cm109.
 
+config INPUT_REGULATOR_HAPTIC
+       tristate "Regulator haptics support"
+       depends on REGULATOR
+       select INPUT_FF_MEMLESS
+       help
+         This option enables device driver support for the haptic controlled
+         by a regulator. This driver supports ff-memless interface
+         from input framework.
+
+         To compile this driver as a module, choose M here: the
+         module will be called regulator-haptic.
+
 config INPUT_RETU_PWRBUTTON
        tristate "Retu Power button Driver"
        depends on MFD_RETU
@@ -404,6 +426,27 @@ config INPUT_RETU_PWRBUTTON
          To compile this driver as a module, choose M here. The module will
          be called retu-pwrbutton.
 
+config INPUT_TPS65218_PWRBUTTON
+       tristate "TPS65218 Power button driver"
+       depends on MFD_TPS65218
+       help
+         Say Y here if you want to enable power buttong reporting for
+         the TPS65218 Power Management IC device.
+
+         To compile this driver as a module, choose M here. The module will
+         be called tps65218-pwrbutton.
+
+config INPUT_AXP20X_PEK
+       tristate "X-Powers AXP20X power button driver"
+       depends on MFD_AXP20X
+       help
+         Say Y here if you want to enable power key reporting via the
+         AXP20X PMIC.
+
+         To compile this driver as a module, choose M here. The module will
+         be called axp20x-pek.
+
+
 config INPUT_TWL4030_PWRBUTTON
        tristate "TWL4030 Power button Driver"
        depends on TWL4030_CORE
index 19c7603..403a1a5 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_INPUT_COBALT_BTNS)               += cobalt_btns.o
 obj-$(CONFIG_INPUT_DA9052_ONKEY)       += da9052_onkey.o
 obj-$(CONFIG_INPUT_DA9055_ONKEY)       += da9055_onkey.o
 obj-$(CONFIG_INPUT_DM355EVM)           += dm355evm_keys.o
+obj-$(CONFIG_INPUT_E3X0_BUTTON)                += e3x0-button.o
 obj-$(CONFIG_INPUT_DRV260X_HAPTICS)    += drv260x.o
 obj-$(CONFIG_INPUT_DRV2667_HAPTICS)    += drv2667.o
 obj-$(CONFIG_INPUT_GP2A)               += gp2ap002a00f.o
@@ -53,12 +54,15 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
 obj-$(CONFIG_INPUT_POWERMATE)          += powermate.o
 obj-$(CONFIG_INPUT_PWM_BEEPER)         += pwm-beeper.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)       += rb532_button.o
+obj-$(CONFIG_INPUT_REGULATOR_HAPTIC)   += regulator-haptic.o
 obj-$(CONFIG_INPUT_RETU_PWRBUTTON)     += retu-pwrbutton.o
+obj-$(CONFIG_INPUT_AXP20X_PEK)         += axp20x-pek.o
 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)        += rotary_encoder.o
 obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
 obj-$(CONFIG_INPUT_SIRFSOC_ONKEY)      += sirfsoc-onkey.o
 obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY)   += soc_button_array.o
 obj-$(CONFIG_INPUT_SPARCSPKR)          += sparcspkr.o
+obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)  += twl4030-pwrbutton.o
 obj-$(CONFIG_INPUT_TWL4030_VIBRA)      += twl4030-vibra.o
 obj-$(CONFIG_INPUT_TWL6040_VIBRA)      += twl6040-vibra.o
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
new file mode 100644 (file)
index 0000000..f1c8447
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * axp20x power button driver.
+ *
+ * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AXP20X_PEK_STARTUP_MASK                (0xc0)
+#define AXP20X_PEK_SHUTDOWN_MASK       (0x03)
+
+struct axp20x_pek {
+       struct axp20x_dev *axp20x;
+       struct input_dev *input;
+       int irq_dbr;
+       int irq_dbf;
+};
+
+struct axp20x_time {
+       unsigned int time;
+       unsigned int idx;
+};
+
+static const struct axp20x_time startup_time[] = {
+       { .time = 128,  .idx = 0 },
+       { .time = 1000, .idx = 2 },
+       { .time = 3000, .idx = 1 },
+       { .time = 2000, .idx = 3 },
+};
+
+static const struct axp20x_time shutdown_time[] = {
+       { .time = 4000,  .idx = 0 },
+       { .time = 6000,  .idx = 1 },
+       { .time = 8000,  .idx = 2 },
+       { .time = 10000, .idx = 3 },
+};
+
+struct axp20x_pek_ext_attr {
+       const struct axp20x_time *p_time;
+       unsigned int mask;
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = {
+       .p_time = startup_time,
+       .mask   = AXP20X_PEK_STARTUP_MASK,
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = {
+       .p_time = shutdown_time,
+       .mask   = AXP20X_PEK_SHUTDOWN_MASK,
+};
+
+static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr)
+{
+       return container_of(attr, struct dev_ext_attribute, attr)->var;
+}
+
+static ssize_t axp20x_show_ext_attr(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+       struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+       unsigned int val;
+       int ret, i;
+
+       ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
+       if (ret != 0)
+               return ret;
+
+       val &= axp20x_ea->mask;
+       val >>= ffs(axp20x_ea->mask) - 1;
+
+       for (i = 0; i < 4; i++)
+               if (val == axp20x_ea->p_time[i].idx)
+                       val = axp20x_ea->p_time[i].time;
+
+       return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t axp20x_store_ext_attr(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+       struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+       char val_str[20];
+       size_t len;
+       int ret, i;
+       unsigned int val, idx = 0;
+       unsigned int best_err = UINT_MAX;
+
+       val_str[sizeof(val_str) - 1] = '\0';
+       strncpy(val_str, buf, sizeof(val_str) - 1);
+       len = strlen(val_str);
+
+       if (len && val_str[len - 1] == '\n')
+               val_str[len - 1] = '\0';
+
+       ret = kstrtouint(val_str, 10, &val);
+       if (ret)
+               return ret;
+
+       for (i = 3; i >= 0; i--) {
+               unsigned int err;
+
+               err = abs(axp20x_ea->p_time[i].time - val);
+               if (err < best_err) {
+                       best_err = err;
+                       idx = axp20x_ea->p_time[i].idx;
+               }
+
+               if (!err)
+                       break;
+       }
+
+       idx <<= ffs(axp20x_ea->mask) - 1;
+       ret = regmap_update_bits(axp20x_pek->axp20x->regmap,
+                                AXP20X_PEK_KEY,
+                                axp20x_ea->mask, idx);
+       if (ret != 0)
+               return -EINVAL;
+
+       return count;
+}
+
+static struct dev_ext_attribute axp20x_dev_attr_startup = {
+       .attr   = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+       .var    = &axp20x_pek_startup_ext_attr,
+};
+
+static struct dev_ext_attribute axp20x_dev_attr_shutdown = {
+       .attr   = __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+       .var    = &axp20x_pek_shutdown_ext_attr,
+};
+
+static struct attribute *axp20x_attributes[] = {
+       &axp20x_dev_attr_startup.attr.attr,
+       &axp20x_dev_attr_shutdown.attr.attr,
+       NULL,
+};
+
+static const struct attribute_group axp20x_attribute_group = {
+       .attrs = axp20x_attributes,
+};
+
+static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
+{
+       struct input_dev *idev = pwr;
+       struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
+
+       if (irq == axp20x_pek->irq_dbr)
+               input_report_key(idev, KEY_POWER, true);
+       else if (irq == axp20x_pek->irq_dbf)
+               input_report_key(idev, KEY_POWER, false);
+
+       input_sync(idev);
+
+       return IRQ_HANDLED;
+}
+
+static void axp20x_remove_sysfs_group(void *_data)
+{
+       struct device *dev = _data;
+
+       sysfs_remove_group(&dev->kobj, &axp20x_attribute_group);
+}
+
+static int axp20x_pek_probe(struct platform_device *pdev)
+{
+       struct axp20x_pek *axp20x_pek;
+       struct axp20x_dev *axp20x;
+       struct input_dev *idev;
+       int error;
+
+       axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
+                                 GFP_KERNEL);
+       if (!axp20x_pek)
+               return -ENOMEM;
+
+       axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
+       axp20x = axp20x_pek->axp20x;
+
+       axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
+       if (axp20x_pek->irq_dbr < 0) {
+               dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
+                               axp20x_pek->irq_dbr);
+               return axp20x_pek->irq_dbr;
+       }
+       axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
+                                                 axp20x_pek->irq_dbr);
+
+       axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
+       if (axp20x_pek->irq_dbf < 0) {
+               dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
+                               axp20x_pek->irq_dbf);
+               return axp20x_pek->irq_dbf;
+       }
+       axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
+                                                 axp20x_pek->irq_dbf);
+
+       axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
+       if (!axp20x_pek->input)
+               return -ENOMEM;
+
+       idev = axp20x_pek->input;
+
+       idev->name = "axp20x-pek";
+       idev->phys = "m1kbd/input2";
+       idev->dev.parent = &pdev->dev;
+
+       input_set_capability(idev, EV_KEY, KEY_POWER);
+
+       input_set_drvdata(idev, axp20x_pek);
+
+       error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
+                                            axp20x_pek_irq, 0,
+                                            "axp20x-pek-dbr", idev);
+       if (error < 0) {
+               dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
+                       axp20x_pek->irq_dbr, error);
+               return error;
+       }
+
+       error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
+                                         axp20x_pek_irq, 0,
+                                         "axp20x-pek-dbf", idev);
+       if (error < 0) {
+               dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
+                       axp20x_pek->irq_dbf, error);
+               return error;
+       }
+
+       error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
+       if (error) {
+               dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n",
+                       error);
+               return error;
+       }
+
+       error = devm_add_action(&pdev->dev,
+                               axp20x_remove_sysfs_group, &pdev->dev);
+       if (error) {
+               axp20x_remove_sysfs_group(&pdev->dev);
+               dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
+                       error);
+               return error;
+       }
+
+       error = input_register_device(idev);
+       if (error) {
+               dev_err(axp20x->dev, "Can't register input device: %d\n",
+                       error);
+               return error;
+       }
+
+       platform_set_drvdata(pdev, axp20x_pek);
+
+       return 0;
+}
+
+static struct platform_driver axp20x_pek_driver = {
+       .probe          = axp20x_pek_probe,
+       .driver         = {
+               .name           = "axp20x-pek",
+       },
+};
+module_platform_driver(axp20x_pek_driver);
+
+MODULE_DESCRIPTION("axp20x Power Button");
+MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/e3x0-button.c b/drivers/input/misc/e3x0-button.c
new file mode 100644 (file)
index 0000000..13bfca8
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014, National Instruments Corp. All rights reserved.
+ *
+ * Driver for NI Ettus Research USRP E3x0 Button Driver
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+static irqreturn_t e3x0_button_release_handler(int irq, void *data)
+{
+       struct input_dev *idev = data;
+
+       input_report_key(idev, KEY_POWER, 0);
+       input_sync(idev);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t e3x0_button_press_handler(int irq, void *data)
+{
+       struct input_dev *idev = data;
+
+       input_report_key(idev, KEY_POWER, 1);
+       pm_wakeup_event(idev->dev.parent, 0);
+       input_sync(idev);
+
+       return IRQ_HANDLED;
+}
+
+static int __maybe_unused e3x0_button_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(platform_get_irq_byname(pdev, "press"));
+
+       return 0;
+}
+
+static int __maybe_unused e3x0_button_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(platform_get_irq_byname(pdev, "press"));
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops,
+                        e3x0_button_suspend, e3x0_button_resume);
+
+static int e3x0_button_probe(struct platform_device *pdev)
+{
+       struct input_dev *input;
+       int irq_press, irq_release;
+       int error;
+
+       irq_press = platform_get_irq_byname(pdev, "press");
+       if (irq_press < 0) {
+               dev_err(&pdev->dev, "No IRQ for 'press', error=%d\n",
+                       irq_press);
+               return irq_press;
+       }
+
+       irq_release = platform_get_irq_byname(pdev, "release");
+       if (irq_release < 0) {
+               dev_err(&pdev->dev, "No IRQ for 'release', error=%d\n",
+                       irq_release);
+               return irq_release;
+       }
+
+       input = devm_input_allocate_device(&pdev->dev);
+       if (!input)
+               return -ENOMEM;
+
+       input->name = "NI Ettus Research USRP E3x0 Button Driver";
+       input->phys = "e3x0_button/input0";
+       input->dev.parent = &pdev->dev;
+
+       input_set_capability(input, EV_KEY, KEY_POWER);
+
+       error = devm_request_irq(&pdev->dev, irq_press,
+                                e3x0_button_press_handler, 0,
+                                "e3x0-button", input);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to request 'press' IRQ#%d: %d\n",
+                       irq_press, error);
+               return error;
+       }
+
+       error = devm_request_irq(&pdev->dev, irq_release,
+                                e3x0_button_release_handler, 0,
+                                "e3x0-button", input);
+       if (error) {
+               dev_err(&pdev->dev, "Failed to request 'release' IRQ#%d: %d\n",
+                       irq_release, error);
+               return error;
+       }
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(&pdev->dev, "Can't register input device: %d\n", error);
+               return error;
+       }
+
+       platform_set_drvdata(pdev, input);
+       device_init_wakeup(&pdev->dev, 1);
+       return 0;
+}
+
+static int e3x0_button_remove(struct platform_device *pdev)
+{
+       device_init_wakeup(&pdev->dev, 0);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id e3x0_button_match[] = {
+       { .compatible = "ettus,e3x0-button", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, e3x0_button_match);
+#endif
+
+static struct platform_driver e3x0_button_driver = {
+       .driver         = {
+               .name   = "e3x0-button",
+               .of_match_table = of_match_ptr(e3x0_button_match),
+               .pm     = &e3x0_button_pm_ops,
+       },
+       .probe          = e3x0_button_probe,
+       .remove         = e3x0_button_remove,
+};
+
+module_platform_driver(e3x0_button_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Moritz Fischer <moritz.fischer@ettus.com>");
+MODULE_DESCRIPTION("NI Ettus Research USRP E3x0 Button driver");
+MODULE_ALIAS("platform:e3x0-button");
diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c
new file mode 100644 (file)
index 0000000..132eb91
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Regulator haptic driver
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Jaewon Kim <jaewon02.kim@samsung.com>
+ * Author: Hyunhee Kim <hyunhee.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_data/regulator-haptic.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define MAX_MAGNITUDE_SHIFT    16
+
+struct regulator_haptic {
+       struct device *dev;
+       struct input_dev *input_dev;
+       struct regulator *regulator;
+
+       struct work_struct work;
+       struct mutex mutex;
+
+       bool active;
+       bool suspended;
+
+       unsigned int max_volt;
+       unsigned int min_volt;
+       unsigned int magnitude;
+};
+
+static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on)
+{
+       int error;
+
+       if (haptic->active != on) {
+
+               error = on ? regulator_enable(haptic->regulator) :
+                            regulator_disable(haptic->regulator);
+               if (error) {
+                       dev_err(haptic->dev,
+                               "failed to switch regulator %s: %d\n",
+                               on ? "on" : "off", error);
+                       return error;
+               }
+
+               haptic->active = on;
+       }
+
+       return 0;
+}
+
+static int regulator_haptic_set_voltage(struct regulator_haptic *haptic,
+                                        unsigned int magnitude)
+{
+       u64 volt_mag_multi;
+       unsigned int intensity;
+       int error;
+
+       volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude;
+       intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT);
+
+       error = regulator_set_voltage(haptic->regulator,
+                                     intensity + haptic->min_volt,
+                                     haptic->max_volt);
+       if (error) {
+               dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n",
+                       intensity + haptic->min_volt, error);
+               return error;
+       }
+
+       regulator_haptic_toggle(haptic, !!magnitude);
+
+       return 0;
+}
+
+static void regulator_haptic_work(struct work_struct *work)
+{
+       struct regulator_haptic *haptic = container_of(work,
+                                       struct regulator_haptic, work);
+
+       mutex_lock(&haptic->mutex);
+
+       if (!haptic->suspended)
+               regulator_haptic_set_voltage(haptic, haptic->magnitude);
+
+       mutex_unlock(&haptic->mutex);
+}
+
+static int regulator_haptic_play_effect(struct input_dev *input, void *data,
+                                       struct ff_effect *effect)
+{
+       struct regulator_haptic *haptic = input_get_drvdata(input);
+
+       haptic->magnitude = effect->u.rumble.strong_magnitude;
+       if (!haptic->magnitude)
+               haptic->magnitude = effect->u.rumble.weak_magnitude;
+
+       schedule_work(&haptic->work);
+
+       return 0;
+}
+
+static void regulator_haptic_close(struct input_dev *input)
+{
+       struct regulator_haptic *haptic = input_get_drvdata(input);
+
+       cancel_work_sync(&haptic->work);
+       regulator_haptic_set_voltage(haptic, 0);
+}
+
+static int __maybe_unused
+regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic)
+{
+       struct device_node *node;
+       int error;
+
+       node = dev->of_node;
+       if(!node) {
+               dev_err(dev, "Missing dveice tree data\n");
+               return -EINVAL;
+       }
+
+       error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt);
+       if (error) {
+               dev_err(dev, "cannot parse max-microvolt\n");
+               return error;
+       }
+
+       error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt);
+       if (error) {
+               dev_err(dev, "cannot parse min-microvolt\n");
+               return error;
+       }
+
+       return 0;
+}
+
+static int regulator_haptic_probe(struct platform_device *pdev)
+{
+       const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev);
+       struct regulator_haptic *haptic;
+       struct input_dev *input_dev;
+       int error;
+
+       haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
+       if (!haptic)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, haptic);
+       haptic->dev = &pdev->dev;
+       mutex_init(&haptic->mutex);
+       INIT_WORK(&haptic->work, regulator_haptic_work);
+
+       if (pdata) {
+               haptic->max_volt = pdata->max_volt;
+               haptic->min_volt = pdata->min_volt;
+       } else if (IS_ENABLED(CONFIG_OF)) {
+               error = regulator_haptic_parse_dt(&pdev->dev, haptic);
+               if (error)
+                       return error;
+       } else {
+               dev_err(&pdev->dev, "Missing platform data\n");
+               return -EINVAL;
+       }
+
+       haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic");
+       if (IS_ERR(haptic->regulator)) {
+               dev_err(&pdev->dev, "failed to get regulator\n");
+               return PTR_ERR(haptic->regulator);
+       }
+
+       input_dev = devm_input_allocate_device(&pdev->dev);
+       if (!input_dev)
+               return  -ENOMEM;
+
+       haptic->input_dev = input_dev;
+       haptic->input_dev->name = "regulator-haptic";
+       haptic->input_dev->dev.parent = &pdev->dev;
+       haptic->input_dev->close = regulator_haptic_close;
+       input_set_drvdata(haptic->input_dev, haptic);
+       input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
+
+       error = input_ff_create_memless(input_dev, NULL,
+                                       regulator_haptic_play_effect);
+       if (error) {
+               dev_err(&pdev->dev, "failed to create force-feedback\n");
+               return error;
+       }
+
+       error = input_register_device(haptic->input_dev);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register input device\n");
+               return error;
+       }
+
+       return 0;
+}
+
+static int __maybe_unused regulator_haptic_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct regulator_haptic *haptic = platform_get_drvdata(pdev);
+       int error;
+
+       error = mutex_lock_interruptible(&haptic->mutex);
+       if (error)
+               return error;
+
+       regulator_haptic_set_voltage(haptic, 0);
+
+       haptic->suspended = true;
+
+       mutex_unlock(&haptic->mutex);
+
+       return 0;
+}
+
+static int __maybe_unused regulator_haptic_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct regulator_haptic *haptic = platform_get_drvdata(pdev);
+       unsigned int magnitude;
+
+       mutex_lock(&haptic->mutex);
+
+       haptic->suspended = false;
+
+       magnitude = ACCESS_ONCE(haptic->magnitude);
+       if (magnitude)
+               regulator_haptic_set_voltage(haptic, magnitude);
+
+       mutex_unlock(&haptic->mutex);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
+               regulator_haptic_suspend, regulator_haptic_resume);
+
+static struct of_device_id regulator_haptic_dt_match[] = {
+       { .compatible = "regulator-haptic" },
+       { /* sentinel */ },
+};
+
+static struct platform_driver regulator_haptic_driver = {
+       .probe          = regulator_haptic_probe,
+       .driver         = {
+               .name           = "regulator-haptic",
+               .of_match_table = regulator_haptic_dt_match,
+               .pm             = &regulator_haptic_pm_ops,
+       },
+};
+module_platform_driver(regulator_haptic_driver);
+
+MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
+MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>");
+MODULE_DESCRIPTION("Regulator haptic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c
new file mode 100644 (file)
index 0000000..54508de
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Texas Instruments' TPS65218 Power Button Input Driver
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Felipe Balbi <balbi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/tps65218.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct tps65218_pwrbutton {
+       struct device *dev;
+       struct tps65218 *tps;
+       struct input_dev *idev;
+};
+
+static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr)
+{
+       struct tps65218_pwrbutton *pwr = _pwr;
+       unsigned int reg;
+       int error;
+
+       error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, &reg);
+       if (error) {
+               dev_err(pwr->dev, "can't read register: %d\n", error);
+               goto out;
+       }
+
+       if (reg & TPS65218_STATUS_PB_STATE) {
+               input_report_key(pwr->idev, KEY_POWER, 1);
+               pm_wakeup_event(pwr->dev, 0);
+       } else {
+               input_report_key(pwr->idev, KEY_POWER, 0);
+       }
+
+       input_sync(pwr->idev);
+
+out:
+       return IRQ_HANDLED;
+}
+
+static int tps65218_pwron_probe(struct platform_device *pdev)
+{
+       struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent);
+       struct device *dev = &pdev->dev;
+       struct tps65218_pwrbutton *pwr;
+       struct input_dev *idev;
+       int error;
+       int irq;
+
+       pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL);
+       if (!pwr)
+               return -ENOMEM;
+
+       idev = devm_input_allocate_device(dev);
+       if (!idev)
+               return -ENOMEM;
+
+       idev->name = "tps65218_pwrbutton";
+       idev->phys = "tps65218_pwrbutton/input0";
+       idev->dev.parent = dev;
+       idev->id.bustype = BUS_I2C;
+
+       input_set_capability(idev, EV_KEY, KEY_POWER);
+
+       pwr->tps = tps;
+       pwr->dev = dev;
+       pwr->idev = idev;
+       platform_set_drvdata(pdev, pwr);
+       device_init_wakeup(dev, true);
+
+       irq = platform_get_irq(pdev, 0);
+       error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq,
+                                         IRQF_TRIGGER_RISING |
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_ONESHOT,
+                                         "tps65218-pwrbutton", pwr);
+       if (error) {
+               dev_err(dev, "failed to request IRQ #%d: %d\n",
+                       irq, error);
+               return error;
+       }
+
+       error= input_register_device(idev);
+       if (error) {
+               dev_err(dev, "Can't register power button: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static struct of_device_id of_tps65218_pwr_match[] = {
+       { .compatible = "ti,tps65218-pwrbutton" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match);
+
+static struct platform_driver tps65218_pwron_driver = {
+       .probe  = tps65218_pwron_probe,
+       .driver = {
+               .name   = "tps65218_pwrbutton",
+               .of_match_table = of_tps65218_pwr_match,
+       },
+};
+module_platform_driver(tps65218_pwron_driver);
+
+MODULE_DESCRIPTION("TPS65218 Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
index d8b46b0..2541bfa 100644 (file)
@@ -146,6 +146,16 @@ config MOUSE_PS2_OLPC
 
          If unsure, say N.
 
+config MOUSE_PS2_FOCALTECH
+       bool "FocalTech PS/2 mouse protocol extension" if EXPERT
+       default y
+       depends on MOUSE_PS2
+       help
+         Say Y here if you have a FocalTech PS/2 TouchPad connected to
+         your system.
+
+         If unsure, say Y.
+
 config MOUSE_SERIAL
        tristate "Serial mouse"
        select SERIO
index d88d73d..f719f28 100644 (file)
@@ -475,6 +475,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
        struct input_dev *dev = priv->dev2;
        int x, y, z, left, right, middle;
 
+       /* It should be a DualPoint when received trackstick packet */
+       if (!(priv->flags & ALPS_DUALPOINT)) {
+               psmouse_warn(psmouse,
+                            "Rejected trackstick packet from non DualPoint device");
+               return;
+       }
+
        /* Sanity check packet */
        if (!(packet[0] & 0x40)) {
                psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n");
@@ -699,7 +706,8 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
 
        alps_report_semi_mt_data(psmouse, fingers);
 
-       if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
+       if ((priv->flags & ALPS_DUALPOINT) &&
+           !(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
                input_report_key(dev2, BTN_LEFT, f->ts_left);
                input_report_key(dev2, BTN_RIGHT, f->ts_right);
                input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
@@ -743,8 +751,11 @@ static void alps_process_packet_v6(struct psmouse *psmouse)
         */
        if (packet[5] == 0x7F) {
                /* It should be a DualPoint when received Trackpoint packet */
-               if (!(priv->flags & ALPS_DUALPOINT))
+               if (!(priv->flags & ALPS_DUALPOINT)) {
+                       psmouse_warn(psmouse,
+                                    "Rejected trackstick packet from non DualPoint device");
                        return;
+               }
 
                /* Trackpoint packet */
                x = packet[1] | ((packet[3] & 0x20) << 2);
@@ -1026,6 +1037,13 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
        struct input_dev *dev2 = priv->dev2;
        int x, y, z, left, right, middle;
 
+       /* It should be a DualPoint when received trackstick packet */
+       if (!(priv->flags & ALPS_DUALPOINT)) {
+               psmouse_warn(psmouse,
+                            "Rejected trackstick packet from non DualPoint device");
+               return;
+       }
+
        /*
         *        b7 b6 b5 b4 b3 b2 b1 b0
         * Byte0   0  1  0  0  1  0  0  0
@@ -2443,14 +2461,24 @@ int alps_init(struct psmouse *psmouse)
                dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
        }
 
+       if (priv->flags & ALPS_DUALPOINT) {
+               /*
+                * format of input device name is: "protocol vendor name"
+                * see function psmouse_switch_protocol() in psmouse-base.c
+                */
+               dev2->name = "AlpsPS/2 ALPS DualPoint Stick";
+               dev2->id.product = PSMOUSE_ALPS;
+               dev2->id.version = priv->proto_version;
+       } else {
+               dev2->name = "PS/2 ALPS Mouse";
+               dev2->id.product = PSMOUSE_PS2;
+               dev2->id.version = 0x0000;
+       }
+
        snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
        dev2->phys = priv->phys;
-       dev2->name = (priv->flags & ALPS_DUALPOINT) ?
-                    "DualPoint Stick" : "ALPS PS/2 Device";
        dev2->id.bustype = BUS_I8042;
        dev2->id.vendor  = 0x0002;
-       dev2->id.product = PSMOUSE_ALPS;
-       dev2->id.version = 0x0000;
        dev2->dev.parent = &psmouse->ps2dev.serio->dev;
 
        dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
index f4d657e..fca38ba 100644 (file)
@@ -2,6 +2,7 @@
  * Focaltech TouchPad PS/2 mouse driver
  *
  * Copyright (c) 2014 Red Hat Inc.
+ * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.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
  * Hans de Goede <hdegoede@redhat.com>
  */
 
-/*
- * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
- * detection only, to avoid further detection attempts confusing the touchpad
- * this way it at least works in PS/2 mouse compatibility mode.
- */
 
 #include <linux/device.h>
 #include <linux/libps2.h>
+#include <linux/input/mt.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
 #include "psmouse.h"
+#include "focaltech.h"
 
 static const char * const focaltech_pnp_ids[] = {
        "FLT0101",
@@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = {
        NULL
 };
 
+/*
+ * Even if the kernel is built without support for Focaltech PS/2 touchpads (or
+ * when the real driver fails to recognize the device), we still have to detect
+ * them in order to avoid further detection attempts confusing the touchpad.
+ * This way it at least works in PS/2 mouse compatibility mode.
+ */
 int focaltech_detect(struct psmouse *psmouse, bool set_properties)
 {
        if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
@@ -37,16 +43,404 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
 
        if (set_properties) {
                psmouse->vendor = "FocalTech";
-               psmouse->name = "FocalTech Touchpad in mouse emulation mode";
+               psmouse->name = "FocalTech Touchpad";
        }
 
        return 0;
 }
 
-int focaltech_init(struct psmouse *psmouse)
+static void focaltech_reset(struct psmouse *psmouse)
 {
        ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
        psmouse_reset(psmouse);
+}
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
+
+/*
+ * Packet types - the numbers are not consecutive, so we might be missing
+ * something here.
+ */
+#define FOC_TOUCH 0x3 /* bitmap of active fingers */
+#define FOC_ABS 0x6 /* absolute position of one finger */
+#define FOC_REL 0x9 /* relative position of 1-2 fingers */
+
+#define FOC_MAX_FINGERS 5
+
+#define FOC_MAX_X 2431
+#define FOC_MAX_Y 1663
+
+/*
+ * Current state of a single finger on the touchpad.
+ */
+struct focaltech_finger_state {
+       /* The touchpad has generated a touch event for the finger */
+       bool active;
+
+       /*
+        * The touchpad has sent position data for the finger. The
+        * flag is 0 when the finger is not active, and there is a
+        * time between the first touch event for the finger and the
+        * following absolute position packet for the finger where the
+        * touchpad has declared the finger to be valid, but we do not
+        * have any valid position yet.
+        */
+       bool valid;
+
+       /*
+        * Absolute position (from the bottom left corner) of the
+        * finger.
+        */
+       unsigned int x;
+       unsigned int y;
+};
+
+/*
+ * Description of the current state of the touchpad hardware.
+ */
+struct focaltech_hw_state {
+       /*
+        * The touchpad tracks the positions of the fingers for us,
+        * the array indices correspond to the finger indices returned
+        * in the report packages.
+        */
+       struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
+
+       /* True if the clickpad has been pressed. */
+       bool pressed;
+};
+
+struct focaltech_data {
+       unsigned int x_max, y_max;
+       struct focaltech_hw_state state;
+};
+
+static void focaltech_report_state(struct psmouse *psmouse)
+{
+       struct focaltech_data *priv = psmouse->private;
+       struct focaltech_hw_state *state = &priv->state;
+       struct input_dev *dev = psmouse->dev;
+       int i;
+
+       for (i = 0; i < FOC_MAX_FINGERS; i++) {
+               struct focaltech_finger_state *finger = &state->fingers[i];
+               bool active = finger->active && finger->valid;
+
+               input_mt_slot(dev, i);
+               input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
+               if (active) {
+                       input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
+                       input_report_abs(dev, ABS_MT_POSITION_Y,
+                                        FOC_MAX_Y - finger->y);
+               }
+       }
+       input_mt_report_pointer_emulation(dev, true);
+
+       input_report_key(psmouse->dev, BTN_LEFT, state->pressed);
+       input_sync(psmouse->dev);
+}
+
+static void focaltech_process_touch_packet(struct psmouse *psmouse,
+                                          unsigned char *packet)
+{
+       struct focaltech_data *priv = psmouse->private;
+       struct focaltech_hw_state *state = &priv->state;
+       unsigned char fingers = packet[1];
+       int i;
+
+       state->pressed = (packet[0] >> 4) & 1;
+
+       /* the second byte contains a bitmap of all fingers touching the pad */
+       for (i = 0; i < FOC_MAX_FINGERS; i++) {
+               state->fingers[i].active = fingers & 0x1;
+               if (!state->fingers[i].active) {
+                       /*
+                        * Even when the finger becomes active again, we still
+                        * will have to wait for the first valid position.
+                        */
+                       state->fingers[i].valid = false;
+               }
+               fingers >>= 1;
+       }
+}
+
+static void focaltech_process_abs_packet(struct psmouse *psmouse,
+                                        unsigned char *packet)
+{
+       struct focaltech_data *priv = psmouse->private;
+       struct focaltech_hw_state *state = &priv->state;
+       unsigned int finger;
+
+       finger = (packet[1] >> 4) - 1;
+       if (finger >= FOC_MAX_FINGERS) {
+               psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
+                           finger);
+               return;
+       }
+
+       state->pressed = (packet[0] >> 4) & 1;
+
+       /*
+        * packet[5] contains some kind of tool size in the most
+        * significant nibble. 0xff is a special value (latching) that
+        * signals a large contact area.
+        */
+       if (packet[5] == 0xff) {
+               state->fingers[finger].valid = false;
+               return;
+       }
+
+       state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
+       state->fingers[finger].y = (packet[3] << 8) | packet[4];
+       state->fingers[finger].valid = true;
+}
+
+static void focaltech_process_rel_packet(struct psmouse *psmouse,
+                                        unsigned char *packet)
+{
+       struct focaltech_data *priv = psmouse->private;
+       struct focaltech_hw_state *state = &priv->state;
+       int finger1, finger2;
+
+       state->pressed = packet[0] >> 7;
+       finger1 = ((packet[0] >> 4) & 0x7) - 1;
+       if (finger1 < FOC_MAX_FINGERS) {
+               state->fingers[finger1].x += (char)packet[1];
+               state->fingers[finger1].y += (char)packet[2];
+       } else {
+               psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
+                           finger1);
+       }
+
+       /*
+        * If there is an odd number of fingers, the last relative
+        * packet only contains one finger. In this case, the second
+        * finger index in the packet is 0 (we subtract 1 in the lines
+        * above to create array indices, so the finger will overflow
+        * and be above FOC_MAX_FINGERS).
+        */
+       finger2 = ((packet[3] >> 4) & 0x7) - 1;
+       if (finger2 < FOC_MAX_FINGERS) {
+               state->fingers[finger2].x += (char)packet[4];
+               state->fingers[finger2].y += (char)packet[5];
+       }
+}
+
+static void focaltech_process_packet(struct psmouse *psmouse)
+{
+       unsigned char *packet = psmouse->packet;
+
+       switch (packet[0] & 0xf) {
+       case FOC_TOUCH:
+               focaltech_process_touch_packet(psmouse, packet);
+               break;
+
+       case FOC_ABS:
+               focaltech_process_abs_packet(psmouse, packet);
+               break;
+
+       case FOC_REL:
+               focaltech_process_rel_packet(psmouse, packet);
+               break;
+
+       default:
+               psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
+               break;
+       }
+
+       focaltech_report_state(psmouse);
+}
+
+static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
+{
+       if (psmouse->pktcnt >= 6) { /* Full packet received */
+               focaltech_process_packet(psmouse);
+               return PSMOUSE_FULL_PACKET;
+       }
+
+       /*
+        * We might want to do some validation of the data here, but
+        * we do not know the protocol well enough
+        */
+       return PSMOUSE_GOOD_DATA;
+}
+
+static int focaltech_switch_protocol(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       unsigned char param[3];
+
+       param[0] = 0;
+       if (ps2_command(ps2dev, param, 0x10f8))
+               return -EIO;
+
+       if (ps2_command(ps2dev, param, 0x10f8))
+               return -EIO;
+
+       if (ps2_command(ps2dev, param, 0x10f8))
+               return -EIO;
+
+       param[0] = 1;
+       if (ps2_command(ps2dev, param, 0x10f8))
+               return -EIO;
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
+               return -EIO;
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
+               return -EIO;
+
+       return 0;
+}
+
+static void focaltech_disconnect(struct psmouse *psmouse)
+{
+       focaltech_reset(psmouse);
+       kfree(psmouse->private);
+       psmouse->private = NULL;
+}
+
+static int focaltech_reconnect(struct psmouse *psmouse)
+{
+       int error;
+
+       focaltech_reset(psmouse);
+
+       error = focaltech_switch_protocol(psmouse);
+       if (error) {
+               psmouse_err(psmouse, "Unable to initialize the device\n");
+               return error;
+       }
+
+       return 0;
+}
+
+static void focaltech_set_input_params(struct psmouse *psmouse)
+{
+       struct input_dev *dev = psmouse->dev;
+       struct focaltech_data *priv = psmouse->private;
+
+       /*
+        * Undo part of setup done for us by psmouse core since touchpad
+        * is not a relative device.
+        */
+       __clear_bit(EV_REL, dev->evbit);
+       __clear_bit(REL_X, dev->relbit);
+       __clear_bit(REL_Y, dev->relbit);
+       __clear_bit(BTN_RIGHT, dev->keybit);
+       __clear_bit(BTN_MIDDLE, dev->keybit);
+
+       /*
+        * Now set up our capabilities.
+        */
+       __set_bit(EV_ABS, dev->evbit);
+       input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
+       input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
+       input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
+       __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
+}
+
+static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
+                                  unsigned char *param)
+{
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
+               return -EIO;
+
+       param[0] = 0;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -EIO;
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -EIO;
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -EIO;
+
+       param[0] = reg;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
+               return -EIO;
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+               return -EIO;
+
+       return 0;
+}
+
+static int focaltech_read_size(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       struct focaltech_data *priv = psmouse->private;
+       char param[3];
+
+       if (focaltech_read_register(ps2dev, 2, param))
+               return -EIO;
+
+       /* not sure whether this is 100% correct */
+       priv->x_max = (unsigned char)param[1] * 128;
+       priv->y_max = (unsigned char)param[2] * 128;
+
+       return 0;
+}
+int focaltech_init(struct psmouse *psmouse)
+{
+       struct focaltech_data *priv;
+       int error;
+
+       psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
+                                         GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       focaltech_reset(psmouse);
+
+       error = focaltech_read_size(psmouse);
+       if (error) {
+               psmouse_err(psmouse,
+                           "Unable to read the size of the touchpad\n");
+               goto fail;
+       }
+
+       error = focaltech_switch_protocol(psmouse);
+       if (error) {
+               psmouse_err(psmouse, "Unable to initialize the device\n");
+               goto fail;
+       }
+
+       focaltech_set_input_params(psmouse);
+
+       psmouse->protocol_handler = focaltech_process_byte;
+       psmouse->pktsize = 6;
+       psmouse->disconnect = focaltech_disconnect;
+       psmouse->reconnect = focaltech_reconnect;
+       psmouse->cleanup = focaltech_reset;
+       /* resync is not supported yet */
+       psmouse->resync_time = 0;
 
        return 0;
+
+fail:
+       focaltech_reset(psmouse);
+       kfree(priv);
+       return error;
 }
+
+bool focaltech_supported(void)
+{
+       return true;
+}
+
+#else /* CONFIG_MOUSE_PS2_FOCALTECH */
+
+int focaltech_init(struct psmouse *psmouse)
+{
+       focaltech_reset(psmouse);
+
+       return 0;
+}
+
+bool focaltech_supported(void)
+{
+       return false;
+}
+
+#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
index 498650c..71870a9 100644 (file)
@@ -2,6 +2,7 @@
  * Focaltech TouchPad PS/2 mouse driver
  *
  * Copyright (c) 2014 Red Hat Inc.
+ * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.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
@@ -18,5 +19,6 @@
 
 int focaltech_detect(struct psmouse *psmouse, bool set_properties);
 int focaltech_init(struct psmouse *psmouse);
+bool focaltech_supported(void);
 
 #endif
index 95a3a6e..68469fe 100644 (file)
@@ -725,16 +725,19 @@ static int psmouse_extensions(struct psmouse *psmouse,
 
 /* Always check for focaltech, this is safe as it uses pnp-id matching */
        if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
-               if (!set_properties || focaltech_init(psmouse) == 0) {
-                       /*
-                        * Not supported yet, use bare protocol.
-                        * Note that we need to also restrict
-                        * psmouse_max_proto so that psmouse_initialize()
-                        * does not try to reset rate and resolution,
-                        * because even that upsets the device.
-                        */
-                       psmouse_max_proto = PSMOUSE_PS2;
-                       return PSMOUSE_PS2;
+               if (max_proto > PSMOUSE_IMEX) {
+                       if (!set_properties || focaltech_init(psmouse) == 0) {
+                               if (focaltech_supported())
+                                       return PSMOUSE_FOCALTECH;
+                               /*
+                                * Note that we need to also restrict
+                                * psmouse_max_proto so that psmouse_initialize()
+                                * does not try to reset rate and resolution,
+                                * because even that upsets the device.
+                                */
+                               psmouse_max_proto = PSMOUSE_PS2;
+                               return PSMOUSE_PS2;
+                       }
                }
        }
 
@@ -1063,6 +1066,15 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .alias          = "cortps",
                .detect         = cortron_detect,
        },
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
+       {
+               .type           = PSMOUSE_FOCALTECH,
+               .name           = "FocalTechPS/2",
+               .alias          = "focaltech",
+               .detect         = focaltech_detect,
+               .init           = focaltech_init,
+       },
+#endif
        {
                .type           = PSMOUSE_AUTO,
                .name           = "auto",
index f4cf664..c2ff137 100644 (file)
@@ -96,6 +96,7 @@ enum psmouse_type {
        PSMOUSE_FSP,
        PSMOUSE_SYNAPTICS_RELATIVE,
        PSMOUSE_CYPRESS,
+       PSMOUSE_FOCALTECH,
        PSMOUSE_AUTO            /* This one should always be last */
 };
 
index f947292..f89de89 100644 (file)
@@ -574,14 +574,6 @@ static void synaptics_pt_create(struct psmouse *psmouse)
  *     Functions to interpret the absolute mode packets
  ****************************************************************************/
 
-static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count,
-                                  int sgm, int agm)
-{
-       state->count = count;
-       state->sgm = sgm;
-       state->agm = agm;
-}
-
 static void synaptics_parse_agm(const unsigned char buf[],
                                struct synaptics_data *priv,
                                struct synaptics_hw_state *hw)
@@ -600,16 +592,13 @@ static void synaptics_parse_agm(const unsigned char buf[],
                break;
 
        case 2:
-               /* AGM-CONTACT packet: (count, sgm, agm) */
-               synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]);
+               /* AGM-CONTACT packet: we are only interested in the count */
+               priv->agm_count = buf[1];
                break;
 
        default:
                break;
        }
-
-       /* Record that at least one AGM has been received since last SGM */
-       priv->agm_pending = true;
 }
 
 static bool is_forcepad;
@@ -803,424 +792,68 @@ static void synaptics_report_buttons(struct psmouse *psmouse,
                input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
 }
 
-static void synaptics_report_slot(struct input_dev *dev, int slot,
-                                 const struct synaptics_hw_state *hw)
-{
-       input_mt_slot(dev, slot);
-       input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL));
-       if (!hw)
-               return;
-
-       input_report_abs(dev, ABS_MT_POSITION_X, hw->x);
-       input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y));
-       input_report_abs(dev, ABS_MT_PRESSURE, hw->z);
-}
-
 static void synaptics_report_mt_data(struct psmouse *psmouse,
-                                    struct synaptics_mt_state *mt_state,
-                                    const struct synaptics_hw_state *sgm)
+                                    const struct synaptics_hw_state *sgm,
+                                    int num_fingers)
 {
        struct input_dev *dev = psmouse->dev;
        struct synaptics_data *priv = psmouse->private;
-       struct synaptics_hw_state *agm = &priv->agm;
-       struct synaptics_mt_state *old = &priv->mt_state;
+       const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
+       struct input_mt_pos pos[2];
+       int slot[2], nsemi, i;
 
-       switch (mt_state->count) {
-       case 0:
-               synaptics_report_slot(dev, 0, NULL);
-               synaptics_report_slot(dev, 1, NULL);
-               break;
-       case 1:
-               if (mt_state->sgm == -1) {
-                       synaptics_report_slot(dev, 0, NULL);
-                       synaptics_report_slot(dev, 1, NULL);
-               } else if (mt_state->sgm == 0) {
-                       synaptics_report_slot(dev, 0, sgm);
-                       synaptics_report_slot(dev, 1, NULL);
-               } else {
-                       synaptics_report_slot(dev, 0, NULL);
-                       synaptics_report_slot(dev, 1, sgm);
-               }
-               break;
-       default:
-               /*
-                * If the finger slot contained in SGM is valid, and either
-                * hasn't changed, or is new, or the old SGM has now moved to
-                * AGM, then report SGM in MTB slot 0.
-                * Otherwise, empty MTB slot 0.
-                */
-               if (mt_state->sgm != -1 &&
-                   (mt_state->sgm == old->sgm ||
-                    old->sgm == -1 || mt_state->agm == old->sgm))
-                       synaptics_report_slot(dev, 0, sgm);
-               else
-                       synaptics_report_slot(dev, 0, NULL);
+       nsemi = clamp_val(num_fingers, 0, 2);
 
-               /*
-                * If the finger slot contained in AGM is valid, and either
-                * hasn't changed, or is new, then report AGM in MTB slot 1.
-                * Otherwise, empty MTB slot 1.
-                *
-                * However, in the case where the AGM is new, make sure that
-                * that it is either the same as the old SGM, or there was no
-                * SGM.
-                *
-                * Otherwise, if the SGM was just 1, and the new AGM is 2, then
-                * the new AGM will keep the old SGM's tracking ID, which can
-                * cause apparent drumroll.  This happens if in the following
-                * valid finger sequence:
-                *
-                *  Action                 SGM  AGM (MTB slot:Contact)
-                *  1. Touch contact 0    (0:0)
-                *  2. Touch contact 1    (0:0, 1:1)
-                *  3. Lift  contact 0    (1:1)
-                *  4. Touch contacts 2,3 (0:2, 1:3)
-                *
-                * In step 4, contact 3, in AGM must not be given the same
-                * tracking ID as contact 1 had in step 3.  To avoid this,
-                * the first agm with contact 3 is dropped and slot 1 is
-                * invalidated (tracking ID = -1).
-                */
-               if (mt_state->agm != -1 &&
-                   (mt_state->agm == old->agm ||
-                    (old->agm == -1 &&
-                     (old->sgm == -1 || mt_state->agm == old->sgm))))
-                       synaptics_report_slot(dev, 1, agm);
-               else
-                       synaptics_report_slot(dev, 1, NULL);
-               break;
+       for (i = 0; i < nsemi; i++) {
+               pos[i].x = hw[i]->x;
+               pos[i].y = synaptics_invert_y(hw[i]->y);
        }
 
+       input_mt_assign_slots(dev, slot, pos, nsemi);
+
+       for (i = 0; i < nsemi; i++) {
+               input_mt_slot(dev, slot[i]);
+               input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+               input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x);
+               input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y);
+               input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z);
+       }
+
+       input_mt_drop_unused(dev);
+
        /* Don't use active slot count to generate BTN_TOOL events. */
        input_mt_report_pointer_emulation(dev, false);
 
        /* Send the number of fingers reported by touchpad itself. */
-       input_mt_report_finger_count(dev, mt_state->count);
+       input_mt_report_finger_count(dev, num_fingers);
 
        synaptics_report_buttons(psmouse, sgm);
 
        input_sync(dev);
 }
 
-/* Handle case where mt_state->count = 0 */
-static void synaptics_image_sensor_0f(struct synaptics_data *priv,
-                                     struct synaptics_mt_state *mt_state)
-{
-       synaptics_mt_state_set(mt_state, 0, -1, -1);
-       priv->mt_state_lost = false;
-}
-
-/* Handle case where mt_state->count = 1 */
-static void synaptics_image_sensor_1f(struct synaptics_data *priv,
-                                     struct synaptics_mt_state *mt_state)
-{
-       struct synaptics_hw_state *agm = &priv->agm;
-       struct synaptics_mt_state *old = &priv->mt_state;
-
-       /*
-        * If the last AGM was (0,0,0), and there is only one finger left,
-        * then we absolutely know that SGM contains slot 0, and all other
-        * fingers have been removed.
-        */
-       if (priv->agm_pending && agm->z == 0) {
-               synaptics_mt_state_set(mt_state, 1, 0, -1);
-               priv->mt_state_lost = false;
-               return;
-       }
-
-       switch (old->count) {
-       case 0:
-               synaptics_mt_state_set(mt_state, 1, 0, -1);
-               break;
-       case 1:
-               /*
-                * If mt_state_lost, then the previous transition was 3->1,
-                * and SGM now contains either slot 0 or 1, but we don't know
-                * which.  So, we just assume that the SGM now contains slot 1.
-                *
-                * If pending AGM and either:
-                *   (a) the previous SGM slot contains slot 0, or
-                *   (b) there was no SGM slot
-                * then, the SGM now contains slot 1
-                *
-                * Case (a) happens with very rapid "drum roll" gestures, where
-                * slot 0 finger is lifted and a new slot 1 finger touches
-                * within one reporting interval.
-                *
-                * Case (b) happens if initially two or more fingers tap
-                * briefly, and all but one lift before the end of the first
-                * reporting interval.
-                *
-                * (In both these cases, slot 0 will becomes empty, so SGM
-                * contains slot 1 with the new finger)
-                *
-                * Else, if there was no previous SGM, it now contains slot 0.
-                *
-                * Otherwise, SGM still contains the same slot.
-                */
-               if (priv->mt_state_lost ||
-                   (priv->agm_pending && old->sgm <= 0))
-                       synaptics_mt_state_set(mt_state, 1, 1, -1);
-               else if (old->sgm == -1)
-                       synaptics_mt_state_set(mt_state, 1, 0, -1);
-               break;
-       case 2:
-               /*
-                * If mt_state_lost, we don't know which finger SGM contains.
-                *
-                * So, report 1 finger, but with both slots empty.
-                * We will use slot 1 on subsequent 1->1
-                */
-               if (priv->mt_state_lost) {
-                       synaptics_mt_state_set(mt_state, 1, -1, -1);
-                       break;
-               }
-               /*
-                * Since the last AGM was NOT (0,0,0), it was the finger in
-                * slot 0 that has been removed.
-                * So, SGM now contains previous AGM's slot, and AGM is now
-                * empty.
-                */
-               synaptics_mt_state_set(mt_state, 1, old->agm, -1);
-               break;
-       case 3:
-               /*
-                * Since last AGM was not (0,0,0), we don't know which finger
-                * is left.
-                *
-                * So, report 1 finger, but with both slots empty.
-                * We will use slot 1 on subsequent 1->1
-                */
-               synaptics_mt_state_set(mt_state, 1, -1, -1);
-               priv->mt_state_lost = true;
-               break;
-       case 4:
-       case 5:
-               /* mt_state was updated by AGM-CONTACT packet */
-               break;
-       }
-}
-
-/* Handle case where mt_state->count = 2 */
-static void synaptics_image_sensor_2f(struct synaptics_data *priv,
-                                     struct synaptics_mt_state *mt_state)
-{
-       struct synaptics_mt_state *old = &priv->mt_state;
-
-       switch (old->count) {
-       case 0:
-               synaptics_mt_state_set(mt_state, 2, 0, 1);
-               break;
-       case 1:
-               /*
-                * If previous SGM contained slot 1 or higher, SGM now contains
-                * slot 0 (the newly touching finger) and AGM contains SGM's
-                * previous slot.
-                *
-                * Otherwise, SGM still contains slot 0 and AGM now contains
-                * slot 1.
-                */
-               if (old->sgm >= 1)
-                       synaptics_mt_state_set(mt_state, 2, 0, old->sgm);
-               else
-                       synaptics_mt_state_set(mt_state, 2, 0, 1);
-               break;
-       case 2:
-               /*
-                * If mt_state_lost, SGM now contains either finger 1 or 2, but
-                * we don't know which.
-                * So, we just assume that the SGM contains slot 0 and AGM 1.
-                */
-               if (priv->mt_state_lost)
-                       synaptics_mt_state_set(mt_state, 2, 0, 1);
-               /*
-                * Otherwise, use the same mt_state, since it either hasn't
-                * changed, or was updated by a recently received AGM-CONTACT
-                * packet.
-                */
-               break;
-       case 3:
-               /*
-                * 3->2 transitions have two unsolvable problems:
-                *  1) no indication is given which finger was removed
-                *  2) no way to tell if agm packet was for finger 3
-                *     before 3->2, or finger 2 after 3->2.
-                *
-                * So, report 2 fingers, but empty all slots.
-                * We will guess slots [0,1] on subsequent 2->2.
-                */
-               synaptics_mt_state_set(mt_state, 2, -1, -1);
-               priv->mt_state_lost = true;
-               break;
-       case 4:
-       case 5:
-               /* mt_state was updated by AGM-CONTACT packet */
-               break;
-       }
-}
-
-/* Handle case where mt_state->count = 3 */
-static void synaptics_image_sensor_3f(struct synaptics_data *priv,
-                                     struct synaptics_mt_state *mt_state)
-{
-       struct synaptics_mt_state *old = &priv->mt_state;
-
-       switch (old->count) {
-       case 0:
-               synaptics_mt_state_set(mt_state, 3, 0, 2);
-               break;
-       case 1:
-               /*
-                * If previous SGM contained slot 2 or higher, SGM now contains
-                * slot 0 (one of the newly touching fingers) and AGM contains
-                * SGM's previous slot.
-                *
-                * Otherwise, SGM now contains slot 0 and AGM contains slot 2.
-                */
-               if (old->sgm >= 2)
-                       synaptics_mt_state_set(mt_state, 3, 0, old->sgm);
-               else
-                       synaptics_mt_state_set(mt_state, 3, 0, 2);
-               break;
-       case 2:
-               /*
-                * If the AGM previously contained slot 3 or higher, then the
-                * newly touching finger is in the lowest available slot.
-                *
-                * If SGM was previously 1 or higher, then the new SGM is
-                * now slot 0 (with a new finger), otherwise, the new finger
-                * is now in a hidden slot between 0 and AGM's slot.
-                *
-                * In all such cases, the SGM now contains slot 0, and the AGM
-                * continues to contain the same slot as before.
-                */
-               if (old->agm >= 3) {
-                       synaptics_mt_state_set(mt_state, 3, 0, old->agm);
-                       break;
-               }
-
-               /*
-                * After some 3->1 and all 3->2 transitions, we lose track
-                * of which slot is reported by SGM and AGM.
-                *
-                * For 2->3 in this state, report 3 fingers, but empty all
-                * slots, and we will guess (0,2) on a subsequent 0->3.
-                *
-                * To userspace, the resulting transition will look like:
-                *    2:[0,1] -> 3:[-1,-1] -> 3:[0,2]
-                */
-               if (priv->mt_state_lost) {
-                       synaptics_mt_state_set(mt_state, 3, -1, -1);
-                       break;
-               }
-
-               /*
-                * If the (SGM,AGM) really previously contained slots (0, 1),
-                * then we cannot know what slot was just reported by the AGM,
-                * because the 2->3 transition can occur either before or after
-                * the AGM packet. Thus, this most recent AGM could contain
-                * either the same old slot 1 or the new slot 2.
-                * Subsequent AGMs will be reporting slot 2.
-                *
-                * To userspace, the resulting transition will look like:
-                *    2:[0,1] -> 3:[0,-1] -> 3:[0,2]
-                */
-               synaptics_mt_state_set(mt_state, 3, 0, -1);
-               break;
-       case 3:
-               /*
-                * If, for whatever reason, the previous agm was invalid,
-                * Assume SGM now contains slot 0, AGM now contains slot 2.
-                */
-               if (old->agm <= 2)
-                       synaptics_mt_state_set(mt_state, 3, 0, 2);
-               /*
-                * mt_state either hasn't changed, or was updated by a recently
-                * received AGM-CONTACT packet.
-                */
-               break;
-
-       case 4:
-       case 5:
-               /* mt_state was updated by AGM-CONTACT packet */
-               break;
-       }
-}
-
-/* Handle case where mt_state->count = 4, or = 5 */
-static void synaptics_image_sensor_45f(struct synaptics_data *priv,
-                                      struct synaptics_mt_state *mt_state)
-{
-       /* mt_state was updated correctly by AGM-CONTACT packet */
-       priv->mt_state_lost = false;
-}
-
 static void synaptics_image_sensor_process(struct psmouse *psmouse,
                                           struct synaptics_hw_state *sgm)
 {
        struct synaptics_data *priv = psmouse->private;
-       struct synaptics_hw_state *agm = &priv->agm;
-       struct synaptics_mt_state mt_state;
-
-       /* Initialize using current mt_state (as updated by last agm) */
-       mt_state = agm->mt_state;
+       int num_fingers;
 
        /*
         * Update mt_state using the new finger count and current mt_state.
         */
        if (sgm->z == 0)
-               synaptics_image_sensor_0f(priv, &mt_state);
+               num_fingers = 0;
        else if (sgm->w >= 4)
-               synaptics_image_sensor_1f(priv, &mt_state);
+               num_fingers = 1;
        else if (sgm->w == 0)
-               synaptics_image_sensor_2f(priv, &mt_state);
-       else if (sgm->w == 1 && mt_state.count <= 3)
-               synaptics_image_sensor_3f(priv, &mt_state);
+               num_fingers = 2;
+       else if (sgm->w == 1)
+               num_fingers = priv->agm_count ? priv->agm_count : 3;
        else
-               synaptics_image_sensor_45f(priv, &mt_state);
+               num_fingers = 4;
 
        /* Send resulting input events to user space */
-       synaptics_report_mt_data(psmouse, &mt_state, sgm);
-
-       /* Store updated mt_state */
-       priv->mt_state = agm->mt_state = mt_state;
-       priv->agm_pending = false;
-}
-
-static void synaptics_profile_sensor_process(struct psmouse *psmouse,
-                                            struct synaptics_hw_state *sgm,
-                                            int num_fingers)
-{
-       struct input_dev *dev = psmouse->dev;
-       struct synaptics_data *priv = psmouse->private;
-       struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
-       struct input_mt_pos pos[2];
-       int slot[2], nsemi, i;
-
-       nsemi = clamp_val(num_fingers, 0, 2);
-
-       for (i = 0; i < nsemi; i++) {
-               pos[i].x = hw[i]->x;
-               pos[i].y = synaptics_invert_y(hw[i]->y);
-       }
-
-       input_mt_assign_slots(dev, slot, pos, nsemi);
-
-       for (i = 0; i < nsemi; i++) {
-               input_mt_slot(dev, slot[i]);
-               input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
-               input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x);
-               input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y);
-               input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z);
-       }
-
-       input_mt_drop_unused(dev);
-       input_mt_report_pointer_emulation(dev, false);
-       input_mt_report_finger_count(dev, num_fingers);
-
-       synaptics_report_buttons(psmouse, sgm);
-
-       input_sync(dev);
+       synaptics_report_mt_data(psmouse, sgm, num_fingers);
 }
 
 /*
@@ -1287,7 +920,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        }
 
        if (cr48_profile_sensor) {
-               synaptics_profile_sensor_process(psmouse, &hw, num_fingers);
+               synaptics_report_mt_data(psmouse, &hw, num_fingers);
                return;
        }
 
@@ -1444,7 +1077,7 @@ static void set_input_params(struct psmouse *psmouse,
                                        ABS_MT_POSITION_Y);
                /* Image sensors can report per-contact pressure */
                input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
-               input_mt_init_slots(dev, 2, INPUT_MT_POINTER);
+               input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
 
                /* Image sensors can signal 4 and 5 finger clicks */
                __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
index 1bd01f2..6faf9bb 100644 (file)
 /* amount to fuzz position data when touchpad reports reduced filtering */
 #define SYN_REDUCED_FILTER_FUZZ                8
 
-/*
- * A structure to describe which internal touchpad finger slots are being
- * reported in raw packets.
- */
-struct synaptics_mt_state {
-       int count;                      /* num fingers being tracked */
-       int sgm;                        /* which slot is reported by sgm pkt */
-       int agm;                        /* which slot is reported by agm pkt*/
-};
-
 /*
  * A structure to describe the state of the touchpad hardware (buttons and pad)
  */
@@ -143,9 +133,6 @@ struct synaptics_hw_state {
        unsigned int down:1;
        unsigned char ext_buttons;
        signed char scroll;
-
-       /* As reported in last AGM-CONTACT packets */
-       struct synaptics_mt_state mt_state;
 };
 
 struct synaptics_data {
@@ -170,15 +157,12 @@ struct synaptics_data {
 
        struct serio *pt_port;                  /* Pass-through serio port */
 
-       struct synaptics_mt_state mt_state;     /* Current mt finger state */
-       bool mt_state_lost;                     /* mt_state may be incorrect */
-
        /*
         * Last received Advanced Gesture Mode (AGM) packet. An AGM packet
         * contains position data for a second contact, at half resolution.
         */
        struct synaptics_hw_state agm;
-       bool agm_pending;                       /* new AGM packet received */
+       unsigned int agm_count;                 /* finger count reported by agm */
 
        /* ForcePad handling */
        unsigned long                           press_start;
index a510f7e..926c58e 100644 (file)
 #include <linux/delay.h>
 #include <linux/uaccess.h>
 #include <linux/buffer_head.h>
-#include <linux/version.h>
 #include <linux/slab.h>
 #include <linux/firmware.h>
-#include <linux/version.h>
 #include <linux/input/mt.h>
 #include <linux/acpi.h>
 #include <linux/of.h>
diff --git a/include/linux/platform_data/regulator-haptic.h b/include/linux/platform_data/regulator-haptic.h
new file mode 100644 (file)
index 0000000..5658e58
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Regulator Haptic Platform Data
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Jaewon Kim <jaewon02.kim@samsung.com>
+ * Author: Hyunhee Kim <hyunhee.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _REGULATOR_HAPTIC_H
+#define _REGULATOR_HAPTIC_H
+
+/*
+ * struct regulator_haptic_data - Platform device data
+ *
+ * @max_volt: maximum voltage value supplied to the haptic motor.
+ *             <The unit of the voltage is a micro>
+ * @min_volt: minimum voltage value supplied to the haptic motor.
+ *             <The unit of the voltage is a micro>
+ */
+struct regulator_haptic_data {
+       unsigned int max_volt;
+       unsigned int min_volt;
+};
+
+#endif /* _REGULATOR_HAPTIC_H */