]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 Apr 2015 01:25:15 +0000 (18:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 Apr 2015 01:25:15 +0000 (18:25 -0700)
Pull input subsystem updates from Dmitry Torokhov:
 "You will get the following new drivers:

   - Qualcomm PM8941 power key drver
   - ChipOne icn8318 touchscreen controller driver
   - Broadcom iProc touchscreen and keypad drivers
   - Semtech SX8654 I2C touchscreen controller driver

  ALPS driver now supports newer SS4 devices; Elantech got a fix that
  should make it work on some ASUS laptops; and a slew of other
  enhancements and random fixes"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (51 commits)
  Input: alps - non interleaved V2 dualpoint has separate stick button bits
  Input: alps - fix touchpad buttons getting stuck when used with trackpoint
  Input: atkbd - document "no new force-release quirks" policy
  Input: ALPS - make alps_get_pkt_id_ss4_v2() and others static
  Input: ALPS - V7 devices can report 5-finger taps
  Input: ALPS - add support for SS4 touchpad devices
  Input: ALPS - refactor alps_set_abs_params_mt()
  Input: elantech - fix absolute mode setting on some ASUS laptops
  Input: atmel_mxt_ts - split out touchpad initialisation logic
  Input: atmel_mxt_ts - implement support for T100 touch object
  Input: cros_ec_keyb - fix clearing keyboard state on wakeup
  Input: gscps2 - drop pci_ids dependency
  Input: synaptics - allocate 3 slots to keep stability in image sensors
  Input: Revert "Revert "synaptics - use dmax in input_mt_assign_slots""
  Input: MT - make slot assignment work for overcovered solutions
  mfd: tc3589x: enforce device-tree only mode
  Input: tc3589x - localize platform data
  Input: tsc2007 - Convert msecs to jiffies only once
  Input: edt-ft5x06 - remove EV_SYN event report
  Input: edt-ft5x06 - allow to setting the maximum axes value through the DT
  ...

59 files changed:
Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/goodix.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
Documentation/devicetree/bindings/input/touchscreen/sx8654.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
MAINTAINERS
drivers/hid/hid-debug.c
drivers/input/input-mt.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/bcm-keypad.c [new file with mode: 0644]
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/keyboard/ipaq-micro-keys.c [new file with mode: 0644]
drivers/input/keyboard/tc3589x-keypad.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/max77843-haptic.c [new file with mode: 0644]
drivers/input/misc/mma8450.c
drivers/input/misc/palmas-pwrbutton.c
drivers/input/misc/pm8941-pwrkey.c [new file with mode: 0644]
drivers/input/misc/pwm-beeper.c
drivers/input/misc/regulator-haptic.c
drivers/input/misc/tps65218-pwrbutton.c
drivers/input/mouse/alps.c
drivers/input/mouse/alps.h
drivers/input/mouse/elan_i2c.h
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/elan_i2c_i2c.c
drivers/input/mouse/elantech.c
drivers/input/mouse/elantech.h
drivers/input/mouse/lifebook.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/serio/gscps2.c
drivers/input/serio/i8042.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ar1021_i2c.c
drivers/input/touchscreen/atmel_mxt_ts.c
drivers/input/touchscreen/bcm_iproc_tsc.c [new file with mode: 0644]
drivers/input/touchscreen/chipone_icn8318.c [new file with mode: 0644]
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/elants_i2c.c
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/of_touchscreen.c
drivers/input/touchscreen/sun4i-ts.c
drivers/input/touchscreen/sx8654.c [new file with mode: 0644]
drivers/input/touchscreen/tsc2007.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/platform/x86/fujitsu-tablet.c
drivers/platform/x86/hp-wmi.c
include/linux/mfd/tc3589x.h
include/uapi/linux/input.h

diff --git a/Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt b/Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt
new file mode 100644 (file)
index 0000000..b77f50b
--- /dev/null
@@ -0,0 +1,108 @@
+* Broadcom Keypad Controller device tree bindings
+
+Broadcom Keypad controller is used to interface a SoC with a matrix-type
+keypad device. The keypad controller supports multiple row and column lines.
+A key can be placed at each intersection of a unique row and a unique column.
+The keypad controller can sense a key-press and key-release and report the
+event using a interrupt to the cpu.
+
+This binding is based on the matrix-keymap binding with the following
+changes:
+
+keypad,num-rows and keypad,num-columns are required.
+
+Required SoC Specific Properties:
+- compatible: should be "brcm,bcm-keypad"
+
+- reg: physical base address of the controller and length of memory mapped
+  region.
+
+- interrupts: The interrupt number to the cpu.
+
+Board Specific Properties:
+- keypad,num-rows: Number of row lines connected to the keypad
+  controller.
+
+- keypad,num-columns: Number of column lines connected to the
+  keypad controller.
+
+- col-debounce-filter-period: The debounce period for the Column filter.
+
+       KEYPAD_DEBOUNCE_1_ms    =       0
+       KEYPAD_DEBOUNCE_2_ms    =       1
+       KEYPAD_DEBOUNCE_4_ms    =       2
+       KEYPAD_DEBOUNCE_8_ms    =       3
+       KEYPAD_DEBOUNCE_16_ms   =       4
+       KEYPAD_DEBOUNCE_32_ms   =       5
+       KEYPAD_DEBOUNCE_64_ms   =       6
+       KEYPAD_DEBOUNCE_128_ms  =       7
+
+- status-debounce-filter-period: The debounce period for the Status filter.
+
+       KEYPAD_DEBOUNCE_1_ms    =       0
+       KEYPAD_DEBOUNCE_2_ms    =       1
+       KEYPAD_DEBOUNCE_4_ms    =       2
+       KEYPAD_DEBOUNCE_8_ms    =       3
+       KEYPAD_DEBOUNCE_16_ms   =       4
+       KEYPAD_DEBOUNCE_32_ms   =       5
+       KEYPAD_DEBOUNCE_64_ms   =       6
+       KEYPAD_DEBOUNCE_128_ms  =       7
+
+- row-output-enabled: An optional property indicating whether the row or
+  column is being used as output. If specified the row is being used
+  as the output. Else defaults to column.
+
+- pull-up-enabled: An optional property indicating the Keypad scan mode.
+  If specified implies the keypad scan pull-up has been enabled.
+
+- autorepeat: Boolean, Enable auto repeat feature of Linux input
+         subsystem (optional).
+
+- linux,keymap: The keymap for keys as described in the binding document
+  devicetree/bindings/input/matrix-keymap.txt.
+
+Example:
+#include "dt-bindings/input/input.h"
+
+/ {
+       keypad: keypad@180ac000 {
+               /* Required SoC specific properties */
+               compatible = "brcm,bcm-keypad";
+
+               /* Required Board specific properties */
+               keypad,num-rows = <5>;
+               keypad,num-columns = <5>;
+               status = "okay";
+
+               linux,keymap = <MATRIX_KEY(0x00, 0x02, KEY_F) /* key_forward */
+               MATRIX_KEY(0x00, 0x03, KEY_HOME) /* key_home */
+               MATRIX_KEY(0x00, 0x04, KEY_M) /* key_message */
+               MATRIX_KEY(0x01, 0x00, KEY_A) /* key_contacts */
+               MATRIX_KEY(0x01, 0x01, KEY_1) /* key_1 */
+               MATRIX_KEY(0x01, 0x02, KEY_2) /* key_2 */
+               MATRIX_KEY(0x01, 0x03, KEY_3) /* key_3 */
+               MATRIX_KEY(0x01, 0x04, KEY_S) /* key_speaker */
+               MATRIX_KEY(0x02, 0x00, KEY_P) /* key_phone */
+               MATRIX_KEY(0x02, 0x01, KEY_4) /* key_4 */
+               MATRIX_KEY(0x02, 0x02, KEY_5) /* key_5 */
+               MATRIX_KEY(0x02, 0x03, KEY_6) /* key_6 */
+               MATRIX_KEY(0x02, 0x04, KEY_VOLUMEUP) /* key_vol_up */
+               MATRIX_KEY(0x03, 0x00, KEY_C) /* key_call_log */
+               MATRIX_KEY(0x03, 0x01, KEY_7) /* key_7 */
+               MATRIX_KEY(0x03, 0x02, KEY_8) /* key_8 */
+               MATRIX_KEY(0x03, 0x03, KEY_9) /* key_9 */
+               MATRIX_KEY(0x03, 0x04, KEY_VOLUMEDOWN) /* key_vol_down */
+               MATRIX_KEY(0x04, 0x00, KEY_H) /* key_headset */
+               MATRIX_KEY(0x04, 0x01, KEY_KPASTERISK) /* key_* */
+               MATRIX_KEY(0x04, 0x02, KEY_0) /* key_0 */
+               MATRIX_KEY(0x04, 0x03, KEY_GRAVE) /* key_# */
+               MATRIX_KEY(0x04, 0x04, KEY_MUTE) /* key_mute */
+               >;
+
+               /* Optional board specific properties */
+               col-debounce-filter-period = <5>;
+               row-output-enabled;
+               pull-up-enabled;
+
+       };
+};
diff --git a/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt b/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt
new file mode 100644 (file)
index 0000000..07bf55f
--- /dev/null
@@ -0,0 +1,43 @@
+Qualcomm PM8941 PMIC Power Key
+
+PROPERTIES
+
+- compatible:
+       Usage: required
+       Value type: <string>
+       Definition: must be one of:
+                   "qcom,pm8941-pwrkey"
+
+- reg:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: base address of registers for block
+
+- interrupts:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: key change interrupt; The format of the specifier is
+                   defined by the binding document describing the node's
+                   interrupt parent.
+
+- debounce:
+       Usage: optional
+       Value type: <u32>
+       Definition: time in microseconds that key must be pressed or released
+                   for state change interrupt to trigger.
+
+- bias-pull-up:
+       Usage: optional
+       Value type: <empty>
+       Definition: presence of this property indicates that the KPDPWR_N pin
+                   should be configured for pull up.
+
+EXAMPLE
+
+       pwrkey@800 {
+               compatible = "qcom,pm8941-pwrkey";
+               reg = <0x800>;
+               interrupts = <0x0 0x8 0 IRQ_TYPE_EDGE_BOTH>;
+               debounce = <15625>;
+               bias-pull-up;
+       };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt
new file mode 100644 (file)
index 0000000..34e3382
--- /dev/null
@@ -0,0 +1,76 @@
+* Broadcom's IPROC Touchscreen Controller
+
+Required properties:
+- compatible: must be "brcm,iproc-touchscreen"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- clocks:  The clock provided by the SOC to driver the tsc
+- clock-name:  name for the clock
+- interrupts: The touchscreen controller's interrupt
+
+Optional properties:
+- scanning_period: Time between scans. Each step is 1024 us.  Valid 1-256.
+- debounce_timeout: Each step is 512 us.  Valid 0-255
+- settling_timeout: The settling duration (in ms) is the amount of time
+                    the tsc waits to allow the voltage to settle after
+                    turning on the drivers in detection mode.
+                    Valid values: 0-11
+                    0 =  0.008 ms
+                    1 =  0.01 ms
+                    2 =  0.02 ms
+                    3 =  0.04 ms
+                    4 =  0.08 ms
+                    5 =  0.16 ms
+                    6 =  0.32 ms
+                    7 =  0.64 ms
+                    8 =  1.28 ms
+                    9 =  2.56 ms
+                   10 =  5.12 ms
+                   11 = 10.24 ms
+- touch_timeout: The continuous number of scan periods in which touch is
+                not detected before the controller returns to idle state.
+                Valid values 0-255.
+- average_data: Number of data samples which are averaged before a final
+                data point is placed into the FIFO
+                Valid values 0-7
+                0 =   1 sample
+                1 =   2 samples
+                2 =   4 samples
+                3 =   8 samples
+                4 =  16 samples
+                5 =  32 samples
+                6 =  64 samples
+                7 = 128 samples
+- fifo_threshold: Interrupt is generated whenever the number of fifo
+                entries exceeds this value
+                Valid values 0-31
+- touchscreen-size-x: horizontal resolution of touchscreen (in pixels)
+- touchscreen-size-y: vertical resolution of touchscreen (in pixels)
+- touchscreen-fuzz-x: horizontal noise value of the absolute input
+                      device (in pixels)
+- touchscreen-fuzz-y: vertical noise value of the absolute input
+                      device (in pixels)
+- touchscreen-inverted-x: X axis is inverted (boolean)
+- touchscreen-inverted-y: Y axis is inverted (boolean)
+
+Example:
+
+       touchscreen: tsc@0x180A6000 {
+               compatible = "brcm,iproc-touchscreen";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               reg = <0x180A6000 0x40>;
+               clocks = <&adc_clk>;
+               clock-names = "tsc_clk";
+               interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+
+               scanning_period = <5>;
+               debounce_timeout = <40>;
+               settling_timeout = <7>;
+               touch_timeout = <10>;
+               average_data = <5>;
+               fifo_threshold = <1>;
+               /* Touchscreen is rotated 180 degrees. */
+               touchscreen-inverted-x;
+               touchscreen-inverted-y;
+       };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt b/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
new file mode 100644 (file)
index 0000000..d11f8d6
--- /dev/null
@@ -0,0 +1,46 @@
+* ChipOne icn8318 I2C touchscreen controller
+
+Required properties:
+ - compatible            : "chipone,icn8318"
+ - reg                   : I2C slave address of the chip (0x40)
+ - interrupt-parent      : a phandle pointing to the interrupt controller
+                           serving the interrupt for this chip
+ - interrupts            : interrupt specification for the icn8318 interrupt
+ - wake-gpios            : GPIO specification for the WAKE input
+ - touchscreen-size-x    : horizontal resolution of touchscreen (in pixels)
+ - touchscreen-size-y    : vertical resolution of touchscreen (in pixels)
+
+Optional properties:
+ - pinctrl-names         : should be "default"
+ - pinctrl-0:            : a phandle pointing to the pin settings for the
+                           control gpios
+ - touchscreen-fuzz-x    : horizontal noise value of the absolute input
+                           device (in pixels)
+ - touchscreen-fuzz-y    : vertical noise value of the absolute input
+                           device (in pixels)
+ - touchscreen-inverted-x : X axis is inverted (boolean)
+ - touchscreen-inverted-y : Y axis is inverted (boolean)
+ - touchscreen-swapped-x-y       : X and Y axis are swapped (boolean)
+                           Swapping is done after inverting the axis
+
+Example:
+
+i2c@00000000 {
+       /* ... */
+
+       chipone_icn8318@40 {
+               compatible = "chipone,icn8318";
+               reg = <0x40>;
+               interrupt-parent = <&pio>;
+               interrupts = <9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */
+               pinctrl-names = "default";
+               pinctrl-0 = <&ts_wake_pin_p66>;
+               wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
+               touchscreen-size-x = <800>;
+               touchscreen-size-y = <480>;
+               touchscreen-inverted-x;
+               touchscreen-swapped-x-y;
+       };
+
+       /* ... */
+};
diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt
new file mode 100644 (file)
index 0000000..8ba98ee
--- /dev/null
@@ -0,0 +1,29 @@
+Device tree bindings for Goodix GT9xx series touchscreen controller
+
+Required properties:
+
+ - compatible          : Should be "goodix,gt911"
+                                or "goodix,gt9110"
+                                or "goodix,gt912"
+                                or "goodix,gt927"
+                                or "goodix,gt9271"
+                                or "goodix,gt928"
+                                or "goodix,gt967"
+ - reg                 : I2C address of the chip. Should be 0x5d or 0x14
+ - interrupt-parent    : Interrupt controller to which the chip is connected
+ - interrupts          : Interrupt to which the chip is connected
+
+Example:
+
+       i2c@00000000 {
+               /* ... */
+
+               gt928@5d {
+                       compatible = "goodix,gt928";
+                       reg = <0x5d>;
+                       interrupt-parent = <&gpio>;
+                       interrupts = <0 0>;
+               };
+
+               /* ... */
+       };
index 433332d3b2ba77b63746b331abcff3072f84c934..89abecd938cb98b55d82070ed108eb92bf914b8d 100644 (file)
@@ -2,14 +2,27 @@ sun4i resistive touchscreen controller
 --------------------------------------
 
 Required properties:
- - compatible: "allwinner,sun4i-a10-ts" or "allwinner,sun6i-a31-ts"
+ - compatible: "allwinner,sun4i-a10-ts", "allwinner,sun5i-a13-ts" or
+   "allwinner,sun6i-a31-ts"
  - reg: mmio address range of the chip
  - interrupts: interrupt to which the chip is connected
  - #thermal-sensor-cells: shall be 0
 
 Optional properties:
- - allwinner,ts-attached: boolean indicating that an actual touchscreen is
-                         attached to the controller
+ - allwinner,ts-attached        : boolean indicating that an actual touchscreen
+                                  is attached to the controller
+ - allwinner,tp-sensitive-adjust : integer (4 bits)
+                                  adjust sensitivity of pen down detection
+                                  between 0 (least sensitive) and 15
+                                  (defaults to 15)
+ - allwinner,filter-type        : integer (2 bits)
+                                  select median and averaging filter
+                                  samples used for median / averaging filter
+                                  0: 4/2
+                                  1: 5/3
+                                  2: 8/4
+                                  3: 16/8
+                                  (defaults to 1)
 
 Example:
 
@@ -19,4 +32,7 @@ Example:
                interrupts = <29>;
                allwinner,ts-attached;
                #thermal-sensor-cells = <0>;
+               /* sensitive/noisy touch panel */
+               allwinner,tp-sensitive-adjust = <0>;
+               allwinner,filter-type = <3>;
        };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt
new file mode 100644 (file)
index 0000000..5aaa6b3
--- /dev/null
@@ -0,0 +1,16 @@
+* Semtech SX8654 I2C Touchscreen Controller
+
+Required properties:
+- compatible: must be "semtech,sx8654"
+- reg: i2c slave address
+- interrupt-parent: the phandle for the interrupt controller
+- interrupts: touch controller interrupt
+
+Example:
+
+       sx8654@48 {
+               compatible = "semtech,sx8654";
+               reg = <0x48>;
+               interrupt-parent = <&gpio6>;
+               interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
+       };
index d8e06163c54edd5d3f5c44bb983b0e01b5593dd9..ac23caf518ad07b59ef0aa17feae18519b3ba0be 100644 (file)
@@ -16,6 +16,8 @@ Optional properties for Touchscreens:
                                  controller)
  - touchscreen-inverted-x      : X axis is inverted (boolean)
  - touchscreen-inverted-y      : Y axis is inverted (boolean)
+ - touchscreen-swapped-x-y     : X and Y axis are swapped (boolean)
+                                 Swapping is done after inverting the axis
 
 Deprecated properties for Touchscreens:
  - x-size                      : deprecated name for touchscreen-size-x
index 8764b5b78772dcc5b8de1ba95f08c9c3b9eaa430..b13aa55b7a6f71fdc6d325426ca3ed5bfece7ad2 100644 (file)
@@ -37,6 +37,7 @@ capella       Capella Microsystems, Inc
 cavium Cavium, Inc.
 cdns   Cadence Design Systems Inc.
 chipidea       Chipidea, Inc
+chipone                ChipOne
 chipspark      ChipSPARK
 chrp   Common Hardware Reference Platform
 chunghwa       Chunghwa Picture Tubes Ltd.
@@ -78,6 +79,7 @@ geniatech     Geniatech, Inc.
 giantplus      Giantplus Technology Co., Ltd.
 globalscale    Globalscale Technologies, Inc.
 gmt    Global Mixed-mode Technology, Inc.
+goodix Shenzhen Huiding Technology Co., Ltd.
 google Google, Inc.
 gumstix        Gumstix, Inc.
 gw     Gateworks Corporation
index 17631e6c0151983a36a0950336495f3c24fc7c4f..283d1145e6d8474a27f439dba841446822970810 100644 (file)
@@ -2525,6 +2525,13 @@ L:       linux-usb@vger.kernel.org
 S:     Maintained
 F:     drivers/usb/chipidea/
 
+CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER
+M:     Hans de Goede <hdegoede@redhat.com>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
+F:     drivers/input/touchscreen/chipone_icn8318.c
+
 CHROME HARDWARE PLATFORM SUPPORT
 M:     Olof Johansson <olof@lixom.net>
 S:     Maintained
index c785095f9f2ceb30808dc721bbe70b08bae8327a..2886b645ced73808fe0f4d507823cf459ef70914 100644 (file)
@@ -815,7 +815,7 @@ static const char *keys[KEY_MAX + 1] = {
        [KEY_DELETEFILE] = "DeleteFile",        [KEY_XFER] = "X-fer",
        [KEY_PROG1] = "Prog1",                  [KEY_PROG2] = "Prog2",
        [KEY_WWW] = "WWW",                      [KEY_MSDOS] = "MSDOS",
-       [KEY_COFFEE] = "Coffee",                [KEY_DIRECTION] = "Direction",
+       [KEY_COFFEE] = "Coffee",                [KEY_ROTATE_DISPLAY] = "RotateDisplay",
        [KEY_CYCLEWINDOWS] = "CycleWindows",    [KEY_MAIL] = "Mail",
        [KEY_BOOKMARKS] = "Bookmarks",          [KEY_COMPUTER] = "Computer",
        [KEY_BACK] = "Back",                    [KEY_FORWARD] = "Forward",
index 34feb3e1127b2b64c31e8d803f98fcb5ec2aaffc..54fce56c80236808d5a6eb02fa6c8d17ebb76415 100644 (file)
@@ -368,27 +368,35 @@ static void input_mt_set_slots(struct input_mt *mt,
                               int *slots, int num_pos)
 {
        struct input_mt_slot *s;
-       int *w = mt->red, *p;
+       int *w = mt->red, j;
 
-       for (p = slots; p != slots + num_pos; p++)
-               *p = -1;
+       for (j = 0; j != num_pos; j++)
+               slots[j] = -1;
 
        for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
                if (!input_mt_is_active(s))
                        continue;
-               for (p = slots; p != slots + num_pos; p++)
-                       if (*w++ < 0)
-                               *p = s - mt->slots;
+
+               for (j = 0; j != num_pos; j++) {
+                       if (w[j] < 0) {
+                               slots[j] = s - mt->slots;
+                               break;
+                       }
+               }
+
+               w += num_pos;
        }
 
        for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
                if (input_mt_is_active(s))
                        continue;
-               for (p = slots; p != slots + num_pos; p++)
-                       if (*p < 0) {
-                               *p = s - mt->slots;
+
+               for (j = 0; j != num_pos; j++) {
+                       if (slots[j] < 0) {
+                               slots[j] = s - mt->slots;
                                break;
                        }
+               }
        }
 }
 
