diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-07 21:26:52 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-07 21:26:52 -0400 |
| commit | 1e345ac6869cd2f2d5d4b780fc5d5332dd1e8905 (patch) | |
| tree | eeb7d4faa5fdc5e27fd024c65384f11d515fe90f | |
| parent | 39520eea198a7fbba35f4c7cffb4323f78455716 (diff) | |
| parent | 447a8b858e4bda41c394b1bc7fdbc9dc0bdf44f6 (diff) | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
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, 2468 insertions, 109 deletions
diff --git a/Documentation/devicetree/bindings/input/ti,drv260x.txt b/Documentation/devicetree/bindings/input/ti,drv260x.txt new file mode 100644 index 000000000000..ee09c8f4474a --- /dev/null +++ b/Documentation/devicetree/bindings/input/ti,drv260x.txt | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | * Texas Instruments - drv260x Haptics driver family | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible - One of: | ||
| 5 | "ti,drv2604" - DRV2604 | ||
| 6 | "ti,drv2605" - DRV2605 | ||
| 7 | "ti,drv2605l" - DRV2605L | ||
| 8 | - reg - I2C slave address | ||
| 9 | - vbat-supply - Required supply regulator | ||
| 10 | - mode - Power up mode of the chip (defined in include/dt-bindings/input/ti-drv260x.h) | ||
| 11 | DRV260X_LRA_MODE - Linear Resonance Actuator mode (Piezoelectric) | ||
| 12 | DRV260X_LRA_NO_CAL_MODE - This is a LRA Mode but there is no calibration | ||
| 13 | sequence during init. And the device is configured for real | ||
| 14 | time playback mode (RTP mode). | ||
| 15 | DRV260X_ERM_MODE - Eccentric Rotating Mass mode (Rotary vibrator) | ||
| 16 | - library-sel - These are ROM based waveforms pre-programmed into the IC. | ||
| 17 | This should be set to set the library to use at power up. | ||
| 18 | (defined in include/dt-bindings/input/ti-drv260x.h) | ||
| 19 | DRV260X_LIB_EMPTY - Do not use a pre-programmed library | ||
| 20 | DRV260X_ERM_LIB_A - Pre-programmed Library | ||
| 21 | DRV260X_ERM_LIB_B - Pre-programmed Library | ||
| 22 | DRV260X_ERM_LIB_C - Pre-programmed Library | ||
| 23 | DRV260X_ERM_LIB_D - Pre-programmed Library | ||
| 24 | DRV260X_ERM_LIB_E - Pre-programmed Library | ||
| 25 | DRV260X_ERM_LIB_F - Pre-programmed Library | ||
| 26 | DRV260X_LIB_LRA - Pre-programmed LRA Library | ||
| 27 | |||
| 28 | Optional properties: | ||
| 29 | - enable-gpio - gpio pin to enable/disable the device. | ||
| 30 | - vib-rated-mv - The rated voltage of the actuator in millivolts. | ||
| 31 | If this is not set then the value will be defaulted to | ||
| 32 | 3.2 v. | ||
| 33 | - vib-overdrive-mv - The overdrive voltage of the actuator in millivolts. | ||
| 34 | If this is not set then the value will be defaulted to | ||
| 35 | 3.2 v. | ||
| 36 | Example: | ||
| 37 | |||
| 38 | haptics: haptics@5a { | ||
| 39 | compatible = "ti,drv2605l"; | ||
| 40 | reg = <0x5a>; | ||
| 41 | vbat-supply = <&vbat>; | ||
| 42 | enable-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>; | ||
| 43 | mode = <DRV260X_LRA_MODE>; | ||
| 44 | library-sel = <DRV260X_LIB_LRA>; | ||
| 45 | vib-rated-mv = <3200>; | ||
| 46 | vib-overdriver-mv = <3200>; | ||
| 47 | } | ||
| 48 | |||
| 49 | For more product information please see the link below: | ||
| 50 | 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 index 000000000000..996382cf994a --- /dev/null +++ b/Documentation/devicetree/bindings/input/ti,drv2667.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | * Texas Instruments - drv2667 Haptics driver | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible - "ti,drv2667" - DRV2667 | ||
| 5 | - reg - I2C slave address | ||
| 6 | - vbat-supply - Required supply regulator | ||
| 7 | |||
| 8 | Example: | ||
| 9 | |||
| 10 | haptics: haptics@59 { | ||
| 11 | compatible = "ti,drv2667"; | ||
| 12 | reg = <0x59>; | ||
| 13 | vbat-supply = <&vbat>; | ||
| 14 | }; | ||
| 15 | |||
| 16 | For more product information please see the link below: | ||
| 17 | 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 index 000000000000..a3dde8c30e67 --- /dev/null +++ b/Documentation/devicetree/bindings/input/ti,palmas-pwrbutton.txt | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | Texas Instruments Palmas family power button module | ||
| 2 | |||
| 3 | This module is part of the Palmas family of PMICs. For more details | ||
| 4 | about the whole chip see: | ||
| 5 | Documentation/devicetree/bindings/mfd/palmas.txt. | ||
| 6 | |||
| 7 | This module provides a simple power button event via an Interrupt. | ||
| 8 | |||
| 9 | Required properties: | ||
| 10 | - compatible: should be one of the following | ||
| 11 | - "ti,palmas-pwrbutton": For Palmas compatible power on button | ||
| 12 | - interrupt-parent: Parent interrupt device, must be handle of palmas node. | ||
| 13 | - interrupts: Interrupt number of power button submodule on device. | ||
| 14 | |||
| 15 | Optional Properties: | ||
| 16 | |||
| 17 | - ti,palmas-long-press-seconds: Duration in seconds which the power | ||
| 18 | button should be kept pressed for Palmas to power off automatically. | ||
| 19 | NOTE: This depends on OTP support and POWERHOLD signal configuration | ||
| 20 | on platform. Valid values are 6, 8, 10 and 12. | ||
| 21 | - ti,palmas-pwron-debounce-milli-seconds: Duration in milliseconds | ||
| 22 | which the power button should be kept pressed for Palmas to register | ||
| 23 | a press for debouncing purposes. NOTE: This depends on specific | ||
| 24 | Palmas variation capability. Valid values are 15, 100, 500 and 1000. | ||
| 25 | |||
| 26 | Example: | ||
| 27 | |||
| 28 | &palmas { | ||
| 29 | palmas_pwr_button: pwrbutton { | ||
| 30 | compatible = "ti,palmas-pwrbutton"; | ||
| 31 | interrupt-parent = <&tps659038>; | ||
| 32 | interrupts = <1 IRQ_TYPE_EDGE_FALLING>; | ||
| 33 | ti,palmas-long-press-seconds = <12>; | ||
| 34 | ti,palmas-pwron-debounce-milli-seconds = <15>; | ||
| 35 | }; | ||
| 36 | }; | ||
diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 996fa1959eea..1f8b20496f32 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c | |||
| @@ -15,8 +15,6 @@ | |||
| 15 | #include <linux/ctype.h> | 15 | #include <linux/ctype.h> |
| 16 | 16 | ||
| 17 | static const struct acpi_device_id acpi_pnp_device_ids[] = { | 17 | static const struct acpi_device_id acpi_pnp_device_ids[] = { |
| 18 | /* soc_button_array */ | ||
| 19 | {"PNP0C40"}, | ||
| 20 | /* pata_isapnp */ | 18 | /* pata_isapnp */ |
| 21 | {"PNP0600"}, /* Generic ESDI/IDE/ATA compatible hard disk controller */ | 19 | {"PNP0600"}, /* Generic ESDI/IDE/ATA compatible hard disk controller */ |
| 22 | /* floppy */ | 20 | /* floppy */ |
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 24c41ba7d4e0..e29c04e2aff4 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/workqueue.h> | 23 | #include <linux/workqueue.h> |
| 24 | #include <linux/sched.h> /* HZ */ | 24 | #include <linux/sched.h> /* HZ */ |
| 25 | #include <linux/mutex.h> | 25 | #include <linux/mutex.h> |
| 26 | #include <linux/timekeeping.h> | ||
| 26 | 27 | ||
| 27 | /*#include <asm/io.h>*/ | 28 | /*#include <asm/io.h>*/ |
| 28 | 29 | ||
| @@ -30,6 +31,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | |||
| 30 | MODULE_DESCRIPTION("Generic gameport layer"); | 31 | MODULE_DESCRIPTION("Generic gameport layer"); |
| 31 | MODULE_LICENSE("GPL"); | 32 | MODULE_LICENSE("GPL"); |
| 32 | 33 | ||
| 34 | static bool use_ktime = true; | ||
| 35 | module_param(use_ktime, bool, 0400); | ||
| 36 | MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed"); | ||
| 37 | |||
| 33 | /* | 38 | /* |
| 34 | * gameport_mutex protects entire gameport subsystem and is taken | 39 | * gameport_mutex protects entire gameport subsystem and is taken |
| 35 | * every time gameport port or driver registrered or unregistered. | 40 | * every time gameport port or driver registrered or unregistered. |
| @@ -76,6 +81,38 @@ static unsigned int get_time_pit(void) | |||
| 76 | 81 | ||
| 77 | static int gameport_measure_speed(struct gameport *gameport) | 82 | static int gameport_measure_speed(struct gameport *gameport) |
| 78 | { | 83 | { |
| 84 | unsigned int i, t, tx; | ||
| 85 | u64 t1, t2, t3; | ||
| 86 | unsigned long flags; | ||
| 87 | |||
| 88 | if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) | ||
| 89 | return 0; | ||
| 90 | |||
| 91 | tx = ~0; | ||
| 92 | |||
| 93 | for (i = 0; i < 50; i++) { | ||
| 94 | local_irq_save(flags); | ||
| 95 | t1 = ktime_get_ns(); | ||
| 96 | for (t = 0; t < 50; t++) | ||
| 97 | gameport_read(gameport); | ||
| 98 | t2 = ktime_get_ns(); | ||
| 99 | t3 = ktime_get_ns(); | ||
| 100 | local_irq_restore(flags); | ||
| 101 | udelay(i * 10); | ||
| 102 | t = (t2 - t1) - (t3 - t2); | ||
| 103 | if (t < tx) | ||
| 104 | tx = t; | ||
| 105 | } | ||
| 106 | |||
| 107 | gameport_close(gameport); | ||
| 108 | t = 1000000 * 50; | ||
| 109 | if (tx) | ||
| 110 | t /= tx; | ||
| 111 | return t; | ||
| 112 | } | ||
| 113 | |||
| 114 | static int old_gameport_measure_speed(struct gameport *gameport) | ||
| 115 | { | ||
| 79 | #if defined(__i386__) | 116 | #if defined(__i386__) |
| 80 | 117 | ||
| 81 | unsigned int i, t, t1, t2, t3, tx; | 118 | unsigned int i, t, t1, t2, t3, tx; |
| @@ -521,7 +558,9 @@ static void gameport_add_port(struct gameport *gameport) | |||
| 521 | if (gameport->parent) | 558 | if (gameport->parent) |
| 522 | gameport->parent->child = gameport; | 559 | gameport->parent->child = gameport; |
| 523 | 560 | ||
| 524 | gameport->speed = gameport_measure_speed(gameport); | 561 | gameport->speed = use_ktime ? |
| 562 | gameport_measure_speed(gameport) : | ||
| 563 | old_gameport_measure_speed(gameport); | ||
| 525 | 564 | ||
| 526 | list_add_tail(&gameport->node, &gameport_list); | 565 | list_add_tail(&gameport->node, &gameport_list); |
| 527 | 566 | ||
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index ab0fdcd36e18..4284080e481d 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | #include <linux/gameport.h> | 36 | #include <linux/gameport.h> |
| 37 | #include <linux/jiffies.h> | 37 | #include <linux/jiffies.h> |
| 38 | #include <linux/timex.h> | 38 | #include <linux/timex.h> |
| 39 | #include <linux/timekeeping.h> | ||
| 39 | 40 | ||
| 40 | #define DRIVER_DESC "Analog joystick and gamepad driver" | 41 | #define DRIVER_DESC "Analog joystick and gamepad driver" |
| 41 | 42 | ||
| @@ -43,6 +44,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); | |||
| 43 | MODULE_DESCRIPTION(DRIVER_DESC); | 44 | MODULE_DESCRIPTION(DRIVER_DESC); |
| 44 | MODULE_LICENSE("GPL"); | 45 | MODULE_LICENSE("GPL"); |
| 45 | 46 | ||
| 47 | static bool use_ktime = true; | ||
| 48 | module_param(use_ktime, bool, 0400); | ||
| 49 | MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed"); | ||
| 50 | |||
| 46 | /* | 51 | /* |
| 47 | * Option parsing. | 52 | * Option parsing. |
| 48 | */ | 53 | */ |
| @@ -171,6 +176,25 @@ static unsigned long analog_faketime = 0; | |||
| 171 | #warning Precise timer not defined for this architecture. | 176 | #warning Precise timer not defined for this architecture. |
| 172 | #endif | 177 | #endif |
| 173 | 178 | ||
| 179 | static inline u64 get_time(void) | ||
| 180 | { | ||
| 181 | if (use_ktime) { | ||
| 182 | return ktime_get_ns(); | ||
| 183 | } else { | ||
| 184 | unsigned int x; | ||
| 185 | GET_TIME(x); | ||
| 186 | return x; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | static inline unsigned int delta(u64 x, u64 y) | ||
| 191 | { | ||
| 192 | if (use_ktime) | ||
| 193 | return y - x; | ||
| 194 | else | ||
| 195 | return DELTA((unsigned int)x, (unsigned int)y); | ||
| 196 | } | ||
| 197 | |||
| 174 | /* | 198 | /* |
| 175 | * analog_decode() decodes analog joystick data and reports input events. | 199 | * analog_decode() decodes analog joystick data and reports input events. |
| 176 | */ | 200 | */ |
| @@ -226,7 +250,8 @@ static void analog_decode(struct analog *analog, int *axes, int *initial, int bu | |||
| 226 | static int analog_cooked_read(struct analog_port *port) | 250 | static int analog_cooked_read(struct analog_port *port) |
| 227 | { | 251 | { |
| 228 | struct gameport *gameport = port->gameport; | 252 | struct gameport *gameport = port->gameport; |
| 229 | unsigned int time[4], start, loop, now, loopout, timeout; | 253 | u64 time[4], start, loop, now; |
| 254 | unsigned int loopout, timeout; | ||
| 230 | unsigned char data[4], this, last; | 255 | unsigned char data[4], this, last; |
| 231 | unsigned long flags; | 256 | unsigned long flags; |
| 232 | int i, j; | 257 | int i, j; |
| @@ -236,7 +261,7 @@ static int analog_cooked_read(struct analog_port *port) | |||
| 236 | 261 | ||
| 237 | local_irq_save(flags); | 262 | local_irq_save(flags); |
| 238 | gameport_trigger(gameport); | 263 | gameport_trigger(gameport); |
| 239 | GET_TIME(now); | 264 | now = get_time(); |
| 240 | local_irq_restore(flags); | 265 | local_irq_restore(flags); |
| 241 | 266 | ||
| 242 | start = now; | 267 | start = now; |
| @@ -249,16 +274,16 @@ static int analog_cooked_read(struct analog_port *port) | |||
| 249 | 274 | ||
| 250 | local_irq_disable(); | 275 | local_irq_disable(); |
| 251 | this = gameport_read(gameport) & port->mask; | 276 | this = gameport_read(gameport) & port->mask; |
| 252 | GET_TIME(now); | 277 | now = get_time(); |
| 253 | local_irq_restore(flags); | 278 | local_irq_restore(flags); |
| 254 | 279 | ||
| 255 | if ((last ^ this) && (DELTA(loop, now) < loopout)) { | 280 | if ((last ^ this) && (delta(loop, now) < loopout)) { |
| 256 | data[i] = last ^ this; | 281 | data[i] = last ^ this; |
| 257 | time[i] = now; | 282 | time[i] = now; |
| 258 | i++; | 283 | i++; |
| 259 | } | 284 | } |
| 260 | 285 | ||
| 261 | } while (this && (i < 4) && (DELTA(start, now) < timeout)); | 286 | } while (this && (i < 4) && (delta(start, now) < timeout)); |
| 262 | 287 | ||
| 263 | this <<= 4; | 288 | this <<= 4; |
| 264 | 289 | ||
| @@ -266,7 +291,7 @@ static int analog_cooked_read(struct analog_port *port) | |||
| 266 | this |= data[i]; | 291 | this |= data[i]; |
| 267 | for (j = 0; j < 4; j++) | 292 | for (j = 0; j < 4; j++) |
| 268 | if (data[i] & (1 << j)) | 293 | if (data[i] & (1 << j)) |
| 269 | port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; | 294 | port->axes[j] = (delta(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; |
| 270 | } | 295 | } |
| 271 | 296 | ||
| 272 | return -(this != port->mask); | 297 | return -(this != port->mask); |
| @@ -365,31 +390,39 @@ static void analog_close(struct input_dev *dev) | |||
| 365 | static void analog_calibrate_timer(struct analog_port *port) | 390 | static void analog_calibrate_timer(struct analog_port *port) |
| 366 | { | 391 | { |
| 367 | struct gameport *gameport = port->gameport; | 392 | struct gameport *gameport = port->gameport; |
| 368 | unsigned int i, t, tx, t1, t2, t3; | 393 | unsigned int i, t, tx; |
| 394 | u64 t1, t2, t3; | ||
| 369 | unsigned long flags; | 395 | unsigned long flags; |
| 370 | 396 | ||
| 371 | local_irq_save(flags); | 397 | if (use_ktime) { |
| 372 | GET_TIME(t1); | 398 | port->speed = 1000000; |
| 399 | } else { | ||
| 400 | local_irq_save(flags); | ||
| 401 | t1 = get_time(); | ||
| 373 | #ifdef FAKE_TIME | 402 | #ifdef FAKE_TIME |
| 374 | analog_faketime += 830; | 403 | analog_faketime += 830; |
| 375 | #endif | 404 | #endif |
| 376 | mdelay(1); | 405 | mdelay(1); |
| 377 | GET_TIME(t2); | 406 | t2 = get_time(); |
| 378 | GET_TIME(t3); | 407 | t3 = get_time(); |
| 379 | local_irq_restore(flags); | 408 | local_irq_restore(flags); |
| 380 | 409 | ||
| 381 | port->speed = DELTA(t1, t2) - DELTA(t2, t3); | 410 | port->speed = delta(t1, t2) - delta(t2, t3); |
| 411 | } | ||
| 382 | 412 | ||
| 383 | tx = ~0; | 413 | tx = ~0; |
| 384 | 414 | ||
| 385 | for (i = 0; i < 50; i++) { | 415 | for (i = 0; i < 50; i++) { |
| 386 | local_irq_save(flags); | 416 | local_irq_save(flags); |
| 387 | GET_TIME(t1); | 417 | t1 = get_time(); |
| 388 | for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); } | 418 | for (t = 0; t < 50; t++) { |
| 389 | GET_TIME(t3); | 419 | gameport_read(gameport); |
| 420 | t2 = get_time(); | ||
| 421 | } | ||
| 422 | t3 = get_time(); | ||
| 390 | local_irq_restore(flags); | 423 | local_irq_restore(flags); |
| 391 | udelay(i); | 424 | udelay(i); |
| 392 | t = DELTA(t1, t2) - DELTA(t2, t3); | 425 | t = delta(t1, t2) - delta(t2, t3); |
| 393 | if (t < tx) tx = t; | 426 | if (t < tx) tx = t; |
| 394 | } | 427 | } |
| 395 | 428 | ||
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 177602cf7079..cd13c82ca0a1 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c | |||
| @@ -126,7 +126,9 @@ static const struct xpad_device { | |||
| 126 | { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, | 126 | { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, |
| 127 | { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, | 127 | { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, |
| 128 | { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, | 128 | { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, |
| 129 | { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 }, | ||
| 129 | { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, | 130 | { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, |
| 131 | { 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 }, | ||
| 130 | { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 }, | 132 | { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 }, |
| 131 | { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, | 133 | { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, |
| 132 | { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, | 134 | { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, |
| @@ -140,10 +142,17 @@ static const struct xpad_device { | |||
| 140 | { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, | 142 | { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, |
| 141 | { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX }, | 143 | { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX }, |
| 142 | { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, | 144 | { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, |
| 145 | { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 }, | ||
| 146 | { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, | ||
| 143 | { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, | 147 | { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, |
| 144 | { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, | 148 | { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, |
| 149 | { 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 }, | ||
| 145 | { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, | 150 | { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, |
| 151 | { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 }, | ||
| 146 | { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 }, | 152 | { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 }, |
| 153 | { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, | ||
| 154 | { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, | ||
| 155 | { 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 }, | ||
| 147 | { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, | 156 | { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, |
| 148 | { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, | 157 | { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, |
| 149 | { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX }, | 158 | { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX }, |
| @@ -156,28 +165,50 @@ static const struct xpad_device { | |||
| 156 | { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX }, | 165 | { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX }, |
| 157 | { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX }, | 166 | { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX }, |
| 158 | { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, | 167 | { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, |
| 168 | { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, | ||
| 159 | { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, | 169 | { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, |
| 160 | { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, | 170 | { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, |
| 171 | { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, | ||
| 172 | { 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 }, | ||
| 173 | { 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 }, | ||
| 161 | { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX }, | 174 | { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX }, |
| 175 | { 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX }, | ||
| 176 | { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 }, | ||
| 162 | { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, | 177 | { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, |
| 163 | { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, | 178 | { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, |
| 164 | { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, | 179 | { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, |
| 165 | { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, | 180 | { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, |
| 166 | { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX }, | 181 | { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX }, |
| 167 | { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, | 182 | { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, |
| 183 | { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 }, | ||
| 168 | { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, | 184 | { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, |
| 169 | { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, | 185 | { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, |
| 170 | { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, | 186 | { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, |
| 171 | { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, | 187 | { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, |
| 188 | { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, | ||
| 189 | { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, | ||
| 190 | { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, | ||
| 191 | { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 }, | ||
| 192 | { 0x162e, 0xbeef, "Joytech Neo-Se Take2", 0, XTYPE_XBOX360 }, | ||
| 172 | { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 }, | 193 | { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 }, |
| 173 | { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 }, | 194 | { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 }, |
| 195 | { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 }, | ||
| 174 | { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, | 196 | { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, |
| 175 | { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, | 197 | { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, |
| 176 | { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, | 198 | { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, |
| 199 | { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 }, | ||
| 177 | { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 }, | 200 | { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 }, |
| 201 | { 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 }, | ||
| 202 | { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 }, | ||
| 178 | { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 }, | 203 | { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 }, |
| 179 | { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 }, | 204 | { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 }, |
| 205 | { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", 0, XTYPE_XBOX360 }, | ||
| 180 | { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, | 206 | { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, |
| 207 | { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, | ||
| 208 | { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 }, | ||
| 209 | { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 }, | ||
| 210 | { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 }, | ||
| 211 | { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 }, | ||
| 181 | { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, | 212 | { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, |
| 182 | { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } | 213 | { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } |
| 183 | }; | 214 | }; |
| @@ -274,6 +305,9 @@ static struct usb_device_id xpad_table[] = { | |||
| 274 | XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ | 305 | XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ |
| 275 | XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ | 306 | XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ |
| 276 | XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ | 307 | XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ |
| 308 | XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */ | ||
| 309 | XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */ | ||
| 310 | XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */ | ||
| 277 | { } | 311 | { } |
| 278 | }; | 312 | }; |
| 279 | 313 | ||
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 791781ade4e7..72d3499bb029 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | */ | 22 | */ |
| 23 | 23 | ||
| 24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
| 25 | #include <linux/bitops.h> | ||
| 25 | #include <linux/i2c.h> | 26 | #include <linux/i2c.h> |
| 26 | #include <linux/input.h> | 27 | #include <linux/input.h> |
| 27 | #include <linux/interrupt.h> | 28 | #include <linux/interrupt.h> |
| @@ -38,6 +39,7 @@ | |||
| 38 | * @row_shift: log2 or number of rows, rounded up | 39 | * @row_shift: log2 or number of rows, rounded up |
| 39 | * @keymap_data: Matrix keymap data used to convert to keyscan values | 40 | * @keymap_data: Matrix keymap data used to convert to keyscan values |
| 40 | * @ghost_filter: true to enable the matrix key-ghosting filter | 41 | * @ghost_filter: true to enable the matrix key-ghosting filter |
| 42 | * @valid_keys: bitmap of existing keys for each matrix column | ||
| 41 | * @old_kb_state: bitmap of keys pressed last scan | 43 | * @old_kb_state: bitmap of keys pressed last scan |
| 42 | * @dev: Device pointer | 44 | * @dev: Device pointer |
| 43 | * @idev: Input device | 45 | * @idev: Input device |
| @@ -49,6 +51,7 @@ struct cros_ec_keyb { | |||
| 49 | int row_shift; | 51 | int row_shift; |
| 50 | const struct matrix_keymap_data *keymap_data; | 52 | const struct matrix_keymap_data *keymap_data; |
| 51 | bool ghost_filter; | 53 | bool ghost_filter; |
| 54 | uint8_t *valid_keys; | ||
| 52 | uint8_t *old_kb_state; | 55 | uint8_t *old_kb_state; |
| 53 | 56 | ||
| 54 | struct device *dev; | 57 | struct device *dev; |
| @@ -57,39 +60,15 @@ struct cros_ec_keyb { | |||
| 57 | }; | 60 | }; |
| 58 | 61 | ||
| 59 | 62 | ||
| 60 | static bool cros_ec_keyb_row_has_ghosting(struct cros_ec_keyb *ckdev, | ||
| 61 | uint8_t *buf, int row) | ||
| 62 | { | ||
| 63 | int pressed_in_row = 0; | ||
| 64 | int row_has_teeth = 0; | ||
| 65 | int col, mask; | ||
| 66 | |||
| 67 | mask = 1 << row; | ||
| 68 | for (col = 0; col < ckdev->cols; col++) { | ||
| 69 | if (buf[col] & mask) { | ||
| 70 | pressed_in_row++; | ||
| 71 | row_has_teeth |= buf[col] & ~mask; | ||
| 72 | if (pressed_in_row > 1 && row_has_teeth) { | ||
| 73 | /* ghosting */ | ||
| 74 | dev_dbg(ckdev->dev, | ||
| 75 | "ghost found at: r%d c%d, pressed %d, teeth 0x%x\n", | ||
| 76 | row, col, pressed_in_row, | ||
| 77 | row_has_teeth); | ||
| 78 | return true; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | return false; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* | 63 | /* |
| 87 | * Returns true when there is at least one combination of pressed keys that | 64 | * Returns true when there is at least one combination of pressed keys that |
| 88 | * results in ghosting. | 65 | * results in ghosting. |
| 89 | */ | 66 | */ |
| 90 | static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) | 67 | static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) |
| 91 | { | 68 | { |
| 92 | int row; | 69 | int col1, col2, buf1, buf2; |
| 70 | struct device *dev = ckdev->dev; | ||
| 71 | uint8_t *valid_keys = ckdev->valid_keys; | ||
| 93 | 72 | ||
| 94 | /* | 73 | /* |
| 95 | * Ghosting happens if for any pressed key X there are other keys | 74 | * 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) | |||
| 103 | * | 82 | * |
| 104 | * In this case only X, Y, and Z are pressed, but g appears to be | 83 | * In this case only X, Y, and Z are pressed, but g appears to be |
| 105 | * pressed too (see Wikipedia). | 84 | * pressed too (see Wikipedia). |
| 106 | * | ||
| 107 | * We can detect ghosting in a single pass (*) over the keyboard state | ||
| 108 | * by maintaining two arrays. pressed_in_row counts how many pressed | ||
| 109 | * keys we have found in a row. row_has_teeth is true if any of the | ||
| 110 | * pressed keys for this row has other pressed keys in its column. If | ||
| 111 | * at any point of the scan we find that a row has multiple pressed | ||
| 112 | * keys, and at least one of them is at the intersection with a column | ||
| 113 | * with multiple pressed keys, we're sure there is ghosting. | ||
| 114 | * Conversely, if there is ghosting, we will detect such situation for | ||
| 115 | * at least one key during the pass. | ||
| 116 | * | ||
| 117 | * (*) This looks linear in the number of keys, but it's not. We can | ||
| 118 | * cheat because the number of rows is small. | ||
| 119 | */ | 85 | */ |
| 120 | for (row = 0; row < ckdev->rows; row++) | 86 | for (col1 = 0; col1 < ckdev->cols; col1++) { |
| 121 | if (cros_ec_keyb_row_has_ghosting(ckdev, buf, row)) | 87 | buf1 = buf[col1] & valid_keys[col1]; |
| 122 | return true; | 88 | for (col2 = col1 + 1; col2 < ckdev->cols; col2++) { |
| 89 | buf2 = buf[col2] & valid_keys[col2]; | ||
| 90 | if (hweight8(buf1 & buf2) > 1) { | ||
| 91 | dev_dbg(dev, "ghost found at: B[%02d]:0x%02x & B[%02d]:0x%02x", | ||
| 92 | col1, buf1, col2, buf2); | ||
| 93 | return true; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 123 | 97 | ||
| 124 | return false; | 98 | return false; |
| 125 | } | 99 | } |
| 126 | 100 | ||
| 101 | |||
| 127 | /* | 102 | /* |
| 128 | * Compares the new keyboard state to the old one and produces key | 103 | * Compares the new keyboard state to the old one and produces key |
| 129 | * press/release events accordingly. The keyboard state is 13 bytes (one byte | 104 | * 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) | |||
| 222 | free_irq(ec->irq, ckdev); | 197 | free_irq(ec->irq, ckdev); |
| 223 | } | 198 | } |
| 224 | 199 | ||
| 200 | /* | ||
| 201 | * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW. Used by | ||
| 202 | * ghosting logic to ignore NULL or virtual keys. | ||
| 203 | */ | ||
| 204 | static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) | ||
| 205 | { | ||
| 206 | int row, col; | ||
| 207 | int row_shift = ckdev->row_shift; | ||
| 208 | unsigned short *keymap = ckdev->idev->keycode; | ||
| 209 | unsigned short code; | ||
| 210 | |||
| 211 | BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap)); | ||
| 212 | |||
| 213 | for (col = 0; col < ckdev->cols; col++) { | ||
| 214 | for (row = 0; row < ckdev->rows; row++) { | ||
| 215 | code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; | ||
| 216 | if (code && (code != KEY_BATTERY)) | ||
| 217 | ckdev->valid_keys[col] |= 1 << row; | ||
| 218 | } | ||
| 219 | dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", | ||
| 220 | col, ckdev->valid_keys[col]); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 225 | static int cros_ec_keyb_probe(struct platform_device *pdev) | 224 | static int cros_ec_keyb_probe(struct platform_device *pdev) |
| 226 | { | 225 | { |
| 227 | struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); | 226 | 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) | |||
| 242 | &ckdev->cols); | 241 | &ckdev->cols); |
| 243 | if (err) | 242 | if (err) |
| 244 | return err; | 243 | return err; |
| 244 | |||
| 245 | ckdev->valid_keys = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); | ||
| 246 | if (!ckdev->valid_keys) | ||
| 247 | return -ENOMEM; | ||
| 248 | |||
| 245 | ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); | 249 | ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); |
| 246 | if (!ckdev->old_kb_state) | 250 | if (!ckdev->old_kb_state) |
| 247 | return -ENOMEM; | 251 | return -ENOMEM; |
| @@ -285,6 +289,8 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) | |||
| 285 | input_set_capability(idev, EV_MSC, MSC_SCAN); | 289 | input_set_capability(idev, EV_MSC, MSC_SCAN); |
| 286 | input_set_drvdata(idev, ckdev); | 290 | input_set_drvdata(idev, ckdev); |
| 287 | ckdev->idev = idev; | 291 | ckdev->idev = idev; |
| 292 | cros_ec_keyb_compute_valid_keys(ckdev); | ||
| 293 | |||
| 288 | err = input_register_device(ckdev->idev); | 294 | err = input_register_device(ckdev->idev); |
| 289 | if (err) { | 295 | if (err) { |
| 290 | dev_err(dev, "cannot register input device\n"); | 296 | dev_err(dev, "cannot register input device\n"); |
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 2ff4425a893b..23297ab6163f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig | |||
| @@ -144,6 +144,17 @@ config INPUT_M68K_BEEP | |||
| 144 | tristate "M68k Beeper support" | 144 | tristate "M68k Beeper support" |
| 145 | depends on M68K | 145 | depends on M68K |
| 146 | 146 | ||
| 147 | config INPUT_MAX77693_HAPTIC | ||
| 148 | tristate "MAXIM MAX77693 haptic controller support" | ||
| 149 | depends on MFD_MAX77693 && PWM | ||
| 150 | select INPUT_FF_MEMLESS | ||
| 151 | help | ||
| 152 | This option enables support for the haptic controller on | ||
| 153 | MAXIM MAX77693 chip. | ||
| 154 | |||
| 155 | To compile this driver as module, choose M here: the | ||
| 156 | module will be called max77693-haptic. | ||
| 157 | |||
| 147 | config INPUT_MAX8925_ONKEY | 158 | config INPUT_MAX8925_ONKEY |
| 148 | tristate "MAX8925 ONKEY support" | 159 | tristate "MAX8925 ONKEY support" |
| 149 | depends on MFD_MAX8925 | 160 | depends on MFD_MAX8925 |
| @@ -451,6 +462,16 @@ config HP_SDC_RTC | |||
| 451 | Say Y here if you want to support the built-in real time clock | 462 | Say Y here if you want to support the built-in real time clock |
| 452 | of the HP SDC controller. | 463 | of the HP SDC controller. |
| 453 | 464 | ||
| 465 | config INPUT_PALMAS_PWRBUTTON | ||
| 466 | tristate "Palmas Power button Driver" | ||
| 467 | depends on MFD_PALMAS | ||
| 468 | help | ||
| 469 | Say Y here if you want to enable power key reporting via the | ||
| 470 | Palmas family of PMICs. | ||
| 471 | |||
| 472 | To compile this driver as a module, choose M here. The module will | ||
| 473 | be called palmas_pwrbutton. | ||
| 474 | |||
| 454 | config INPUT_PCF50633_PMU | 475 | config INPUT_PCF50633_PMU |
| 455 | tristate "PCF50633 PMU events" | 476 | tristate "PCF50633 PMU events" |
| 456 | depends on MFD_PCF50633 | 477 | depends on MFD_PCF50633 |
| @@ -676,4 +697,26 @@ config INPUT_SOC_BUTTON_ARRAY | |||
| 676 | To compile this driver as a module, choose M here: the | 697 | To compile this driver as a module, choose M here: the |
| 677 | module will be called soc_button_array. | 698 | module will be called soc_button_array. |
| 678 | 699 | ||
| 700 | config INPUT_DRV260X_HAPTICS | ||
| 701 | tristate "TI DRV260X haptics support" | ||
| 702 | depends on INPUT && I2C && GPIOLIB | ||
| 703 | select INPUT_FF_MEMLESS | ||
| 704 | select REGMAP_I2C | ||
| 705 | help | ||
| 706 | Say Y to enable support for the TI DRV260X haptics driver. | ||
| 707 | |||
| 708 | To compile this driver as a module, choose M here: the | ||
| 709 | module will be called drv260x-haptics. | ||
| 710 | |||
| 711 | config INPUT_DRV2667_HAPTICS | ||
| 712 | tristate "TI DRV2667 haptics support" | ||
| 713 | depends on INPUT && I2C | ||
| 714 | select INPUT_FF_MEMLESS | ||
| 715 | select REGMAP_I2C | ||
| 716 | help | ||
| 717 | Say Y to enable support for the TI DRV2667 haptics driver. | ||
| 718 | |||
| 719 | To compile this driver as a module, choose M here: the | ||
| 720 | module will be called drv260x-haptics. | ||
| 721 | |||
| 679 | endif | 722 | endif |
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 4955ad322a01..19c760361f80 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile | |||
| @@ -26,6 +26,8 @@ obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o | |||
| 26 | obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o | 26 | obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o |
| 27 | obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o | 27 | obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o |
| 28 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o | 28 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o |
| 29 | obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o | ||
| 30 | obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o | ||
| 29 | obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o | 31 | obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o |
| 30 | obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o | 32 | obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o |
| 31 | obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o | 33 | obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o |
| @@ -35,11 +37,13 @@ obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o | |||
| 35 | obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o | 37 | obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o |
| 36 | obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o | 38 | obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o |
| 37 | obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o | 39 | obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o |
| 40 | obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o | ||
| 38 | obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o | 41 | obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o |
| 39 | obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o | 42 | obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o |
| 40 | obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o | 43 | obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o |
| 41 | obj-$(CONFIG_INPUT_MMA8450) += mma8450.o | 44 | obj-$(CONFIG_INPUT_MMA8450) += mma8450.o |
| 42 | obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o | 45 | obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o |
| 46 | obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o | ||
| 43 | obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o | 47 | obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o |
| 44 | obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o | 48 | obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o |
| 45 | obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o | 49 | 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 index 000000000000..cab87f5ce6d3 --- /dev/null +++ b/drivers/input/misc/drv260x.c | |||
| @@ -0,0 +1,741 @@ | |||
| 1 | /* | ||
| 2 | * DRV260X haptics driver family | ||
| 3 | * | ||
| 4 | * Author: Dan Murphy <dmurphy@ti.com> | ||
| 5 | * | ||
| 6 | * Copyright: (C) 2014 Texas Instruments, Inc. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/i2c.h> | ||
| 19 | #include <linux/input.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/of_gpio.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/regmap.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/delay.h> | ||
| 26 | #include <linux/gpio/consumer.h> | ||
| 27 | #include <linux/regulator/consumer.h> | ||
| 28 | |||
| 29 | #include <dt-bindings/input/ti-drv260x.h> | ||
| 30 | #include <linux/platform_data/drv260x-pdata.h> | ||
| 31 | |||
| 32 | #define DRV260X_STATUS 0x0 | ||
| 33 | #define DRV260X_MODE 0x1 | ||
| 34 | #define DRV260X_RT_PB_IN 0x2 | ||
| 35 | #define DRV260X_LIB_SEL 0x3 | ||
| 36 | #define DRV260X_WV_SEQ_1 0x4 | ||
| 37 | #define DRV260X_WV_SEQ_2 0x5 | ||
| 38 | #define DRV260X_WV_SEQ_3 0x6 | ||
| 39 | #define DRV260X_WV_SEQ_4 0x7 | ||
| 40 | #define DRV260X_WV_SEQ_5 0x8 | ||
| 41 | #define DRV260X_WV_SEQ_6 0x9 | ||
| 42 | #define DRV260X_WV_SEQ_7 0xa | ||
| 43 | #define DRV260X_WV_SEQ_8 0xb | ||
| 44 | #define DRV260X_GO 0xc | ||
| 45 | #define DRV260X_OVERDRIVE_OFF 0xd | ||
| 46 | #define DRV260X_SUSTAIN_P_OFF 0xe | ||
| 47 | #define DRV260X_SUSTAIN_N_OFF 0xf | ||
| 48 | #define DRV260X_BRAKE_OFF 0x10 | ||
| 49 | #define DRV260X_A_TO_V_CTRL 0x11 | ||
| 50 | #define DRV260X_A_TO_V_MIN_INPUT 0x12 | ||
| 51 | #define DRV260X_A_TO_V_MAX_INPUT 0x13 | ||
| 52 | #define DRV260X_A_TO_V_MIN_OUT 0x14 | ||
| 53 | #define DRV260X_A_TO_V_MAX_OUT 0x15 | ||
| 54 | #define DRV260X_RATED_VOLT 0x16 | ||
| 55 | #define DRV260X_OD_CLAMP_VOLT 0x17 | ||
| 56 | #define DRV260X_CAL_COMP 0x18 | ||
| 57 | #define DRV260X_CAL_BACK_EMF 0x19 | ||
| 58 | #define DRV260X_FEEDBACK_CTRL 0x1a | ||
| 59 | #define DRV260X_CTRL1 0x1b | ||
| 60 | #define DRV260X_CTRL2 0x1c | ||
| 61 | #define DRV260X_CTRL3 0x1d | ||
| 62 | #define DRV260X_CTRL4 0x1e | ||
| 63 | #define DRV260X_CTRL5 0x1f | ||
| 64 | #define DRV260X_LRA_LOOP_PERIOD 0x20 | ||
| 65 | #define DRV260X_VBAT_MON 0x21 | ||
| 66 | #define DRV260X_LRA_RES_PERIOD 0x22 | ||
| 67 | #define DRV260X_MAX_REG 0x23 | ||
| 68 | |||
| 69 | #define DRV260X_GO_BIT 0x01 | ||
| 70 | |||
| 71 | /* Library Selection */ | ||
| 72 | #define DRV260X_LIB_SEL_MASK 0x07 | ||
| 73 | #define DRV260X_LIB_SEL_RAM 0x0 | ||
| 74 | #define DRV260X_LIB_SEL_OD 0x1 | ||
| 75 | #define DRV260X_LIB_SEL_40_60 0x2 | ||
| 76 | #define DRV260X_LIB_SEL_60_80 0x3 | ||
| 77 | #define DRV260X_LIB_SEL_100_140 0x4 | ||
| 78 | #define DRV260X_LIB_SEL_140_PLUS 0x5 | ||
| 79 | |||
| 80 | #define DRV260X_LIB_SEL_HIZ_MASK 0x10 | ||
| 81 | #define DRV260X_LIB_SEL_HIZ_EN 0x01 | ||
| 82 | #define DRV260X_LIB_SEL_HIZ_DIS 0 | ||
| 83 | |||
| 84 | /* Mode register */ | ||
| 85 | #define DRV260X_STANDBY (1 << 6) | ||
| 86 | #define DRV260X_STANDBY_MASK 0x40 | ||
| 87 | #define DRV260X_INTERNAL_TRIGGER 0x00 | ||
| 88 | #define DRV260X_EXT_TRIGGER_EDGE 0x01 | ||
| 89 | #define DRV260X_EXT_TRIGGER_LEVEL 0x02 | ||
| 90 | #define DRV260X_PWM_ANALOG_IN 0x03 | ||
| 91 | #define DRV260X_AUDIOHAPTIC 0x04 | ||
| 92 | #define DRV260X_RT_PLAYBACK 0x05 | ||
| 93 | #define DRV260X_DIAGNOSTICS 0x06 | ||
| 94 | #define DRV260X_AUTO_CAL 0x07 | ||
| 95 | |||
| 96 | /* Audio to Haptics Control */ | ||
| 97 | #define DRV260X_AUDIO_HAPTICS_PEAK_10MS (0 << 2) | ||
| 98 | #define DRV260X_AUDIO_HAPTICS_PEAK_20MS (1 << 2) | ||
| 99 | #define DRV260X_AUDIO_HAPTICS_PEAK_30MS (2 << 2) | ||
| 100 | #define DRV260X_AUDIO_HAPTICS_PEAK_40MS (3 << 2) | ||
| 101 | |||
| 102 | #define DRV260X_AUDIO_HAPTICS_FILTER_100HZ 0x00 | ||
| 103 | #define DRV260X_AUDIO_HAPTICS_FILTER_125HZ 0x01 | ||
| 104 | #define DRV260X_AUDIO_HAPTICS_FILTER_150HZ 0x02 | ||
| 105 | #define DRV260X_AUDIO_HAPTICS_FILTER_200HZ 0x03 | ||
| 106 | |||
| 107 | /* Min/Max Input/Output Voltages */ | ||
| 108 | #define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT 0x19 | ||
| 109 | #define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT 0x64 | ||
| 110 | #define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT 0x19 | ||
| 111 | #define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT 0xFF | ||
| 112 | |||
| 113 | /* Feedback register */ | ||
| 114 | #define DRV260X_FB_REG_ERM_MODE 0x7f | ||
| 115 | #define DRV260X_FB_REG_LRA_MODE (1 << 7) | ||
| 116 | |||
| 117 | #define DRV260X_BRAKE_FACTOR_MASK 0x1f | ||
| 118 | #define DRV260X_BRAKE_FACTOR_2X (1 << 0) | ||
| 119 | #define DRV260X_BRAKE_FACTOR_3X (2 << 4) | ||
| 120 | #define DRV260X_BRAKE_FACTOR_4X (3 << 4) | ||
| 121 | #define DRV260X_BRAKE_FACTOR_6X (4 << 4) | ||
| 122 | #define DRV260X_BRAKE_FACTOR_8X (5 << 4) | ||
| 123 | #define DRV260X_BRAKE_FACTOR_16 (6 << 4) | ||
| 124 | #define DRV260X_BRAKE_FACTOR_DIS (7 << 4) | ||
| 125 | |||
| 126 | #define DRV260X_LOOP_GAIN_LOW 0xf3 | ||
| 127 | #define DRV260X_LOOP_GAIN_MED (1 << 2) | ||
| 128 | #define DRV260X_LOOP_GAIN_HIGH (2 << 2) | ||
| 129 | #define DRV260X_LOOP_GAIN_VERY_HIGH (3 << 2) | ||
| 130 | |||
| 131 | #define DRV260X_BEMF_GAIN_0 0xfc | ||
| 132 | #define DRV260X_BEMF_GAIN_1 (1 << 0) | ||
| 133 | #define DRV260X_BEMF_GAIN_2 (2 << 0) | ||
| 134 | #define DRV260X_BEMF_GAIN_3 (3 << 0) | ||
| 135 | |||
| 136 | /* Control 1 register */ | ||
| 137 | #define DRV260X_AC_CPLE_EN (1 << 5) | ||
| 138 | #define DRV260X_STARTUP_BOOST (1 << 7) | ||
| 139 | |||
| 140 | /* Control 2 register */ | ||
| 141 | |||
| 142 | #define DRV260X_IDISS_TIME_45 0 | ||
| 143 | #define DRV260X_IDISS_TIME_75 (1 << 0) | ||
| 144 | #define DRV260X_IDISS_TIME_150 (1 << 1) | ||
| 145 | #define DRV260X_IDISS_TIME_225 0x03 | ||
| 146 | |||
| 147 | #define DRV260X_BLANK_TIME_45 (0 << 2) | ||
| 148 | #define DRV260X_BLANK_TIME_75 (1 << 2) | ||
| 149 | #define DRV260X_BLANK_TIME_150 (2 << 2) | ||
| 150 | #define DRV260X_BLANK_TIME_225 (3 << 2) | ||
| 151 | |||
| 152 | #define DRV260X_SAMP_TIME_150 (0 << 4) | ||
| 153 | #define DRV260X_SAMP_TIME_200 (1 << 4) | ||
| 154 | #define DRV260X_SAMP_TIME_250 (2 << 4) | ||
| 155 | #define DRV260X_SAMP_TIME_300 (3 << 4) | ||
| 156 | |||
| 157 | #define DRV260X_BRAKE_STABILIZER (1 << 6) | ||
| 158 | #define DRV260X_UNIDIR_IN (0 << 7) | ||
| 159 | #define DRV260X_BIDIR_IN (1 << 7) | ||
| 160 | |||
| 161 | /* Control 3 Register */ | ||
| 162 | #define DRV260X_LRA_OPEN_LOOP (1 << 0) | ||
| 163 | #define DRV260X_ANANLOG_IN (1 << 1) | ||
| 164 | #define DRV260X_LRA_DRV_MODE (1 << 2) | ||
| 165 | #define DRV260X_RTP_UNSIGNED_DATA (1 << 3) | ||
| 166 | #define DRV260X_SUPPLY_COMP_DIS (1 << 4) | ||
| 167 | #define DRV260X_ERM_OPEN_LOOP (1 << 5) | ||
| 168 | #define DRV260X_NG_THRESH_0 (0 << 6) | ||
| 169 | #define DRV260X_NG_THRESH_2 (1 << 6) | ||
| 170 | #define DRV260X_NG_THRESH_4 (2 << 6) | ||
| 171 | #define DRV260X_NG_THRESH_8 (3 << 6) | ||
| 172 | |||
| 173 | /* Control 4 Register */ | ||
| 174 | #define DRV260X_AUTOCAL_TIME_150MS (0 << 4) | ||
| 175 | #define DRV260X_AUTOCAL_TIME_250MS (1 << 4) | ||
| 176 | #define DRV260X_AUTOCAL_TIME_500MS (2 << 4) | ||
| 177 | #define DRV260X_AUTOCAL_TIME_1000MS (3 << 4) | ||
| 178 | |||
| 179 | /** | ||
| 180 | * struct drv260x_data - | ||
| 181 | * @input_dev - Pointer to the input device | ||
| 182 | * @client - Pointer to the I2C client | ||
| 183 | * @regmap - Register map of the device | ||
| 184 | * @work - Work item used to off load the enable/disable of the vibration | ||
| 185 | * @enable_gpio - Pointer to the gpio used for enable/disabling | ||
| 186 | * @regulator - Pointer to the regulator for the IC | ||
| 187 | * @magnitude - Magnitude of the vibration event | ||
| 188 | * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA) | ||
| 189 | * @library - The vibration library to be used | ||
| 190 | * @rated_voltage - The rated_voltage of the actuator | ||
| 191 | * @overdriver_voltage - The over drive voltage of the actuator | ||
| 192 | **/ | ||
| 193 | struct drv260x_data { | ||
| 194 | struct input_dev *input_dev; | ||
| 195 | struct i2c_client *client; | ||
| 196 | struct regmap *regmap; | ||
| 197 | struct work_struct work; | ||
| 198 | struct gpio_desc *enable_gpio; | ||
| 199 | struct regulator *regulator; | ||
| 200 | u32 magnitude; | ||
| 201 | u32 mode; | ||
| 202 | u32 library; | ||
| 203 | int rated_voltage; | ||
| 204 | int overdrive_voltage; | ||
| 205 | }; | ||
| 206 | |||
| 207 | static struct reg_default drv260x_reg_defs[] = { | ||
| 208 | { DRV260X_STATUS, 0xe0 }, | ||
| 209 | { DRV260X_MODE, 0x40 }, | ||
| 210 | { DRV260X_RT_PB_IN, 0x00 }, | ||
| 211 | { DRV260X_LIB_SEL, 0x00 }, | ||
| 212 | { DRV260X_WV_SEQ_1, 0x01 }, | ||
| 213 | { DRV260X_WV_SEQ_2, 0x00 }, | ||
| 214 | { DRV260X_WV_SEQ_3, 0x00 }, | ||
| 215 | { DRV260X_WV_SEQ_4, 0x00 }, | ||
| 216 | { DRV260X_WV_SEQ_5, 0x00 }, | ||
| 217 | { DRV260X_WV_SEQ_6, 0x00 }, | ||
| 218 | { DRV260X_WV_SEQ_7, 0x00 }, | ||
| 219 | { DRV260X_WV_SEQ_8, 0x00 }, | ||
| 220 | { DRV260X_GO, 0x00 }, | ||
| 221 | { DRV260X_OVERDRIVE_OFF, 0x00 }, | ||
| 222 | { DRV260X_SUSTAIN_P_OFF, 0x00 }, | ||
| 223 | { DRV260X_SUSTAIN_N_OFF, 0x00 }, | ||
| 224 | { DRV260X_BRAKE_OFF, 0x00 }, | ||
| 225 | { DRV260X_A_TO_V_CTRL, 0x05 }, | ||
| 226 | { DRV260X_A_TO_V_MIN_INPUT, 0x19 }, | ||
| 227 | { DRV260X_A_TO_V_MAX_INPUT, 0xff }, | ||
| 228 | { DRV260X_A_TO_V_MIN_OUT, 0x19 }, | ||
| 229 | { DRV260X_A_TO_V_MAX_OUT, 0xff }, | ||
| 230 | { DRV260X_RATED_VOLT, 0x3e }, | ||
| 231 | { DRV260X_OD_CLAMP_VOLT, 0x8c }, | ||
| 232 | { DRV260X_CAL_COMP, 0x0c }, | ||
| 233 | { DRV260X_CAL_BACK_EMF, 0x6c }, | ||
| 234 | { DRV260X_FEEDBACK_CTRL, 0x36 }, | ||
| 235 | { DRV260X_CTRL1, 0x93 }, | ||
| 236 | { DRV260X_CTRL2, 0xfa }, | ||
| 237 | { DRV260X_CTRL3, 0xa0 }, | ||
| 238 | { DRV260X_CTRL4, 0x20 }, | ||
| 239 | { DRV260X_CTRL5, 0x80 }, | ||
| 240 | { DRV260X_LRA_LOOP_PERIOD, 0x33 }, | ||
| 241 | { DRV260X_VBAT_MON, 0x00 }, | ||
| 242 | { DRV260X_LRA_RES_PERIOD, 0x00 }, | ||
| 243 | }; | ||
| 244 | |||
| 245 | #define DRV260X_DEF_RATED_VOLT 0x90 | ||
| 246 | #define DRV260X_DEF_OD_CLAMP_VOLT 0x90 | ||
| 247 | |||
| 248 | /** | ||
| 249 | * Rated and Overdriver Voltages: | ||
| 250 | * Calculated using the formula r = v * 255 / 5.6 | ||
| 251 | * where r is what will be written to the register | ||
| 252 | * and v is the rated or overdriver voltage of the actuator | ||
| 253 | **/ | ||
| 254 | static int drv260x_calculate_voltage(unsigned int voltage) | ||
| 255 | { | ||
| 256 | return (voltage * 255 / 5600); | ||
| 257 | } | ||
| 258 | |||
| 259 | static void drv260x_worker(struct work_struct *work) | ||
| 260 | { | ||
| 261 | struct drv260x_data *haptics = container_of(work, struct drv260x_data, work); | ||
| 262 | int error; | ||
| 263 | |||
| 264 | gpiod_set_value(haptics->enable_gpio, 1); | ||
| 265 | /* Data sheet says to wait 250us before trying to communicate */ | ||
| 266 | udelay(250); | ||
| 267 | |||
| 268 | error = regmap_write(haptics->regmap, | ||
| 269 | DRV260X_MODE, DRV260X_RT_PLAYBACK); | ||
| 270 | if (error) { | ||
| 271 | dev_err(&haptics->client->dev, | ||
| 272 | "Failed to write set mode: %d\n", error); | ||
| 273 | } else { | ||
| 274 | error = regmap_write(haptics->regmap, | ||
| 275 | DRV260X_RT_PB_IN, haptics->magnitude); | ||
| 276 | if (error) | ||
| 277 | dev_err(&haptics->client->dev, | ||
| 278 | "Failed to set magnitude: %d\n", error); | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | static int drv260x_haptics_play(struct input_dev *input, void *data, | ||
| 283 | struct ff_effect *effect) | ||
| 284 | { | ||
| 285 | struct drv260x_data *haptics = input_get_drvdata(input); | ||
| 286 | |||
| 287 | haptics->mode = DRV260X_LRA_NO_CAL_MODE; | ||
| 288 | |||
| 289 | if (effect->u.rumble.strong_magnitude > 0) | ||
| 290 | haptics->magnitude = effect->u.rumble.strong_magnitude; | ||
| 291 | else if (effect->u.rumble.weak_magnitude > 0) | ||
| 292 | haptics->magnitude = effect->u.rumble.weak_magnitude; | ||
| 293 | else | ||
| 294 | haptics->magnitude = 0; | ||
| 295 | |||
| 296 | schedule_work(&haptics->work); | ||
| 297 | |||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static void drv260x_close(struct input_dev *input) | ||
| 302 | { | ||
| 303 | struct drv260x_data *haptics = input_get_drvdata(input); | ||
| 304 | int error; | ||
| 305 | |||
| 306 | cancel_work_sync(&haptics->work); | ||
| 307 | |||
| 308 | error = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY); | ||
| 309 | if (error) | ||
| 310 | dev_err(&haptics->client->dev, | ||
| 311 | "Failed to enter standby mode: %d\n", error); | ||
| 312 | |||
| 313 | gpiod_set_value(haptics->enable_gpio, 0); | ||
| 314 | } | ||
| 315 | |||
| 316 | static const struct reg_default drv260x_lra_cal_regs[] = { | ||
| 317 | { DRV260X_MODE, DRV260X_AUTO_CAL }, | ||
| 318 | { DRV260X_CTRL3, DRV260X_NG_THRESH_2 }, | ||
| 319 | { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | | ||
| 320 | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH }, | ||
| 321 | }; | ||
| 322 | |||
| 323 | static const struct reg_default drv260x_lra_init_regs[] = { | ||
| 324 | { DRV260X_MODE, DRV260X_RT_PLAYBACK }, | ||
| 325 | { DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | | ||
| 326 | DRV260X_AUDIO_HAPTICS_FILTER_125HZ }, | ||
| 327 | { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, | ||
| 328 | { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, | ||
| 329 | { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT }, | ||
| 330 | { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT }, | ||
| 331 | { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | | ||
| 332 | DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED | | ||
| 333 | DRV260X_BEMF_GAIN_3 }, | ||
| 334 | { DRV260X_CTRL1, DRV260X_STARTUP_BOOST }, | ||
| 335 | { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 }, | ||
| 336 | { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN }, | ||
| 337 | { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, | ||
| 338 | }; | ||
| 339 | |||
| 340 | static const struct reg_default drv260x_erm_cal_regs[] = { | ||
| 341 | { DRV260X_MODE, DRV260X_AUTO_CAL }, | ||
| 342 | { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, | ||
| 343 | { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, | ||
| 344 | { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT }, | ||
| 345 | { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT }, | ||
| 346 | { DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X | | ||
| 347 | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 }, | ||
| 348 | { DRV260X_CTRL1, DRV260X_STARTUP_BOOST }, | ||
| 349 | { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 | | ||
| 350 | DRV260X_IDISS_TIME_75 }, | ||
| 351 | { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP }, | ||
| 352 | { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, | ||
| 353 | }; | ||
| 354 | |||
| 355 | static int drv260x_init(struct drv260x_data *haptics) | ||
| 356 | { | ||
| 357 | int error; | ||
| 358 | unsigned int cal_buf; | ||
| 359 | |||
| 360 | error = regmap_write(haptics->regmap, | ||
| 361 | DRV260X_RATED_VOLT, haptics->rated_voltage); | ||
| 362 | if (error) { | ||
| 363 | dev_err(&haptics->client->dev, | ||
| 364 | "Failed to write DRV260X_RATED_VOLT register: %d\n", | ||
| 365 | error); | ||
| 366 | return error; | ||
| 367 | } | ||
| 368 | |||
| 369 | error = regmap_write(haptics->regmap, | ||
| 370 | DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage); | ||
| 371 | if (error) { | ||
| 372 | dev_err(&haptics->client->dev, | ||
| 373 | "Failed to write DRV260X_OD_CLAMP_VOLT register: %d\n", | ||
| 374 | error); | ||
| 375 | return error; | ||
| 376 | } | ||
| 377 | |||
| 378 | switch (haptics->mode) { | ||
| 379 | case DRV260X_LRA_MODE: | ||
| 380 | error = regmap_register_patch(haptics->regmap, | ||
| 381 | drv260x_lra_cal_regs, | ||
| 382 | ARRAY_SIZE(drv260x_lra_cal_regs)); | ||
| 383 | if (error) { | ||
| 384 | dev_err(&haptics->client->dev, | ||
| 385 | "Failed to write LRA calibration registers: %d\n", | ||
| 386 | error); | ||
| 387 | return error; | ||
| 388 | } | ||
| 389 | |||
| 390 | break; | ||
| 391 | |||
| 392 | case DRV260X_ERM_MODE: | ||
| 393 | error = regmap_register_patch(haptics->regmap, | ||
| 394 | drv260x_erm_cal_regs, | ||
| 395 | ARRAY_SIZE(drv260x_erm_cal_regs)); | ||
| 396 | if (error) { | ||
| 397 | dev_err(&haptics->client->dev, | ||
| 398 | "Failed to write ERM calibration registers: %d\n", | ||
| 399 | error); | ||
| 400 | return error; | ||
| 401 | } | ||
| 402 | |||
| 403 | error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL, | ||
| 404 | DRV260X_LIB_SEL_MASK, | ||
| 405 | haptics->library); | ||
| 406 | if (error) { | ||
| 407 | dev_err(&haptics->client->dev, | ||
| 408 | "Failed to write DRV260X_LIB_SEL register: %d\n", | ||
| 409 | error); | ||
| 410 | return error; | ||
| 411 | } | ||
| 412 | |||
| 413 | break; | ||
| 414 | |||
| 415 | default: | ||
| 416 | error = regmap_register_patch(haptics->regmap, | ||
| 417 | drv260x_lra_init_regs, | ||
| 418 | ARRAY_SIZE(drv260x_lra_init_regs)); | ||
| 419 | if (error) { | ||
| 420 | dev_err(&haptics->client->dev, | ||
| 421 | "Failed to write LRA init registers: %d\n", | ||
| 422 | error); | ||
| 423 | return error; | ||
| 424 | } | ||
| 425 | |||
| 426 | error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL, | ||
| 427 | DRV260X_LIB_SEL_MASK, | ||
| 428 | haptics->library); | ||
| 429 | if (error) { | ||
| 430 | dev_err(&haptics->client->dev, | ||
| 431 | "Failed to write DRV260X_LIB_SEL register: %d\n", | ||
| 432 | error); | ||
| 433 | return error; | ||
| 434 | } | ||
| 435 | |||
| 436 | /* No need to set GO bit here */ | ||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | error = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT); | ||
| 441 | if (error) { | ||
| 442 | dev_err(&haptics->client->dev, | ||
| 443 | "Failed to write GO register: %d\n", | ||
| 444 | error); | ||
| 445 | return error; | ||
| 446 | } | ||
| 447 | |||
| 448 | do { | ||
| 449 | error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf); | ||
| 450 | if (error) { | ||
| 451 | dev_err(&haptics->client->dev, | ||
| 452 | "Failed to read GO register: %d\n", | ||
| 453 | error); | ||
| 454 | return error; | ||
| 455 | } | ||
| 456 | } while (cal_buf == DRV260X_GO_BIT); | ||
| 457 | |||
| 458 | return 0; | ||
| 459 | } | ||
| 460 | |||
| 461 | static const struct regmap_config drv260x_regmap_config = { | ||
| 462 | .reg_bits = 8, | ||
| 463 | .val_bits = 8, | ||
| 464 | |||
| 465 | .max_register = DRV260X_MAX_REG, | ||
| 466 | .reg_defaults = drv260x_reg_defs, | ||
| 467 | .num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs), | ||
| 468 | .cache_type = REGCACHE_NONE, | ||
| 469 | }; | ||
| 470 | |||
| 471 | #ifdef CONFIG_OF | ||
| 472 | static int drv260x_parse_dt(struct device *dev, | ||
| 473 | struct drv260x_data *haptics) | ||
| 474 | { | ||
| 475 | struct device_node *np = dev->of_node; | ||
| 476 | unsigned int voltage; | ||
| 477 | int error; | ||
| 478 | |||
| 479 | error = of_property_read_u32(np, "mode", &haptics->mode); | ||
| 480 | if (error) { | ||
| 481 | dev_err(dev, "%s: No entry for mode\n", __func__); | ||
| 482 | return error; | ||
| 483 | } | ||
| 484 | |||
| 485 | error = of_property_read_u32(np, "library-sel", &haptics->library); | ||
| 486 | if (error) { | ||
| 487 | dev_err(dev, "%s: No entry for library selection\n", | ||
| 488 | __func__); | ||
| 489 | return error; | ||
| 490 | } | ||
| 491 | |||
| 492 | error = of_property_read_u32(np, "vib-rated-mv", &voltage); | ||
| 493 | if (!error) | ||
| 494 | haptics->rated_voltage = drv260x_calculate_voltage(voltage); | ||
| 495 | |||
| 496 | |||
| 497 | error = of_property_read_u32(np, "vib-overdrive-mv", &voltage); | ||
| 498 | if (!error) | ||
| 499 | haptics->overdrive_voltage = drv260x_calculate_voltage(voltage); | ||
| 500 | |||
| 501 | return 0; | ||
| 502 | } | ||
| 503 | #else | ||
| 504 | static inline int drv260x_parse_dt(struct device *dev, | ||
| 505 | struct drv260x_data *haptics) | ||
| 506 | { | ||
| 507 | dev_err(dev, "no platform data defined\n"); | ||
| 508 | |||
| 509 | return -EINVAL; | ||
| 510 | } | ||
| 511 | #endif | ||
| 512 | |||
| 513 | static int drv260x_probe(struct i2c_client *client, | ||
| 514 | const struct i2c_device_id *id) | ||
| 515 | { | ||
| 516 | const struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev); | ||
| 517 | struct drv260x_data *haptics; | ||
| 518 | int error; | ||
| 519 | |||
| 520 | haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL); | ||
| 521 | if (!haptics) | ||
| 522 | return -ENOMEM; | ||
| 523 | |||
| 524 | haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT; | ||
| 525 | haptics->rated_voltage = DRV260X_DEF_RATED_VOLT; | ||
| 526 | |||
| 527 | if (pdata) { | ||
| 528 | haptics->mode = pdata->mode; | ||
| 529 | haptics->library = pdata->library_selection; | ||
| 530 | if (pdata->vib_overdrive_voltage) | ||
| 531 | haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage); | ||
| 532 | if (pdata->vib_rated_voltage) | ||
| 533 | haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage); | ||
| 534 | } else if (client->dev.of_node) { | ||
| 535 | error = drv260x_parse_dt(&client->dev, haptics); | ||
| 536 | if (error) | ||
| 537 | return error; | ||
| 538 | } else { | ||
| 539 | dev_err(&client->dev, "Platform data not set\n"); | ||
| 540 | return -ENODEV; | ||
| 541 | } | ||
| 542 | |||
| 543 | |||
| 544 | if (haptics->mode < DRV260X_LRA_MODE || | ||
| 545 | haptics->mode > DRV260X_ERM_MODE) { | ||
| 546 | dev_err(&client->dev, | ||
| 547 | "Vibrator mode is invalid: %i\n", | ||
| 548 | haptics->mode); | ||
| 549 | return -EINVAL; | ||
| 550 | } | ||
| 551 | |||
| 552 | if (haptics->library < DRV260X_LIB_EMPTY || | ||
| 553 | haptics->library > DRV260X_ERM_LIB_F) { | ||
| 554 | dev_err(&client->dev, | ||
| 555 | "Library value is invalid: %i\n", haptics->library); | ||
| 556 | return -EINVAL; | ||
| 557 | } | ||
| 558 | |||
| 559 | if (haptics->mode == DRV260X_LRA_MODE && | ||
| 560 | haptics->library != DRV260X_LIB_EMPTY && | ||
| 561 | haptics->library != DRV260X_LIB_LRA) { | ||
| 562 | dev_err(&client->dev, | ||
| 563 | "LRA Mode with ERM Library mismatch\n"); | ||
| 564 | return -EINVAL; | ||
| 565 | } | ||
| 566 | |||
| 567 | if (haptics->mode == DRV260X_ERM_MODE && | ||
| 568 | (haptics->library == DRV260X_LIB_EMPTY || | ||
| 569 | haptics->library == DRV260X_LIB_LRA)) { | ||
| 570 | dev_err(&client->dev, | ||
| 571 | "ERM Mode with LRA Library mismatch\n"); | ||
| 572 | return -EINVAL; | ||
| 573 | } | ||
| 574 | |||
| 575 | haptics->regulator = devm_regulator_get(&client->dev, "vbat"); | ||
| 576 | if (IS_ERR(haptics->regulator)) { | ||
| 577 | error = PTR_ERR(haptics->regulator); | ||
| 578 | dev_err(&client->dev, | ||
| 579 | "unable to get regulator, error: %d\n", error); | ||
| 580 | return error; | ||
| 581 | } | ||
| 582 | |||
| 583 | haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable"); | ||
| 584 | if (IS_ERR(haptics->enable_gpio)) { | ||
| 585 | error = PTR_ERR(haptics->enable_gpio); | ||
| 586 | if (error != -ENOENT && error != -ENOSYS) | ||
| 587 | return error; | ||
| 588 | haptics->enable_gpio = NULL; | ||
| 589 | } else { | ||
| 590 | gpiod_direction_output(haptics->enable_gpio, 1); | ||
| 591 | } | ||
| 592 | |||
| 593 | haptics->input_dev = devm_input_allocate_device(&client->dev); | ||
| 594 | if (!haptics->input_dev) { | ||
| 595 | dev_err(&client->dev, "Failed to allocate input device\n"); | ||
| 596 | return -ENOMEM; | ||
| 597 | } | ||
| 598 | |||
| 599 | haptics->input_dev->name = "drv260x:haptics"; | ||
| 600 | haptics->input_dev->dev.parent = client->dev.parent; | ||
| 601 | haptics->input_dev->close = drv260x_close; | ||
| 602 | input_set_drvdata(haptics->input_dev, haptics); | ||
| 603 | input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); | ||
| 604 | |||
| 605 | error = input_ff_create_memless(haptics->input_dev, NULL, | ||
| 606 | drv260x_haptics_play); | ||
| 607 | if (error) { | ||
| 608 | dev_err(&client->dev, "input_ff_create() failed: %d\n", | ||
| 609 | error); | ||
| 610 | return error; | ||
| 611 | } | ||
| 612 | |||
| 613 | INIT_WORK(&haptics->work, drv260x_worker); | ||
| 614 | |||
| 615 | haptics->client = client; | ||
| 616 | i2c_set_clientdata(client, haptics); | ||
| 617 | |||
| 618 | haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config); | ||
| 619 | if (IS_ERR(haptics->regmap)) { | ||
| 620 | error = PTR_ERR(haptics->regmap); | ||
| 621 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | ||
| 622 | error); | ||
| 623 | return error; | ||
| 624 | } | ||
| 625 | |||
| 626 | error = drv260x_init(haptics); | ||
| 627 | if (error) { | ||
| 628 | dev_err(&client->dev, "Device init failed: %d\n", error); | ||
| 629 | return error; | ||
| 630 | } | ||
| 631 | |||
| 632 | error = input_register_device(haptics->input_dev); | ||
| 633 | if (error) { | ||
| 634 | dev_err(&client->dev, "couldn't register input device: %d\n", | ||
| 635 | error); | ||
| 636 | return error; | ||
| 637 | } | ||
| 638 | |||
| 639 | return 0; | ||
| 640 | } | ||
| 641 | |||
| 642 | #ifdef CONFIG_PM_SLEEP | ||
| 643 | static int drv260x_suspend(struct device *dev) | ||
| 644 | { | ||
| 645 | struct drv260x_data *haptics = dev_get_drvdata(dev); | ||
| 646 | int ret = 0; | ||
| 647 | |||
| 648 | mutex_lock(&haptics->input_dev->mutex); | ||
| 649 | |||
| 650 | if (haptics->input_dev->users) { | ||
| 651 | ret = regmap_update_bits(haptics->regmap, | ||
| 652 | DRV260X_MODE, | ||
| 653 | DRV260X_STANDBY_MASK, | ||
| 654 | DRV260X_STANDBY); | ||
| 655 | if (ret) { | ||
| 656 | dev_err(dev, "Failed to set standby mode\n"); | ||
| 657 | goto out; | ||
| 658 | } | ||
| 659 | |||
| 660 | gpiod_set_value(haptics->enable_gpio, 0); | ||
| 661 | |||
| 662 | ret = regulator_disable(haptics->regulator); | ||
| 663 | if (ret) { | ||
| 664 | dev_err(dev, "Failed to disable regulator\n"); | ||
| 665 | regmap_update_bits(haptics->regmap, | ||
| 666 | DRV260X_MODE, | ||
| 667 | DRV260X_STANDBY_MASK, 0); | ||
| 668 | } | ||
| 669 | } | ||
| 670 | out: | ||
| 671 | mutex_unlock(&haptics->input_dev->mutex); | ||
| 672 | return ret; | ||
| 673 | } | ||
| 674 | |||
| 675 | static int drv260x_resume(struct device *dev) | ||
| 676 | { | ||
| 677 | struct drv260x_data *haptics = dev_get_drvdata(dev); | ||
| 678 | int ret = 0; | ||
| 679 | |||
| 680 | mutex_lock(&haptics->input_dev->mutex); | ||
| 681 | |||
| 682 | if (haptics->input_dev->users) { | ||
| 683 | ret = regulator_enable(haptics->regulator); | ||
| 684 | if (ret) { | ||
| 685 | dev_err(dev, "Failed to enable regulator\n"); | ||
| 686 | goto out; | ||
| 687 | } | ||
| 688 | |||
| 689 | ret = regmap_update_bits(haptics->regmap, | ||
| 690 | DRV260X_MODE, | ||
| 691 | DRV260X_STANDBY_MASK, 0); | ||
| 692 | if (ret) { | ||
| 693 | dev_err(dev, "Failed to unset standby mode\n"); | ||
| 694 | regulator_disable(haptics->regulator); | ||
| 695 | goto out; | ||
| 696 | } | ||
| 697 | |||
| 698 | gpiod_set_value(haptics->enable_gpio, 1); | ||
| 699 | } | ||
| 700 | |||
| 701 | out: | ||
| 702 | mutex_unlock(&haptics->input_dev->mutex); | ||
| 703 | return ret; | ||
| 704 | } | ||
| 705 | #endif | ||
| 706 | |||
| 707 | static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume); | ||
| 708 | |||
| 709 | static const struct i2c_device_id drv260x_id[] = { | ||
| 710 | { "drv2605l", 0 }, | ||
| 711 | { } | ||
| 712 | }; | ||
| 713 | MODULE_DEVICE_TABLE(i2c, drv260x_id); | ||
| 714 | |||
| 715 | #ifdef CONFIG_OF | ||
| 716 | static const struct of_device_id drv260x_of_match[] = { | ||
| 717 | { .compatible = "ti,drv2604", }, | ||
| 718 | { .compatible = "ti,drv2604l", }, | ||
| 719 | { .compatible = "ti,drv2605", }, | ||
| 720 | { .compatible = "ti,drv2605l", }, | ||
| 721 | { } | ||
| 722 | }; | ||
| 723 | MODULE_DEVICE_TABLE(of, drv260x_of_match); | ||
| 724 | #endif | ||
| 725 | |||
| 726 | static struct i2c_driver drv260x_driver = { | ||
| 727 | .probe = drv260x_probe, | ||
| 728 | .driver = { | ||
| 729 | .name = "drv260x-haptics", | ||
| 730 | .owner = THIS_MODULE, | ||
| 731 | .of_match_table = of_match_ptr(drv260x_of_match), | ||
| 732 | .pm = &drv260x_pm_ops, | ||
| 733 | }, | ||
| 734 | .id_table = drv260x_id, | ||
| 735 | }; | ||
| 736 | module_i2c_driver(drv260x_driver); | ||
| 737 | |||
| 738 | MODULE_ALIAS("platform:drv260x-haptics"); | ||
| 739 | MODULE_DESCRIPTION("TI DRV260x haptics driver"); | ||
| 740 | MODULE_LICENSE("GPL"); | ||
| 741 | 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 index 000000000000..0f437581cc04 --- /dev/null +++ b/drivers/input/misc/drv2667.c | |||
| @@ -0,0 +1,500 @@ | |||
| 1 | /* | ||
| 2 | * DRV2667 haptics driver family | ||
| 3 | * | ||
| 4 | * Author: Dan Murphy <dmurphy@ti.com> | ||
| 5 | * | ||
| 6 | * Copyright: (C) 2014 Texas Instruments, Inc. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/i2c.h> | ||
| 19 | #include <linux/input.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/regmap.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/delay.h> | ||
| 25 | #include <linux/regulator/consumer.h> | ||
| 26 | |||
| 27 | /* Contol registers */ | ||
| 28 | #define DRV2667_STATUS 0x00 | ||
| 29 | #define DRV2667_CTRL_1 0x01 | ||
| 30 | #define DRV2667_CTRL_2 0x02 | ||
| 31 | /* Waveform sequencer */ | ||
| 32 | #define DRV2667_WV_SEQ_0 0x03 | ||
| 33 | #define DRV2667_WV_SEQ_1 0x04 | ||
| 34 | #define DRV2667_WV_SEQ_2 0x05 | ||
| 35 | #define DRV2667_WV_SEQ_3 0x06 | ||
| 36 | #define DRV2667_WV_SEQ_4 0x07 | ||
| 37 | #define DRV2667_WV_SEQ_5 0x08 | ||
| 38 | #define DRV2667_WV_SEQ_6 0x09 | ||
| 39 | #define DRV2667_WV_SEQ_7 0x0A | ||
| 40 | #define DRV2667_FIFO 0x0B | ||
| 41 | #define DRV2667_PAGE 0xFF | ||
| 42 | #define DRV2667_MAX_REG DRV2667_PAGE | ||
| 43 | |||
| 44 | #define DRV2667_PAGE_0 0x00 | ||
| 45 | #define DRV2667_PAGE_1 0x01 | ||
| 46 | #define DRV2667_PAGE_2 0x02 | ||
| 47 | #define DRV2667_PAGE_3 0x03 | ||
| 48 | #define DRV2667_PAGE_4 0x04 | ||
| 49 | #define DRV2667_PAGE_5 0x05 | ||
| 50 | #define DRV2667_PAGE_6 0x06 | ||
| 51 | #define DRV2667_PAGE_7 0x07 | ||
| 52 | #define DRV2667_PAGE_8 0x08 | ||
| 53 | |||
| 54 | /* RAM fields */ | ||
| 55 | #define DRV2667_RAM_HDR_SZ 0x0 | ||
| 56 | /* RAM Header addresses */ | ||
| 57 | #define DRV2667_RAM_START_HI 0x01 | ||
| 58 | #define DRV2667_RAM_START_LO 0x02 | ||
| 59 | #define DRV2667_RAM_STOP_HI 0x03 | ||
| 60 | #define DRV2667_RAM_STOP_LO 0x04 | ||
| 61 | #define DRV2667_RAM_REPEAT_CT 0x05 | ||
| 62 | /* RAM data addresses */ | ||
| 63 | #define DRV2667_RAM_AMP 0x06 | ||
| 64 | #define DRV2667_RAM_FREQ 0x07 | ||
| 65 | #define DRV2667_RAM_DURATION 0x08 | ||
| 66 | #define DRV2667_RAM_ENVELOPE 0x09 | ||
| 67 | |||
| 68 | /* Control 1 Register */ | ||
| 69 | #define DRV2667_25_VPP_GAIN 0x00 | ||
| 70 | #define DRV2667_50_VPP_GAIN 0x01 | ||
| 71 | #define DRV2667_75_VPP_GAIN 0x02 | ||
| 72 | #define DRV2667_100_VPP_GAIN 0x03 | ||
| 73 | #define DRV2667_DIGITAL_IN 0xfc | ||
| 74 | #define DRV2667_ANALOG_IN (1 << 2) | ||
| 75 | |||
| 76 | /* Control 2 Register */ | ||
| 77 | #define DRV2667_GO (1 << 0) | ||
| 78 | #define DRV2667_STANDBY (1 << 6) | ||
| 79 | #define DRV2667_DEV_RST (1 << 7) | ||
| 80 | |||
| 81 | /* RAM Envelope settings */ | ||
| 82 | #define DRV2667_NO_ENV 0x00 | ||
| 83 | #define DRV2667_32_MS_ENV 0x01 | ||
| 84 | #define DRV2667_64_MS_ENV 0x02 | ||
| 85 | #define DRV2667_96_MS_ENV 0x03 | ||
| 86 | #define DRV2667_128_MS_ENV 0x04 | ||
| 87 | #define DRV2667_160_MS_ENV 0x05 | ||
| 88 | #define DRV2667_192_MS_ENV 0x06 | ||
| 89 | #define DRV2667_224_MS_ENV 0x07 | ||
| 90 | #define DRV2667_256_MS_ENV 0x08 | ||
| 91 | #define DRV2667_512_MS_ENV 0x09 | ||
| 92 | #define DRV2667_768_MS_ENV 0x0a | ||
| 93 | #define DRV2667_1024_MS_ENV 0x0b | ||
| 94 | #define DRV2667_1280_MS_ENV 0x0c | ||
| 95 | #define DRV2667_1536_MS_ENV 0x0d | ||
| 96 | #define DRV2667_1792_MS_ENV 0x0e | ||
| 97 | #define DRV2667_2048_MS_ENV 0x0f | ||
| 98 | |||
| 99 | /** | ||
| 100 | * struct drv2667_data - | ||
| 101 | * @input_dev - Pointer to the input device | ||
| 102 | * @client - Pointer to the I2C client | ||
| 103 | * @regmap - Register map of the device | ||
| 104 | * @work - Work item used to off load the enable/disable of the vibration | ||
| 105 | * @regulator - Pointer to the regulator for the IC | ||
| 106 | * @magnitude - Magnitude of the vibration event | ||
| 107 | **/ | ||
| 108 | struct drv2667_data { | ||
| 109 | struct input_dev *input_dev; | ||
| 110 | struct i2c_client *client; | ||
| 111 | struct regmap *regmap; | ||
| 112 | struct work_struct work; | ||
| 113 | struct regulator *regulator; | ||
| 114 | u32 page; | ||
| 115 | u32 magnitude; | ||
| 116 | u32 frequency; | ||
| 117 | }; | ||
| 118 | |||
| 119 | static struct reg_default drv2667_reg_defs[] = { | ||
| 120 | { DRV2667_STATUS, 0x02 }, | ||
| 121 | { DRV2667_CTRL_1, 0x28 }, | ||
| 122 | { DRV2667_CTRL_2, 0x40 }, | ||
| 123 | { DRV2667_WV_SEQ_0, 0x00 }, | ||
| 124 | { DRV2667_WV_SEQ_1, 0x00 }, | ||
| 125 | { DRV2667_WV_SEQ_2, 0x00 }, | ||
| 126 | { DRV2667_WV_SEQ_3, 0x00 }, | ||
| 127 | { DRV2667_WV_SEQ_4, 0x00 }, | ||
| 128 | { DRV2667_WV_SEQ_5, 0x00 }, | ||
| 129 | { DRV2667_WV_SEQ_6, 0x00 }, | ||
| 130 | { DRV2667_WV_SEQ_7, 0x00 }, | ||
| 131 | { DRV2667_FIFO, 0x00 }, | ||
| 132 | { DRV2667_PAGE, 0x00 }, | ||
| 133 | }; | ||
| 134 | |||
| 135 | static int drv2667_set_waveform_freq(struct drv2667_data *haptics) | ||
| 136 | { | ||
| 137 | unsigned int read_buf; | ||
| 138 | int freq; | ||
| 139 | int error; | ||
| 140 | |||
| 141 | /* Per the data sheet: | ||
| 142 | * Sinusoid Frequency (Hz) = 7.8125 x Frequency | ||
| 143 | */ | ||
| 144 | freq = (haptics->frequency * 1000) / 78125; | ||
| 145 | if (freq <= 0) { | ||
| 146 | dev_err(&haptics->client->dev, | ||
| 147 | "ERROR: Frequency calculated to %i\n", freq); | ||
| 148 | return -EINVAL; | ||
| 149 | } | ||
| 150 | |||
| 151 | error = regmap_read(haptics->regmap, DRV2667_PAGE, &read_buf); | ||
| 152 | if (error) { | ||
| 153 | dev_err(&haptics->client->dev, | ||
| 154 | "Failed to read the page number: %d\n", error); | ||
| 155 | return -EIO; | ||
| 156 | } | ||
| 157 | |||
| 158 | if (read_buf == DRV2667_PAGE_0 || | ||
| 159 | haptics->page != read_buf) { | ||
| 160 | error = regmap_write(haptics->regmap, | ||
| 161 | DRV2667_PAGE, haptics->page); | ||
| 162 | if (error) { | ||
| 163 | dev_err(&haptics->client->dev, | ||
| 164 | "Failed to set the page: %d\n", error); | ||
| 165 | return -EIO; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | error = regmap_write(haptics->regmap, DRV2667_RAM_FREQ, freq); | ||
| 170 | if (error) | ||
| 171 | dev_err(&haptics->client->dev, | ||
| 172 | "Failed to set the frequency: %d\n", error); | ||
| 173 | |||
| 174 | /* Reset back to original page */ | ||
| 175 | if (read_buf == DRV2667_PAGE_0 || | ||
| 176 | haptics->page != read_buf) { | ||
| 177 | error = regmap_write(haptics->regmap, DRV2667_PAGE, read_buf); | ||
| 178 | if (error) { | ||
| 179 | dev_err(&haptics->client->dev, | ||
| 180 | "Failed to set the page: %d\n", error); | ||
| 181 | return -EIO; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | return error; | ||
| 186 | } | ||
| 187 | |||
| 188 | static void drv2667_worker(struct work_struct *work) | ||
| 189 | { | ||
| 190 | struct drv2667_data *haptics = container_of(work, struct drv2667_data, work); | ||
| 191 | int error; | ||
| 192 | |||
| 193 | if (haptics->magnitude) { | ||
| 194 | error = regmap_write(haptics->regmap, | ||
| 195 | DRV2667_PAGE, haptics->page); | ||
| 196 | if (error) { | ||
| 197 | dev_err(&haptics->client->dev, | ||
| 198 | "Failed to set the page: %d\n", error); | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | |||
| 202 | error = regmap_write(haptics->regmap, DRV2667_RAM_AMP, | ||
| 203 | haptics->magnitude); | ||
| 204 | if (error) { | ||
| 205 | dev_err(&haptics->client->dev, | ||
| 206 | "Failed to set the amplitude: %d\n", error); | ||
| 207 | return; | ||
| 208 | } | ||
| 209 | |||
| 210 | error = regmap_write(haptics->regmap, | ||
| 211 | DRV2667_PAGE, DRV2667_PAGE_0); | ||
| 212 | if (error) { | ||
| 213 | dev_err(&haptics->client->dev, | ||
| 214 | "Failed to set the page: %d\n", error); | ||
| 215 | return; | ||
| 216 | } | ||
| 217 | |||
| 218 | error = regmap_write(haptics->regmap, | ||
| 219 | DRV2667_CTRL_2, DRV2667_GO); | ||
| 220 | if (error) { | ||
| 221 | dev_err(&haptics->client->dev, | ||
| 222 | "Failed to set the GO bit: %d\n", error); | ||
| 223 | } | ||
| 224 | } else { | ||
| 225 | error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, | ||
| 226 | DRV2667_GO, 0); | ||
| 227 | if (error) { | ||
| 228 | dev_err(&haptics->client->dev, | ||
| 229 | "Failed to unset the GO bit: %d\n", error); | ||
| 230 | } | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | static int drv2667_haptics_play(struct input_dev *input, void *data, | ||
| 235 | struct ff_effect *effect) | ||
| 236 | { | ||
| 237 | struct drv2667_data *haptics = input_get_drvdata(input); | ||
| 238 | |||
| 239 | if (effect->u.rumble.strong_magnitude > 0) | ||
| 240 | haptics->magnitude = effect->u.rumble.strong_magnitude; | ||
| 241 | else if (effect->u.rumble.weak_magnitude > 0) | ||
| 242 | haptics->magnitude = effect->u.rumble.weak_magnitude; | ||
| 243 | else | ||
| 244 | haptics->magnitude = 0; | ||
| 245 | |||
| 246 | schedule_work(&haptics->work); | ||
| 247 | |||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | static void drv2667_close(struct input_dev *input) | ||
| 252 | { | ||
| 253 | struct drv2667_data *haptics = input_get_drvdata(input); | ||
| 254 | int error; | ||
| 255 | |||
| 256 | cancel_work_sync(&haptics->work); | ||
| 257 | |||
| 258 | error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, | ||
| 259 | DRV2667_STANDBY, 1); | ||
| 260 | if (error) | ||
| 261 | dev_err(&haptics->client->dev, | ||
| 262 | "Failed to enter standby mode: %d\n", error); | ||
| 263 | } | ||
| 264 | |||
| 265 | static const struct reg_default drv2667_init_regs[] = { | ||
| 266 | { DRV2667_CTRL_2, 0 }, | ||
| 267 | { DRV2667_CTRL_1, DRV2667_25_VPP_GAIN }, | ||
| 268 | { DRV2667_WV_SEQ_0, 1 }, | ||
| 269 | { DRV2667_WV_SEQ_1, 0 } | ||
| 270 | }; | ||
| 271 | |||
| 272 | static const struct reg_default drv2667_page1_init[] = { | ||
| 273 | { DRV2667_RAM_HDR_SZ, 0x05 }, | ||
| 274 | { DRV2667_RAM_START_HI, 0x80 }, | ||
| 275 | { DRV2667_RAM_START_LO, 0x06 }, | ||
| 276 | { DRV2667_RAM_STOP_HI, 0x00 }, | ||
| 277 | { DRV2667_RAM_STOP_LO, 0x09 }, | ||
| 278 | { DRV2667_RAM_REPEAT_CT, 0 }, | ||
| 279 | { DRV2667_RAM_DURATION, 0x05 }, | ||
| 280 | { DRV2667_RAM_ENVELOPE, DRV2667_NO_ENV }, | ||
| 281 | { DRV2667_RAM_AMP, 0x60 }, | ||
| 282 | }; | ||
| 283 | |||
| 284 | static int drv2667_init(struct drv2667_data *haptics) | ||
| 285 | { | ||
| 286 | int error; | ||
| 287 | |||
| 288 | /* Set default haptic frequency to 195Hz on Page 1*/ | ||
| 289 | haptics->frequency = 195; | ||
| 290 | haptics->page = DRV2667_PAGE_1; | ||
| 291 | |||
| 292 | error = regmap_register_patch(haptics->regmap, | ||
| 293 | drv2667_init_regs, | ||
| 294 | ARRAY_SIZE(drv2667_init_regs)); | ||
| 295 | if (error) { | ||
| 296 | dev_err(&haptics->client->dev, | ||
| 297 | "Failed to write init registers: %d\n", | ||
| 298 | error); | ||
| 299 | return error; | ||
| 300 | } | ||
| 301 | |||
| 302 | error = regmap_write(haptics->regmap, DRV2667_PAGE, haptics->page); | ||
| 303 | if (error) { | ||
| 304 | dev_err(&haptics->client->dev, "Failed to set page: %d\n", | ||
| 305 | error); | ||
| 306 | goto error_out; | ||
| 307 | } | ||
| 308 | |||
| 309 | error = drv2667_set_waveform_freq(haptics); | ||
| 310 | if (error) | ||
| 311 | goto error_page; | ||
| 312 | |||
| 313 | error = regmap_register_patch(haptics->regmap, | ||
| 314 | drv2667_page1_init, | ||
| 315 | ARRAY_SIZE(drv2667_page1_init)); | ||
| 316 | if (error) { | ||
| 317 | dev_err(&haptics->client->dev, | ||
| 318 | "Failed to write page registers: %d\n", | ||
| 319 | error); | ||
| 320 | return error; | ||
| 321 | } | ||
| 322 | |||
| 323 | error = regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0); | ||
| 324 | return error; | ||
| 325 | |||
| 326 | error_page: | ||
| 327 | regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0); | ||
| 328 | error_out: | ||
| 329 | return error; | ||
| 330 | } | ||
| 331 | |||
| 332 | static const struct regmap_config drv2667_regmap_config = { | ||
| 333 | .reg_bits = 8, | ||
| 334 | .val_bits = 8, | ||
| 335 | |||
| 336 | .max_register = DRV2667_MAX_REG, | ||
| 337 | .reg_defaults = drv2667_reg_defs, | ||
| 338 | .num_reg_defaults = ARRAY_SIZE(drv2667_reg_defs), | ||
| 339 | .cache_type = REGCACHE_NONE, | ||
| 340 | }; | ||
| 341 | |||
| 342 | static int drv2667_probe(struct i2c_client *client, | ||
| 343 | const struct i2c_device_id *id) | ||
| 344 | { | ||
| 345 | struct drv2667_data *haptics; | ||
| 346 | int error; | ||
| 347 | |||
| 348 | haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL); | ||
| 349 | if (!haptics) | ||
| 350 | return -ENOMEM; | ||
| 351 | |||
| 352 | haptics->regulator = devm_regulator_get(&client->dev, "vbat"); | ||
| 353 | if (IS_ERR(haptics->regulator)) { | ||
| 354 | error = PTR_ERR(haptics->regulator); | ||
| 355 | dev_err(&client->dev, | ||
| 356 | "unable to get regulator, error: %d\n", error); | ||
| 357 | return error; | ||
| 358 | } | ||
| 359 | |||
| 360 | haptics->input_dev = devm_input_allocate_device(&client->dev); | ||
| 361 | if (!haptics->input_dev) { | ||
| 362 | dev_err(&client->dev, "Failed to allocate input device\n"); | ||
| 363 | return -ENOMEM; | ||
| 364 | } | ||
| 365 | |||
| 366 | haptics->input_dev->name = "drv2667:haptics"; | ||
| 367 | haptics->input_dev->dev.parent = client->dev.parent; | ||
| 368 | haptics->input_dev->close = drv2667_close; | ||
| 369 | input_set_drvdata(haptics->input_dev, haptics); | ||
| 370 | input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); | ||
| 371 | |||
| 372 | error = input_ff_create_memless(haptics->input_dev, NULL, | ||
| 373 | drv2667_haptics_play); | ||
| 374 | if (error) { | ||
| 375 | dev_err(&client->dev, "input_ff_create() failed: %d\n", | ||
| 376 | error); | ||
| 377 | return error; | ||
| 378 | } | ||
| 379 | |||
| 380 | INIT_WORK(&haptics->work, drv2667_worker); | ||
| 381 | |||
| 382 | haptics->client = client; | ||
| 383 | i2c_set_clientdata(client, haptics); | ||
| 384 | |||
| 385 | haptics->regmap = devm_regmap_init_i2c(client, &drv2667_regmap_config); | ||
| 386 | if (IS_ERR(haptics->regmap)) { | ||
| 387 | error = PTR_ERR(haptics->regmap); | ||
| 388 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | ||
| 389 | error); | ||
| 390 | return error; | ||
| 391 | } | ||
| 392 | |||
| 393 | error = drv2667_init(haptics); | ||
| 394 | if (error) { | ||
| 395 | dev_err(&client->dev, "Device init failed: %d\n", error); | ||
| 396 | return error; | ||
| 397 | } | ||
| 398 | |||
| 399 | error = input_register_device(haptics->input_dev); | ||
| 400 | if (error) { | ||
| 401 | dev_err(&client->dev, "couldn't register input device: %d\n", | ||
| 402 | error); | ||
| 403 | return error; | ||
| 404 | } | ||
| 405 | |||
| 406 | return 0; | ||
| 407 | } | ||
| 408 | |||
| 409 | #ifdef CONFIG_PM_SLEEP | ||
| 410 | static int drv2667_suspend(struct device *dev) | ||
| 411 | { | ||
| 412 | struct drv2667_data *haptics = dev_get_drvdata(dev); | ||
| 413 | int ret = 0; | ||
| 414 | |||
| 415 | mutex_lock(&haptics->input_dev->mutex); | ||
| 416 | |||
| 417 | if (haptics->input_dev->users) { | ||
| 418 | ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, | ||
| 419 | DRV2667_STANDBY, 1); | ||
| 420 | if (ret) { | ||
| 421 | dev_err(dev, "Failed to set standby mode\n"); | ||
| 422 | regulator_disable(haptics->regulator); | ||
| 423 | goto out; | ||
| 424 | } | ||
| 425 | |||
| 426 | ret = regulator_disable(haptics->regulator); | ||
| 427 | if (ret) { | ||
| 428 | dev_err(dev, "Failed to disable regulator\n"); | ||
| 429 | regmap_update_bits(haptics->regmap, | ||
| 430 | DRV2667_CTRL_2, | ||
| 431 | DRV2667_STANDBY, 0); | ||
| 432 | } | ||
| 433 | } | ||
| 434 | out: | ||
| 435 | mutex_unlock(&haptics->input_dev->mutex); | ||
| 436 | return ret; | ||
| 437 | } | ||
| 438 | |||
| 439 | static int drv2667_resume(struct device *dev) | ||
| 440 | { | ||
| 441 | struct drv2667_data *haptics = dev_get_drvdata(dev); | ||
| 442 | int ret = 0; | ||
| 443 | |||
| 444 | mutex_lock(&haptics->input_dev->mutex); | ||
| 445 | |||
| 446 | if (haptics->input_dev->users) { | ||
| 447 | ret = regulator_enable(haptics->regulator); | ||
| 448 | if (ret) { | ||
| 449 | dev_err(dev, "Failed to enable regulator\n"); | ||
| 450 | goto out; | ||
| 451 | } | ||
| 452 | |||
| 453 | ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, | ||
| 454 | DRV2667_STANDBY, 0); | ||
| 455 | if (ret) { | ||
| 456 | dev_err(dev, "Failed to unset standby mode\n"); | ||
| 457 | regulator_disable(haptics->regulator); | ||
| 458 | goto out; | ||
| 459 | } | ||
| 460 | |||
| 461 | } | ||
| 462 | |||
| 463 | out: | ||
| 464 | mutex_unlock(&haptics->input_dev->mutex); | ||
| 465 | return ret; | ||
| 466 | } | ||
| 467 | #endif | ||
| 468 | |||
| 469 | static SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume); | ||
| 470 | |||
| 471 | static const struct i2c_device_id drv2667_id[] = { | ||
| 472 | { "drv2667", 0 }, | ||
| 473 | { } | ||
| 474 | }; | ||
| 475 | MODULE_DEVICE_TABLE(i2c, drv2667_id); | ||
| 476 | |||
| 477 | #ifdef CONFIG_OF | ||
| 478 | static const struct of_device_id drv2667_of_match[] = { | ||
| 479 | { .compatible = "ti,drv2667", }, | ||
| 480 | { } | ||
| 481 | }; | ||
| 482 | MODULE_DEVICE_TABLE(of, drv2667_of_match); | ||
| 483 | #endif | ||
| 484 | |||
| 485 | static struct i2c_driver drv2667_driver = { | ||
| 486 | .probe = drv2667_probe, | ||
| 487 | .driver = { | ||
| 488 | .name = "drv2667-haptics", | ||
| 489 | .owner = THIS_MODULE, | ||
| 490 | .of_match_table = of_match_ptr(drv2667_of_match), | ||
| 491 | .pm = &drv2667_pm_ops, | ||
| 492 | }, | ||
| 493 | .id_table = drv2667_id, | ||
| 494 | }; | ||
| 495 | module_i2c_driver(drv2667_driver); | ||
| 496 | |||
| 497 | MODULE_ALIAS("platform:drv2667-haptics"); | ||
| 498 | MODULE_DESCRIPTION("TI DRV2667 haptics driver"); | ||
| 499 | MODULE_LICENSE("GPL"); | ||
| 500 | 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 index 000000000000..d605db4d2f39 --- /dev/null +++ b/drivers/input/misc/max77693-haptic.c | |||
| @@ -0,0 +1,357 @@ | |||
| 1 | /* | ||
| 2 | * MAXIM MAX77693 Haptic device driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014 Samsung Electronics | ||
| 5 | * Jaewon Kim <jaewon02.kim@samsung.com> | ||
| 6 | * | ||
| 7 | * This program is not provided / owned by Maxim Integrated Products. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/err.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/i2c.h> | ||
| 18 | #include <linux/regmap.h> | ||
| 19 | #include <linux/input.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/pwm.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/workqueue.h> | ||
| 25 | #include <linux/regulator/consumer.h> | ||
| 26 | #include <linux/mfd/max77693.h> | ||
| 27 | #include <linux/mfd/max77693-private.h> | ||
| 28 | |||
| 29 | #define MAX_MAGNITUDE_SHIFT 16 | ||
| 30 | |||
| 31 | enum max77693_haptic_motor_type { | ||
| 32 | MAX77693_HAPTIC_ERM = 0, | ||
| 33 | MAX77693_HAPTIC_LRA, | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum max77693_haptic_pulse_mode { | ||
| 37 | MAX77693_HAPTIC_EXTERNAL_MODE = 0, | ||
| 38 | MAX77693_HAPTIC_INTERNAL_MODE, | ||
| 39 | }; | ||
| 40 | |||
| 41 | enum max77693_haptic_pwm_divisor { | ||
| 42 | MAX77693_HAPTIC_PWM_DIVISOR_32 = 0, | ||
| 43 | MAX77693_HAPTIC_PWM_DIVISOR_64, | ||
| 44 | MAX77693_HAPTIC_PWM_DIVISOR_128, | ||
| 45 | MAX77693_HAPTIC_PWM_DIVISOR_256, | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct max77693_haptic { | ||
| 49 | struct regmap *regmap_pmic; | ||
| 50 | struct regmap *regmap_haptic; | ||
| 51 | struct device *dev; | ||
| 52 | struct input_dev *input_dev; | ||
| 53 | struct pwm_device *pwm_dev; | ||
| 54 | struct regulator *motor_reg; | ||
| 55 | |||
| 56 | bool enabled; | ||
| 57 | bool suspend_state; | ||
| 58 | unsigned int magnitude; | ||
| 59 | unsigned int pwm_duty; | ||
| 60 | enum max77693_haptic_motor_type type; | ||
| 61 | enum max77693_haptic_pulse_mode mode; | ||
| 62 | enum max77693_haptic_pwm_divisor pwm_divisor; | ||
| 63 | |||
| 64 | struct work_struct work; | ||
| 65 | }; | ||
| 66 | |||
| 67 | static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) | ||
| 68 | { | ||
| 69 | int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2; | ||
| 70 | int error; | ||
| 71 | |||
| 72 | error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period); | ||
| 73 | if (error) { | ||
| 74 | dev_err(haptic->dev, "failed to configure pwm: %d\n", error); | ||
| 75 | return error; | ||
| 76 | } | ||
| 77 | |||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | |||
| 81 | static int max77693_haptic_configure(struct max77693_haptic *haptic, | ||
| 82 | bool enable) | ||
| 83 | { | ||
| 84 | unsigned int value; | ||
| 85 | int error; | ||
| 86 | |||
| 87 | value = ((haptic->type << MAX77693_CONFIG2_MODE) | | ||
| 88 | (enable << MAX77693_CONFIG2_MEN) | | ||
| 89 | (haptic->mode << MAX77693_CONFIG2_HTYP) | | ||
| 90 | (haptic->pwm_divisor)); | ||
| 91 | |||
| 92 | error = regmap_write(haptic->regmap_haptic, | ||
| 93 | MAX77693_HAPTIC_REG_CONFIG2, value); | ||
| 94 | if (error) { | ||
| 95 | dev_err(haptic->dev, | ||
| 96 | "failed to update haptic config: %d\n", error); | ||
| 97 | return error; | ||
| 98 | } | ||
| 99 | |||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable) | ||
| 104 | { | ||
| 105 | int error; | ||
| 106 | |||
| 107 | error = regmap_update_bits(haptic->regmap_pmic, | ||
| 108 | MAX77693_PMIC_REG_LSCNFG, | ||
| 109 | MAX77693_PMIC_LOW_SYS_MASK, | ||
| 110 | enable << MAX77693_PMIC_LOW_SYS_SHIFT); | ||
| 111 | if (error) { | ||
| 112 | dev_err(haptic->dev, "cannot update pmic regmap: %d\n", error); | ||
| 113 | return error; | ||
| 114 | } | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | static void max77693_haptic_enable(struct max77693_haptic *haptic) | ||
| 120 | { | ||
| 121 | int error; | ||
| 122 | |||
| 123 | if (haptic->enabled) | ||
| 124 | return; | ||
| 125 | |||
| 126 | error = pwm_enable(haptic->pwm_dev); | ||
| 127 | if (error) { | ||
| 128 | dev_err(haptic->dev, | ||
| 129 | "failed to enable haptic pwm device: %d\n", error); | ||
| 130 | return; | ||
| 131 | } | ||
| 132 | |||
| 133 | error = max77693_haptic_lowsys(haptic, true); | ||
| 134 | if (error) | ||
| 135 | goto err_enable_lowsys; | ||
| 136 | |||
| 137 | error = max77693_haptic_configure(haptic, true); | ||
| 138 | if (error) | ||
| 139 | goto err_enable_config; | ||
| 140 | |||
| 141 | haptic->enabled = true; | ||
| 142 | |||
| 143 | return; | ||
| 144 | |||
| 145 | err_enable_config: | ||
| 146 | max77693_haptic_lowsys(haptic, false); | ||
| 147 | err_enable_lowsys: | ||
| 148 | pwm_disable(haptic->pwm_dev); | ||
| 149 | } | ||
| 150 | |||
| 151 | static void max77693_haptic_disable(struct max77693_haptic *haptic) | ||
| 152 | { | ||
| 153 | int error; | ||
| 154 | |||
| 155 | if (haptic->enabled) | ||
| 156 | return; | ||
| 157 | |||
| 158 | error = max77693_haptic_configure(haptic, false); | ||
| 159 | if (error) | ||
| 160 | return; | ||
| 161 | |||
| 162 | error = max77693_haptic_lowsys(haptic, false); | ||
| 163 | if (error) | ||
| 164 | goto err_disable_lowsys; | ||
| 165 | |||
| 166 | pwm_disable(haptic->pwm_dev); | ||
| 167 | haptic->enabled = false; | ||
| 168 | |||
| 169 | return; | ||
| 170 | |||
| 171 | err_disable_lowsys: | ||
| 172 | max77693_haptic_configure(haptic, true); | ||
| 173 | } | ||
| 174 | |||
| 175 | static void max77693_haptic_play_work(struct work_struct *work) | ||
| 176 | { | ||
| 177 | struct max77693_haptic *haptic = | ||
| 178 | container_of(work, struct max77693_haptic, work); | ||
| 179 | int error; | ||
| 180 | |||
| 181 | error = max77693_haptic_set_duty_cycle(haptic); | ||
| 182 | if (error) { | ||
| 183 | dev_err(haptic->dev, "failed to set duty cycle: %d\n", error); | ||
| 184 | return; | ||
| 185 | } | ||
| 186 | |||
| 187 | if (haptic->magnitude) | ||
| 188 | max77693_haptic_enable(haptic); | ||
| 189 | else | ||
| 190 | max77693_haptic_disable(haptic); | ||
| 191 | } | ||
| 192 | |||
| 193 | static int max77693_haptic_play_effect(struct input_dev *dev, void *data, | ||
| 194 | struct ff_effect *effect) | ||
| 195 | { | ||
| 196 | struct max77693_haptic *haptic = input_get_drvdata(dev); | ||
| 197 | uint64_t period_mag_multi; | ||
| 198 | |||
| 199 | haptic->magnitude = effect->u.rumble.strong_magnitude; | ||
| 200 | if (!haptic->magnitude) | ||
| 201 | haptic->magnitude = effect->u.rumble.weak_magnitude; | ||
| 202 | |||
| 203 | /* | ||
| 204 | * The magnitude comes from force-feedback interface. | ||
| 205 | * The formula to convert magnitude to pwm_duty as follows: | ||
| 206 | * - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF) | ||
| 207 | */ | ||
| 208 | period_mag_multi = (int64_t)(haptic->pwm_dev->period * | ||
| 209 | haptic->magnitude); | ||
| 210 | haptic->pwm_duty = (unsigned int)(period_mag_multi >> | ||
| 211 | MAX_MAGNITUDE_SHIFT); | ||
| 212 | |||
| 213 | schedule_work(&haptic->work); | ||
| 214 | |||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | static int max77693_haptic_open(struct input_dev *dev) | ||
| 219 | { | ||
| 220 | struct max77693_haptic *haptic = input_get_drvdata(dev); | ||
| 221 | int error; | ||
| 222 | |||
| 223 | error = regulator_enable(haptic->motor_reg); | ||
| 224 | if (error) { | ||
| 225 | dev_err(haptic->dev, | ||
| 226 | "failed to enable regulator: %d\n", error); | ||
| 227 | return error; | ||
| 228 | } | ||
| 229 | |||
| 230 | return 0; | ||
| 231 | } | ||
| 232 | |||
| 233 | static void max77693_haptic_close(struct input_dev *dev) | ||
| 234 | { | ||
| 235 | struct max77693_haptic *haptic = input_get_drvdata(dev); | ||
| 236 | int error; | ||
| 237 | |||
| 238 | cancel_work_sync(&haptic->work); | ||
| 239 | max77693_haptic_disable(haptic); | ||
| 240 | |||
| 241 | error = regulator_disable(haptic->motor_reg); | ||
| 242 | if (error) | ||
| 243 | dev_err(haptic->dev, | ||
| 244 | "failed to disable regulator: %d\n", error); | ||
| 245 | } | ||
| 246 | |||
| 247 | static int max77693_haptic_probe(struct platform_device *pdev) | ||
| 248 | { | ||
| 249 | struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); | ||
| 250 | struct max77693_haptic *haptic; | ||
| 251 | int error; | ||
| 252 | |||
| 253 | haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); | ||
| 254 | if (!haptic) | ||
| 255 | return -ENOMEM; | ||
| 256 | |||
| 257 | haptic->regmap_pmic = max77693->regmap; | ||
| 258 | haptic->regmap_haptic = max77693->regmap_haptic; | ||
| 259 | haptic->dev = &pdev->dev; | ||
| 260 | haptic->type = MAX77693_HAPTIC_LRA; | ||
| 261 | haptic->mode = MAX77693_HAPTIC_EXTERNAL_MODE; | ||
| 262 | haptic->pwm_divisor = MAX77693_HAPTIC_PWM_DIVISOR_128; | ||
| 263 | haptic->suspend_state = false; | ||
| 264 | |||
| 265 | INIT_WORK(&haptic->work, max77693_haptic_play_work); | ||
| 266 | |||
| 267 | /* Get pwm and regulatot for haptic device */ | ||
| 268 | haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL); | ||
| 269 | if (IS_ERR(haptic->pwm_dev)) { | ||
| 270 | dev_err(&pdev->dev, "failed to get pwm device\n"); | ||
| 271 | return PTR_ERR(haptic->pwm_dev); | ||
| 272 | } | ||
| 273 | |||
| 274 | haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic"); | ||
| 275 | if (IS_ERR(haptic->motor_reg)) { | ||
| 276 | dev_err(&pdev->dev, "failed to get regulator\n"); | ||
| 277 | return PTR_ERR(haptic->motor_reg); | ||
| 278 | } | ||
| 279 | |||
| 280 | /* Initialize input device for haptic device */ | ||
| 281 | haptic->input_dev = devm_input_allocate_device(&pdev->dev); | ||
| 282 | if (!haptic->input_dev) { | ||
| 283 | dev_err(&pdev->dev, "failed to allocate input device\n"); | ||
| 284 | return -ENOMEM; | ||
| 285 | } | ||
| 286 | |||
| 287 | haptic->input_dev->name = "max77693-haptic"; | ||
| 288 | haptic->input_dev->id.version = 1; | ||
| 289 | haptic->input_dev->dev.parent = &pdev->dev; | ||
| 290 | haptic->input_dev->open = max77693_haptic_open; | ||
| 291 | haptic->input_dev->close = max77693_haptic_close; | ||
| 292 | input_set_drvdata(haptic->input_dev, haptic); | ||
| 293 | input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); | ||
| 294 | |||
| 295 | error = input_ff_create_memless(haptic->input_dev, NULL, | ||
| 296 | max77693_haptic_play_effect); | ||
| 297 | if (error) { | ||
| 298 | dev_err(&pdev->dev, "failed to create force-feedback\n"); | ||
| 299 | return error; | ||
| 300 | } | ||
| 301 | |||
| 302 | error = input_register_device(haptic->input_dev); | ||
| 303 | if (error) { | ||
| 304 | dev_err(&pdev->dev, "failed to register input device\n"); | ||
| 305 | return error; | ||
| 306 | } | ||
| 307 | |||
| 308 | platform_set_drvdata(pdev, haptic); | ||
| 309 | |||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | #ifdef CONFIG_PM_SLEEP | ||
| 314 | static int max77693_haptic_suspend(struct device *dev) | ||
| 315 | { | ||
| 316 | struct platform_device *pdev = to_platform_device(dev); | ||
| 317 | struct max77693_haptic *haptic = platform_get_drvdata(pdev); | ||
| 318 | |||
| 319 | if (haptic->enabled) { | ||
| 320 | max77693_haptic_disable(haptic); | ||
| 321 | haptic->suspend_state = true; | ||
| 322 | } | ||
| 323 | |||
| 324 | return 0; | ||
| 325 | } | ||
| 326 | |||
| 327 | static int max77693_haptic_resume(struct device *dev) | ||
| 328 | { | ||
| 329 | struct platform_device *pdev = to_platform_device(dev); | ||
| 330 | struct max77693_haptic *haptic = platform_get_drvdata(pdev); | ||
| 331 | |||
| 332 | if (haptic->suspend_state) { | ||
| 333 | max77693_haptic_enable(haptic); | ||
| 334 | haptic->suspend_state = false; | ||
| 335 | } | ||
| 336 | |||
| 337 | return 0; | ||
| 338 | } | ||
| 339 | #endif | ||
| 340 | |||
| 341 | static SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops, | ||
| 342 | max77693_haptic_suspend, max77693_haptic_resume); | ||
| 343 | |||
| 344 | static struct platform_driver max77693_haptic_driver = { | ||
| 345 | .driver = { | ||
| 346 | .name = "max77693-haptic", | ||
| 347 | .owner = THIS_MODULE, | ||
| 348 | .pm = &max77693_haptic_pm_ops, | ||
| 349 | }, | ||
| 350 | .probe = max77693_haptic_probe, | ||
| 351 | }; | ||
| 352 | module_platform_driver(max77693_haptic_driver); | ||
| 353 | |||
| 354 | MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); | ||
| 355 | MODULE_DESCRIPTION("MAXIM MAX77693 Haptic driver"); | ||
| 356 | MODULE_ALIAS("platform:max77693-haptic"); | ||
| 357 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c new file mode 100644 index 000000000000..f505ac3a8d87 --- /dev/null +++ b/drivers/input/misc/palmas-pwrbutton.c | |||
| @@ -0,0 +1,332 @@ | |||
| 1 | /* | ||
| 2 | * Texas Instruments' Palmas Power Button Input Driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/ | ||
| 5 | * Girish S Ghongdemath | ||
| 6 | * Nishanth Menon | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
| 13 | * kind, whether express or implied; without even the implied warranty | ||
| 14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/init.h> | ||
| 19 | #include <linux/input.h> | ||
| 20 | #include <linux/interrupt.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/mfd/palmas.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/of.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | |||
| 28 | #define PALMAS_LPK_TIME_MASK 0x0c | ||
| 29 | #define PALMAS_PWRON_DEBOUNCE_MASK 0x03 | ||
| 30 | #define PALMAS_PWR_KEY_Q_TIME_MS 20 | ||
| 31 | |||
| 32 | /** | ||
| 33 | * struct palmas_pwron - Palmas power on data | ||
| 34 | * @palmas: pointer to palmas device | ||
| 35 | * @input_dev: pointer to input device | ||
| 36 | * @input_work: work for detecting release of key | ||
| 37 | * @irq: irq that we are hooked on to | ||
| 38 | */ | ||
| 39 | struct palmas_pwron { | ||
| 40 | struct palmas *palmas; | ||
| 41 | struct input_dev *input_dev; | ||
| 42 | struct delayed_work input_work; | ||
| 43 | int irq; | ||
| 44 | }; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * struct palmas_pwron_config - configuration of palmas power on | ||
| 48 | * @long_press_time_val: value for long press h/w shutdown event | ||
| 49 | * @pwron_debounce_val: value for debounce of power button | ||
| 50 | */ | ||
| 51 | struct palmas_pwron_config { | ||
| 52 | u8 long_press_time_val; | ||
| 53 | u8 pwron_debounce_val; | ||
| 54 | }; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * palmas_power_button_work() - Detects the button release event | ||
| 58 | * @work: work item to detect button release | ||
| 59 | */ | ||
| 60 | static void palmas_power_button_work(struct work_struct *work) | ||
| 61 | { | ||
| 62 | struct palmas_pwron *pwron = container_of(work, | ||
| 63 | struct palmas_pwron, | ||
| 64 | input_work.work); | ||
| 65 | struct input_dev *input_dev = pwron->input_dev; | ||
| 66 | unsigned int reg; | ||
| 67 | int error; | ||
| 68 | |||
| 69 | error = palmas_read(pwron->palmas, PALMAS_INTERRUPT_BASE, | ||
| 70 | PALMAS_INT1_LINE_STATE, ®); | ||
| 71 | if (error) { | ||
| 72 | dev_err(input_dev->dev.parent, | ||
| 73 | "Cannot read palmas PWRON status: %d\n", error); | ||
| 74 | } else if (reg & BIT(1)) { | ||
| 75 | /* The button is released, report event. */ | ||
| 76 | input_report_key(input_dev, KEY_POWER, 0); | ||
| 77 | input_sync(input_dev); | ||
| 78 | } else { | ||
| 79 | /* The button is still depressed, keep checking. */ | ||
| 80 | schedule_delayed_work(&pwron->input_work, | ||
| 81 | msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS)); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /** | ||
| 86 | * pwron_irq() - button press isr | ||
| 87 | * @irq: irq | ||
| 88 | * @palmas_pwron: pwron struct | ||
| 89 | * | ||
| 90 | * Return: IRQ_HANDLED | ||
| 91 | */ | ||
| 92 | static irqreturn_t pwron_irq(int irq, void *palmas_pwron) | ||
| 93 | { | ||
| 94 | struct palmas_pwron *pwron = palmas_pwron; | ||
| 95 | struct input_dev *input_dev = pwron->input_dev; | ||
| 96 | |||
| 97 | input_report_key(input_dev, KEY_POWER, 1); | ||
| 98 | pm_wakeup_event(input_dev->dev.parent, 0); | ||
| 99 | input_sync(input_dev); | ||
| 100 | |||
| 101 | mod_delayed_work(system_wq, &pwron->input_work, | ||
| 102 | msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS)); | ||
| 103 | |||
| 104 | return IRQ_HANDLED; | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * palmas_pwron_params_ofinit() - device tree parameter parser | ||
| 109 | * @dev: palmas button device | ||
| 110 | * @config: configuration params that this fills up | ||
| 111 | */ | ||
| 112 | static void palmas_pwron_params_ofinit(struct device *dev, | ||
| 113 | struct palmas_pwron_config *config) | ||
| 114 | { | ||
| 115 | struct device_node *np; | ||
| 116 | u32 val; | ||
| 117 | int i, error; | ||
| 118 | u8 lpk_times[] = { 6, 8, 10, 12 }; | ||
| 119 | int pwr_on_deb_ms[] = { 15, 100, 500, 1000 }; | ||
| 120 | |||
| 121 | memset(config, 0, sizeof(*config)); | ||
| 122 | |||
| 123 | /* Default config parameters */ | ||
| 124 | config->long_press_time_val = ARRAY_SIZE(lpk_times) - 1; | ||
| 125 | |||
| 126 | np = dev->of_node; | ||
| 127 | if (!np) | ||
| 128 | return; | ||
| 129 | |||
| 130 | error = of_property_read_u32(np, "ti,palmas-long-press-seconds", &val); | ||
| 131 | if (!error) { | ||
| 132 | for (i = 0; i < ARRAY_SIZE(lpk_times); i++) { | ||
| 133 | if (val <= lpk_times[i]) { | ||
| 134 | config->long_press_time_val = i; | ||
| 135 | break; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | error = of_property_read_u32(np, | ||
| 141 | "ti,palmas-pwron-debounce-milli-seconds", | ||
| 142 | &val); | ||
| 143 | if (!error) { | ||
| 144 | for (i = 0; i < ARRAY_SIZE(pwr_on_deb_ms); i++) { | ||
| 145 | if (val <= pwr_on_deb_ms[i]) { | ||
| 146 | config->pwron_debounce_val = i; | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | dev_info(dev, "h/w controlled shutdown duration=%d seconds\n", | ||
| 153 | lpk_times[config->long_press_time_val]); | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * palmas_pwron_probe() - probe | ||
| 158 | * @pdev: platform device for the button | ||
| 159 | * | ||
| 160 | * Return: 0 for successful probe else appropriate error | ||
| 161 | */ | ||
| 162 | static int palmas_pwron_probe(struct platform_device *pdev) | ||
| 163 | { | ||
| 164 | struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); | ||
| 165 | struct device *dev = &pdev->dev; | ||
| 166 | struct input_dev *input_dev; | ||
| 167 | struct palmas_pwron *pwron; | ||
| 168 | struct palmas_pwron_config config; | ||
| 169 | int val; | ||
| 170 | int error; | ||
| 171 | |||
| 172 | palmas_pwron_params_ofinit(dev, &config); | ||
| 173 | |||
| 174 | pwron = kzalloc(sizeof(*pwron), GFP_KERNEL); | ||
| 175 | if (!pwron) | ||
| 176 | return -ENOMEM; | ||
| 177 | |||
| 178 | input_dev = input_allocate_device(); | ||
| 179 | if (!input_dev) { | ||
| 180 | dev_err(dev, "Can't allocate power button\n"); | ||
| 181 | error = -ENOMEM; | ||
| 182 | goto err_free_mem; | ||
| 183 | } | ||
| 184 | |||
| 185 | input_dev->name = "palmas_pwron"; | ||
| 186 | input_dev->phys = "palmas_pwron/input0"; | ||
| 187 | input_dev->dev.parent = dev; | ||
| 188 | |||
| 189 | input_set_capability(input_dev, EV_KEY, KEY_POWER); | ||
| 190 | |||
| 191 | /* | ||
| 192 | * Setup default hardware shutdown option (long key press) | ||
| 193 | * and debounce. | ||
| 194 | */ | ||
| 195 | val = config.long_press_time_val << __ffs(PALMAS_LPK_TIME_MASK); | ||
| 196 | val |= config.pwron_debounce_val << __ffs(PALMAS_PWRON_DEBOUNCE_MASK); | ||
| 197 | error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, | ||
| 198 | PALMAS_LONG_PRESS_KEY, | ||
| 199 | PALMAS_LPK_TIME_MASK | | ||
| 200 | PALMAS_PWRON_DEBOUNCE_MASK, | ||
| 201 | val); | ||
| 202 | if (error) { | ||
| 203 | dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error); | ||
| 204 | goto err_free_input; | ||
| 205 | } | ||
| 206 | |||
| 207 | pwron->palmas = palmas; | ||
| 208 | pwron->input_dev = input_dev; | ||
| 209 | |||
| 210 | INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work); | ||
| 211 | |||
| 212 | pwron->irq = platform_get_irq(pdev, 0); | ||
| 213 | error = request_threaded_irq(pwron->irq, NULL, pwron_irq, | ||
| 214 | IRQF_TRIGGER_HIGH | | ||
| 215 | IRQF_TRIGGER_LOW | | ||
| 216 | IRQF_ONESHOT, | ||
| 217 | dev_name(dev), pwron); | ||
| 218 | if (error) { | ||
| 219 | dev_err(dev, "Can't get IRQ for pwron: %d\n", error); | ||
| 220 | goto err_free_input; | ||
| 221 | } | ||
| 222 | |||
| 223 | error = input_register_device(input_dev); | ||
| 224 | if (error) { | ||
| 225 | dev_err(dev, "Can't register power button: %d\n", error); | ||
| 226 | goto err_free_irq; | ||
| 227 | } | ||
| 228 | |||
| 229 | platform_set_drvdata(pdev, pwron); | ||
| 230 | device_init_wakeup(dev, true); | ||
| 231 | |||
| 232 | return 0; | ||
| 233 | |||
| 234 | err_free_irq: | ||
| 235 | cancel_delayed_work_sync(&pwron->input_work); | ||
| 236 | free_irq(pwron->irq, pwron); | ||
| 237 | err_free_input: | ||
| 238 | input_free_device(input_dev); | ||
| 239 | err_free_mem: | ||
| 240 | kfree(pwron); | ||
| 241 | return error; | ||
| 242 | } | ||
| 243 | |||
| 244 | /** | ||
| 245 | * palmas_pwron_remove() - Cleanup on removal | ||
| 246 | * @pdev: platform device for the button | ||
| 247 | * | ||
| 248 | * Return: 0 | ||
| 249 | */ | ||
| 250 | static int palmas_pwron_remove(struct platform_device *pdev) | ||
| 251 | { | ||
| 252 | struct palmas_pwron *pwron = platform_get_drvdata(pdev); | ||
| 253 | |||
| 254 | free_irq(pwron->irq, pwron); | ||
| 255 | cancel_delayed_work_sync(&pwron->input_work); | ||
| 256 | |||
| 257 | input_unregister_device(pwron->input_dev); | ||
| 258 | kfree(pwron); | ||
| 259 | |||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | #ifdef CONFIG_PM_SLEEP | ||
| 264 | /** | ||
| 265 | * palmas_pwron_suspend() - suspend handler | ||
| 266 | * @dev: power button device | ||
| 267 | * | ||
| 268 | * Cancel all pending work items for the power button, setup irq for wakeup | ||
| 269 | * | ||
| 270 | * Return: 0 | ||
| 271 | */ | ||
| 272 | static int palmas_pwron_suspend(struct device *dev) | ||
| 273 | { | ||
| 274 | struct platform_device *pdev = to_platform_device(dev); | ||
| 275 | struct palmas_pwron *pwron = platform_get_drvdata(pdev); | ||
| 276 | |||
| 277 | cancel_delayed_work_sync(&pwron->input_work); | ||
| 278 | |||
| 279 | if (device_may_wakeup(dev)) | ||
| 280 | enable_irq_wake(pwron->irq); | ||
| 281 | |||
| 282 | return 0; | ||
| 283 | } | ||
| 284 | |||
| 285 | /** | ||
| 286 | * palmas_pwron_resume() - resume handler | ||
| 287 | * @dev: power button device | ||
| 288 | * | ||
| 289 | * Just disable the wakeup capability of irq here. | ||
| 290 | * | ||
| 291 | * Return: 0 | ||
| 292 | */ | ||
| 293 | static int palmas_pwron_resume(struct device *dev) | ||
| 294 | { | ||
| 295 | struct platform_device *pdev = to_platform_device(dev); | ||
| 296 | struct palmas_pwron *pwron = platform_get_drvdata(pdev); | ||
| 297 | |||
| 298 | if (device_may_wakeup(dev)) | ||
| 299 | disable_irq_wake(pwron->irq); | ||
| 300 | |||
| 301 | return 0; | ||
| 302 | } | ||
| 303 | #endif | ||
| 304 | |||
| 305 | static SIMPLE_DEV_PM_OPS(palmas_pwron_pm, | ||
| 306 | palmas_pwron_suspend, palmas_pwron_resume); | ||
| 307 | |||
| 308 | #ifdef CONFIG_OF | ||
| 309 | static struct of_device_id of_palmas_pwr_match[] = { | ||
| 310 | { .compatible = "ti,palmas-pwrbutton" }, | ||
| 311 | { }, | ||
| 312 | }; | ||
| 313 | |||
| 314 | MODULE_DEVICE_TABLE(of, of_palmas_pwr_match); | ||
| 315 | #endif | ||
| 316 | |||
| 317 | static struct platform_driver palmas_pwron_driver = { | ||
| 318 | .probe = palmas_pwron_probe, | ||
| 319 | .remove = palmas_pwron_remove, | ||
| 320 | .driver = { | ||
| 321 | .name = "palmas_pwrbutton", | ||
| 322 | .owner = THIS_MODULE, | ||
| 323 | .of_match_table = of_match_ptr(of_palmas_pwr_match), | ||
| 324 | .pm = &palmas_pwron_pm, | ||
| 325 | }, | ||
| 326 | }; | ||
| 327 | module_platform_driver(palmas_pwron_driver); | ||
| 328 | |||
| 329 | MODULE_ALIAS("platform:palmas-pwrbutton"); | ||
| 330 | MODULE_DESCRIPTION("Palmas Power Button"); | ||
| 331 | MODULE_LICENSE("GPL v2"); | ||
| 332 | MODULE_AUTHOR("Texas Instruments Inc."); | ||
diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index e34dfc29beb3..735604753568 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c | |||
| @@ -18,7 +18,7 @@ | |||
| 18 | #include <linux/gpio/consumer.h> | 18 | #include <linux/gpio/consumer.h> |
| 19 | #include <linux/gpio_keys.h> | 19 | #include <linux/gpio_keys.h> |
| 20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
| 21 | #include <linux/pnp.h> | 21 | #include <linux/acpi.h> |
| 22 | 22 | ||
| 23 | /* | 23 | /* |
| 24 | * Definition of buttons on the tablet. The ACPI index of each button | 24 | * 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) | |||
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | static struct platform_device * | 69 | static struct platform_device * |
| 70 | soc_button_device_create(struct pnp_dev *pdev, | 70 | soc_button_device_create(struct platform_device *pdev, |
| 71 | const struct soc_button_info *button_info, | 71 | const struct soc_button_info *button_info, |
| 72 | bool autorepeat) | 72 | bool autorepeat) |
| 73 | { | 73 | { |
| @@ -138,30 +138,40 @@ err_free_mem: | |||
| 138 | return ERR_PTR(error); | 138 | return ERR_PTR(error); |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | static void soc_button_remove(struct pnp_dev *pdev) | 141 | static int soc_button_remove(struct platform_device *pdev) |
| 142 | { | 142 | { |
| 143 | struct soc_button_data *priv = pnp_get_drvdata(pdev); | 143 | struct soc_button_data *priv = platform_get_drvdata(pdev); |
| 144 | |||
| 144 | int i; | 145 | int i; |
| 145 | 146 | ||
| 146 | for (i = 0; i < BUTTON_TYPES; i++) | 147 | for (i = 0; i < BUTTON_TYPES; i++) |
| 147 | if (priv->children[i]) | 148 | if (priv->children[i]) |
| 148 | platform_device_unregister(priv->children[i]); | 149 | platform_device_unregister(priv->children[i]); |
| 150 | |||
| 151 | return 0; | ||
| 149 | } | 152 | } |
| 150 | 153 | ||
| 151 | static int soc_button_pnp_probe(struct pnp_dev *pdev, | 154 | static int soc_button_probe(struct platform_device *pdev) |
| 152 | const struct pnp_device_id *id) | ||
| 153 | { | 155 | { |
| 154 | const struct soc_button_info *button_info = (void *)id->driver_data; | 156 | struct device *dev = &pdev->dev; |
| 157 | const struct acpi_device_id *id; | ||
| 158 | struct soc_button_info *button_info; | ||
| 155 | struct soc_button_data *priv; | 159 | struct soc_button_data *priv; |
| 156 | struct platform_device *pd; | 160 | struct platform_device *pd; |
| 157 | int i; | 161 | int i; |
| 158 | int error; | 162 | int error; |
| 159 | 163 | ||
| 164 | id = acpi_match_device(dev->driver->acpi_match_table, dev); | ||
| 165 | if (!id) | ||
| 166 | return -ENODEV; | ||
| 167 | |||
| 168 | button_info = (struct soc_button_info *)id->driver_data; | ||
| 169 | |||
| 160 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | 170 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); |
| 161 | if (!priv) | 171 | if (!priv) |
| 162 | return -ENOMEM; | 172 | return -ENOMEM; |
| 163 | 173 | ||
| 164 | pnp_set_drvdata(pdev, priv); | 174 | platform_set_drvdata(pdev, priv); |
| 165 | 175 | ||
| 166 | for (i = 0; i < BUTTON_TYPES; i++) { | 176 | for (i = 0; i < BUTTON_TYPES; i++) { |
| 167 | pd = soc_button_device_create(pdev, button_info, i == 0); | 177 | pd = soc_button_device_create(pdev, button_info, i == 0); |
| @@ -192,30 +202,22 @@ static struct soc_button_info soc_button_PNP0C40[] = { | |||
| 192 | { } | 202 | { } |
| 193 | }; | 203 | }; |
| 194 | 204 | ||
| 195 | static const struct pnp_device_id soc_button_pnp_match[] = { | 205 | static const struct acpi_device_id soc_button_acpi_match[] = { |
| 196 | { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 }, | 206 | { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, |
| 197 | { .id = "" } | 207 | { } |
| 198 | }; | 208 | }; |
| 199 | MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match); | ||
| 200 | 209 | ||
| 201 | static struct pnp_driver soc_button_pnp_driver = { | 210 | MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match); |
| 202 | .name = KBUILD_MODNAME, | 211 | |
| 203 | .id_table = soc_button_pnp_match, | 212 | static struct platform_driver soc_button_driver = { |
| 204 | .probe = soc_button_pnp_probe, | 213 | .probe = soc_button_probe, |
| 205 | .remove = soc_button_remove, | 214 | .remove = soc_button_remove, |
| 215 | .driver = { | ||
| 216 | .name = KBUILD_MODNAME, | ||
| 217 | .owner = THIS_MODULE, | ||
| 218 | .acpi_match_table = ACPI_PTR(soc_button_acpi_match), | ||
| 219 | }, | ||
| 206 | }; | 220 | }; |
| 207 | 221 | module_platform_driver(soc_button_driver); | |
| 208 | static int __init soc_button_init(void) | ||
| 209 | { | ||
| 210 | return pnp_register_driver(&soc_button_pnp_driver); | ||
| 211 | } | ||
| 212 | |||
| 213 | static void __exit soc_button_exit(void) | ||
| 214 | { | ||
| 215 | pnp_unregister_driver(&soc_button_pnp_driver); | ||
| 216 | } | ||
| 217 | |||
| 218 | module_init(soc_button_init); | ||
| 219 | module_exit(soc_button_exit); | ||
| 220 | 222 | ||
| 221 | MODULE_LICENSE("GPL"); | 223 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index c25efdb3f288..dda507f8b3a2 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile | |||
| @@ -23,7 +23,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o | |||
| 23 | obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o | 23 | obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o |
| 24 | obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o | 24 | obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o |
| 25 | 25 | ||
| 26 | psmouse-objs := psmouse-base.o synaptics.o | 26 | psmouse-objs := psmouse-base.o synaptics.o focaltech.o |
| 27 | 27 | ||
| 28 | psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o | 28 | psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o |
| 29 | psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o | 29 | 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 index 000000000000..f4d657ee1cc0 --- /dev/null +++ b/drivers/input/mouse/focaltech.c | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | * Focaltech TouchPad PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Red Hat Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * Red Hat authors: | ||
| 12 | * | ||
| 13 | * Hans de Goede <hdegoede@redhat.com> | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* | ||
| 17 | * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with | ||
| 18 | * detection only, to avoid further detection attempts confusing the touchpad | ||
| 19 | * this way it at least works in PS/2 mouse compatibility mode. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/device.h> | ||
| 23 | #include <linux/libps2.h> | ||
| 24 | #include "psmouse.h" | ||
| 25 | |||
| 26 | static const char * const focaltech_pnp_ids[] = { | ||
| 27 | "FLT0101", | ||
| 28 | "FLT0102", | ||
| 29 | "FLT0103", | ||
| 30 | NULL | ||
| 31 | }; | ||
| 32 | |||
| 33 | int focaltech_detect(struct psmouse *psmouse, bool set_properties) | ||
| 34 | { | ||
| 35 | if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids)) | ||
| 36 | return -ENODEV; | ||
| 37 | |||
| 38 | if (set_properties) { | ||
| 39 | psmouse->vendor = "FocalTech"; | ||
| 40 | psmouse->name = "FocalTech Touchpad in mouse emulation mode"; | ||
| 41 | } | ||
| 42 | |||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | int focaltech_init(struct psmouse *psmouse) | ||
| 47 | { | ||
| 48 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
| 49 | psmouse_reset(psmouse); | ||
| 50 | |||
| 51 | return 0; | ||
| 52 | } | ||
diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h new file mode 100644 index 000000000000..498650c61e28 --- /dev/null +++ b/drivers/input/mouse/focaltech.h | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* | ||
| 2 | * Focaltech TouchPad PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Red Hat Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * Red Hat authors: | ||
| 12 | * | ||
| 13 | * Hans de Goede <hdegoede@redhat.com> | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef _FOCALTECH_H | ||
| 17 | #define _FOCALTECH_H | ||
| 18 | |||
| 19 | int focaltech_detect(struct psmouse *psmouse, bool set_properties); | ||
| 20 | int focaltech_init(struct psmouse *psmouse); | ||
| 21 | |||
| 22 | #endif | ||
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b4e1f014ddc2..26994f6a2b2a 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include "elantech.h" | 35 | #include "elantech.h" |
| 36 | #include "sentelic.h" | 36 | #include "sentelic.h" |
| 37 | #include "cypress_ps2.h" | 37 | #include "cypress_ps2.h" |
| 38 | #include "focaltech.h" | ||
| 38 | 39 | ||
| 39 | #define DRIVER_DESC "PS/2 mouse driver" | 40 | #define DRIVER_DESC "PS/2 mouse driver" |
| 40 | 41 | ||
| @@ -462,6 +463,20 @@ static int psmouse_poll(struct psmouse *psmouse) | |||
| 462 | PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); | 463 | PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); |
| 463 | } | 464 | } |
| 464 | 465 | ||
| 466 | /* | ||
| 467 | * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids. | ||
| 468 | */ | ||
| 469 | bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) | ||
| 470 | { | ||
| 471 | int i; | ||
| 472 | |||
| 473 | if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) | ||
| 474 | for (i = 0; ids[i]; i++) | ||
| 475 | if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) | ||
| 476 | return true; | ||
| 477 | |||
| 478 | return false; | ||
| 479 | } | ||
| 465 | 480 | ||
| 466 | /* | 481 | /* |
| 467 | * Genius NetMouse magic init. | 482 | * Genius NetMouse magic init. |
| @@ -708,6 +723,21 @@ static int psmouse_extensions(struct psmouse *psmouse, | |||
| 708 | { | 723 | { |
| 709 | bool synaptics_hardware = false; | 724 | bool synaptics_hardware = false; |
| 710 | 725 | ||
| 726 | /* Always check for focaltech, this is safe as it uses pnp-id matching */ | ||
| 727 | if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { | ||
| 728 | if (!set_properties || focaltech_init(psmouse) == 0) { | ||
| 729 | /* | ||
| 730 | * Not supported yet, use bare protocol. | ||
| 731 | * Note that we need to also restrict | ||
| 732 | * psmouse_max_proto so that psmouse_initialize() | ||
| 733 | * does not try to reset rate and resolution, | ||
| 734 | * because even that upsets the device. | ||
| 735 | */ | ||
| 736 | psmouse_max_proto = PSMOUSE_PS2; | ||
| 737 | return PSMOUSE_PS2; | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 711 | /* | 741 | /* |
| 712 | * We always check for lifebook because it does not disturb mouse | 742 | * We always check for lifebook because it does not disturb mouse |
| 713 | * (it only checks DMI information). | 743 | * (it only checks DMI information). |
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 2f0b39d59a9b..f4cf664c7db3 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h | |||
| @@ -108,6 +108,7 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); | |||
| 108 | psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); | 108 | psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); |
| 109 | int psmouse_activate(struct psmouse *psmouse); | 109 | int psmouse_activate(struct psmouse *psmouse); |
| 110 | int psmouse_deactivate(struct psmouse *psmouse); | 110 | int psmouse_deactivate(struct psmouse *psmouse); |
| 111 | bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]); | ||
| 111 | 112 | ||
| 112 | struct psmouse_attribute { | 113 | struct psmouse_attribute { |
| 113 | struct device_attribute dattr; | 114 | struct device_attribute dattr; |
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index fd23181c1fb7..6394d9b5bfd3 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c | |||
| @@ -185,18 +185,6 @@ static const char * const topbuttonpad_pnp_ids[] = { | |||
| 185 | NULL | 185 | NULL |
| 186 | }; | 186 | }; |
| 187 | 187 | ||
| 188 | static bool matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) | ||
| 189 | { | ||
| 190 | int i; | ||
| 191 | |||
| 192 | if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) | ||
| 193 | for (i = 0; ids[i]; i++) | ||
| 194 | if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) | ||
| 195 | return true; | ||
| 196 | |||
| 197 | return false; | ||
| 198 | } | ||
| 199 | |||
| 200 | /***************************************************************************** | 188 | /***************************************************************************** |
| 201 | * Synaptics communications functions | 189 | * Synaptics communications functions |
| 202 | ****************************************************************************/ | 190 | ****************************************************************************/ |
| @@ -362,7 +350,8 @@ static int synaptics_resolution(struct psmouse *psmouse) | |||
| 362 | } | 350 | } |
| 363 | 351 | ||
| 364 | for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { | 352 | for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { |
| 365 | if (matches_pnp_id(psmouse, min_max_pnpid_table[i].pnp_ids)) { | 353 | if (psmouse_matches_pnp_id(psmouse, |
| 354 | min_max_pnpid_table[i].pnp_ids)) { | ||
| 366 | priv->x_min = min_max_pnpid_table[i].x_min; | 355 | priv->x_min = min_max_pnpid_table[i].x_min; |
| 367 | priv->x_max = min_max_pnpid_table[i].x_max; | 356 | priv->x_max = min_max_pnpid_table[i].x_max; |
| 368 | priv->y_min = min_max_pnpid_table[i].y_min; | 357 | priv->y_min = min_max_pnpid_table[i].y_min; |
| @@ -1492,7 +1481,7 @@ static void set_input_params(struct psmouse *psmouse, | |||
| 1492 | 1481 | ||
| 1493 | if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { | 1482 | if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { |
| 1494 | __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); | 1483 | __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); |
| 1495 | if (matches_pnp_id(psmouse, topbuttonpad_pnp_ids)) | 1484 | if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids)) |
| 1496 | __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); | 1485 | __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); |
| 1497 | /* Clickpads report only left button */ | 1486 | /* Clickpads report only left button */ |
| 1498 | __clear_bit(BTN_RIGHT, dev->keybit); | 1487 | __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 index 000000000000..2626e6d9f707 --- /dev/null +++ b/include/dt-bindings/input/ti-drv260x.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * DRV260X haptics driver family | ||
| 3 | * | ||
| 4 | * Author: Dan Murphy <dmurphy@ti.com> | ||
| 5 | * | ||
| 6 | * Copyright: (C) 2014 Texas Instruments, Inc. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef _DT_BINDINGS_TI_DRV260X_H | ||
| 19 | #define _DT_BINDINGS_TI_DRV260X_H | ||
| 20 | |||
| 21 | /* Calibration Types */ | ||
| 22 | #define DRV260X_LRA_MODE 0x00 | ||
| 23 | #define DRV260X_LRA_NO_CAL_MODE 0x01 | ||
| 24 | #define DRV260X_ERM_MODE 0x02 | ||
| 25 | |||
| 26 | /* Library Selection */ | ||
| 27 | #define DRV260X_LIB_EMPTY 0x00 | ||
| 28 | #define DRV260X_ERM_LIB_A 0x01 | ||
| 29 | #define DRV260X_ERM_LIB_B 0x02 | ||
| 30 | #define DRV260X_ERM_LIB_C 0x03 | ||
| 31 | #define DRV260X_ERM_LIB_D 0x04 | ||
| 32 | #define DRV260X_ERM_LIB_E 0x05 | ||
| 33 | #define DRV260X_LIB_LRA 0x06 | ||
| 34 | #define DRV260X_ERM_LIB_F 0x07 | ||
| 35 | |||
| 36 | #endif | ||
diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index c466ff3e16b8..d0e578fd7053 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h | |||
| @@ -251,6 +251,15 @@ enum max77693_haptic_reg { | |||
| 251 | MAX77693_HAPTIC_REG_END, | 251 | MAX77693_HAPTIC_REG_END, |
| 252 | }; | 252 | }; |
| 253 | 253 | ||
| 254 | /* max77693-pmic LSCNFG configuraton register */ | ||
| 255 | #define MAX77693_PMIC_LOW_SYS_MASK 0x80 | ||
| 256 | #define MAX77693_PMIC_LOW_SYS_SHIFT 7 | ||
| 257 | |||
| 258 | /* max77693-haptic configuration register */ | ||
| 259 | #define MAX77693_CONFIG2_MODE 7 | ||
| 260 | #define MAX77693_CONFIG2_MEN 6 | ||
| 261 | #define MAX77693_CONFIG2_HTYP 5 | ||
| 262 | |||
| 254 | enum max77693_irq_source { | 263 | enum max77693_irq_source { |
| 255 | LED_INT = 0, | 264 | LED_INT = 0, |
| 256 | TOPSYS_INT, | 265 | TOPSYS_INT, |
diff --git a/include/linux/platform_data/drv260x-pdata.h b/include/linux/platform_data/drv260x-pdata.h new file mode 100644 index 000000000000..0a03b0944411 --- /dev/null +++ b/include/linux/platform_data/drv260x-pdata.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* | ||
| 2 | * Platform data for DRV260X haptics driver family | ||
| 3 | * | ||
| 4 | * Author: Dan Murphy <dmurphy@ti.com> | ||
| 5 | * | ||
| 6 | * Copyright: (C) 2014 Texas Instruments, Inc. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef _LINUX_DRV260X_PDATA_H | ||
| 19 | #define _LINUX_DRV260X_PDATA_H | ||
| 20 | |||
| 21 | struct drv260x_platform_data { | ||
| 22 | u32 library_selection; | ||
| 23 | u32 mode; | ||
| 24 | u32 vib_rated_voltage; | ||
| 25 | u32 vib_overdrive_voltage; | ||
| 26 | }; | ||
| 27 | |||
| 28 | #endif | ||
