Merge tag 'v4.0-rc5' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 23 Mar 2015 16:18:27 +0000 (09:18 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 23 Mar 2015 16:18:27 +0000 (09:18 -0700)
Merge with the latest upstream to synchronize Synaptics changes
and bring in new infrastructure pieces.

Conflicts:
drivers/input/mouse/synaptics.c

37 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/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/vendor-prefixes.txt
drivers/hid/hid-debug.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/bcm-keypad.c [new file with mode: 0644]
drivers/input/keyboard/ipaq-micro-keys.c [new file with mode: 0644]
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/max77843-haptic.c [new file with mode: 0644]
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/elan_i2c.h
drivers/input/mouse/elan_i2c_core.c
drivers/input/mouse/elan_i2c_i2c.c
drivers/input/mouse/lifebook.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/serio/i8042.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ar1021_i2c.c
drivers/input/touchscreen/elants_i2c.c
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/sun4i-ts.c
drivers/input/touchscreen/sx8654.c [new file with mode: 0644]
drivers/platform/x86/fujitsu-tablet.c
drivers/platform/x86/hp-wmi.c
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/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 fae26d014aaf25c83f499889969e1cb017491f34..c862cf94c9c8780768dcf603f21394c1d866f318 100644 (file)
@@ -78,6 +78,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 8bf61d295ffd7efd638e1f14570508e9c8498f50..1086800693b211000a40812d3dc986eeff8d6da0 100644 (file)
@@ -814,7 +814,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 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
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");
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 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 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 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 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 dda605836546847afbdbdd4c77c976134a914a2e..d73a94270211041293c140b9144300621dcba9af 100644 (file)
@@ -198,6 +198,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
  ****************************************************************************/
@@ -682,8 +689,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)
@@ -713,7 +718,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.
@@ -1413,29 +1418,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)
@@ -1470,6 +1457,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 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..1130c40591045e3c8919b68e71cc36c2806142fe 100644 (file)
@@ -297,11 +297,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.
 
@@ -962,6 +963,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..a06a752966fecd4eaf59fdb8ceee7ba68d1baf1d 100644 (file)
@@ -79,5 +79,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 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 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 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 b0a81307985282005ade90ee6da96e3048c405d3..8ddbf1fbbed5255291e58575933eb2a3adbb29e7 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 */