index a89ba7cb96f1ec21780916a23d44966743e5ad4c..106fbac7f8c5b024acf458c5564ef18588f1158a 100644 (file)
@@ -588,6 +588,16 @@ config KEYBOARD_DAVINCI
          To compile this driver as a module, choose M here: the
          module will be called davinci_keyscan.
 
+config KEYBOARD_IPAQ_MICRO
+       tristate "Buttons on Micro SoC (iPaq h3100,h3600,h3700)"
+       depends on MFD_IPAQ_MICRO
+       help
+         Say Y to enable support for the buttons attached to
+         Micro peripheral controller on iPAQ h3100/h3600/h3700
+
+         To compile this driver as a module, choose M here: the
+         module will be called ipaq-micro-keys.
+
 config KEYBOARD_OMAP
        tristate "TI OMAP keypad support"
        depends on ARCH_OMAP1
@@ -686,4 +696,15 @@ config KEYBOARD_CAP11XX
          To compile this driver as a module, choose M here: the
          module will be called cap11xx.
 
+config KEYBOARD_BCM
+       tristate "Broadcom keypad driver"
+       depends on OF && HAVE_CLK
+       select INPUT_MATRIXKMAP
+       default ARCH_BCM_CYGNUS
+       help
+         Say Y here if you want to use Broadcom keypad.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bcm-keypad.
+
 endif
index 470767884bd8c33ec51b7c3a5e5aa63a8e7eb59f..df28d5553c05c4e1a17964eee3ee88d0feebb7f3 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5589)                += adp5589-keys.o
 obj-$(CONFIG_KEYBOARD_AMIGA)           += amikbd.o
 obj-$(CONFIG_KEYBOARD_ATARI)           += atakbd.o
 obj-$(CONFIG_KEYBOARD_ATKBD)           += atkbd.o
+obj-$(CONFIG_KEYBOARD_BCM)             += bcm-keypad.o
 obj-$(CONFIG_KEYBOARD_BFIN)            += bf54x-keys.o
 obj-$(CONFIG_KEYBOARD_CAP11XX)         += cap11xx.o
 obj-$(CONFIG_KEYBOARD_CLPS711X)                += clps711x-keypad.o
@@ -23,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_TCA6416)                += tca6416-keypad.o
 obj-$(CONFIG_KEYBOARD_TCA8418)         += tca8418_keypad.o
 obj-$(CONFIG_KEYBOARD_HIL)             += hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)         += hilkbd.o
+obj-$(CONFIG_KEYBOARD_IPAQ_MICRO)      += ipaq-micro-keys.o
 obj-$(CONFIG_KEYBOARD_IMX)             += imx_keypad.o
 obj-$(CONFIG_KEYBOARD_HP6XX)           += jornada680_kbd.o
 obj-$(CONFIG_KEYBOARD_HP7XX)           += jornada720_kbd.o
index 387c51f4b4e42960ad1de701cb421d9ee1463962..ec876b5b1382ff7da2e28b1eed6e953f0729ed32 100644 (file)
@@ -1653,6 +1653,12 @@ static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id)
        return 1;
 }
 
