diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-07-06 13:51:43 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-07-06 13:51:43 -0700 |
commit | ede2e7cdc58e1847cc73bd26b88a634631a43112 (patch) | |
tree | f4582f9dc0f4ec8c06975017e036479805d77a4b /drivers/input | |
parent | 9768935264c4f0e4afd788a185d8e8d89c28e41d (diff) | |
parent | 65938133784a3092c61b00aa63d1830fb465c1ac (diff) | |
download | linux-ede2e7cdc58e1847cc73bd26b88a634631a43112.tar.bz2 |
Merge branch 'next' into for-linus
Prepare input updates for 4.13 merge window.
Diffstat (limited to 'drivers/input')
23 files changed, 1273 insertions, 67 deletions
diff --git a/drivers/input/input.c b/drivers/input/input.c index 067d648028a2..7e6842bd525c 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -481,7 +481,7 @@ EXPORT_SYMBOL(input_inject_event); void input_alloc_absinfo(struct input_dev *dev) { if (!dev->absinfo) - dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo), + dev->absinfo = kcalloc(ABS_CNT, sizeof(*dev->absinfo), GFP_KERNEL); WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__); @@ -1126,7 +1126,7 @@ static void input_seq_print_bitmap(struct seq_file *seq, const char *name, * If no output was produced print a single 0. */ if (skip_empty) - seq_puts(seq, "0"); + seq_putc(seq, '0'); seq_putc(seq, '\n'); } @@ -1144,7 +1144,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : ""); seq_printf(seq, "S: Sysfs=%s\n", path ? path : ""); seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : ""); - seq_printf(seq, "H: Handlers="); + seq_puts(seq, "H: Handlers="); list_for_each_entry(handle, &dev->h_list, d_node) seq_printf(seq, "%s ", handle->name); @@ -1783,7 +1783,7 @@ struct input_dev *input_allocate_device(void) static atomic_t input_no = ATOMIC_INIT(-1); struct input_dev *dev; - dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev) { dev->dev.type = &input_dev_type; dev->dev.class = &input_class; @@ -1849,7 +1849,7 @@ struct input_dev *devm_input_allocate_device(struct device *dev) struct input_devres *devres; devres = devres_alloc(devm_input_device_release, - sizeof(struct input_devres), GFP_KERNEL); + sizeof(*devres), GFP_KERNEL); if (!devres) return NULL; @@ -2099,7 +2099,7 @@ int input_register_device(struct input_dev *dev) if (dev->devres_managed) { devres = devres_alloc(devm_input_device_unregister, - sizeof(struct input_devres), GFP_KERNEL); + sizeof(*devres), GFP_KERNEL); if (!devres) return -ENOMEM; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index def96cd2479b..298a6ba51411 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -126,13 +126,18 @@ static const struct xpad_device { u8 mapping; u8 xtype; } xpad_device[] = { + { 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX }, + { 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, + { 0x044f, 0x0f10, "Thrustmaster Modena GT Wheel", 0, XTYPE_XBOX }, { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 }, { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX }, { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX }, { 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX }, + { 0x045e, 0x0288, "Microsoft Xbox Controller S v2", 0, XTYPE_XBOX }, { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 }, + { 0x045e, 0x028f, "Microsoft X-Box 360 pad v2", 0, XTYPE_XBOX360 }, { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE }, @@ -145,28 +150,52 @@ static const struct xpad_device { { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", 0, XTYPE_XBOX }, + { 0x046d, 0xca8a, "Logitech Precision Vibration Feedback Wheel", 0, XTYPE_XBOX }, + { 0x046d, 0xcaa3, "Logitech DriveFx Racing Wheel", 0, XTYPE_XBOX360 }, { 0x056e, 0x2004, "Elecom JC-U3613M", 0, XTYPE_XBOX360 }, { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", 0, XTYPE_XBOX }, { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", 0, XTYPE_XBOX }, + { 0x05fe, 0x3030, "Chic Controller", 0, XTYPE_XBOX }, + { 0x05fe, 0x3031, "Chic Controller", 0, XTYPE_XBOX }, + { 0x062a, 0x0020, "Logic3 Xbox GamePad", 0, XTYPE_XBOX }, + { 0x062a, 0x0033, "Competition Pro Steering Wheel", 0, XTYPE_XBOX }, + { 0x06a3, 0x0200, "Saitek Racing Wheel", 0, XTYPE_XBOX }, + { 0x06a3, 0x0201, "Saitek Adrenalin", 0, XTYPE_XBOX }, + { 0x06a3, 0xf51a, "Saitek P3600", 0, XTYPE_XBOX360 }, + { 0x0738, 0x4506, "Mad Catz 4506 Wireless Controller", 0, XTYPE_XBOX }, { 0x0738, 0x4516, "Mad Catz Control Pad", 0, XTYPE_XBOX }, + { 0x0738, 0x4520, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX }, { 0x0738, 0x4522, "Mad Catz LumiCON", 0, XTYPE_XBOX }, { 0x0738, 0x4526, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX }, + { 0x0738, 0x4530, "Mad Catz Universal MC2 Racing Wheel and Pedals", 0, XTYPE_XBOX }, { 0x0738, 0x4536, "Mad Catz MicroCON", 0, XTYPE_XBOX }, { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX }, + { 0x0738, 0x4586, "Mad Catz MicroCon Wireless Controller", 0, XTYPE_XBOX }, + { 0x0738, 0x4588, "Mad Catz Blaster", 0, XTYPE_XBOX }, + { 0x0738, 0x45ff, "Mad Catz Beat Pad (w/ Handle)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 }, { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0738, 0x4736, "Mad Catz MicroCon Gamepad", 0, XTYPE_XBOX360 }, { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 }, + { 0x0738, 0x4743, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0738, 0x4758, "Mad Catz Arcade Game Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0x4a01, "Mad Catz FightStick TE 2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0738, 0x9871, "Mad Catz Portable Drum", 0, XTYPE_XBOX360 }, { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 }, + { 0x0738, 0xb738, "Mad Catz MVC2TE Stick 2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 }, { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, + { 0x0738, 0xcb29, "Saitek Aviator Stick AV8R02", 0, XTYPE_XBOX360 }, { 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 }, + { 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 }, + { 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX }, + { 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX }, @@ -174,37 +203,63 @@ static const struct xpad_device { { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", 0, XTYPE_XBOX }, { 0x0d2f, 0x0002, "Andamiro Pump It Up pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0e4c, 0x1097, "Radica Gamester Controller", 0, XTYPE_XBOX }, + { 0x0e4c, 0x1103, "Radica Gamester Reflex", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX }, { 0x0e4c, 0x2390, "Radica Games Jtech Controller", 0, XTYPE_XBOX }, + { 0x0e4c, 0x3510, "Radica Gamester", 0, XTYPE_XBOX }, { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX }, + { 0x0e6f, 0x0008, "After Glow Pro Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x011f, "Rock Candy Gamepad Wired Controller", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0131, "PDP EA Sports Controller", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0133, "Xbox 360 Wired Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x013a, "PDP Xbox One Controller", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0146, "Rock Candy Wired Controller for Xbox One", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x0147, "PDP Marvel Xbox One Controller", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x015c, "PDP Xbox One Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, + { 0x0e6f, 0x0161, "PDP Xbox One Controller", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x0162, "PDP Xbox One Controller", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x0163, "PDP Xbox One Controller", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x0164, "PDP Battlefield One", 0, XTYPE_XBOXONE }, + { 0x0e6f, 0x0165, "PDP Titanfall 2", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016", 0, XTYPE_XBOXONE }, { 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0413, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0501, "PDP Xbox 360 Controller", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0xf900, "PDP Afterglow AX.1", 0, XTYPE_XBOX360 }, { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX }, { 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX }, { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 }, + { 0x0f0d, 0x000c, "Hori PadEX Turbo", 0, XTYPE_XBOX360 }, { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0f0d, 0x001b, "Hori Real Arcade Pro VX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE }, + { 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, + { 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX }, { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX }, + { 0x11c9, 0x55f0, "Nacon GC-100XF", 0, XTYPE_XBOX360 }, { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 }, + { 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 }, { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, + { 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE }, { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, @@ -215,22 +270,44 @@ static const struct xpad_device { { 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0x0130, "Ion Drum Rocker", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf018, "Mad Catz Street Fighter IV SE Fighting Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xf019, "Mad Catz Brawlstick for Xbox 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xf021, "Mad Cats Ghost Recon FS GamePad", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf025, "Mad Catz Call Of Duty", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf027, "Mad Catz FPS Pro", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf02e, "Mad Catz Fightpad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf030, "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf036, "Mad Catz MicroCon GamePad Pro", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf039, "Mad Catz MvC2 TE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xf03a, "Mad Catz SFxT Fightstick Pro", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf03d, "Street Fighter IV Arcade Stick TE - Chun Li", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf03e, "Mad Catz MLG FightStick TE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf03f, "Mad Catz FightStick SoulCaliber", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf042, "Mad Catz FightStick TES+", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf080, "Mad Catz FightStick TE2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf501, "HoriPad EX2 Turbo", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf502, "Hori Real Arcade Pro.VX SA", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf503, "Hori Fighting Stick VX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf504, "Hori Real Arcade Pro. EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf505, "Hori Fighting Stick EX2B", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x1bad, 0xf506, "Hori Real Arcade Pro.EX Premium VLX", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf904, "PDP Versus Fighting Pad", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf906, "MortalKombat FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xfa01, "MadCatz GamePad", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x530a, "Xbox 360 Pro EX Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x531a, "PowerA Pro Ex", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5397, "FUS1ON Tournament Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x541a, "PowerA Xbox One Mini Wired Controller", 0, XTYPE_XBOXONE }, @@ -238,12 +315,19 @@ static const struct xpad_device { { 0x24c6, 0x543a, "PowerA Xbox One wired controller", 0, XTYPE_XBOXONE }, { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 }, { 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE }, + { 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE }, + { 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 }, + { 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, + { 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } }; @@ -331,13 +415,16 @@ static struct usb_device_id xpad_table[] = { XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */ XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ XPAD_XBOX360_VENDOR(0x056e), /* Elecom JC-U3613M */ + XPAD_XBOX360_VENDOR(0x06a3), /* Saitek P3600 */ XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */ + XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */ XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */ + XPAD_XBOX360_VENDOR(0x11c9), /* Nacon GC100XF */ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */ diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 97acd6524ad7..4c4ab1ced235 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -178,6 +178,17 @@ config KEYBOARD_CLPS711X To compile this driver as a module, choose M here: the module will be called clps711x-keypad. +config KEYBOARD_DLINK_DIR685 + tristate "D-Link DIR-685 touchkeys support" + depends on I2C + default ARCH_GEMINI + help + If you say yes here you get support for the D-Link DIR-685 + touchkeys. + + To compile this driver as a module, choose M here: the + module will be called dlink-dir685-touchkeys. + config KEYBOARD_LKKBD tristate "DECstation/VAXstation LK201/LK401 keyboard" select SERIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 7d9acff819a7..d2338bacdad1 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o +obj-$(CONFIG_KEYBOARD_DLINK_DIR685) += dlink-dir685-touchkeys.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o diff --git a/drivers/input/keyboard/dlink-dir685-touchkeys.c b/drivers/input/keyboard/dlink-dir685-touchkeys.c new file mode 100644 index 000000000000..88e321b76397 --- /dev/null +++ b/drivers/input/keyboard/dlink-dir685-touchkeys.c @@ -0,0 +1,155 @@ +/* + * D-Link DIR-685 router I2C-based Touchkeys input driver + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * + * This is a one-off touchkey controller based on the Cypress Semiconductor + * CY8C214 MCU with some firmware in its internal 8KB flash. The circuit + * board inside the router is named E119921 + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/bitops.h> + +struct dir685_touchkeys { + struct device *dev; + struct i2c_client *client; + struct input_dev *input; + unsigned long cur_key; + u16 codes[7]; +}; + +static irqreturn_t dir685_tk_irq_thread(int irq, void *data) +{ + struct dir685_touchkeys *tk = data; + const int num_bits = min_t(int, ARRAY_SIZE(tk->codes), 16); + unsigned long changed; + u8 buf[6]; + unsigned long key; + int i; + int err; + + memset(buf, 0, sizeof(buf)); + err = i2c_master_recv(tk->client, buf, sizeof(buf)); + if (err != sizeof(buf)) { + dev_err(tk->dev, "short read %d\n", err); + return IRQ_HANDLED; + } + + dev_dbg(tk->dev, "IN: %*ph\n", (int)sizeof(buf), buf); + key = be16_to_cpup((__be16 *) &buf[4]); + + /* Figure out if any bits went high or low since last message */ + changed = tk->cur_key ^ key; + for_each_set_bit(i, &changed, num_bits) { + dev_dbg(tk->dev, "key %d is %s\n", i, + test_bit(i, &key) ? "down" : "up"); + input_report_key(tk->input, tk->codes[i], test_bit(i, &key)); + } + + /* Store currently down keys */ + tk->cur_key = key; + input_sync(tk->input); + + return IRQ_HANDLED; +} + +static int dir685_tk_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct dir685_touchkeys *tk; + struct device *dev = &client->dev; + u8 bl_data[] = { 0xa7, 0x40 }; + int err; + int i; + + tk = devm_kzalloc(&client->dev, sizeof(*tk), GFP_KERNEL); + if (!tk) + return -ENOMEM; + + tk->input = devm_input_allocate_device(dev); + if (!tk->input) + return -ENOMEM; + + tk->client = client; + tk->dev = dev; + + tk->input->keycodesize = sizeof(u16); + tk->input->keycodemax = ARRAY_SIZE(tk->codes); + tk->input->keycode = tk->codes; + tk->codes[0] = KEY_UP; + tk->codes[1] = KEY_DOWN; + tk->codes[2] = KEY_LEFT; + tk->codes[3] = KEY_RIGHT; + tk->codes[4] = KEY_ENTER; + tk->codes[5] = KEY_WPS_BUTTON; + /* + * This key appears in the vendor driver, but I have + * not been able to activate it. + */ + tk->codes[6] = KEY_RESERVED; + + __set_bit(EV_KEY, tk->input->evbit); + for (i = 0; i < ARRAY_SIZE(tk->codes); i++) + __set_bit(tk->codes[i], tk->input->keybit); + __clear_bit(KEY_RESERVED, tk->input->keybit); + + tk->input->name = "D-Link DIR-685 touchkeys"; + tk->input->id.bustype = BUS_I2C; + + err = input_register_device(tk->input); + if (err) + return err; + + /* Set the brightness to max level */ + err = i2c_master_send(client, bl_data, sizeof(bl_data)); + if (err != sizeof(bl_data)) + dev_warn(tk->dev, "error setting brightness level\n"); + + if (!client->irq) { + dev_err(dev, "no IRQ on the I2C device\n"); + return -ENODEV; + } + err = devm_request_threaded_irq(dev, client->irq, + NULL, dir685_tk_irq_thread, + IRQF_ONESHOT, + "dir685-tk", tk); + if (err) { + dev_err(dev, "can't request IRQ\n"); + return err; + } + + return 0; +} + +static const struct i2c_device_id dir685_tk_id[] = { + { "dir685tk", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dir685_tk_id); + +#ifdef CONFIG_OF +static const struct of_device_id dir685_tk_of_match[] = { + { .compatible = "dlink,dir685-touchkeys" }, + {}, +}; +MODULE_DEVICE_TABLE(of, dir685_tk_of_match); +#endif + +static struct i2c_driver dir685_tk_i2c_driver = { + .driver = { + .name = "dlin-dir685-touchkeys", + .of_match_table = of_match_ptr(dir685_tk_of_match), + }, + .probe = dir685_tk_probe, + .id_table = dir685_tk_id, +}; +module_i2c_driver(dir685_tk_i2c_driver); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("D-Link DIR-685 touchkeys driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 21bea52d4365..04a5d7e134d7 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -30,8 +30,8 @@ #include <linux/delay.h> #include <linux/input.h> #include <linux/leds.h> +#include <linux/platform_data/lm8323.h> #include <linux/pm.h> -#include <linux/i2c/lm8323.h> #include <linux/slab.h> /* Commands to send to the chip. */ diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index 31090d71a685..be56d4f262a7 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -13,11 +13,11 @@ #include <linux/module.h> #include <linux/i2c.h> -#include <linux/i2c/mcs.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/platform_data/mcs.h> #include <linux/pm.h> /* MCS5000 Touchkey */ diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index 400869e61a06..38c79ebff033 100644 --- a/drivers/input/misc/axp20x-pek.c +++ b/drivers/input/misc/axp20x-pek.c @@ -253,6 +253,9 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, return error; } + if (axp20x_pek->axp20x->variant == AXP288_ID) + enable_irq_wake(axp20x_pek->irq_dbr); + return 0; } @@ -331,10 +334,35 @@ static int axp20x_pek_probe(struct platform_device *pdev) return 0; } +static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev) +{ + struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); + + if (axp20x_pek->axp20x->variant != AXP288_ID) + return 0; + + /* + * Clear interrupts from button presses during suspend, to avoid + * a wakeup power-button press getting reported to userspace. + */ + regmap_write(axp20x_pek->axp20x->regmap, + AXP20X_IRQ1_STATE + AXP288_IRQ_POKN / 8, + BIT(AXP288_IRQ_POKN % 8)); + + return 0; +} + +static const struct dev_pm_ops axp20x_pek_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .resume_noirq = axp20x_pek_resume_noirq, +#endif +}; + static struct platform_driver axp20x_pek_driver = { .probe = axp20x_pek_probe, .driver = { .name = "axp20x-pek", + .pm = &axp20x_pek_pm_ops, }, }; module_platform_driver(axp20x_pek_driver); diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index 690148f9940e..eb770613a9bd 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -135,14 +135,17 @@ static int xenkbd_probe(struct xenbus_device *dev, goto error_nomem; /* Set input abs params to match backend screen res */ - abs = xenbus_read_unsigned(dev->otherend, "feature-abs-pointer", 0); - ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend, "width", + abs = xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_FEAT_ABS_POINTER, 0); + ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_WIDTH, ptr_size[KPARAM_X]); - ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend, "height", + ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend, + XENKBD_FIELD_HEIGHT, ptr_size[KPARAM_Y]); if (abs) { ret = xenbus_write(XBT_NIL, dev->nodename, - "request-abs-pointer", "1"); + XENKBD_FIELD_REQ_ABS_POINTER, "1"); if (ret) { pr_warn("xenkbd: can't request abs-pointer\n"); abs = 0; @@ -271,14 +274,15 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, xenbus_dev_fatal(dev, ret, "starting transaction"); goto error_irqh; } - ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", + ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_REF, "%lu", virt_to_gfn(info->page)); if (ret) goto error_xenbus; - ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref); + ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_GREF, + "%u", info->gref); if (ret) goto error_xenbus; - ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_EVT_CHANNEL, "%u", evtchn); if (ret) goto error_xenbus; @@ -353,7 +357,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, } static const struct xenbus_device_id xenkbd_ids[] = { - { "vkbd" }, + { XENKBD_DRIVER_NAME }, { "" } }; @@ -390,4 +394,4 @@ module_exit(xenkbd_cleanup); MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("xen:vkbd"); +MODULE_ALIAS("xen:" XENKBD_DRIVER_NAME); diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index c0ec26118732..61c202436250 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -58,7 +58,7 @@ struct elan_transport_ops { int (*get_version)(struct i2c_client *client, bool iap, u8 *version); int (*get_sm_version)(struct i2c_client *client, - u8* ic_type, u8 *version); + u16 *ic_type, u8 *version); int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum); int (*get_product_id)(struct i2c_client *client, u16 *id); @@ -82,6 +82,7 @@ struct elan_transport_ops { int (*get_report)(struct i2c_client *client, u8 *report); int (*get_pressure_adjustment)(struct i2c_client *client, int *adjustment); + int (*get_pattern)(struct i2c_client *client, u8 *pattern); }; extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index d5ab9ddef3e3..3b616cb7c67f 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -5,7 +5,7 @@ * * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw> * Author: KT Liao <kt.liao@emc.com.tw> - * Version: 1.6.2 + * Version: 1.6.3 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -41,7 +41,7 @@ #include "elan_i2c.h" #define DRIVER_NAME "elan_i2c" -#define ELAN_DRIVER_VERSION "1.6.2" +#define ELAN_DRIVER_VERSION "1.6.3" #define ELAN_VENDOR_ID 0x04f3 #define ETP_MAX_PRESSURE 255 #define ETP_FWIDTH_REDUCE 90 @@ -78,6 +78,7 @@ struct elan_tp_data { unsigned int x_res; unsigned int y_res; + u8 pattern; u16 product_id; u8 fw_version; u8 sm_version; @@ -85,7 +86,7 @@ struct elan_tp_data { u16 fw_checksum; int pressure_adjustment; u8 mode; - u8 ic_type; + u16 ic_type; u16 fw_validpage_count; u16 fw_signature_address; @@ -96,10 +97,10 @@ struct elan_tp_data { bool baseline_ready; }; -static int elan_get_fwinfo(u8 iap_version, u16 *validpage_count, +static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count, u16 *signature_address) { - switch (iap_version) { + switch (ic_type) { case 0x00: case 0x06: case 0x08: @@ -119,6 +120,9 @@ static int elan_get_fwinfo(u8 iap_version, u16 *validpage_count, case 0x0E: *validpage_count = 640; break; + case 0x10: + *validpage_count = 1024; + break; default: /* unknown ic type clear value */ *validpage_count = 0; @@ -305,6 +309,7 @@ static int elan_initialize(struct elan_tp_data *data) static int elan_query_device_info(struct elan_tp_data *data) { int error; + u16 ic_type; error = data->ops->get_version(data->client, false, &data->fw_version); if (error) @@ -324,7 +329,16 @@ static int elan_query_device_info(struct elan_tp_data *data) if (error) return error; - error = elan_get_fwinfo(data->iap_version, &data->fw_validpage_count, + error = data->ops->get_pattern(data->client, &data->pattern); + if (error) + return error; + + if (data->pattern == 0x01) + ic_type = data->ic_type; + else + ic_type = data->iap_version; + + error = elan_get_fwinfo(ic_type, &data->fw_validpage_count, &data->fw_signature_address); if (error) dev_warn(&data->client->dev, @@ -1077,6 +1091,13 @@ static int elan_probe(struct i2c_client *client, return error; } + /* Make sure there is something at this address */ + error = i2c_smbus_read_byte(client); + if (error < 0) { + dev_dbg(&client->dev, "nothing at this address: %d\n", error); + return -ENXIO; + } + /* Initialize the touchpad. */ error = elan_initialize(data); if (error) @@ -1101,10 +1122,13 @@ static int elan_probe(struct i2c_client *client, "Elan Touchpad Extra Information:\n" " Max ABS X,Y: %d,%d\n" " Width X,Y: %d,%d\n" - " Resolution X,Y: %d,%d (dots/mm)\n", + " Resolution X,Y: %d,%d (dots/mm)\n" + " ic type: 0x%x\n" + " info pattern: 0x%x\n", data->max_x, data->max_y, data->width_x, data->width_y, - data->x_res, data->y_res); + data->x_res, data->y_res, + data->ic_type, data->pattern); /* Set up input device properties based on queried parameters. */ error = elan_setup_input_device(data); diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index f431da07f861..80172f25974d 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -34,9 +34,12 @@ #define ETP_I2C_DESC_CMD 0x0001 #define ETP_I2C_REPORT_DESC_CMD 0x0002 #define ETP_I2C_STAND_CMD 0x0005 +#define ETP_I2C_PATTERN_CMD 0x0100 #define ETP_I2C_UNIQUEID_CMD 0x0101 #define ETP_I2C_FW_VERSION_CMD 0x0102 -#define ETP_I2C_SM_VERSION_CMD 0x0103 +#define ETP_I2C_IC_TYPE_CMD 0x0103 +#define ETP_I2C_OSM_VERSION_CMD 0x0103 +#define ETP_I2C_NSM_VERSION_CMD 0x0104 #define ETP_I2C_XY_TRACENUM_CMD 0x0105 #define ETP_I2C_MAX_X_AXIS_CMD 0x0106 #define ETP_I2C_MAX_Y_AXIS_CMD 0x0107 @@ -239,12 +242,34 @@ static int elan_i2c_get_baseline_data(struct i2c_client *client, return 0; } +static int elan_i2c_get_pattern(struct i2c_client *client, u8 *pattern) +{ + int error; + u8 val[3]; + + error = elan_i2c_read_cmd(client, ETP_I2C_PATTERN_CMD, val); + if (error) { + dev_err(&client->dev, "failed to get pattern: %d\n", error); + return error; + } + *pattern = val[1]; + + return 0; +} + static int elan_i2c_get_version(struct i2c_client *client, bool iap, u8 *version) { int error; + u8 pattern_ver; u8 val[3]; + error = elan_i2c_get_pattern(client, &pattern_ver); + if (error) { + dev_err(&client->dev, "failed to get pattern version\n"); + return error; + } + error = elan_i2c_read_cmd(client, iap ? ETP_I2C_IAP_VERSION_CMD : ETP_I2C_FW_VERSION_CMD, @@ -255,24 +280,54 @@ static int elan_i2c_get_version(struct i2c_client *client, return error; } - *version = val[0]; + if (pattern_ver == 0x01) + *version = iap ? val[1] : val[0]; + else + *version = val[0]; return 0; } static int elan_i2c_get_sm_version(struct i2c_client *client, - u8 *ic_type, u8 *version) + u16 *ic_type, u8 *version) { int error; + u8 pattern_ver; u8 val[3]; - error = elan_i2c_read_cmd(client, ETP_I2C_SM_VERSION_CMD, val); + error = elan_i2c_get_pattern(client, &pattern_ver); if (error) { - dev_err(&client->dev, "failed to get SM version: %d\n", error); + dev_err(&client->dev, "failed to get pattern version\n"); return error; } - *version = val[0]; - *ic_type = val[1]; + if (pattern_ver == 0x01) { + error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_CMD, val); + if (error) { + dev_err(&client->dev, "failed to get ic type: %d\n", + error); + return error; + } + *ic_type = be16_to_cpup((__be16 *)val); + + error = elan_i2c_read_cmd(client, ETP_I2C_NSM_VERSION_CMD, + val); + if (error) { + dev_err(&client->dev, "failed to get SM version: %d\n", + error); + return error; + } + *version = val[1]; + } else { + error = elan_i2c_read_cmd(client, ETP_I2C_OSM_VERSION_CMD, val); + if (error) { + dev_err(&client->dev, "failed to get SM version: %d\n", + error); + return error; + } + *version = val[0]; + *ic_type = val[1]; + } + return 0; } @@ -641,5 +696,7 @@ const struct elan_transport_ops elan_i2c_ops = { .write_fw_block = elan_i2c_write_fw_block, .finish_fw_update = elan_i2c_finish_fw_update, + .get_pattern = elan_i2c_get_pattern, + .get_report = elan_i2c_get_report, }; diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index e23b2495d52e..df7a57ca7331 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -166,7 +166,7 @@ static int elan_smbus_get_version(struct i2c_client *client, } static int elan_smbus_get_sm_version(struct i2c_client *client, - u8 *ic_type, u8 *version) + u16 *ic_type, u8 *version) { int error; u8 val[3]; @@ -495,6 +495,12 @@ static int elan_smbus_finish_fw_update(struct i2c_client *client, return 0; } +static int elan_smbus_get_pattern(struct i2c_client *client, u8 *pattern) +{ + *pattern = 0; + return 0; +} + const struct elan_transport_ops elan_smbus_ops = { .initialize = elan_smbus_initialize, .sleep_control = elan_smbus_sleep_control, @@ -524,4 +530,5 @@ const struct elan_transport_ops elan_smbus_ops = { .finish_fw_update = elan_smbus_finish_fw_update, .get_report = elan_smbus_get_report, + .get_pattern = elan_smbus_get_pattern, }; diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index f1fa1f172107..791993215ea3 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1711,6 +1711,17 @@ int elantech_init(struct psmouse *psmouse) etd->samples[0], etd->samples[1], etd->samples[2]); } + if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) { + /* + * This module has a bug which makes absolute mode + * unusable, so let's abort so we'll be using standard + * PS/2 protocol. + */ + psmouse_info(psmouse, + "absolute mode broken, forcing standard PS/2 protocol\n"); + goto init_fail; + } + if (elantech_set_absolute_mode(psmouse)) { psmouse_err(psmouse, "failed to put touchpad into absolute mode.\n"); diff --git a/drivers/input/rmi4/rmi_f34v7.c b/drivers/input/rmi4/rmi_f34v7.c index 10c0d11b72c9..3991d2943660 100644 --- a/drivers/input/rmi4/rmi_f34v7.c +++ b/drivers/input/rmi4/rmi_f34v7.c @@ -9,13 +9,14 @@ * the Free Software Foundation. */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/rmi.h> #include <linux/firmware.h> -#include <asm/unaligned.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/jiffies.h> +#include <asm/unaligned.h> #include "rmi_driver.h" #include "rmi_f34.h" @@ -464,7 +465,7 @@ static int rmi_f34v7_read_queries_bl_version(struct f34_data *f34) static int rmi_f34v7_read_queries(struct f34_data *f34) { int ret; - int i, j; + int i; u8 base; int offset; u8 *ptable; @@ -518,10 +519,7 @@ static int rmi_f34v7_read_queries(struct f34_data *f34) query_1_7.partition_support[1] & HAS_GUEST_CODE; if (query_0 & HAS_CONFIG_ID) { - char f34_ctrl[CONFIG_ID_SIZE]; - int i = 0; - u8 *p = f34->configuration_id; - *p = '\0'; + u8 f34_ctrl[CONFIG_ID_SIZE]; ret = rmi_read_block(f34->fn->rmi_dev, f34->fn->fd.control_base_addr, @@ -531,13 +529,11 @@ static int rmi_f34v7_read_queries(struct f34_data *f34) return ret; /* Eat leading zeros */ - while (i < sizeof(f34_ctrl) && !f34_ctrl[i]) - i++; + for (i = 0; i < sizeof(f34_ctrl) - 1 && !f34_ctrl[i]; i++) + /* Empty */; - for (; i < sizeof(f34_ctrl); i++) - p += snprintf(p, f34->configuration_id - + sizeof(f34->configuration_id) - p, - "%02X", f34_ctrl[i]); + snprintf(f34->configuration_id, sizeof(f34->configuration_id), + "%*phN", (int)sizeof(f34_ctrl) - i, f34_ctrl + i); rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Configuration ID: %s\n", f34->configuration_id); @@ -545,9 +541,7 @@ static int rmi_f34v7_read_queries(struct f34_data *f34) f34->v7.partitions = 0; for (i = 0; i < sizeof(query_1_7.partition_support); i++) - for (j = 0; j < 8; j++) - if (query_1_7.partition_support[i] & (1 << j)) - f34->v7.partitions++; + f34->v7.partitions += hweight8(query_1_7.partition_support[i]); rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: Supported partitions: %*ph\n", __func__, sizeof(query_1_7.partition_support), diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index 12a3ad83296d..bb0349fa64bc 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -224,20 +224,6 @@ int sparse_keymap_setup(struct input_dev *dev, EXPORT_SYMBOL(sparse_keymap_setup); /** - * sparse_keymap_free - free memory allocated for sparse keymap - * @dev: Input device using sparse keymap - * - * This function used to free memory allocated by sparse keymap - * in an input device that was set up by sparse_keymap_setup(). - * Since sparse_keymap_setup() now uses a managed allocation for the - * keymap copy, use of this function is deprecated. - */ -void sparse_keymap_free(struct input_dev *dev) -{ -} -EXPORT_SYMBOL(sparse_keymap_free); - -/** * sparse_keymap_report_entry - report event corresponding to given key entry * @dev: Input device for which event should be reported * @ke: key entry describing event diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index cf26ca49ae6d..64b30fe273fd 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1114,6 +1114,17 @@ config TOUCHSCREEN_ST1232 To compile this driver as a module, choose M here: the module will be called st1232_ts. +config TOUCHSCREEN_STMFTS + tristate "STMicroelectronics STMFTS touchscreen" + depends on I2C + depends on LEDS_CLASS + help + Say Y here if you want support for STMicroelectronics + STMFTS touchscreen. + + To compile this driver as a module, choose M here: the + module will be called stmfts. + config TOUCHSCREEN_STMPE tristate "STMicroelectronics STMPE touchscreens" depends on MFD_STMPE diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 18e476948e44..6badce87037b 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_SILEAD) += silead.o obj-$(CONFIG_TOUCHSCREEN_SIS_I2C) += sis_i2c.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o +obj-$(CONFIG_TOUCHSCREEN_STMFTS) += stmfts.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 90fc07dc98a6..8868573133ab 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -15,10 +15,10 @@ #include <linux/module.h> #include <linux/i2c.h> -#include <linux/i2c/mcs.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/irq.h> +#include <linux/platform_data/mcs.h> #include <linux/slab.h> /* Registers */ diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 1fafc9f57af6..e5eeb6311f7d 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -11,9 +11,9 @@ #include <linux/delay.h> #include <linux/of.h> #include <linux/i2c.h> -#include <linux/i2c/mms114.h> #include <linux/input/mt.h> #include <linux/interrupt.h> +#include <linux/platform_data/mms114.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 41d58e88cc8a..3b3db8c868e0 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -264,7 +264,11 @@ static int s3c2410ts_probe(struct platform_device *pdev) return -ENOENT; } - clk_prepare_enable(ts.clock); + ret = clk_prepare_enable(ts.clock); + if (ret) { + dev_err(dev, "Failed! to enabled clocks\n"); + goto err_clk_get; + } dev_dbg(dev, "got and enabled clocks\n"); ts.irq_tc = ret = platform_get_irq(pdev, 0); @@ -353,7 +357,9 @@ static int s3c2410ts_probe(struct platform_device *pdev) err_iomap: iounmap(ts.io); err_clk: + clk_disable_unprepare(ts.clock); del_timer_sync(&touch_timer); + err_clk_get: clk_put(ts.clock); return ret; } diff --git a/drivers/input/touchscreen/stmfts.c b/drivers/input/touchscreen/stmfts.c new file mode 100644 index 000000000000..157fdb4bb2e8 --- /dev/null +++ b/drivers/input/touchscreen/stmfts.c @@ -0,0 +1,822 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Author: Andi Shyti <andi.shyti@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * STMicroelectronics FTS Touchscreen device driver + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> + +/* I2C commands */ +#define STMFTS_READ_INFO 0x80 +#define STMFTS_READ_STATUS 0x84 +#define STMFTS_READ_ONE_EVENT 0x85 +#define STMFTS_READ_ALL_EVENT 0x86 +#define STMFTS_LATEST_EVENT 0x87 +#define STMFTS_SLEEP_IN 0x90 +#define STMFTS_SLEEP_OUT 0x91 +#define STMFTS_MS_MT_SENSE_OFF 0x92 +#define STMFTS_MS_MT_SENSE_ON 0x93 +#define STMFTS_SS_HOVER_SENSE_OFF 0x94 +#define STMFTS_SS_HOVER_SENSE_ON 0x95 +#define STMFTS_MS_KEY_SENSE_OFF 0x9a +#define STMFTS_MS_KEY_SENSE_ON 0x9b +#define STMFTS_SYSTEM_RESET 0xa0 +#define STMFTS_CLEAR_EVENT_STACK 0xa1 +#define STMFTS_FULL_FORCE_CALIBRATION 0xa2 +#define STMFTS_MS_CX_TUNING 0xa3 +#define STMFTS_SS_CX_TUNING 0xa4 + +/* events */ +#define STMFTS_EV_NO_EVENT 0x00 +#define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02 +#define STMFTS_EV_MULTI_TOUCH_ENTER 0x03 +#define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04 +#define STMFTS_EV_MULTI_TOUCH_MOTION 0x05 +#define STMFTS_EV_HOVER_ENTER 0x07 +#define STMFTS_EV_HOVER_LEAVE 0x08 +#define STMFTS_EV_HOVER_MOTION 0x09 +#define STMFTS_EV_KEY_STATUS 0x0e +#define STMFTS_EV_ERROR 0x0f +#define STMFTS_EV_CONTROLLER_READY 0x10 +#define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11 +#define STMFTS_EV_STATUS 0x16 +#define STMFTS_EV_DEBUG 0xdb + +/* multi touch related event masks */ +#define STMFTS_MASK_EVENT_ID 0x0f +#define STMFTS_MASK_TOUCH_ID 0xf0 +#define STMFTS_MASK_LEFT_EVENT 0x0f +#define STMFTS_MASK_X_MSB 0x0f +#define STMFTS_MASK_Y_LSB 0xf0 + +/* key related event masks */ +#define STMFTS_MASK_KEY_NO_TOUCH 0x00 +#define STMFTS_MASK_KEY_MENU 0x01 +#define STMFTS_MASK_KEY_BACK 0x02 + +#define STMFTS_EVENT_SIZE 8 +#define STMFTS_STACK_DEPTH 32 +#define STMFTS_DATA_MAX_SIZE (STMFTS_EVENT_SIZE * STMFTS_STACK_DEPTH) +#define STMFTS_MAX_FINGERS 10 +#define STMFTS_DEV_NAME "stmfts" + +enum stmfts_regulators { + STMFTS_REGULATOR_VDD, + STMFTS_REGULATOR_AVDD, +}; + +struct stmfts_data { + struct i2c_client *client; + struct input_dev *input; + struct led_classdev led_cdev; + struct mutex mutex; + + struct touchscreen_properties prop; + + struct regulator_bulk_data regulators[2]; + + /* + * Presence of ledvdd will be used also to check + * whether the LED is supported. + */ + struct regulator *ledvdd; + + u16 chip_id; + u8 chip_ver; + u16 fw_ver; + u8 config_id; + u8 config_ver; + + u8 data[STMFTS_DATA_MAX_SIZE]; + + struct completion cmd_done; + + bool use_key; + bool led_status; + bool hover_enabled; + bool running; +}; + +static void stmfts_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct stmfts_data *sdata = container_of(led_cdev, + struct stmfts_data, led_cdev); + int err; + + if (value == sdata->led_status || !sdata->ledvdd) + return; + + if (!value) { + regulator_disable(sdata->ledvdd); + } else { + err = regulator_enable(sdata->ledvdd); + if (err) + dev_warn(&sdata->client->dev, + "failed to disable ledvdd regulator: %d\n", + err); + } + + sdata->led_status = value; +} + +static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev) +{ + struct stmfts_data *sdata = container_of(led_cdev, + struct stmfts_data, led_cdev); + + return !!regulator_is_enabled(sdata->ledvdd); +} + +/* + * We can't simply use i2c_smbus_read_i2c_block_data because we + * need to read more than 255 bytes ( + */ +static int stmfts_read_events(struct stmfts_data *sdata) +{ + u8 cmd = STMFTS_READ_ALL_EVENT; + struct i2c_msg msgs[2] = { + { + .addr = sdata->client->addr, + .len = 1, + .buf = &cmd, + }, + { + .addr = sdata->client->addr, + .flags = I2C_M_RD, + .len = STMFTS_DATA_MAX_SIZE, + .buf = sdata->data, + }, + }; + int ret; + + ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + return ret == ARRAY_SIZE(msgs) ? 0 : -EIO; +} + +static void stmfts_report_contact_event(struct stmfts_data *sdata, + const u8 event[]) +{ + u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; + u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8); + u16 y = (event[2] >> 4) | (event[3] << 4); + u8 maj = event[4]; + u8 min = event[5]; + u8 orientation = event[6]; + u8 area = event[7]; + + input_mt_slot(sdata->input, slot_id); + + input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true); + input_report_abs(sdata->input, ABS_MT_POSITION_X, x); + input_report_abs(sdata->input, ABS_MT_POSITION_Y, y); + input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj); + input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min); + input_report_abs(sdata->input, ABS_MT_PRESSURE, area); + input_report_abs(sdata->input, ABS_MT_ORIENTATION, orientation); + + input_sync(sdata->input); +} + +static void stmfts_report_contact_release(struct stmfts_data *sdata, + const u8 event[]) +{ + u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; + + input_mt_slot(sdata->input, slot_id); + input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, false); + + input_sync(sdata->input); +} + +static void stmfts_report_hover_event(struct stmfts_data *sdata, + const u8 event[]) +{ + u16 x = (event[2] << 4) | (event[4] >> 4); + u16 y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB); + u8 z = event[5]; + + input_report_abs(sdata->input, ABS_X, x); + input_report_abs(sdata->input, ABS_Y, y); + input_report_abs(sdata->input, ABS_DISTANCE, z); + + input_sync(sdata->input); +} + +static void stmfts_report_key_event(struct stmfts_data *sdata, const u8 event[]) +{ + switch (event[2]) { + case 0: + input_report_key(sdata->input, KEY_BACK, 0); + input_report_key(sdata->input, KEY_MENU, 0); + break; + + case STMFTS_MASK_KEY_BACK: + input_report_key(sdata->input, KEY_BACK, 1); + break; + + case STMFTS_MASK_KEY_MENU: + input_report_key(sdata->input, KEY_MENU, 1); + break; + + default: + dev_warn(&sdata->client->dev, + "unknown key event: %#02x\n", event[2]); + break; + } + + input_sync(sdata->input); +} + +static void stmfts_parse_events(struct stmfts_data *sdata) +{ + int i; + + for (i = 0; i < STMFTS_STACK_DEPTH; i++) { + u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE]; + + switch (event[0]) { + + case STMFTS_EV_CONTROLLER_READY: + case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY: + case STMFTS_EV_STATUS: + complete(&sdata->cmd_done); + /* fall through */ + + case STMFTS_EV_NO_EVENT: + case STMFTS_EV_DEBUG: + return; + } + + switch (event[0] & STMFTS_MASK_EVENT_ID) { + + case STMFTS_EV_MULTI_TOUCH_ENTER: + case STMFTS_EV_MULTI_TOUCH_MOTION: + stmfts_report_contact_event(sdata, event); + break; + + case STMFTS_EV_MULTI_TOUCH_LEAVE: + stmfts_report_contact_release(sdata, event); + break; + + case STMFTS_EV_HOVER_ENTER: + case STMFTS_EV_HOVER_LEAVE: + case STMFTS_EV_HOVER_MOTION: + stmfts_report_hover_event(sdata, event); + break; + + case STMFTS_EV_KEY_STATUS: + stmfts_report_key_event(sdata, event); + break; + + case STMFTS_EV_ERROR: + dev_warn(&sdata->client->dev, + "error code: 0x%x%x%x%x%x%x", + event[6], event[5], event[4], + event[3], event[2], event[1]); + break; + + default: + dev_err(&sdata->client->dev, + "unknown event %#02x\n", event[0]); + } + } +} + +static irqreturn_t stmfts_irq_handler(int irq, void *dev) +{ + struct stmfts_data *sdata = dev; + int err; + + mutex_lock(&sdata->mutex); + + err = stmfts_read_events(sdata); + if (unlikely(err)) + dev_err(&sdata->client->dev, + "failed to read events: %d\n", err); + else + stmfts_parse_events(sdata); + + mutex_unlock(&sdata->mutex); + return IRQ_HANDLED; +} + +static int stmfts_command(struct stmfts_data *sdata, const u8 cmd) +{ + int err; + + reinit_completion(&sdata->cmd_done); + + err = i2c_smbus_write_byte(sdata->client, cmd); + if (err) + return err; + + if (!wait_for_completion_timeout(&sdata->cmd_done, + msecs_to_jiffies(1000))) + return -ETIMEDOUT; + + return 0; +} + +static int stmfts_input_open(struct input_dev *dev) +{ + struct stmfts_data *sdata = input_get_drvdata(dev); + int err; + + err = pm_runtime_get_sync(&sdata->client->dev); + if (err < 0) + return err; + + err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON); + if (err) + return err; + + mutex_lock(&sdata->mutex); + sdata->running = true; + + if (sdata->hover_enabled) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_SS_HOVER_SENSE_ON); + if (err) + dev_warn(&sdata->client->dev, + "failed to enable hover\n"); + } + mutex_unlock(&sdata->mutex); + + if (sdata->use_key) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_MS_KEY_SENSE_ON); + if (err) + /* I can still use only the touch screen */ + dev_warn(&sdata->client->dev, + "failed to enable touchkey\n"); + } + + return 0; +} + +static void stmfts_input_close(struct input_dev *dev) +{ + struct stmfts_data *sdata = input_get_drvdata(dev); + int err; + + err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF); + if (err) + dev_warn(&sdata->client->dev, + "failed to disable touchscreen: %d\n", err); + + mutex_lock(&sdata->mutex); + + sdata->running = false; + + if (sdata->hover_enabled) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_SS_HOVER_SENSE_OFF); + if (err) + dev_warn(&sdata->client->dev, + "failed to disable hover: %d\n", err); + } + mutex_unlock(&sdata->mutex); + + if (sdata->use_key) { + err = i2c_smbus_write_byte(sdata->client, + STMFTS_MS_KEY_SENSE_OFF); + if (err) + dev_warn(&sdata->client->dev, + "failed to disable touchkey: %d\n", err); + } + + pm_runtime_put_sync(&sdata->client->dev); +} + +static ssize_t stmfts_sysfs_chip_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + return sprintf(buf, "%#x\n", sdata->chip_id); +} + +static ssize_t stmfts_sysfs_chip_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", sdata->chip_ver); +} + +static ssize_t stmfts_sysfs_fw_ver(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", sdata->fw_ver); +} + +static ssize_t stmfts_sysfs_config_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + return sprintf(buf, "%#x\n", sdata->config_id); +} + +static ssize_t stmfts_sysfs_config_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", sdata->config_ver); +} + +static ssize_t stmfts_sysfs_read_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + u8 status[4]; + int err; + + err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_STATUS, + sizeof(status), status); + if (err) + return err; + + return sprintf(buf, "%#02x\n", status[0]); +} + +static ssize_t stmfts_sysfs_hover_enable_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", sdata->hover_enabled); +} + +static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + unsigned long value; + int err = 0; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + mutex_lock(&sdata->mutex); + + if (value & sdata->hover_enabled) + goto out; + + if (sdata->running) + err = i2c_smbus_write_byte(sdata->client, + value ? STMFTS_SS_HOVER_SENSE_ON : + STMFTS_SS_HOVER_SENSE_OFF); + + if (!err) + sdata->hover_enabled = !!value; + +out: + mutex_unlock(&sdata->mutex); + + return len; +} + +static DEVICE_ATTR(chip_id, 0444, stmfts_sysfs_chip_id, NULL); +static DEVICE_ATTR(chip_version, 0444, stmfts_sysfs_chip_version, NULL); +static DEVICE_ATTR(fw_ver, 0444, stmfts_sysfs_fw_ver, NULL); +static DEVICE_ATTR(config_id, 0444, stmfts_sysfs_config_id, NULL); +static DEVICE_ATTR(config_version, 0444, stmfts_sysfs_config_version, NULL); +static DEVICE_ATTR(status, 0444, stmfts_sysfs_read_status, NULL); +static DEVICE_ATTR(hover_enable, 0644, stmfts_sysfs_hover_enable_read, + stmfts_sysfs_hover_enable_write); + +static struct attribute *stmfts_sysfs_attrs[] = { + &dev_attr_chip_id.attr, + &dev_attr_chip_version.attr, + &dev_attr_fw_ver.attr, + &dev_attr_config_id.attr, + &dev_attr_config_version.attr, + &dev_attr_status.attr, + &dev_attr_hover_enable.attr, + NULL +}; + +static struct attribute_group stmfts_attribute_group = { + .attrs = stmfts_sysfs_attrs +}; + +static int stmfts_power_on(struct stmfts_data *sdata) +{ + int err; + u8 reg[8]; + + err = regulator_bulk_enable(ARRAY_SIZE(sdata->regulators), + sdata->regulators); + if (err) + return err; + + /* + * The datasheet does not specify the power on time, but considering + * that the reset time is < 10ms, I sleep 20ms to be sure + */ + msleep(20); + + err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_INFO, + sizeof(reg), reg); + if (err < 0) + return err; + if (err != sizeof(reg)) + return -EIO; + + sdata->chip_id = be16_to_cpup((__be16 *)®[6]); + sdata->chip_ver = reg[0]; + sdata->fw_ver = be16_to_cpup((__be16 *)®[2]); + sdata->config_id = reg[4]; + sdata->config_ver = reg[5]; + + enable_irq(sdata->client->irq); + + msleep(50); + + err = stmfts_command(sdata, STMFTS_SYSTEM_RESET); + if (err) + return err; + + err = stmfts_command(sdata, STMFTS_SLEEP_OUT); + if (err) + return err; + + /* optional tuning */ + err = stmfts_command(sdata, STMFTS_MS_CX_TUNING); + if (err) + dev_warn(&sdata->client->dev, + "failed to perform mutual auto tune: %d\n", err); + + /* optional tuning */ + err = stmfts_command(sdata, STMFTS_SS_CX_TUNING); + if (err) + dev_warn(&sdata->client->dev, + "failed to perform self auto tune: %d\n", err); + + err = stmfts_command(sdata, STMFTS_FULL_FORCE_CALIBRATION); + if (err) + return err; + + /* + * At this point no one is using the touchscreen + * and I don't really care about the return value + */ + (void) i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); + + return 0; +} + +static void stmfts_power_off(void *data) +{ + struct stmfts_data *sdata = data; + + disable_irq(sdata->client->irq); + regulator_bulk_disable(ARRAY_SIZE(sdata->regulators), + sdata->regulators); +} + +/* This function is void because I don't want to prevent using the touch key + * only because the LEDs don't get registered + */ +static int stmfts_enable_led(struct stmfts_data *sdata) +{ + int err; + + /* get the regulator for powering the leds on */ + sdata->ledvdd = devm_regulator_get(&sdata->client->dev, "ledvdd"); + if (IS_ERR(sdata->ledvdd)) + return PTR_ERR(sdata->ledvdd); + + sdata->led_cdev.name = STMFTS_DEV_NAME; + sdata->led_cdev.max_brightness = LED_ON; + sdata->led_cdev.brightness = LED_OFF; + sdata->led_cdev.brightness_set = stmfts_brightness_set; + sdata->led_cdev.brightness_get = stmfts_brightness_get; + + err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev); + if (err) { + devm_regulator_put(sdata->ledvdd); + return err; + } + + return 0; +} + +static int stmfts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err; + struct stmfts_data *sdata; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENODEV; + + sdata = devm_kzalloc(&client->dev, sizeof(*sdata), GFP_KERNEL); + if (!sdata) + return -ENOMEM; + + i2c_set_clientdata(client, sdata); + + sdata->client = client; + mutex_init(&sdata->mutex); + init_completion(&sdata->cmd_done); + + sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd"; + sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd"; + err = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(sdata->regulators), + sdata->regulators); + if (err) + return err; + + sdata->input = devm_input_allocate_device(&client->dev); + if (!sdata->input) + return -ENOMEM; + + sdata->input->name = STMFTS_DEV_NAME; + sdata->input->id.bustype = BUS_I2C; + sdata->input->open = stmfts_input_open; + sdata->input->close = stmfts_input_close; + + touchscreen_parse_properties(sdata->input, true, &sdata->prop); + + input_set_abs_params(sdata->input, ABS_MT_POSITION_X, 0, + sdata->prop.max_x, 0, 0); + input_set_abs_params(sdata->input, ABS_MT_POSITION_Y, 0, + sdata->prop.max_y, 0, 0); + input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); + input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0); + input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0); + + sdata->use_key = device_property_read_bool(&client->dev, + "touch-key-connected"); + if (sdata->use_key) { + input_set_capability(sdata->input, EV_KEY, KEY_MENU); + input_set_capability(sdata->input, EV_KEY, KEY_BACK); + } + + err = input_mt_init_slots(sdata->input, + STMFTS_MAX_FINGERS, INPUT_MT_DIRECT); + if (err) + return err; + + input_set_drvdata(sdata->input, sdata); + + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, stmfts_irq_handler, + IRQF_ONESHOT, + "stmfts_irq", sdata); + if (err) + return err; + + /* stmfts_power_on expects interrupt to be disabled */ + disable_irq(client->irq); + + dev_dbg(&client->dev, "initializing ST-Microelectronics FTS...\n"); + + err = stmfts_power_on(sdata); + if (err) + return err; + + err = devm_add_action_or_reset(&client->dev, stmfts_power_off, sdata); + if (err) + return err; + + err = input_register_device(sdata->input); + if (err) + return err; + + if (sdata->use_key) { + err = stmfts_enable_led(sdata); + if (err) { + /* + * Even if the LEDs have failed to be initialized and + * used in the driver, I can still use the device even + * without LEDs. The ledvdd regulator pointer will be + * used as a flag. + */ + dev_warn(&client->dev, "unable to use touchkey leds\n"); + sdata->ledvdd = NULL; + } + } + + err = sysfs_create_group(&sdata->client->dev.kobj, + &stmfts_attribute_group); + if (err) + return err; + + pm_runtime_enable(&client->dev); + + return 0; +} + +static int stmfts_remove(struct i2c_client *client) +{ + pm_runtime_disable(&client->dev); + sysfs_remove_group(&client->dev.kobj, &stmfts_attribute_group); + + return 0; +} + +static int __maybe_unused stmfts_runtime_suspend(struct device *dev) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + int ret; + + ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN); + if (ret) + dev_warn(dev, "failed to suspend device: %d\n", ret); + + return ret; +} + +static int __maybe_unused stmfts_runtime_resume(struct device *dev) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + int ret; + + ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT); + if (ret) + dev_err(dev, "failed to resume device: %d\n", ret); + + return ret; +} + +static int __maybe_unused stmfts_suspend(struct device *dev) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + stmfts_power_off(sdata); + + return 0; +} + +static int __maybe_unused stmfts_resume(struct device *dev) +{ + struct stmfts_data *sdata = dev_get_drvdata(dev); + + return stmfts_power_on(sdata); +} + +static const struct dev_pm_ops stmfts_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stmfts_suspend, stmfts_resume) + SET_RUNTIME_PM_OPS(stmfts_runtime_suspend, stmfts_runtime_resume, NULL) +}; + +#ifdef CONFIG_OF +static const struct of_device_id stmfts_of_match[] = { + { .compatible = "st,stmfts", }, + { }, +}; +MODULE_DEVICE_TABLE(of, stmfts_of_match); +#endif + +static const struct i2c_device_id stmfts_id[] = { + { "stmfts", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, stmfts_id); + +static struct i2c_driver stmfts_driver = { + .driver = { + .name = STMFTS_DEV_NAME, + .of_match_table = of_match_ptr(stmfts_of_match), + .pm = &stmfts_pm_ops, + }, + .probe = stmfts_probe, + .remove = stmfts_remove, + .id_table = stmfts_id, +}; + +module_i2c_driver(stmfts_driver); + +MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>"); +MODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/tsc2007_core.c b/drivers/input/touchscreen/tsc2007_core.c index fc7384936011..8342e0c48a53 100644 --- a/drivers/input/touchscreen/tsc2007_core.c +++ b/drivers/input/touchscreen/tsc2007_core.c @@ -25,9 +25,9 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/i2c/tsc2007.h> #include <linux/of_device.h> #include <linux/of_gpio.h> +#include <linux/platform_data/tsc2007.h> #include "tsc2007.h" int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) |