]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Oct 2014 01:26:52 +0000 (21:26 -0400)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Oct 2014 01:26:52 +0000 (21:26 -0400)
Pull input updates from Dmitry Torokhov:
 "A few new haptic/button drivers, a rudimentary support for laptops
  using FocalTech touchpads; xpad driver will bind to more devices, and
  a few other driver fixes."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: soc_button_array - convert to platform bus
  Input: palmas-pwrbutton - fix typo in the license string
  Input: palmas-pwrbutton - use IRQF_ONESHOT
  Input: psmouse - add support for detecting FocalTech PS/2 touchpads
  Input: psmouse - add psmouse_matches_pnp_id helper function
  Input: joystick - use ktime for measuring timing
  Input: add haptic driver on max77693
  Input: introduce palmas-pwrbutton
  Input: add support for the DRV2667 haptic driver
  Input: xpad - sync device IDs with xboxdrv
  Input: xpad - add VID/PID for Razer Sabertooth
  Input: cros_ec_keyb - optimize ghosting algorithm
  Input: drv260x - fix binding document
  Input: drv260x - add check for ERM mode and LRA Libraries
  Input: drv260x - remove unused defines
  Input: drv260x - add TI drv260x haptics driver

24 files changed:
Documentation/devicetree/bindings/input/ti,drv260x.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/ti,drv2667.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt [new file with mode: 0644]
drivers/acpi/acpi_pnp.c
drivers/input/gameport/gameport.c
drivers/input/joystick/analog.c
drivers/input/joystick/xpad.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/drv260x.c [new file with mode: 0644]
drivers/input/misc/drv2667.c [new file with mode: 0644]
drivers/input/misc/max77693-haptic.c [new file with mode: 0644]
drivers/input/misc/palmas-pwrbutton.c [new file with mode: 0644]
drivers/input/misc/soc_button_array.c
drivers/input/mouse/Makefile
drivers/input/mouse/focaltech.c [new file with mode: 0644]
drivers/input/mouse/focaltech.h [new file with mode: 0644]
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse.h
drivers/input/mouse/synaptics.c
include/dt-bindings/input/ti-drv260x.h [new file with mode: 0644]
include/linux/mfd/max77693-private.h
include/linux/platform_data/drv260x-pdata.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/input/ti,drv260x.txt b/Documentation/devicetree/bindings/input/ti,drv260x.txt
new file mode 100644 (file)
index 0000000..ee09c8f
--- /dev/null
@@ -0,0 +1,50 @@
+* Texas Instruments - drv260x Haptics driver family
+
+Required properties:
+       - compatible - One of:
+               "ti,drv2604" - DRV2604
+               "ti,drv2605" - DRV2605
+               "ti,drv2605l" - DRV2605L
+       - reg -  I2C slave address
+       - vbat-supply - Required supply regulator
+       - mode - Power up mode of the chip (defined in include/dt-bindings/input/ti-drv260x.h)
+               DRV260X_LRA_MODE - Linear Resonance Actuator mode (Piezoelectric)
+               DRV260X_LRA_NO_CAL_MODE - This is a LRA Mode but there is no calibration
+                               sequence during init.  And the device is configured for real
+                               time playback mode (RTP mode).
+               DRV260X_ERM_MODE - Eccentric Rotating Mass mode (Rotary vibrator)
+       - library-sel - These are ROM based waveforms pre-programmed into the IC.
+                               This should be set to set the library to use at power up.
+                               (defined in include/dt-bindings/input/ti-drv260x.h)
+               DRV260X_LIB_EMPTY - Do not use a pre-programmed library
+               DRV260X_ERM_LIB_A - Pre-programmed Library
+               DRV260X_ERM_LIB_B - Pre-programmed Library
+               DRV260X_ERM_LIB_C - Pre-programmed Library
+               DRV260X_ERM_LIB_D - Pre-programmed Library
+               DRV260X_ERM_LIB_E - Pre-programmed Library
+               DRV260X_ERM_LIB_F - Pre-programmed Library
+               DRV260X_LIB_LRA - Pre-programmed LRA Library
+
+Optional properties:
+       - enable-gpio - gpio pin to enable/disable the device.
+       - vib-rated-mv - The rated voltage of the actuator in millivolts.
+                         If this is not set then the value will be defaulted to
+                         3.2 v.
+       - vib-overdrive-mv - The overdrive voltage of the actuator in millivolts.
+                         If this is not set then the value will be defaulted to
+                         3.2 v.
+Example:
+
+haptics: haptics@5a {
+       compatible = "ti,drv2605l";
+       reg = <0x5a>;
+       vbat-supply = <&vbat>;
+       enable-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+       mode = <DRV260X_LRA_MODE>;
+       library-sel = <DRV260X_LIB_LRA>;
+       vib-rated-mv = <3200>;
+       vib-overdriver-mv = <3200>;
+}
+
+For more product information please see the link below:
+http://www.ti.com/product/drv2605
diff --git a/Documentation/devicetree/bindings/input/ti,drv2667.txt b/Documentation/devicetree/bindings/input/ti,drv2667.txt
new file mode 100644 (file)
index 0000000..996382c
--- /dev/null
@@ -0,0 +1,17 @@
+* Texas Instruments - drv2667 Haptics driver
+
+Required properties:
+       - compatible - "ti,drv2667" - DRV2667
+       - reg -  I2C slave address
+       - vbat-supply - Required supply regulator
+
+Example:
+
+haptics: haptics@59 {
+       compatible = "ti,drv2667";
+       reg = <0x59>;
+       vbat-supply = <&vbat>;
+};
+
+For more product information please see the link below:
+http://www.ti.com/product/drv2667
diff --git a/Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt b/Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt
new file mode 100644 (file)
index 0000000..a3dde8c
--- /dev/null
@@ -0,0 +1,36 @@
+Texas Instruments Palmas family power button module
+
+This module is part of the Palmas family of PMICs. For more details
+about the whole chip see:
+Documentation/devicetree/bindings/mfd/palmas.txt.
+
+This module provides a simple power button event via an Interrupt.
+
+Required properties:
+- compatible: should be one of the following
+   - "ti,palmas-pwrbutton": For Palmas compatible power on button
+- interrupt-parent: Parent interrupt device, must be handle of palmas node.
+- interrupts: Interrupt number of power button submodule on device.
+
+Optional Properties:
+
+- ti,palmas-long-press-seconds: Duration in seconds which the power
+  button should be kept pressed for Palmas to power off automatically.
+  NOTE: This depends on OTP support and POWERHOLD signal configuration
+  on platform. Valid values are 6, 8, 10 and 12.
+- ti,palmas-pwron-debounce-milli-seconds: Duration in milliseconds
+  which the power button should be kept pressed for Palmas to register
+  a press for debouncing purposes. NOTE: This depends on specific
+  Palmas variation capability. Valid values are 15, 100, 500 and 1000.
+
+Example:
+
+&palmas {
+       palmas_pwr_button: pwrbutton {
+               compatible = "ti,palmas-pwrbutton";
+               interrupt-parent = <&tps659038>;
+               interrupts = <1 IRQ_TYPE_EDGE_FALLING>;
+               ti,palmas-long-press-seconds = <12>;
+               ti,palmas-pwron-debounce-milli-seconds = <15>;
+       };
+};
index 996fa1959eeacdc8335b0844eddbcf810bff9846..1f8b20496f32698a76bf7958ded72ae9961e34d4 100644 (file)
@@ -15,8 +15,6 @@
 #include <linux/ctype.h>
 
 static const struct acpi_device_id acpi_pnp_device_ids[] = {
-       /* soc_button_array */
-       {"PNP0C40"},
        /* pata_isapnp */
        {"PNP0600"},            /* Generic ESDI/IDE/ATA compatible hard disk controller */
        /* floppy */
index 24c41ba7d4e01dcf852050d3f1d8ef27b5f0a22f..e29c04e2aff4ea1ae4e570be4fa677cb9e014fb3 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/workqueue.h>
 #include <linux/sched.h>       /* HZ */
 #include <linux/mutex.h>
+#include <linux/timekeeping.h>
 
 /*#include <asm/io.h>*/
 
@@ -30,6 +31,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Generic gameport layer");
 MODULE_LICENSE("GPL");
 
+static bool use_ktime = true;
+module_param(use_ktime, bool, 0400);
+MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");
+
 /*
  * gameport_mutex protects entire gameport subsystem and is taken
  * every time gameport port or driver registrered or unregistered.
@@ -75,6 +80,38 @@ static unsigned int get_time_pit(void)
  */
 
 static int gameport_measure_speed(struct gameport *gameport)
+{
+       unsigned int i, t, tx;
+       u64 t1, t2, t3;
+       unsigned long flags;
+
+       if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+               return 0;
+
+       tx = ~0;
+
+       for (i = 0; i < 50; i++) {
+               local_irq_save(flags);
+               t1 = ktime_get_ns();
+               for (t = 0; t < 50; t++)
+                       gameport_read(gameport);
+               t2 = ktime_get_ns();
+               t3 = ktime_get_ns();
+               local_irq_restore(flags);
+               udelay(i * 10);
+               t = (t2 - t1) - (t3 - t2);
+               if (t < tx)
+                       tx = t;
+       }
+
+       gameport_close(gameport);
+       t = 1000000 * 50;
+       if (tx)
+               t /= tx;
+       return t;
+}
+
+static int old_gameport_measure_speed(struct gameport *gameport)
 {
 #if defined(__i386__)
 
@@ -521,7 +558,9 @@ static void gameport_add_port(struct gameport *gameport)
        if (gameport->parent)
                gameport->parent->child = gameport;
 
-       gameport->speed = gameport_measure_speed(gameport);
+       gameport->speed = use_ktime ?
+               gameport_measure_speed(gameport) :
+               old_gameport_measure_speed(gameport);
 
        list_add_tail(&gameport->node, &gameport_list);
 
index ab0fdcd36e18d7746ce7f8d5e41c51b5dfae9304..4284080e481d323bc028ea5b4a10d82cb595b66a 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/gameport.h>
 #include <linux/jiffies.h>
 #include <linux/timex.h>
+#include <linux/timekeeping.h>
 
 #define DRIVER_DESC    "Analog joystick and gamepad driver"
 
@@ -43,6 +44,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
+static bool use_ktime = true;
+module_param(use_ktime, bool, 0400);
+MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");
+
 /*
  * Option parsing.
  */
@@ -171,6 +176,25 @@ static unsigned long analog_faketime = 0;
 #warning Precise timer not defined for this architecture.
 #endif
 
+static inline u64 get_time(void)
+{
+       if (use_ktime) {
+               return ktime_get_ns();
+       } else {
+               unsigned int x;
+               GET_TIME(x);
+               return x;
+       }
+}
+
+static inline unsigned int delta(u64 x, u64 y)
+{
+       if (use_ktime)
+               return y - x;
+       else
+               return DELTA((unsigned int)x, (unsigned int)y);
+}
+
 /*
  * analog_decode() decodes analog joystick data and reports input events.
  */
@@ -226,7 +250,8 @@ static void analog_decode(struct analog *analog, int *axes, int *initial, int bu
 static int analog_cooked_read(struct analog_port *port)
 {
        struct gameport *gameport = port->gameport;
-       unsigned int time[4], start, loop, now, loopout, timeout;
+       u64 time[4], start, loop, now;
+       unsigned int loopout, timeout;
        unsigned char data[4], this, last;
        unsigned long flags;
        int i, j;
@@ -236,7 +261,7 @@ static int analog_cooked_read(struct analog_port *port)
 
        local_irq_save(flags);
        gameport_trigger(gameport);
-       GET_TIME(now);
+       now = get_time();
        local_irq_restore(flags);
 
        start = now;
@@ -249,16 +274,16 @@ static int analog_cooked_read(struct analog_port *port)
 
                local_irq_disable();
                this = gameport_read(gameport) & port->mask;
-               GET_TIME(now);
+               now = get_time();
                local_irq_restore(flags);
 
-               if ((last ^ this) && (DELTA(loop, now) < loopout)) {
+               if ((last ^ this) && (delta(loop, now) < loopout)) {
                        data[i] = last ^ this;
                        time[i] = now;
                        i++;
                }
 
-       } while (this && (i < 4) && (DELTA(start, now) < timeout));
+       } while (this && (i < 4) && (delta(start, now) < timeout));
 
        this <<= 4;
 
@@ -266,7 +291,7 @@ static int analog_cooked_read(struct analog_port *port)
                this |= data[i];
                for (j = 0; j < 4; j++)
                        if (data[i] & (1 << j))
-                               port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
+                               port->axes[j] = (delta(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
        }
 
        return -(this != port->mask);
@@ -365,31 +390,39 @@ static void analog_close(struct input_dev *dev)
 static void analog_calibrate_timer(struct analog_port *port)
 {
        struct gameport *gameport = port->gameport;
-       unsigned int i, t, tx, t1, t2, t3;
+       unsigned int i, t, tx;
+       u64 t1, t2, t3;
        unsigned long flags;
 
-       local_irq_save(flags);
-       GET_TIME(t1);
+       if (use_ktime) {
+               port->speed = 1000000;
+       } else {
+               local_irq_save(flags);
+               t1 = get_time();
 #ifdef FAKE_TIME
-       analog_faketime += 830;
+               analog_faketime += 830;
 #endif
-       mdelay(1);
-       GET_TIME(t2);
-       GET_TIME(t3);
-       local_irq_restore(flags);
+               mdelay(1);
+               t2 = get_time();
+               t3 = get_time();
+               local_irq_restore(flags);
 
-       port->speed = DELTA(t1, t2) - DELTA(t2, t3);
+               port->speed = delta(t1, t2) - delta(t2, t3);
+       }
 
        tx = ~0;
 
        for (i = 0; i < 50; i++) {
                local_irq_save(flags);
-               GET_TIME(t1);
-               for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); }
-               GET_TIME(t3);
+               t1 = get_time();
+               for (t = 0; t < 50; t++) {
+                       gameport_read(gameport);
+                       t2 = get_time();
+               }
+               t3 = get_time();
                local_irq_restore(flags);
                udelay(i);
-               t = DELTA(t1, t2) - DELTA(t2, t3);
+               t = delta(t1, t2) - delta(t2, t3);
                if (t < tx) tx = t;
        }
 
index 177602cf7079a41edf22553d348400d8035e649a..cd13c82ca0a152ead829b1165e8386482043c03c 100644 (file)
@@ -126,7 +126,9 @@ static const struct xpad_device {
        { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
        { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
        { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
+       { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 },
        { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
+       { 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
        { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 },
        { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 },
        { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX },
@@ -140,10 +142,17 @@ static const struct xpad_device {
        { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
        { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX },
        { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
+       { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 },
+       { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
        { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
        { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+       { 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 },
        { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+       { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 },
        { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 },
+       { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
+       { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
+       { 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 },
        { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
        { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX },
        { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX },
@@ -156,28 +165,50 @@ static const struct xpad_device {
        { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX },
        { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX },
        { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+       { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
        { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
        { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+       { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+       { 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 },
+       { 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 },
        { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX },
+       { 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX },
+       { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 },
        { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
        { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
        { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
        { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
        { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX },
        { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+       { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 },
        { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
        { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 },
        { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
        { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
+       { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+       { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
+       { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
+       { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 },
+       { 0x162e, 0xbeef, "Joytech Neo-Se Take2", 0, XTYPE_XBOX360 },
        { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 },
        { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 },
+       { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
        { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
        { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
        { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
+       { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 },
        { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 },
+       { 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 },
+       { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 },
        { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 },
        { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 },
+       { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", 0, XTYPE_XBOX360 },
        { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
+       { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
+       { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 },
+       { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 },
+       { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
+       { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 },
        { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
        { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }
 };
@@ -274,6 +305,9 @@ static struct usb_device_id xpad_table[] = {
        XPAD_XBOX360_VENDOR(0x0f0d),            /* Hori Controllers */
        XPAD_XBOX360_VENDOR(0x1689),            /* Razer Onza */
        XPAD_XBOX360_VENDOR(0x24c6),            /* PowerA Controllers */
+       XPAD_XBOX360_VENDOR(0x1532),            /* Razer Sabertooth */
+       XPAD_XBOX360_VENDOR(0x15e4),            /* Numark X-Box 360 controllers */
+       XPAD_XBOX360_VENDOR(0x162e),            /* Joytech X-Box 360 controllers */
        { }
 };
 
index 791781ade4e71535027dd9ba707e295fa9df57da..72d3499bb0290c7a0d68c9be116752569a50ad78 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/bitops.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
@@ -38,6 +39,7 @@
  * @row_shift: log2 or number of rows, rounded up
  * @keymap_data: Matrix keymap data used to convert to keyscan values
  * @ghost_filter: true to enable the matrix key-ghosting filter
+ * @valid_keys: bitmap of existing keys for each matrix column
  * @old_kb_state: bitmap of keys pressed last scan
  * @dev: Device pointer
  * @idev: Input device
@@ -49,6 +51,7 @@ struct cros_ec_keyb {
        int row_shift;
        const struct matrix_keymap_data *keymap_data;
        bool ghost_filter;
+       uint8_t *valid_keys;
        uint8_t *old_kb_state;
 
        struct device *dev;
@@ -57,39 +60,15 @@ struct cros_ec_keyb {
 };
 
 
-static bool cros_ec_keyb_row_has_ghosting(struct cros_ec_keyb *ckdev,
-                                         uint8_t *buf, int row)
-{
-       int pressed_in_row = 0;
-       int row_has_teeth = 0;
-       int col, mask;
-
-       mask = 1 << row;
-       for (col = 0; col < ckdev->cols; col++) {
-               if (buf[col] & mask) {
-                       pressed_in_row++;
-                       row_has_teeth |= buf[col] & ~mask;
-                       if (pressed_in_row > 1 && row_has_teeth) {
-                               /* ghosting */
-                               dev_dbg(ckdev->dev,
-                                       "ghost found at: r%d c%d, pressed %d, teeth 0x%x\n",
-                                       row, col, pressed_in_row,
-                                       row_has_teeth);
-                               return true;
-                       }
-               }
-       }
-
-       return false;
-}
-
 /*
  * Returns true when there is at least one combination of pressed keys that
  * results in ghosting.
  */
 static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf)
 {
-       int row;
+       int col1, col2, buf1, buf2;
+       struct device *dev = ckdev->dev;
+       uint8_t *valid_keys = ckdev->valid_keys;
 
        /*
         * Ghosting happens if for any pressed key X there are other keys
@@ -103,27 +82,23 @@ static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf)
         *
         * In this case only X, Y, and Z are pressed, but g appears to be
         * pressed too (see Wikipedia).
-        *
-        * We can detect ghosting in a single pass (*) over the keyboard state
-        * by maintaining two arrays.  pressed_in_row counts how many pressed
-        * keys we have found in a row.  row_has_teeth is true if any of the
-        * pressed keys for this row has other pressed keys in its column.  If
-        * at any point of the scan we find that a row has multiple pressed
-        * keys, and at least one of them is at the intersection with a column
-        * with multiple pressed keys, we're sure there is ghosting.
-        * Conversely, if there is ghosting, we will detect such situation for
-        * at least one key during the pass.
-        *
-        * (*) This looks linear in the number of keys, but it's not.  We can
-        * cheat because the number of rows is small.
         */
-       for (row = 0; row < ckdev->rows; row++)
-               if (cros_ec_keyb_row_has_ghosting(ckdev, buf, row))
-                       return true;
+       for (col1 = 0; col1 < ckdev->cols; col1++) {
+               buf1 = buf[col1] & valid_keys[col1];
+               for (col2 = col1 + 1; col2 < ckdev->cols; col2++) {
+                       buf2 = buf[col2] & valid_keys[col2];
+                       if (hweight8(buf1 & buf2) > 1) {
+                               dev_dbg(dev, "ghost found at: B[%02d]:0x%02x & B[%02d]:0x%02x",
+                                       col1, buf1, col2, buf2);
+                               return true;
+                       }
+               }
+       }
 
        return false;
 }
 
+
 /*
  * Compares the new keyboard state to the old one and produces key
  * press/release events accordingly.  The keyboard state is 13 bytes (one byte
@@ -222,6 +197,30 @@ static void cros_ec_keyb_close(struct input_dev *dev)
        free_irq(ec->irq, ckdev);
 }
 
+/*
+ * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW.  Used by
+ * ghosting logic to ignore NULL or virtual keys.
+ */
+static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev)
+{
+       int row, col;
+       int row_shift = ckdev->row_shift;
+       unsigned short *keymap = ckdev->idev->keycode;
+       unsigned short code;
+
+       BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap));
+
+       for (col = 0; col < ckdev->cols; col++) {
+               for (row = 0; row < ckdev->rows; row++) {
+                       code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
+                       if (code && (code != KEY_BATTERY))
+                               ckdev->valid_keys[col] |= 1 << row;
+               }
+               dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n",
+                       col, ckdev->valid_keys[col]);
+       }
+}
+
 static int cros_ec_keyb_probe(struct platform_device *pdev)
 {
        struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
@@ -242,6 +241,11 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
                                            &ckdev->cols);
        if (err)
                return err;
+
+       ckdev->valid_keys = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL);
+       if (!ckdev->valid_keys)
+               return -ENOMEM;
+
        ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL);
        if (!ckdev->old_kb_state)
                return -ENOMEM;
@@ -285,6 +289,8 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
        input_set_capability(idev, EV_MSC, MSC_SCAN);
        input_set_drvdata(idev, ckdev);
        ckdev->idev = idev;
+       cros_ec_keyb_compute_valid_keys(ckdev);
+
        err = input_register_device(ckdev->idev);
        if (err) {
                dev_err(dev, "cannot register input device\n");
index 2ff4425a893b36787afc8a54e6fa9d9a64450419..23297ab6163f562e1202b99c93443260bf2451e9 100644 (file)
@@ -144,6 +144,17 @@ config INPUT_M68K_BEEP
        tristate "M68k Beeper support"
        depends on M68K
 
+config INPUT_MAX77693_HAPTIC
+       tristate "MAXIM MAX77693 haptic controller support"
+       depends on MFD_MAX77693 && PWM
+       select INPUT_FF_MEMLESS
+       help
+         This option enables support for the haptic controller on
+         MAXIM MAX77693 chip.
+
+         To compile this driver as module, choose M here: the
+         module will be called max77693-haptic.
+
 config INPUT_MAX8925_ONKEY
        tristate "MAX8925 ONKEY support"
        depends on MFD_MAX8925
@@ -451,6 +462,16 @@ config HP_SDC_RTC
          Say Y here if you want to support the built-in real time clock
          of the HP SDC controller.
 
+config INPUT_PALMAS_PWRBUTTON
+       tristate "Palmas Power button Driver"
+       depends on MFD_PALMAS
+       help
+         Say Y here if you want to enable power key reporting via the
+         Palmas family of PMICs.
+
+         To compile this driver as a module, choose M here. The module will
+         be called palmas_pwrbutton.
+
 config INPUT_PCF50633_PMU
        tristate "PCF50633 PMU events"
        depends on MFD_PCF50633
@@ -676,4 +697,26 @@ config INPUT_SOC_BUTTON_ARRAY
          To compile this driver as a module, choose M here: the
          module will be called soc_button_array.
 
+config INPUT_DRV260X_HAPTICS
+       tristate "TI DRV260X haptics support"
+       depends on INPUT && I2C && GPIOLIB
+       select INPUT_FF_MEMLESS
+       select REGMAP_I2C
+       help
+         Say Y to enable support for the TI DRV260X haptics driver.
+
+         To compile this driver as a module, choose M here: the
+         module will be called drv260x-haptics.
+
+config INPUT_DRV2667_HAPTICS
+       tristate "TI DRV2667 haptics support"
+       depends on INPUT && I2C
+       select INPUT_FF_MEMLESS
+       select REGMAP_I2C
+       help
+         Say Y to enable support for the TI DRV2667 haptics driver.
+
+         To compile this driver as a module, choose M here: the
+         module will be called drv260x-haptics.
+
 endif
index 4955ad322a0127ad44558f24125bed23271f39a1..19c760361f8021bbc0e283caa65e0fb2b2be9abb 100644 (file)
@@ -26,6 +26,8 @@ obj-$(CONFIG_INPUT_COBALT_BTNS)               += cobalt_btns.o
 obj-$(CONFIG_INPUT_DA9052_ONKEY)       += da9052_onkey.o
 obj-$(CONFIG_INPUT_DA9055_ONKEY)       += da9055_onkey.o
 obj-$(CONFIG_INPUT_DM355EVM)           += dm355evm_keys.o
+obj-$(CONFIG_INPUT_DRV260X_HAPTICS)    += drv260x.o
+obj-$(CONFIG_INPUT_DRV2667_HAPTICS)    += drv2667.o
 obj-$(CONFIG_INPUT_GP2A)               += gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_BEEPER)                += gpio-beeper.o
 obj-$(CONFIG_INPUT_GPIO_TILT_POLLED)   += gpio_tilt_polled.o
@@ -35,11 +37,13 @@ obj-$(CONFIG_INPUT_IXP4XX_BEEPER)   += ixp4xx-beeper.o
 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_MAX8925_ONKEY)      += max8925_onkey.o
 obj-$(CONFIG_INPUT_MAX8997_HAPTIC)     += max8997_haptic.o
 obj-$(CONFIG_INPUT_MC13783_PWRBUTTON)  += mc13783-pwrbutton.o
 obj-$(CONFIG_INPUT_MMA8450)            += mma8450.o
 obj-$(CONFIG_INPUT_MPU3050)            += mpu3050.o
+obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON)   += palmas-pwrbutton.o
 obj-$(CONFIG_INPUT_PCAP)               += pcap_keys.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
 obj-$(CONFIG_INPUT_PCF8574)            += pcf8574_keypad.o
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
new file mode 100644 (file)
index 0000000..cab87f5
--- /dev/null
@@ -0,0 +1,741 @@
+/*
+ * DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, 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 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/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <dt-bindings/input/ti-drv260x.h>
+#include <linux/platform_data/drv260x-pdata.h>
+
+#define DRV260X_STATUS         0x0
+#define DRV260X_MODE           0x1
+#define DRV260X_RT_PB_IN       0x2
+#define DRV260X_LIB_SEL                0x3
+#define DRV260X_WV_SEQ_1       0x4
+#define DRV260X_WV_SEQ_2       0x5
+#define DRV260X_WV_SEQ_3       0x6
+#define DRV260X_WV_SEQ_4       0x7
+#define DRV260X_WV_SEQ_5       0x8
+#define DRV260X_WV_SEQ_6       0x9
+#define DRV260X_WV_SEQ_7       0xa
+#define DRV260X_WV_SEQ_8       0xb
+#define DRV260X_GO                             0xc
+#define DRV260X_OVERDRIVE_OFF  0xd
+#define DRV260X_SUSTAIN_P_OFF  0xe
+#define DRV260X_SUSTAIN_N_OFF  0xf
+#define DRV260X_BRAKE_OFF              0x10
+#define DRV260X_A_TO_V_CTRL            0x11
+#define DRV260X_A_TO_V_MIN_INPUT       0x12
+#define DRV260X_A_TO_V_MAX_INPUT       0x13
+#define DRV260X_A_TO_V_MIN_OUT 0x14
+#define DRV260X_A_TO_V_MAX_OUT 0x15
+#define DRV260X_RATED_VOLT             0x16
+#define DRV260X_OD_CLAMP_VOLT  0x17
+#define DRV260X_CAL_COMP               0x18
+#define DRV260X_CAL_BACK_EMF   0x19
+#define DRV260X_FEEDBACK_CTRL  0x1a
+#define DRV260X_CTRL1                  0x1b
+#define DRV260X_CTRL2                  0x1c
+#define DRV260X_CTRL3                  0x1d
+#define DRV260X_CTRL4                  0x1e
+#define DRV260X_CTRL5                  0x1f
+#define DRV260X_LRA_LOOP_PERIOD        0x20
+#define DRV260X_VBAT_MON               0x21
+#define DRV260X_LRA_RES_PERIOD 0x22
+#define DRV260X_MAX_REG                        0x23
+
+#define DRV260X_GO_BIT                         0x01
+
+/* Library Selection */
+#define DRV260X_LIB_SEL_MASK           0x07
+#define DRV260X_LIB_SEL_RAM                    0x0
+#define DRV260X_LIB_SEL_OD                     0x1
+#define DRV260X_LIB_SEL_40_60          0x2
+#define DRV260X_LIB_SEL_60_80          0x3
+#define DRV260X_LIB_SEL_100_140                0x4
+#define DRV260X_LIB_SEL_140_PLUS       0x5
+
+#define DRV260X_LIB_SEL_HIZ_MASK       0x10
+#define DRV260X_LIB_SEL_HIZ_EN         0x01
+#define DRV260X_LIB_SEL_HIZ_DIS                0
+
+/* Mode register */
+#define DRV260X_STANDBY                                (1 << 6)
+#define DRV260X_STANDBY_MASK           0x40
+#define DRV260X_INTERNAL_TRIGGER       0x00
+#define DRV260X_EXT_TRIGGER_EDGE       0x01
+#define DRV260X_EXT_TRIGGER_LEVEL      0x02
+#define DRV260X_PWM_ANALOG_IN          0x03
+#define DRV260X_AUDIOHAPTIC                    0x04
+#define DRV260X_RT_PLAYBACK                    0x05
+#define DRV260X_DIAGNOSTICS                    0x06
+#define DRV260X_AUTO_CAL                       0x07
+
+/* Audio to Haptics Control */
+#define DRV260X_AUDIO_HAPTICS_PEAK_10MS                (0 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_20MS                (1 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_30MS                (2 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_40MS                (3 << 2)
+
+#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ     0x00
+#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ     0x01
+#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ     0x02
+#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ     0x03
+
+/* Min/Max Input/Output Voltages */
+#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT      0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT      0x64
+#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT     0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT     0xFF
+
+/* Feedback register */
+#define DRV260X_FB_REG_ERM_MODE                        0x7f
+#define DRV260X_FB_REG_LRA_MODE                        (1 << 7)
+
+#define DRV260X_BRAKE_FACTOR_MASK      0x1f
+#define DRV260X_BRAKE_FACTOR_2X                (1 << 0)
+#define DRV260X_BRAKE_FACTOR_3X                (2 << 4)
+#define DRV260X_BRAKE_FACTOR_4X                (3 << 4)
+#define DRV260X_BRAKE_FACTOR_6X                (4 << 4)
+#define DRV260X_BRAKE_FACTOR_8X                (5 << 4)
+#define DRV260X_BRAKE_FACTOR_16                (6 << 4)
+#define DRV260X_BRAKE_FACTOR_DIS       (7 << 4)
+
+#define DRV260X_LOOP_GAIN_LOW          0xf3
+#define DRV260X_LOOP_GAIN_MED          (1 << 2)
+#define DRV260X_LOOP_GAIN_HIGH         (2 << 2)
+#define DRV260X_LOOP_GAIN_VERY_HIGH    (3 << 2)
+
+#define DRV260X_BEMF_GAIN_0                    0xfc
+#define DRV260X_BEMF_GAIN_1            (1 << 0)
+#define DRV260X_BEMF_GAIN_2            (2 << 0)
+#define DRV260X_BEMF_GAIN_3            (3 << 0)
+
+/* Control 1 register */
+#define DRV260X_AC_CPLE_EN                     (1 << 5)
+#define DRV260X_STARTUP_BOOST          (1 << 7)
+
+/* Control 2 register */
+
+#define DRV260X_IDISS_TIME_45          0
+#define DRV260X_IDISS_TIME_75          (1 << 0)
+#define DRV260X_IDISS_TIME_150         (1 << 1)
+#define DRV260X_IDISS_TIME_225         0x03
+
+#define DRV260X_BLANK_TIME_45  (0 << 2)
+#define DRV260X_BLANK_TIME_75  (1 << 2)
+#define DRV260X_BLANK_TIME_150 (2 << 2)
+#define DRV260X_BLANK_TIME_225 (3 << 2)
+
+#define DRV260X_SAMP_TIME_150  (0 << 4)
+#define DRV260X_SAMP_TIME_200  (1 << 4)
+#define DRV260X_SAMP_TIME_250  (2 << 4)
+#define DRV260X_SAMP_TIME_300  (3 << 4)
+
+#define DRV260X_BRAKE_STABILIZER       (1 << 6)
+#define DRV260X_UNIDIR_IN                      (0 << 7)
+#define DRV260X_BIDIR_IN                       (1 << 7)
+
+/* Control 3 Register */
+#define DRV260X_LRA_OPEN_LOOP          (1 << 0)
+#define DRV260X_ANANLOG_IN                     (1 << 1)
+#define DRV260X_LRA_DRV_MODE           (1 << 2)
+#define DRV260X_RTP_UNSIGNED_DATA      (1 << 3)
+#define DRV260X_SUPPLY_COMP_DIS                (1 << 4)
+#define DRV260X_ERM_OPEN_LOOP          (1 << 5)
+#define DRV260X_NG_THRESH_0                    (0 << 6)
+#define DRV260X_NG_THRESH_2                    (1 << 6)
+#define DRV260X_NG_THRESH_4                    (2 << 6)
+#define DRV260X_NG_THRESH_8                    (3 << 6)
+
+/* Control 4 Register */
+#define DRV260X_AUTOCAL_TIME_150MS             (0 << 4)
+#define DRV260X_AUTOCAL_TIME_250MS             (1 << 4)
+#define DRV260X_AUTOCAL_TIME_500MS             (2 << 4)
+#define DRV260X_AUTOCAL_TIME_1000MS            (3 << 4)
+
+/**
+ * struct drv260x_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @enable_gpio - Pointer to the gpio used for enable/disabling
+ * @regulator - Pointer to the regulator for the IC
+ * @magnitude - Magnitude of the vibration event
+ * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
+ * @library - The vibration library to be used
+ * @rated_voltage - The rated_voltage of the actuator
+ * @overdriver_voltage - The over drive voltage of the actuator
+**/
+struct drv260x_data {
+       struct input_dev *input_dev;
+       struct i2c_client *client;
+       struct regmap *regmap;
+       struct work_struct work;
+       struct gpio_desc *enable_gpio;
+       struct regulator *regulator;
+       u32 magnitude;
+       u32 mode;
+       u32 library;
+       int rated_voltage;
+       int overdrive_voltage;
+};
+
+static struct reg_default drv260x_reg_defs[] = {
+       { DRV260X_STATUS, 0xe0 },
+       { DRV260X_MODE, 0x40 },
+       { DRV260X_RT_PB_IN, 0x00 },
+       { DRV260X_LIB_SEL, 0x00 },
+       { DRV260X_WV_SEQ_1, 0x01 },
+       { DRV260X_WV_SEQ_2, 0x00 },
+       { DRV260X_WV_SEQ_3, 0x00 },
+       { DRV260X_WV_SEQ_4, 0x00 },
+       { DRV260X_WV_SEQ_5, 0x00 },
+       { DRV260X_WV_SEQ_6, 0x00 },
+       { DRV260X_WV_SEQ_7, 0x00 },
+       { DRV260X_WV_SEQ_8, 0x00 },
+       { DRV260X_GO, 0x00 },
+       { DRV260X_OVERDRIVE_OFF, 0x00 },
+       { DRV260X_SUSTAIN_P_OFF, 0x00 },
+       { DRV260X_SUSTAIN_N_OFF, 0x00 },
+       { DRV260X_BRAKE_OFF, 0x00 },
+       { DRV260X_A_TO_V_CTRL, 0x05 },
+       { DRV260X_A_TO_V_MIN_INPUT, 0x19 },
+       { DRV260X_A_TO_V_MAX_INPUT, 0xff },
+       { DRV260X_A_TO_V_MIN_OUT, 0x19 },
+       { DRV260X_A_TO_V_MAX_OUT, 0xff },
+       { DRV260X_RATED_VOLT, 0x3e },
+       { DRV260X_OD_CLAMP_VOLT, 0x8c },
+       { DRV260X_CAL_COMP, 0x0c },
+       { DRV260X_CAL_BACK_EMF, 0x6c },
+       { DRV260X_FEEDBACK_CTRL, 0x36 },
+       { DRV260X_CTRL1, 0x93 },
+       { DRV260X_CTRL2, 0xfa },
+       { DRV260X_CTRL3, 0xa0 },
+       { DRV260X_CTRL4, 0x20 },
+       { DRV260X_CTRL5, 0x80 },
+       { DRV260X_LRA_LOOP_PERIOD, 0x33 },
+       { DRV260X_VBAT_MON, 0x00 },
+       { DRV260X_LRA_RES_PERIOD, 0x00 },
+};
+
+#define DRV260X_DEF_RATED_VOLT         0x90
+#define DRV260X_DEF_OD_CLAMP_VOLT      0x90
+
+/**
+ * Rated and Overdriver Voltages:
+ * Calculated using the formula r = v * 255 / 5.6
+ * where r is what will be written to the register
+ * and v is the rated or overdriver voltage of the actuator
+ **/
+static int drv260x_calculate_voltage(unsigned int voltage)
+{
+       return (voltage * 255 / 5600);
+}
+
+static void drv260x_worker(struct work_struct *work)
+{
+       struct drv260x_data *haptics = container_of(work, struct drv260x_data, work);
+       int error;
+
+       gpiod_set_value(haptics->enable_gpio, 1);
+       /* Data sheet says to wait 250us before trying to communicate */
+       udelay(250);
+
+       error = regmap_write(haptics->regmap,
+                            DRV260X_MODE, DRV260X_RT_PLAYBACK);
+       if (error) {
+               dev_err(&haptics->client->dev,
+                       "Failed to write set mode: %d\n", error);
+       } else {
+               error = regmap_write(haptics->regmap,
+                                    DRV260X_RT_PB_IN, haptics->magnitude);
+               if (error)
+                       dev_err(&haptics->client->dev,
+                               "Failed to set magnitude: %d\n", error);
+       }
+}
+
+static int drv260x_haptics_play(struct input_dev *input, void *data,
+                               struct ff_effect *effect)
+{
+       struct drv260x_data *haptics = input_get_drvdata(input);
+
+       haptics->mode = DRV260X_LRA_NO_CAL_MODE;
+
+       if (effect->u.rumble.strong_magnitude > 0)
+               haptics->magnitude = effect->u.rumble.strong_magnitude;
+       else if (effect->u.rumble.weak_magnitude > 0)
+               haptics->magnitude = effect->u.rumble.weak_magnitude;
+       else
+               haptics->magnitude = 0;
+
+       schedule_work(&haptics->work);
+
+       return 0;
+}
+
+static void drv260x_close(struct input_dev *input)
+{
+       struct drv260x_data *haptics = input_get_drvdata(input);
+       int error;
+
+       cancel_work_sync(&haptics->work);
+
+       error = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
+       if (error)
+               dev_err(&haptics->client->dev,
+                       "Failed to enter standby mode: %d\n", error);
+
+       gpiod_set_value(haptics->enable_gpio, 0);
+}
+
+static const struct reg_default drv260x_lra_cal_regs[] = {
+       { DRV260X_MODE, DRV260X_AUTO_CAL },
+       { DRV260X_CTRL3, DRV260X_NG_THRESH_2 },
+       { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE |
+               DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
+};
+
+static const struct reg_default drv260x_lra_init_regs[] = {
+       { DRV260X_MODE, DRV260X_RT_PLAYBACK },
+       { DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS |
+               DRV260X_AUDIO_HAPTICS_FILTER_125HZ },
+       { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+       { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+       { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+       { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+       { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE |
+               DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED |
+               DRV260X_BEMF_GAIN_3 },
+       { DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+       { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 },
+       { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN },
+       { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static const struct reg_default drv260x_erm_cal_regs[] = {
+       { DRV260X_MODE, DRV260X_AUTO_CAL },
+       { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+       { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+       { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+       { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+       { DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X |
+               DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 },
+       { DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+       { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 |
+               DRV260X_IDISS_TIME_75 },
+       { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP },
+       { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static int drv260x_init(struct drv260x_data *haptics)
+{
+       int error;
+       unsigned int cal_buf;
+
+       error = regmap_write(haptics->regmap,
+                            DRV260X_RATED_VOLT, haptics->rated_voltage);
+       if (error) {
+               dev_err(&haptics->client->dev,
+                       "Failed to write DRV260X_RATED_VOLT register: %d\n",
+                       error);
+               return error;
+       }
+
+       error = regmap_write(haptics->regmap,
+                            DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage);
+       if (error) {
+               dev_err(&haptics->client->dev,
+                       "Failed to write DRV260X_OD_CLAMP_VOLT register: %d\n",
+                       error);
+               return error;
+       }
+
+       switch (haptics->mode) {
+       case DRV260X_LRA_MODE:
+               error = regmap_register_patch(haptics->regmap,
+                                             drv260x_lra_cal_regs,
+                                             ARRAY_SIZE(drv260x_lra_cal_regs));
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to write LRA calibration registers: %d\n",
+                               error);
+                       return error;
+               }
+
+               break;
+
+       case DRV260X_ERM_MODE:
+               error = regmap_register_patch(haptics->regmap,
+                                             drv260x_erm_cal_regs,
+                                             ARRAY_SIZE(drv260x_erm_cal_regs));
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to write ERM calibration registers: %d\n",
+                               error);
+                       return error;
+               }
+
+               error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+                                          DRV260X_LIB_SEL_MASK,
+                                          haptics->library);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to write DRV260X_LIB_SEL register: %d\n",
+                               error);
+                       return error;
+               }
+
+               break;
+
+       default:
+               error = regmap_register_patch(haptics->regmap,
+                                             drv260x_lra_init_regs,
+                                             ARRAY_SIZE(drv260x_lra_init_regs));
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to write LRA init registers: %d\n",
+                               error);
+                       return error;
+               }
+
+               error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+                                          DRV260X_LIB_SEL_MASK,
+                                          haptics->library);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to write DRV260X_LIB_SEL register: %d\n",
+                               error);
+                       return error;
+               }
+
+               /* No need to set GO bit here */
+               return 0;
+       }
+
+       error = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT);
+       if (error) {
+               dev_err(&haptics->client->dev,
+                       "Failed to write GO register: %d\n",
+                       error);
+               return error;
+       }
+
+       do {
+               error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to read GO register: %d\n",
+                               error);
+                       return error;
+               }
+       } while (cal_buf == DRV260X_GO_BIT);
+
+       return 0;
+}
+
+static const struct regmap_config drv260x_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = DRV260X_MAX_REG,
+       .reg_defaults = drv260x_reg_defs,
+       .num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
+       .cache_type = REGCACHE_NONE,
+};
+
+#ifdef CONFIG_OF
+static int drv260x_parse_dt(struct device *dev,
+                           struct drv260x_data *haptics)
+{
+       struct device_node *np = dev->of_node;
+       unsigned int voltage;
+       int error;
+
+       error = of_property_read_u32(np, "mode", &haptics->mode);
+       if (error) {
+               dev_err(dev, "%s: No entry for mode\n", __func__);
+               return error;
+       }
+
+       error = of_property_read_u32(np, "library-sel", &haptics->library);
+       if (error) {
+               dev_err(dev, "%s: No entry for library selection\n",
+                       __func__);
+               return error;
+       }
+
+       error = of_property_read_u32(np, "vib-rated-mv", &voltage);
+       if (!error)
+               haptics->rated_voltage = drv260x_calculate_voltage(voltage);
+
+
+       error = of_property_read_u32(np, "vib-overdrive-mv", &voltage);
+       if (!error)
+               haptics->overdrive_voltage = drv260x_calculate_voltage(voltage);
+
+       return 0;
+}
+#else
+static inline int drv260x_parse_dt(struct device *dev,
+                                  struct drv260x_data *haptics)
+{
+       dev_err(dev, "no platform data defined\n");
+
+       return -EINVAL;
+}
+#endif
+
+static int drv260x_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       const struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev);
+       struct drv260x_data *haptics;
+       int error;
+
+       haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+       if (!haptics)
+               return -ENOMEM;
+
+       haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
+       haptics->rated_voltage = DRV260X_DEF_RATED_VOLT;
+
+       if (pdata) {
+               haptics->mode = pdata->mode;
+               haptics->library = pdata->library_selection;
+               if (pdata->vib_overdrive_voltage)
+                       haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage);
+               if (pdata->vib_rated_voltage)
+                       haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage);
+       } else if (client->dev.of_node) {
+               error = drv260x_parse_dt(&client->dev, haptics);
+               if (error)
+                       return error;
+       } else {
+               dev_err(&client->dev, "Platform data not set\n");
+               return -ENODEV;
+       }
+
+
+       if (haptics->mode < DRV260X_LRA_MODE ||
+           haptics->mode > DRV260X_ERM_MODE) {
+               dev_err(&client->dev,
+                       "Vibrator mode is invalid: %i\n",
+                       haptics->mode);
+               return -EINVAL;
+       }
+
+       if (haptics->library < DRV260X_LIB_EMPTY ||
+           haptics->library > DRV260X_ERM_LIB_F) {
+               dev_err(&client->dev,
+                       "Library value is invalid: %i\n", haptics->library);
+               return -EINVAL;
+       }
+
+       if (haptics->mode == DRV260X_LRA_MODE &&
+           haptics->library != DRV260X_LIB_EMPTY &&
+           haptics->library != DRV260X_LIB_LRA) {
+               dev_err(&client->dev,
+                       "LRA Mode with ERM Library mismatch\n");
+               return -EINVAL;
+       }
+
+       if (haptics->mode == DRV260X_ERM_MODE &&
+           (haptics->library == DRV260X_LIB_EMPTY ||
+            haptics->library == DRV260X_LIB_LRA)) {
+               dev_err(&client->dev,
+                       "ERM Mode with LRA Library mismatch\n");
+               return -EINVAL;
+       }
+
+       haptics->regulator = devm_regulator_get(&client->dev, "vbat");
+       if (IS_ERR(haptics->regulator)) {
+               error = PTR_ERR(haptics->regulator);
+               dev_err(&client->dev,
+                       "unable to get regulator, error: %d\n", error);
+               return error;
+       }
+
+       haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable");
+       if (IS_ERR(haptics->enable_gpio)) {
+               error = PTR_ERR(haptics->enable_gpio);
+               if (error != -ENOENT && error != -ENOSYS)
+                       return error;
+               haptics->enable_gpio = NULL;
+       } else {
+               gpiod_direction_output(haptics->enable_gpio, 1);
+       }
+
+       haptics->input_dev = devm_input_allocate_device(&client->dev);
+       if (!haptics->input_dev) {
+               dev_err(&client->dev, "Failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       haptics->input_dev->name = "drv260x:haptics";
+       haptics->input_dev->dev.parent = client->dev.parent;
+       haptics->input_dev->close = drv260x_close;
+       input_set_drvdata(haptics->input_dev, haptics);
+       input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+       error = input_ff_create_memless(haptics->input_dev, NULL,
+                                       drv260x_haptics_play);
+       if (error) {
+               dev_err(&client->dev, "input_ff_create() failed: %d\n",
+                       error);
+               return error;
+       }
+
+       INIT_WORK(&haptics->work, drv260x_worker);
+
+       haptics->client = client;
+       i2c_set_clientdata(client, haptics);
+
+       haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config);
+       if (IS_ERR(haptics->regmap)) {
+               error = PTR_ERR(haptics->regmap);
+               dev_err(&client->dev, "Failed to allocate register map: %d\n",
+                       error);
+               return error;
+       }
+
+       error = drv260x_init(haptics);
+       if (error) {
+               dev_err(&client->dev, "Device init failed: %d\n", error);
+               return error;
+       }
+
+       error = input_register_device(haptics->input_dev);
+       if (error) {
+               dev_err(&client->dev, "couldn't register input device: %d\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int drv260x_suspend(struct device *dev)
+{
+       struct drv260x_data *haptics = dev_get_drvdata(dev);
+       int ret = 0;
+
+       mutex_lock(&haptics->input_dev->mutex);
+
+       if (haptics->input_dev->users) {
+               ret = regmap_update_bits(haptics->regmap,
+                                        DRV260X_MODE,
+                                        DRV260X_STANDBY_MASK,
+                                        DRV260X_STANDBY);
+               if (ret) {
+                       dev_err(dev, "Failed to set standby mode\n");
+                       goto out;
+               }
+
+               gpiod_set_value(haptics->enable_gpio, 0);
+
+               ret = regulator_disable(haptics->regulator);
+               if (ret) {
+                       dev_err(dev, "Failed to disable regulator\n");
+                       regmap_update_bits(haptics->regmap,
+                                          DRV260X_MODE,
+                                          DRV260X_STANDBY_MASK, 0);
+               }
+       }
+out:
+       mutex_unlock(&haptics->input_dev->mutex);
+       return ret;
+}
+
+static int drv260x_resume(struct device *dev)
+{
+       struct drv260x_data *haptics = dev_get_drvdata(dev);
+       int ret = 0;
+
+       mutex_lock(&haptics->input_dev->mutex);
+
+       if (haptics->input_dev->users) {
+               ret = regulator_enable(haptics->regulator);
+               if (ret) {
+                       dev_err(dev, "Failed to enable regulator\n");
+                       goto out;
+               }
+
+               ret = regmap_update_bits(haptics->regmap,
+                                        DRV260X_MODE,
+                                        DRV260X_STANDBY_MASK, 0);
+               if (ret) {
+                       dev_err(dev, "Failed to unset standby mode\n");
+                       regulator_disable(haptics->regulator);
+                       goto out;
+               }
+
+               gpiod_set_value(haptics->enable_gpio, 1);
+       }
+
+out:
+       mutex_unlock(&haptics->input_dev->mutex);
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
+
+static const struct i2c_device_id drv260x_id[] = {
+       { "drv2605l", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, drv260x_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv260x_of_match[] = {
+       { .compatible = "ti,drv2604", },
+       { .compatible = "ti,drv2604l", },
+       { .compatible = "ti,drv2605", },
+       { .compatible = "ti,drv2605l", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, drv260x_of_match);
+#endif
+
+static struct i2c_driver drv260x_driver = {
+       .probe          = drv260x_probe,
+       .driver         = {
+               .name   = "drv260x-haptics",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(drv260x_of_match),
+               .pm     = &drv260x_pm_ops,
+       },
+       .id_table = drv260x_id,
+};
+module_i2c_driver(drv260x_driver);
+
+MODULE_ALIAS("platform:drv260x-haptics");
+MODULE_DESCRIPTION("TI DRV260x haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c
new file mode 100644 (file)
index 0000000..0f43758
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * DRV2667 haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright: (C) 2014 Texas Instruments, 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 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/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+/* Contol registers */
+#define DRV2667_STATUS 0x00
+#define DRV2667_CTRL_1 0x01
+#define DRV2667_CTRL_2 0x02
+/* Waveform sequencer */
+#define DRV2667_WV_SEQ_0       0x03
+#define DRV2667_WV_SEQ_1       0x04
+#define DRV2667_WV_SEQ_2       0x05
+#define DRV2667_WV_SEQ_3       0x06
+#define DRV2667_WV_SEQ_4       0x07
+#define DRV2667_WV_SEQ_5       0x08
+#define DRV2667_WV_SEQ_6       0x09
+#define DRV2667_WV_SEQ_7       0x0A
+#define DRV2667_FIFO           0x0B
+#define DRV2667_PAGE           0xFF
+#define DRV2667_MAX_REG                DRV2667_PAGE
+
+#define DRV2667_PAGE_0         0x00
+#define DRV2667_PAGE_1         0x01
+#define DRV2667_PAGE_2         0x02
+#define DRV2667_PAGE_3         0x03
+#define DRV2667_PAGE_4         0x04
+#define DRV2667_PAGE_5         0x05
+#define DRV2667_PAGE_6         0x06
+#define DRV2667_PAGE_7         0x07
+#define DRV2667_PAGE_8         0x08
+
+/* RAM fields */
+#define DRV2667_RAM_HDR_SZ     0x0
+/* RAM Header addresses */
+#define DRV2667_RAM_START_HI   0x01
+#define DRV2667_RAM_START_LO   0x02
+#define DRV2667_RAM_STOP_HI            0x03
+#define DRV2667_RAM_STOP_LO            0x04
+#define DRV2667_RAM_REPEAT_CT  0x05
+/* RAM data addresses */
+#define DRV2667_RAM_AMP                0x06
+#define DRV2667_RAM_FREQ       0x07
+#define DRV2667_RAM_DURATION   0x08
+#define DRV2667_RAM_ENVELOPE   0x09
+
+/* Control 1 Register */
+#define DRV2667_25_VPP_GAIN            0x00
+#define DRV2667_50_VPP_GAIN            0x01
+#define DRV2667_75_VPP_GAIN            0x02
+#define DRV2667_100_VPP_GAIN   0x03
+#define DRV2667_DIGITAL_IN             0xfc
+#define DRV2667_ANALOG_IN              (1 << 2)
+
+/* Control 2 Register */
+#define DRV2667_GO                     (1 << 0)
+#define DRV2667_STANDBY                (1 << 6)
+#define DRV2667_DEV_RST                (1 << 7)
+
+/* RAM Envelope settings */
+#define DRV2667_NO_ENV                 0x00
+#define DRV2667_32_MS_ENV              0x01
+#define DRV2667_64_MS_ENV              0x02
+#define DRV2667_96_MS_ENV              0x03
+#define DRV2667_128_MS_ENV             0x04
+#define DRV2667_160_MS_ENV             0x05
+#define DRV2667_192_MS_ENV             0x06
+#define DRV2667_224_MS_ENV             0x07
+#define DRV2667_256_MS_ENV             0x08
+#define DRV2667_512_MS_ENV             0x09
+#define DRV2667_768_MS_ENV             0x0a
+#define DRV2667_1024_MS_ENV            0x0b
+#define DRV2667_1280_MS_ENV            0x0c
+#define DRV2667_1536_MS_ENV            0x0d
+#define DRV2667_1792_MS_ENV            0x0e
+#define DRV2667_2048_MS_ENV            0x0f
+
+/**
+ * struct drv2667_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @regulator - Pointer to the regulator for the IC
+ * @magnitude - Magnitude of the vibration event
+**/
+struct drv2667_data {
+       struct input_dev *input_dev;
+       struct i2c_client *client;
+       struct regmap *regmap;
+       struct work_struct work;
+       struct regulator *regulator;
+       u32 page;
+       u32 magnitude;
+       u32 frequency;
+};
+
+static struct reg_default drv2667_reg_defs[] = {
+       { DRV2667_STATUS, 0x02 },
+       { DRV2667_CTRL_1, 0x28 },
+       { DRV2667_CTRL_2, 0x40 },
+       { DRV2667_WV_SEQ_0, 0x00 },
+       { DRV2667_WV_SEQ_1, 0x00 },
+       { DRV2667_WV_SEQ_2, 0x00 },
+       { DRV2667_WV_SEQ_3, 0x00 },
+       { DRV2667_WV_SEQ_4, 0x00 },
+       { DRV2667_WV_SEQ_5, 0x00 },
+       { DRV2667_WV_SEQ_6, 0x00 },
+       { DRV2667_WV_SEQ_7, 0x00 },
+       { DRV2667_FIFO, 0x00 },
+       { DRV2667_PAGE, 0x00 },
+};
+
+static int drv2667_set_waveform_freq(struct drv2667_data *haptics)
+{
+       unsigned int read_buf;
+       int freq;
+       int error;
+
+       /* Per the data sheet:
+        * Sinusoid Frequency (Hz) = 7.8125 x Frequency
+        */
+       freq = (haptics->frequency * 1000) / 78125;
+       if (freq <= 0) {
+               dev_err(&haptics->client->dev,
+                       "ERROR: Frequency calculated to %i\n", freq);
+               return -EINVAL;
+       }
+
+       error = regmap_read(haptics->regmap, DRV2667_PAGE, &read_buf);
+       if (error) {
+               dev_err(&haptics->client->dev,
+                       "Failed to read the page number: %d\n", error);
+               return -EIO;
+       }
+
+       if (read_buf == DRV2667_PAGE_0 ||
+               haptics->page != read_buf) {
+               error = regmap_write(haptics->regmap,
+                               DRV2667_PAGE, haptics->page);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to set the page: %d\n", error);
+                       return -EIO;
+               }
+       }
+
+       error = regmap_write(haptics->regmap, DRV2667_RAM_FREQ, freq);
+       if (error)
+               dev_err(&haptics->client->dev,
+                               "Failed to set the frequency: %d\n", error);
+
+       /* Reset back to original page */
+       if (read_buf == DRV2667_PAGE_0 ||
+               haptics->page != read_buf) {
+               error = regmap_write(haptics->regmap, DRV2667_PAGE, read_buf);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                                       "Failed to set the page: %d\n", error);
+                               return -EIO;
+                       }
+       }
+
+       return error;
+}
+
+static void drv2667_worker(struct work_struct *work)
+{
+       struct drv2667_data *haptics = container_of(work, struct drv2667_data, work);
+       int error;
+
+       if (haptics->magnitude) {
+               error = regmap_write(haptics->regmap,
+                               DRV2667_PAGE, haptics->page);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to set the page: %d\n", error);
+                       return;
+               }
+
+               error = regmap_write(haptics->regmap, DRV2667_RAM_AMP,
+                               haptics->magnitude);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to set the amplitude: %d\n", error);
+                       return;
+               }
+
+               error = regmap_write(haptics->regmap,
+                               DRV2667_PAGE, DRV2667_PAGE_0);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to set the page: %d\n", error);
+                       return;
+               }
+
+               error = regmap_write(haptics->regmap,
+                               DRV2667_CTRL_2, DRV2667_GO);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to set the GO bit: %d\n", error);
+               }
+       } else {
+               error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+                               DRV2667_GO, 0);
+               if (error) {
+                       dev_err(&haptics->client->dev,
+                               "Failed to unset the GO bit: %d\n", error);
+               }
+       }
+}
+
+static int drv2667_haptics_play(struct input_dev *input, void *data,
+                               struct ff_effect *effect)
+{
+       struct drv2667_data *haptics = input_get_drvdata(input);
+
+       if (effect->u.rumble.strong_magnitude > 0)
+               haptics->magnitude = effect->u.rumble.strong_magnitude;
+       else if (effect->u.rumble.weak_magnitude > 0)
+               haptics->magnitude = effect->u.rumble.weak_magnitude;
+       else
+               haptics->magnitude = 0;
+
+       schedule_work(&haptics->work);
+
+       return 0;
+}
+
+static void drv2667_close(struct input_dev *input)
+{
+       struct drv2667_data *haptics = input_get_drvdata(input);
+       int error;
+
+       cancel_work_sync(&haptics->work);
+
+       error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+                               DRV2667_STANDBY, 1);
+       if (error)
+               dev_err(&haptics->client->dev,
+                       "Failed to enter standby mode: %d\n", error);
+}
+
+static const struct reg_default drv2667_init_regs[] = {
+       { DRV2667_CTRL_2, 0 },
+       { DRV2667_CTRL_1, DRV2667_25_VPP_GAIN },
+       { DRV2667_WV_SEQ_0, 1 },
+       { DRV2667_WV_SEQ_1, 0 }
+};
+
+static const struct reg_default drv2667_page1_init[] = {
+       { DRV2667_RAM_HDR_SZ, 0x05 },
+       { DRV2667_RAM_START_HI, 0x80 },
+       { DRV2667_RAM_START_LO, 0x06 },
+       { DRV2667_RAM_STOP_HI, 0x00 },
+       { DRV2667_RAM_STOP_LO, 0x09 },
+       { DRV2667_RAM_REPEAT_CT, 0 },
+       { DRV2667_RAM_DURATION, 0x05 },
+       { DRV2667_RAM_ENVELOPE, DRV2667_NO_ENV },
+       { DRV2667_RAM_AMP, 0x60 },
+};
+
+static int drv2667_init(struct drv2667_data *haptics)
+{
+       int error;
+
+       /* Set default haptic frequency to 195Hz on Page 1*/
+       haptics->frequency = 195;
+       haptics->page = DRV2667_PAGE_1;
+
+       error = regmap_register_patch(haptics->regmap,
+                                     drv2667_init_regs,
+                                     ARRAY_SIZE(drv2667_init_regs));
+       if (error) {
+               dev_err(&haptics->client->dev,
+                       "Failed to write init registers: %d\n",
+                       error);
+               return error;
+       }
+
+       error = regmap_write(haptics->regmap, DRV2667_PAGE, haptics->page);
+       if (error) {
+               dev_err(&haptics->client->dev, "Failed to set page: %d\n",
+                       error);
+               goto error_out;
+       }
+
+       error = drv2667_set_waveform_freq(haptics);
+       if (error)
+               goto error_page;
+
+       error = regmap_register_patch(haptics->regmap,
+                                     drv2667_page1_init,
+                                     ARRAY_SIZE(drv2667_page1_init));
+       if (error) {
+               dev_err(&haptics->client->dev,
+                       "Failed to write page registers: %d\n",
+                       error);
+               return error;
+       }
+
+       error = regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0);
+       return error;
+
+error_page:
+       regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0);
+error_out:
+       return error;
+}
+
+static const struct regmap_config drv2667_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = DRV2667_MAX_REG,
+       .reg_defaults = drv2667_reg_defs,
+       .num_reg_defaults = ARRAY_SIZE(drv2667_reg_defs),
+       .cache_type = REGCACHE_NONE,
+};
+
+static int drv2667_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct drv2667_data *haptics;
+       int error;
+
+       haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+       if (!haptics)
+               return -ENOMEM;
+
+       haptics->regulator = devm_regulator_get(&client->dev, "vbat");
+       if (IS_ERR(haptics->regulator)) {
+               error = PTR_ERR(haptics->regulator);
+               dev_err(&client->dev,
+                       "unable to get regulator, error: %d\n", error);
+               return error;
+       }
+
+       haptics->input_dev = devm_input_allocate_device(&client->dev);
+       if (!haptics->input_dev) {
+               dev_err(&client->dev, "Failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       haptics->input_dev->name = "drv2667:haptics";
+       haptics->input_dev->dev.parent = client->dev.parent;
+       haptics->input_dev->close = drv2667_close;
+       input_set_drvdata(haptics->input_dev, haptics);
+       input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+       error = input_ff_create_memless(haptics->input_dev, NULL,
+                                       drv2667_haptics_play);
+       if (error) {
+               dev_err(&client->dev, "input_ff_create() failed: %d\n",
+                       error);
+               return error;
+       }
+
+       INIT_WORK(&haptics->work, drv2667_worker);
+
+       haptics->client = client;
+       i2c_set_clientdata(client, haptics);
+
+       haptics->regmap = devm_regmap_init_i2c(client, &drv2667_regmap_config);
+       if (IS_ERR(haptics->regmap)) {
+               error = PTR_ERR(haptics->regmap);
+               dev_err(&client->dev, "Failed to allocate register map: %d\n",
+                       error);
+               return error;
+       }
+
+       error = drv2667_init(haptics);
+       if (error) {
+               dev_err(&client->dev, "Device init failed: %d\n", error);
+               return error;
+       }
+
+       error = input_register_device(haptics->input_dev);
+       if (error) {
+               dev_err(&client->dev, "couldn't register input device: %d\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int drv2667_suspend(struct device *dev)
+{
+       struct drv2667_data *haptics = dev_get_drvdata(dev);
+       int ret = 0;
+
+       mutex_lock(&haptics->input_dev->mutex);
+
+       if (haptics->input_dev->users) {
+               ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+                               DRV2667_STANDBY, 1);
+               if (ret) {
+                       dev_err(dev, "Failed to set standby mode\n");
+                       regulator_disable(haptics->regulator);
+                       goto out;
+               }
+
+               ret = regulator_disable(haptics->regulator);
+               if (ret) {
+                       dev_err(dev, "Failed to disable regulator\n");
+                       regmap_update_bits(haptics->regmap,
+                                          DRV2667_CTRL_2,
+                                          DRV2667_STANDBY, 0);
+               }
+       }
+out:
+       mutex_unlock(&haptics->input_dev->mutex);
+       return ret;
+}
+
+static int drv2667_resume(struct device *dev)
+{
+       struct drv2667_data *haptics = dev_get_drvdata(dev);
+       int ret = 0;
+
+       mutex_lock(&haptics->input_dev->mutex);
+
+       if (haptics->input_dev->users) {
+               ret = regulator_enable(haptics->regulator);
+               if (ret) {
+                       dev_err(dev, "Failed to enable regulator\n");
+                       goto out;
+               }
+
+               ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2,
+                                        DRV2667_STANDBY, 0);
+               if (ret) {
+                       dev_err(dev, "Failed to unset standby mode\n");
+                       regulator_disable(haptics->regulator);
+                       goto out;
+               }
+
+       }
+
+out:
+       mutex_unlock(&haptics->input_dev->mutex);
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume);
+
+static const struct i2c_device_id drv2667_id[] = {
+       { "drv2667", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, drv2667_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv2667_of_match[] = {
+       { .compatible = "ti,drv2667", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, drv2667_of_match);
+#endif
+
+static struct i2c_driver drv2667_driver = {
+       .probe          = drv2667_probe,
+       .driver         = {
+               .name   = "drv2667-haptics",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(drv2667_of_match),
+               .pm     = &drv2667_pm_ops,
+       },
+       .id_table = drv2667_id,
+};
+module_i2c_driver(drv2667_driver);
+
+MODULE_ALIAS("platform:drv2667-haptics");
+MODULE_DESCRIPTION("TI DRV2667 haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c
new file mode 100644 (file)
index 0000000..d605db4
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * MAXIM MAX77693 Haptic device driver
+ *
+ * Copyright (C) 2014 Samsung Electronics
+ * Jaewon Kim <jaewon02.kim@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+
+#define MAX_MAGNITUDE_SHIFT    16
+
+enum max77693_haptic_motor_type {
+       MAX77693_HAPTIC_ERM = 0,
+       MAX77693_HAPTIC_LRA,
+};
+
+enum max77693_haptic_pulse_mode {
+       MAX77693_HAPTIC_EXTERNAL_MODE = 0,
+       MAX77693_HAPTIC_INTERNAL_MODE,
+};
+
+enum max77693_haptic_pwm_divisor {
+       MAX77693_HAPTIC_PWM_DIVISOR_32 = 0,
+       MAX77693_HAPTIC_PWM_DIVISOR_64,
+       MAX77693_HAPTIC_PWM_DIVISOR_128,
+       MAX77693_HAPTIC_PWM_DIVISOR_256,
+};
+
+struct max77693_haptic {
+       struct regmap *regmap_pmic;
+       struct regmap *regmap_haptic;
+       struct device *dev;
+       struct input_dev *input_dev;
+       struct pwm_device *pwm_dev;
+       struct regulator *motor_reg;
+
+       bool enabled;
+       bool suspend_state;
+       unsigned int magnitude;
+       unsigned int pwm_duty;
+       enum max77693_haptic_motor_type type;
+       enum max77693_haptic_pulse_mode mode;
+       enum max77693_haptic_pwm_divisor pwm_divisor;
+
+       struct work_struct work;
+};
+
+static int max77693_haptic_set_duty_cycle(struct max77693_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 max77693_haptic_configure(struct max77693_haptic *haptic,
+                                    bool enable)
+{
+       unsigned int value;
+       int error;
+
+       value = ((haptic->type << MAX77693_CONFIG2_MODE) |
+               (enable << MAX77693_CONFIG2_MEN) |
+               (haptic->mode << MAX77693_CONFIG2_HTYP) |
+               (haptic->pwm_divisor));
+
+       error = regmap_write(haptic->regmap_haptic,
+                            MAX77693_HAPTIC_REG_CONFIG2, value);
+       if (error) {
+               dev_err(haptic->dev,
+                       "failed to update haptic config: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable)
+{
+       int error;
+
+       error = regmap_update_bits(haptic->regmap_pmic,
+                                  MAX77693_PMIC_REG_LSCNFG,
+                                  MAX77693_PMIC_LOW_SYS_MASK,
+                                  enable << MAX77693_PMIC_LOW_SYS_SHIFT);
+       if (error) {
+               dev_err(haptic->dev, "cannot update pmic regmap: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+static void max77693_haptic_enable(struct max77693_haptic *haptic)
+{
+       int error;
+
+       if (haptic->enabled)
+               return;
+
+       error = pwm_enable(haptic->pwm_dev);
+       if (error) {
+               dev_err(haptic->dev,
+                       "failed to enable haptic pwm device: %d\n", error);
+               return;
+       }
+
+       error = max77693_haptic_lowsys(haptic, true);
+       if (error)
+               goto err_enable_lowsys;
+
+       error = max77693_haptic_configure(haptic, true);
+       if (error)
+               goto err_enable_config;
+
+       haptic->enabled = true;
+
+       return;
+
+err_enable_config:
+       max77693_haptic_lowsys(haptic, false);
+err_enable_lowsys:
+       pwm_disable(haptic->pwm_dev);
+}
+
+static void max77693_haptic_disable(struct max77693_haptic *haptic)
+{
+       int error;
+
+       if (haptic->enabled)
+               return;
+
+       error = max77693_haptic_configure(haptic, false);
+       if (error)
+               return;
+
+       error = max77693_haptic_lowsys(haptic, false);
+       if (error)
+               goto err_disable_lowsys;
+
+       pwm_disable(haptic->pwm_dev);
+       haptic->enabled = false;
+
+       return;
+
+err_disable_lowsys:
+       max77693_haptic_configure(haptic, true);
+}
+
+static void max77693_haptic_play_work(struct work_struct *work)
+{
+       struct max77693_haptic *haptic =
+                       container_of(work, struct max77693_haptic, work);
+       int error;
+
+       error = max77693_haptic_set_duty_cycle(haptic);
+       if (error) {
+               dev_err(haptic->dev, "failed to set duty cycle: %d\n", error);
+               return;
+       }
+
+       if (haptic->magnitude)
+               max77693_haptic_enable(haptic);
+       else
+               max77693_haptic_disable(haptic);
+}
+
+static int max77693_haptic_play_effect(struct input_dev *dev, void *data,
+                                      struct ff_effect *effect)
+{
+       struct max77693_haptic *haptic = input_get_drvdata(dev);
+       uint64_t period_mag_multi;
+
+       haptic->magnitude = effect->u.rumble.strong_magnitude;
+       if (!haptic->magnitude)
+               haptic->magnitude = effect->u.rumble.weak_magnitude;
+
+       /*
+        * The magnitude comes from force-feedback interface.
+        * The formula to convert magnitude to pwm_duty as follows:
+        * - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF)
+        */
+       period_mag_multi = (int64_t)(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 max77693_haptic_open(struct input_dev *dev)
+{
+       struct max77693_haptic *haptic = input_get_drvdata(dev);
+       int 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 max77693_haptic_close(struct input_dev *dev)
+{
+       struct max77693_haptic *haptic = input_get_drvdata(dev);
+       int error;
+
+       cancel_work_sync(&haptic->work);
+       max77693_haptic_disable(haptic);
+
+       error = regulator_disable(haptic->motor_reg);
+       if (error)
+               dev_err(haptic->dev,
+                       "failed to disable regulator: %d\n", error);
+}
+
+static int max77693_haptic_probe(struct platform_device *pdev)
+{
+       struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
+       struct max77693_haptic *haptic;
+       int error;
+
+       haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
+       if (!haptic)
+               return -ENOMEM;
+
+       haptic->regmap_pmic = max77693->regmap;
+       haptic->regmap_haptic = max77693->regmap_haptic;
+       haptic->dev = &pdev->dev;
+       haptic->type = MAX77693_HAPTIC_LRA;
+       haptic->mode = MAX77693_HAPTIC_EXTERNAL_MODE;
+       haptic->pwm_divisor = MAX77693_HAPTIC_PWM_DIVISOR_128;
+       haptic->suspend_state = false;
+
+       INIT_WORK(&haptic->work, max77693_haptic_play_work);
+
+       /* Get pwm and regulatot for haptic device */
+       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(&pdev->dev, "haptic");
+       if (IS_ERR(haptic->motor_reg)) {
+               dev_err(&pdev->dev, "failed to get regulator\n");
+               return PTR_ERR(haptic->motor_reg);
+       }
+
+       /* Initialize input device for haptic device */
+       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 = "max77693-haptic";
+       haptic->input_dev->id.version = 1;
+       haptic->input_dev->dev.parent = &pdev->dev;
+       haptic->input_dev->open = max77693_haptic_open;
+       haptic->input_dev->close = max77693_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,
+                               max77693_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;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77693_haptic_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct max77693_haptic *haptic = platform_get_drvdata(pdev);
+
+       if (haptic->enabled) {
+               max77693_haptic_disable(haptic);
+               haptic->suspend_state = true;
+       }
+
+       return 0;
+}
+
+static int max77693_haptic_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct max77693_haptic *haptic = platform_get_drvdata(pdev);
+
+       if (haptic->suspend_state) {
+               max77693_haptic_enable(haptic);
+               haptic->suspend_state = false;
+       }
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops,
+                        max77693_haptic_suspend, max77693_haptic_resume);
+
+static struct platform_driver max77693_haptic_driver = {
+       .driver         = {
+               .name   = "max77693-haptic",
+               .owner  = THIS_MODULE,
+               .pm     = &max77693_haptic_pm_ops,
+       },
+       .probe          = max77693_haptic_probe,
+};
+module_platform_driver(max77693_haptic_driver);
+
+MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
+MODULE_DESCRIPTION("MAXIM MAX77693 Haptic driver");
+MODULE_ALIAS("platform:max77693-haptic");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c
new file mode 100644 (file)
index 0000000..f505ac3
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Texas Instruments' Palmas Power Button Input Driver
+ *
+ * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/
+ *     Girish S Ghongdemath
+ *     Nishanth Menon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/palmas.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PALMAS_LPK_TIME_MASK           0x0c
+#define PALMAS_PWRON_DEBOUNCE_MASK     0x03
+#define PALMAS_PWR_KEY_Q_TIME_MS       20
+
+/**
+ * struct palmas_pwron - Palmas power on data
+ * @palmas:            pointer to palmas device
+ * @input_dev:         pointer to input device
+ * @input_work:                work for detecting release of key
+ * @irq:               irq that we are hooked on to
+ */
+struct palmas_pwron {
+       struct palmas *palmas;
+       struct input_dev *input_dev;
+       struct delayed_work input_work;
+       int irq;
+};
+
+/**
+ * struct palmas_pwron_config - configuration of palmas power on
+ * @long_press_time_val:       value for long press h/w shutdown event
+ * @pwron_debounce_val:                value for debounce of power button
+ */
+struct palmas_pwron_config {
+       u8 long_press_time_val;
+       u8 pwron_debounce_val;
+};
+
+/**
+ * palmas_power_button_work() - Detects the button release event
+ * @work:      work item to detect button release
+ */
+static void palmas_power_button_work(struct work_struct *work)
+{
+       struct palmas_pwron *pwron = container_of(work,
+                                                 struct palmas_pwron,
+                                                 input_work.work);
+       struct input_dev *input_dev = pwron->input_dev;
+       unsigned int reg;
+       int error;
+
+       error = palmas_read(pwron->palmas, PALMAS_INTERRUPT_BASE,
+                           PALMAS_INT1_LINE_STATE, &reg);
+       if (error) {
+               dev_err(input_dev->dev.parent,
+                       "Cannot read palmas PWRON status: %d\n", error);
+       } else if (reg & BIT(1)) {
+               /* The button is released, report event. */
+               input_report_key(input_dev, KEY_POWER, 0);
+               input_sync(input_dev);
+       } else {
+               /* The button is still depressed, keep checking. */
+               schedule_delayed_work(&pwron->input_work,
+                               msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
+       }
+}
+
+/**
+ * pwron_irq() - button press isr
+ * @irq:               irq
+ * @palmas_pwron:      pwron struct
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t pwron_irq(int irq, void *palmas_pwron)
+{
+       struct palmas_pwron *pwron = palmas_pwron;
+       struct input_dev *input_dev = pwron->input_dev;
+
+       input_report_key(input_dev, KEY_POWER, 1);
+       pm_wakeup_event(input_dev->dev.parent, 0);
+       input_sync(input_dev);
+
+       mod_delayed_work(system_wq, &pwron->input_work,
+                        msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS));
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * palmas_pwron_params_ofinit() - device tree parameter parser
+ * @dev:       palmas button device
+ * @config:    configuration params that this fills up
+ */
+static void palmas_pwron_params_ofinit(struct device *dev,
+                                      struct palmas_pwron_config *config)
+{
+       struct device_node *np;
+       u32 val;
+       int i, error;
+       u8 lpk_times[] = { 6, 8, 10, 12 };
+       int pwr_on_deb_ms[] = { 15, 100, 500, 1000 };
+
+       memset(config, 0, sizeof(*config));
+
+       /* Default config parameters */
+       config->long_press_time_val = ARRAY_SIZE(lpk_times) - 1;
+
+       np = dev->of_node;
+       if (!np)
+               return;
+
+       error = of_property_read_u32(np, "ti,palmas-long-press-seconds", &val);
+       if (!error) {
+               for (i = 0; i < ARRAY_SIZE(lpk_times); i++) {
+                       if (val <= lpk_times[i]) {
+                               config->long_press_time_val = i;
+                               break;
+                       }
+               }
+       }
+
+       error = of_property_read_u32(np,
+                                    "ti,palmas-pwron-debounce-milli-seconds",
+                                    &val);
+       if (!error) {
+               for (i = 0; i < ARRAY_SIZE(pwr_on_deb_ms); i++) {
+                       if (val <= pwr_on_deb_ms[i]) {
+                               config->pwron_debounce_val = i;
+                               break;
+                       }
+               }
+       }
+
+       dev_info(dev, "h/w controlled shutdown duration=%d seconds\n",
+                lpk_times[config->long_press_time_val]);
+}
+
+/**
+ * palmas_pwron_probe() - probe
+ * @pdev:      platform device for the button
+ *
+ * Return: 0 for successful probe else appropriate error
+ */
+static int palmas_pwron_probe(struct platform_device *pdev)
+{
+       struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+       struct device *dev = &pdev->dev;
+       struct input_dev *input_dev;
+       struct palmas_pwron *pwron;
+       struct palmas_pwron_config config;
+       int val;
+       int error;
+
+       palmas_pwron_params_ofinit(dev, &config);
+
+       pwron = kzalloc(sizeof(*pwron), GFP_KERNEL);
+       if (!pwron)
+               return -ENOMEM;
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(dev, "Can't allocate power button\n");
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       input_dev->name = "palmas_pwron";
+       input_dev->phys = "palmas_pwron/input0";
+       input_dev->dev.parent = dev;
+
+       input_set_capability(input_dev, EV_KEY, KEY_POWER);
+
+       /*
+        * Setup default hardware shutdown option (long key press)
+        * and debounce.
+        */
+       val = config.long_press_time_val << __ffs(PALMAS_LPK_TIME_MASK);
+       val |= config.pwron_debounce_val << __ffs(PALMAS_PWRON_DEBOUNCE_MASK);
+       error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+                                  PALMAS_LONG_PRESS_KEY,
+                                  PALMAS_LPK_TIME_MASK |
+                                       PALMAS_PWRON_DEBOUNCE_MASK,
+                                  val);
+       if (error) {
+               dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error);
+               goto err_free_input;
+       }
+
+       pwron->palmas = palmas;
+       pwron->input_dev = input_dev;
+
+       INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work);
+
+       pwron->irq = platform_get_irq(pdev, 0);
+       error = request_threaded_irq(pwron->irq, NULL, pwron_irq,
+                                    IRQF_TRIGGER_HIGH |
+                                       IRQF_TRIGGER_LOW |
+                                       IRQF_ONESHOT,
+                                    dev_name(dev), pwron);
+       if (error) {
+               dev_err(dev, "Can't get IRQ for pwron: %d\n", error);
+               goto err_free_input;
+       }
+
+       error = input_register_device(input_dev);
+       if (error) {
+               dev_err(dev, "Can't register power button: %d\n", error);
+               goto err_free_irq;
+       }
+
+       platform_set_drvdata(pdev, pwron);
+       device_init_wakeup(dev, true);
+
+       return 0;
+
+err_free_irq:
+       cancel_delayed_work_sync(&pwron->input_work);
+       free_irq(pwron->irq, pwron);
+err_free_input:
+       input_free_device(input_dev);
+err_free_mem:
+       kfree(pwron);
+       return error;
+}
+
+/**
+ * palmas_pwron_remove() - Cleanup on removal
+ * @pdev:      platform device for the button
+ *
+ * Return: 0
+ */
+static int palmas_pwron_remove(struct platform_device *pdev)
+{
+       struct palmas_pwron *pwron = platform_get_drvdata(pdev);
+
+       free_irq(pwron->irq, pwron);
+       cancel_delayed_work_sync(&pwron->input_work);
+
+       input_unregister_device(pwron->input_dev);
+       kfree(pwron);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * palmas_pwron_suspend() - suspend handler
+ * @dev:       power button device
+ *
+ * Cancel all pending work items for the power button, setup irq for wakeup
+ *
+ * Return: 0
+ */
+static int palmas_pwron_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct palmas_pwron *pwron = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&pwron->input_work);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(pwron->irq);
+
+       return 0;
+}
+
+/**
+ * palmas_pwron_resume() - resume handler
+ * @dev:       power button device
+ *
+ * Just disable the wakeup capability of irq here.
+ *
+ * Return: 0
+ */
+static int palmas_pwron_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct palmas_pwron *pwron = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(pwron->irq);
+
+       return 0;
+}
+#endif
+
+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[] = {
+       { .compatible = "ti,palmas-pwrbutton" },
+       { },
+};
+
+MODULE_DEVICE_TABLE(of, of_palmas_pwr_match);
+#endif
+
+static struct platform_driver palmas_pwron_driver = {
+       .probe  = palmas_pwron_probe,
+       .remove = palmas_pwron_remove,
+       .driver = {
+               .name   = "palmas_pwrbutton",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(of_palmas_pwr_match),
+               .pm     = &palmas_pwron_pm,
+       },
+};
+module_platform_driver(palmas_pwron_driver);
+
+MODULE_ALIAS("platform:palmas-pwrbutton");
+MODULE_DESCRIPTION("Palmas Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Texas Instruments Inc.");
index e34dfc29beb3bb920d9548c2bd4a057d10297179..73560475356841ad2474e612fd8917ea6416407e 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/gpio_keys.h>
 #include <linux/platform_device.h>
-#include <linux/pnp.h>
+#include <linux/acpi.h>
 
 /*
  * Definition of buttons on the tablet. The ACPI index of each button
@@ -67,7 +67,7 @@ static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
 }
 
 static struct platform_device *
-soc_button_device_create(struct pnp_dev *pdev,
+soc_button_device_create(struct platform_device *pdev,
                         const struct soc_button_info *button_info,
                         bool autorepeat)
 {
@@ -138,30 +138,40 @@ err_free_mem:
        return ERR_PTR(error);
 }
 
-static void soc_button_remove(struct pnp_dev *pdev)
+static int soc_button_remove(struct platform_device *pdev)
 {
-       struct soc_button_data *priv = pnp_get_drvdata(pdev);
+       struct soc_button_data *priv = platform_get_drvdata(pdev);
+
        int i;
 
        for (i = 0; i < BUTTON_TYPES; i++)
                if (priv->children[i])
                        platform_device_unregister(priv->children[i]);
+
+       return 0;
 }
 
-static int soc_button_pnp_probe(struct pnp_dev *pdev,
-                               const struct pnp_device_id *id)
+static int soc_button_probe(struct platform_device *pdev)
 {
-       const struct soc_button_info *button_info = (void *)id->driver_data;
+       struct device *dev = &pdev->dev;
+       const struct acpi_device_id *id;
+       struct soc_button_info *button_info;
        struct soc_button_data *priv;
        struct platform_device *pd;
        int i;
        int error;
 
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return -ENODEV;
+
+       button_info = (struct soc_button_info *)id->driver_data;
+
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       pnp_set_drvdata(pdev, priv);
+       platform_set_drvdata(pdev, priv);
 
        for (i = 0; i < BUTTON_TYPES; i++) {
                pd = soc_button_device_create(pdev, button_info, i == 0);
@@ -192,30 +202,22 @@ static struct soc_button_info soc_button_PNP0C40[] = {
        { }
 };
 
-static const struct pnp_device_id soc_button_pnp_match[] = {
-       { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 },
-       { .id = "" }
+static const struct acpi_device_id soc_button_acpi_match[] = {
+       { "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+       { }
 };
-MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match);
 
-static struct pnp_driver soc_button_pnp_driver = {
-       .name           = KBUILD_MODNAME,
-       .id_table       = soc_button_pnp_match,
-       .probe          = soc_button_pnp_probe,
+MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match);
+
+static struct platform_driver soc_button_driver = {
+       .probe          = soc_button_probe,
        .remove         = soc_button_remove,
+       .driver         = {
+               .name = KBUILD_MODNAME,
+               .owner = THIS_MODULE,
+               .acpi_match_table = ACPI_PTR(soc_button_acpi_match),
+       },
 };
-
-static int __init soc_button_init(void)
-{
-       return pnp_register_driver(&soc_button_pnp_driver);
-}
-
-static void __exit soc_button_exit(void)
-{
-       pnp_unregister_driver(&soc_button_pnp_driver);
-}
-
-module_init(soc_button_init);
-module_exit(soc_button_exit);
+module_platform_driver(soc_button_driver);
 
 MODULE_LICENSE("GPL");
index c25efdb3f288b0284e041c82dca3cbf42b122b9a..dda507f8b3a226ca619a9c0947fff3df1bef1211 100644 (file)
@@ -23,7 +23,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C)     += synaptics_i2c.o
 obj-$(CONFIG_MOUSE_SYNAPTICS_USB)      += synaptics_usb.o
 obj-$(CONFIG_MOUSE_VSXXXAA)            += vsxxxaa.o
 
-psmouse-objs := psmouse-base.o synaptics.o
+psmouse-objs := psmouse-base.o synaptics.o focaltech.o
 
 psmouse-$(CONFIG_MOUSE_PS2_ALPS)       += alps.o
 psmouse-$(CONFIG_MOUSE_PS2_ELANTECH)   += elantech.o
diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c
new file mode 100644 (file)
index 0000000..f4d657e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Focaltech TouchPad PS/2 mouse driver
+ *
+ * Copyright (c) 2014 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Red Hat authors:
+ *
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+/*
+ * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with
+ * detection only, to avoid further detection attempts confusing the touchpad
+ * this way it at least works in PS/2 mouse compatibility mode.
+ */
+
+#include <linux/device.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+
+static const char * const focaltech_pnp_ids[] = {
+       "FLT0101",
+       "FLT0102",
+       "FLT0103",
+       NULL
+};
+
+int focaltech_detect(struct psmouse *psmouse, bool set_properties)
+{
+       if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
+               return -ENODEV;
+
+       if (set_properties) {
+               psmouse->vendor = "FocalTech";
+               psmouse->name = "FocalTech Touchpad in mouse emulation mode";
+       }
+
+       return 0;
+}
+
+int focaltech_init(struct psmouse *psmouse)
+{
+       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+       psmouse_reset(psmouse);
+
+       return 0;
+}
diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h
new file mode 100644 (file)
index 0000000..498650c
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Focaltech TouchPad PS/2 mouse driver
+ *
+ * Copyright (c) 2014 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Red Hat authors:
+ *
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#ifndef _FOCALTECH_H
+#define _FOCALTECH_H
+
+int focaltech_detect(struct psmouse *psmouse, bool set_properties);
+int focaltech_init(struct psmouse *psmouse);
+
+#endif
index b4e1f014ddc2297affeda8abc73c15954fdfabf0..26994f6a2b2ae7abe3f04a953eab9004b25d40d1 100644 (file)
@@ -35,6 +35,7 @@
 #include "elantech.h"
 #include "sentelic.h"
 #include "cypress_ps2.h"
+#include "focaltech.h"
 
 #define DRIVER_DESC    "PS/2 mouse driver"
 
@@ -462,6 +463,20 @@ static int psmouse_poll(struct psmouse *psmouse)
                           PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
 }
 
+/*
+ * 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;
+
+       return false;
+}
 
 /*
  * Genius NetMouse magic init.
@@ -708,6 +723,21 @@ static int psmouse_extensions(struct psmouse *psmouse,
 {
        bool synaptics_hardware = false;
 
+/* Always check for focaltech, this is safe as it uses pnp-id matching */
+       if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
+               if (!set_properties || focaltech_init(psmouse) == 0) {
+                       /*
+                        * Not supported yet, use bare protocol.
+                        * Note that we need to also restrict
+                        * psmouse_max_proto so that psmouse_initialize()
+                        * does not try to reset rate and resolution,
+                        * because even that upsets the device.
+                        */
+                       psmouse_max_proto = PSMOUSE_PS2;
+                       return PSMOUSE_PS2;
+               }
+       }
+
 /*
  * We always check for lifebook because it does not disturb mouse
  * (it only checks DMI information).
index 2f0b39d59a9bd51aa9dd3e803f9502a0515c5f61..f4cf664c7db3107f0f2503d21463257241dd0158 100644 (file)
@@ -108,6 +108,7 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
 psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse);
 int psmouse_activate(struct psmouse *psmouse);
 int psmouse_deactivate(struct psmouse *psmouse);
+bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]);
 
 struct psmouse_attribute {
        struct device_attribute dattr;
index fd23181c1fb741112f1cc33fa0fc6111284823c3..6394d9b5bfd35526e7f093543cfab67fcf6ec0e2 100644 (file)
@@ -185,18 +185,6 @@ static const char * const topbuttonpad_pnp_ids[] = {
        NULL
 };
 
-static bool 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;
-
-       return false;
-}
-
 /*****************************************************************************
  *     Synaptics communications functions
  ****************************************************************************/
@@ -362,7 +350,8 @@ static int synaptics_resolution(struct psmouse *psmouse)
        }
 
        for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
-               if (matches_pnp_id(psmouse, min_max_pnpid_table[i].pnp_ids)) {
+               if (psmouse_matches_pnp_id(psmouse,
+                                          min_max_pnpid_table[i].pnp_ids)) {
                        priv->x_min = min_max_pnpid_table[i].x_min;
                        priv->x_max = min_max_pnpid_table[i].x_max;
                        priv->y_min = min_max_pnpid_table[i].y_min;
@@ -1492,7 +1481,7 @@ static void set_input_params(struct psmouse *psmouse,
 
        if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
                __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
-               if (matches_pnp_id(psmouse, topbuttonpad_pnp_ids))
+               if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids))
                        __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
                /* Clickpads report only left button */
                __clear_bit(BTN_RIGHT, dev->keybit);
diff --git a/include/dt-bindings/input/ti-drv260x.h b/include/dt-bindings/input/ti-drv260x.h
new file mode 100644 (file)
index 0000000..2626e6d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, 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 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.
+ */
+
+#ifndef _DT_BINDINGS_TI_DRV260X_H
+#define _DT_BINDINGS_TI_DRV260X_H
+
+/* Calibration Types */
+#define DRV260X_LRA_MODE               0x00
+#define DRV260X_LRA_NO_CAL_MODE        0x01
+#define DRV260X_ERM_MODE               0x02
+
+/* Library Selection */
+#define DRV260X_LIB_EMPTY                      0x00
+#define DRV260X_ERM_LIB_A                      0x01
+#define DRV260X_ERM_LIB_B                      0x02
+#define DRV260X_ERM_LIB_C                      0x03
+#define DRV260X_ERM_LIB_D                      0x04
+#define DRV260X_ERM_LIB_E                      0x05
+#define DRV260X_LIB_LRA                        0x06
+#define DRV260X_ERM_LIB_F                      0x07
+
+#endif
index c466ff3e16b8906cbe5ff8700ba3912ea7a296a6..d0e578fd7053fc769fae4c93ed468b8feaec3ecb 100644 (file)
@@ -251,6 +251,15 @@ enum max77693_haptic_reg {
        MAX77693_HAPTIC_REG_END,
 };
 
+/* max77693-pmic LSCNFG configuraton register */
+#define MAX77693_PMIC_LOW_SYS_MASK      0x80
+#define MAX77693_PMIC_LOW_SYS_SHIFT     7
+
+/* max77693-haptic configuration register */
+#define MAX77693_CONFIG2_MODE           7
+#define MAX77693_CONFIG2_MEN            6
+#define MAX77693_CONFIG2_HTYP           5
+
 enum max77693_irq_source {
        LED_INT = 0,
        TOPSYS_INT,
diff --git a/include/linux/platform_data/drv260x-pdata.h b/include/linux/platform_data/drv260x-pdata.h
new file mode 100644 (file)
index 0000000..0a03b09
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Platform data for DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, 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 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.
+ */
+
+#ifndef _LINUX_DRV260X_PDATA_H
+#define _LINUX_DRV260X_PDATA_H
+
+struct drv260x_platform_data {
+       u32 library_selection;
+       u32 mode;
+       u32 vib_rated_voltage;
+       u32 vib_overdrive_voltage;
+};
+
+#endif