+/*
+ * NOTE: do not add any more "force release" quirks to this table.  The
+ * task of adjusting list of keys that should be "released" automatically
+ * by the driver is now delegated to userspace tools, such as udev, so
+ * submit such quirks there.
+ */
 static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
        {
                .matches = {
diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c
new file mode 100644 (file)
index 0000000..86a8b72
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * 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/bitops.h>
+#include <linux/clk.h>
+#include <linux/gfp.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+
+#define DEFAULT_CLK_HZ                 31250
+#define MAX_ROWS                       8
+#define MAX_COLS                       8
+
+/* Register/field definitions */
+#define KPCR_OFFSET                    0x00000080
+#define KPCR_MODE                      0x00000002
+#define KPCR_MODE_SHIFT                        1
+#define KPCR_MODE_MASK                 1
+#define KPCR_ENABLE                    0x00000001
+#define KPCR_STATUSFILTERENABLE                0x00008000
+#define KPCR_STATUSFILTERTYPE_SHIFT    12
+#define KPCR_COLFILTERENABLE           0x00000800
+#define KPCR_COLFILTERTYPE_SHIFT       8
+#define KPCR_ROWWIDTH_SHIFT            20
+#define KPCR_COLUMNWIDTH_SHIFT         16
+
+#define KPIOR_OFFSET                   0x00000084
+#define KPIOR_ROWOCONTRL_SHIFT         24
+#define KPIOR_ROWOCONTRL_MASK          0xFF000000
+#define KPIOR_COLUMNOCONTRL_SHIFT      16
+#define KPIOR_COLUMNOCONTRL_MASK       0x00FF0000
+#define KPIOR_COLUMN_IO_DATA_SHIFT     0
+
+#define KPEMR0_OFFSET                  0x00000090
+#define KPEMR1_OFFSET                  0x00000094
+#define KPEMR2_OFFSET                  0x00000098
+#define KPEMR3_OFFSET                  0x0000009C
+#define KPEMR_EDGETYPE_BOTH            3
+
+#define KPSSR0_OFFSET                  0x000000A0
+#define KPSSR1_OFFSET                  0x000000A4
+#define KPSSRN_OFFSET(reg_n)           (KPSSR0_OFFSET + 4 * (reg_n))
+#define KPIMR0_OFFSET                  0x000000B0
+#define KPIMR1_OFFSET                  0x000000B4
+#define KPICR0_OFFSET                  0x000000B8
+#define KPICR1_OFFSET                  0x000000BC
+#define KPICRN_OFFSET(reg_n)           (KPICR0_OFFSET + 4 * (reg_n))
+#define KPISR0_OFFSET                  0x000000C0
+#define KPISR1_OFFSET                  0x000000C4
+
+#define KPCR_STATUSFILTERTYPE_MAX      7
+#define KPCR_COLFILTERTYPE_MAX         7
+
+/* Macros to determine the row/column from a bit that is set in SSR0/1. */
+#define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n))
+#define BIT_TO_COL(bit_nr)             ((bit_nr) % 8)
+
+/* Structure representing various run-time entities */
+struct bcm_kp {
+       void __iomem *base;
+       int irq;
+       struct clk *clk;
+       struct input_dev *input_dev;
+       unsigned long last_state[2];
+       unsigned int n_rows;
+       unsigned int n_cols;
+       u32 kpcr;
+       u32 kpior;
+       u32 kpemr;
+       u32 imr0_val;
+       u32 imr1_val;
+};
+
+/*
+ * Returns the keycode from the input device keymap given the row and
+ * column.
+ */
+static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col)
+{
+       unsigned int row_shift = get_count_order(kp->n_cols);
+       unsigned short *keymap = kp->input_dev->keycode;
+
+       return keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
+}
+
+static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode)
+{
+       unsigned long state, change;
+       int bit_nr;
+       int key_press;
+       int row, col;
+       unsigned int keycode;
+
+       /* Clear interrupts */
+       writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num));
+
+       state = readl(kp->base + KPSSRN_OFFSET(reg_num));
+       change = kp->last_state[reg_num] ^ state;
+       kp->last_state[reg_num] = state;
+
+       for_each_set_bit(bit_nr, &change, BITS_PER_LONG) {
+               key_press = state & BIT(bit_nr);
+               /* The meaning of SSR register depends on pull mode. */
+               key_press = pull_mode ? !key_press : key_press;
+               row = BIT_TO_ROW_SSRN(bit_nr, reg_num);
+               col = BIT_TO_COL(bit_nr);
+               keycode = bcm_kp_get_keycode(kp, row, col);
+               input_report_key(kp->input_dev, keycode, key_press);
+       }
+}
+
+static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id)
+{
+       struct bcm_kp *kp = dev_id;
+       int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK;
+       int reg_num;
+
+       for (reg_num = 0; reg_num <= 1; reg_num++)
+               bcm_kp_report_keys(kp, reg_num, pull_mode);
+
+       input_sync(kp->input_dev);
+
+       return IRQ_HANDLED;
+}
+
+static int bcm_kp_start(struct bcm_kp *kp)
+{
+       int error;
+
+       if (kp->clk) {
+               error = clk_prepare_enable(kp->clk);
+               if (error)
+                       return error;
+       }
+
+       writel(kp->kpior, kp->base + KPIOR_OFFSET);
+
+       writel(kp->imr0_val, kp->base + KPIMR0_OFFSET);
+       writel(kp->imr1_val, kp->base + KPIMR1_OFFSET);
+
+       writel(kp->kpemr, kp->base + KPEMR0_OFFSET);
+       writel(kp->kpemr, kp->base + KPEMR1_OFFSET);
+       writel(kp->kpemr, kp->base + KPEMR2_OFFSET);
+       writel(kp->kpemr, kp->base + KPEMR3_OFFSET);
+
+       writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
+       writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
+
+       kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET);
+       kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET);
+
+       writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET);
+
+       return 0;
+}
+
+static void bcm_kp_stop(const struct bcm_kp *kp)
+{
+       u32 val;
+
+       val = readl(kp->base + KPCR_OFFSET);
+       val &= ~KPCR_ENABLE;
+       writel(0, kp->base + KPCR_OFFSET);
+       writel(0, kp->base + KPIMR0_OFFSET);
+       writel(0, kp->base + KPIMR1_OFFSET);
+       writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET);
+       writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET);
+
+       if (kp->clk)
+               clk_disable_unprepare(kp->clk);
+}
+
+static int bcm_kp_open(struct input_dev *dev)
+{
+       struct bcm_kp *kp = input_get_drvdata(dev);
+
+       return bcm_kp_start(kp);
+}
+
+static void bcm_kp_close(struct input_dev *dev)
+{
+       struct bcm_kp *kp = input_get_drvdata(dev);
+
+       bcm_kp_stop(kp);
+}
+
+static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp)
+{
+       struct device *dev = kp->input_dev->dev.parent;
+       struct device_node *np = dev->of_node;
+       int error;
+       unsigned int dt_val;
+       unsigned int i;
+       unsigned int num_rows, col_mask, rows_set;
+
+       /* Initialize the KPCR Keypad Configuration Register */
+       kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE;
+
+       error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols);
+       if (error) {
+               dev_err(dev, "failed to parse kp params\n");
+               return error;
+       }
+
+       /* Set row width for the ASIC block. */
+       kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT;
+
+       /* Set column width for the ASIC block. */
+       kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT;
+
+       /* Configure the IMR registers */
+
+       /*
+        * IMR registers contain interrupt enable bits for 8x8 matrix
+        * IMR0 register format: <row3> <row2> <row1> <row0>
+        * IMR1 register format: <row7> <row6> <row5> <row4>
+        */
+       col_mask = (1 << (kp->n_cols)) - 1;
+       num_rows = kp->n_rows;
+
+       /* Set column bits in rows 0 to 3 in IMR0 */
+       kp->imr0_val = col_mask;
+
+       rows_set = 1;
+       while (--num_rows && rows_set++ < 4)
+               kp->imr0_val |= kp->imr0_val << MAX_COLS;
+
+       /* Set column bits in rows 4 to 7 in IMR1 */
+       kp->imr1_val = 0;
+       if (num_rows) {
+               kp->imr1_val = col_mask;
+               while (--num_rows)
+                       kp->imr1_val |= kp->imr1_val << MAX_COLS;
+       }
+
+       /* Initialize the KPEMR Keypress Edge Mode Registers */
+       /* Trigger on both edges */
+       kp->kpemr = 0;
+       for (i = 0; i <= 30; i += 2)
+               kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i);
+
+       /*
+        * Obtain the Status filter debounce value and verify against the
+        * possible values specified in the DT binding.
+        */
+       of_property_read_u32(np, "status-debounce-filter-period", &dt_val);
+
+       if (dt_val > KPCR_STATUSFILTERTYPE_MAX) {
+               dev_err(dev, "Invalid Status filter debounce value %d\n",
+                       dt_val);
+               return -EINVAL;
+       }
+
+       kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT;
+
+       /*
+        * Obtain the Column filter debounce value and verify against the
+        * possible values specified in the DT binding.
+        */
+       of_property_read_u32(np, "col-debounce-filter-period", &dt_val);
+
+       if (dt_val > KPCR_COLFILTERTYPE_MAX) {
+               dev_err(dev, "Invalid Column filter debounce value %d\n",
+                       dt_val);
+               return -EINVAL;
+       }
+
+       kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT;
+
+       /*
+        * Determine between the row and column,
+        * which should be configured as output.
+        */
+       if (of_property_read_bool(np, "row-output-enabled")) {
+               /*
+               * Set RowOContrl or ColumnOContrl in KPIOR
+               * to the number of pins to drive as outputs
+               */
+               kp->kpior = ((1 << kp->n_rows) - 1) <<
+                               KPIOR_ROWOCONTRL_SHIFT;
+       } else {
+               kp->kpior = ((1 << kp->n_cols) - 1) <<
+                               KPIOR_COLUMNOCONTRL_SHIFT;
+       }
+
+       /*
+        * Determine if the scan pull up needs to be enabled
+        */
+       if (of_property_read_bool(np, "pull-up-enabled"))
+               kp->kpcr |= KPCR_MODE;
+
+       dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n",
+               kp->n_rows, kp->n_cols,
+               kp->kpcr, kp->kpior, kp->kpemr);
+
+       return 0;
+}
+
+
+static int bcm_kp_probe(struct platform_device *pdev)
+{
+       struct bcm_kp *kp;
+       struct input_dev *input_dev;
+       struct resource *res;
+       int error;
+
+       kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
+       if (!kp)
+               return -ENOMEM;
+
+       input_dev = devm_input_allocate_device(&pdev->dev);
+       if (!input_dev) {
+               dev_err(&pdev->dev, "failed to allocate the input device\n");
+               return -ENOMEM;
+       }
+
+       __set_bit(EV_KEY, input_dev->evbit);
+
+       /* Enable auto repeat feature of Linux input subsystem */
+       if (of_property_read_bool(pdev->dev.of_node, "autorepeat"))
+               __set_bit(EV_REP, input_dev->evbit);
+
+       input_dev->name = pdev->name;
+       input_dev->phys = "keypad/input0";
+       input_dev->dev.parent = &pdev->dev;
+       input_dev->open = bcm_kp_open;
+       input_dev->close = bcm_kp_close;
+
+       input_dev->id.bustype = BUS_HOST;
+       input_dev->id.vendor = 0x0001;
+       input_dev->id.product = 0x0001;
+       input_dev->id.version = 0x0100;
+
+       input_set_drvdata(input_dev, kp);
+
+       kp->input_dev = input_dev;
+
+       platform_set_drvdata(pdev, kp);
+
+       error = bcm_kp_matrix_key_parse_dt(kp);
+       if (error)
+               return error;
+
+       error = matrix_keypad_build_keymap(NULL, NULL,
+                                          kp->n_rows, kp->n_cols,
+                                          NULL, input_dev);
+       if (error) {
+               dev_err(&pdev->dev, "failed to build keymap\n");
+               return error;
+       }
+
+       /* Get the KEYPAD base address */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Missing keypad base address resource\n");
+               return -ENODEV;
+       }
+
+       kp->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(kp->base))
+               return PTR_ERR(kp->base);
+
+       /* Enable clock */
+       kp->clk = devm_clk_get(&pdev->dev, "peri_clk");
+       if (IS_ERR(kp->clk)) {
+               error = PTR_ERR(kp->clk);
+               if (error != -ENOENT) {
+                       if (error != -EPROBE_DEFER)
+                               dev_err(&pdev->dev, "Failed to get clock\n");
+                       return error;
+               }
+               dev_dbg(&pdev->dev,
+                       "No clock specified. Assuming it's enabled\n");
+               kp->clk = NULL;
+       } else {
+               unsigned int desired_rate;
+               long actual_rate;
+
+               error = of_property_read_u32(pdev->dev.of_node,
+                                            "clock-frequency", &desired_rate);
+               if (error < 0)
+                       desired_rate = DEFAULT_CLK_HZ;
+
+               actual_rate = clk_round_rate(kp->clk, desired_rate);
+               if (actual_rate <= 0)
+                       return -EINVAL;
+
+               error = clk_set_rate(kp->clk, actual_rate);
+               if (error)
+                       return error;
+
+               error = clk_prepare_enable(kp->clk);
+               if (error)
+                       return error;
+       }
+
+       /* Put the kp into a known sane state */
+       bcm_kp_stop(kp);
+
+       kp->irq = platform_get_irq(pdev, 0);
+       if (kp->irq < 0) {
+               dev_err(&pdev->dev, "no IRQ specified\n");
+               return -EINVAL;
+       }
+
+       error = devm_request_threaded_irq(&pdev->dev, kp->irq,
+                                         NULL, bcm_kp_isr_thread,
+                                         IRQF_ONESHOT, pdev->name, kp);
+       if (error) {
+               dev_err(&pdev->dev, "failed to request IRQ\n");
+               return error;
+       }
+
+       error = input_register_device(input_dev);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register input device\n");
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id bcm_kp_of_match[] = {
+       { .compatible = "brcm,bcm-keypad" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, bcm_kp_of_match);
+
+static struct platform_driver bcm_kp_device_driver = {
+       .probe          = bcm_kp_probe,
+       .driver         = {
+               .name   = "bcm-keypad",
+               .of_match_table = of_match_ptr(bcm_kp_of_match),
+       }
+};
+
+module_platform_driver(bcm_kp_device_driver);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("BCM Keypad Driver");
+MODULE_LICENSE("GPL v2");
index ffa989f2c785100a0006f145816ca73e4f90c462..64b9b59ad4cbf434e658336dc7351496ba38d94e 100644 (file)
@@ -338,7 +338,7 @@ static int cros_ec_keyb_resume(struct device *dev)
         * wake source (e.g. the lid is open and the user might press a key to
         * wake) then the key scan buffer should be preserved.
         */
-       if (ckdev->ec->was_wake_device)
+       if (!ckdev->ec->was_wake_device)
                cros_ec_keyb_clear_keyboard(ckdev);
 
        return 0;
diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c
new file mode 100644 (file)
index 0000000..602900d
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ *
+ * h3600 atmel micro companion support, key subdevice
+ * based on previous kernel 2.4 version
+ * Author : Alessandro Gardich <gremlin@gremlin.it>
+ * Author : Linus Walleij <linus.walleij@linaro.org>
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/ipaq-micro.h>
+
+struct ipaq_micro_keys {
+       struct ipaq_micro *micro;
+       struct input_dev *input;
+       u16 *codes;
+};
+
+static const u16 micro_keycodes[] = {
+       KEY_RECORD,             /* 1:  Record button                    */
+       KEY_CALENDAR,           /* 2:  Calendar                         */
+       KEY_ADDRESSBOOK,        /* 3:  Contacts (looks like Outlook)    */
+       KEY_MAIL,               /* 4:  Envelope (Q on older iPAQs)      */
+       KEY_HOMEPAGE,           /* 5:  Start (looks like swoopy arrow)  */
+       KEY_UP,                 /* 6:  Up                               */
+       KEY_RIGHT,              /* 7:  Right                            */
+       KEY_LEFT,               /* 8:  Left                             */
+       KEY_DOWN,               /* 9:  Down                             */
+};
+
+static void micro_key_receive(void *data, int len, unsigned char *msg)
+{
+       struct ipaq_micro_keys *keys = data;
+       int key, down;
+
+       down = 0x80 & msg[0];
+       key  = 0x7f & msg[0];
+
+       if (key < ARRAY_SIZE(micro_keycodes)) {
+               input_report_key(keys->input, keys->codes[key], down);
+               input_sync(keys->input);
+       }
+}
+
+static void micro_key_start(struct ipaq_micro_keys *keys)
+{
+       spin_lock(&keys->micro->lock);
+       keys->micro->key = micro_key_receive;
+       keys->micro->key_data = keys;
+       spin_unlock(&keys->micro->lock);
+}
+
+static void micro_key_stop(struct ipaq_micro_keys *keys)
+{
+       spin_lock(&keys->micro->lock);
+       keys->micro->key = NULL;
+       keys->micro->key_data = NULL;
+       spin_unlock(&keys->micro->lock);
+}
+
+static int micro_key_open(struct input_dev *input)
+{
+       struct ipaq_micro_keys *keys = input_get_drvdata(input);
+
+       micro_key_start(keys);
+
+       return 0;
+}
+
+static void micro_key_close(struct input_dev *input)
+{
+       struct ipaq_micro_keys *keys = input_get_drvdata(input);
+
+       micro_key_stop(keys);
+}
+
+static int micro_key_probe(struct platform_device *pdev)
+{
+       struct ipaq_micro_keys *keys;
+       int error;
+       int i;
+
+       keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
+       if (!keys)
+               return -ENOMEM;
+
+       keys->micro = dev_get_drvdata(pdev->dev.parent);
+
+       keys->input = devm_input_allocate_device(&pdev->dev);
+       if (!keys->input)
+               return -ENOMEM;
+
+       keys->input->keycodesize = sizeof(micro_keycodes[0]);
+       keys->input->keycodemax = ARRAY_SIZE(micro_keycodes);
+       keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes,
+                          keys->input->keycodesize * keys->input->keycodemax,
+                          GFP_KERNEL);
+       keys->input->keycode = keys->codes;
+
+       __set_bit(EV_KEY, keys->input->evbit);
+       for (i = 0; i < ARRAY_SIZE(micro_keycodes); i++)
+               __set_bit(micro_keycodes[i], keys->input->keybit);
+
+       keys->input->name = "h3600 micro keys";
+       keys->input->open = micro_key_open;
+       keys->input->close = micro_key_close;
+       input_set_drvdata(keys->input, keys);
+
+       error = input_register_device(keys->input);
+       if (error)
+               return error;
+
+       platform_set_drvdata(pdev, keys);
+       return 0;
+}
+
+static int __maybe_unused micro_key_suspend(struct device *dev)
+{
+       struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
+
+       micro_key_stop(keys);
+
+       return 0;
+}
+
+static int __maybe_unused micro_key_resume(struct device *dev)
+{
+       struct ipaq_micro_keys *keys = dev_get_drvdata(dev);
+       struct input_dev *input = keys->input;
+
+       mutex_lock(&input->mutex);
+
+       if (input->users)
+               micro_key_start(keys);
+
+       mutex_unlock(&input->mutex);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(micro_key_dev_pm_ops,
+                        micro_key_suspend, micro_key_resume);
+
+static struct platform_driver micro_key_device_driver = {
+       .driver = {
+               .name    = "ipaq-micro-keys",
+               .pm     = &micro_key_dev_pm_ops,
+       },
+       .probe   = micro_key_probe,
+};
+module_platform_driver(micro_key_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("driver for iPAQ Atmel micro keys");
+MODULE_ALIAS("platform:ipaq-micro-keys");
index 32522176b0bd26e6336105667103a11588d78922..31c606a4dd310cbfa0ccfdbfce39769f3e86beda 100644 (file)
 #define TC3589x_EVT_INT_CLR    0x2
 #define TC3589x_KBD_INT_CLR    0x1
 
+/**
+ * struct tc35893_keypad_platform_data - platform specific keypad data
+ * @keymap_data:        matrix scan code table for keycodes
+ * @krow:               mask for available rows, value is 0xFF
+ * @kcol:               mask for available columns, value is 0xFF
+ * @debounce_period:    platform specific debounce time
+ * @settle_time:        platform specific settle down time
+ * @irqtype:            type of interrupt, falling or rising edge
+ * @enable_wakeup:      specifies if keypad event can wake up system from sleep
+ * @no_autorepeat:      flag for auto repetition
+ */
+struct tc3589x_keypad_platform_data {
+       const struct matrix_keymap_data *keymap_data;
+       u8                      krow;
+       u8                      kcol;
+       u8                      debounce_period;
+       u8                      settle_time;
+       unsigned long           irqtype;
+       bool                    enable_wakeup;
+       bool                    no_autorepeat;
+};
+
 /**
  * struct tc_keypad - data structure used by keypad driver
  * @tc3589x:    pointer to tc35893
@@ -354,13 +376,10 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
        const struct tc3589x_keypad_platform_data *plat;
        int error, irq;
 
-       plat = tc3589x->pdata->keypad;
-       if (!plat) {
-               plat = tc3589x_keypad_of_probe(&pdev->dev);
-               if (IS_ERR(plat)) {
-                       dev_err(&pdev->dev, "invalid keypad platform data\n");
-                       return PTR_ERR(plat);
-               }
+       plat = tc3589x_keypad_of_probe(&pdev->dev);
+       if (IS_ERR(plat)) {
+               dev_err(&pdev->dev, "invalid keypad platform data\n");
+               return PTR_ERR(plat);
        }
 
        irq = platform_get_irq(pdev, 0);
index 6deb8dae320511cacd4211999b51fdedfad3e9f5..4436ab1b9735d608821309eea92b486a1bc147e2 100644 (file)
@@ -115,6 +115,18 @@ config INPUT_PCSPKR
          To compile this driver as a module, choose M here: the
          module will be called pcspkr.
 
+config INPUT_PM8941_PWRKEY
+       tristate "Qualcomm PM8941 power key support"
+       depends on MFD_SPMI_PMIC
+       help
+         Say Y here if you want support for the power key usually found
+         on boards using a Qualcomm PM8941 compatible PMIC.
+
+         If unsure, say Y.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pm8941-pwrkey.
+
 config INPUT_PM8XXX_VIBRATOR
        tristate "Qualcomm PM8XXX vibrator support"
        depends on MFD_PM8XXX
@@ -165,6 +177,18 @@ config INPUT_MAX77693_HAPTIC
          To compile this driver as module, choose M here: the
          module will be called max77693-haptic.
 
+config INPUT_MAX77843_HAPTIC
+    tristate "MAXIM MAX77843 haptic controller support"
+    depends on MFD_MAX77843 && REGULATOR
+    select INPUT_FF_MEMLESS
+    help
+      This option enables support for the haptic controller on
+      MAXIM MAX77843 chip. The driver supports ff-memless interface
+      from input framework.
+
+      To compile this driver as module, choose M here: the
+      module will be called max77843-haptic.
+
 config INPUT_MAX8925_ONKEY
        tristate "MAX8925 ONKEY support"
        depends on MFD_MAX8925
index 403a1a54a76c3050081b79e65e08bb17387aa388..78ba4c1b8532c5093c4259f69761cf7a0b751d3b 100644 (file)
@@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)    += keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)              += kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)          += m68kspkr.o
 obj-$(CONFIG_INPUT_MAX77693_HAPTIC)    += max77693-haptic.o
+obj-$(CONFIG_INPUT_MAX77843_HAPTIC)    += max77843-haptic.o
 obj-$(CONFIG_INPUT_MAX8925_ONKEY)      += max8925_onkey.o
 obj-$(CONFIG_INPUT_MAX8997_HAPTIC)     += max8997_haptic.o
 obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)  += mc13783-pwrbutton.o
@@ -49,6 +50,7 @@ obj-$(CONFIG_INPUT_PCAP)              += pcap_keys.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
 obj-$(CONFIG_INPUT_PCF8574)            += pcf8574_keypad.o
 obj-$(CONFIG_INPUT_PCSPKR)             += pcspkr.o
+obj-$(CONFIG_INPUT_PM8941_PWRKEY)      += pm8941-pwrkey.o
 obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR)    += pm8xxx-vibrator.o
 obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)    += pmic8xxx-pwrkey.o
 obj-$(CONFIG_INPUT_POWERMATE)          += powermate.o
diff --git a/drivers/input/misc/max77843-haptic.c b/drivers/input/misc/max77843-haptic.c
new file mode 100644 (file)
index 0000000..dccbb46
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * MAXIM MAX77693 Haptic device driver
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ * Author: Jaewon Kim <jaewon02.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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/mfd/max77843-private.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define MAX_MAGNITUDE_SHIFT        16
+
+enum max77843_haptic_motor_type {
+       MAX77843_HAPTIC_ERM = 0,
+       MAX77843_HAPTIC_LRA,
+};
+
+enum max77843_haptic_pwm_divisor {
+       MAX77843_HAPTIC_PWM_DIVISOR_32 = 0,
+       MAX77843_HAPTIC_PWM_DIVISOR_64,
+       MAX77843_HAPTIC_PWM_DIVISOR_128,
+       MAX77843_HAPTIC_PWM_DIVISOR_256,
+};
+
+struct max77843_haptic {
+       struct regmap *regmap_haptic;
+       struct device *dev;
+       struct input_dev *input_dev;
+       struct pwm_device *pwm_dev;
+       struct regulator *motor_reg;
+       struct work_struct work;
+       struct mutex mutex;
+
+       unsigned int magnitude;
+       unsigned int pwm_duty;
+
+       bool active;
+       bool suspended;
+
+       enum max77843_haptic_motor_type type;
+       enum max77843_haptic_pwm_divisor pwm_divisor;
+};
+
+static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic)
+{
+       int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
+       int error;
+
+       error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
+       if (error) {
+               dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on)
+{
+       int error;
+
+       error = regmap_update_bits(haptic->regmap_haptic,
+                                  MAX77843_SYS_REG_MAINCTRL1,
+                                  MAX77843_MAINCTRL1_BIASEN_MASK,
+                                  on << MAINCTRL1_BIASEN_SHIFT);
+       if (error) {
+               dev_err(haptic->dev, "failed to %s bias: %d\n",
+                       on ? "enable" : "disable", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable)
+{
+       unsigned int value;
+       int error;
+
+       value = (haptic->type << MCONFIG_MODE_SHIFT) |
+               (enable << MCONFIG_MEN_SHIFT) |
+               (haptic->pwm_divisor << MCONFIG_PDIV_SHIFT);
+
+       error = regmap_write(haptic->regmap_haptic,
+                            MAX77843_HAP_REG_MCONFIG, value);
+       if (error) {
+               dev_err(haptic->dev,
+                       "failed to update haptic config: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int max77843_haptic_enable(struct max77843_haptic *haptic)
+{
+       int error;
+
+       if (haptic->active)
+               return 0;
+
+       error = pwm_enable(haptic->pwm_dev);
+       if (error) {
+               dev_err(haptic->dev,
+                       "failed to enable pwm device: %d\n", error);
+               return error;
+       }
+
+       error = max77843_haptic_config(haptic, true);
+       if (error)
+               goto err_config;
+
+       haptic->active = true;
+
+       return 0;
+
+err_config:
+       pwm_disable(haptic->pwm_dev);
+
+       return error;
+}
+
+static int max77843_haptic_disable(struct max77843_haptic *haptic)
+{
+       int error;
+
+       if (!haptic->active)
+               return 0;
+
+       error = max77843_haptic_config(haptic, false);
+       if (error)
+               return error;
+
+       pwm_disable(haptic->pwm_dev);
+
+       haptic->active = false;
+
+       return 0;
+}
+
+static void max77843_haptic_play_work(struct work_struct *work)
+{
+       struct max77843_haptic *haptic =
+                       container_of(work, struct max77843_haptic, work);
+       int error;
+
+       mutex_lock(&haptic->mutex);
+
+       if (haptic->suspended)
+               goto out_unlock;
+
+       if (haptic->magnitude) {
+               error = max77843_haptic_set_duty_cycle(haptic);
+               if (error) {
+                       dev_err(haptic->dev,
+                               "failed to set duty cycle: %d\n", error);
+                       goto out_unlock;
+               }
+
+               error = max77843_haptic_enable(haptic);
+               if (error)
+                       dev_err(haptic->dev,
+                               "cannot enable haptic: %d\n", error);
+       } else {
+               error = max77843_haptic_disable(haptic);
+               if (error)
+                       dev_err(haptic->dev,
+                               "cannot disable haptic: %d\n", error);
+       }
+
+out_unlock:
+       mutex_unlock(&haptic->mutex);
+}
+
+static int max77843_haptic_play_effect(struct input_dev *dev, void *data,
+               struct ff_effect *effect)
+{
+       struct max77843_haptic *haptic = input_get_drvdata(dev);
+       u64 period_mag_multi;
+
+       haptic->magnitude = effect->u.rumble.strong_magnitude;
+       if (!haptic->magnitude)
+               haptic->magnitude = effect->u.rumble.weak_magnitude;
+
+       period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
+       haptic->pwm_duty = (unsigned int)(period_mag_multi >>
+                                               MAX_MAGNITUDE_SHIFT);
+
+       schedule_work(&haptic->work);
+
+       return 0;
+}
+
+static int max77843_haptic_open(struct input_dev *dev)
+{
+       struct max77843_haptic *haptic = input_get_drvdata(dev);
+       int error;
+
+       error = max77843_haptic_bias(haptic, true);
+       if (error)
+               return error;
+
+       error = regulator_enable(haptic->motor_reg);
+       if (error) {
+               dev_err(haptic->dev,
+                       "failed to enable regulator: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static void max77843_haptic_close(struct input_dev *dev)
+{
+       struct max77843_haptic *haptic = input_get_drvdata(dev);
+       int error;
+
+       cancel_work_sync(&haptic->work);
+       max77843_haptic_disable(haptic);
+
+       error = regulator_disable(haptic->motor_reg);
+       if (error)
+               dev_err(haptic->dev,
+                       "failed to disable regulator: %d\n", error);
+
+       max77843_haptic_bias(haptic, false);
+}
+
+static int max77843_haptic_probe(struct platform_device *pdev)
+{
+       struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent);
+       struct max77843_haptic *haptic;
+       int error;
+
+       haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
+       if (!haptic)
+               return -ENOMEM;
+
+       haptic->regmap_haptic = max77843->regmap;
+       haptic->dev = &pdev->dev;
+       haptic->type = MAX77843_HAPTIC_LRA;
+       haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128;
+
+       INIT_WORK(&haptic->work, max77843_haptic_play_work);
+       mutex_init(&haptic->mutex);
+
+       haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL);
+       if (IS_ERR(haptic->pwm_dev)) {
+               dev_err(&pdev->dev, "failed to get pwm device\n");
+               return PTR_ERR(haptic->pwm_dev);
+       }
+
+       haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic");
+       if (IS_ERR(haptic->motor_reg)) {
+               dev_err(&pdev->dev, "failed to get regulator\n");
+               return PTR_ERR(haptic->motor_reg);
+       }
+
+       haptic->input_dev = devm_input_allocate_device(&pdev->dev);
+       if (!haptic->input_dev) {
+               dev_err(&pdev->dev, "failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       haptic->input_dev->name = "max77843-haptic";
+       haptic->input_dev->id.version = 1;
+       haptic->input_dev->dev.parent = &pdev->dev;
+       haptic->input_dev->open = max77843_haptic_open;
+       haptic->input_dev->close = max77843_haptic_close;
+       input_set_drvdata(haptic->input_dev, haptic);
+       input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
+
+       error = input_ff_create_memless(haptic->input_dev, NULL,
+                       max77843_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;
+       }
+
+       platform_set_drvdata(pdev, haptic);
+
+       return 0;
+}
+
+static int __maybe_unused max77843_haptic_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct max77843_haptic *haptic = platform_get_drvdata(pdev);
+       int error;
+
+       error = mutex_lock_interruptible(&haptic->mutex);
+       if (error)
+               return error;
+
+       max77843_haptic_disable(haptic);
+
+       haptic->suspended = true;
+
+       mutex_unlock(&haptic->mutex);
+
+       return 0;
+}
+
+static int __maybe_unused max77843_haptic_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct max77843_haptic *haptic = platform_get_drvdata(pdev);
+       unsigned int magnitude;
+
+       mutex_lock(&haptic->mutex);
+
+       haptic->suspended = false;
+
+       magnitude = ACCESS_ONCE(haptic->magnitude);
+       if (magnitude)
+               max77843_haptic_enable(haptic);
+
+       mutex_unlock(&haptic->mutex);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops,
+               max77843_haptic_suspend, max77843_haptic_resume);
+
+static struct platform_driver max77843_haptic_driver = {
+       .driver         = {
+               .name   = "max77843-haptic",
+               .pm     = &max77843_haptic_pm_ops,
+       },
+       .probe          = max77843_haptic_probe,
+};
+module_platform_driver(max77843_haptic_driver);
+
+MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
+MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver");
+MODULE_LICENSE("GPL");
index 98228773a1118bfd448ec0143c4b572a534af499..19c73574458e8201bc8c36d3b083e241cf1fc841 100644 (file)
@@ -174,12 +174,13 @@ static int mma8450_probe(struct i2c_client *c,
        struct mma8450 *m;
        int err;
 
-       m = kzalloc(sizeof(struct mma8450), GFP_KERNEL);
-       idev = input_allocate_polled_device();
-       if (!m || !idev) {
-               err = -ENOMEM;
-               goto err_free_mem;
-       }
+       m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL);
+       if (!m)
+               return -ENOMEM;
+
+       idev = devm_input_allocate_polled_device(&c->dev);
+       if (!idev)
+               return -ENOMEM;
 
        m->client = c;
        m->idev = idev;
@@ -187,7 +188,6 @@ static int mma8450_probe(struct i2c_client *c,
        idev->private           = m;
        idev->input->name       = MMA8450_DRV_NAME;
        idev->input->id.bustype = BUS_I2C;
-       idev->input->dev.parent = &c->dev;
        idev->poll              = mma8450_poll;
        idev->poll_interval     = POLL_INTERVAL;
        idev->poll_interval_max = POLL_INTERVAL_MAX;
@@ -202,28 +202,11 @@ static int mma8450_probe(struct i2c_client *c,
        err = input_register_polled_device(idev);
        if (err) {
                dev_err(&c->dev, "failed to register polled input device\n");
-               goto err_free_mem;
+               return err;
        }
 
        i2c_set_clientdata(c, m);
 
-       return 0;
-
-err_free_mem:
-       input_free_polled_device(idev);
-       kfree(m);
-       return err;
-}
-
-static int mma8450_remove(struct i2c_client *c)
-{
-       struct mma8450 *m = i2c_get_clientdata(c);
-       struct input_polled_dev *idev = m->idev;
-
-       input_unregister_polled_device(idev);
-       input_free_polled_device(idev);
-       kfree(m);
-
        return 0;
 }
 
@@ -242,11 +225,9 @@ MODULE_DEVICE_TABLE(of, mma8450_dt_ids);
 static struct i2c_driver mma8450_driver = {
        .driver = {
                .name   = MMA8450_DRV_NAME,
-               .owner  = THIS_MODULE,
                .of_match_table = mma8450_dt_ids,
        },
        .probe          = mma8450_probe,
-       .remove         = mma8450_remove,
        .id_table       = mma8450_id,
 };
 
index 1f9b5ee927460c74f8c1dc49ee9d9dc0755d3f50..1e1baed63929084f96fe79f47f7b0d8c440960e9 100644 (file)
@@ -304,7 +304,7 @@ static SIMPLE_DEV_PM_OPS(palmas_pwron_pm,
                         palmas_pwron_suspend, palmas_pwron_resume);
 
 #ifdef CONFIG_OF
-static struct of_device_id of_palmas_pwr_match[] = {
+static const struct of_device_id of_palmas_pwr_match[] = {
        { .compatible = "ti,palmas-pwrbutton" },
        { },
 };
diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c
new file mode 100644 (file)
index 0000000..867db8a
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2014, Sony Mobile Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+
+#define PON_REV2                       0x01
+
+#define PON_RT_STS                     0x10
+#define  PON_KPDPWR_N_SET              BIT(0)
+
+#define PON_PS_HOLD_RST_CTL            0x5a
+#define PON_PS_HOLD_RST_CTL2           0x5b
+#define  PON_PS_HOLD_ENABLE            BIT(7)
+#define  PON_PS_HOLD_TYPE_MASK         0x0f
+#define  PON_PS_HOLD_TYPE_SHUTDOWN     4
+#define  PON_PS_HOLD_TYPE_HARD_RESET   7
+
+#define PON_PULL_CTL                   0x70
+#define  PON_KPDPWR_PULL_UP            BIT(1)
+
+#define PON_DBC_CTL                    0x71
+#define  PON_DBC_DELAY_MASK            0x7
+
+
+struct pm8941_pwrkey {
+       struct device *dev;
+       int irq;
+       u32 baseaddr;
+       struct regmap *regmap;
+       struct input_dev *input;
+
+       unsigned int revision;
+       struct notifier_block reboot_notifier;
+};
+
+static int pm8941_reboot_notify(struct notifier_block *nb,
+                               unsigned long code, void *unused)
+{
+       struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey,
+                                                   reboot_notifier);
+       unsigned int enable_reg;
+       unsigned int reset_type;
+       int error;
+
+       /* PMICs with revision 0 have the enable bit in same register as ctrl */
+       if (pwrkey->revision == 0)
+               enable_reg = PON_PS_HOLD_RST_CTL;
+       else
+               enable_reg = PON_PS_HOLD_RST_CTL2;
+
+       error = regmap_update_bits(pwrkey->regmap,
+                                  pwrkey->baseaddr + enable_reg,
+                                  PON_PS_HOLD_ENABLE,
+                                  0);
+       if (error)
+               dev_err(pwrkey->dev,
+                       "unable to clear ps hold reset enable: %d\n",
+                       error);
+
+       /*
+        * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between
+        * writes.
+        */
+       usleep_range(100, 1000);
+
+       switch (code) {
+       case SYS_HALT:
+       case SYS_POWER_OFF:
+               reset_type = PON_PS_HOLD_TYPE_SHUTDOWN;
+               break;
+       case SYS_RESTART:
+       default:
+               reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
+               break;
+       };
+
+       error = regmap_update_bits(pwrkey->regmap,
+                                  pwrkey->baseaddr + PON_PS_HOLD_RST_CTL,
+                                  PON_PS_HOLD_TYPE_MASK,
+                                  reset_type);
+       if (error)
+               dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n",
+                       error);
+
+       error = regmap_update_bits(pwrkey->regmap,
+                                  pwrkey->baseaddr + enable_reg,
+                                  PON_PS_HOLD_ENABLE,
+                                  PON_PS_HOLD_ENABLE);
+       if (error)
+               dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error);
+
+       return NOTIFY_DONE;
+}
+
+static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
+{
+       struct pm8941_pwrkey *pwrkey = _data;
+       unsigned int sts;
+       int error;
+
+       error = regmap_read(pwrkey->regmap,
+                           pwrkey->baseaddr + PON_RT_STS, &sts);
+       if (error)
+               return IRQ_HANDLED;
+
+       input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET));
+       input_sync(pwrkey->input);
+
+       return IRQ_HANDLED;
+}
+
+static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
+{
+       struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(pwrkey->irq);
+
+       return 0;
+}
+
+static int __maybe_unused pm8941_pwrkey_resume(struct device *dev)
+{
+       struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(pwrkey->irq);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
+                        pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
+
+static int pm8941_pwrkey_probe(struct platform_device *pdev)
+{
+       struct pm8941_pwrkey *pwrkey;
+       bool pull_up;
+       u32 req_delay;
+       int error;
+
+       if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
+               req_delay = 15625;
+
+       if (req_delay > 2000000 || req_delay == 0) {
+               dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay);
+               return -EINVAL;
+       }
+
+       pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up");
+
+       pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
+       if (!pwrkey)
+               return -ENOMEM;
+
+       pwrkey->dev = &pdev->dev;
+
+       pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!pwrkey->regmap) {
+               dev_err(&pdev->dev, "failed to locate regmap\n");
+               return -ENODEV;
+       }
+
+       pwrkey->irq = platform_get_irq(pdev, 0);
+       if (pwrkey->irq < 0) {
+               dev_err(&pdev->dev, "failed to get irq\n");
+               return pwrkey->irq;
+       }
+
+       error = of_property_read_u32(pdev->dev.of_node, "reg",
+                                    &pwrkey->baseaddr);
+       if (error)
+               return error;
+
+       error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
+                           &pwrkey->revision);
+       if (error) {
+               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+               return error;
+       }
+
+       pwrkey->input = devm_input_allocate_device(&pdev->dev);
+       if (!pwrkey->input) {
+               dev_dbg(&pdev->dev, "unable to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       input_set_capability(pwrkey->input, EV_KEY, KEY_POWER);
+
+       pwrkey->input->name = "pm8941_pwrkey";
+       pwrkey->input->phys = "pm8941_pwrkey/input0";
+
+       req_delay = (req_delay << 6) / USEC_PER_SEC;
+       req_delay = ilog2(req_delay);
+
+       error = regmap_update_bits(pwrkey->regmap,
+                                  pwrkey->baseaddr + PON_DBC_CTL,
+                                  PON_DBC_DELAY_MASK,
+                                  req_delay);
+       if (error) {
+               dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
+               return error;
+       }
+
+       error = regmap_update_bits(pwrkey->regmap,
+                                  pwrkey->baseaddr + PON_PULL_CTL,
+                                  PON_KPDPWR_PULL_UP,
+                                  pull_up ? PON_KPDPWR_PULL_UP : 0);
+       if (error) {
+               dev_err(&pdev->dev, "failed to set pull: %d\n", error);
+               return error;
+       }
+
+       error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
+                                         NULL, pm8941_pwrkey_irq,
+                                         IRQF_ONESHOT,
+                                         "pm8941_pwrkey", pwrkey);
+       if (error) {
+               dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
+               return error;
+       }
+
+       error = input_register_device(pwrkey->input);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register input device: %d\n",
+                       error);
+               return error;
+       }
+
+       pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify,
+       error = register_reboot_notifier(&pwrkey->reboot_notifier);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
+                       error);
+               return error;
+       }
+
+       platform_set_drvdata(pdev, pwrkey);
+       device_init_wakeup(&pdev->dev, 1);
+
+       return 0;
+}
+
+static int pm8941_pwrkey_remove(struct platform_device *pdev)
+{
+       struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
+
+       device_init_wakeup(&pdev->dev, 0);
+       unregister_reboot_notifier(&pwrkey->reboot_notifier);
+
+       return 0;
+}
+
+static const struct of_device_id pm8941_pwr_key_id_table[] = {
+       { .compatible = "qcom,pm8941-pwrkey" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
+
+static struct platform_driver pm8941_pwrkey_driver = {
+       .probe = pm8941_pwrkey_probe,
+       .remove = pm8941_pwrkey_remove,
+       .driver = {
+               .name = "pm8941-pwrkey",
+               .pm = &pm8941_pwr_key_pm_ops,
+               .of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
+       },
+};
+module_platform_driver(pm8941_pwrkey_driver);
+
+MODULE_DESCRIPTION("PM8941 Power Key driver");
+MODULE_LICENSE("GPL v2");
index a28ee70ff1585601a3bc34da5028d799e8674191..e82edf810d1f3e6354b992f852ddbe8f433cd0b3 100644 (file)
@@ -50,7 +50,6 @@ static int pwm_beeper_event(struct input_dev *input,
        }
 
        if (value == 0) {
-               pwm_config(beeper->pwm, 0, 0);
                pwm_disable(beeper->pwm);
        } else {
                period = HZ_TO_NANOSECONDS(value);
@@ -169,12 +168,6 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
                         pwm_beeper_suspend, pwm_beeper_resume);
 
-#ifdef CONFIG_PM_SLEEP
-#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
-#else
-#define PWM_BEEPER_PM_OPS NULL
-#endif
-
 #ifdef CONFIG_OF
 static const struct of_device_id pwm_beeper_match[] = {
        { .compatible = "pwm-beeper", },
@@ -187,7 +180,7 @@ static struct platform_driver pwm_beeper_driver = {
        .remove = pwm_beeper_remove,
        .driver = {
                .name   = "pwm-beeper",
-               .pm     = PWM_BEEPER_PM_OPS,
+               .pm     = &pwm_beeper_pm_ops,
                .of_match_table = of_match_ptr(pwm_beeper_match),
        },
 };
index 132eb914ea3e0e9883a89dc04470217631270c04..6bf3f1082f71ea6c6802fe7e4b73a4c5378df120 100644 (file)
@@ -245,7 +245,7 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,
                regulator_haptic_suspend, regulator_haptic_resume);
 
-static struct of_device_id regulator_haptic_dt_match[] = {
+static const struct of_device_id regulator_haptic_dt_match[] = {
        { .compatible = "regulator-haptic" },
        { /* sentinel */ },
 };
index 54508dec4eb308e74579b04f73c201a257307372..a39b62651a4b334d452fd2105848a8a0e7add473 100644 (file)
@@ -106,7 +106,7 @@ static int tps65218_pwron_probe(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id of_tps65218_pwr_match[] = {
+static const struct of_device_id of_tps65218_pwr_match[] = {
        { .compatible = "ti,tps65218-pwrbutton" },
        { },
 };
index 27bcdbc950c9fc2df9067504cfe055bf379eee78..e6708f6efb4db7189dccdc1ed1fc0b3ca117a6ef 100644 (file)
@@ -153,10 +153,18 @@ static const struct alps_protocol_info alps_v7_protocol_data = {
        ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT
 };
 
+static const struct alps_protocol_info alps_v8_protocol_data = {
+       ALPS_PROTO_V8, 0x18, 0x18, 0
+};
+
 static void alps_set_abs_params_st(struct alps_data *priv,
                                   struct input_dev *dev1);
 static void alps_set_abs_params_mt(struct alps_data *priv,
                                   struct input_dev *dev1);
+static void alps_set_abs_params_v7(struct alps_data *priv,
+                                  struct input_dev *dev1);
+static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
+                                      struct input_dev *dev1);
 
 /* Packet formats are described in Documentation/input/alps.txt */
 
@@ -243,6 +251,14 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
                return;
        }
 
+       /* Non interleaved V2 dualpoint has separate stick button bits */
+       if (priv->proto_version == ALPS_PROTO_V2 &&
+           priv->flags == (ALPS_PASS | ALPS_DUALPOINT)) {
+               left |= packet[0] & 1;
+               right |= packet[0] & 2;
+               middle |= packet[0] & 4;
+       }
+
        alps_report_buttons(dev, dev2, left, right, middle);
 
        /* Convert hardware tap to a reasonable Z value */
@@ -1085,6 +1101,176 @@ static void alps_process_packet_v7(struct psmouse *psmouse)
                alps_process_touchpad_packet_v7(psmouse);
 }
 
+static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte)
+{
+       unsigned char pkt_id = SS4_PACKET_ID_IDLE;
+
+       if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
+           (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) {
+               pkt_id = SS4_PACKET_ID_IDLE;
+       } else if (!(byte[3] & 0x10)) {
+               pkt_id = SS4_PACKET_ID_ONE;
+       } else if (!(byte[3] & 0x20)) {
+               pkt_id = SS4_PACKET_ID_TWO;
+       } else {
+               pkt_id = SS4_PACKET_ID_MULTI;
+       }
+
+       return pkt_id;
+}
+
+static int alps_decode_ss4_v2(struct alps_fields *f,
+                             unsigned char *p, struct psmouse *psmouse)
+{
+       struct alps_data *priv = psmouse->private;
+       unsigned char pkt_id;
+       unsigned int no_data_x, no_data_y;
+
+       pkt_id = alps_get_pkt_id_ss4_v2(p);
+
+       /* Current packet is 1Finger coordinate packet */
+       switch (pkt_id) {
+       case SS4_PACKET_ID_ONE:
+               f->mt[0].x = SS4_1F_X_V2(p);
+               f->mt[0].y = SS4_1F_Y_V2(p);
+               f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;
+               f->fingers = 1;
+               f->first_mp = 0;
+               f->is_mp = 0;
+               break;
+
+       case SS4_PACKET_ID_TWO:
+               if (priv->flags & ALPS_BUTTONPAD) {
+                       f->mt[0].x = SS4_BTL_MF_X_V2(p, 0);
+                       f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0);
+                       f->mt[1].x = SS4_BTL_MF_X_V2(p, 1);
+                       f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1);
+               } else {
+                       f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
+                       f->mt[0].y = SS4_STD_MF_Y_V2(p, 0);
+                       f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
+                       f->mt[1].y = SS4_STD_MF_Y_V2(p, 1);
+               }
+               f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0;
+
+               if (SS4_IS_MF_CONTINUE(p)) {
+                       f->first_mp = 1;
+               } else {
+                       f->fingers = 2;
+                       f->first_mp = 0;
+               }
+               f->is_mp = 0;
+
+               break;
+
+       case SS4_PACKET_ID_MULTI:
+               if (priv->flags & ALPS_BUTTONPAD) {
+                       f->mt[2].x = SS4_BTL_MF_X_V2(p, 0);
+                       f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0);
+                       f->mt[3].x = SS4_BTL_MF_X_V2(p, 1);
+                       f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1);
+                       no_data_x = SS4_MFPACKET_NO_AX_BL;
+                       no_data_y = SS4_MFPACKET_NO_AY_BL;
+               } else {
+                       f->mt[2].x = SS4_STD_MF_X_V2(p, 0);
+                       f->mt[2].y = SS4_STD_MF_Y_V2(p, 0);
+                       f->mt[3].x = SS4_STD_MF_X_V2(p, 1);
+                       f->mt[3].y = SS4_STD_MF_Y_V2(p, 1);
+                       no_data_x = SS4_MFPACKET_NO_AX;
+                       no_data_y = SS4_MFPACKET_NO_AY;
+               }
+
+               f->first_mp = 0;
+               f->is_mp = 1;
+
+               if (SS4_IS_5F_DETECTED(p)) {
+                       f->fingers = 5;
+               } else if (f->mt[3].x == no_data_x &&
+                            f->mt[3].y == no_data_y) {
+                       f->mt[3].x = 0;
+                       f->mt[3].y = 0;
+                       f->fingers = 3;
+               } else {
+                       f->fingers = 4;
+               }
+               break;
+
+       case SS4_PACKET_ID_IDLE:
+       default:
+               memset(f, 0, sizeof(struct alps_fields));
+               break;
+       }
+
+       f->left = !!(SS4_BTN_V2(p) & 0x01);
+       if (!(priv->flags & ALPS_BUTTONPAD)) {
+               f->right = !!(SS4_BTN_V2(p) & 0x02);
+               f->middle = !!(SS4_BTN_V2(p) & 0x04);
+       }
+
+       return 0;
+}
+
+static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
+{
+       struct alps_data *priv = psmouse->private;
+       unsigned char *packet = psmouse->packet;
+       struct input_dev *dev = psmouse->dev;
+       struct alps_fields *f = &priv->f;
+
+       memset(f, 0, sizeof(struct alps_fields));
+       priv->decode_fields(f, packet, psmouse);
+       if (priv->multi_packet) {
+               /*
+                * Sometimes the first packet will indicate a multi-packet
+                * sequence, but sometimes the next multi-packet would not
+                * come. Check for this, and when it happens process the
+                * position packet as usual.
+                */
+               if (f->is_mp) {
+                       /* Now process the 1st packet */
+                       priv->decode_fields(f, priv->multi_data, psmouse);
+               } else {
+                       priv->multi_packet = 0;
+               }
+       }
+
+       /*
+        * "f.is_mp" would always be '0' after merging the 1st and 2nd packet.
+        * When it is set, it means 2nd packet comes without 1st packet come.
+        */
+       if (f->is_mp)
+               return;
+
+       /* Save the first packet */
+       if (!priv->multi_packet && f->first_mp) {
+               priv->multi_packet = 1;
+               memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
+               return;
+       }
+
+       priv->multi_packet = 0;
+
+       alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4);
+
+       input_mt_report_finger_count(dev, f->fingers);
+
+       input_report_key(dev, BTN_LEFT, f->left);
+       input_report_key(dev, BTN_RIGHT, f->right);
+       input_report_key(dev, BTN_MIDDLE, f->middle);
+
+       input_report_abs(dev, ABS_PRESSURE, f->pressure);
+       input_sync(dev);
+}
+
+static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
+{
+       if (psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08))
+               return false;
+       if (psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0))
+               return false;
+       return true;
+}
+
 static DEFINE_MUTEX(alps_mutex);
 
 static void alps_register_bare_ps2_mouse(struct work_struct *work)
@@ -1159,13 +1345,14 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
                                        bool report_buttons)
 {
        struct alps_data *priv = psmouse->private;
-       struct input_dev *dev;
+       struct input_dev *dev, *dev2 = NULL;
 
        /* Figure out which device to use to report the bare packet */
        if (priv->proto_version == ALPS_PROTO_V2 &&
            (priv->flags & ALPS_DUALPOINT)) {
                /* On V2 devices the DualPoint Stick reports bare packets */
                dev = priv->dev2;
+               dev2 = psmouse->dev;
        } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) {
                /* Register dev3 mouse if we received PS/2 packet first time */
                if (!IS_ERR(priv->dev3))
@@ -1177,7 +1364,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
        }
 
        if (report_buttons)
-               alps_report_buttons(dev, NULL,
+               alps_report_buttons(dev, dev2,
                                packet[0] & 1, packet[0] & 2, packet[0] & 4);
 
        input_report_rel(dev, REL_X,
@@ -1305,8 +1492,12 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
         * a device connected to the external PS/2 port. Because bare PS/2
         * protocol does not have enough constant bits to self-synchronize
         * properly we only do this if the device is fully synchronized.
+        * Can not distinguish V8's first byte from PS/2 packet's
         */
-       if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
+       if (priv->proto_version != ALPS_PROTO_V8 &&
+           !psmouse->out_of_sync_cnt &&
+           (psmouse->packet[0] & 0xc8) == 0x08) {
+
                if (psmouse->pktcnt == 3) {
                        alps_report_bare_ps2_packet(psmouse, psmouse->packet,
                                                    true);
@@ -1354,8 +1545,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
                return PSMOUSE_BAD_DATA;
        }
 
-       if (priv->proto_version == ALPS_PROTO_V7 &&
-           !alps_is_valid_package_v7(psmouse)) {
+       if ((priv->proto_version == ALPS_PROTO_V7 &&
+                       !alps_is_valid_package_v7(psmouse)) ||
+           (priv->proto_version == ALPS_PROTO_V8 &&
+                       !alps_is_valid_package_ss4_v2(psmouse))) {
                psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
                            psmouse->pktcnt - 1,
                            psmouse->packet[psmouse->pktcnt - 1]);
@@ -2130,6 +2323,88 @@ error:
        return -1;
 }
 
+static int alps_get_otp_values_ss4_v2(struct psmouse *psmouse,
+                                     unsigned char index, unsigned char otp[])
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+       switch (index) {
+       case 0:
+               if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
+                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)  ||
+                   ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
+                       return -1;
+
+               break;
+
+       case 1:
+               if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
+                   ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL)  ||
+                   ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO))
+                       return -1;
+
+               break;
+       }
+
+       return 0;
+}
+
+static int alps_update_device_area_ss4_v2(unsigned char otp[][4],
+                                         struct alps_data *priv)
+{
+       int num_x_electrode;
+       int num_y_electrode;
+       int x_pitch, y_pitch, x_phys, y_phys;
+
+       num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F);
+       num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x0F);
+
+       priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+       priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE;
+
+       x_pitch = ((otp[1][2] >> 2) & 0x07) + SS4_MIN_PITCH_MM;
+       y_pitch = ((otp[1][2] >> 5) & 0x07) + SS4_MIN_PITCH_MM;
+
+       x_phys = x_pitch * (num_x_electrode - 1); /* In 0.1 mm units */
+       y_phys = y_pitch * (num_y_electrode - 1); /* In 0.1 mm units */
+
+       priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
+       priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
+
+       return 0;
+}
+
+static int alps_update_btn_info_ss4_v2(unsigned char otp[][4],
+                                      struct alps_data *priv)
+{
+       unsigned char is_btnless;
+
+       is_btnless = (otp[1][1] >> 3) & 0x01;
+
+       if (is_btnless)
+               priv->flags |= ALPS_BUTTONPAD;
+
+       return 0;
+}
+
+static int alps_set_defaults_ss4_v2(struct psmouse *psmouse,
+                                   struct alps_data *priv)
+{
+       unsigned char otp[2][4];
+
+       memset(otp, 0, sizeof(otp));
+
+       if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) ||
+           alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0]))
+               return -1;
+
+       alps_update_device_area_ss4_v2(otp, priv);
+
+       alps_update_btn_info_ss4_v2(otp, priv);
+
+       return 0;
+}
+
 static int alps_dolphin_get_device_area(struct psmouse *psmouse,
                                        struct alps_data *priv)
 {
@@ -2222,6 +2497,35 @@ error:
        return ret;
 }
 
+static int alps_hw_init_ss4_v2(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       char param[2] = {0x64, 0x28};
+       int ret = -1;
+
+       /* enter absolute mode */
+       if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) ||
+           ps2_command(ps2dev, &param[0], PSMOUSE_CMD_SETRATE) ||
+           ps2_command(ps2dev, &param[1], PSMOUSE_CMD_SETRATE)) {
+               goto error;
+       }
+
+       /* T.B.D. Decread noise packet number, delete in the future */
+       if (alps_exit_command_mode(psmouse) ||
+           alps_enter_command_mode(psmouse) ||
+           alps_command_mode_write_reg(psmouse, 0x001D, 0x20)) {
+               goto error;
+       }
+       alps_exit_command_mode(psmouse);
+
+       return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+error:
+       alps_exit_command_mode(psmouse);
+       return ret;
+}
+
 static int alps_set_protocol(struct psmouse *psmouse,
                             struct alps_data *priv,
                             const struct alps_protocol_info *protocol)
@@ -2311,7 +2615,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->hw_init = alps_hw_init_v7;
                priv->process_packet = alps_process_packet_v7;
                priv->decode_fields = alps_decode_packet_v7;
-               priv->set_abs_params = alps_set_abs_params_mt;
+               priv->set_abs_params = alps_set_abs_params_v7;
                priv->nibble_commands = alps_v3_nibble_commands;
                priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
                priv->x_max = 0xfff;
@@ -2321,6 +2625,19 @@ static int alps_set_protocol(struct psmouse *psmouse,
                        priv->flags |= ALPS_BUTTONPAD;
 
                break;
+
+       case ALPS_PROTO_V8:
+               priv->hw_init = alps_hw_init_ss4_v2;
+               priv->process_packet = alps_process_packet_ss4_v2;
+               priv->decode_fields = alps_decode_ss4_v2;
+               priv->set_abs_params = alps_set_abs_params_ss4_v2;
+               priv->nibble_commands = alps_v3_nibble_commands;
+               priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+
+               if (alps_set_defaults_ss4_v2(psmouse, priv))
+                       return -EIO;
+
+               break;
        }
 
        return 0;
@@ -2389,6 +2706,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
                } else if (ec[0] == 0x88 && ec[1] == 0x07 &&
                           ec[2] >= 0x90 && ec[2] <= 0x9d) {
                        protocol = &alps_v3_protocol_data;
+               } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
+                          e7[2] == 0x14 && ec[1] == 0x02) {
+                       protocol = &alps_v8_protocol_data;
                } else {
                        psmouse_dbg(psmouse,
                                    "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
@@ -2437,10 +2757,11 @@ static void alps_set_abs_params_st(struct alps_data *priv,
 {
        input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
        input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
+       input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
 }
 
-static void alps_set_abs_params_mt(struct alps_data *priv,
-                                  struct input_dev *dev1)
+static void alps_set_abs_params_mt_common(struct alps_data *priv,
+                                         struct input_dev *dev1)
 {
        input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
        input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
@@ -2448,15 +2769,44 @@ static void alps_set_abs_params_mt(struct alps_data *priv,
        input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
        input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
 
-       input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER |
-               INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
-
        set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
        set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
+}
+
+static void alps_set_abs_params_mt(struct alps_data *priv,
+                                  struct input_dev *dev1)
+{
+       alps_set_abs_params_mt_common(priv, dev1);
+       input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+
+       input_mt_init_slots(dev1, MAX_TOUCHES,
+                           INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+                               INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
+}
+
+static void alps_set_abs_params_v7(struct alps_data *priv,
+                                  struct input_dev *dev1)
+{
+       alps_set_abs_params_mt_common(priv, dev1);
+       set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
+
+       input_mt_init_slots(dev1, MAX_TOUCHES,
+                           INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+                               INPUT_MT_TRACK);
+
+       set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
+}
+
+static void alps_set_abs_params_ss4_v2(struct alps_data *priv,
+                                      struct input_dev *dev1)
+{
+       alps_set_abs_params_mt_common(priv, dev1);
+       input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+       set_bit(BTN_TOOL_QUINTTAP, dev1->keybit);
 
-       /* V7 is real multi-touch */
-       if (priv->proto_version == ALPS_PROTO_V7)
-               clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
+       input_mt_init_slots(dev1, MAX_TOUCHES,
+                           INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+                               INPUT_MT_TRACK);
 }
 
 int alps_init(struct psmouse *psmouse)
@@ -2489,9 +2839,6 @@ int alps_init(struct psmouse *psmouse)
        dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
 
        priv->set_abs_params(priv, dev1);
-       /* No pressure on V7 */
-       if (priv->proto_version != ALPS_PROTO_V7)
-               input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
 
        if (priv->flags & ALPS_WHEEL) {
                dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
index 02513c0502fc1309a9f8285ea2f4e3237fd7464a..6dfdccc3a7c6e892ab1379d8597d5cc1e84bc19c 100644 (file)
 #define ALPS_PROTO_V5          0x500
 #define ALPS_PROTO_V6          0x600
 #define ALPS_PROTO_V7          0x700   /* t3btl t4s */
+#define ALPS_PROTO_V8          0x800   /* SS4btl SS4s */
 
-#define MAX_TOUCHES    2
+#define MAX_TOUCHES    4
 
 #define DOLPHIN_COUNT_PER_ELECTRODE    64
 #define DOLPHIN_PROFILE_XOFFSET                8       /* x-electrode offset */
 #define DOLPHIN_PROFILE_YOFFSET                1       /* y-electrode offset */
 
+/*
+ * enum SS4_PACKET_ID - defines the packet type for V8
+ * SS4_PACKET_ID_IDLE: There's no finger and no button activity.
+ * SS4_PACKET_ID_ONE: There's one finger on touchpad
+ *  or there's button activities.
+ * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
+ * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
+*/
+enum SS4_PACKET_ID {
+       SS4_PACKET_ID_IDLE = 0,
+       SS4_PACKET_ID_ONE,
+       SS4_PACKET_ID_TWO,
+       SS4_PACKET_ID_MULTI,
+};
+
+#define SS4_COUNT_PER_ELECTRODE                256
+#define SS4_NUMSENSOR_XOFFSET          7
+#define SS4_NUMSENSOR_YOFFSET          7
+#define SS4_MIN_PITCH_MM               50
+
+#define SS4_MASK_NORMAL_BUTTONS                0x07
+
+#define SS4_1F_X_V2(_b)                ((_b[0] & 0x0007) |             \
+                                ((_b[1] << 3) & 0x0078) |      \
+                                ((_b[1] << 2) & 0x0380) |      \
+                                ((_b[2] << 5) & 0x1C00)        \
+                               )
+
+#define SS4_1F_Y_V2(_b)                (((_b[2]) & 0x000F) |           \
+                                ((_b[3] >> 2) & 0x0030) |      \
+                                ((_b[4] << 6) & 0x03C0) |      \
+                                ((_b[4] << 5) & 0x0C00)        \
+                               )
+
+#define SS4_1F_Z_V2(_b)                (((_b[5]) & 0x0F) |             \
+                                ((_b[5] >> 1) & 0x70) |        \
+                                ((_b[4]) & 0x80)               \
+                               )
+
+#define SS4_1F_LFB_V2(_b)      (((_b[2] >> 4) & 0x01) == 0x01)
+
+#define SS4_MF_LF_V2(_b, _i)   ((_b[1 + (_i) * 3] & 0x0004) == 0x0004)
+
+#define SS4_BTN_V2(_b)         ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS)
+
+#define SS4_STD_MF_X_V2(_b, _i)        (((_b[0 + (_i) * 3] << 5) & 0x00E0) |   \
+                                ((_b[1 + _i * 3]  << 5) & 0x1F00)      \
+                               )
+
+#define SS4_STD_MF_Y_V2(_b, _i)        (((_b[1 + (_i) * 3] << 3) & 0x0010) |   \
+                                ((_b[2 + (_i) * 3] << 5) & 0x01E0) |   \
+                                ((_b[2 + (_i) * 3] << 4) & 0x0E00)     \
+                               )
+
+#define SS4_BTL_MF_X_V2(_b, _i)        (SS4_STD_MF_X_V2(_b, _i) |              \
+                                ((_b[0 + (_i) * 3] >> 3) & 0x0010)     \
+                               )
+
+#define SS4_BTL_MF_Y_V2(_b, _i)        (SS4_STD_MF_Y_V2(_b, _i) | \
+                                ((_b[0 + (_i) * 3] >> 3) & 0x0008)     \
+                               )
+
+#define SS4_MF_Z_V2(_b, _i)    (((_b[1 + (_i) * 3]) & 0x0001) |        \
+                                ((_b[1 + (_i) * 3] >> 1) & 0x0002)     \
+                               )
+
+#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10)
+#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10)
+
+
+#define SS4_MFPACKET_NO_AX     8160    /* X-Coordinate value */
+#define SS4_MFPACKET_NO_AY     4080    /* Y-Coordinate value */
+#define SS4_MFPACKET_NO_AX_BL  8176    /* Buttonless X-Coordinate value */
+#define SS4_MFPACKET_NO_AY_BL  4088    /* Buttonless Y-Coordinate value */
+
 /*
  * enum V7_PACKET_ID - defines the packet type for V7
  * V7_PACKET_ID_IDLE: There's no finger and no button activity.
index e100c1b31597d7efe67ae358983e21c5dd234fb8..9b2dc015f20c8a24d65bb1019a2e39e8e1bf1279 100644 (file)
@@ -17,7 +17,7 @@
  */
 
 #ifndef _ELAN_I2C_H
-#define _ELAN_i2C_H
+#define _ELAN_I2C_H
 
 #include <linux/types.h>
 
index 7ce8bfe22d7eeb2672804b2303692635df5be9fc..375d98f47483d309f35a561de11a376982713b17 100644 (file)
@@ -99,7 +99,7 @@ static int elan_enable_power(struct elan_tp_data *data)
        error = regulator_enable(data->vcc);
        if (error) {
                dev_err(&data->client->dev,
-                       "Failed to enable regulator: %d\n", error);
+                       "failed to enable regulator: %d\n", error);
                return error;
        }
 
@@ -111,6 +111,7 @@ static int elan_enable_power(struct elan_tp_data *data)
                msleep(30);
        } while (--repeat > 0);
 
+       dev_err(&data->client->dev, "failed to enable power: %d\n", error);
        return error;
 }
 
@@ -125,7 +126,7 @@ static int elan_disable_power(struct elan_tp_data *data)
                        error = regulator_disable(data->vcc);
                        if (error) {
                                dev_err(&data->client->dev,
-                                       "Failed to disable regulator: %d\n",
+                                       "failed to disable regulator: %d\n",
                                        error);
                                /* Attempt to power the chip back up */
                                data->ops->power_control(data->client, true);
@@ -138,6 +139,7 @@ static int elan_disable_power(struct elan_tp_data *data)
                msleep(30);
        } while (--repeat > 0);
 
+       dev_err(&data->client->dev, "failed to disable power: %d\n", error);
        return error;
 }
 
@@ -196,7 +198,6 @@ static int elan_initialize(struct elan_tp_data *data)
                if (!error)
                        return 0;
 
-               repeat--;
                msleep(30);
        } while (--repeat > 0);
 
@@ -1084,16 +1085,18 @@ static int __maybe_unused elan_resume(struct device *dev)
        }
 
        error = elan_enable_power(data);
-       if (error)
+       if (error) {
                dev_err(dev, "power up when resuming failed: %d\n", error);
+               goto err;
+       }
 
        error = elan_initialize(data);
        if (error)
                dev_err(dev, "initialize when resuming failed: %d\n", error);
 
+err:
        enable_irq(data->client->irq);
-
-       return 0;
+       return error;
 }
 
 static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
index 029941f861afd64a8d6f5c6b1235637163743ae9..6cf0def6d35ee764cb686bb5c9d2f90bb9f81a86 100644 (file)
@@ -117,7 +117,15 @@ static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
        int ret;
 
        ret = i2c_transfer(client->adapter, &msg, 1);
-       return ret == 1 ? 0 : (ret < 0 ? ret : -EIO);
+       if (ret != 1) {
+               if (ret >= 0)
+                       ret = -EIO;
+               dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n",
+                       reg, ret);
+               return ret;
+       }
+
+       return 0;
 }
 
 static int elan_i2c_initialize(struct i2c_client *client)
index 6e22682c8255cfff41e1fc49f6a76198911aafc5..991dc6b20a58594cbc28ee1dfc8f45d677497fe5 100644 (file)
@@ -892,6 +892,21 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
        return PSMOUSE_FULL_PACKET;
 }
 
+/*
+ * This writes the reg_07 value again to the hardware at the end of every
+ * set_rate call because the register loses its value. reg_07 allows setting
+ * absolute mode on v4 hardware
+ */
+static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse,
+               unsigned int rate)
+{
+       struct elantech_data *etd = psmouse->private;
+
+       etd->original_set_rate(psmouse, rate);
+       if (elantech_write_reg(psmouse, 0x07, etd->reg_07))
+               psmouse_err(psmouse, "restoring reg_07 failed\n");
+}
+
 /*
  * Put the touchpad into absolute mode
  */
@@ -1094,6 +1109,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
  * Asus K53SV              0x450f01        78, 15, 0c      2 hw buttons
  * Asus G46VW              0x460f02        00, 18, 0c      2 hw buttons
  * Asus G750JX             0x360f00        00, 16, 0c      2 hw buttons
+ * Asus TP500LN            0x381f17        10, 14, 0e      clickpad
+ * Asus X750JN             0x381f17        10, 14, 0e      clickpad
  * Asus UX31               0x361f00        20, 15, 0e      clickpad
  * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
  * Avatar AVIU-145A2       0x361f00        ?               clickpad
@@ -1635,6 +1652,11 @@ int elantech_init(struct psmouse *psmouse)
                goto init_fail;
        }
 
+       if (etd->fw_version == 0x381f17) {
+               etd->original_set_rate = psmouse->set_rate;
+               psmouse->set_rate = elantech_set_rate_restore_reg_07;
+       }
+
        if (elantech_set_input_params(psmouse)) {
                psmouse_err(psmouse, "failed to query touchpad range.\n");
                goto init_fail;
index 6f3afec02f03ee0cec8b74cbe13bae6a5cba83c5..f965d1569cc338059cdd540bad44ed927c6ddc3e 100644 (file)
@@ -142,6 +142,7 @@ struct elantech_data {
        struct finger_pos mt[ETP_MAX_FINGERS];
        unsigned char parity[256];
        int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
+       void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
 };
 
 #ifdef CONFIG_MOUSE_PS2_ELANTECH
index 23222dd5a66fb146d7b53774818c750050fb6511..e5ed216824e917ed2db576e125d1287a680fc9bf 100644 (file)
@@ -256,8 +256,8 @@ static void lifebook_disconnect(struct psmouse *psmouse)
 
 int lifebook_detect(struct psmouse *psmouse, bool set_properties)
 {
-        if (!lifebook_present)
-                return -1;
+       if (!lifebook_present)
+               return -1;
 
        if (desired_serio_phys &&
            strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
@@ -268,7 +268,7 @@ int lifebook_detect(struct psmouse *psmouse, bool set_properties)
                psmouse->name = "Lifebook TouchScreen";
        }
 
-        return 0;
+       return 0;
 }
 
 static int lifebook_create_relative_device(struct psmouse *psmouse)
index 8bc61237bc1b1c95d43b0d7e3d11e90275aa8d4a..27057df7ba74eeeba06f24f0e24fcf6d4387eda3 100644 (file)
@@ -474,19 +474,45 @@ static int psmouse_poll(struct psmouse *psmouse)
                           PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
 }
 
+static bool psmouse_check_pnp_id(const char *id, const char * const ids[])
+{
+       int i;
+
+       for (i = 0; ids[i]; i++)
+               if (!strcasecmp(id, ids[i]))
+                       return true;
+
+       return false;
+}
+
 /*
  * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids.
  */
 bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
 {
-       int i;
-
-       if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4))
-               for (i = 0; ids[i]; i++)
-                       if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i]))
-                               return true;
+       struct serio *serio = psmouse->ps2dev.serio;
+       char *p, *fw_id_copy, *save_ptr;
+       bool found = false;
+
+       if (strncmp(serio->firmware_id, "PNP: ", 5))
+               return false;
+
+       fw_id_copy = kstrndup(&serio->firmware_id[5],
+                             sizeof(serio->firmware_id) - 5,
+                             GFP_KERNEL);
+       if (!fw_id_copy)
+               return false;
+
+       save_ptr = fw_id_copy;
+       while ((p = strsep(&fw_id_copy, " ")) != NULL) {
+               if (psmouse_check_pnp_id(p, ids)) {
+                       found = true;
+                       break;
+               }
+       }
 
-       return false;
+       kfree(save_ptr);
+       return found;
 }
 
 /*
index 3b06c8a360b661f02ed3aa826e5a996b93e5b358..630af73e98c488a5e266e4ccb6eed5dba622f3d3 100644 (file)
@@ -67,6 +67,9 @@
 #define X_MAX_POSITIVE 8176
 #define Y_MAX_POSITIVE 8176
 
+/* maximum ABS_MT_POSITION displacement (in mm) */
+#define DMAX 10
+
 /*****************************************************************************
  *     Stuff we need even when we do not want native Synaptics support
  ****************************************************************************/
@@ -203,6 +206,13 @@ static const char * const topbuttonpad_pnp_ids[] = {
        NULL
 };
 
+/* This list has been kindly provided by Synaptics. */
+static const char * const forcepad_pnp_ids[] = {
+       "SYN300D",
+       "SYN3014",
+       NULL
+};
+
 /*****************************************************************************
  *     Synaptics communications functions
  ****************************************************************************/
@@ -687,8 +697,6 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[],
        hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits;
 }
 
-static bool is_forcepad;
-
 static int synaptics_parse_hw_state(const unsigned char buf[],
                                    struct synaptics_data *priv,
                                    struct synaptics_hw_state *hw)
@@ -718,7 +726,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
                hw->left  = (buf[0] & 0x01) ? 1 : 0;
                hw->right = (buf[0] & 0x02) ? 1 : 0;
 
-               if (is_forcepad) {
+               if (priv->is_forcepad) {
                        /*
                         * ForcePads, like Clickpads, use middle button
                         * bits to report primary button clicks.
@@ -917,7 +925,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
                pos[i].y = synaptics_invert_y(hw[i]->y);
        }
 
-       input_mt_assign_slots(dev, slot, pos, nsemi, 0);
+       input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
 
        for (i = 0; i < nsemi; i++) {
                input_mt_slot(dev, slot[i]);
@@ -1186,7 +1194,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_TRACK);
+               input_mt_init_slots(dev, 3, INPUT_MT_POINTER | INPUT_MT_TRACK);
 
                /* Image sensors can signal 4 and 5 finger clicks */
                __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
@@ -1418,29 +1426,11 @@ static const struct dmi_system_id __initconst cr48_dmi_table[] = {
        { }
 };
 
-static const struct dmi_system_id forcepad_dmi_table[] __initconst = {
-#if defined(CONFIG_DMI) && defined(CONFIG_X86)
-       {
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook Folio 1040 G1"),
-               },
-       },
-#endif
-       { }
-};
-
 void __init synaptics_module_init(void)
 {
        impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
        broken_olpc_ec = dmi_check_system(olpc_dmi_table);
        cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
-
-       /*
-        * Unfortunately ForcePad capability is not exported over PS/2,
-        * so we have to resort to checking DMI.
-        */
-       is_forcepad = dmi_check_system(forcepad_dmi_table);
 }
 
 static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
@@ -1475,6 +1465,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
        if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
                priv->disable_gesture = true;
 
+       /*
+        * Unfortunately ForcePad capability is not exported over PS/2,
+        * so we have to resort to checking PNP IDs.
+        */
+       priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
+
        if (synaptics_set_mode(psmouse)) {
                psmouse_err(psmouse, "Unable to initialize device.\n");
                goto init_fail;
index ee4bd0d12b26fa2770445a381134b62124be6741..56faa7ec443480af63e66b56819be46fb44a6ee7 100644 (file)
@@ -196,6 +196,7 @@ struct synaptics_data {
        unsigned long                           press_start;
        bool                                    press;
        bool                                    report_press;
+       bool                                    is_forcepad;
 };
 
 void synaptics_module_init(void);
index 94ab494a6ade8d492cbf01d32bc06366c59fd762..ecba666afadb7c544e9d2078a857e3fca0aaa49c 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/spinlock.h>
 #include <linux/delay.h>
 #include <linux/ioport.h>
-#include <linux/pci_ids.h>
 
 #include <asm/irq.h>
 #include <asm/io.h>
index 986a71c614b0461bce7825ba34fc78aefcd4a7e4..cb5ece77fd7d8c47f29db0400da5184de0939644 100644 (file)
@@ -1162,13 +1162,32 @@ static int i8042_controller_resume(bool force_reset)
 
 static int i8042_pm_suspend(struct device *dev)
 {
+       int i;
+
        i8042_controller_reset(true);
 
+       /* Set up serio interrupts for system wakeup. */
+       for (i = 0; i < I8042_NUM_PORTS; i++) {
+               struct serio *serio = i8042_ports[i].serio;
+
+               if (serio && device_may_wakeup(&serio->dev))
+                       enable_irq_wake(i8042_ports[i].irq);
+       }
+
        return 0;
 }
 
 static int i8042_pm_resume(struct device *dev)
 {
+       int i;
+
+       for (i = 0; i < I8042_NUM_PORTS; i++) {
+               struct serio *serio = i8042_ports[i].serio;
+
+               if (serio && device_may_wakeup(&serio->dev))
+                       disable_irq_wake(i8042_ports[i].irq);
+       }
+
        /*
         * On resume from S2R we always try to reset the controller
         * to bring it in a sane state. (In case of S2D we expect
@@ -1300,13 +1319,16 @@ static void __init i8042_register_ports(void)
        int i;
 
        for (i = 0; i < I8042_NUM_PORTS; i++) {
-               if (i8042_ports[i].serio) {
+               struct serio *serio = i8042_ports[i].serio;
+
+               if (serio) {
                        printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
-                               i8042_ports[i].serio->name,
+                               serio->name,
                                (unsigned long) I8042_DATA_REG,
                                (unsigned long) I8042_COMMAND_REG,
                                i8042_ports[i].irq);
-                       serio_register_port(i8042_ports[i].serio);
+                       serio_register_port(serio);
+                       device_set_wakeup_capable(&serio->dev, true);
                }
        }
 }
index 6261fd6d7c3c4ddcb758b6796b0ba6ca0c222c99..547f67d653728a09601f43ce9beb8a6471ff2145 100644 (file)
@@ -140,6 +140,19 @@ config TOUCHSCREEN_BU21013
          To compile this driver as a module, choose M here: the
          module will be called bu21013_ts.
 
+config TOUCHSCREEN_CHIPONE_ICN8318
+       tristate "chipone icn8318 touchscreen controller"
+       depends on GPIOLIB
+       depends on I2C
+       depends on OF
+       help
+         Say Y here if you have a ChipOne icn8318 based I2C touchscreen.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called chipone_icn8318.
+
 config TOUCHSCREEN_CY8CTMG110
        tristate "cy8ctmg110 touchscreen"
        depends on I2C
@@ -297,11 +310,12 @@ config TOUCHSCREEN_FUJITSU
 
 config TOUCHSCREEN_GOODIX
        tristate "Goodix I2C touchscreen"
-       depends on I2C && ACPI
+       depends on I2C
        help
          Say Y here if you have the Goodix touchscreen (such as one
          installed in Onda v975w tablets) connected to your
-         system.
+         system. It also supports 5-finger chip models, which can be
+         found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
 
          If unsure, say N.
 
@@ -323,6 +337,18 @@ config TOUCHSCREEN_ILI210X
          To compile this driver as a module, choose M here: the
          module will be called ili210x.
 
+config TOUCHSCREEN_IPROC
+       tristate "IPROC touch panel driver support"
+       depends on ARCH_BCM_IPROC || COMPILE_TEST
+       help
+         Say Y here if you want to add support for the IPROC touch
+         controller to your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bcm_iproc_tsc.
+
 config TOUCHSCREEN_S3C2410
        tristate "Samsung S3C2410/generic touchscreen input driver"
        depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
@@ -962,6 +988,17 @@ config TOUCHSCREEN_SUR40
          To compile this driver as a module, choose M here: the
          module will be called sur40.
 
+config TOUCHSCREEN_SX8654
+       tristate "Semtech SX8654 touchscreen"
+       depends on I2C
+       help
+         Say Y here if you have a Semtech SX8654 touchscreen controller.
+
+         If unsure, say N
+
+         To compile this driver as a module, choose M here: the
+         module will be called sx8654.
+
 config TOUCHSCREEN_TPS6507X
        tristate "TPS6507x based touchscreens"
        depends on I2C
index 0242fea2102ac7cc5782224cb4a6705499c9431d..44deea743d022539a08a44d49c7611f7bfbd4908 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C)  += ar1021_i2c.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT)    += atmel_mxt_ts.o
 obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR)   += auo-pixcir-ts.o
 obj-$(CONFIG_TOUCHSCREEN_BU21013)      += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318)      += chipone_icn8318.o
 obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)   += cy8ctmg110_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE)  += cyttsp_core.o
 obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C)   += cyttsp_i2c.o cyttsp_i2c_common.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX)      += goodix.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)      += ili210x.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)       += inexio.o
 obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)    += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_IPROC)                += bcm_iproc_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_LPC32XX)      += lpc32xx_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MAX11801)     += max11801_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MC13783)      += mc13783_ts.o
@@ -79,5 +81,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL)        += atmel-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)     += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SX8654)       += sx8654.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)       += zforce_ts.o
index ba30578e296e4efa3032b307388975d7a6ec9172..f0b954d46a259726d94766e0191f4523351c748a 100644 (file)
@@ -157,7 +157,7 @@ static const struct i2c_device_id ar1021_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
 
-static struct of_device_id ar1021_i2c_of_match[] = {
+static const struct of_device_id ar1021_i2c_of_match[] = {
        { .compatible = "microchip,ar1021-i2c", },
        { }
 };
index 95ee92a91bd21353bf58020c9d87fda42fad6633..2875ddf37289521c2059ad64d1301e6bb40b3ba4 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/interrupt.h>
 #include <linux/of.h>
 #include <linux/slab.h>
+#include <asm/unaligned.h>
 
 /* Version */
 #define MXT_VER_20             20
@@ -79,6 +80,7 @@
 #define MXT_SPT_DIGITIZER_T43          43
 #define MXT_SPT_MESSAGECOUNT_T44       44
 #define MXT_SPT_CTECONFIG_T46          46
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
 
 /* MXT_GEN_MESSAGE_T5 object */
 #define MXT_RPTID_NOMSG                0xff
@@ -185,6 +187,36 @@ struct t9_range {
 #define MXT_RESET_VALUE                0x01
 #define MXT_BACKUP_VALUE       0x55
 
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL          0
+#define MXT_T100_CFG1          1
+#define MXT_T100_TCHAUX                3
+#define MXT_T100_XRANGE                13
+#define MXT_T100_YRANGE                24
+
+#define MXT_T100_CFG_SWITCHXY  BIT(5)
+
+#define MXT_T100_TCHAUX_VECT   BIT(0)
+#define MXT_T100_TCHAUX_AMPL   BIT(1)
+#define MXT_T100_TCHAUX_AREA   BIT(2)
+
+#define MXT_T100_DETECT                BIT(7)
+#define MXT_T100_TYPE_MASK     0x70
+
+enum t100_type {
+       MXT_T100_TYPE_FINGER            = 1,
+       MXT_T100_TYPE_PASSIVE_STYLUS    = 2,
+       MXT_T100_TYPE_HOVERING_FINGER   = 4,
+       MXT_T100_TYPE_GLOVE             = 5,
+       MXT_T100_TYPE_LARGE_TOUCH       = 6,
+};
+
+#define MXT_DISTANCE_ACTIVE_TOUCH      0
+#define MXT_DISTANCE_HOVERING          1
+
+#define MXT_TOUCH_MAJOR_DEFAULT                1
+#define MXT_PRESSURE_DEFAULT           1
+
 /* Delay times */
 #define MXT_BACKUP_TIME                50      /* msec */
 #define MXT_RESET_TIME         200     /* msec */
@@ -244,6 +276,9 @@ struct mxt_data {
        unsigned int max_y;
        bool in_bootloader;
        u16 mem_size;
+       u8 t100_aux_ampl;
+       u8 t100_aux_area;
+       u8 t100_aux_vect;
        u8 max_reportid;
        u32 config_crc;
        u32 info_crc;
@@ -253,6 +288,7 @@ struct mxt_data {
        bool update_input;
        u8 last_message_count;
        u8 num_touchids;
+       u8 multitouch;
 
        /* Cached parameters from object table */
        u16 T5_address;
@@ -264,6 +300,8 @@ struct mxt_data {
        u8 T9_reportid_max;
        u8 T19_reportid;
        u16 T44_address;
+       u8 T100_reportid_min;
+       u8 T100_reportid_max;
 
        /* for fw update in bootloader */
        struct completion bl_completion;
@@ -771,6 +809,114 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
        data->update_input = true;
 }
 
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
+{
+       struct device *dev = &data->client->dev;
+       struct input_dev *input_dev = data->input_dev;
+       int id;
+       u8 status;
+       u8 type = 0;
+       u16 x;
+       u16 y;
+       int distance = 0;
+       int tool = 0;
+       u8 major = 0;
+       u8 pressure = 0;
+       u8 orientation = 0;
+
+       id = message[0] - data->T100_reportid_min - 2;
+
+       /* ignore SCRSTATUS events */
+       if (id < 0)
+               return;
+
+       status = message[1];
+       x = get_unaligned_le16(&message[2]);
+       y = get_unaligned_le16(&message[4]);
+
+       if (status & MXT_T100_DETECT) {
+               type = (status & MXT_T100_TYPE_MASK) >> 4;
+
+               switch (type) {
+               case MXT_T100_TYPE_HOVERING_FINGER:
+                       tool = MT_TOOL_FINGER;
+                       distance = MXT_DISTANCE_HOVERING;
+
+                       if (data->t100_aux_vect)
+                               orientation = message[data->t100_aux_vect];
+
+                       break;
+
+               case MXT_T100_TYPE_FINGER:
+               case MXT_T100_TYPE_GLOVE:
+                       tool = MT_TOOL_FINGER;
+                       distance = MXT_DISTANCE_ACTIVE_TOUCH;
+
+                       if (data->t100_aux_area)
+                               major = message[data->t100_aux_area];
+
+                       if (data->t100_aux_ampl)
+                               pressure = message[data->t100_aux_ampl];
+
+                       if (data->t100_aux_vect)
+                               orientation = message[data->t100_aux_vect];
+
+                       break;
+
+               case MXT_T100_TYPE_PASSIVE_STYLUS:
+                       tool = MT_TOOL_PEN;
+
+                       /*
+                        * Passive stylus is reported with size zero so
+                        * hardcode.
+                        */
+                       major = MXT_TOUCH_MAJOR_DEFAULT;
+
+                       if (data->t100_aux_ampl)
+                               pressure = message[data->t100_aux_ampl];
+
+                       break;
+
+               case MXT_T100_TYPE_LARGE_TOUCH:
+                       /* Ignore suppressed touch */
+                       break;
+
+               default:
+                       dev_dbg(dev, "Unexpected T100 type\n");
+                       return;
+               }
+       }
+
+       /*
+        * Values reported should be non-zero if tool is touching the
+        * device
+        */
+       if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
+               pressure = MXT_PRESSURE_DEFAULT;
+
+       input_mt_slot(input_dev, id);
+
+       if (status & MXT_T100_DETECT) {
+               dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
+                       id, type, x, y, major, pressure, orientation);
+
+               input_mt_report_slot_state(input_dev, tool, 1);
+               input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+               input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+               input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
+               input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+               input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
+               input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
+       } else {
+               dev_dbg(dev, "[%u] release\n", id);
+
+               /* close out slot */
+               input_mt_report_slot_state(input_dev, 0, 0);
+       }
+
+       data->update_input = true;
+}
+
 static int mxt_proc_message(struct mxt_data *data, u8 *message)
 {
        u8 report_id = message[0];
@@ -786,9 +932,12 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message)
                 * is not yet registered.
                 */
                mxt_dump_message(data, message);
-       } else if (report_id >= data->T9_reportid_min
-           && report_id <= data->T9_reportid_max) {
+       } else if (report_id >= data->T9_reportid_min &&
+                  report_id <= data->T9_reportid_max) {
                mxt_proc_t9_message(data, message);
+       } else if (report_id >= data->T100_reportid_min &&
+                  report_id <= data->T100_reportid_max) {
+               mxt_proc_t100_message(data, message);
        } else if (report_id == data->T19_reportid) {
                mxt_input_button(data, message);
                data->update_input = true;
@@ -1411,6 +1560,8 @@ static void mxt_free_object_table(struct mxt_data *data)
        data->T9_reportid_max = 0;
        data->T19_reportid = 0;
        data->T44_address = 0;
+       data->T100_reportid_min = 0;
+       data->T100_reportid_max = 0;
        data->max_reportid = 0;
 }
 
@@ -1487,6 +1638,7 @@ static int mxt_get_object_table(struct mxt_data *data)
                        data->T7_address = object->start_address;
                        break;
                case MXT_TOUCH_MULTI_T9:
+                       data->multitouch = MXT_TOUCH_MULTI_T9;
                        data->T9_reportid_min = min_id;
                        data->T9_reportid_max = max_id;
                        data->num_touchids = object->num_report_ids
@@ -1498,6 +1650,13 @@ static int mxt_get_object_table(struct mxt_data *data)
                case MXT_SPT_GPIOPWM_T19:
                        data->T19_reportid = min_id;
                        break;
+               case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+                       data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
+                       data->T100_reportid_min = min_id;
+                       data->T100_reportid_max = max_id;
+                       /* first two report IDs reserved */
+                       data->num_touchids = object->num_report_ids - 2;
+                       break;
                }
 
                end_address = object->start_address
@@ -1582,22 +1741,138 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
        return 0;
 }
 
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+       struct i2c_client *client = data->client;
+       int error;
+       struct mxt_object *object;
+       u16 range_x, range_y;
+       u8 cfg, tchaux;
+       u8 aux;
+
+       object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+       if (!object)
+               return -EINVAL;
+
+       error = __mxt_read_reg(client,
+                              object->start_address + MXT_T100_XRANGE,
+                              sizeof(range_x), &range_x);
+       if (error)
+               return error;
+
+       le16_to_cpus(&range_x);
+
+       error = __mxt_read_reg(client,
+                              object->start_address + MXT_T100_YRANGE,
+                              sizeof(range_y), &range_y);
+       if (error)
+               return error;
+
+       le16_to_cpus(&range_y);
+
+       error =  __mxt_read_reg(client,
+                               object->start_address + MXT_T100_CFG1,
+                               1, &cfg);
+       if (error)
+               return error;
+
+       error =  __mxt_read_reg(client,
+                               object->start_address + MXT_T100_TCHAUX,
+                               1, &tchaux);
+       if (error)
+               return error;
+
+       /* Handle default values */
+       if (range_x == 0)
+               range_x = 1023;
+
+       if (range_y == 0)
+               range_y = 1023;
+
+       if (cfg & MXT_T100_CFG_SWITCHXY) {
+               data->max_x = range_y;
+               data->max_y = range_x;
+       } else {
+               data->max_x = range_x;
+               data->max_y = range_y;
+       }
+
+       /* allocate aux bytes */
+       aux = 6;
+
+       if (tchaux & MXT_T100_TCHAUX_VECT)
+               data->t100_aux_vect = aux++;
+
+       if (tchaux & MXT_T100_TCHAUX_AMPL)
+               data->t100_aux_ampl = aux++;
+
+       if (tchaux & MXT_T100_TCHAUX_AREA)
+               data->t100_aux_area = aux++;
+
+       dev_dbg(&client->dev,
+               "T100 aux mappings vect:%u ampl:%u area:%u\n",
+               data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
+
+       dev_info(&client->dev,
+                "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+       return 0;
+}
+
 static int mxt_input_open(struct input_dev *dev);
 static void mxt_input_close(struct input_dev *dev);
 
-static int mxt_initialize_t9_input_device(struct mxt_data *data)
+static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
+                                  struct mxt_data *data)
 {
-       struct device *dev = &data->client->dev;
        const struct mxt_platform_data *pdata = data->pdata;
+       int i;
+
+       input_dev->name = "Atmel maXTouch Touchpad";
+
+       __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+       input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+       input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+       input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+                         MXT_PIXELS_PER_MM);
+       input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+                         MXT_PIXELS_PER_MM);
+
+       for (i = 0; i < pdata->t19_num_keys; i++)
+               if (pdata->t19_keymap[i] != KEY_RESERVED)
+                       input_set_capability(input_dev, EV_KEY,
+                                            pdata->t19_keymap[i]);
+}
+
+static int mxt_initialize_input_device(struct mxt_data *data)
+{
+       const struct mxt_platform_data *pdata = data->pdata;
+       struct device *dev = &data->client->dev;
        struct input_dev *input_dev;
        int error;
        unsigned int num_mt_slots;
        unsigned int mt_flags = 0;
-       int i;
 
-       error = mxt_read_t9_resolution(data);
-       if (error)
-               dev_warn(dev, "Failed to initialize T9 resolution\n");
+       switch (data->multitouch) {
+       case MXT_TOUCH_MULTI_T9:
+               num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+               error = mxt_read_t9_resolution(data);
+               if (error)
+                       dev_warn(dev, "Failed to initialize T9 resolution\n");
+               break;
+
+       case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+               num_mt_slots = data->num_touchids;
+               error = mxt_read_t100_config(data);
+               if (error)
+                       dev_warn(dev, "Failed to read T100 config\n");
+               break;
+
+       default:
+               dev_err(dev, "Invalid multitouch object\n");
+               return -EINVAL;
+       }
 
        input_dev = input_allocate_device();
        if (!input_dev) {
@@ -1612,54 +1887,76 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)
        input_dev->open = mxt_input_open;
        input_dev->close = mxt_input_close;
 
-       __set_bit(EV_ABS, input_dev->evbit);
-       __set_bit(EV_KEY, input_dev->evbit);
-       __set_bit(BTN_TOUCH, input_dev->keybit);
+       input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
 
-       if (pdata->t19_num_keys) {
-               __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+       /* For single touch */
+       input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0);
 
-               for (i = 0; i < pdata->t19_num_keys; i++)
-                       if (pdata->t19_keymap[i] != KEY_RESERVED)
-                               input_set_capability(input_dev, EV_KEY,
-                                                    pdata->t19_keymap[i]);
+       if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+           (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+            data->t100_aux_ampl)) {
+               input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+       }
 
+       /* If device has buttons we assume it is a touchpad */
+       if (pdata->t19_num_keys) {
+               mxt_set_up_as_touchpad(input_dev, data);
                mt_flags |= INPUT_MT_POINTER;
-
-               input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
-               input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
-               input_abs_set_res(input_dev, ABS_MT_POSITION_X,
-                                 MXT_PIXELS_PER_MM);
-               input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
-                                 MXT_PIXELS_PER_MM);
-
-               input_dev->name = "Atmel maXTouch Touchpad";
        }
 
-       /* For single touch */
-       input_set_abs_params(input_dev, ABS_X,
-                            0, data->max_x, 0, 0);
-       input_set_abs_params(input_dev, ABS_Y,
-                            0, data->max_y, 0, 0);
-       input_set_abs_params(input_dev, ABS_PRESSURE,
-                            0, 255, 0, 0);
-
        /* For multi touch */
-       num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
        error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
        if (error) {
                dev_err(dev, "Error %d initialising slots\n", error);
                goto err_free_mem;
        }
 
-       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
-                            0, MXT_MAX_AREA, 0, 0);
+       if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
+               input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+                                    0, MT_TOOL_MAX, 0, 0);
+               input_set_abs_params(input_dev, ABS_MT_DISTANCE,
+                                    MXT_DISTANCE_ACTIVE_TOUCH,
+                                    MXT_DISTANCE_HOVERING,
+                                    0, 0);
+       }
+
        input_set_abs_params(input_dev, ABS_MT_POSITION_X,
                             0, data->max_x, 0, 0);
        input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
                             0, data->max_y, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_PRESSURE,
-                            0, 255, 0, 0);
+
+       if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+           (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+            data->t100_aux_area)) {
+               input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+                                    0, MXT_MAX_AREA, 0, 0);
+       }
+
+       if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+           (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+            data->t100_aux_ampl)) {
+               input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+                                    0, 255, 0, 0);
+       }
+
+       if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+           data->t100_aux_vect) {
+               input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+                                    0, 255, 0, 0);
+       }
+
+       if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+           data->t100_aux_ampl) {
+               input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+                                    0, 255, 0, 0);
+       }
+
+       if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+           data->t100_aux_vect) {
+               input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+                                    0, 255, 0, 0);
+       }
 
        input_set_drvdata(input_dev, data);
 
@@ -1765,9 +2062,13 @@ static int mxt_configure_objects(struct mxt_data *data,
                        dev_warn(dev, "Error %d updating config\n", error);
        }
 
-       error = mxt_initialize_t9_input_device(data);
-       if (error)
-               return error;
+       if (data->multitouch) {
+               error = mxt_initialize_input_device(data);
+               if (error)
+                       return error;
+       } else {
+               dev_warn(dev, "No touch object detected\n");
+       }
 
        dev_info(dev,
                 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
@@ -2044,15 +2345,13 @@ static const struct attribute_group mxt_attr_group = {
 static void mxt_start(struct mxt_data *data)
 {
        /* Touch enable */
-       mxt_write_object(data,
-                       MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
+       mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83);
 }
 
 static void mxt_stop(struct mxt_data *data)
 {
        /* Touch disable */
-       mxt_write_object(data,
-                       MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
+       mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0);
 }
 
 static int mxt_input_open(struct input_dev *dev)
diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c
new file mode 100644 (file)
index 0000000..ae460a5
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation version 2.
+*
+* 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/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/serio.h>
+
+#define IPROC_TS_NAME "iproc-ts"
+
+#define PEN_DOWN_STATUS     1
+#define PEN_UP_STATUS       0
+
+#define X_MIN               0
+#define Y_MIN               0
+#define X_MAX               0xFFF
+#define Y_MAX               0xFFF
+
+/* Value given by controller for invalid coordinate. */
+#define INVALID_COORD       0xFFFFFFFF
+
+/* Register offsets */
+#define REGCTL1             0x00
+#define REGCTL2             0x04
+#define INTERRUPT_THRES     0x08
+#define INTERRUPT_MASK      0x0c
+
+#define INTERRUPT_STATUS    0x10
+#define CONTROLLER_STATUS   0x14
+#define FIFO_DATA           0x18
+#define FIFO_DATA_X_Y_MASK  0xFFFF
+#define ANALOG_CONTROL      0x1c
+
+#define AUX_DATA            0x20
+#define DEBOUNCE_CNTR_STAT  0x24
+#define SCAN_CNTR_STAT      0x28
+#define REM_CNTR_STAT       0x2c
+
+#define SETTLING_TIMER_STAT 0x30
+#define SPARE_REG           0x34
+#define SOFT_BYPASS_CONTROL 0x38
+#define SOFT_BYPASS_DATA    0x3c
+
+
+/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */
+#define TS_PEN_INTR_MASK        BIT(0)
+#define TS_FIFO_INTR_MASK       BIT(2)
+
+/* Bit values for CONTROLLER_STATUS reg1 */
+#define TS_PEN_DOWN             BIT(0)
+
+/* Shift values for control reg1 */
+#define SCANNING_PERIOD_SHIFT   24
+#define DEBOUNCE_TIMEOUT_SHIFT  16
+#define SETTLING_TIMEOUT_SHIFT  8
+#define TOUCH_TIMEOUT_SHIFT     0
+
+/* Shift values for coordinates from fifo */
+#define X_COORD_SHIFT  0
+#define Y_COORD_SHIFT  16
+
+/* Bit values for REGCTL2 */
+#define TS_CONTROLLER_EN_BIT    BIT(16)
+#define TS_CONTROLLER_AVGDATA_SHIFT 8
+#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT)
+#define TS_CONTROLLER_PWR_LDO   BIT(5)
+#define TS_CONTROLLER_PWR_ADC   BIT(4)
+#define TS_CONTROLLER_PWR_BGP   BIT(3)
+#define TS_CONTROLLER_PWR_TS    BIT(2)
+#define TS_WIRE_MODE_BIT        BIT(1)
+
+#define dbg_reg(dev, priv, reg) \
+       dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg))
+
+struct tsc_param {
+       /* Each step is 1024 us.  Valid 1-256 */
+       u32 scanning_period;
+
+       /*  Each step is 512 us.  Valid 0-255 */
+       u32 debounce_timeout;
+
+       /*
+        * The settling duration (in ms) is the amount of time the tsc
+        * waits to allow the voltage to settle after turning on the
+        * drivers in detection mode. Valid values: 0-11
+        *   0 =  0.008 ms
+        *   1 =  0.01 ms
+        *   2 =  0.02 ms
+        *   3 =  0.04 ms
+        *   4 =  0.08 ms
+        *   5 =  0.16 ms
+        *   6 =  0.32 ms
+        *   7 =  0.64 ms
+        *   8 =  1.28 ms
+        *   9 =  2.56 ms
+        *   10 = 5.12 ms
+        *   11 = 10.24 ms
+        */
+       u32 settling_timeout;
+
+       /* touch timeout in sample counts */
+       u32 touch_timeout;
+
+       /*
+        * Number of data samples which are averaged before a final data point
+        * is placed into the FIFO
+        */
+       u32 average_data;
+
+       /* FIFO threshold */
+       u32 fifo_threshold;
+
+       /* Optional standard touchscreen properties. */
+       u32 max_x;
+       u32 max_y;
+       u32 fuzz_x;
+       u32 fuzz_y;
+       bool invert_x;
+       bool invert_y;
+};
+
+struct iproc_ts_priv {
+       struct platform_device *pdev;
+       struct input_dev *idev;
+
+       void __iomem *regs;
+       struct clk *tsc_clk;
+
+       int  pen_status;
+       struct tsc_param cfg_params;
+};
+
+/*
+ * Set default values the same as hardware reset values
+ * except for fifo_threshold with is set to 1.
+ */
+static const struct tsc_param iproc_default_config = {
+       .scanning_period  = 0x5,  /* 1 to 256 */
+       .debounce_timeout = 0x28, /* 0 to 255 */
+       .settling_timeout = 0x7,  /* 0 to 11 */
+       .touch_timeout    = 0xa,  /* 0 to 255 */
+       .average_data     = 5,    /* entry 5 = 32 pts */
+       .fifo_threshold   = 1,    /* 0 to 31 */
+       .max_x            = X_MAX,
+       .max_y            = Y_MAX,
+};
+
+static void ts_reg_dump(struct iproc_ts_priv *priv)
+{
+       struct device *dev = &priv->pdev->dev;
+
+       dbg_reg(dev, priv, REGCTL1);
+       dbg_reg(dev, priv, REGCTL2);
+       dbg_reg(dev, priv, INTERRUPT_THRES);
+       dbg_reg(dev, priv, INTERRUPT_MASK);
+       dbg_reg(dev, priv, INTERRUPT_STATUS);
+       dbg_reg(dev, priv, CONTROLLER_STATUS);
+       dbg_reg(dev, priv, FIFO_DATA);
+       dbg_reg(dev, priv, ANALOG_CONTROL);
+       dbg_reg(dev, priv, AUX_DATA);
+       dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT);
+       dbg_reg(dev, priv, SCAN_CNTR_STAT);
+       dbg_reg(dev, priv, REM_CNTR_STAT);
+       dbg_reg(dev, priv, SETTLING_TIMER_STAT);
+       dbg_reg(dev, priv, SPARE_REG);
+       dbg_reg(dev, priv, SOFT_BYPASS_CONTROL);
+       dbg_reg(dev, priv, SOFT_BYPASS_DATA);
+}
+
+static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
+{
+       struct platform_device *pdev = data;
+       struct iproc_ts_priv *priv = platform_get_drvdata(pdev);
+       u32 intr_status;
+       u32 raw_coordinate;
+       u16 x;
+       u16 y;
+       int i;
+       bool needs_sync = false;
+
+       intr_status = readl(priv->regs + INTERRUPT_STATUS);
+       intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+       if (intr_status == 0)
+               return IRQ_NONE;
+
+       /* Clear all interrupt status bits, write-1-clear */
+       writel(intr_status, priv->regs + INTERRUPT_STATUS);
+
+       /* Pen up/down */
+       if (intr_status & TS_PEN_INTR_MASK) {
+               if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN)
+                       priv->pen_status = PEN_DOWN_STATUS;
+               else
+                       priv->pen_status = PEN_UP_STATUS;
+
+               input_report_key(priv->idev, BTN_TOUCH, priv->pen_status);
+               needs_sync = true;
+
+               dev_dbg(&priv->pdev->dev,
+                       "pen up-down (%d)\n", priv->pen_status);
+       }
+
+       /* coordinates in FIFO exceed the theshold */
+       if (intr_status & TS_FIFO_INTR_MASK) {
+               for (i = 0; i < priv->cfg_params.fifo_threshold; i++) {
+                       raw_coordinate = readl(priv->regs + FIFO_DATA);
+                       if (raw_coordinate == INVALID_COORD)
+                               continue;
+
+                       /*
+                        * The x and y coordinate are 16 bits each
+                        * with the x in the lower 16 bits and y in the
+                        * upper 16 bits.
+                        */
+                       x = (raw_coordinate >> X_COORD_SHIFT) &
+                               FIFO_DATA_X_Y_MASK;
+                       y = (raw_coordinate >> Y_COORD_SHIFT) &
+                               FIFO_DATA_X_Y_MASK;
+
+                       /* We only want to retain the 12 msb of the 16 */
+                       x = (x >> 4) & 0x0FFF;
+                       y = (y >> 4) & 0x0FFF;
+
+                       /* adjust x y according to lcd tsc mount angle */
+                       if (priv->cfg_params.invert_x)
+                               x = priv->cfg_params.max_x - x;
+
+                       if (priv->cfg_params.invert_y)
+                               y = priv->cfg_params.max_y - y;
+
+                       input_report_abs(priv->idev, ABS_X, x);
+                       input_report_abs(priv->idev, ABS_Y, y);
+                       needs_sync = true;
+
+                       dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y);
+               }
+       }
+
+       if (needs_sync)
+               input_sync(priv->idev);
+
+       return IRQ_HANDLED;
+}
+
+static int iproc_ts_start(struct input_dev *idev)
+{
+       struct iproc_ts_priv *priv = input_get_drvdata(idev);
+       u32 val;
+       int error;
+
+       /* Enable clock */
+       error = clk_prepare_enable(priv->tsc_clk);
+       if (error) {
+               dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n",
+                       __func__, error);
+               return error;
+       }
+
+       /*
+        * Interrupt is generated when:
+        *  FIFO reaches the int_th value, and pen event(up/down)
+        */
+       val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+       writel(val, priv->regs + INTERRUPT_MASK);
+
+       writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES);
+
+       /* Initialize control reg1 */
+       val = 0;
+       val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT;
+       val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
+       val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
+       val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT;
+       writel(val, priv->regs + REGCTL1);
+
+       /* Try to clear all interrupt status */
+       val = readl(priv->regs + INTERRUPT_STATUS);
+       val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
+       writel(val, priv->regs + INTERRUPT_STATUS);
+
+       /* Initialize control reg2 */
+       val = readl(priv->regs + REGCTL2);
+       val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
+
+       val &= ~TS_CONTROLLER_AVGDATA_MASK;
+       val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT;
+
+       val &= ~(TS_CONTROLLER_PWR_LDO |        /* PWR up LDO */
+                  TS_CONTROLLER_PWR_ADC |      /* PWR up ADC */
+                  TS_CONTROLLER_PWR_BGP |      /* PWR up BGP */
+                  TS_CONTROLLER_PWR_TS);       /* PWR up TS */
+
+       writel(val, priv->regs + REGCTL2);
+
+       ts_reg_dump(priv);
+
+       return 0;
+}
+
+static void iproc_ts_stop(struct input_dev *dev)
+{
+       u32 val;
+       struct iproc_ts_priv *priv = input_get_drvdata(dev);
+
+       writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */
+
+       /* Only power down touch screen controller */
+       val = readl(priv->regs + REGCTL2);
+       val |= TS_CONTROLLER_PWR_TS;
+       writel(val, priv->regs + REGCTL2);
+
+       clk_disable(priv->tsc_clk);
+}
+
+static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv)
+{
+       struct device_node *np = dev->of_node;
+       u32 val;
+
+       priv->cfg_params = iproc_default_config;
+
+       if (!np)
+               return 0;
+
+       if (of_property_read_u32(np, "scanning_period", &val) >= 0) {
+               if (val < 1 || val > 256) {
+                       dev_err(dev, "scanning_period (%u) must be [1-256]\n",
+                               val);
+                       return -EINVAL;
+               }
+               priv->cfg_params.scanning_period = val;
+       }
+
+       if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) {
+               if (val > 255) {
+                       dev_err(dev, "debounce_timeout (%u) must be [0-255]\n",
+                               val);
+                       return -EINVAL;
+               }
+               priv->cfg_params.debounce_timeout = val;
+       }
+
+       if (of_property_read_u32(np, "settling_timeout", &val) >= 0) {
+               if (val > 11) {
+                       dev_err(dev, "settling_timeout (%u) must be [0-11]\n",
+                               val);
+                       return -EINVAL;
+               }
+               priv->cfg_params.settling_timeout = val;
+       }
+
+       if (of_property_read_u32(np, "touch_timeout", &val) >= 0) {
+               if (val > 255) {
+                       dev_err(dev, "touch_timeout (%u) must be [0-255]\n",
+                               val);
+                       return -EINVAL;
+               }
+               priv->cfg_params.touch_timeout = val;
+       }
+
+       if (of_property_read_u32(np, "average_data", &val) >= 0) {
+               if (val > 8) {
+                       dev_err(dev, "average_data (%u) must be [0-8]\n", val);
+                       return -EINVAL;
+               }
+               priv->cfg_params.average_data = val;
+       }
+
+       if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) {
+               if (val > 31) {
+                       dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n",
+                               val);
+                       return -EINVAL;
+               }
+               priv->cfg_params.fifo_threshold = val;
+       }
+
+       /* Parse optional properties. */
+       of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x);
+       of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y);
+
+       of_property_read_u32(np, "touchscreen-fuzz-x",
+                            &priv->cfg_params.fuzz_x);
+       of_property_read_u32(np, "touchscreen-fuzz-y",
+                            &priv->cfg_params.fuzz_y);
+
+       priv->cfg_params.invert_x =
+               of_property_read_bool(np, "touchscreen-inverted-x");
+       priv->cfg_params.invert_y =
+               of_property_read_bool(np, "touchscreen-inverted-y");
+
+       return 0;
+}
+
+static int iproc_ts_probe(struct platform_device *pdev)
+{
+       struct iproc_ts_priv *priv;
+       struct input_dev *idev;
+       struct resource *res;
+       int irq;
+       int error;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /* touchscreen controller memory mapped regs */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->regs)) {
+               error = PTR_ERR(priv->regs);
+               dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error);
+               return error;
+       }
+
+       priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
+       if (IS_ERR(priv->tsc_clk)) {
+               error = PTR_ERR(priv->tsc_clk);
+               dev_err(&pdev->dev,
+                       "failed getting clock tsc_clk: %d\n", error);
+               return error;
+       }
+
+       priv->pdev = pdev;
+       error = iproc_get_tsc_config(&pdev->dev, priv);
+       if (error) {
+               dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error);
+               return error;
+       }
+
+       idev = devm_input_allocate_device(&pdev->dev);
+       if (!idev) {
+               dev_err(&pdev->dev, "failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       priv->idev = idev;
+       priv->pen_status = PEN_UP_STATUS;
+
+       /* Set input device info  */
+       idev->name = IPROC_TS_NAME;
+       idev->dev.parent = &pdev->dev;
+
+       idev->id.bustype = BUS_HOST;
+       idev->id.vendor = SERIO_UNKNOWN;
+       idev->id.product = 0;
+       idev->id.version = 0;
+
+       idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+       __set_bit(BTN_TOUCH, idev->keybit);
+
+       input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x,
+                            priv->cfg_params.fuzz_x, 0);
+       input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y,
+                            priv->cfg_params.fuzz_y, 0);
+
+       idev->open = iproc_ts_start;
+       idev->close = iproc_ts_stop;
+
+       input_set_drvdata(idev, priv);
+       platform_set_drvdata(pdev, priv);
+
+       /* get interrupt */
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq);
+               return irq;
+       }
+
+       error = devm_request_irq(&pdev->dev, irq,
+                                iproc_touchscreen_interrupt,
+                                IRQF_SHARED, IPROC_TS_NAME, pdev);
+       if (error)
+               return error;
+
+       error = input_register_device(priv->idev);
+       if (error) {
+               dev_err(&pdev->dev,
+                       "failed to register input device: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id iproc_ts_of_match[] = {
+       {.compatible = "brcm,iproc-touchscreen", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, iproc_ts_of_match);
+
+static struct platform_driver iproc_ts_driver = {
+       .probe = iproc_ts_probe,
+       .driver = {
+               .name   = IPROC_TS_NAME,
+               .of_match_table = of_match_ptr(iproc_ts_of_match),
+       },
+};
+
+module_platform_driver(iproc_ts_driver);
+
+MODULE_DESCRIPTION("IPROC Touchscreen driver");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c
new file mode 100644 (file)
index 0000000..32e9db0
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Driver for ChipOne icn8318 i2c touchscreen controller
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ *
+ * 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.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define ICN8318_REG_POWER              4
+#define ICN8318_REG_TOUCHDATA          16
+
+#define ICN8318_POWER_ACTIVE           0
+#define ICN8318_POWER_MONITOR          1
+#define ICN8318_POWER_HIBERNATE                2
+
+#define ICN8318_MAX_TOUCHES            5
+
+struct icn8318_touch {
+       __u8 slot;
+       __be16 x;
+       __be16 y;
+       __u8 pressure;  /* Seems more like finger width then pressure really */
+       __u8 event;
+/* The difference between 2 and 3 is unclear */
+#define ICN8318_EVENT_NO_DATA  1 /* No finger seen yet since wakeup */
+#define ICN8318_EVENT_UPDATE1  2 /* New or updated coordinates */
+#define ICN8318_EVENT_UPDATE2  3 /* New or updated coordinates */
+#define ICN8318_EVENT_END      4 /* Finger lifted */
+} __packed;
+
+struct icn8318_touch_data {
+       __u8 softbutton;
+       __u8 touch_count;
+       struct icn8318_touch touches[ICN8318_MAX_TOUCHES];
+} __packed;
+
+struct icn8318_data {
+       struct i2c_client *client;
+       struct input_dev *input;
+       struct gpio_desc *wake_gpio;
+       u32 max_x;
+       u32 max_y;
+       bool invert_x;
+       bool invert_y;
+       bool swap_x_y;
+};
+
+static int icn8318_read_touch_data(struct i2c_client *client,
+                                  struct icn8318_touch_data *touch_data)
+{
+       u8 reg = ICN8318_REG_TOUCHDATA;
+       struct i2c_msg msg[2] = {
+               {
+                       .addr = client->addr,
+                       .len = 1,
+                       .buf = &reg
+               },
+               {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = sizeof(struct icn8318_touch_data),
+                       .buf = (u8 *)touch_data
+               }
+       };
+
+       return i2c_transfer(client->adapter, msg, 2);
+}
+
+static inline bool icn8318_touch_active(u8 event)
+{
+       return (event == ICN8318_EVENT_UPDATE1) ||
+              (event == ICN8318_EVENT_UPDATE2);
+}
+
+static irqreturn_t icn8318_irq(int irq, void *dev_id)
+{
+       struct icn8318_data *data = dev_id;
+       struct device *dev = &data->client->dev;
+       struct icn8318_touch_data touch_data;
+       int i, ret, x, y;
+
+       ret = icn8318_read_touch_data(data->client, &touch_data);
+       if (ret < 0) {
+               dev_err(dev, "Error reading touch data: %d\n", ret);
+               return IRQ_HANDLED;
+       }
+
+       if (touch_data.softbutton) {
+               /*
+                * Other data is invalid when a softbutton is pressed.
+                * This needs some extra devicetree bindings to map the icn8318
+                * softbutton codes to evdev codes. Currently no known devices
+                * use this.
+                */
+               return IRQ_HANDLED;
+       }
+
+       if (touch_data.touch_count > ICN8318_MAX_TOUCHES) {
+               dev_warn(dev, "Too much touches %d > %d\n",
+                        touch_data.touch_count, ICN8318_MAX_TOUCHES);
+               touch_data.touch_count = ICN8318_MAX_TOUCHES;
+       }
+
+       for (i = 0; i < touch_data.touch_count; i++) {
+               struct icn8318_touch *touch = &touch_data.touches[i];
+               bool act = icn8318_touch_active(touch->event);
+
+               input_mt_slot(data->input, touch->slot);
+               input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act);
+               if (!act)
+                       continue;
+
+               x = be16_to_cpu(touch->x);
+               y = be16_to_cpu(touch->y);
+
+               if (data->invert_x)
+                       x = data->max_x - x;
+
+               if (data->invert_y)
+                       y = data->max_y - y;
+
+               if (!data->swap_x_y) {
+                       input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
+                       input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
+               } else {
+                       input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
+                       input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
+               }
+       }
+
+       input_mt_sync_frame(data->input);
+       input_sync(data->input);
+
+       return IRQ_HANDLED;
+}
+
+static int icn8318_start(struct input_dev *dev)
+{
+       struct icn8318_data *data = input_get_drvdata(dev);
+
+       enable_irq(data->client->irq);
+       gpiod_set_value_cansleep(data->wake_gpio, 1);
+
+       return 0;
+}
+
+static void icn8318_stop(struct input_dev *dev)
+{
+       struct icn8318_data *data = input_get_drvdata(dev);
+
+       disable_irq(data->client->irq);
+       i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER,
+                                 ICN8318_POWER_HIBERNATE);
+       gpiod_set_value_cansleep(data->wake_gpio, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int icn8318_suspend(struct device *dev)
+{
+       struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+       mutex_lock(&data->input->mutex);
+       if (data->input->users)
+               icn8318_stop(data->input);
+       mutex_unlock(&data->input->mutex);
+
+       return 0;
+}
+
+static int icn8318_resume(struct device *dev)
+{
+       struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+       mutex_lock(&data->input->mutex);
+       if (data->input->users)
+               icn8318_start(data->input);
+       mutex_unlock(&data->input->mutex);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume);
+
+static int icn8318_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct device_node *np = dev->of_node;
+       struct icn8318_data *data;
+       struct input_dev *input;
+       u32 fuzz_x = 0, fuzz_y = 0;
+       int error;
+
+       if (!client->irq) {
+               dev_err(dev, "Error no irq specified\n");
+               return -EINVAL;
+       }
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW);
+       if (IS_ERR(data->wake_gpio)) {
+               error = PTR_ERR(data->wake_gpio);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev, "Error getting wake gpio: %d\n", error);
+               return error;
+       }
+
+       if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
+           of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
+               dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+               return -EINVAL;
+       }
+
+       /* Optional */
+       of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
+       of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
+       data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
+       data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
+       data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
+
+       input = devm_input_allocate_device(dev);
+       if (!input)
+               return -ENOMEM;
+
+       input->name = client->name;
+       input->id.bustype = BUS_I2C;
+       input->open = icn8318_start;
+       input->close = icn8318_stop;
+       input->dev.parent = dev;
+
+       if (!data->swap_x_y) {
+               input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+                                    data->max_x, fuzz_x, 0);
+               input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+                                    data->max_y, fuzz_y, 0);
+       } else {
+               input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+                                    data->max_y, fuzz_y, 0);
+               input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+                                    data->max_x, fuzz_x, 0);
+       }
+
+       error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
+                                   INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+       if (error)
+               return error;
+
+       data->client = client;
+       data->input = input;
+       input_set_drvdata(input, data);
+
+       error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq,
+                                         IRQF_ONESHOT, client->name, data);
+       if (error) {
+               dev_err(dev, "Error requesting irq: %d\n", error);
+               return error;
+       }
+
+       /* Stop device till opened */
+       icn8318_stop(data->input);
+
+       error = input_register_device(input);
+       if (error)
+               return error;
+
+       i2c_set_clientdata(client, data);
+
+       return 0;
+}
+
+static const struct of_device_id icn8318_of_match[] = {
+       { .compatible = "chipone,icn8318" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, icn8318_of_match);
+
+/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */
+static const struct i2c_device_id icn8318_i2c_id[] = {
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
+
+static struct i2c_driver icn8318_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "chipone_icn8318",
+               .pm     = &icn8318_pm_ops,
+               .of_match_table = icn8318_of_match,
+       },
+       .probe = icn8318_probe,
+       .id_table = icn8318_i2c_id,
+};
+
+module_i2c_driver(icn8318_driver);
+
+MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
index d4c24fb7704f5e2d87f299ac01e2aa2ba5866521..e6aef3e48bd9207f982a73da37e60537c491835c 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
 #include <linux/input/edt-ft5x06.h>
 
 #define MAX_SUPPORT_POINTS             5
@@ -1034,7 +1035,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
        input->id.bustype = BUS_I2C;
        input->dev.parent = &client->dev;
 
-       __set_bit(EV_SYN, input->evbit);
        __set_bit(EV_KEY, input->evbit);
        __set_bit(EV_ABS, input->evbit);
        __set_bit(BTN_TOUCH, input->keybit);
@@ -1044,6 +1044,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
                             0, tsdata->num_x * 64 - 1, 0, 0);
        input_set_abs_params(input, ABS_MT_POSITION_Y,
                             0, tsdata->num_y * 64 - 1, 0, 0);
+
+       if (!pdata)
+               touchscreen_parse_of_params(input);
+
        error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
        if (error) {
                dev_err(&client->dev, "Unable to init MT slots.\n");
index 926c58e540c08957924c3f964a6756f484b93d53..43b3c9c2d788ed2637178e4e6b4395cec3a552af 100644 (file)
@@ -98,7 +98,6 @@
 #define MAX_FW_UPDATE_RETRIES  30
 
 #define ELAN_FW_PAGESIZE       132
-#define ELAN_FW_FILENAME       "elants_i2c.bin"
 
 /* calibration timeout definition */
 #define ELAN_CALI_TIMEOUT_MSEC 10000
@@ -697,12 +696,19 @@ static int elants_i2c_fw_update(struct elants_data *ts)
 {
        struct i2c_client *client = ts->client;
        const struct firmware *fw;
+       char *fw_name;
        int error;
 
-       error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev);
+       fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%4x.bin", ts->hw_version);
+       if (!fw_name)
+               return -ENOMEM;
+
+       dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+       error = request_firmware(&fw, fw_name, &client->dev);
+       kfree(fw_name);
        if (error) {
-               dev_err(&client->dev, "failed to request firmware %s: %d\n",
-                       ELAN_FW_FILENAME, error);
+               dev_err(&client->dev, "failed to request firmware: %d\n",
+                       error);
                return error;
        }
 
index ca196689f025222845af079eb578af28e4621747..3af16984d57c908afdf9b801370ab2c76389d7f6 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/irq.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
 #include <asm/unaligned.h>
 
 struct goodix_ts_data {
@@ -48,6 +50,7 @@ struct goodix_ts_data {
 #define GOODIX_REG_VERSION             0x8140
 
 #define RESOLUTION_LOC         1
+#define MAX_CONTACTS_LOC       5
 #define TRIGGER_LOC            6
 
 static const unsigned long goodix_irq_flags[] = {
@@ -99,7 +102,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
        }
 
        touch_num = data[0] & 0x0f;
-       if (touch_num > GOODIX_MAX_CONTACTS)
+       if (touch_num > ts->max_touch_num)
                return -EPROTO;
 
        if (touch_num > 1) {
@@ -141,7 +144,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
  */
 static void goodix_process_events(struct goodix_ts_data *ts)
 {
-       u8  point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
+       u8  point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num];
        int touch_num;
        int i;
 
@@ -202,21 +205,23 @@ static void goodix_read_config(struct goodix_ts_data *ts)
                ts->abs_x_max = GOODIX_MAX_WIDTH;
                ts->abs_y_max = GOODIX_MAX_HEIGHT;
                ts->int_trigger_type = GOODIX_INT_TRIGGER;
+               ts->max_touch_num = GOODIX_MAX_CONTACTS;
                return;
        }
 
        ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
        ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
-       ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
-       if (!ts->abs_x_max || !ts->abs_y_max) {
+       ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
+       ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
+       if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
                dev_err(&ts->client->dev,
                        "Invalid config, using defaults\n");
                ts->abs_x_max = GOODIX_MAX_WIDTH;
                ts->abs_y_max = GOODIX_MAX_HEIGHT;
+               ts->max_touch_num = GOODIX_MAX_CONTACTS;
        }
 }
 
-
 /**
  * goodix_read_version - Read goodix touchscreen version
  *
@@ -295,7 +300,7 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts)
        input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
        input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
 
-       input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS,
+       input_mt_init_slots(ts->input_dev, ts->max_touch_num,
                            INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
 
        ts->input_dev->name = "Goodix Capacitive TouchScreen";
@@ -372,11 +377,27 @@ static const struct i2c_device_id goodix_ts_id[] = {
        { }
 };
 
+#ifdef CONFIG_ACPI
 static const struct acpi_device_id goodix_acpi_match[] = {
        { "GDIX1001", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id goodix_of_match[] = {
+       { .compatible = "goodix,gt911" },
+       { .compatible = "goodix,gt9110" },
+       { .compatible = "goodix,gt912" },
+       { .compatible = "goodix,gt927" },
+       { .compatible = "goodix,gt9271" },
+       { .compatible = "goodix,gt928" },
+       { .compatible = "goodix,gt967" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, goodix_of_match);
+#endif
 
 static struct i2c_driver goodix_ts_driver = {
        .probe = goodix_ts_probe,
@@ -384,7 +405,8 @@ static struct i2c_driver goodix_ts_driver = {
        .driver = {
                .name = "Goodix-TS",
                .owner = THIS_MODULE,
-               .acpi_match_table = goodix_acpi_match,
+               .acpi_match_table = ACPI_PTR(goodix_acpi_match),
+               .of_match_table = of_match_ptr(goodix_of_match),
        },
 };
 module_i2c_driver(goodix_ts_driver);
index f8f9b84230b1f58c5ccf37833d54fb632808cf8b..b82b5207c78bdd3597af9f3abd11215abbaf9c2e 100644 (file)
 
 #include <linux/of.h>
 #include <linux/input.h>
+#include <linux/input/mt.h>
 #include <linux/input/touchscreen.h>
 
+static u32 of_get_optional_u32(struct device_node *np,
+                              const char *property)
+{
+       u32 val = 0;
+
+       of_property_read_u32(np, property, &val);
+
+       return val;
+}
+
+static void touchscreen_set_params(struct input_dev *dev,
+                                  unsigned long axis,
+                                  int max, int fuzz)
+{
+       struct input_absinfo *absinfo;
+
+       if (!test_bit(axis, dev->absbit)) {
+               /*
+                * Emit a warning only if the axis is not a multitouch
+                * axis, which might not be set by the driver.
+                */
+               if (!input_is_mt_axis(axis))
+                       dev_warn(&dev->dev,
+                                "DT specifies parameters but the axis is not set up\n");
+               return;
+       }
+
+       absinfo = &dev->absinfo[axis];
+       absinfo->maximum = max;
+       absinfo->fuzz = fuzz;
+}
+
 /**
  * touchscreen_parse_of_params - parse common touchscreen DT properties
  * @dev: device that should be parsed
 void touchscreen_parse_of_params(struct input_dev *dev)
 {
        struct device_node *np = dev->dev.parent->of_node;
-       struct input_absinfo *absinfo;
+       u32 maximum, fuzz;
 
        input_alloc_absinfo(dev);
        if (!dev->absinfo)
                return;
 
-       absinfo = &dev->absinfo[ABS_X];
-       of_property_read_u32(np, "touchscreen-size-x", &absinfo->maximum);
-       of_property_read_u32(np, "touchscreen-fuzz-x", &absinfo->fuzz);
+       maximum = of_get_optional_u32(np, "touchscreen-size-x");
+       fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x");
+       if (maximum || fuzz) {
+               touchscreen_set_params(dev, ABS_X, maximum, fuzz);
+               touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz);
+       }
 
-       absinfo = &dev->absinfo[ABS_Y];
-       of_property_read_u32(np, "touchscreen-size-y", &absinfo->maximum);
-       of_property_read_u32(np, "touchscreen-fuzz-y", &absinfo->fuzz);
+       maximum = of_get_optional_u32(np, "touchscreen-size-y");
+       fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y");
+       if (maximum || fuzz) {
+               touchscreen_set_params(dev, ABS_Y, maximum, fuzz);
+               touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz);
+       }
 
-       absinfo = &dev->absinfo[ABS_PRESSURE];
-       of_property_read_u32(np, "touchscreen-max-pressure", &absinfo->maximum);
-       of_property_read_u32(np, "touchscreen-fuzz-pressure", &absinfo->fuzz);
+       maximum = of_get_optional_u32(np, "touchscreen-max-pressure");
+       fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure");
+       if (maximum || fuzz) {
+               touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz);
+               touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz);
+       }
 }
 EXPORT_SYMBOL(touchscreen_parse_of_params);
index b93a28b955fda327ac0248ccac00678acc5a2ed5..c0116994067d5cf46503a14d51de62a33a2ea05b 100644 (file)
  * These kinds of heuristics are just asking for trouble (and don't belong
  * in the kernel). So this driver offers straight forward, reliable single
  * touch functionality only.
+ *
+ * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README)
+ * (looks like the description in the A20 User Manual v1.3 is better
+ * than the one in the A10 User Manual v.1.5)
  */
 
 #include <linux/err.h>
@@ -193,7 +197,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
        if (ts->temp_data == -1)
                return -EAGAIN;
 
-       *temp = (ts->temp_data - ts->temp_offset) * ts->temp_step;
+       *temp = ts->temp_data * ts->temp_step - ts->temp_offset;
 
        return 0;
 }
@@ -246,6 +250,8 @@ static int sun4i_ts_probe(struct platform_device *pdev)
        int error;
        u32 reg;
        bool ts_attached;
+       u32 tp_sensitive_adjust = 15;
+       u32 filter_type = 1;
 
        ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
        if (!ts)
@@ -255,22 +261,31 @@ static int sun4i_ts_probe(struct platform_device *pdev)
        ts->ignore_fifo_data = true;
        ts->temp_data = -1;
        if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
-               /* Allwinner SDK has temperature = -271 + (value / 6) (C) */
-               ts->temp_offset = 1626;
+               /* Allwinner SDK has temperature (C) = (value / 6) - 271 */
+               ts->temp_offset = 271000;
                ts->temp_step = 167;
+       } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) {
+               /*
+                * The A10 temperature sensor has quite a wide spread, these
+                * parameters are based on the averaging of the calibration
+                * results of 4 completely different boards, with a spread of
+                * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331.
+                */
+               ts->temp_offset = 257000;
+               ts->temp_step = 133;
        } else {
                /*
                 * The user manuals do not contain the formula for calculating
                 * the temperature. The formula used here is from the AXP209,
                 * which is designed by X-Powers, an affiliate of Allwinner:
                 *
-                *     temperature = -144.7 + (value * 0.1)
+                *     temperature (C) = (value * 0.1) - 144.7
                 *
                 * Allwinner does not have any documentation whatsoever for
                 * this hardware. Moreover, it is claimed that the sensor
                 * is inaccurate and cannot work properly.
                 */
-               ts->temp_offset = 1447;
+               ts->temp_offset = 144700;
                ts->temp_step = 100;
        }
 
@@ -313,14 +328,20 @@ static int sun4i_ts_probe(struct platform_device *pdev)
               ts->base + TP_CTRL0);
 
        /*
-        * sensitive_adjust = 15 : max, which is not all that sensitive,
+        * tp_sensitive_adjust is an optional property
         * tp_mode = 0 : only x and y coordinates, as we don't use dual touch
         */
-       writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0),
+       of_property_read_u32(np, "allwinner,tp-sensitive-adjust",
+                            &tp_sensitive_adjust);
+       writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0),
               ts->base + TP_CTRL2);
 
-       /* Enable median filter, type 1 : 5/3 */
-       writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3);
+       /*
+        * Enable median and averaging filter, optional property for
+        * filter type.
+        */
+       of_property_read_u32(np, "allwinner,filter-type", &filter_type);
+       writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3);
 
        /* Enable temperature measurement, period 1953 (2 seconds) */
        writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
@@ -330,10 +351,10 @@ static int sun4i_ts_probe(struct platform_device *pdev)
         * finally enable tp mode.
         */
        reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
-       if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
-               reg |= TP_MODE_EN(1);
-       else
+       if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts"))
                reg |= SUN6I_TP_MODE_EN(1);
+       else
+               reg |= TP_MODE_EN(1);
        writel(reg, ts->base + TP_CTRL1);
 
        /*
@@ -383,6 +404,7 @@ static int sun4i_ts_remove(struct platform_device *pdev)
 
 static const struct of_device_id sun4i_ts_of_match[] = {
        { .compatible = "allwinner,sun4i-a10-ts", },
+       { .compatible = "allwinner,sun5i-a13-ts", },
        { .compatible = "allwinner,sun6i-a31-ts", },
        { /* sentinel */ }
 };
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
new file mode 100644 (file)
index 0000000..aecb9ad
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Driver for Semtech SX8654 I2C touchscreen controller.
+ *
+ * Copyright (c) 2015 Armadeus Systems
+ *     Sébastien Szymanski <sebastien.szymanski@armadeus.com>
+ *
+ * Using code from:
+ *  - sx865x.c
+ *     Copyright (c) 2013 U-MoBo Srl
+ *     Pierluigi Passaro <p.passaro@u-mobo.com>
+ *  - sx8650.c
+ *      Copyright (c) 2009 Wayne Roberts
+ *  - tsc2007.c
+ *      Copyright (c) 2008 Kwangwoo Lee
+ *  - ads7846.c
+ *      Copyright (c) 2005 David Brownell
+ *      Copyright (c) 2006 Nokia Corporation
+ *  - corgi_ts.c
+ *      Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *      Copyright (C) 2002 MontaVista Software
+ *      Copyright (C) 2004 Texas Instruments
+ *      Copyright (C) 2005 Dirk Behme
+ *
+ *  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/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+/* register addresses */
+#define I2C_REG_TOUCH0                 0x00
+#define I2C_REG_TOUCH1                 0x01
+#define I2C_REG_CHANMASK               0x04
+#define I2C_REG_IRQMASK                        0x22
+#define I2C_REG_IRQSRC                 0x23
+#define I2C_REG_SOFTRESET              0x3f
+
+/* commands */
+#define CMD_READ_REGISTER              0x40
+#define CMD_MANUAL                     0xc0
+#define CMD_PENTRG                     0xe0
+
+/* value for I2C_REG_SOFTRESET */
+#define SOFTRESET_VALUE                        0xde
+
+/* bits for I2C_REG_IRQSRC */
+#define IRQ_PENTOUCH_TOUCHCONVDONE     0x08
+#define IRQ_PENRELEASE                 0x04
+
+/* bits for RegTouch1 */
+#define CONDIRQ                                0x20
+#define FILT_7SA                       0x03
+
+/* bits for I2C_REG_CHANMASK */
+#define CONV_X                         0x80
+#define CONV_Y                         0x40
+
+/* coordinates rate: higher nibble of CTRL0 register */
+#define RATE_MANUAL                    0x00
+#define RATE_5000CPS                   0xf0
+
+/* power delay: lower nibble of CTRL0 register */
+#define POWDLY_1_1MS                   0x0b
+
+#define MAX_12BIT                      ((1 << 12) - 1)
+
+struct sx8654 {
+       struct input_dev *input;
+       struct i2c_client *client;
+};
+
+static irqreturn_t sx8654_irq(int irq, void *handle)
+{
+       struct sx8654 *sx8654 = handle;
+       int irqsrc;
+       u8 data[4];
+       unsigned int x, y;
+       int retval;
+
+       irqsrc = i2c_smbus_read_byte_data(sx8654->client,
+                                         CMD_READ_REGISTER | I2C_REG_IRQSRC);
+       dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc);
+
+       if (irqsrc < 0)
+               goto out;
+
+       if (irqsrc & IRQ_PENRELEASE) {
+               dev_dbg(&sx8654->client->dev, "pen release interrupt");
+
+               input_report_key(sx8654->input, BTN_TOUCH, 0);
+               input_sync(sx8654->input);
+       }
+
+       if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
+               dev_dbg(&sx8654->client->dev, "pen touch interrupt");
+
+               retval = i2c_master_recv(sx8654->client, data, sizeof(data));
+               if (retval != sizeof(data))
+                       goto out;
+
+               /* invalid data */
+               if (unlikely(data[0] & 0x80 || data[2] & 0x80))
+                       goto out;
+
+               x = ((data[0] & 0xf) << 8) | (data[1]);
+               y = ((data[2] & 0xf) << 8) | (data[3]);
+
+               input_report_abs(sx8654->input, ABS_X, x);
+               input_report_abs(sx8654->input, ABS_Y, y);
+               input_report_key(sx8654->input, BTN_TOUCH, 1);
+               input_sync(sx8654->input);
+
+               dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y);
+       }
+
+out:
+       return IRQ_HANDLED;
+}
+
+static int sx8654_open(struct input_dev *dev)
+{
+       struct sx8654 *sx8654 = input_get_drvdata(dev);
+       struct i2c_client *client = sx8654->client;
+       int error;
+
+       /* enable pen trigger mode */
+       error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0,
+                                         RATE_5000CPS | POWDLY_1_1MS);
+       if (error) {
+               dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+               return error;
+       }
+
+       error = i2c_smbus_write_byte(client, CMD_PENTRG);
+       if (error) {
+               dev_err(&client->dev, "writing command CMD_PENTRG failed");
+               return error;
+       }
+
+       enable_irq(client->irq);
+
+       return 0;
+}
+
+static void sx8654_close(struct input_dev *dev)
+{
+       struct sx8654 *sx8654 = input_get_drvdata(dev);
+       struct i2c_client *client = sx8654->client;
+       int error;
+
+       disable_irq(client->irq);
+
+       /* enable manual mode mode */
+       error = i2c_smbus_write_byte(client, CMD_MANUAL);
+       if (error) {
+               dev_err(&client->dev, "writing command CMD_MANUAL failed");
+               return;
+       }
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+       if (error) {
+               dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+               return;
+       }
+}
+
+static int sx8654_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct sx8654 *sx8654;
+       struct input_dev *input;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_WORD_DATA))
+               return -ENXIO;
+
+       sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
+       if (!sx8654)
+               return -ENOMEM;
+
+       input = devm_input_allocate_device(&client->dev);
+       if (!sx8654)
+               return -ENOMEM;
+
+       input->name = "SX8654 I2C Touchscreen";
+       input->id.bustype = BUS_I2C;
+       input->dev.parent = &client->dev;
+       input->open = sx8654_open;
+       input->close = sx8654_close;
+
+       __set_bit(INPUT_PROP_DIRECT, input->propbit);
+       input_set_capability(input, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+       sx8654->client = client;
+       sx8654->input = input;
+
+       input_set_drvdata(sx8654->input, sx8654);
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
+                                         SOFTRESET_VALUE);
+       if (error) {
+               dev_err(&client->dev, "writing softreset value failed");
+               return error;
+       }
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
+                                         CONV_X | CONV_Y);
+       if (error) {
+               dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
+               return error;
+       }
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+                                         IRQ_PENTOUCH_TOUCHCONVDONE |
+                                               IRQ_PENRELEASE);
+       if (error) {
+               dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
+               return error;
+       }
+
+       error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
+                                         CONDIRQ | FILT_7SA);
+       if (error) {
+               dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
+               return error;
+       }
+
+       error = devm_request_threaded_irq(&client->dev, client->irq,
+                                         NULL, sx8654_irq,
+                                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                         client->name, sx8654);
+       if (error) {
+               dev_err(&client->dev,
+                       "Failed to enable IRQ %d, error: %d\n",
+                       client->irq, error);
+               return error;
+       }
+
+       /* Disable the IRQ, we'll enable it in sx8654_open() */
+       disable_irq(client->irq);
+
+       error = input_register_device(sx8654->input);
+       if (error)
+               return error;
+
+       i2c_set_clientdata(client, sx8654);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx8654_of_match[] = {
+       { .compatible = "semtech,sx8654", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sx8654_of_match);
+#endif
+
+static const struct i2c_device_id sx8654_id_table[] = {
+       { "semtech_sx8654", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
+
+static struct i2c_driver sx8654_driver = {
+       .driver = {
+               .name = "sx8654",
+               .of_match_table = of_match_ptr(sx8654_of_match),
+       },
+       .id_table = sx8654_id_table,
+       .probe = sx8654_probe,
+};
+module_i2c_driver(sx8654_driver);
+
+MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>");
+MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
index 1bf9906b5a3fc189276c39d065a2cc58b36ddbd3..ccc8aa6157096896511ab7777d6d8f22161c12e1 100644 (file)
@@ -75,7 +75,7 @@ struct tsc2007 {
        u16                     model;
        u16                     x_plate_ohms;
        u16                     max_rt;
-       unsigned long           poll_period;
+       unsigned long           poll_period; /* in jiffies */
        int                     fuzzx;
        int                     fuzzy;
        int                     fuzzz;
@@ -214,8 +214,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
                        dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
                }
 
-               wait_event_timeout(ts->wait, ts->stopped,
-                                  msecs_to_jiffies(ts->poll_period));
+               wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
        }
 
        dev_dbg(&ts->client->dev, "UP\n");
@@ -314,9 +313,9 @@ static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
                ts->fuzzz = val32;
 
        if (!of_property_read_u64(np, "ti,poll-period", &val64))
-               ts->poll_period = val64;
+               ts->poll_period = msecs_to_jiffies(val64);
        else
-               ts->poll_period = 1;
+               ts->poll_period = msecs_to_jiffies(1);
 
        if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
                ts->x_plate_ohms = val32;
@@ -350,7 +349,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
        ts->model             = pdata->model;
        ts->x_plate_ohms      = pdata->x_plate_ohms;
        ts->max_rt            = pdata->max_rt ? : MAX_12BIT;
-       ts->poll_period       = pdata->poll_period ? : 1;
+       ts->poll_period       = msecs_to_jiffies(pdata->poll_period ? : 1);
        ts->get_pendown_state = pdata->get_pendown_state;
        ts->clear_penirq      = pdata->clear_penirq;
        ts->fuzzx             = pdata->fuzzx;
index a0966331a89bb1269f358f13bc90be40161dad2c..f2c6c352c55af2d3bb4dbf243516b19b9c62a65a 100644 (file)
@@ -132,6 +132,7 @@ enum {
        DEVTYPE_GUNZE,
        DEVTYPE_DMC_TSC10,
        DEVTYPE_IRTOUCH,
+       DEVTYPE_IRTOUCH_HIRES,
        DEVTYPE_IDEALTEK,
        DEVTYPE_GENERAL_TOUCH,
        DEVTYPE_GOTOP,
@@ -198,6 +199,7 @@ static const struct usb_device_id usbtouch_devices[] = {
 #ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
        {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
        {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+       {USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES},
 #endif
 
 #ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
@@ -1177,6 +1179,15 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
                .rept_size      = 8,
                .read_data      = irtouch_read_data,
        },
+
+       [DEVTYPE_IRTOUCH_HIRES] = {
+               .min_xc         = 0x0,
+               .max_xc         = 0x7fff,
+               .min_yc         = 0x0,
+               .max_yc         = 0x7fff,
+               .rept_size      = 8,
+               .read_data      = irtouch_read_data,
+       },
 #endif
 
 #ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
index 53bdbb01bd3f375508e888fe43c726127f44ff45..baea077a02ccc53978e94e506182d5bf2752d5aa 100644 (file)
@@ -59,7 +59,7 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
        KEY_RESERVED,
        KEY_SCROLLDOWN,
        KEY_SCROLLUP,
-       KEY_DIRECTION,
+       KEY_ROTATE_DISPLAY,
        KEY_LEFTCTRL,
        KEY_BRIGHTNESSUP,
        KEY_BRIGHTNESSDOWN,
@@ -116,7 +116,7 @@ static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
        KEY_RESERVED,
        KEY_PROG1,
        KEY_PROG2,
-       KEY_DIRECTION,
+       KEY_ROTATE_DISPLAY,
        KEY_RESERVED,
        KEY_RESERVED,
        KEY_RESERVED,
@@ -153,7 +153,7 @@ static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = {
        KEY_RESERVED,
        KEY_RESERVED,
        KEY_MAIL,
-       KEY_DIRECTION,
+       KEY_ROTATE_DISPLAY,
        KEY_ESC,
        KEY_ENTER,
        KEY_BRIGHTNESSUP,
index 0ab2b377a778aaf673815736f305b0c8edb5ff3a..06697315a0887f6493c0f55185b864c12f8bc76e 100644 (file)
@@ -144,7 +144,7 @@ static const struct key_entry hp_wmi_keymap[] = {
        { KE_KEY, 0x20e8, { KEY_MEDIA } },
        { KE_KEY, 0x2142, { KEY_MEDIA } },
        { KE_KEY, 0x213b, { KEY_INFO } },
-       { KE_KEY, 0x2169, { KEY_DIRECTION } },
+       { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } },
        { KE_KEY, 0x216a, { KEY_SETUP } },
        { KE_KEY, 0x231b, { KEY_HELP } },
        { KE_END, 0 }
index c203c9c56776268913415993e3053a15b8665c24..468c31a27fcfb6f4228a37d70c094dc1986a9e77 100644 (file)
@@ -140,36 +140,13 @@ extern int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val);
 #define TC_KPD_DEBOUNCE_PERIOD  0xA3
 #define TC_KPD_SETTLE_TIME      0xA3
 
-/**
- * struct tc35893_platform_data - data structure for platform specific data
- * @keymap_data:        matrix scan code table for keycodes
- * @krow:               mask for available rows, value is 0xFF
- * @kcol:               mask for available columns, value is 0xFF
- * @debounce_period:    platform specific debounce time
- * @settle_time:        platform specific settle down time
- * @irqtype:            type of interrupt, falling or rising edge
- * @enable_wakeup:      specifies if keypad event can wake up system from sleep
- * @no_autorepeat:      flag for auto repetition
- */
-struct tc3589x_keypad_platform_data {
-       const struct matrix_keymap_data *keymap_data;
-       u8                      krow;
-       u8                      kcol;
-       u8                      debounce_period;
-       u8                      settle_time;
-       unsigned long           irqtype;
-       bool                    enable_wakeup;
-       bool                    no_autorepeat;
-};
 
 /**
  * struct tc3589x_platform_data - TC3589x platform data
  * @block: bitmask of blocks to enable (use TC3589x_BLOCK_*)
- * @keypad: keypad-specific platform data
  */
 struct tc3589x_platform_data {
        unsigned int block;
-       const struct tc3589x_keypad_platform_data *keypad;
 };
 
 #endif
index 8038e9aa3b9593cc055088c0f56aa6407bbc3e08..731417c025f6feff03ea257b46abde05c7a06b7d 100644 (file)
@@ -369,7 +369,8 @@ struct input_keymap_entry {
 #define KEY_MSDOS              151
 #define KEY_COFFEE             152     /* AL Terminal Lock/Screensaver */
 #define KEY_SCREENLOCK         KEY_COFFEE
-#define KEY_DIRECTION          153
+#define KEY_ROTATE_DISPLAY     153     /* Display orientation for e.g. tablets */
+#define KEY_DIRECTION          KEY_ROTATE_DISPLAY
 #define KEY_CYCLEWINDOWS       154
 #define KEY_MAIL               155
 #define KEY_BOOKMARKS          156     /* AC Bookmarks */