diff options
Diffstat (limited to 'drivers/input')
75 files changed, 6302 insertions, 5346 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index de055451d1af..bc203485716d 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -738,20 +738,23 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) */ static int evdev_handle_get_val(struct evdev_client *client, struct input_dev *dev, unsigned int type, - unsigned long *bits, unsigned int max, - unsigned int size, void __user *p, int compat) + unsigned long *bits, unsigned int maxbit, + unsigned int maxlen, void __user *p, + int compat) { int ret; unsigned long *mem; + size_t len; - mem = kmalloc(sizeof(unsigned long) * max, GFP_KERNEL); + len = BITS_TO_LONGS(maxbit) * sizeof(unsigned long); + mem = kmalloc(len, GFP_KERNEL); if (!mem) return -ENOMEM; spin_lock_irq(&dev->event_lock); spin_lock(&client->buffer_lock); - memcpy(mem, bits, sizeof(unsigned long) * max); + memcpy(mem, bits, len); spin_unlock(&dev->event_lock); @@ -759,7 +762,7 @@ static int evdev_handle_get_val(struct evdev_client *client, spin_unlock_irq(&client->buffer_lock); - ret = bits_to_user(mem, max, size, p, compat); + ret = bits_to_user(mem, maxbit, maxlen, p, compat); if (ret < 0) evdev_queue_syn_dropped(client); 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 @@ #include <linux/workqueue.h> #include <linux/sched.h> /* HZ */ #include <linux/mutex.h> +#include <linux/timekeeping.h> /*#include <asm/io.h>*/ @@ -30,6 +31,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Generic gameport layer"); MODULE_LICENSE("GPL"); +static bool use_ktime = true; +module_param(use_ktime, bool, 0400); +MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed"); + /* * gameport_mutex protects entire gameport subsystem and is taken * every time gameport port or driver registrered or unregistered. @@ -76,6 +81,38 @@ static unsigned int get_time_pit(void) static int gameport_measure_speed(struct gameport *gameport) { + unsigned int i, t, tx; + u64 t1, t2, t3; + unsigned long flags; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + tx = ~0; + + for (i = 0; i < 50; i++) { + local_irq_save(flags); + t1 = ktime_get_ns(); + for (t = 0; t < 50; t++) + gameport_read(gameport); + t2 = ktime_get_ns(); + t3 = ktime_get_ns(); + local_irq_restore(flags); + udelay(i * 10); + t = (t2 - t1) - (t3 - t2); + if (t < tx) + tx = t; + } + + gameport_close(gameport); + t = 1000000 * 50; + if (tx) + t /= tx; + return t; +} + +static int old_gameport_measure_speed(struct gameport *gameport) +{ #if defined(__i386__) unsigned int i, t, t1, t2, t3, tx; @@ -521,7 +558,9 @@ static void gameport_add_port(struct gameport *gameport) if (gameport->parent) gameport->parent->child = gameport; - gameport->speed = gameport_measure_speed(gameport); + gameport->speed = use_ktime ? + gameport_measure_speed(gameport) : + old_gameport_measure_speed(gameport); list_add_tail(&gameport->node, &gameport_list); diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index d398f1321f14..fbe29fcb15c5 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -236,6 +236,35 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) } EXPORT_SYMBOL(input_mt_report_pointer_emulation); +static void __input_mt_drop_unused(struct input_dev *dev, struct input_mt *mt) +{ + int i; + + for (i = 0; i < mt->num_slots; i++) { + if (!input_mt_is_used(mt, &mt->slots[i])) { + input_mt_slot(dev, i); + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + } + } +} + +/** + * input_mt_drop_unused() - Inactivate slots not seen in this frame + * @dev: input device with allocated MT slots + * + * Lift all slots not seen since the last call to this function. + */ +void input_mt_drop_unused(struct input_dev *dev) +{ + struct input_mt *mt = dev->mt; + + if (mt) { + __input_mt_drop_unused(dev, mt); + mt->frame++; + } +} +EXPORT_SYMBOL(input_mt_drop_unused); + /** * input_mt_sync_frame() - synchronize mt frame * @dev: input device with allocated MT slots @@ -247,20 +276,13 @@ EXPORT_SYMBOL(input_mt_report_pointer_emulation); void input_mt_sync_frame(struct input_dev *dev) { struct input_mt *mt = dev->mt; - struct input_mt_slot *s; bool use_count = false; if (!mt) return; - if (mt->flags & INPUT_MT_DROP_UNUSED) { - for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { - if (input_mt_is_used(mt, s)) - continue; - input_mt_slot(dev, s - mt->slots); - input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); - } - } + if (mt->flags & INPUT_MT_DROP_UNUSED) + __input_mt_drop_unused(dev, mt); if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT)) use_count = true; diff --git a/drivers/input/input.c b/drivers/input/input.c index 29ca0bb4f561..0f175f55782b 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -498,7 +498,8 @@ void input_set_abs_params(struct input_dev *dev, unsigned int axis, absinfo->fuzz = fuzz; absinfo->flat = flat; - dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis); + __set_bit(EV_ABS, dev->evbit); + __set_bit(axis, dev->absbit); } EXPORT_SYMBOL(input_set_abs_params); @@ -1788,7 +1789,7 @@ struct input_dev *input_allocate_device(void) INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); - dev_set_name(&dev->dev, "input%ld", + dev_set_name(&dev->dev, "input%lu", (unsigned long) atomic_inc_return(&input_no) - 1); __module_get(THIS_MODULE); diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 9135606c8649..4284080e481d 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -36,6 +36,7 @@ #include <linux/gameport.h> #include <linux/jiffies.h> #include <linux/timex.h> +#include <linux/timekeeping.h> #define DRIVER_DESC "Analog joystick and gamepad driver" @@ -43,6 +44,10 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); +static bool use_ktime = true; +module_param(use_ktime, bool, 0400); +MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed"); + /* * Option parsing. */ @@ -158,7 +163,7 @@ static unsigned int get_time_pit(void) #define GET_TIME(x) rdtscl(x) #define DELTA(x,y) ((y)-(x)) #define TIME_NAME "TSC" -#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_TILE) +#elif defined(__alpha__) || defined(CONFIG_MN10300) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_TILE) #define GET_TIME(x) do { x = get_cycles(); } while (0) #define DELTA(x,y) ((y)-(x)) #define TIME_NAME "get_cycles" @@ -171,6 +176,25 @@ static unsigned long analog_faketime = 0; #warning Precise timer not defined for this architecture. #endif +static inline u64 get_time(void) +{ + if (use_ktime) { + return ktime_get_ns(); + } else { + unsigned int x; + GET_TIME(x); + return x; + } +} + +static inline unsigned int delta(u64 x, u64 y) +{ + if (use_ktime) + return y - x; + else + return DELTA((unsigned int)x, (unsigned int)y); +} + /* * analog_decode() decodes analog joystick data and reports input events. */ @@ -226,7 +250,8 @@ static void analog_decode(struct analog *analog, int *axes, int *initial, int bu static int analog_cooked_read(struct analog_port *port) { struct gameport *gameport = port->gameport; - unsigned int time[4], start, loop, now, loopout, timeout; + u64 time[4], start, loop, now; + unsigned int loopout, timeout; unsigned char data[4], this, last; unsigned long flags; int i, j; @@ -236,7 +261,7 @@ static int analog_cooked_read(struct analog_port *port) local_irq_save(flags); gameport_trigger(gameport); - GET_TIME(now); + now = get_time(); local_irq_restore(flags); start = now; @@ -249,16 +274,16 @@ static int analog_cooked_read(struct analog_port *port) local_irq_disable(); this = gameport_read(gameport) & port->mask; - GET_TIME(now); + now = get_time(); local_irq_restore(flags); - if ((last ^ this) && (DELTA(loop, now) < loopout)) { + if ((last ^ this) && (delta(loop, now) < loopout)) { data[i] = last ^ this; time[i] = now; i++; } - } while (this && (i < 4) && (DELTA(start, now) < timeout)); + } while (this && (i < 4) && (delta(start, now) < timeout)); this <<= 4; @@ -266,7 +291,7 @@ static int analog_cooked_read(struct analog_port *port) this |= data[i]; for (j = 0; j < 4; j++) if (data[i] & (1 << j)) - port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; + port->axes[j] = (delta(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; } return -(this != port->mask); @@ -365,31 +390,39 @@ static void analog_close(struct input_dev *dev) static void analog_calibrate_timer(struct analog_port *port) { struct gameport *gameport = port->gameport; - unsigned int i, t, tx, t1, t2, t3; + unsigned int i, t, tx; + u64 t1, t2, t3; unsigned long flags; - local_irq_save(flags); - GET_TIME(t1); + if (use_ktime) { + port->speed = 1000000; + } else { + local_irq_save(flags); + t1 = get_time(); #ifdef FAKE_TIME - analog_faketime += 830; + analog_faketime += 830; #endif - mdelay(1); - GET_TIME(t2); - GET_TIME(t3); - local_irq_restore(flags); + mdelay(1); + t2 = get_time(); + t3 = get_time(); + local_irq_restore(flags); - port->speed = DELTA(t1, t2) - DELTA(t2, t3); + port->speed = delta(t1, t2) - delta(t2, t3); + } tx = ~0; for (i = 0; i < 50; i++) { local_irq_save(flags); - GET_TIME(t1); - for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); } - GET_TIME(t3); + t1 = get_time(); + for (t = 0; t < 50; t++) { + gameport_read(gameport); + t2 = get_time(); + } + t3 = get_time(); local_irq_restore(flags); udelay(i); - t = DELTA(t1, t2) - DELTA(t2, t3); + t = delta(t1, t2) - delta(t2, t3); if (t < tx) tx = t; } diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 603fe0dd3682..2ed7905a068f 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -95,7 +95,8 @@ #define XTYPE_XBOX 0 #define XTYPE_XBOX360 1 #define XTYPE_XBOX360W 2 -#define XTYPE_UNKNOWN 3 +#define XTYPE_XBOXONE 3 +#define XTYPE_UNKNOWN 4 static bool dpad_to_buttons; module_param(dpad_to_buttons, bool, S_IRUGO); @@ -121,10 +122,13 @@ static const struct xpad_device { { 0x045e, 0x0287, "Microsoft Xbox Controller S", 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, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE }, { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, + { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 }, + { 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 }, { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 }, { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, @@ -138,10 +142,17 @@ static const struct xpad_device { { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX }, { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, + { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 }, + { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 }, { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 }, { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 }, + { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, + { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 }, + { 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX }, @@ -154,28 +165,51 @@ static const struct xpad_device { { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX }, { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 }, + { 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 }, { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX }, + { 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX }, + { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 }, { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX }, { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, + { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 }, { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 }, { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 }, + { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 }, + { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 }, + { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, + { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 }, + { 0x162e, 0xbeef, "Joytech Neo-Se Take2", 0, XTYPE_XBOX360 }, { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 }, { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 }, { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 }, { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 }, + { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 }, { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", 0, XTYPE_XBOX360 }, { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 }, + { 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } }; @@ -231,10 +265,12 @@ static const signed short xpad_abs_triggers[] = { -1 }; -/* Xbox 360 has a vendor-specific class, so we cannot match it with only +/* + * Xbox 360 has a vendor-specific class, so we cannot match it with only * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we * match against vendor id as well. Wired Xbox 360 devices have protocol 1, - * wireless controllers have protocol 129. */ + * wireless controllers have protocol 129. + */ #define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ .idVendor = (vend), \ @@ -245,9 +281,21 @@ static const signed short xpad_abs_triggers[] = { { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \ { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) } +/* The Xbox One controller uses subclass 71 and protocol 208. */ +#define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ + .idVendor = (vend), \ + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ + .bInterfaceSubClass = 71, \ + .bInterfaceProtocol = (pr) +#define XPAD_XBOXONE_VENDOR(vend) \ + { XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) } + static struct usb_device_id xpad_table[] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ + XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ + XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */ XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ @@ -259,6 +307,9 @@ static struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */ XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */ XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */ + XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */ + XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */ { } }; @@ -278,12 +329,10 @@ struct usb_xpad { struct urb *bulk_out; unsigned char *bdata; -#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) struct urb *irq_out; /* urb for interrupt out report */ unsigned char *odata; /* output data */ dma_addr_t odata_dma; struct mutex odata_mutex; -#endif #if defined(CONFIG_JOYSTICK_XPAD_LEDS) struct xpad_led *led; @@ -470,6 +519,105 @@ static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned cha xpad360_process_packet(xpad, cmd, &data[4]); } +/* + * xpadone_process_buttons + * + * Process a button update packet from an Xbox one controller. + */ +static void xpadone_process_buttons(struct usb_xpad *xpad, + struct input_dev *dev, + unsigned char *data) +{ + /* menu/view buttons */ + input_report_key(dev, BTN_START, data[4] & 0x04); + input_report_key(dev, BTN_SELECT, data[4] & 0x08); + + /* buttons A,B,X,Y */ + input_report_key(dev, BTN_A, data[4] & 0x10); + input_report_key(dev, BTN_B, data[4] & 0x20); + input_report_key(dev, BTN_X, data[4] & 0x40); + input_report_key(dev, BTN_Y, data[4] & 0x80); + + /* digital pad */ + if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { + /* dpad as buttons (left, right, up, down) */ + input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04); + input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08); + input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01); + input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02); + } else { + input_report_abs(dev, ABS_HAT0X, + !!(data[5] & 0x08) - !!(data[5] & 0x04)); + input_report_abs(dev, ABS_HAT0Y, + !!(data[5] & 0x02) - !!(data[5] & 0x01)); + } + + /* TL/TR */ + input_report_key(dev, BTN_TL, data[5] & 0x10); + input_report_key(dev, BTN_TR, data[5] & 0x20); + + /* stick press left/right */ + input_report_key(dev, BTN_THUMBL, data[5] & 0x40); + input_report_key(dev, BTN_THUMBR, data[5] & 0x80); + + if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { + /* left stick */ + input_report_abs(dev, ABS_X, + (__s16) le16_to_cpup((__le16 *)(data + 10))); + input_report_abs(dev, ABS_Y, + ~(__s16) le16_to_cpup((__le16 *)(data + 12))); + + /* right stick */ + input_report_abs(dev, ABS_RX, + (__s16) le16_to_cpup((__le16 *)(data + 14))); + input_report_abs(dev, ABS_RY, + ~(__s16) le16_to_cpup((__le16 *)(data + 16))); + } + + /* triggers left/right */ + if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { + input_report_key(dev, BTN_TL2, + (__u16) le16_to_cpup((__le16 *)(data + 6))); + input_report_key(dev, BTN_TR2, + (__u16) le16_to_cpup((__le16 *)(data + 8))); + } else { + input_report_abs(dev, ABS_Z, + (__u16) le16_to_cpup((__le16 *)(data + 6))); + input_report_abs(dev, ABS_RZ, + (__u16) le16_to_cpup((__le16 *)(data + 8))); + } + + input_sync(dev); +} + +/* + * xpadone_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. This version is for the Xbox One controller. + * + * The report format was gleaned from + * https://github.com/kylelemons/xbox/blob/master/xbox.go + */ + +static void xpadone_process_packet(struct usb_xpad *xpad, + u16 cmd, unsigned char *data) +{ + struct input_dev *dev = xpad->dev; + + switch (data[0]) { + case 0x20: + xpadone_process_buttons(xpad, dev, data); + break; + + case 0x07: + /* the xbox button has its own special report */ + input_report_key(dev, BTN_MODE, data[4] & 0x01); + input_sync(dev); + break; + } +} + static void xpad_irq_in(struct urb *urb) { struct usb_xpad *xpad = urb->context; @@ -502,6 +650,9 @@ static void xpad_irq_in(struct urb *urb) case XTYPE_XBOX360W: xpad360w_process_packet(xpad, 0, xpad->idata); break; + case XTYPE_XBOXONE: + xpadone_process_packet(xpad, 0, xpad->idata); + break; default: xpad_process_packet(xpad, 0, xpad->idata); } @@ -535,7 +686,6 @@ static void xpad_bulk_out(struct urb *urb) } } -#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) static void xpad_irq_out(struct urb *urb) { struct usb_xpad *xpad = urb->context; @@ -573,6 +723,7 @@ exit: static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { struct usb_endpoint_descriptor *ep_irq_out; + int ep_irq_out_idx; int error; if (xpad->xtype == XTYPE_UNKNOWN) @@ -593,7 +744,10 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) goto fail2; } - ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; + /* Xbox One controller has in/out endpoints swapped. */ + ep_irq_out_idx = xpad->xtype == XTYPE_XBOXONE ? 0 : 1; + ep_irq_out = &intf->cur_altsetting->endpoint[ep_irq_out_idx].desc; + usb_fill_int_urb(xpad->irq_out, xpad->udev, usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), xpad->odata, XPAD_PKT_LEN, @@ -621,11 +775,6 @@ static void xpad_deinit_output(struct usb_xpad *xpad) xpad->odata, xpad->odata_dma); } } -#else -static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } -static void xpad_deinit_output(struct usb_xpad *xpad) {} -static void xpad_stop_output(struct usb_xpad *xpad) {} -#endif #ifdef CONFIG_JOYSTICK_XPAD_FF static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) @@ -692,7 +841,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect static int xpad_init_ff(struct usb_xpad *xpad) { - if (xpad->xtype == XTYPE_UNKNOWN) + if (xpad->xtype == XTYPE_UNKNOWN || xpad->xtype == XTYPE_XBOXONE) return 0; input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); @@ -801,6 +950,14 @@ static int xpad_open(struct input_dev *dev) if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) return -EIO; + if (xpad->xtype == XTYPE_XBOXONE) { + /* Xbox one controller needs to be initialized. */ + xpad->odata[0] = 0x05; + xpad->odata[1] = 0x20; + xpad->irq_out->transfer_buffer_length = 2; + return usb_submit_urb(xpad->irq_out, GFP_KERNEL); + } + return 0; } @@ -816,6 +973,7 @@ static void xpad_close(struct input_dev *dev) static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) { + struct usb_xpad *xpad = input_get_drvdata(input_dev); set_bit(abs, input_dev->absbit); switch (abs) { @@ -827,7 +985,10 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) break; case ABS_Z: case ABS_RZ: /* the triggers (if mapped to axes) */ - input_set_abs_params(input_dev, abs, 0, 255, 0, 0); + if (xpad->xtype == XTYPE_XBOXONE) + input_set_abs_params(input_dev, abs, 0, 1023, 0, 0); + else + input_set_abs_params(input_dev, abs, 0, 255, 0, 0); break; case ABS_HAT0X: case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */ @@ -842,6 +1003,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id struct usb_xpad *xpad; struct input_dev *input_dev; struct usb_endpoint_descriptor *ep_irq_in; + int ep_irq_in_idx; int i, error; for (i = 0; xpad_device[i].idVendor; i++) { @@ -850,6 +1012,16 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id break; } + if (xpad_device[i].xtype == XTYPE_XBOXONE && + intf->cur_altsetting->desc.bInterfaceNumber != 0) { + /* + * The Xbox One controller lists three interfaces all with the + * same interface class, subclass and protocol. Differentiate by + * interface number. + */ + return -ENODEV; + } + xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL); input_dev = input_allocate_device(); if (!xpad || !input_dev) { @@ -920,7 +1092,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id __set_bit(xpad_common_btn[i], input_dev->keybit); /* set up model-specific ones */ - if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) { + if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W || + xpad->xtype == XTYPE_XBOXONE) { for (i = 0; xpad360_btn[i] >= 0; i++) __set_bit(xpad360_btn[i], input_dev->keybit); } else { @@ -933,7 +1106,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id __set_bit(xpad_btn_pad[i], input_dev->keybit); } else { for (i = 0; xpad_abs_pad[i] >= 0; i++) - xpad_set_up_abs(input_dev, xpad_abs_pad[i]); + xpad_set_up_abs(input_dev, xpad_abs_pad[i]); } if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { @@ -956,7 +1129,10 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id if (error) goto fail5; - ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; + /* Xbox One controller has in/out endpoints swapped. */ + ep_irq_in_idx = xpad->xtype == XTYPE_XBOXONE ? 1 : 0; + ep_irq_in = &intf->cur_altsetting->endpoint[ep_irq_in_idx].desc; + usb_fill_int_urb(xpad->irq_in, udev, usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress), xpad->idata, XPAD_PKT_LEN, xpad_irq_in, diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index f7e79b481349..a3958c63d7d5 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -665,4 +665,14 @@ config KEYBOARD_CROS_EC To compile this driver as a module, choose M here: the module will be called cros_ec_keyb. +config KEYBOARD_CAP1106 + tristate "Microchip CAP1106 touch sensor" + depends on OF && I2C + select REGMAP_I2C + help + Say Y here to enable the CAP1106 touch sensor driver. + + To compile this driver as a module, choose M here: the + module will be called cap1106. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 7504ae19049d..0a3345634d79 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o +obj-$(CONFIG_KEYBOARD_CAP1106) += cap1106.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 5ef7fcf0e250..21a62d0fa764 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -251,9 +251,7 @@ static void adp5588_gpio_remove(struct adp5588_kpad *kpad) dev_warn(dev, "teardown failed %d\n", error); } - error = gpiochip_remove(&kpad->gc); - if (error) - dev_warn(dev, "gpiochip_remove failed %d\n", error); + gpiochip_remove(&kpad->gc); } #else static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) @@ -589,6 +587,7 @@ static int adp5588_probe(struct i2c_client *client, err_free_irq: free_irq(client->irq, kpad); + cancel_delayed_work_sync(&kpad->work); err_unreg_dev: input_unregister_device(input); input = NULL; diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 6329549bf6ad..a45267729dfc 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -567,9 +567,7 @@ static void adp5589_gpio_remove(struct adp5589_kpad *kpad) dev_warn(dev, "teardown failed %d\n", error); } - error = gpiochip_remove(&kpad->gc); - if (error) - dev_warn(dev, "gpiochip_remove failed %d\n", error); + gpiochip_remove(&kpad->gc); } #else static inline int adp5589_gpio_add(struct adp5589_kpad *kpad) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 2dd1d0dd4f7d..6f5d79569136 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1791,14 +1791,6 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), - DMI_MATCH(DMI_PRODUCT_NAME, "LW25-B7HV"), - }, - .callback = atkbd_deactivate_fixup, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), - DMI_MATCH(DMI_PRODUCT_NAME, "P1-J273B"), }, .callback = atkbd_deactivate_fixup, }, diff --git a/drivers/input/keyboard/cap1106.c b/drivers/input/keyboard/cap1106.c new file mode 100644 index 000000000000..d70b65a14ced --- /dev/null +++ b/drivers/input/keyboard/cap1106.c @@ -0,0 +1,341 @@ +/* + * Input driver for Microchip CAP1106, 6 channel capacitive touch sensor + * + * http://www.microchip.com/wwwproducts/Devices.aspx?product=CAP1106 + * + * (c) 2014 Daniel Mack <linux@zonque.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/i2c.h> +#include <linux/gpio/consumer.h> + +#define CAP1106_REG_MAIN_CONTROL 0x00 +#define CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT (6) +#define CAP1106_REG_MAIN_CONTROL_GAIN_MASK (0xc0) +#define CAP1106_REG_MAIN_CONTROL_DLSEEP BIT(4) +#define CAP1106_REG_GENERAL_STATUS 0x02 +#define CAP1106_REG_SENSOR_INPUT 0x03 +#define CAP1106_REG_NOISE_FLAG_STATUS 0x0a +#define CAP1106_REG_SENOR_DELTA(X) (0x10 + (X)) +#define CAP1106_REG_SENSITIVITY_CONTROL 0x1f +#define CAP1106_REG_CONFIG 0x20 +#define CAP1106_REG_SENSOR_ENABLE 0x21 +#define CAP1106_REG_SENSOR_CONFIG 0x22 +#define CAP1106_REG_SENSOR_CONFIG2 0x23 +#define CAP1106_REG_SAMPLING_CONFIG 0x24 +#define CAP1106_REG_CALIBRATION 0x26 +#define CAP1106_REG_INT_ENABLE 0x27 +#define CAP1106_REG_REPEAT_RATE 0x28 +#define CAP1106_REG_MT_CONFIG 0x2a +#define CAP1106_REG_MT_PATTERN_CONFIG 0x2b +#define CAP1106_REG_MT_PATTERN 0x2d +#define CAP1106_REG_RECALIB_CONFIG 0x2f +#define CAP1106_REG_SENSOR_THRESH(X) (0x30 + (X)) +#define CAP1106_REG_SENSOR_NOISE_THRESH 0x38 +#define CAP1106_REG_STANDBY_CHANNEL 0x40 +#define CAP1106_REG_STANDBY_CONFIG 0x41 +#define CAP1106_REG_STANDBY_SENSITIVITY 0x42 +#define CAP1106_REG_STANDBY_THRESH 0x43 +#define CAP1106_REG_CONFIG2 0x44 +#define CAP1106_REG_SENSOR_BASE_CNT(X) (0x50 + (X)) +#define CAP1106_REG_SENSOR_CALIB (0xb1 + (X)) +#define CAP1106_REG_SENSOR_CALIB_LSB1 0xb9 +#define CAP1106_REG_SENSOR_CALIB_LSB2 0xba +#define CAP1106_REG_PRODUCT_ID 0xfd +#define CAP1106_REG_MANUFACTURER_ID 0xfe +#define CAP1106_REG_REVISION 0xff + +#define CAP1106_NUM_CHN 6 +#define CAP1106_PRODUCT_ID 0x55 +#define CAP1106_MANUFACTURER_ID 0x5d + +struct cap1106_priv { + struct regmap *regmap; + struct input_dev *idev; + + /* config */ + unsigned short keycodes[CAP1106_NUM_CHN]; +}; + +static const struct reg_default cap1106_reg_defaults[] = { + { CAP1106_REG_MAIN_CONTROL, 0x00 }, + { CAP1106_REG_GENERAL_STATUS, 0x00 }, + { CAP1106_REG_SENSOR_INPUT, 0x00 }, + { CAP1106_REG_NOISE_FLAG_STATUS, 0x00 }, + { CAP1106_REG_SENSITIVITY_CONTROL, 0x2f }, + { CAP1106_REG_CONFIG, 0x20 }, + { CAP1106_REG_SENSOR_ENABLE, 0x3f }, + { CAP1106_REG_SENSOR_CONFIG, 0xa4 }, + { CAP1106_REG_SENSOR_CONFIG2, 0x07 }, + { CAP1106_REG_SAMPLING_CONFIG, 0x39 }, + { CAP1106_REG_CALIBRATION, 0x00 }, + { CAP1106_REG_INT_ENABLE, 0x3f }, + { CAP1106_REG_REPEAT_RATE, 0x3f }, + { CAP1106_REG_MT_CONFIG, 0x80 }, + { CAP1106_REG_MT_PATTERN_CONFIG, 0x00 }, + { CAP1106_REG_MT_PATTERN, 0x3f }, + { CAP1106_REG_RECALIB_CONFIG, 0x8a }, + { CAP1106_REG_SENSOR_THRESH(0), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(1), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(2), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(3), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(4), 0x40 }, + { CAP1106_REG_SENSOR_THRESH(5), 0x40 }, + { CAP1106_REG_SENSOR_NOISE_THRESH, 0x01 }, + { CAP1106_REG_STANDBY_CHANNEL, 0x00 }, + { CAP1106_REG_STANDBY_CONFIG, 0x39 }, + { CAP1106_REG_STANDBY_SENSITIVITY, 0x02 }, + { CAP1106_REG_STANDBY_THRESH, 0x40 }, + { CAP1106_REG_CONFIG2, 0x40 }, + { CAP1106_REG_SENSOR_CALIB_LSB1, 0x00 }, + { CAP1106_REG_SENSOR_CALIB_LSB2, 0x00 }, +}; + +static bool cap1106_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CAP1106_REG_MAIN_CONTROL: + case CAP1106_REG_SENSOR_INPUT: + case CAP1106_REG_SENOR_DELTA(0): + case CAP1106_REG_SENOR_DELTA(1): + case CAP1106_REG_SENOR_DELTA(2): + case CAP1106_REG_SENOR_DELTA(3): + case CAP1106_REG_SENOR_DELTA(4): + case CAP1106_REG_SENOR_DELTA(5): + case CAP1106_REG_PRODUCT_ID: + case CAP1106_REG_MANUFACTURER_ID: + case CAP1106_REG_REVISION: + return true; + } + + return false; +} + +static const struct regmap_config cap1106_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CAP1106_REG_REVISION, + .reg_defaults = cap1106_reg_defaults, + + .num_reg_defaults = ARRAY_SIZE(cap1106_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = cap1106_volatile_reg, +}; + +static irqreturn_t cap1106_thread_func(int irq_num, void *data) +{ + struct cap1106_priv *priv = data; + unsigned int status; + int ret, i; + + /* + * Deassert interrupt. This needs to be done before reading the status + * registers, which will not carry valid values otherwise. + */ + ret = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, 1, 0); + if (ret < 0) + goto out; + + ret = regmap_read(priv->regmap, CAP1106_REG_SENSOR_INPUT, &status); + if (ret < 0) + goto out; + + for (i = 0; i < CAP1106_NUM_CHN; i++) + input_report_key(priv->idev, priv->keycodes[i], + status & (1 << i)); + + input_sync(priv->idev); + +out: + return IRQ_HANDLED; +} + +static int cap1106_set_sleep(struct cap1106_priv *priv, bool sleep) +{ + return regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, + CAP1106_REG_MAIN_CONTROL_DLSEEP, + sleep ? CAP1106_REG_MAIN_CONTROL_DLSEEP : 0); +} + +static int cap1106_input_open(struct input_dev *idev) +{ + struct cap1106_priv *priv = input_get_drvdata(idev); + + return cap1106_set_sleep(priv, false); +} + +static void cap1106_input_close(struct input_dev *idev) +{ + struct cap1106_priv *priv = input_get_drvdata(idev); + + cap1106_set_sleep(priv, true); +} + +static int cap1106_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c_client->dev; + struct cap1106_priv *priv; + struct device_node *node; + int i, error, irq, gain = 0; + unsigned int val, rev; + u32 gain32, keycodes[CAP1106_NUM_CHN]; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(i2c_client, &cap1106_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + error = regmap_read(priv->regmap, CAP1106_REG_PRODUCT_ID, &val); + if (error) + return error; + + if (val != CAP1106_PRODUCT_ID) { + dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n", + val, CAP1106_PRODUCT_ID); + return -ENODEV; + } + + error = regmap_read(priv->regmap, CAP1106_REG_MANUFACTURER_ID, &val); + if (error) + return error; + + if (val != CAP1106_MANUFACTURER_ID) { + dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n", + val, CAP1106_MANUFACTURER_ID); + return -ENODEV; + } + + error = regmap_read(priv->regmap, CAP1106_REG_REVISION, &rev); + if (error < 0) + return error; + + dev_info(dev, "CAP1106 detected, revision 0x%02x\n", rev); + i2c_set_clientdata(i2c_client, priv); + node = dev->of_node; + + if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) { + if (is_power_of_2(gain32) && gain32 <= 8) + gain = ilog2(gain32); + else + dev_err(dev, "Invalid sensor-gain value %d\n", gain32); + } + + BUILD_BUG_ON(ARRAY_SIZE(keycodes) != ARRAY_SIZE(priv->keycodes)); + + /* Provide some useful defaults */ + for (i = 0; i < ARRAY_SIZE(keycodes); i++) + keycodes[i] = KEY_A + i; + + of_property_read_u32_array(node, "linux,keycodes", + keycodes, ARRAY_SIZE(keycodes)); + + for (i = 0; i < ARRAY_SIZE(keycodes); i++) + priv->keycodes[i] = keycodes[i]; + + error = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, + CAP1106_REG_MAIN_CONTROL_GAIN_MASK, + gain << CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT); + if (error) + return error; + + /* Disable autorepeat. The Linux input system has its own handling. */ + error = regmap_write(priv->regmap, CAP1106_REG_REPEAT_RATE, 0); + if (error) + return error; + + priv->idev = devm_input_allocate_device(dev); + if (!priv->idev) + return -ENOMEM; + + priv->idev->name = "CAP1106 capacitive touch sensor"; + priv->idev->id.bustype = BUS_I2C; + priv->idev->evbit[0] = BIT_MASK(EV_KEY); + + if (of_property_read_bool(node, "autorepeat")) + __set_bit(EV_REP, priv->idev->evbit); + + for (i = 0; i < CAP1106_NUM_CHN; i++) + __set_bit(priv->keycodes[i], priv->idev->keybit); + + __clear_bit(KEY_RESERVED, priv->idev->keybit); + + priv->idev->keycode = priv->keycodes; + priv->idev->keycodesize = sizeof(priv->keycodes[0]); + priv->idev->keycodemax = ARRAY_SIZE(priv->keycodes); + + priv->idev->id.vendor = CAP1106_MANUFACTURER_ID; + priv->idev->id.product = CAP1106_PRODUCT_ID; + priv->idev->id.version = rev; + + priv->idev->open = cap1106_input_open; + priv->idev->close = cap1106_input_close; + + input_set_drvdata(priv->idev, priv); + + /* + * Put the device in deep sleep mode for now. + * ->open() will bring it back once the it is actually needed. + */ + cap1106_set_sleep(priv, true); + + error = input_register_device(priv->idev); + if (error) + return error; + + irq = irq_of_parse_and_map(node, 0); + if (!irq) { + dev_err(dev, "Unable to parse or map IRQ\n"); + return -ENXIO; + } + + error = devm_request_threaded_irq(dev, irq, NULL, cap1106_thread_func, + IRQF_ONESHOT, dev_name(dev), priv); + if (error) + return error; + + return 0; +} + +static const struct of_device_id cap1106_dt_ids[] = { + { .compatible = "microchip,cap1106", }, + {} +}; +MODULE_DEVICE_TABLE(of, cap1106_dt_ids); + +static const struct i2c_device_id cap1106_i2c_ids[] = { + { "cap1106", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cap1106_i2c_ids); + +static struct i2c_driver cap1106_i2c_driver = { + .driver = { + .name = "cap1106", + .owner = THIS_MODULE, + .of_match_table = cap1106_dt_ids, + }, + .id_table = cap1106_i2c_ids, + .probe = cap1106_i2c_probe, +}; + +module_i2c_driver(cap1106_i2c_driver); + +MODULE_ALIAS("platform:cap1106"); +MODULE_DESCRIPTION("Microchip CAP1106 driver"); +MODULE_AUTHOR("Daniel Mack <linux@zonque.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 408379669d3c..ffa989f2c785 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -22,10 +22,11 @@ */ #include <linux/module.h> +#include <linux/bitops.h> #include <linux/i2c.h> #include <linux/input.h> +#include <linux/interrupt.h> #include <linux/kernel.h> -#include <linux/notifier.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/input/matrix_keypad.h> @@ -38,11 +39,11 @@ * @row_shift: log2 or number of rows, rounded up * @keymap_data: Matrix keymap data used to convert to keyscan values * @ghost_filter: true to enable the matrix key-ghosting filter + * @valid_keys: bitmap of existing keys for each matrix column * @old_kb_state: bitmap of keys pressed last scan * @dev: Device pointer * @idev: Input device * @ec: Top level ChromeOS device to use to talk to EC - * @event_notifier: interrupt event notifier for transport devices */ struct cros_ec_keyb { unsigned int rows; @@ -50,48 +51,24 @@ struct cros_ec_keyb { int row_shift; const struct matrix_keymap_data *keymap_data; bool ghost_filter; + uint8_t *valid_keys; uint8_t *old_kb_state; struct device *dev; struct input_dev *idev; struct cros_ec_device *ec; - struct notifier_block notifier; }; -static bool cros_ec_keyb_row_has_ghosting(struct cros_ec_keyb *ckdev, - uint8_t *buf, int row) -{ - int pressed_in_row = 0; - int row_has_teeth = 0; - int col, mask; - - mask = 1 << row; - for (col = 0; col < ckdev->cols; col++) { - if (buf[col] & mask) { - pressed_in_row++; - row_has_teeth |= buf[col] & ~mask; - if (pressed_in_row > 1 && row_has_teeth) { - /* ghosting */ - dev_dbg(ckdev->dev, - "ghost found at: r%d c%d, pressed %d, teeth 0x%x\n", - row, col, pressed_in_row, - row_has_teeth); - return true; - } - } - } - - return false; -} - /* * Returns true when there is at least one combination of pressed keys that * results in ghosting. */ static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) { - int row; + int col1, col2, buf1, buf2; + struct device *dev = ckdev->dev; + uint8_t *valid_keys = ckdev->valid_keys; /* * Ghosting happens if for any pressed key X there are other keys @@ -105,27 +82,23 @@ static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) * * In this case only X, Y, and Z are pressed, but g appears to be * pressed too (see Wikipedia). - * - * We can detect ghosting in a single pass (*) over the keyboard state - * by maintaining two arrays. pressed_in_row counts how many pressed - * keys we have found in a row. row_has_teeth is true if any of the - * pressed keys for this row has other pressed keys in its column. If - * at any point of the scan we find that a row has multiple pressed - * keys, and at least one of them is at the intersection with a column - * with multiple pressed keys, we're sure there is ghosting. - * Conversely, if there is ghosting, we will detect such situation for - * at least one key during the pass. - * - * (*) This looks linear in the number of keys, but it's not. We can - * cheat because the number of rows is small. */ - for (row = 0; row < ckdev->rows; row++) - if (cros_ec_keyb_row_has_ghosting(ckdev, buf, row)) - return true; + for (col1 = 0; col1 < ckdev->cols; col1++) { + buf1 = buf[col1] & valid_keys[col1]; + for (col2 = col1 + 1; col2 < ckdev->cols; col2++) { + buf2 = buf[col2] & valid_keys[col2]; + if (hweight8(buf1 & buf2) > 1) { + dev_dbg(dev, "ghost found at: B[%02d]:0x%02x & B[%02d]:0x%02x", + col1, buf1, col2, buf2); + return true; + } + } + } return false; } + /* * Compares the new keyboard state to the old one and produces key * press/release events accordingly. The keyboard state is 13 bytes (one byte @@ -173,41 +146,79 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, input_sync(ckdev->idev); } -static int cros_ec_keyb_open(struct input_dev *dev) +static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) { - struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + struct cros_ec_command msg = { + .version = 0, + .command = EC_CMD_MKBP_STATE, + .outdata = NULL, + .outsize = 0, + .indata = kb_state, + .insize = ckdev->cols, + }; + + return cros_ec_cmd_xfer(ckdev->ec, &msg); +} + +static irqreturn_t cros_ec_keyb_irq(int irq, void *data) +{ + struct cros_ec_keyb *ckdev = data; + struct cros_ec_device *ec = ckdev->ec; + int ret; + uint8_t kb_state[ckdev->cols]; + + if (device_may_wakeup(ec->dev)) + pm_wakeup_event(ec->dev, 0); + + ret = cros_ec_keyb_get_state(ckdev, kb_state); + if (ret >= 0) + cros_ec_keyb_process(ckdev, kb_state, ret); + else + dev_err(ec->dev, "failed to get keyboard state: %d\n", ret); - return blocking_notifier_chain_register(&ckdev->ec->event_notifier, - &ckdev->notifier); + return IRQ_HANDLED; } -static void cros_ec_keyb_close(struct input_dev *dev) +static int cros_ec_keyb_open(struct input_dev *dev) { struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + struct cros_ec_device *ec = ckdev->ec; - blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, - &ckdev->notifier); + return request_threaded_irq(ec->irq, NULL, cros_ec_keyb_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "cros_ec_keyb", ckdev); } -static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) +static void cros_ec_keyb_close(struct input_dev *dev) { - return ckdev->ec->command_recv(ckdev->ec, EC_CMD_MKBP_STATE, - kb_state, ckdev->cols); + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + struct cros_ec_device *ec = ckdev->ec; + + free_irq(ec->irq, ckdev); } -static int cros_ec_keyb_work(struct notifier_block *nb, - unsigned long state, void *_notify) +/* + * Walks keycodes flipping bit in buffer COLUMNS deep where bit is ROW. Used by + * ghosting logic to ignore NULL or virtual keys. + */ +static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev) { - int ret; - struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, - notifier); - uint8_t kb_state[ckdev->cols]; + int row, col; + int row_shift = ckdev->row_shift; + unsigned short *keymap = ckdev->idev->keycode; + unsigned short code; - ret = cros_ec_keyb_get_state(ckdev, kb_state); - if (ret >= 0) - cros_ec_keyb_process(ckdev, kb_state, ret); + BUG_ON(ckdev->idev->keycodesize != sizeof(*keymap)); - return NOTIFY_DONE; + for (col = 0; col < ckdev->cols; col++) { + for (row = 0; row < ckdev->rows; row++) { + code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; + if (code && (code != KEY_BATTERY)) + ckdev->valid_keys[col] |= 1 << row; + } + dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n", + col, ckdev->valid_keys[col]); + } } static int cros_ec_keyb_probe(struct platform_device *pdev) @@ -230,6 +241,11 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) &ckdev->cols); if (err) return err; + + ckdev->valid_keys = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); + if (!ckdev->valid_keys) + return -ENOMEM; + ckdev->old_kb_state = devm_kzalloc(&pdev->dev, ckdev->cols, GFP_KERNEL); if (!ckdev->old_kb_state) return -ENOMEM; @@ -238,8 +254,12 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) if (!idev) return -ENOMEM; + if (!ec->irq) { + dev_err(dev, "no EC IRQ specified\n"); + return -EINVAL; + } + ckdev->ec = ec; - ckdev->notifier.notifier_call = cros_ec_keyb_work; ckdev->dev = dev; dev_set_drvdata(&pdev->dev, ckdev); @@ -269,6 +289,8 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) input_set_capability(idev, EV_MSC, MSC_SCAN); input_set_drvdata(idev, ckdev); ckdev->idev = idev; + cros_ec_keyb_compute_valid_keys(ckdev); + err = input_register_device(ckdev->idev); if (err) { dev_err(dev, "cannot register input device\n"); @@ -326,10 +348,19 @@ static int cros_ec_keyb_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); +#ifdef CONFIG_OF +static const struct of_device_id cros_ec_keyb_of_match[] = { + { .compatible = "google,cros-ec-keyb" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match); +#endif + static struct platform_driver cros_ec_keyb_driver = { .probe = cros_ec_keyb_probe, .driver = { .name = "cros-ec-keyb", + .of_match_table = of_match_ptr(cros_ec_keyb_of_match), .pm = &cros_ec_keyb_pm_ops, }, }; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 8280cb16260b..20a99c368d16 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -531,8 +531,7 @@ static int imx_keypad_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int imx_kbd_suspend(struct device *dev) +static int __maybe_unused imx_kbd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_keypad *kbd = platform_get_drvdata(pdev); @@ -552,7 +551,7 @@ static int imx_kbd_suspend(struct device *dev) return 0; } -static int imx_kbd_resume(struct device *dev) +static int __maybe_unused imx_kbd_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct imx_keypad *kbd = platform_get_drvdata(pdev); @@ -575,7 +574,6 @@ err_clk: return ret; } -#endif static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 0b42118cbf8f..cb32e2b506b7 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -558,6 +558,12 @@ static ssize_t lm8323_pwm_store_time(struct device *dev, } static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time); +static struct attribute *lm8323_pwm_attrs[] = { + &dev_attr_time.attr, + NULL +}; +ATTRIBUTE_GROUPS(lm8323_pwm); + static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, const char *name) { @@ -580,16 +586,11 @@ static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, if (name) { pwm->cdev.name = name; pwm->cdev.brightness_set = lm8323_pwm_set_brightness; + pwm->cdev.groups = lm8323_pwm_groups; if (led_classdev_register(dev, &pwm->cdev) < 0) { dev_err(dev, "couldn't register PWM %d\n", id); return -1; } - if (device_create_file(pwm->cdev.dev, - &dev_attr_time) < 0) { - dev_err(dev, "couldn't register time attribute\n"); - led_classdev_unregister(&pwm->cdev); - return -1; - } pwm->enabled = true; } @@ -753,11 +754,8 @@ fail3: device_remove_file(&client->dev, &dev_attr_disable_kp); fail2: while (--pwm >= 0) - if (lm->pwm[pwm].enabled) { - device_remove_file(lm->pwm[pwm].cdev.dev, - &dev_attr_time); + if (lm->pwm[pwm].enabled) led_classdev_unregister(&lm->pwm[pwm].cdev); - } fail1: input_free_device(idev); kfree(lm); @@ -777,10 +775,8 @@ static int lm8323_remove(struct i2c_client *client) device_remove_file(&lm->client->dev, &dev_attr_disable_kp); for (i = 0; i < 3; i++) - if (lm->pwm[i].enabled) { - device_remove_file(lm->pwm[i].cdev.dev, &dev_attr_time); + if (lm->pwm[i].enabled) led_classdev_unregister(&lm->pwm[i].cdev); - } kfree(lm); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 8d2e19e81e1e..e651fa692afe 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -332,23 +332,24 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev, } if (pdata->clustered_irq > 0) { - err = request_irq(pdata->clustered_irq, + err = request_any_context_irq(pdata->clustered_irq, matrix_keypad_interrupt, pdata->clustered_irq_flags, "matrix-keypad", keypad); - if (err) { + if (err < 0) { dev_err(&pdev->dev, "Unable to acquire clustered interrupt\n"); goto err_free_rows; } } else { for (i = 0; i < pdata->num_row_gpios; i++) { - err = request_irq(gpio_to_irq(pdata->row_gpios[i]), + err = request_any_context_irq( + gpio_to_irq(pdata->row_gpios[i]), matrix_keypad_interrupt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "matrix-keypad", keypad); - if (err) { + if (err < 0) { dev_err(&pdev->dev, "Unable to acquire interrupt for GPIO line %i\n", pdata->row_gpios[i]); diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c index 430b54539720..faa6da53eba8 100644 --- a/drivers/input/keyboard/max7359_keypad.c +++ b/drivers/input/keyboard/max7359_keypad.c @@ -203,12 +203,17 @@ static int max7359_probe(struct i2c_client *client, dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret); - keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!keypad || !input_dev) { + keypad = devm_kzalloc(&client->dev, sizeof(struct max7359_keypad), + GFP_KERNEL); + if (!keypad) { dev_err(&client->dev, "failed to allocate memory\n"); - error = -ENOMEM; - goto failed_free_mem; + return -ENOMEM; + } + + input_dev = devm_input_allocate_device(&client->dev); + if (!input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + return -ENOMEM; } keypad->client = client; @@ -230,19 +235,20 @@ static int max7359_probe(struct i2c_client *client, max7359_build_keycode(keypad, keymap_data); - error = request_threaded_irq(client->irq, NULL, max7359_interrupt, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - client->name, keypad); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, + max7359_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, keypad); if (error) { dev_err(&client->dev, "failed to register interrupt\n"); - goto failed_free_mem; + return error; } /* Register the input device */ error = input_register_device(input_dev); if (error) { dev_err(&client->dev, "failed to register input device\n"); - goto failed_free_irq; + return error; } /* Initialize MAX7359 */ @@ -252,24 +258,6 @@ static int max7359_probe(struct i2c_client *client, device_init_wakeup(&client->dev, 1); return 0; - -failed_free_irq: - free_irq(client->irq, keypad); -failed_free_mem: - input_free_device(input_dev); - kfree(keypad); - return error; -} - -static int max7359_remove(struct i2c_client *client) -{ - struct max7359_keypad *keypad = i2c_get_clientdata(client); - - free_irq(client->irq, keypad); - input_unregister_device(keypad->input_dev); - kfree(keypad); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -313,7 +301,6 @@ static struct i2c_driver max7359_i2c_driver = { .pm = &max7359_pm, }, .probe = max7359_probe, - .remove = max7359_remove, .id_table = max7359_ids, }; diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c index 7b9b44158ad1..f8502bb29176 100644 --- a/drivers/input/keyboard/opencores-kbd.c +++ b/drivers/input/keyboard/opencores-kbd.c @@ -18,7 +18,6 @@ struct opencores_kbd { struct input_dev *input; - struct resource *addr_res; void __iomem *addr; int irq; unsigned short keycodes[128]; @@ -56,35 +55,25 @@ static int opencores_kbd_probe(struct platform_device *pdev) return -EINVAL; } - opencores_kbd = kzalloc(sizeof(*opencores_kbd), GFP_KERNEL); - input = input_allocate_device(); - if (!opencores_kbd || !input) { - dev_err(&pdev->dev, "failed to allocate device structures\n"); - error = -ENOMEM; - goto err_free_mem; - } - - opencores_kbd->addr_res = res; - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto err_free_mem; - } + opencores_kbd = devm_kzalloc(&pdev->dev, sizeof(*opencores_kbd), + GFP_KERNEL); + if (!opencores_kbd) + return -ENOMEM; - opencores_kbd->addr = ioremap(res->start, resource_size(res)); - if (!opencores_kbd->addr) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto err_rel_mem; + input = devm_input_allocate_device(&pdev->dev); + if (!input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; } opencores_kbd->input = input; - opencores_kbd->irq = irq; + + opencores_kbd->addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(opencores_kbd->addr)) + return PTR_ERR(opencores_kbd->addr); input->name = pdev->name; input->phys = "opencores-kbd/input0"; - input->dev.parent = &pdev->dev; input_set_drvdata(input, opencores_kbd); @@ -109,54 +98,27 @@ static int opencores_kbd_probe(struct platform_device *pdev) } __clear_bit(KEY_RESERVED, input->keybit); - error = request_irq(irq, &opencores_kbd_isr, - IRQF_TRIGGER_RISING, pdev->name, opencores_kbd); + error = devm_request_irq(&pdev->dev, irq, &opencores_kbd_isr, + IRQF_TRIGGER_RISING, + pdev->name, opencores_kbd); if (error) { dev_err(&pdev->dev, "unable to claim irq %d\n", irq); - goto err_unmap_mem; + return error; } error = input_register_device(input); if (error) { dev_err(&pdev->dev, "unable to register input device\n"); - goto err_free_irq; + return error; } platform_set_drvdata(pdev, opencores_kbd); return 0; - - err_free_irq: - free_irq(irq, opencores_kbd); - err_unmap_mem: - iounmap(opencores_kbd->addr); - err_rel_mem: - release_mem_region(res->start, resource_size(res)); - err_free_mem: - input_free_device(input); - kfree(opencores_kbd); - - return error; -} - -static int opencores_kbd_remove(struct platform_device *pdev) -{ - struct opencores_kbd *opencores_kbd = platform_get_drvdata(pdev); - - free_irq(opencores_kbd->irq, opencores_kbd); - - iounmap(opencores_kbd->addr); - release_mem_region(opencores_kbd->addr_res->start, - resource_size(opencores_kbd->addr_res)); - input_unregister_device(opencores_kbd->input); - kfree(opencores_kbd); - - return 0; } static struct platform_driver opencores_kbd_device_driver = { .probe = opencores_kbd_probe, - .remove = opencores_kbd_remove, .driver = { .name = "opencores-kbd", }, diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index c6727dda68f2..ef5e67fb567e 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -86,7 +86,7 @@ static const struct stmpe_keypad_variant stmpe_keypad_variants[] = { .max_cols = 8, .max_rows = 12, .col_gpios = 0x0000ff, /* GPIO 0 - 7*/ - .row_gpios = 0x1fef00, /* GPIO 8-14, 16-20 */ + .row_gpios = 0x1f7f00, /* GPIO 8-14, 16-20 */ }, [STMPE2403] = { .auto_increment = true, 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 tristate "M68k Beeper support" depends on M68K +config INPUT_MAX77693_HAPTIC + tristate "MAXIM MAX77693 haptic controller support" + depends on MFD_MAX77693 && PWM + select INPUT_FF_MEMLESS + help + This option enables support for the haptic controller on + MAXIM MAX77693 chip. + + To compile this driver as module, choose M here: the + module will be called max77693-haptic. + config INPUT_MAX8925_ONKEY tristate "MAX8925 ONKEY support" depends on MFD_MAX8925 @@ -451,6 +462,16 @@ config HP_SDC_RTC Say Y here if you want to support the built-in real time clock of the HP SDC controller. +config INPUT_PALMAS_PWRBUTTON + tristate "Palmas Power button Driver" + depends on MFD_PALMAS + help + Say Y here if you want to enable power key reporting via the + Palmas family of PMICs. + + To compile this driver as a module, choose M here. The module will + be called palmas_pwrbutton. + config INPUT_PCF50633_PMU tristate "PCF50633 PMU events" depends on MFD_PCF50633 @@ -676,4 +697,26 @@ config INPUT_SOC_BUTTON_ARRAY To compile this driver as a module, choose M here: the module will be called soc_button_array. +config INPUT_DRV260X_HAPTICS + tristate "TI DRV260X haptics support" + depends on INPUT && I2C && GPIOLIB + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y to enable support for the TI DRV260X haptics driver. + + To compile this driver as a module, choose M here: the + module will be called drv260x-haptics. + +config INPUT_DRV2667_HAPTICS + tristate "TI DRV2667 haptics support" + depends on INPUT && I2C + select INPUT_FF_MEMLESS + select REGMAP_I2C + help + Say Y to enable support for the TI DRV2667 haptics driver. + + To compile this driver as a module, choose M here: the + module will be called drv260x-haptics. + endif 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 obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o +obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o +obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o @@ -35,11 +37,13 @@ obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o +obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c new file mode 100644 index 000000000000..cab87f5ce6d3 --- /dev/null +++ b/drivers/input/misc/drv260x.c @@ -0,0 +1,741 @@ +/* + * DRV260X haptics driver family + * + * Author: Dan Murphy <dmurphy@ti.com> + * + * Copyright: (C) 2014 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +#include <dt-bindings/input/ti-drv260x.h> +#include <linux/platform_data/drv260x-pdata.h> + +#define DRV260X_STATUS 0x0 +#define DRV260X_MODE 0x1 +#define DRV260X_RT_PB_IN 0x2 +#define DRV260X_LIB_SEL 0x3 +#define DRV260X_WV_SEQ_1 0x4 +#define DRV260X_WV_SEQ_2 0x5 +#define DRV260X_WV_SEQ_3 0x6 +#define DRV260X_WV_SEQ_4 0x7 +#define DRV260X_WV_SEQ_5 0x8 +#define DRV260X_WV_SEQ_6 0x9 +#define DRV260X_WV_SEQ_7 0xa +#define DRV260X_WV_SEQ_8 0xb +#define DRV260X_GO 0xc +#define DRV260X_OVERDRIVE_OFF 0xd +#define DRV260X_SUSTAIN_P_OFF 0xe +#define DRV260X_SUSTAIN_N_OFF 0xf +#define DRV260X_BRAKE_OFF 0x10 +#define DRV260X_A_TO_V_CTRL 0x11 +#define DRV260X_A_TO_V_MIN_INPUT 0x12 +#define DRV260X_A_TO_V_MAX_INPUT 0x13 +#define DRV260X_A_TO_V_MIN_OUT 0x14 +#define DRV260X_A_TO_V_MAX_OUT 0x15 +#define DRV260X_RATED_VOLT 0x16 +#define DRV260X_OD_CLAMP_VOLT 0x17 +#define DRV260X_CAL_COMP 0x18 +#define DRV260X_CAL_BACK_EMF 0x19 +#define DRV260X_FEEDBACK_CTRL 0x1a +#define DRV260X_CTRL1 0x1b +#define DRV260X_CTRL2 0x1c +#define DRV260X_CTRL3 0x1d +#define DRV260X_CTRL4 0x1e +#define DRV260X_CTRL5 0x1f +#define DRV260X_LRA_LOOP_PERIOD 0x20 +#define DRV260X_VBAT_MON 0x21 +#define DRV260X_LRA_RES_PERIOD 0x22 +#define DRV260X_MAX_REG 0x23 + +#define DRV260X_GO_BIT 0x01 + +/* Library Selection */ +#define DRV260X_LIB_SEL_MASK 0x07 +#define DRV260X_LIB_SEL_RAM 0x0 +#define DRV260X_LIB_SEL_OD 0x1 +#define DRV260X_LIB_SEL_40_60 0x2 +#define DRV260X_LIB_SEL_60_80 0x3 +#define DRV260X_LIB_SEL_100_140 0x4 +#define DRV260X_LIB_SEL_140_PLUS 0x5 + +#define DRV260X_LIB_SEL_HIZ_MASK 0x10 +#define DRV260X_LIB_SEL_HIZ_EN 0x01 +#define DRV260X_LIB_SEL_HIZ_DIS 0 + +/* Mode register */ +#define DRV260X_STANDBY (1 << 6) +#define DRV260X_STANDBY_MASK 0x40 +#define DRV260X_INTERNAL_TRIGGER 0x00 +#define DRV260X_EXT_TRIGGER_EDGE 0x01 +#define DRV260X_EXT_TRIGGER_LEVEL 0x02 +#define DRV260X_PWM_ANALOG_IN 0x03 +#define DRV260X_AUDIOHAPTIC 0x04 +#define DRV260X_RT_PLAYBACK 0x05 +#define DRV260X_DIAGNOSTICS 0x06 +#define DRV260X_AUTO_CAL 0x07 + +/* Audio to Haptics Control */ +#define DRV260X_AUDIO_HAPTICS_PEAK_10MS (0 << 2) +#define DRV260X_AUDIO_HAPTICS_PEAK_20MS (1 << 2) +#define DRV260X_AUDIO_HAPTICS_PEAK_30MS (2 << 2) +#define DRV260X_AUDIO_HAPTICS_PEAK_40MS (3 << 2) + +#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ 0x00 +#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ 0x01 +#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ 0x02 +#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ 0x03 + +/* Min/Max Input/Output Voltages */ +#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT 0x19 +#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT 0x64 +#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT 0x19 +#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT 0xFF + +/* Feedback register */ +#define DRV260X_FB_REG_ERM_MODE 0x7f +#define DRV260X_FB_REG_LRA_MODE (1 << 7) + +#define DRV260X_BRAKE_FACTOR_MASK 0x1f +#define DRV260X_BRAKE_FACTOR_2X (1 << 0) +#define DRV260X_BRAKE_FACTOR_3X (2 << 4) +#define DRV260X_BRAKE_FACTOR_4X (3 << 4) +#define DRV260X_BRAKE_FACTOR_6X (4 << 4) +#define DRV260X_BRAKE_FACTOR_8X (5 << 4) +#define DRV260X_BRAKE_FACTOR_16 (6 << 4) +#define DRV260X_BRAKE_FACTOR_DIS (7 << 4) + +#define DRV260X_LOOP_GAIN_LOW 0xf3 +#define DRV260X_LOOP_GAIN_MED (1 << 2) +#define DRV260X_LOOP_GAIN_HIGH (2 << 2) +#define DRV260X_LOOP_GAIN_VERY_HIGH (3 << 2) + +#define DRV260X_BEMF_GAIN_0 0xfc +#define DRV260X_BEMF_GAIN_1 (1 << 0) +#define DRV260X_BEMF_GAIN_2 (2 << 0) +#define DRV260X_BEMF_GAIN_3 (3 << 0) + +/* Control 1 register */ +#define DRV260X_AC_CPLE_EN (1 << 5) +#define DRV260X_STARTUP_BOOST (1 << 7) + +/* Control 2 register */ + +#define DRV260X_IDISS_TIME_45 0 +#define DRV260X_IDISS_TIME_75 (1 << 0) +#define DRV260X_IDISS_TIME_150 (1 << 1) +#define DRV260X_IDISS_TIME_225 0x03 + +#define DRV260X_BLANK_TIME_45 (0 << 2) +#define DRV260X_BLANK_TIME_75 (1 << 2) +#define DRV260X_BLANK_TIME_150 (2 << 2) +#define DRV260X_BLANK_TIME_225 (3 << 2) + +#define DRV260X_SAMP_TIME_150 (0 << 4) +#define DRV260X_SAMP_TIME_200 (1 << 4) +#define DRV260X_SAMP_TIME_250 (2 << 4) +#define DRV260X_SAMP_TIME_300 (3 << 4) + +#define DRV260X_BRAKE_STABILIZER (1 << 6) +#define DRV260X_UNIDIR_IN (0 << 7) +#define DRV260X_BIDIR_IN (1 << 7) + +/* Control 3 Register */ +#define DRV260X_LRA_OPEN_LOOP (1 << 0) +#define DRV260X_ANANLOG_IN (1 << 1) +#define DRV260X_LRA_DRV_MODE (1 << 2) +#define DRV260X_RTP_UNSIGNED_DATA (1 << 3) +#define DRV260X_SUPPLY_COMP_DIS (1 << 4) +#define DRV260X_ERM_OPEN_LOOP (1 << 5) +#define DRV260X_NG_THRESH_0 (0 << 6) +#define DRV260X_NG_THRESH_2 (1 << 6) +#define DRV260X_NG_THRESH_4 (2 << 6) +#define DRV260X_NG_THRESH_8 (3 << 6) + +/* Control 4 Register */ +#define DRV260X_AUTOCAL_TIME_150MS (0 << 4) +#define DRV260X_AUTOCAL_TIME_250MS (1 << 4) +#define DRV260X_AUTOCAL_TIME_500MS (2 << 4) +#define DRV260X_AUTOCAL_TIME_1000MS (3 << 4) + +/** + * struct drv260x_data - + * @input_dev - Pointer to the input device + * @client - Pointer to the I2C client + * @regmap - Register map of the device + * @work - Work item used to off load the enable/disable of the vibration + * @enable_gpio - Pointer to the gpio used for enable/disabling + * @regulator - Pointer to the regulator for the IC + * @magnitude - Magnitude of the vibration event + * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA) + * @library - The vibration library to be used + * @rated_voltage - The rated_voltage of the actuator + * @overdriver_voltage - The over drive voltage of the actuator +**/ +struct drv260x_data { + struct input_dev *input_dev; + struct i2c_client *client; + struct regmap *regmap; + struct work_struct work; + struct gpio_desc *enable_gpio; + struct regulator *regulator; + u32 magnitude; + u32 mode; + u32 library; + int rated_voltage; + int overdrive_voltage; +}; + +static struct reg_default drv260x_reg_defs[] = { + { DRV260X_STATUS, 0xe0 }, + { DRV260X_MODE, 0x40 }, + { DRV260X_RT_PB_IN, 0x00 }, + { DRV260X_LIB_SEL, 0x00 }, + { DRV260X_WV_SEQ_1, 0x01 }, + { DRV260X_WV_SEQ_2, 0x00 }, + { DRV260X_WV_SEQ_3, 0x00 }, + { DRV260X_WV_SEQ_4, 0x00 }, + { DRV260X_WV_SEQ_5, 0x00 }, + { DRV260X_WV_SEQ_6, 0x00 }, + { DRV260X_WV_SEQ_7, 0x00 }, + { DRV260X_WV_SEQ_8, 0x00 }, + { DRV260X_GO, 0x00 }, + { DRV260X_OVERDRIVE_OFF, 0x00 }, + { DRV260X_SUSTAIN_P_OFF, 0x00 }, + { DRV260X_SUSTAIN_N_OFF, 0x00 }, + { DRV260X_BRAKE_OFF, 0x00 }, + { DRV260X_A_TO_V_CTRL, 0x05 }, + { DRV260X_A_TO_V_MIN_INPUT, 0x19 }, + { DRV260X_A_TO_V_MAX_INPUT, 0xff }, + { DRV260X_A_TO_V_MIN_OUT, 0x19 }, + { DRV260X_A_TO_V_MAX_OUT, 0xff }, + { DRV260X_RATED_VOLT, 0x3e }, + { DRV260X_OD_CLAMP_VOLT, 0x8c }, + { DRV260X_CAL_COMP, 0x0c }, + { DRV260X_CAL_BACK_EMF, 0x6c }, + { DRV260X_FEEDBACK_CTRL, 0x36 }, + { DRV260X_CTRL1, 0x93 }, + { DRV260X_CTRL2, 0xfa }, + { DRV260X_CTRL3, 0xa0 }, + { DRV260X_CTRL4, 0x20 }, + { DRV260X_CTRL5, 0x80 }, + { DRV260X_LRA_LOOP_PERIOD, 0x33 }, + { DRV260X_VBAT_MON, 0x00 }, + { DRV260X_LRA_RES_PERIOD, 0x00 }, +}; + +#define DRV260X_DEF_RATED_VOLT 0x90 +#define DRV260X_DEF_OD_CLAMP_VOLT 0x90 + +/** + * Rated and Overdriver Voltages: + * Calculated using the formula r = v * 255 / 5.6 + * where r is what will be written to the register + * and v is the rated or overdriver voltage of the actuator + **/ +static int drv260x_calculate_voltage(unsigned int voltage) +{ + return (voltage * 255 / 5600); +} + +static void drv260x_worker(struct work_struct *work) +{ + struct drv260x_data *haptics = container_of(work, struct drv260x_data, work); + int error; + + gpiod_set_value(haptics->enable_gpio, 1); + /* Data sheet says to wait 250us before trying to communicate */ + udelay(250); + + error = regmap_write(haptics->regmap, + DRV260X_MODE, DRV260X_RT_PLAYBACK); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write set mode: %d\n", error); + } else { + error = regmap_write(haptics->regmap, + DRV260X_RT_PB_IN, haptics->magnitude); + if (error) + dev_err(&haptics->client->dev, + "Failed to set magnitude: %d\n", error); + } +} + +static int drv260x_haptics_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct drv260x_data *haptics = input_get_drvdata(input); + + haptics->mode = DRV260X_LRA_NO_CAL_MODE; + + if (effect->u.rumble.strong_magnitude > 0) + haptics->magnitude = effect->u.rumble.strong_magnitude; + else if (effect->u.rumble.weak_magnitude > 0) + haptics->magnitude = effect->u.rumble.weak_magnitude; + else + haptics->magnitude = 0; + + schedule_work(&haptics->work); + + return 0; +} + +static void drv260x_close(struct input_dev *input) +{ + struct drv260x_data *haptics = input_get_drvdata(input); + int error; + + cancel_work_sync(&haptics->work); + + error = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY); + if (error) + dev_err(&haptics->client->dev, + "Failed to enter standby mode: %d\n", error); + + gpiod_set_value(haptics->enable_gpio, 0); +} + +static const struct reg_default drv260x_lra_cal_regs[] = { + { DRV260X_MODE, DRV260X_AUTO_CAL }, + { DRV260X_CTRL3, DRV260X_NG_THRESH_2 }, + { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | + DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH }, +}; + +static const struct reg_default drv260x_lra_init_regs[] = { + { DRV260X_MODE, DRV260X_RT_PLAYBACK }, + { DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | + DRV260X_AUDIO_HAPTICS_FILTER_125HZ }, + { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, + { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, + { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT }, + { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT }, + { DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | + DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED | + DRV260X_BEMF_GAIN_3 }, + { DRV260X_CTRL1, DRV260X_STARTUP_BOOST }, + { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 }, + { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN }, + { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, +}; + +static const struct reg_default drv260x_erm_cal_regs[] = { + { DRV260X_MODE, DRV260X_AUTO_CAL }, + { DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT }, + { DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT }, + { DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT }, + { DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT }, + { DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X | + DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 }, + { DRV260X_CTRL1, DRV260X_STARTUP_BOOST }, + { DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 | + DRV260X_IDISS_TIME_75 }, + { DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP }, + { DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS }, +}; + +static int drv260x_init(struct drv260x_data *haptics) +{ + int error; + unsigned int cal_buf; + + error = regmap_write(haptics->regmap, + DRV260X_RATED_VOLT, haptics->rated_voltage); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write DRV260X_RATED_VOLT register: %d\n", + error); + return error; + } + + error = regmap_write(haptics->regmap, + DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write DRV260X_OD_CLAMP_VOLT register: %d\n", + error); + return error; + } + + switch (haptics->mode) { + case DRV260X_LRA_MODE: + error = regmap_register_patch(haptics->regmap, + drv260x_lra_cal_regs, + ARRAY_SIZE(drv260x_lra_cal_regs)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write LRA calibration registers: %d\n", + error); + return error; + } + + break; + + case DRV260X_ERM_MODE: + error = regmap_register_patch(haptics->regmap, + drv260x_erm_cal_regs, + ARRAY_SIZE(drv260x_erm_cal_regs)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write ERM calibration registers: %d\n", + error); + return error; + } + + error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL, + DRV260X_LIB_SEL_MASK, + haptics->library); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write DRV260X_LIB_SEL register: %d\n", + error); + return error; + } + + break; + + default: + error = regmap_register_patch(haptics->regmap, + drv260x_lra_init_regs, + ARRAY_SIZE(drv260x_lra_init_regs)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write LRA init registers: %d\n", + error); + return error; + } + + error = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL, + DRV260X_LIB_SEL_MASK, + haptics->library); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write DRV260X_LIB_SEL register: %d\n", + error); + return error; + } + + /* No need to set GO bit here */ + return 0; + } + + error = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write GO register: %d\n", + error); + return error; + } + + do { + error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf); + if (error) { + dev_err(&haptics->client->dev, + "Failed to read GO register: %d\n", + error); + return error; + } + } while (cal_buf == DRV260X_GO_BIT); + + return 0; +} + +static const struct regmap_config drv260x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DRV260X_MAX_REG, + .reg_defaults = drv260x_reg_defs, + .num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs), + .cache_type = REGCACHE_NONE, +}; + +#ifdef CONFIG_OF +static int drv260x_parse_dt(struct device *dev, + struct drv260x_data *haptics) +{ + struct device_node *np = dev->of_node; + unsigned int voltage; + int error; + + error = of_property_read_u32(np, "mode", &haptics->mode); + if (error) { + dev_err(dev, "%s: No entry for mode\n", __func__); + return error; + } + + error = of_property_read_u32(np, "library-sel", &haptics->library); + if (error) { + dev_err(dev, "%s: No entry for library selection\n", + __func__); + return error; + } + + error = of_property_read_u32(np, "vib-rated-mv", &voltage); + if (!error) + haptics->rated_voltage = drv260x_calculate_voltage(voltage); + + + error = of_property_read_u32(np, "vib-overdrive-mv", &voltage); + if (!error) + haptics->overdrive_voltage = drv260x_calculate_voltage(voltage); + + return 0; +} +#else +static inline int drv260x_parse_dt(struct device *dev, + struct drv260x_data *haptics) +{ + dev_err(dev, "no platform data defined\n"); + + return -EINVAL; +} +#endif + +static int drv260x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct drv260x_platform_data *pdata = dev_get_platdata(&client->dev); + struct drv260x_data *haptics; + int error; + + haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT; + haptics->rated_voltage = DRV260X_DEF_RATED_VOLT; + + if (pdata) { + haptics->mode = pdata->mode; + haptics->library = pdata->library_selection; + if (pdata->vib_overdrive_voltage) + haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage); + if (pdata->vib_rated_voltage) + haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage); + } else if (client->dev.of_node) { + error = drv260x_parse_dt(&client->dev, haptics); + if (error) + return error; + } else { + dev_err(&client->dev, "Platform data not set\n"); + return -ENODEV; + } + + + if (haptics->mode < DRV260X_LRA_MODE || + haptics->mode > DRV260X_ERM_MODE) { + dev_err(&client->dev, + "Vibrator mode is invalid: %i\n", + haptics->mode); + return -EINVAL; + } + + if (haptics->library < DRV260X_LIB_EMPTY || + haptics->library > DRV260X_ERM_LIB_F) { + dev_err(&client->dev, + "Library value is invalid: %i\n", haptics->library); + return -EINVAL; + } + + if (haptics->mode == DRV260X_LRA_MODE && + haptics->library != DRV260X_LIB_EMPTY && + haptics->library != DRV260X_LIB_LRA) { + dev_err(&client->dev, + "LRA Mode with ERM Library mismatch\n"); + return -EINVAL; + } + + if (haptics->mode == DRV260X_ERM_MODE && + (haptics->library == DRV260X_LIB_EMPTY || + haptics->library == DRV260X_LIB_LRA)) { + dev_err(&client->dev, + "ERM Mode with LRA Library mismatch\n"); + return -EINVAL; + } + + haptics->regulator = devm_regulator_get(&client->dev, "vbat"); + if (IS_ERR(haptics->regulator)) { + error = PTR_ERR(haptics->regulator); + dev_err(&client->dev, + "unable to get regulator, error: %d\n", error); + return error; + } + + haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable"); + if (IS_ERR(haptics->enable_gpio)) { + error = PTR_ERR(haptics->enable_gpio); + if (error != -ENOENT && error != -ENOSYS) + return error; + haptics->enable_gpio = NULL; + } else { + gpiod_direction_output(haptics->enable_gpio, 1); + } + + haptics->input_dev = devm_input_allocate_device(&client->dev); + if (!haptics->input_dev) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + haptics->input_dev->name = "drv260x:haptics"; + haptics->input_dev->dev.parent = client->dev.parent; + haptics->input_dev->close = drv260x_close; + input_set_drvdata(haptics->input_dev, haptics); + input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptics->input_dev, NULL, + drv260x_haptics_play); + if (error) { + dev_err(&client->dev, "input_ff_create() failed: %d\n", + error); + return error; + } + + INIT_WORK(&haptics->work, drv260x_worker); + + haptics->client = client; + i2c_set_clientdata(client, haptics); + + haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config); + if (IS_ERR(haptics->regmap)) { + error = PTR_ERR(haptics->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + error = drv260x_init(haptics); + if (error) { + dev_err(&client->dev, "Device init failed: %d\n", error); + return error; + } + + error = input_register_device(haptics->input_dev); + if (error) { + dev_err(&client->dev, "couldn't register input device: %d\n", + error); + return error; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int drv260x_suspend(struct device *dev) +{ + struct drv260x_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regmap_update_bits(haptics->regmap, + DRV260X_MODE, + DRV260X_STANDBY_MASK, + DRV260X_STANDBY); + if (ret) { + dev_err(dev, "Failed to set standby mode\n"); + goto out; + } + + gpiod_set_value(haptics->enable_gpio, 0); + + ret = regulator_disable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to disable regulator\n"); + regmap_update_bits(haptics->regmap, + DRV260X_MODE, + DRV260X_STANDBY_MASK, 0); + } + } +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} + +static int drv260x_resume(struct device *dev) +{ + struct drv260x_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regulator_enable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator\n"); + goto out; + } + + ret = regmap_update_bits(haptics->regmap, + DRV260X_MODE, + DRV260X_STANDBY_MASK, 0); + if (ret) { + dev_err(dev, "Failed to unset standby mode\n"); + regulator_disable(haptics->regulator); + goto out; + } + + gpiod_set_value(haptics->enable_gpio, 1); + } + +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume); + +static const struct i2c_device_id drv260x_id[] = { + { "drv2605l", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, drv260x_id); + +#ifdef CONFIG_OF +static const struct of_device_id drv260x_of_match[] = { + { .compatible = "ti,drv2604", }, + { .compatible = "ti,drv2604l", }, + { .compatible = "ti,drv2605", }, + { .compatible = "ti,drv2605l", }, + { } +}; +MODULE_DEVICE_TABLE(of, drv260x_of_match); +#endif + +static struct i2c_driver drv260x_driver = { + .probe = drv260x_probe, + .driver = { + .name = "drv260x-haptics", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(drv260x_of_match), + .pm = &drv260x_pm_ops, + }, + .id_table = drv260x_id, +}; +module_i2c_driver(drv260x_driver); + +MODULE_ALIAS("platform:drv260x-haptics"); +MODULE_DESCRIPTION("TI DRV260x haptics driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c new file mode 100644 index 000000000000..0f437581cc04 --- /dev/null +++ b/drivers/input/misc/drv2667.c @@ -0,0 +1,500 @@ +/* + * DRV2667 haptics driver family + * + * Author: Dan Murphy <dmurphy@ti.com> + * + * Copyright: (C) 2014 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/regulator/consumer.h> + +/* Contol registers */ +#define DRV2667_STATUS 0x00 +#define DRV2667_CTRL_1 0x01 +#define DRV2667_CTRL_2 0x02 +/* Waveform sequencer */ +#define DRV2667_WV_SEQ_0 0x03 +#define DRV2667_WV_SEQ_1 0x04 +#define DRV2667_WV_SEQ_2 0x05 +#define DRV2667_WV_SEQ_3 0x06 +#define DRV2667_WV_SEQ_4 0x07 +#define DRV2667_WV_SEQ_5 0x08 +#define DRV2667_WV_SEQ_6 0x09 +#define DRV2667_WV_SEQ_7 0x0A +#define DRV2667_FIFO 0x0B +#define DRV2667_PAGE 0xFF +#define DRV2667_MAX_REG DRV2667_PAGE + +#define DRV2667_PAGE_0 0x00 +#define DRV2667_PAGE_1 0x01 +#define DRV2667_PAGE_2 0x02 +#define DRV2667_PAGE_3 0x03 +#define DRV2667_PAGE_4 0x04 +#define DRV2667_PAGE_5 0x05 +#define DRV2667_PAGE_6 0x06 +#define DRV2667_PAGE_7 0x07 +#define DRV2667_PAGE_8 0x08 + +/* RAM fields */ +#define DRV2667_RAM_HDR_SZ 0x0 +/* RAM Header addresses */ +#define DRV2667_RAM_START_HI 0x01 +#define DRV2667_RAM_START_LO 0x02 +#define DRV2667_RAM_STOP_HI 0x03 +#define DRV2667_RAM_STOP_LO 0x04 +#define DRV2667_RAM_REPEAT_CT 0x05 +/* RAM data addresses */ +#define DRV2667_RAM_AMP 0x06 +#define DRV2667_RAM_FREQ 0x07 +#define DRV2667_RAM_DURATION 0x08 +#define DRV2667_RAM_ENVELOPE 0x09 + +/* Control 1 Register */ +#define DRV2667_25_VPP_GAIN 0x00 +#define DRV2667_50_VPP_GAIN 0x01 +#define DRV2667_75_VPP_GAIN 0x02 +#define DRV2667_100_VPP_GAIN 0x03 +#define DRV2667_DIGITAL_IN 0xfc +#define DRV2667_ANALOG_IN (1 << 2) + +/* Control 2 Register */ +#define DRV2667_GO (1 << 0) +#define DRV2667_STANDBY (1 << 6) +#define DRV2667_DEV_RST (1 << 7) + +/* RAM Envelope settings */ +#define DRV2667_NO_ENV 0x00 +#define DRV2667_32_MS_ENV 0x01 +#define DRV2667_64_MS_ENV 0x02 +#define DRV2667_96_MS_ENV 0x03 +#define DRV2667_128_MS_ENV 0x04 +#define DRV2667_160_MS_ENV 0x05 +#define DRV2667_192_MS_ENV 0x06 +#define DRV2667_224_MS_ENV 0x07 +#define DRV2667_256_MS_ENV 0x08 +#define DRV2667_512_MS_ENV 0x09 +#define DRV2667_768_MS_ENV 0x0a +#define DRV2667_1024_MS_ENV 0x0b +#define DRV2667_1280_MS_ENV 0x0c +#define DRV2667_1536_MS_ENV 0x0d +#define DRV2667_1792_MS_ENV 0x0e +#define DRV2667_2048_MS_ENV 0x0f + +/** + * struct drv2667_data - + * @input_dev - Pointer to the input device + * @client - Pointer to the I2C client + * @regmap - Register map of the device + * @work - Work item used to off load the enable/disable of the vibration + * @regulator - Pointer to the regulator for the IC + * @magnitude - Magnitude of the vibration event +**/ +struct drv2667_data { + struct input_dev *input_dev; + struct i2c_client *client; + struct regmap *regmap; + struct work_struct work; + struct regulator *regulator; + u32 page; + u32 magnitude; + u32 frequency; +}; + +static struct reg_default drv2667_reg_defs[] = { + { DRV2667_STATUS, 0x02 }, + { DRV2667_CTRL_1, 0x28 }, + { DRV2667_CTRL_2, 0x40 }, + { DRV2667_WV_SEQ_0, 0x00 }, + { DRV2667_WV_SEQ_1, 0x00 }, + { DRV2667_WV_SEQ_2, 0x00 }, + { DRV2667_WV_SEQ_3, 0x00 }, + { DRV2667_WV_SEQ_4, 0x00 }, + { DRV2667_WV_SEQ_5, 0x00 }, + { DRV2667_WV_SEQ_6, 0x00 }, + { DRV2667_WV_SEQ_7, 0x00 }, + { DRV2667_FIFO, 0x00 }, + { DRV2667_PAGE, 0x00 }, +}; + +static int drv2667_set_waveform_freq(struct drv2667_data *haptics) +{ + unsigned int read_buf; + int freq; + int error; + + /* Per the data sheet: + * Sinusoid Frequency (Hz) = 7.8125 x Frequency + */ + freq = (haptics->frequency * 1000) / 78125; + if (freq <= 0) { + dev_err(&haptics->client->dev, + "ERROR: Frequency calculated to %i\n", freq); + return -EINVAL; + } + + error = regmap_read(haptics->regmap, DRV2667_PAGE, &read_buf); + if (error) { + dev_err(&haptics->client->dev, + "Failed to read the page number: %d\n", error); + return -EIO; + } + + if (read_buf == DRV2667_PAGE_0 || + haptics->page != read_buf) { + error = regmap_write(haptics->regmap, + DRV2667_PAGE, haptics->page); + if (error) { + dev_err(&haptics->client->dev, + "Failed to set the page: %d\n", error); + return -EIO; + } + } + + error = regmap_write(haptics->regmap, DRV2667_RAM_FREQ, freq); + if (error) + dev_err(&haptics->client->dev, + "Failed to set the frequency: %d\n", error); + + /* Reset back to original page */ + if (read_buf == DRV2667_PAGE_0 || + haptics->page != read_buf) { + error = regmap_write(haptics->regmap, DRV2667_PAGE, read_buf); + if (error) { + dev_err(&haptics->client->dev, + "Failed to set the page: %d\n", error); + return -EIO; + } + } + + return error; +} + +static void drv2667_worker(struct work_struct *work) +{ + struct drv2667_data *haptics = container_of(work, struct drv2667_data, work); + int error; + + if (haptics->magnitude) { + error = regmap_write(haptics->regmap, + DRV2667_PAGE, haptics->page); + if (error) { + dev_err(&haptics->client->dev, + "Failed to set the page: %d\n", error); + return; + } + + error = regmap_write(haptics->regmap, DRV2667_RAM_AMP, + haptics->magnitude); + if (error) { + dev_err(&haptics->client->dev, + "Failed to set the amplitude: %d\n", error); + return; + } + + error = regmap_write(haptics->regmap, + DRV2667_PAGE, DRV2667_PAGE_0); + if (error) { + dev_err(&haptics->client->dev, + "Failed to set the page: %d\n", error); + return; + } + + error = regmap_write(haptics->regmap, + DRV2667_CTRL_2, DRV2667_GO); + if (error) { + dev_err(&haptics->client->dev, + "Failed to set the GO bit: %d\n", error); + } + } else { + error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, + DRV2667_GO, 0); + if (error) { + dev_err(&haptics->client->dev, + "Failed to unset the GO bit: %d\n", error); + } + } +} + +static int drv2667_haptics_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct drv2667_data *haptics = input_get_drvdata(input); + + if (effect->u.rumble.strong_magnitude > 0) + haptics->magnitude = effect->u.rumble.strong_magnitude; + else if (effect->u.rumble.weak_magnitude > 0) + haptics->magnitude = effect->u.rumble.weak_magnitude; + else + haptics->magnitude = 0; + + schedule_work(&haptics->work); + + return 0; +} + +static void drv2667_close(struct input_dev *input) +{ + struct drv2667_data *haptics = input_get_drvdata(input); + int error; + + cancel_work_sync(&haptics->work); + + error = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, + DRV2667_STANDBY, 1); + if (error) + dev_err(&haptics->client->dev, + "Failed to enter standby mode: %d\n", error); +} + +static const struct reg_default drv2667_init_regs[] = { + { DRV2667_CTRL_2, 0 }, + { DRV2667_CTRL_1, DRV2667_25_VPP_GAIN }, + { DRV2667_WV_SEQ_0, 1 }, + { DRV2667_WV_SEQ_1, 0 } +}; + +static const struct reg_default drv2667_page1_init[] = { + { DRV2667_RAM_HDR_SZ, 0x05 }, + { DRV2667_RAM_START_HI, 0x80 }, + { DRV2667_RAM_START_LO, 0x06 }, + { DRV2667_RAM_STOP_HI, 0x00 }, + { DRV2667_RAM_STOP_LO, 0x09 }, + { DRV2667_RAM_REPEAT_CT, 0 }, + { DRV2667_RAM_DURATION, 0x05 }, + { DRV2667_RAM_ENVELOPE, DRV2667_NO_ENV }, + { DRV2667_RAM_AMP, 0x60 }, +}; + +static int drv2667_init(struct drv2667_data *haptics) +{ + int error; + + /* Set default haptic frequency to 195Hz on Page 1*/ + haptics->frequency = 195; + haptics->page = DRV2667_PAGE_1; + + error = regmap_register_patch(haptics->regmap, + drv2667_init_regs, + ARRAY_SIZE(drv2667_init_regs)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write init registers: %d\n", + error); + return error; + } + + error = regmap_write(haptics->regmap, DRV2667_PAGE, haptics->page); + if (error) { + dev_err(&haptics->client->dev, "Failed to set page: %d\n", + error); + goto error_out; + } + + error = drv2667_set_waveform_freq(haptics); + if (error) + goto error_page; + + error = regmap_register_patch(haptics->regmap, + drv2667_page1_init, + ARRAY_SIZE(drv2667_page1_init)); + if (error) { + dev_err(&haptics->client->dev, + "Failed to write page registers: %d\n", + error); + return error; + } + + error = regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0); + return error; + +error_page: + regmap_write(haptics->regmap, DRV2667_PAGE, DRV2667_PAGE_0); +error_out: + return error; +} + +static const struct regmap_config drv2667_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DRV2667_MAX_REG, + .reg_defaults = drv2667_reg_defs, + .num_reg_defaults = ARRAY_SIZE(drv2667_reg_defs), + .cache_type = REGCACHE_NONE, +}; + +static int drv2667_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct drv2667_data *haptics; + int error; + + haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->regulator = devm_regulator_get(&client->dev, "vbat"); + if (IS_ERR(haptics->regulator)) { + error = PTR_ERR(haptics->regulator); + dev_err(&client->dev, + "unable to get regulator, error: %d\n", error); + return error; + } + + haptics->input_dev = devm_input_allocate_device(&client->dev); + if (!haptics->input_dev) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + haptics->input_dev->name = "drv2667:haptics"; + haptics->input_dev->dev.parent = client->dev.parent; + haptics->input_dev->close = drv2667_close; + input_set_drvdata(haptics->input_dev, haptics); + input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptics->input_dev, NULL, + drv2667_haptics_play); + if (error) { + dev_err(&client->dev, "input_ff_create() failed: %d\n", + error); + return error; + } + + INIT_WORK(&haptics->work, drv2667_worker); + + haptics->client = client; + i2c_set_clientdata(client, haptics); + + haptics->regmap = devm_regmap_init_i2c(client, &drv2667_regmap_config); + if (IS_ERR(haptics->regmap)) { + error = PTR_ERR(haptics->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + error = drv2667_init(haptics); + if (error) { + dev_err(&client->dev, "Device init failed: %d\n", error); + return error; + } + + error = input_register_device(haptics->input_dev); + if (error) { + dev_err(&client->dev, "couldn't register input device: %d\n", + error); + return error; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int drv2667_suspend(struct device *dev) +{ + struct drv2667_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, + DRV2667_STANDBY, 1); + if (ret) { + dev_err(dev, "Failed to set standby mode\n"); + regulator_disable(haptics->regulator); + goto out; + } + + ret = regulator_disable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to disable regulator\n"); + regmap_update_bits(haptics->regmap, + DRV2667_CTRL_2, + DRV2667_STANDBY, 0); + } + } +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} + +static int drv2667_resume(struct device *dev) +{ + struct drv2667_data *haptics = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&haptics->input_dev->mutex); + + if (haptics->input_dev->users) { + ret = regulator_enable(haptics->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator\n"); + goto out; + } + + ret = regmap_update_bits(haptics->regmap, DRV2667_CTRL_2, + DRV2667_STANDBY, 0); + if (ret) { + dev_err(dev, "Failed to unset standby mode\n"); + regulator_disable(haptics->regulator); + goto out; + } + + } + +out: + mutex_unlock(&haptics->input_dev->mutex); + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(drv2667_pm_ops, drv2667_suspend, drv2667_resume); + +static const struct i2c_device_id drv2667_id[] = { + { "drv2667", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, drv2667_id); + +#ifdef CONFIG_OF +static const struct of_device_id drv2667_of_match[] = { + { .compatible = "ti,drv2667", }, + { } +}; +MODULE_DEVICE_TABLE(of, drv2667_of_match); +#endif + +static struct i2c_driver drv2667_driver = { + .probe = drv2667_probe, + .driver = { + .name = "drv2667-haptics", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(drv2667_of_match), + .pm = &drv2667_pm_ops, + }, + .id_table = drv2667_id, +}; +module_i2c_driver(drv2667_driver); + +MODULE_ALIAS("platform:drv2667-haptics"); +MODULE_DESCRIPTION("TI DRV2667 haptics driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 719410feb84b..afed8e2b2f94 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1381,7 +1381,7 @@ static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev, pcu->ofn_reg_addr = value; mutex_unlock(&pcu->cmd_mutex); - return error ?: count; + return count; } static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR, diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c index 01f3b5b300f3..a3fe4a990cc9 100644 --- a/drivers/input/misc/keyspan_remote.c +++ b/drivers/input/misc/keyspan_remote.c @@ -392,7 +392,6 @@ static void keyspan_irq_recv(struct urb *urb) default: goto resubmit; - break; } if (debug) diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c new file mode 100644 index 000000000000..ef6a9d650d69 --- /dev/null +++ b/drivers/input/misc/max77693-haptic.c @@ -0,0 +1,356 @@ +/* + * MAXIM MAX77693 Haptic device driver + * + * Copyright (C) 2014 Samsung Electronics + * Jaewon Kim <jaewon02.kim@samsung.com> + * + * This program is not provided / owned by Maxim Integrated Products. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/regulator/consumer.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> + +#define MAX_MAGNITUDE_SHIFT 16 + +enum max77693_haptic_motor_type { + MAX77693_HAPTIC_ERM = 0, + MAX77693_HAPTIC_LRA, +}; + +enum max77693_haptic_pulse_mode { + MAX77693_HAPTIC_EXTERNAL_MODE = 0, + MAX77693_HAPTIC_INTERNAL_MODE, +}; + +enum max77693_haptic_pwm_divisor { + MAX77693_HAPTIC_PWM_DIVISOR_32 = 0, + MAX77693_HAPTIC_PWM_DIVISOR_64, + MAX77693_HAPTIC_PWM_DIVISOR_128, + MAX77693_HAPTIC_PWM_DIVISOR_256, +}; + +struct max77693_haptic { + struct regmap *regmap_pmic; + struct regmap *regmap_haptic; + struct device *dev; + struct input_dev *input_dev; + struct pwm_device *pwm_dev; + struct regulator *motor_reg; + + bool enabled; + bool suspend_state; + unsigned int magnitude; + unsigned int pwm_duty; + enum max77693_haptic_motor_type type; + enum max77693_haptic_pulse_mode mode; + enum max77693_haptic_pwm_divisor pwm_divisor; + + struct work_struct work; +}; + +static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic) +{ + int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2; + int error; + + error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period); + if (error) { + dev_err(haptic->dev, "failed to configure pwm: %d\n", error); + return error; + } + + return 0; +} + +static int max77693_haptic_configure(struct max77693_haptic *haptic, + bool enable) +{ + unsigned int value; + int error; + + value = ((haptic->type << MAX77693_CONFIG2_MODE) | + (enable << MAX77693_CONFIG2_MEN) | + (haptic->mode << MAX77693_CONFIG2_HTYP) | + (haptic->pwm_divisor)); + + error = regmap_write(haptic->regmap_haptic, + MAX77693_HAPTIC_REG_CONFIG2, value); + if (error) { + dev_err(haptic->dev, + "failed to update haptic config: %d\n", error); + return error; + } + + return 0; +} + +static int max77693_haptic_lowsys(struct max77693_haptic *haptic, bool enable) +{ + int error; + + error = regmap_update_bits(haptic->regmap_pmic, + MAX77693_PMIC_REG_LSCNFG, + MAX77693_PMIC_LOW_SYS_MASK, + enable << MAX77693_PMIC_LOW_SYS_SHIFT); + if (error) { + dev_err(haptic->dev, "cannot update pmic regmap: %d\n", error); + return error; + } + + return 0; +} + +static void max77693_haptic_enable(struct max77693_haptic *haptic) +{ + int error; + + if (haptic->enabled) + return; + + error = pwm_enable(haptic->pwm_dev); + if (error) { + dev_err(haptic->dev, + "failed to enable haptic pwm device: %d\n", error); + return; + } + + error = max77693_haptic_lowsys(haptic, true); + if (error) + goto err_enable_lowsys; + + error = max77693_haptic_configure(haptic, true); + if (error) + goto err_enable_config; + + haptic->enabled = true; + + return; + +err_enable_config: + max77693_haptic_lowsys(haptic, false); +err_enable_lowsys: + pwm_disable(haptic->pwm_dev); +} + +static void max77693_haptic_disable(struct max77693_haptic *haptic) +{ + int error; + + if (!haptic->enabled) + return; + + error = max77693_haptic_configure(haptic, false); + if (error) + return; + + error = max77693_haptic_lowsys(haptic, false); + if (error) + goto err_disable_lowsys; + + pwm_disable(haptic->pwm_dev); + haptic->enabled = false; + + return; + +err_disable_lowsys: + max77693_haptic_configure(haptic, true); +} + +static void max77693_haptic_play_work(struct work_struct *work) +{ + struct max77693_haptic *haptic = + container_of(work, struct max77693_haptic, work); + int error; + + error = max77693_haptic_set_duty_cycle(haptic); + if (error) { + dev_err(haptic->dev, "failed to set duty cycle: %d\n", error); + return; + } + + if (haptic->magnitude) + max77693_haptic_enable(haptic); + else + max77693_haptic_disable(haptic); +} + +static int max77693_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max77693_haptic *haptic = input_get_drvdata(dev); + u64 period_mag_multi; + + haptic->magnitude = effect->u.rumble.strong_magnitude; + if (!haptic->magnitude) + haptic->magnitude = effect->u.rumble.weak_magnitude; + + /* + * The magnitude comes from force-feedback interface. + * The formula to convert magnitude to pwm_duty as follows: + * - pwm_duty = (magnitude * pwm_period) / MAX_MAGNITUDE(0xFFFF) + */ + period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude; + haptic->pwm_duty = (unsigned int)(period_mag_multi >> + MAX_MAGNITUDE_SHIFT); + + schedule_work(&haptic->work); + + return 0; +} + +static int max77693_haptic_open(struct input_dev *dev) +{ + struct max77693_haptic *haptic = input_get_drvdata(dev); + int error; + + error = regulator_enable(haptic->motor_reg); + if (error) { + dev_err(haptic->dev, + "failed to enable regulator: %d\n", error); + return error; + } + + return 0; +} + +static void max77693_haptic_close(struct input_dev *dev) +{ + struct max77693_haptic *haptic = input_get_drvdata(dev); + int error; + + cancel_work_sync(&haptic->work); + max77693_haptic_disable(haptic); + + error = regulator_disable(haptic->motor_reg); + if (error) + dev_err(haptic->dev, + "failed to disable regulator: %d\n", error); +} + +static int max77693_haptic_probe(struct platform_device *pdev) +{ + struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); + struct max77693_haptic *haptic; + int error; + + haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); + if (!haptic) + return -ENOMEM; + + haptic->regmap_pmic = max77693->regmap; + haptic->regmap_haptic = max77693->regmap_haptic; + haptic->dev = &pdev->dev; + haptic->type = MAX77693_HAPTIC_LRA; + haptic->mode = MAX77693_HAPTIC_EXTERNAL_MODE; + haptic->pwm_divisor = MAX77693_HAPTIC_PWM_DIVISOR_128; + haptic->suspend_state = false; + + INIT_WORK(&haptic->work, max77693_haptic_play_work); + + /* Get pwm and regulatot for haptic device */ + haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL); + if (IS_ERR(haptic->pwm_dev)) { + dev_err(&pdev->dev, "failed to get pwm device\n"); + return PTR_ERR(haptic->pwm_dev); + } + + haptic->motor_reg = devm_regulator_get(&pdev->dev, "haptic"); + if (IS_ERR(haptic->motor_reg)) { + dev_err(&pdev->dev, "failed to get regulator\n"); + return PTR_ERR(haptic->motor_reg); + } + + /* Initialize input device for haptic device */ + haptic->input_dev = devm_input_allocate_device(&pdev->dev); + if (!haptic->input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + haptic->input_dev->name = "max77693-haptic"; + haptic->input_dev->id.version = 1; + haptic->input_dev->dev.parent = &pdev->dev; + haptic->input_dev->open = max77693_haptic_open; + haptic->input_dev->close = max77693_haptic_close; + input_set_drvdata(haptic->input_dev, haptic); + input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptic->input_dev, NULL, + max77693_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, "failed to create force-feedback\n"); + return error; + } + + error = input_register_device(haptic->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + platform_set_drvdata(pdev, haptic); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max77693_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77693_haptic *haptic = platform_get_drvdata(pdev); + + if (haptic->enabled) { + max77693_haptic_disable(haptic); + haptic->suspend_state = true; + } + + return 0; +} + +static int max77693_haptic_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77693_haptic *haptic = platform_get_drvdata(pdev); + + if (haptic->suspend_state) { + max77693_haptic_enable(haptic); + haptic->suspend_state = false; + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max77693_haptic_pm_ops, + max77693_haptic_suspend, max77693_haptic_resume); + +static struct platform_driver max77693_haptic_driver = { + .driver = { + .name = "max77693-haptic", + .owner = THIS_MODULE, + .pm = &max77693_haptic_pm_ops, + }, + .probe = max77693_haptic_probe, +}; +module_platform_driver(max77693_haptic_driver); + +MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); +MODULE_DESCRIPTION("MAXIM MAX77693 Haptic driver"); +MODULE_ALIAS("platform:max77693-haptic"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c new file mode 100644 index 000000000000..f505ac3a8d87 --- /dev/null +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -0,0 +1,332 @@ +/* + * Texas Instruments' Palmas Power Button Input Driver + * + * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/ + * Girish S Ghongdemath + * Nishanth Menon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/palmas.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define PALMAS_LPK_TIME_MASK 0x0c +#define PALMAS_PWRON_DEBOUNCE_MASK 0x03 +#define PALMAS_PWR_KEY_Q_TIME_MS 20 + +/** + * struct palmas_pwron - Palmas power on data + * @palmas: pointer to palmas device + * @input_dev: pointer to input device + * @input_work: work for detecting release of key + * @irq: irq that we are hooked on to + */ +struct palmas_pwron { + struct palmas *palmas; + struct input_dev *input_dev; + struct delayed_work input_work; + int irq; +}; + +/** + * struct palmas_pwron_config - configuration of palmas power on + * @long_press_time_val: value for long press h/w shutdown event + * @pwron_debounce_val: value for debounce of power button + */ +struct palmas_pwron_config { + u8 long_press_time_val; + u8 pwron_debounce_val; +}; + +/** + * palmas_power_button_work() - Detects the button release event + * @work: work item to detect button release + */ +static void palmas_power_button_work(struct work_struct *work) +{ + struct palmas_pwron *pwron = container_of(work, + struct palmas_pwron, + input_work.work); + struct input_dev *input_dev = pwron->input_dev; + unsigned int reg; + int error; + + error = palmas_read(pwron->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT1_LINE_STATE, ®); + if (error) { + dev_err(input_dev->dev.parent, + "Cannot read palmas PWRON status: %d\n", error); + } else if (reg & BIT(1)) { + /* The button is released, report event. */ + input_report_key(input_dev, KEY_POWER, 0); + input_sync(input_dev); + } else { + /* The button is still depressed, keep checking. */ + schedule_delayed_work(&pwron->input_work, + msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS)); + } +} + +/** + * pwron_irq() - button press isr + * @irq: irq + * @palmas_pwron: pwron struct + * + * Return: IRQ_HANDLED + */ +static irqreturn_t pwron_irq(int irq, void *palmas_pwron) +{ + struct palmas_pwron *pwron = palmas_pwron; + struct input_dev *input_dev = pwron->input_dev; + + input_report_key(input_dev, KEY_POWER, 1); + pm_wakeup_event(input_dev->dev.parent, 0); + input_sync(input_dev); + + mod_delayed_work(system_wq, &pwron->input_work, + msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS)); + + return IRQ_HANDLED; +} + +/** + * palmas_pwron_params_ofinit() - device tree parameter parser + * @dev: palmas button device + * @config: configuration params that this fills up + */ +static void palmas_pwron_params_ofinit(struct device *dev, + struct palmas_pwron_config *config) +{ + struct device_node *np; + u32 val; + int i, error; + u8 lpk_times[] = { 6, 8, 10, 12 }; + int pwr_on_deb_ms[] = { 15, 100, 500, 1000 }; + + memset(config, 0, sizeof(*config)); + + /* Default config parameters */ + config->long_press_time_val = ARRAY_SIZE(lpk_times) - 1; + + np = dev->of_node; + if (!np) + return; + + error = of_property_read_u32(np, "ti,palmas-long-press-seconds", &val); + if (!error) { + for (i = 0; i < ARRAY_SIZE(lpk_times); i++) { + if (val <= lpk_times[i]) { + config->long_press_time_val = i; + break; + } + } + } + + error = of_property_read_u32(np, + "ti,palmas-pwron-debounce-milli-seconds", + &val); + if (!error) { + for (i = 0; i < ARRAY_SIZE(pwr_on_deb_ms); i++) { + if (val <= pwr_on_deb_ms[i]) { + config->pwron_debounce_val = i; + break; + } + } + } + + dev_info(dev, "h/w controlled shutdown duration=%d seconds\n", + lpk_times[config->long_press_time_val]); +} + +/** + * palmas_pwron_probe() - probe + * @pdev: platform device for the button + * + * Return: 0 for successful probe else appropriate error + */ +static int palmas_pwron_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct input_dev *input_dev; + struct palmas_pwron *pwron; + struct palmas_pwron_config config; + int val; + int error; + + palmas_pwron_params_ofinit(dev, &config); + + pwron = kzalloc(sizeof(*pwron), GFP_KERNEL); + if (!pwron) + return -ENOMEM; + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Can't allocate power button\n"); + error = -ENOMEM; + goto err_free_mem; + } + + input_dev->name = "palmas_pwron"; + input_dev->phys = "palmas_pwron/input0"; + input_dev->dev.parent = dev; + + input_set_capability(input_dev, EV_KEY, KEY_POWER); + + /* + * Setup default hardware shutdown option (long key press) + * and debounce. + */ + val = config.long_press_time_val << __ffs(PALMAS_LPK_TIME_MASK); + val |= config.pwron_debounce_val << __ffs(PALMAS_PWRON_DEBOUNCE_MASK); + error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, + PALMAS_LONG_PRESS_KEY, + PALMAS_LPK_TIME_MASK | + PALMAS_PWRON_DEBOUNCE_MASK, + val); + if (error) { + dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error); + goto err_free_input; + } + + pwron->palmas = palmas; + pwron->input_dev = input_dev; + + INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work); + + pwron->irq = platform_get_irq(pdev, 0); + error = request_threaded_irq(pwron->irq, NULL, pwron_irq, + IRQF_TRIGGER_HIGH | + IRQF_TRIGGER_LOW | + IRQF_ONESHOT, + dev_name(dev), pwron); + if (error) { + dev_err(dev, "Can't get IRQ for pwron: %d\n", error); + goto err_free_input; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + goto err_free_irq; + } + + platform_set_drvdata(pdev, pwron); + device_init_wakeup(dev, true); + + return 0; + +err_free_irq: + cancel_delayed_work_sync(&pwron->input_work); + free_irq(pwron->irq, pwron); +err_free_input: + input_free_device(input_dev); +err_free_mem: + kfree(pwron); + return error; +} + +/** + * palmas_pwron_remove() - Cleanup on removal + * @pdev: platform device for the button + * + * Return: 0 + */ +static int palmas_pwron_remove(struct platform_device *pdev) +{ + struct palmas_pwron *pwron = platform_get_drvdata(pdev); + + free_irq(pwron->irq, pwron); + cancel_delayed_work_sync(&pwron->input_work); + + input_unregister_device(pwron->input_dev); + kfree(pwron); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +/** + * palmas_pwron_suspend() - suspend handler + * @dev: power button device + * + * Cancel all pending work items for the power button, setup irq for wakeup + * + * Return: 0 + */ +static int palmas_pwron_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct palmas_pwron *pwron = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&pwron->input_work); + + if (device_may_wakeup(dev)) + enable_irq_wake(pwron->irq); + + return 0; +} + +/** + * palmas_pwron_resume() - resume handler + * @dev: power button device + * + * Just disable the wakeup capability of irq here. + * + * Return: 0 + */ +static int palmas_pwron_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct palmas_pwron *pwron = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pwron->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(palmas_pwron_pm, + palmas_pwron_suspend, palmas_pwron_resume); + +#ifdef CONFIG_OF +static struct of_device_id of_palmas_pwr_match[] = { + { .compatible = "ti,palmas-pwrbutton" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, of_palmas_pwr_match); +#endif + +static struct platform_driver palmas_pwron_driver = { + .probe = palmas_pwron_probe, + .remove = palmas_pwron_remove, + .driver = { + .name = "palmas_pwrbutton", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_palmas_pwr_match), + .pm = &palmas_pwron_pm, + }, +}; +module_platform_driver(palmas_pwron_driver); + +MODULE_ALIAS("platform:palmas-pwrbutton"); +MODULE_DESCRIPTION("Palmas Power Button"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Texas Instruments Inc."); diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 5a6334be30b8..e097f1ab427f 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -18,7 +18,7 @@ #include <linux/gpio/consumer.h> #include <linux/gpio_keys.h> #include <linux/platform_device.h> -#include <linux/pnp.h> +#include <linux/acpi.h> /* * Definition of buttons on the tablet. The ACPI index of each button @@ -55,7 +55,7 @@ static int soc_button_lookup_gpio(struct device *dev, int acpi_index) struct gpio_desc *desc; int gpio; - desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index); + desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index, GPIOD_ASIS); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -67,7 +67,7 @@ static int soc_button_lookup_gpio(struct device *dev, int acpi_index) } static struct platform_device * -soc_button_device_create(struct pnp_dev *pdev, +soc_button_device_create(struct platform_device *pdev, const struct soc_button_info *button_info, bool autorepeat) { @@ -83,6 +83,9 @@ soc_button_device_create(struct pnp_dev *pdev, sizeof(*gpio_keys_pdata) + sizeof(*gpio_keys) * MAX_NBUTTONS, GFP_KERNEL); + if (!gpio_keys_pdata) + return ERR_PTR(-ENOMEM); + gpio_keys = (void *)(gpio_keys_pdata + 1); for (info = button_info; info->name; info++) { @@ -135,30 +138,40 @@ err_free_mem: return ERR_PTR(error); } -static void soc_button_remove(struct pnp_dev *pdev) +static int soc_button_remove(struct platform_device *pdev) { - struct soc_button_data *priv = pnp_get_drvdata(pdev); + struct soc_button_data *priv = platform_get_drvdata(pdev); + int i; for (i = 0; i < BUTTON_TYPES; i++) if (priv->children[i]) platform_device_unregister(priv->children[i]); + + return 0; } -static int soc_button_pnp_probe(struct pnp_dev *pdev, - const struct pnp_device_id *id) +static int soc_button_probe(struct platform_device *pdev) { - const struct soc_button_info *button_info = (void *)id->driver_data; + struct device *dev = &pdev->dev; + const struct acpi_device_id *id; + struct soc_button_info *button_info; struct soc_button_data *priv; struct platform_device *pd; int i; int error; + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + button_info = (struct soc_button_info *)id->driver_data; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - pnp_set_drvdata(pdev, priv); + platform_set_drvdata(pdev, priv); for (i = 0; i < BUTTON_TYPES; i++) { pd = soc_button_device_create(pdev, button_info, i == 0); @@ -189,30 +202,22 @@ static struct soc_button_info soc_button_PNP0C40[] = { { } }; -static const struct pnp_device_id soc_button_pnp_match[] = { - { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 }, - { .id = "" } +static const struct acpi_device_id soc_button_acpi_match[] = { + { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, + { } }; -MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match); -static struct pnp_driver soc_button_pnp_driver = { - .name = KBUILD_MODNAME, - .id_table = soc_button_pnp_match, - .probe = soc_button_pnp_probe, +MODULE_DEVICE_TABLE(acpi, soc_button_acpi_match); + +static struct platform_driver soc_button_driver = { + .probe = soc_button_probe, .remove = soc_button_remove, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(soc_button_acpi_match), + }, }; - -static int __init soc_button_init(void) -{ - return pnp_register_driver(&soc_button_pnp_driver); -} - -static void __exit soc_button_exit(void) -{ - pnp_unregister_driver(&soc_button_pnp_driver); -} - -module_init(soc_button_init); -module_exit(soc_button_exit); +module_platform_driver(soc_button_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index fb3b63b2f85c..8400a1a34d87 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -85,6 +85,7 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, pwr); + device_init_wakeup(&pdev->dev, true); return 0; } diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 856936247500..421e29e4cd81 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -311,7 +311,14 @@ static int uinput_open(struct inode *inode, struct file *file) static int uinput_validate_absbits(struct input_dev *dev) { unsigned int cnt; - int retval = 0; + int nslot; + + if (!test_bit(EV_ABS, dev->evbit)) + return 0; + + /* + * Check if absmin/absmax/absfuzz/absflat are sane. + */ for (cnt = 0; cnt < ABS_CNT; cnt++) { int min, max; @@ -327,8 +334,7 @@ static int uinput_validate_absbits(struct input_dev *dev) UINPUT_NAME, cnt, input_abs_get_min(dev, cnt), input_abs_get_max(dev, cnt)); - retval = -EINVAL; - break; + return -EINVAL; } if (input_abs_get_flat(dev, cnt) > @@ -340,11 +346,18 @@ static int uinput_validate_absbits(struct input_dev *dev) input_abs_get_flat(dev, cnt), input_abs_get_min(dev, cnt), input_abs_get_max(dev, cnt)); - retval = -EINVAL; - break; + return -EINVAL; } } - return retval; + + if (test_bit(ABS_MT_SLOT, dev->absbit)) { + nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; + input_mt_init_slots(dev, nslot, 0); + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + input_set_events_per_packet(dev, 60); + } + + return 0; } static int uinput_allocate_device(struct uinput_device *udev) @@ -410,19 +423,9 @@ static int uinput_setup_device(struct uinput_device *udev, input_abs_set_flat(dev, i, user_dev->absflat[i]); } - /* check if absmin/absmax/absfuzz/absflat are filled as - * told in Documentation/input/input-programming.txt */ - if (test_bit(EV_ABS, dev->evbit)) { - retval = uinput_validate_absbits(dev); - if (retval < 0) - goto exit; - if (test_bit(ABS_MT_SLOT, dev->absbit)) { - int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot, 0); - } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { - input_set_events_per_packet(dev, 60); - } - } + retval = uinput_validate_absbits(dev); + if (retval < 0) + goto exit; udev->state = UIST_SETUP_COMPLETE; retval = count; @@ -720,6 +723,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, } switch (cmd) { + case UI_GET_VERSION: + if (put_user(UINPUT_VERSION, + (unsigned int __user *)p)) + retval = -EFAULT; + goto out; + case UI_DEV_CREATE: retval = uinput_create_device(udev); goto out; diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c index fbfdc10573be..95599e478e19 100644 --- a/drivers/input/misc/xen-kbdfront.c +++ b/drivers/input/misc/xen-kbdfront.c @@ -285,7 +285,7 @@ static int xenkbd_connect_backend(struct xenbus_device *dev, error_evtchan: xenbus_free_evtchn(dev, evtchn); error_grant: - gnttab_end_foreign_access_ref(info->gref, 0); + gnttab_end_foreign_access(info->gref, 0, 0UL); info->gref = -1; return ret; } @@ -296,7 +296,7 @@ static void xenkbd_disconnect_backend(struct xenkbd_info *info) unbind_from_irqhandler(info->irq, info); info->irq = -1; if (info->gref >= 0) - gnttab_end_foreign_access_ref(info->gref, 0); + gnttab_end_foreign_access(info->gref, 0, 0UL); info->gref = -1; } @@ -365,12 +365,13 @@ static const struct xenbus_device_id xenkbd_ids[] = { { "" } }; -static DEFINE_XENBUS_DRIVER(xenkbd, , +static struct xenbus_driver xenkbd_driver = { + .ids = xenkbd_ids, .probe = xenkbd_probe, .remove = xenkbd_remove, .resume = xenkbd_resume, .otherend_changed = xenkbd_backend_changed, -); +}; static int __init xenkbd_init(void) { 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 obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o -psmouse-objs := psmouse-base.o synaptics.o +psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index fb15c64ffb95..d125a019383f 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -99,6 +99,8 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { #define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ #define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with 6-byte ALPS packet */ +#define ALPS_IS_RUSHMORE 0x100 /* device is a rushmore */ +#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ static const struct alps_model_info alps_model_data[] = { { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ @@ -281,11 +283,10 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) * * The bitmaps don't have enough data to track fingers, so this function * only generates points representing a bounding box of at most two contacts. - * These two points are returned in x1, y1, x2, and y2. + * These two points are returned in fields->mt. */ static void alps_process_bitmap_dolphin(struct alps_data *priv, - struct alps_fields *fields, - int *x1, int *y1, int *x2, int *y2) + struct alps_fields *fields) { int box_middle_x, box_middle_y; unsigned int x_map, y_map; @@ -308,8 +309,6 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv, if (x_msb > priv->x_bits || y_msb > priv->y_bits) return; - *x1 = *y1 = *x2 = *y2 = 0; - if (fields->fingers > 1) { start_bit = priv->x_bits - x_msb; end_bit = priv->x_bits - x_lsb; @@ -320,10 +319,35 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv, end_bit = y_msb - 1; box_middle_y = (priv->y_max * (start_bit + end_bit)) / (2 * (priv->y_bits - 1)); - *x1 = fields->x; - *y1 = fields->y; - *x2 = 2 * box_middle_x - *x1; - *y2 = 2 * box_middle_y - *y1; + fields->mt[0] = fields->st; + fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x; + fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y; + } +} + +static void alps_get_bitmap_points(unsigned int map, + struct alps_bitmap_point *low, + struct alps_bitmap_point *high, + int *fingers) +{ + struct alps_bitmap_point *point; + int i, bit, prev_bit = 0; + + point = low; + for (i = 0; map != 0; i++, map >>= 1) { + bit = map & 1; + if (bit) { + if (!prev_bit) { + point->start_bit = i; + point->num_bits = 0; + (*fingers)++; + } + point->num_bits++; + } else { + if (prev_bit) + point = high; + } + prev_bit = bit; } } @@ -334,71 +358,21 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv, * * The bitmaps don't have enough data to track fingers, so this function * only generates points representing a bounding box of all contacts. - * These points are returned in x1, y1, x2, and y2 when the return value + * These points are returned in fields->mt when the return value * is greater than 0. */ static int alps_process_bitmap(struct alps_data *priv, - unsigned int x_map, unsigned int y_map, - int *x1, int *y1, int *x2, int *y2) + struct alps_fields *fields) { - struct alps_bitmap_point { - int start_bit; - int num_bits; - }; - - int fingers_x = 0, fingers_y = 0, fingers; - int i, bit, prev_bit; + int i, fingers_x = 0, fingers_y = 0, fingers; struct alps_bitmap_point x_low = {0,}, x_high = {0,}; struct alps_bitmap_point y_low = {0,}, y_high = {0,}; - struct alps_bitmap_point *point; - if (!x_map || !y_map) + if (!fields->x_map || !fields->y_map) return 0; - *x1 = *y1 = *x2 = *y2 = 0; - - prev_bit = 0; - point = &x_low; - for (i = 0; x_map != 0; i++, x_map >>= 1) { - bit = x_map & 1; - if (bit) { - if (!prev_bit) { - point->start_bit = i; - fingers_x++; - } - point->num_bits++; - } else { - if (prev_bit) - point = &x_high; - else - point->num_bits = 0; - } - prev_bit = bit; - } - - /* - * y bitmap is reversed for what we need (lower positions are in - * higher bits), so we process from the top end. - */ - y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits); - prev_bit = 0; - point = &y_low; - for (i = 0; y_map != 0; i++, y_map <<= 1) { - bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1)); - if (bit) { - if (!prev_bit) { - point->start_bit = i; - fingers_y++; - } - point->num_bits++; - } else { - if (prev_bit) - point = &y_high; - else - point->num_bits = 0; - } - prev_bit = bit; - } + alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x); + alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y); /* * Fingers can overlap, so we use the maximum count of fingers @@ -407,58 +381,91 @@ static int alps_process_bitmap(struct alps_data *priv, fingers = max(fingers_x, fingers_y); /* - * If total fingers is > 1 but either axis reports only a single - * contact, we have overlapping or adjacent fingers. For the - * purposes of creating a bounding box, divide the single contact - * (roughly) equally between the two points. + * If an axis reports only a single contact, we have overlapping or + * adjacent fingers. Divide the single contact between the two points. */ - if (fingers > 1) { - if (fingers_x == 1) { - i = x_low.num_bits / 2; - x_low.num_bits = x_low.num_bits - i; - x_high.start_bit = x_low.start_bit + i; - x_high.num_bits = max(i, 1); - } else if (fingers_y == 1) { - i = y_low.num_bits / 2; - y_low.num_bits = y_low.num_bits - i; - y_high.start_bit = y_low.start_bit + i; - y_high.num_bits = max(i, 1); - } + if (fingers_x == 1) { + i = (x_low.num_bits - 1) / 2; + x_low.num_bits = x_low.num_bits - i; + x_high.start_bit = x_low.start_bit + i; + x_high.num_bits = max(i, 1); + } + if (fingers_y == 1) { + i = (y_low.num_bits - 1) / 2; + y_low.num_bits = y_low.num_bits - i; + y_high.start_bit = y_low.start_bit + i; + y_high.num_bits = max(i, 1); } - *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / - (2 * (priv->x_bits - 1)); - *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / - (2 * (priv->y_bits - 1)); - - if (fingers > 1) { - *x2 = (priv->x_max * - (2 * x_high.start_bit + x_high.num_bits - 1)) / - (2 * (priv->x_bits - 1)); - *y2 = (priv->y_max * - (2 * y_high.start_bit + y_high.num_bits - 1)) / - (2 * (priv->y_bits - 1)); + fields->mt[0].x = + (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + fields->mt[0].y = + (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + fields->mt[1].x = + (priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + fields->mt[1].y = + (priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); + + /* y-bitmap order is reversed, except on rushmore */ + if (!(priv->flags & ALPS_IS_RUSHMORE)) { + fields->mt[0].y = priv->y_max - fields->mt[0].y; + fields->mt[1].y = priv->y_max - fields->mt[1].y; } return fingers; } -static void alps_set_slot(struct input_dev *dev, int slot, bool active, - int x, int y) +static void alps_set_slot(struct input_dev *dev, int slot, int x, int y) { input_mt_slot(dev, slot); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); - if (active) { - input_report_abs(dev, ABS_MT_POSITION_X, x); - input_report_abs(dev, ABS_MT_POSITION_Y, y); - } + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); } -static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers, - int x1, int y1, int x2, int y2) +static void alps_report_mt_data(struct psmouse *psmouse, int n) { - alps_set_slot(dev, 0, num_fingers != 0, x1, y1); - alps_set_slot(dev, 1, num_fingers == 2, x2, y2); + struct alps_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + int i, slot[MAX_TOUCHES]; + + input_mt_assign_slots(dev, slot, f->mt, n); + for (i = 0; i < n; i++) + alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y); + + input_mt_sync_frame(dev); +} + +static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers) +{ + struct alps_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + + /* Use st data when we don't have mt data */ + if (fingers < 2) { + f->mt[0].x = f->st.x; + f->mt[0].y = f->st.y; + fingers = f->pressure > 0 ? 1 : 0; + } + + alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2); + + input_mt_report_finger_count(dev, fingers); + + input_report_key(dev, BTN_LEFT, f->left); + input_report_key(dev, BTN_RIGHT, f->right); + input_report_key(dev, BTN_MIDDLE, f->middle); + + input_report_abs(dev, ABS_PRESSURE, f->pressure); + + input_sync(dev); } static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) @@ -532,7 +539,7 @@ static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) f->ts_middle = !!(p[3] & 0x40); } -static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, +static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { f->first_mp = !!(p[4] & 0x40); @@ -546,24 +553,31 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, ((p[2] & 0x7f) << 1) | (p[4] & 0x01); - f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | ((p[0] & 0x30) >> 4); - f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); - f->z = p[5] & 0x7f; + f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->pressure = p[5] & 0x7f; alps_decode_buttons_v3(f, p); + + return 0; } -static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p, +static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { alps_decode_pinnacle(f, p, psmouse); + /* Rushmore's packet decode has a bit difference with Pinnacle's */ + f->is_mp = !!(p[5] & 0x40); + f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1; f->x_map |= (p[5] & 0x10) << 11; f->y_map |= (p[5] & 0x20) << 6; + + return 0; } -static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, +static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse) { u64 palm_data = 0; @@ -573,9 +587,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, f->is_mp = !!(p[0] & 0x20); if (!f->is_mp) { - f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); - f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); - f->z = (p[0] & 4) ? 0 : p[5] & 0x7f; + f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); + f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); + f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f; alps_decode_buttons_v3(f, p); } else { f->fingers = ((p[0] & 0x6) >> 1 | @@ -596,19 +610,21 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, f->x_map = (palm_data >> priv->y_bits) & (BIT(priv->x_bits) - 1); } + + return 0; } static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; - struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; - int x1 = 0, y1 = 0, x2 = 0, y2 = 0; - int fingers = 0, bmap_fn; - struct alps_fields f = {0}; + struct alps_fields *f = &priv->f; + int fingers = 0; + + memset(f, 0, sizeof(*f)); - priv->decode_fields(&f, packet, psmouse); + priv->decode_fields(f, packet, psmouse); /* * There's no single feature of touchpad position and bitmap packets @@ -623,22 +639,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * packet. Check for this, and when it happens process the * position packet as usual. */ - if (f.is_mp) { - fingers = f.fingers; + if (f->is_mp) { + fingers = f->fingers; if (priv->proto_version == ALPS_PROTO_V3) { - bmap_fn = alps_process_bitmap(priv, f.x_map, - f.y_map, &x1, &y1, - &x2, &y2); - - /* - * We shouldn't report more than one finger if - * we don't have two coordinates. - */ - if (fingers > 1 && bmap_fn < 2) - fingers = bmap_fn; + if (alps_process_bitmap(priv, f) == 0) + fingers = 0; /* Use st data */ /* Now process position packet */ - priv->decode_fields(&f, priv->multi_data, + priv->decode_fields(f, priv->multi_data, psmouse); } else { /* @@ -647,15 +655,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * calculate Pt2, so we need to do position * packet decode first. */ - priv->decode_fields(&f, priv->multi_data, + priv->decode_fields(f, priv->multi_data, psmouse); /* * Since Dolphin's finger number is reliable, * there is no need to compare with bmap_fn. */ - alps_process_bitmap_dolphin(priv, &f, &x1, &y1, - &x2, &y2); + alps_process_bitmap_dolphin(priv, f); } } else { priv->multi_packet = 0; @@ -670,10 +677,10 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * out misidentified bitmap packets, we reject anything with this * bit set. */ - if (f.is_mp) + if (f->is_mp) return; - if (!priv->multi_packet && f.first_mp) { + if (!priv->multi_packet && f->first_mp) { priv->multi_packet = 1; memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); return; @@ -687,44 +694,15 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) * with x, y, and z all zero, so these seem to be flukes. * Ignore them. */ - if (f.x && f.y && !f.z) + if (f->st.x && f->st.y && !f->pressure) return; - /* - * If we don't have MT data or the bitmaps were empty, we have - * to rely on ST data. - */ - if (!fingers) { - x1 = f.x; - y1 = f.y; - fingers = f.z > 0 ? 1 : 0; - } - - if (f.z >= 64) - input_report_key(dev, BTN_TOUCH, 1); - else - input_report_key(dev, BTN_TOUCH, 0); - - alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); - - input_mt_report_finger_count(dev, fingers); - - input_report_key(dev, BTN_LEFT, f.left); - input_report_key(dev, BTN_RIGHT, f.right); - input_report_key(dev, BTN_MIDDLE, f.middle); - - if (f.z > 0) { - input_report_abs(dev, ABS_X, f.x); - input_report_abs(dev, ABS_Y, f.y); - } - input_report_abs(dev, ABS_PRESSURE, f.z); - - input_sync(dev); + alps_report_semi_mt_data(psmouse, fingers); if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { - input_report_key(dev2, BTN_LEFT, f.ts_left); - input_report_key(dev2, BTN_RIGHT, f.ts_right); - input_report_key(dev2, BTN_MIDDLE, f.ts_middle); + input_report_key(dev2, BTN_LEFT, f->ts_left); + input_report_key(dev2, BTN_RIGHT, f->ts_right); + input_report_key(dev2, BTN_MIDDLE, f->ts_middle); input_sync(dev2); } } @@ -823,13 +801,8 @@ static void alps_process_packet_v4(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; - struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; int offset; - int x, y, z; - int left, right; - int x1, y1, x2, y2; - int fingers = 0; - unsigned int x_bitmap, y_bitmap; /* * v4 has a 6-byte encoding for bitmap data, but this data is @@ -851,71 +824,207 @@ static void alps_process_packet_v4(struct psmouse *psmouse) if (++priv->multi_packet > 2) { priv->multi_packet = 0; - x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) | + f->x_map = ((priv->multi_data[2] & 0x1f) << 10) | ((priv->multi_data[3] & 0x60) << 3) | ((priv->multi_data[0] & 0x3f) << 2) | ((priv->multi_data[1] & 0x60) >> 5); - y_bitmap = ((priv->multi_data[5] & 0x01) << 10) | + f->y_map = ((priv->multi_data[5] & 0x01) << 10) | ((priv->multi_data[3] & 0x1f) << 5) | (priv->multi_data[1] & 0x1f); - fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap, - &x1, &y1, &x2, &y2); - - /* Store MT data.*/ - priv->fingers = fingers; - priv->x1 = x1; - priv->x2 = x2; - priv->y1 = y1; - priv->y2 = y2; + f->fingers = alps_process_bitmap(priv, f); } - left = packet[4] & 0x01; - right = packet[4] & 0x02; + f->left = !!(packet[4] & 0x01); + f->right = !!(packet[4] & 0x02); - x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | - ((packet[0] & 0x30) >> 4); - y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); - z = packet[5] & 0x7f; + f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | + ((packet[0] & 0x30) >> 4); + f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); + f->pressure = packet[5] & 0x7f; - /* - * If there were no contacts in the bitmap, use ST - * points in MT reports. - * If there were two contacts or more, report MT data. - */ - if (priv->fingers < 2) { - x1 = x; - y1 = y; - fingers = z > 0 ? 1 : 0; - } else { - fingers = priv->fingers; - x1 = priv->x1; - x2 = priv->x2; - y1 = priv->y1; - y2 = priv->y2; + alps_report_semi_mt_data(psmouse, f->fingers); +} + +static bool alps_is_valid_package_v7(struct psmouse *psmouse) +{ + switch (psmouse->pktcnt) { + case 3: + return (psmouse->packet[2] & 0x40) == 0x40; + case 4: + return (psmouse->packet[3] & 0x48) == 0x48; + case 6: + return (psmouse->packet[5] & 0x40) == 0x00; } + return true; +} - if (z >= 64) - input_report_key(dev, BTN_TOUCH, 1); +static unsigned char alps_get_packet_id_v7(char *byte) +{ + unsigned char packet_id; + + if (byte[4] & 0x40) + packet_id = V7_PACKET_ID_TWO; + else if (byte[4] & 0x01) + packet_id = V7_PACKET_ID_MULTI; + else if ((byte[0] & 0x10) && !(byte[4] & 0x43)) + packet_id = V7_PACKET_ID_NEW; + else if (byte[1] == 0x00 && byte[4] == 0x00) + packet_id = V7_PACKET_ID_IDLE; else - input_report_key(dev, BTN_TOUCH, 0); + packet_id = V7_PACKET_ID_UNKNOWN; - alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + return packet_id; +} - input_mt_report_finger_count(dev, fingers); +static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt, + unsigned char *pkt, + unsigned char pkt_id) +{ + mt[0].x = ((pkt[2] & 0x80) << 4); + mt[0].x |= ((pkt[2] & 0x3F) << 5); + mt[0].x |= ((pkt[3] & 0x30) >> 1); + mt[0].x |= (pkt[3] & 0x07); + mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07); + + mt[1].x = ((pkt[3] & 0x80) << 4); + mt[1].x |= ((pkt[4] & 0x80) << 3); + mt[1].x |= ((pkt[4] & 0x3F) << 4); + mt[1].y = ((pkt[5] & 0x80) << 3); + mt[1].y |= ((pkt[5] & 0x3F) << 4); + + switch (pkt_id) { + case V7_PACKET_ID_TWO: + mt[1].x &= ~0x000F; + mt[1].y |= 0x000F; + break; - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); + case V7_PACKET_ID_MULTI: + mt[1].x &= ~0x003F; + mt[1].y &= ~0x0020; + mt[1].y |= ((pkt[4] & 0x02) << 4); + mt[1].y |= 0x001F; + break; - if (z > 0) { - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + case V7_PACKET_ID_NEW: + mt[1].x &= ~0x003F; + mt[1].x |= (pkt[0] & 0x20); + mt[1].y |= 0x000F; + break; } - input_report_abs(dev, ABS_PRESSURE, z); + + mt[0].y = 0x7FF - mt[0].y; + mt[1].y = 0x7FF - mt[1].y; +} + +static int alps_get_mt_count(struct input_mt_pos *mt) +{ + int i; + + for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++) + /* empty */; + + return i; +} + +static int alps_decode_packet_v7(struct alps_fields *f, + unsigned char *p, + struct psmouse *psmouse) +{ + unsigned char pkt_id; + + pkt_id = alps_get_packet_id_v7(p); + if (pkt_id == V7_PACKET_ID_IDLE) + return 0; + if (pkt_id == V7_PACKET_ID_UNKNOWN) + return -1; + + alps_get_finger_coordinate_v7(f->mt, p, pkt_id); + + if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) { + f->left = (p[0] & 0x80) >> 7; + f->right = (p[0] & 0x20) >> 5; + f->middle = (p[0] & 0x10) >> 4; + } + + if (pkt_id == V7_PACKET_ID_TWO) + f->fingers = alps_get_mt_count(f->mt); + else if (pkt_id == V7_PACKET_ID_MULTI) + f->fingers = 3 + (p[5] & 0x03); + + return 0; +} + +static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev2 = priv->dev2; + int x, y, z, left, right, middle; + + /* + * b7 b6 b5 b4 b3 b2 b1 b0 + * Byte0 0 1 0 0 1 0 0 0 + * Byte1 1 1 * * 1 M R L + * Byte2 X7 1 X5 X4 X3 X2 X1 X0 + * Byte3 Z6 1 Y6 X6 1 Y2 Y1 Y0 + * Byte4 Y7 0 Y5 Y4 Y3 1 1 0 + * Byte5 T&P 0 Z5 Z4 Z3 Z2 Z1 Z0 + * M / R / L: Middle / Right / Left button + */ + + x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2); + y = (packet[3] & 0x07) | (packet[4] & 0xb8) | + ((packet[3] & 0x20) << 1); + z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1); + + left = (packet[1] & 0x01); + right = (packet[1] & 0x02) >> 1; + middle = (packet[1] & 0x04) >> 2; + + /* Divide 2 since trackpoint's speed is too fast */ + input_report_rel(dev2, REL_X, (char)x / 2); + input_report_rel(dev2, REL_Y, -((char)y / 2)); + + input_report_key(dev2, BTN_LEFT, left); + input_report_key(dev2, BTN_RIGHT, right); + input_report_key(dev2, BTN_MIDDLE, middle); + + input_sync(dev2); +} + +static void alps_process_touchpad_packet_v7(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + + memset(f, 0, sizeof(*f)); + + if (priv->decode_fields(f, psmouse->packet, psmouse)) + return; + + alps_report_mt_data(psmouse, alps_get_mt_count(f->mt)); + + input_mt_report_finger_count(dev, f->fingers); + + input_report_key(dev, BTN_LEFT, f->left); + input_report_key(dev, BTN_RIGHT, f->right); + input_report_key(dev, BTN_MIDDLE, f->middle); input_sync(dev); } +static void alps_process_packet_v7(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06) + alps_process_trackstick_packet_v7(psmouse); + else + alps_process_touchpad_packet_v7(psmouse); +} + static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) @@ -1047,7 +1156,13 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ + /* + * Check if we are dealing with a bare PS/2 packet, presumably from + * a device connected to the external PS/2 port. Because bare PS/2 + * protocol does not have enough constant bits to self-synchronize + * properly we only do this if the device is fully synchronized. + */ + if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) { if (psmouse->pktcnt == 3) { alps_report_bare_ps2_packet(psmouse, psmouse->packet, true); @@ -1071,12 +1186,35 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) } /* Bytes 2 - pktsize should have 0 in the highest bit */ - if ((priv->proto_version < ALPS_PROTO_V5) && + if (priv->proto_version < ALPS_PROTO_V5 && psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]); + + if (priv->proto_version == ALPS_PROTO_V3 && + psmouse->pktcnt == psmouse->pktsize) { + /* + * Some Dell boxes, such as Latitude E6440 or E7440 + * with closed lid, quite often smash last byte of + * otherwise valid packet with 0xff. Given that the + * next packet is very likely to be valid let's + * report PSMOUSE_FULL_PACKET but not process data, + * rather than reporting PSMOUSE_BAD_DATA and + * filling the logs. + */ + return PSMOUSE_FULL_PACKET; + } + + return PSMOUSE_BAD_DATA; + } + + if (priv->proto_version == ALPS_PROTO_V7 && + !alps_is_valid_package_v7(psmouse)) { + psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", + psmouse->pktcnt - 1, + psmouse->packet[psmouse->pktcnt - 1]); return PSMOUSE_BAD_DATA; } @@ -1192,6 +1330,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, return 0; } +static bool alps_check_valid_firmware_id(unsigned char id[]) +{ + if (id[0] == 0x73) + return true; + + if (id[0] == 0x88 && + (id[1] == 0x07 || + id[1] == 0x08 || + (id[1] & 0xf0) == 0xb0 || + (id[1] & 0xf0) == 0xc0)) { + return true; + } + + return false; +} + static int alps_enter_command_mode(struct psmouse *psmouse) { unsigned char param[4]; @@ -1201,8 +1355,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse) return -1; } - if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) && - param[0] != 0x73) { + if (!alps_check_valid_firmware_id(param)) { psmouse_dbg(psmouse, "unknown response while entering command mode\n"); return -1; @@ -1660,6 +1813,45 @@ error: return -1; } +static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch) +{ + int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys; + struct alps_data *priv = psmouse->private; + + reg = alps_command_mode_read_reg(psmouse, reg_pitch); + if (reg < 0) + return reg; + + x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */ + + y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */ + y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */ + + reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1); + if (reg < 0) + return reg; + + x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */ + x_electrode = 17 + x_electrode; + + y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */ + y_electrode = 13 + y_electrode; + + x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */ + y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */ + + priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */ + priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */ + + psmouse_dbg(psmouse, + "pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n", + x_pitch, y_pitch, x_electrode, y_electrode, + x_phys / 10, y_phys / 10, priv->x_res, priv->y_res); + + return 0; +} + static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; @@ -1680,6 +1872,9 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00)) goto error; + if (alps_get_v3_v7_resolution(psmouse, 0xc2da)) + goto error; + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6); if (reg_val == -1) goto error; @@ -1856,6 +2051,35 @@ static int alps_hw_init_dolphin_v1(struct psmouse *psmouse) return 0; } +static int alps_hw_init_v7(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int reg_val, ret = -1; + + if (alps_enter_command_mode(psmouse) || + alps_command_mode_read_reg(psmouse, 0xc2d9) == -1) + goto error; + + if (alps_get_v3_v7_resolution(psmouse, 0xc397)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64)) + goto error; + + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02)) + goto error; + + alps_exit_command_mode(psmouse); + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + +error: + alps_exit_command_mode(psmouse); + return ret; +} + static void alps_set_defaults(struct alps_data *priv) { priv->byte0 = 0x8f; @@ -1914,6 +2138,21 @@ static void alps_set_defaults(struct alps_data *priv) priv->x_max = 2047; priv->y_max = 1535; break; + case ALPS_PROTO_V7: + priv->hw_init = alps_hw_init_v7; + priv->process_packet = alps_process_packet_v7; + priv->decode_fields = alps_decode_packet_v7; + priv->set_abs_params = alps_set_abs_params_mt; + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + priv->x_max = 0xfff; + priv->y_max = 0x7ff; + priv->byte0 = 0x48; + priv->mask0 = 0x48; + + if (priv->fw_ver[1] != 0xba) + priv->flags |= ALPS_BUTTONPAD; + break; } } @@ -1972,6 +2211,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) alps_exit_command_mode(psmouse)) return -EIO; + /* Save the Firmware version */ + memcpy(priv->fw_ver, ec, 3); + if (alps_match_table(psmouse, priv, e7, ec) == 0) { return 0; } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 && @@ -1982,6 +2224,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) return -EIO; else return 0; + } else if (ec[0] == 0x88 && + ((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) { + priv->proto_version = ALPS_PROTO_V7; + alps_set_defaults(priv); + + return 0; } else if (ec[0] == 0x88 && ec[1] == 0x08) { priv->proto_version = ALPS_PROTO_V3; alps_set_defaults(priv); @@ -1990,6 +2238,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) priv->decode_fields = alps_decode_rushmore; priv->x_bits = 16; priv->y_bits = 12; + priv->flags |= ALPS_IS_RUSHMORE; /* hack to make addr_command, nibble_command available */ psmouse->private = priv; @@ -2006,8 +2255,8 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) return 0; } - psmouse_info(psmouse, - "Unknown ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); + psmouse_dbg(psmouse, + "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); return -EINVAL; } @@ -2044,17 +2293,21 @@ static void alps_set_abs_params_st(struct alps_data *priv, static void alps_set_abs_params_mt(struct alps_data *priv, struct input_dev *dev1) { - set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); - input_mt_init_slots(dev1, 2, 0); input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); - set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); + input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res); + input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res); + + input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | + INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT); + set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); set_bit(BTN_TOOL_QUADTAP, dev1->keybit); - input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); + /* V7 is real multi-touch */ + if (priv->proto_version == ALPS_PROTO_V7) + clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit); } int alps_init(struct psmouse *psmouse) @@ -2100,7 +2353,9 @@ int alps_init(struct psmouse *psmouse) dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); priv->set_abs_params(priv, dev1); - input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); + /* No pressure on V7 */ + if (priv->proto_version != ALPS_PROTO_V7) + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); if (priv->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); @@ -2117,6 +2372,9 @@ int alps_init(struct psmouse *psmouse) dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + } else if (priv->flags & ALPS_BUTTONPAD) { + set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit); + clear_bit(BTN_RIGHT, dev1->keybit); } else { dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); } @@ -2136,6 +2394,10 @@ int alps_init(struct psmouse *psmouse) dev2->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + __set_bit(INPUT_PROP_POINTER, dev2->propbit); + if (priv->flags & ALPS_DUALPOINT) + __set_bit(INPUT_PROP_POINTING_STICK, dev2->propbit); + if (input_register_device(priv->dev2)) goto init_fail; @@ -2148,6 +2410,9 @@ int alps_init(struct psmouse *psmouse) /* We are having trouble resyncing ALPS touchpads so disable it for now */ psmouse->resync_time = 0; + /* Allow 2 invalid packets without resetting device */ + psmouse->resetafter = psmouse->pktsize * 2; + return 0; init_fail: diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 03f88b6940c7..66240b47819a 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -12,17 +12,39 @@ #ifndef _ALPS_H #define _ALPS_H +#include <linux/input/mt.h> + #define ALPS_PROTO_V1 1 #define ALPS_PROTO_V2 2 #define ALPS_PROTO_V3 3 #define ALPS_PROTO_V4 4 #define ALPS_PROTO_V5 5 #define ALPS_PROTO_V6 6 +#define ALPS_PROTO_V7 7 /* t3btl t4s */ + +#define MAX_TOUCHES 2 #define DOLPHIN_COUNT_PER_ELECTRODE 64 #define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ #define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ +/* + * enum V7_PACKET_ID - defines the packet type for V7 + * V7_PACKET_ID_IDLE: There's no finger and no button activity. + * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad + * or there's button activities. + * V7_PACKET_ID_MULTI: There are at least three non-resting fingers. + * V7_PACKET_ID_NEW: The finger position in slot is not continues from + * previous packet. +*/ +enum V7_PACKET_ID { + V7_PACKET_ID_IDLE, + V7_PACKET_ID_TWO, + V7_PACKET_ID_MULTI, + V7_PACKET_ID_NEW, + V7_PACKET_ID_UNKNOWN, +}; + /** * struct alps_model_info - touchpad ID table * @signature: E7 response string to match. @@ -46,7 +68,7 @@ struct alps_model_info { unsigned char command_mode_resp; unsigned char proto_version; unsigned char byte0, mask0; - unsigned char flags; + int flags; }; /** @@ -65,14 +87,19 @@ struct alps_nibble_commands { unsigned char data; }; +struct alps_bitmap_point { + int start_bit; + int num_bits; +}; + /** * struct alps_fields - decoded version of the report packet * @x_map: Bitmap of active X positions for MT. * @y_map: Bitmap of active Y positions for MT. * @fingers: Number of fingers for MT. - * @x: X position for ST. - * @y: Y position for ST. - * @z: Z position for ST. + * @pressure: Pressure. + * @st: position for ST. + * @mt: position for MT. * @first_mp: Packet is the first of a multi-packet report. * @is_mp: Packet is part of a multi-packet report. * @left: Left touchpad button is active. @@ -86,9 +113,11 @@ struct alps_fields { unsigned int x_map; unsigned int y_map; unsigned int fingers; - unsigned int x; - unsigned int y; - unsigned int z; + + int pressure; + struct input_mt_pos st; + struct input_mt_pos mt[MAX_TOUCHES]; + unsigned int first_mp:1; unsigned int is_mp:1; @@ -113,6 +142,7 @@ struct alps_fields { * known format for this model. The first byte of the report, ANDed with * mask0, should match byte0. * @mask0: The mask used to check the first byte of the report. + * @fw_ver: cached copy of firmware version (EC report) * @flags: Additional device capabilities (passthrough port, trackstick, etc.). * @x_max: Largest possible X position value. * @y_max: Largest possible Y position value. @@ -125,11 +155,7 @@ struct alps_fields { * @prev_fin: Finger bit from previous packet. * @multi_packet: Multi-packet data in progress. * @multi_data: Saved multi-packet data. - * @x1: First X coordinate from last MT report. - * @x2: Second X coordinate from last MT report. - * @y1: First Y coordinate from last MT report. - * @y2: Second Y coordinate from last MT report. - * @fingers: Number of fingers from last MT report. + * @f: Decoded packet data fields. * @quirks: Bitmap of ALPS_QUIRK_*. * @timer: Timer for flushing out the final report packet in the stream. */ @@ -142,23 +168,25 @@ struct alps_data { int addr_command; unsigned char proto_version; unsigned char byte0, mask0; - unsigned char flags; + unsigned char fw_ver[3]; + int flags; int x_max; int y_max; int x_bits; int y_bits; + unsigned int x_res; + unsigned int y_res; int (*hw_init)(struct psmouse *psmouse); void (*process_packet)(struct psmouse *psmouse); - void (*decode_fields)(struct alps_fields *f, unsigned char *p, + int (*decode_fields)(struct alps_fields *f, unsigned char *p, struct psmouse *psmouse); void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1); int prev_fin; int multi_packet; unsigned char multi_data[6]; - int x1, x2, y1, y2; - int fingers; + struct alps_fields f; u8 quirks; struct timer_list timer; }; diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index ee2a04d90d20..3fcb6b3cb0bd 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -18,6 +18,7 @@ #include <linux/input/mt.h> #include <linux/serio.h> #include <linux/libps2.h> +#include <asm/unaligned.h> #include "psmouse.h" #include "elantech.h" @@ -403,6 +404,68 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse) input_sync(dev); } +static void elantech_report_trackpoint(struct psmouse *psmouse, + int packet_type) +{ + /* + * byte 0: 0 0 sx sy 0 M R L + * byte 1:~sx 0 0 0 0 0 0 0 + * byte 2:~sy 0 0 0 0 0 0 0 + * byte 3: 0 0 ~sy ~sx 0 1 1 0 + * byte 4: x7 x6 x5 x4 x3 x2 x1 x0 + * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 + * + * x and y are written in two's complement spread + * over 9 bits with sx/sy the relative top bit and + * x7..x0 and y7..y0 the lower bits. + * The sign of y is opposite to what the input driver + * expects for a relative movement + */ + + struct elantech_data *etd = psmouse->private; + struct input_dev *tp_dev = etd->tp_dev; + unsigned char *packet = psmouse->packet; + int x, y; + u32 t; + + if (dev_WARN_ONCE(&psmouse->ps2dev.serio->dev, + !tp_dev, + psmouse_fmt("Unexpected trackpoint message\n"))) { + if (etd->debug == 1) + elantech_packet_dump(psmouse); + return; + } + + t = get_unaligned_le32(&packet[0]); + + switch (t & ~7U) { + case 0x06000030U: + case 0x16008020U: + case 0x26800010U: + case 0x36808000U: + x = packet[4] - (int)((packet[1]^0x80) << 1); + y = (int)((packet[2]^0x80) << 1) - packet[5]; + + input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01); + input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04); + + input_report_rel(tp_dev, REL_X, x); + input_report_rel(tp_dev, REL_Y, y); + + input_sync(tp_dev); + + break; + + default: + /* Dump unexpected packet sequences if debug=1 (default) */ + if (etd->debug == 1) + elantech_packet_dump(psmouse); + + break; + } +} + /* * Interpret complete data packets and report absolute mode input events for * hardware version 3. (12 byte packets for two fingers) @@ -500,6 +563,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse) } else { input_report_key(dev, BTN_LEFT, packet[0] & 0x01); input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); + input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04); } input_mt_report_pointer_emulation(dev, true); @@ -715,6 +779,8 @@ static int elantech_packet_check_v3(struct psmouse *psmouse) if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c) return PACKET_V3_TAIL; + if ((packet[3] & 0x0f) == 0x06) + return PACKET_TRACKPOINT; } return PACKET_UNKNOWN; @@ -727,6 +793,9 @@ static int elantech_packet_check_v4(struct psmouse *psmouse) unsigned char packet_type = packet[3] & 0x03; bool sanity_check; + if ((packet[3] & 0x0f) == 0x06) + return PACKET_TRACKPOINT; + /* * Sanity check based on the constant bits of a packet. * The constant bits change depending on the value of @@ -791,22 +860,40 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) case 3: packet_type = elantech_packet_check_v3(psmouse); - /* ignore debounce */ - if (packet_type == PACKET_DEBOUNCE) - return PSMOUSE_FULL_PACKET; - - if (packet_type == PACKET_UNKNOWN) + switch (packet_type) { + case PACKET_UNKNOWN: return PSMOUSE_BAD_DATA; - elantech_report_absolute_v3(psmouse, packet_type); + case PACKET_DEBOUNCE: + /* ignore debounce */ + break; + + case PACKET_TRACKPOINT: + elantech_report_trackpoint(psmouse, packet_type); + break; + + default: + elantech_report_absolute_v3(psmouse, packet_type); + break; + } + break; case 4: packet_type = elantech_packet_check_v4(psmouse); - if (packet_type == PACKET_UNKNOWN) + switch (packet_type) { + case PACKET_UNKNOWN: return PSMOUSE_BAD_DATA; - elantech_report_absolute_v4(psmouse, packet_type); + case PACKET_TRACKPOINT: + elantech_report_trackpoint(psmouse, packet_type); + break; + + default: + elantech_report_absolute_v4(psmouse, packet_type); + break; + } + break; } @@ -1018,8 +1105,10 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad + * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) + * Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*) * Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons * Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad * Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad @@ -1029,6 +1118,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Samsung RF710 0x450f00 ? 2 hw buttons * System76 Pangolin 0x250f01 ? 2 hw buttons * (*) + 3 trackpoint buttons + * (**) + 0 trackpoint buttons + * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps */ static void elantech_set_buttonpad_prop(struct psmouse *psmouse) { @@ -1042,6 +1133,22 @@ static void elantech_set_buttonpad_prop(struct psmouse *psmouse) } /* + * Some hw_version 4 models do have a middle button + */ +static const struct dmi_system_id elantech_dmi_has_middle_button[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) + { + /* Fujitsu H730 has a middle button */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), + }, + }, +#endif + { } +}; + +/* * Set the appropriate event bits for the input subsystem */ static int elantech_set_input_params(struct psmouse *psmouse) @@ -1060,6 +1167,8 @@ static int elantech_set_input_params(struct psmouse *psmouse) __clear_bit(EV_REL, dev->evbit); __set_bit(BTN_LEFT, dev->keybit); + if (dmi_check_system(elantech_dmi_has_middle_button)) + __set_bit(BTN_MIDDLE, dev->keybit); __set_bit(BTN_RIGHT, dev->keybit); __set_bit(BTN_TOUCH, dev->keybit); @@ -1221,6 +1330,7 @@ ELANTECH_INT_ATTR(reg_25, 0x25); ELANTECH_INT_ATTR(reg_26, 0x26); ELANTECH_INT_ATTR(debug, 0); ELANTECH_INT_ATTR(paritycheck, 0); +ELANTECH_INT_ATTR(crc_enabled, 0); static struct attribute *elantech_attrs[] = { &psmouse_attr_reg_07.dattr.attr, @@ -1235,6 +1345,7 @@ static struct attribute *elantech_attrs[] = { &psmouse_attr_reg_26.dattr.attr, &psmouse_attr_debug.dattr.attr, &psmouse_attr_paritycheck.dattr.attr, + &psmouse_attr_crc_enabled.dattr.attr, NULL }; @@ -1253,6 +1364,13 @@ static bool elantech_is_signature_valid(const unsigned char *param) if (param[1] == 0) return true; + /* + * Some models have a revision higher then 20. Meaning param[2] may + * be 10 or 20, skip the rates check for these. + */ + if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40) + return true; + for (i = 0; i < ARRAY_SIZE(rates); i++) if (param[2] == rates[i]) return false; @@ -1324,6 +1442,10 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties) */ static void elantech_disconnect(struct psmouse *psmouse) { + struct elantech_data *etd = psmouse->private; + + if (etd->tp_dev) + input_unregister_device(etd->tp_dev); sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &elantech_attr_group); kfree(psmouse->private); @@ -1350,6 +1472,22 @@ static int elantech_reconnect(struct psmouse *psmouse) } /* + * Some hw_version 4 models do not work with crc_disabled + */ +static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) + { + /* Fujitsu H730 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), + }, + }, +#endif + { } +}; + +/* * Some hw_version 3 models go into error state when we try to set * bit 3 and/or bit 1 of r10. */ @@ -1424,7 +1562,8 @@ static int elantech_set_properties(struct elantech_data *etd) * The signatures of v3 and v4 packets change depending on the * value of this hardware flag. */ - etd->crc_enabled = ((etd->fw_version & 0x4000) == 0x4000); + etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 || + dmi_check_system(elantech_dmi_force_crc_enabled); /* Enable real hardware resolution on hw_version 3 ? */ etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table); @@ -1438,8 +1577,10 @@ static int elantech_set_properties(struct elantech_data *etd) int elantech_init(struct psmouse *psmouse) { struct elantech_data *etd; - int i, error; + int i; + int error = -EINVAL; unsigned char param[3]; + struct input_dev *tp_dev; psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); if (!etd) @@ -1498,14 +1639,53 @@ int elantech_init(struct psmouse *psmouse) goto init_fail; } + /* The MSB indicates the presence of the trackpoint */ + if ((etd->capabilities[0] & 0x80) == 0x80) { + tp_dev = input_allocate_device(); + + if (!tp_dev) { + error = -ENOMEM; + goto init_fail_tp_alloc; + } + + etd->tp_dev = tp_dev; + snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1", + psmouse->ps2dev.serio->phys); + tp_dev->phys = etd->tp_phys; + tp_dev->name = "Elantech PS/2 TrackPoint"; + tp_dev->id.bustype = BUS_I8042; + tp_dev->id.vendor = 0x0002; + tp_dev->id.product = PSMOUSE_ELANTECH; + tp_dev->id.version = 0x0000; + tp_dev->dev.parent = &psmouse->ps2dev.serio->dev; + tp_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + tp_dev->relbit[BIT_WORD(REL_X)] = + BIT_MASK(REL_X) | BIT_MASK(REL_Y); + tp_dev->keybit[BIT_WORD(BTN_LEFT)] = + BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | + BIT_MASK(BTN_RIGHT); + + __set_bit(INPUT_PROP_POINTER, tp_dev->propbit); + __set_bit(INPUT_PROP_POINTING_STICK, tp_dev->propbit); + + error = input_register_device(etd->tp_dev); + if (error < 0) + goto init_fail_tp_reg; + } + psmouse->protocol_handler = elantech_process_byte; psmouse->disconnect = elantech_disconnect; psmouse->reconnect = elantech_reconnect; psmouse->pktsize = etd->hw_version > 1 ? 6 : 4; return 0; - + init_fail_tp_reg: + input_free_device(tp_dev); + init_fail_tp_alloc: + sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, + &elantech_attr_group); init_fail: + psmouse_reset(psmouse); kfree(etd); - return -1; + return error; } diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 9e0e2a1f340d..6f3afec02f03 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -94,6 +94,7 @@ #define PACKET_V4_HEAD 0x05 #define PACKET_V4_MOTION 0x06 #define PACKET_V4_STATUS 0x07 +#define PACKET_TRACKPOINT 0x08 /* * track up to 5 fingers for v4 hardware @@ -114,6 +115,8 @@ struct finger_pos { }; struct elantech_data { + struct input_dev *tp_dev; /* Relative device for trackpoint */ + char tp_phys[32]; unsigned char reg_07; unsigned char reg_10; unsigned char reg_11; 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 @@ +/* + * Focaltech TouchPad PS/2 mouse driver + * + * Copyright (c) 2014 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Red Hat authors: + * + * Hans de Goede <hdegoede@redhat.com> + */ + +/* + * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with + * detection only, to avoid further detection attempts confusing the touchpad + * this way it at least works in PS/2 mouse compatibility mode. + */ + +#include <linux/device.h> +#include <linux/libps2.h> +#include "psmouse.h" + +static const char * const focaltech_pnp_ids[] = { + "FLT0101", + "FLT0102", + "FLT0103", + NULL +}; + +int focaltech_detect(struct psmouse *psmouse, bool set_properties) +{ + if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids)) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "FocalTech"; + psmouse->name = "FocalTech Touchpad in mouse emulation mode"; + } + + return 0; +} + +int focaltech_init(struct psmouse *psmouse) +{ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); + psmouse_reset(psmouse); + + return 0; +} diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h new file mode 100644 index 000000000000..498650c61e28 --- /dev/null +++ b/drivers/input/mouse/focaltech.h @@ -0,0 +1,22 @@ +/* + * Focaltech TouchPad PS/2 mouse driver + * + * Copyright (c) 2014 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Red Hat authors: + * + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef _FOCALTECH_H +#define _FOCALTECH_H + +int focaltech_detect(struct psmouse *psmouse, bool set_properties); +int focaltech_init(struct psmouse *psmouse); + +#endif diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index cff065f6261c..95a3a6e2faf6 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -35,6 +35,7 @@ #include "elantech.h" #include "sentelic.h" #include "cypress_ps2.h" +#include "focaltech.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -462,6 +463,20 @@ static int psmouse_poll(struct psmouse *psmouse) PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); } +/* + * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids. + */ +bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) +{ + int i; + + if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) + for (i = 0; ids[i]; i++) + if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) + return true; + + return false; +} /* * Genius NetMouse magic init. @@ -670,6 +685,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) __set_bit(REL_X, input_dev->relbit); __set_bit(REL_Y, input_dev->relbit); + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + psmouse->set_rate = psmouse_set_rate; psmouse->set_resolution = psmouse_set_resolution; psmouse->poll = psmouse_poll; @@ -706,6 +723,21 @@ static int psmouse_extensions(struct psmouse *psmouse, { bool synaptics_hardware = false; +/* Always check for focaltech, this is safe as it uses pnp-id matching */ + if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { + if (!set_properties || focaltech_init(psmouse) == 0) { + /* + * Not supported yet, use bare protocol. + * Note that we need to also restrict + * psmouse_max_proto so that psmouse_initialize() + * does not try to reset rate and resolution, + * because even that upsets the device. + */ + psmouse_max_proto = PSMOUSE_PS2; + return PSMOUSE_PS2; + } + } + /* * We always check for lifebook because it does not disturb mouse * (it only checks DMI information). @@ -1504,16 +1536,9 @@ static int psmouse_reconnect(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *parent = NULL; - struct serio_driver *drv = serio->drv; unsigned char type; int rc = -1; - if (!drv || !psmouse) { - psmouse_dbg(psmouse, - "reconnect request, but serio is disconnected, ignoring...\n"); - return -1; - } - mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { 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); psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse); int psmouse_activate(struct psmouse *psmouse); int psmouse_deactivate(struct psmouse *psmouse); +bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]); struct psmouse_attribute { struct device_attribute dattr; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index ef9e0b8a9aa7..2a7a9174c702 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -117,6 +117,9 @@ void synaptics_reset(struct psmouse *psmouse) } #ifdef CONFIG_MOUSE_PS2_SYNAPTICS + +static bool cr48_profile_sensor; + struct min_max_quirk { const char * const *pnp_ids; int x_min, x_max, y_min, y_max; @@ -132,8 +135,8 @@ static const struct min_max_quirk min_max_pnpid_table[] = { 1232, 5710, 1156, 4696 }, { - (const char * const []){"LEN0034", "LEN0036", "LEN2002", - "LEN2004", NULL}, + (const char * const []){"LEN0034", "LEN0036", "LEN0039", + "LEN2002", "LEN2004", NULL}, 1024, 5112, 2024, 4832 }, { @@ -160,6 +163,7 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN0036", /* T440 */ "LEN0037", "LEN0038", + "LEN0039", /* T440s */ "LEN0041", "LEN0042", /* Yoga */ "LEN0045", @@ -182,18 +186,6 @@ static const char * const topbuttonpad_pnp_ids[] = { NULL }; -static bool matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) -{ - int i; - - if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) - for (i = 0; ids[i]; i++) - if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) - return true; - - return false; -} - /***************************************************************************** * Synaptics communications functions ****************************************************************************/ @@ -359,7 +351,8 @@ static int synaptics_resolution(struct psmouse *psmouse) } for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) { - if (matches_pnp_id(psmouse, min_max_pnpid_table[i].pnp_ids)) { + if (psmouse_matches_pnp_id(psmouse, + min_max_pnpid_table[i].pnp_ids)) { priv->x_min = min_max_pnpid_table[i].x_min; priv->x_max = min_max_pnpid_table[i].x_max; priv->y_min = min_max_pnpid_table[i].y_min; @@ -615,6 +608,8 @@ static void synaptics_parse_agm(const unsigned char buf[], priv->agm_pending = true; } +static bool is_forcepad; + static int synaptics_parse_hw_state(const unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) @@ -626,10 +621,61 @@ static int synaptics_parse_hw_state(const unsigned char buf[], ((buf[0] & 0x04) >> 1) | ((buf[3] & 0x04) >> 2)); + if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || + SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) && + hw->w == 2) { + synaptics_parse_agm(buf, priv, hw); + return 1; + } + + hw->x = (((buf[3] & 0x10) << 8) | + ((buf[1] & 0x0f) << 8) | + buf[4]); + hw->y = (((buf[3] & 0x20) << 7) | + ((buf[1] & 0xf0) << 4) | + buf[5]); + hw->z = buf[2]; + hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; - if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + if (is_forcepad) { + /* + * ForcePads, like Clickpads, use middle button + * bits to report primary button clicks. + * Unfortunately they report primary button not + * only when user presses on the pad above certain + * threshold, but also when there are more than one + * finger on the touchpad, which interferes with + * out multi-finger gestures. + */ + if (hw->z == 0) { + /* No contacts */ + priv->press = priv->report_press = false; + } else if (hw->w >= 4 && ((buf[0] ^ buf[3]) & 0x01)) { + /* + * Single-finger touch with pressure above + * the threshold. If pressure stays long + * enough, we'll start reporting primary + * button. We rely on the device continuing + * sending data even if finger does not + * move. + */ + if (!priv->press) { + priv->press_start = jiffies; + priv->press = true; + } else if (time_after(jiffies, + priv->press_start + + msecs_to_jiffies(50))) { + priv->report_press = true; + } + } else { + priv->press = false; + } + + hw->left = priv->report_press; + + } else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { /* * Clickpad's button is transmitted as middle button, * however, since it is primary button, we will report @@ -648,21 +694,6 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; } - if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || - SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) && - hw->w == 2) { - synaptics_parse_agm(buf, priv, hw); - return 1; - } - - hw->x = (((buf[3] & 0x10) << 8) | - ((buf[1] & 0x0f) << 8) | - buf[4]); - hw->y = (((buf[3] & 0x20) << 7) | - ((buf[1] & 0xf0) << 4) | - buf[5]); - hw->z = buf[2]; - if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && ((buf[0] ^ buf[3]) & 0x02)) { switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { @@ -1152,6 +1183,42 @@ static void synaptics_image_sensor_process(struct psmouse *psmouse, priv->agm_pending = false; } +static void synaptics_profile_sensor_process(struct psmouse *psmouse, + struct synaptics_hw_state *sgm, + int num_fingers) +{ + struct input_dev *dev = psmouse->dev; + struct synaptics_data *priv = psmouse->private; + struct synaptics_hw_state *hw[2] = { sgm, &priv->agm }; + struct input_mt_pos pos[2]; + int slot[2], nsemi, i; + + nsemi = clamp_val(num_fingers, 0, 2); + + for (i = 0; i < nsemi; i++) { + pos[i].x = hw[i]->x; + pos[i].y = synaptics_invert_y(hw[i]->y); + } + + input_mt_assign_slots(dev, slot, pos, nsemi); + + for (i = 0; i < nsemi; i++) { + input_mt_slot(dev, slot[i]); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x); + input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y); + input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z); + } + + input_mt_drop_unused(dev); + input_mt_report_pointer_emulation(dev, false); + input_mt_report_finger_count(dev, num_fingers); + + synaptics_report_buttons(psmouse, sgm); + + input_sync(dev); +} + /* * called for each full received packet from the touchpad */ @@ -1215,6 +1282,11 @@ static void synaptics_process_packet(struct psmouse *psmouse) finger_width = 0; } + if (cr48_profile_sensor) { + synaptics_profile_sensor_process(psmouse, &hw, num_fingers); + return; + } + if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) synaptics_report_semi_mt_data(dev, &hw, &priv->agm, num_fingers); @@ -1360,6 +1432,9 @@ static void set_input_params(struct psmouse *psmouse, set_abs_position_params(dev, priv, ABS_X, ABS_Y); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + if (cr48_profile_sensor) + input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { set_abs_position_params(dev, priv, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); @@ -1371,11 +1446,16 @@ static void set_input_params(struct psmouse *psmouse, __set_bit(BTN_TOOL_QUADTAP, dev->keybit); __set_bit(BTN_TOOL_QUINTTAP, dev->keybit); } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { - /* Non-image sensors with AGM use semi-mt */ - __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); - input_mt_init_slots(dev, 2, 0); set_abs_position_params(dev, priv, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); + /* + * Profile sensor in CR-48 tracks contacts reasonably well, + * other non-image sensors with AGM use semi-mt. + */ + input_mt_init_slots(dev, 2, + INPUT_MT_POINTER | + (cr48_profile_sensor ? + INPUT_MT_TRACK : INPUT_MT_SEMI_MT)); } if (SYN_CAP_PALMDETECT(priv->capabilities)) @@ -1404,7 +1484,7 @@ static void set_input_params(struct psmouse *psmouse, if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); - if (matches_pnp_id(psmouse, topbuttonpad_pnp_ids)) + if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids)) __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit); /* Clickpads report only left button */ __clear_bit(BTN_RIGHT, dev->keybit); @@ -1577,10 +1657,42 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = { { } }; +static const struct dmi_system_id __initconst cr48_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) + { + /* Cr-48 Chromebook (Codename Mario) */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IEC"), + DMI_MATCH(DMI_PRODUCT_NAME, "Mario"), + }, + }, +#endif + { } +}; + +static const struct dmi_system_id forcepad_dmi_table[] __initconst = { +#if defined(CONFIG_DMI) && defined(CONFIG_X86) + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook Folio 1040 G1"), + }, + }, +#endif + { } +}; + void __init synaptics_module_init(void) { impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); broken_olpc_ec = dmi_check_system(olpc_dmi_table); + cr48_profile_sensor = dmi_check_system(cr48_dmi_table); + + /* + * Unfortunately ForcePad capability is not exported over PS/2, + * so we have to resort to checking DMI. + */ + is_forcepad = dmi_check_system(forcepad_dmi_table); } static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index e594af0b264b..1bd01f21783b 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -77,6 +77,8 @@ * for noise. * 2 0x08 image sensor image sensor tracks 5 fingers, but only * reports 2. + * 2 0x01 uniform clickpad whole clickpad moves instead of being + * hinged at the top. * 2 0x20 report min query 0x0f gives min coord reported */ #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */ @@ -177,6 +179,11 @@ struct synaptics_data { */ struct synaptics_hw_state agm; bool agm_pending; /* new AGM packet received */ + + /* ForcePad handling */ + unsigned long press_start; + bool press; + bool report_press; }; void synaptics_module_init(void); diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c index e122bda16aab..6bcc0189c1c9 100644 --- a/drivers/input/mouse/synaptics_usb.c +++ b/drivers/input/mouse/synaptics_usb.c @@ -387,6 +387,7 @@ static int synusb_probe(struct usb_interface *intf, __set_bit(EV_REL, input_dev->evbit); __set_bit(REL_X, input_dev->relbit); __set_bit(REL_Y, input_dev->relbit); + __set_bit(INPUT_PROP_POINTING_STICK, input_dev->propbit); input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0); } else { input_set_abs_params(input_dev, ABS_X, @@ -401,6 +402,11 @@ static int synusb_probe(struct usb_interface *intf, __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); } + if (synusb->flags & SYNUSB_TOUCHSCREEN) + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + else + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_RIGHT, input_dev->keybit); __set_bit(BTN_MIDDLE, input_dev->keybit); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index ca843b6cf6bd..30c8b6998808 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -393,6 +393,9 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) if ((button_info & 0x0f) >= 3) __set_bit(BTN_MIDDLE, psmouse->dev->keybit); + __set_bit(INPUT_PROP_POINTER, psmouse->dev->propbit); + __set_bit(INPUT_PROP_POINTING_STICK, psmouse->dev->propbit); + trackpoint_defaults(psmouse->private); error = trackpoint_power_on_reset(&psmouse->ps2dev); diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c index 38298232124f..abd494411e69 100644 --- a/drivers/input/mouse/vsxxxaa.c +++ b/drivers/input/mouse/vsxxxaa.c @@ -128,7 +128,7 @@ static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num) if (num >= mouse->count) { mouse->count = 0; } else { - memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num); + memmove(mouse->buf, mouse->buf + num, BUFLEN - num); mouse->count -= num; } } diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c index cce69d6b9587..58781c8a8aec 100644 --- a/drivers/input/serio/altera_ps2.c +++ b/drivers/input/serio/altera_ps2.c @@ -37,7 +37,7 @@ static irqreturn_t altera_ps2_rxint(int irq, void *dev_id) { struct ps2if *ps2if = dev_id; unsigned int status; - int handled = IRQ_NONE; + irqreturn_t handled = IRQ_NONE; while ((status = readl(ps2if->base)) & 0xffff0000) { serio_interrupt(ps2if->io, status & 0xff, 0); @@ -74,7 +74,7 @@ static void altera_ps2_close(struct serio *io) { struct ps2if *ps2if = io->port_data; - writel(0, ps2if->base); /* disable rx irq */ + writel(0, ps2if->base + 4); /* disable rx irq */ } /* diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c index 613261994621..e74e5d6e5f9f 100644 --- a/drivers/input/serio/hyperv-keyboard.c +++ b/drivers/input/serio/hyperv-keyboard.c @@ -170,6 +170,15 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev, serio_interrupt(kbd_dev->hv_serio, scan_code, 0); } spin_unlock_irqrestore(&kbd_dev->lock, flags); + + /* + * Only trigger a wakeup on key down, otherwise + * "echo freeze > /sys/power/state" can't really enter the + * state because the Enter-UP can trigger a wakeup at once. + */ + if (!(info & IS_BREAK)) + pm_wakeup_event(&hv_dev->device, 0); + break; default: @@ -376,6 +385,9 @@ static int hv_kbd_probe(struct hv_device *hv_dev, goto err_close_vmbus; serio_register_port(kbd_dev->hv_serio); + + device_init_wakeup(&hv_dev->device, true); + return 0; err_close_vmbus: @@ -390,6 +402,7 @@ static int hv_kbd_remove(struct hv_device *hv_dev) { struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev); + device_init_wakeup(&hv_dev->device, false); serio_unregister_port(kbd_dev->hv_serio); vmbus_close(hv_dev->channel); kfree(kbd_dev); diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index d6aa4c67dbb6..93cb7912703c 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -17,7 +17,6 @@ static int i8042_aux_irq = -1; #define I8042_MUX_PHYS_DESC "sparcps2/serio%d" static void __iomem *kbd_iobase; -static struct resource *kbd_res; #define I8042_COMMAND_REG (kbd_iobase + 0x64UL) #define I8042_DATA_REG (kbd_iobase + 0x60UL) @@ -44,6 +43,8 @@ static inline void i8042_write_command(int val) #ifdef CONFIG_PCI +static struct resource *kbd_res; + #define OBP_PS2KBD_NAME1 "kb_ps2" #define OBP_PS2KBD_NAME2 "keyboard" #define OBP_PS2MS_NAME1 "kdmouse" diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 136b7b204f56..faeeb1372462 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -101,6 +101,12 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { }, { .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"), + }, + }, + { + .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), @@ -465,6 +471,20 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), }, }, + { + /* Asus X450LCP */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"), + }, + }, + { + /* Avatar AVIU-145A6 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel"), + DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"), + }, + }, { } }; @@ -608,6 +628,30 @@ static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"), }, }, + { + /* Fujitsu A544 laptop */ + /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"), + }, + }, + { + /* Fujitsu AH544 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"), + }, + }, + { + /* Fujitsu U574 laptop */ + /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"), + }, + }, { } }; diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 3807c3e971cc..f5a98af3b325 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1254,6 +1254,8 @@ static int __init i8042_create_aux_port(int idx) } else { snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx); snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1); + strlcpy(serio->firmware_id, i8042_aux_firmware_id, + sizeof(serio->firmware_id)); } port->serio = serio; diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index b29134de983b..d399b8b0f000 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -524,8 +524,8 @@ static void serio_init_port(struct serio *serio) spin_lock_init(&serio->lock); mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); - dev_set_name(&serio->dev, "serio%ld", - (long)atomic_inc_return(&serio_no) - 1); + dev_set_name(&serio->dev, "serio%lu", + (unsigned long)atomic_inc_return(&serio_no) - 1); serio->dev.bus = &serio_bus; serio->dev.release = serio_release_port; serio->dev.groups = serio_device_attr_groups; diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 0cb7ef59071b..69175b825346 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/serio.h> #include <linux/tty.h> +#include <linux/compat.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Input device TTY line discipline"); @@ -198,28 +199,55 @@ static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, u return 0; } +static void serport_set_type(struct tty_struct *tty, unsigned long type) +{ + struct serport *serport = tty->disc_data; + + serport->id.proto = type & 0x000000ff; + serport->id.id = (type & 0x0000ff00) >> 8; + serport->id.extra = (type & 0x00ff0000) >> 16; +} + /* * serport_ldisc_ioctl() allows to set the port protocol, and device ID */ -static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) +static int serport_ldisc_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) { - struct serport *serport = (struct serport*) tty->disc_data; - unsigned long type; - if (cmd == SPIOCSTYPE) { + unsigned long type; + if (get_user(type, (unsigned long __user *) arg)) return -EFAULT; - serport->id.proto = type & 0x000000ff; - serport->id.id = (type & 0x0000ff00) >> 8; - serport->id.extra = (type & 0x00ff0000) >> 16; + serport_set_type(tty, type); + return 0; + } + + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +#define COMPAT_SPIOCSTYPE _IOW('q', 0x01, compat_ulong_t) +static long serport_ldisc_compat_ioctl(struct tty_struct *tty, + struct file *file, + unsigned int cmd, unsigned long arg) +{ + if (cmd == COMPAT_SPIOCSTYPE) { + void __user *uarg = compat_ptr(arg); + compat_ulong_t compat_type; + + if (get_user(compat_type, (compat_ulong_t __user *)uarg)) + return -EFAULT; + serport_set_type(tty, compat_type); return 0; } return -EINVAL; } +#endif static void serport_ldisc_write_wakeup(struct tty_struct * tty) { @@ -243,6 +271,9 @@ static struct tty_ldisc_ops serport_ldisc = { .close = serport_ldisc_close, .read = serport_ldisc_read, .ioctl = serport_ldisc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = serport_ldisc_compat_ioctl, +#endif .receive_buf = serport_ldisc_receive, .write_wakeup = serport_ldisc_write_wakeup }; diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index bed7cbf84cfd..623bb9e0d5a4 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -73,20 +73,14 @@ config TABLET_USB_KBTAB To compile this driver as a module, choose M here: the module will be called kbtab. -config TABLET_USB_WACOM - tristate "Wacom Intuos/Graphire tablet support (USB)" - depends on USB_ARCH_HAS_HCD - select POWER_SUPPLY - select USB - select NEW_LEDS - select LEDS_CLASS +config TABLET_SERIAL_WACOM4 + tristate "Wacom protocol 4 serial tablet support" + select SERIO help - Say Y here if you want to use the USB version of the Wacom Intuos - or Graphire tablet. Make sure to say Y to "Mouse support" - (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" - (CONFIG_INPUT_EVDEV) as well. + Say Y here if you want to use Wacom protocol 4 serial tablets. + E.g. serial versions of the Cintiq, Graphire or Penpartner. To compile this driver as a module, choose M here: the - module will be called wacom. + module will be called wacom_serial4. endif diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile index 3f6c25220638..2e130101cf3c 100644 --- a/drivers/input/tablet/Makefile +++ b/drivers/input/tablet/Makefile @@ -2,12 +2,10 @@ # Makefile for the tablet drivers # -# Multipart objects. -wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o -obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o +obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h deleted file mode 100644 index 9ebf0ed3b3b3..000000000000 --- a/drivers/input/tablet/wacom.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * drivers/input/tablet/wacom.h - * - * USB Wacom tablet support - * - * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz> - * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk> - * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at> - * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org> - * Copyright (c) 2000 James E. Blair <corvus@gnu.org> - * Copyright (c) 2000 Daniel Egger <egger@suse.de> - * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com> - * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be> - * Copyright (c) 2002-2011 Ping Cheng <pingc@wacom.com> - * - * ChangeLog: - * v0.1 (vp) - Initial release - * v0.2 (aba) - Support for all buttons / combinations - * v0.3 (vp) - Support for Intuos added - * v0.4 (sm) - Support for more Intuos models, menustrip - * relative mode, proximity. - * v0.5 (vp) - Big cleanup, nifty features removed, - * they belong in userspace - * v1.8 (vp) - Submit URB only when operating, moved to CVS, - * use input_report_key instead of report_btn and - * other cleanups - * v1.11 (vp) - Add URB ->dev setting for new kernels - * v1.11 (jb) - Add support for the 4D Mouse & Lens - * v1.12 (de) - Add support for two more inking pen IDs - * v1.14 (vp) - Use new USB device id probing scheme. - * Fix Wacom Graphire mouse wheel - * v1.18 (vp) - Fix mouse wheel direction - * Make mouse relative - * v1.20 (fl) - Report tool id for Intuos devices - * - Multi tools support - * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...) - * - Add PL models support - * - Fix Wacom Graphire mouse wheel again - * v1.21 (vp) - Removed protocol descriptions - * - Added MISC_SERIAL for tool serial numbers - * (gb) - Identify version on module load. - * v1.21.1 (fl) - added Graphire2 support - * v1.21.2 (fl) - added Intuos2 support - * - added all the PL ids - * v1.21.3 (fl) - added another eraser id from Neil Okamoto - * - added smooth filter for Graphire from Peri Hankey - * - added PenPartner support from Olaf van Es - * - new tool ids from Ole Martin Bjoerndalen - * v1.29 (pc) - Add support for more tablets - * - Fix pressure reporting - * v1.30 (vp) - Merge 2.4 and 2.5 drivers - * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse - * - Cleanups here and there - * v1.30.1 (pi) - Added Graphire3 support - * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ... - * v1.43 (pc) - Added support for Cintiq 21UX - * - Fixed a Graphire bug - * - Merged wacom_intuos3_irq into wacom_intuos_irq - * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc. - * - Report Device IDs - * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19 - * - Minor data report fix - * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c, - * - where wacom_sys.c deals with system specific code, - * - and wacom_wac.c deals with Wacom specific code - * - Support Intuos3 4x6 - * v1.47 (pc) - Added support for Bamboo - * v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX - * v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A) - * v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28 - * v1.51 (pc) - Added support for Intuos4 - * v1.52 (pc) - Query Wacom data upon system resume - * - add defines for features->type - * - add new devices (0x9F, 0xE2, and 0XE3) - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#ifndef WACOM_H -#define WACOM_H -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/usb/input.h> -#include <linux/power_supply.h> -#include <asm/unaligned.h> - -/* - * Version Information - */ -#define DRIVER_VERSION "v1.53" -#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>" -#define DRIVER_DESC "USB Wacom tablet driver" -#define DRIVER_LICENSE "GPL" - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - -#define USB_VENDOR_ID_WACOM 0x056a -#define USB_VENDOR_ID_LENOVO 0x17ef - -struct wacom { - dma_addr_t data_dma; - struct usb_device *usbdev; - struct usb_interface *intf; - struct urb *irq; - struct wacom_wac wacom_wac; - struct mutex lock; - struct work_struct work; - bool open; - char phys[32]; - struct wacom_led { - u8 select[2]; /* status led selector (0..3) */ - u8 llv; /* status led brightness no button (1..127) */ - u8 hlv; /* status led brightness button pressed (1..127) */ - u8 img_lum; /* OLED matrix display brightness */ - } led; - struct power_supply battery; -}; - -static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) -{ - struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); - schedule_work(&wacom->work); -} - -extern const struct usb_device_id wacom_ids[]; - -void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); -void wacom_setup_device_quirks(struct wacom_features *features); -int wacom_setup_input_capabilities(struct input_dev *input_dev, - struct wacom_wac *wacom_wac); -#endif diff --git a/drivers/input/tablet/wacom_serial4.c b/drivers/input/tablet/wacom_serial4.c new file mode 100644 index 000000000000..20ab802461e7 --- /dev/null +++ b/drivers/input/tablet/wacom_serial4.c @@ -0,0 +1,620 @@ +/* + * Wacom protocol 4 serial tablet driver + * + * Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * Copyright 2011-2012 Julian Squires <julian@cipht.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version of 2 of the License, or (at your + * option) any later version. See the file COPYING in the main directory of + * this archive for more details. + * + * Many thanks to Bill Seremetis, without whom PenPartner support + * would not have been possible. Thanks to Patrick Mahoney. + * + * This driver was developed with reference to much code written by others, + * particularly: + * - elo, gunze drivers by Vojtech Pavlik <vojtech@ucw.cz>; + * - wacom_w8001 driver by Jaya Kumar <jayakumar.lkml@gmail.com>; + * - the USB wacom input driver, credited to many people + * (see drivers/input/tablet/wacom.h); + * - new and old versions of linuxwacom / xf86-input-wacom credited to + * Frederic Lepied, France. <Lepied@XFree86.org> and + * Ping Cheng, Wacom. <pingc@wacom.com>; + * - and xf86wacom.c (a presumably ancient version of the linuxwacom code), + * by Frederic Lepied and Raph Levien <raph@gtk.org>. + * + * To do: + * - support pad buttons; (requires access to a model with pad buttons) + * - support (protocol 4-style) tilt (requires access to a > 1.4 rom model) + */ + +/* + * Wacom serial protocol 4 documentation taken from linuxwacom-0.9.9 code, + * protocol 4 uses 7 or 9 byte of data in the following format: + * + * Byte 1 + * bit 7 Sync bit always 1 + * bit 6 Pointing device detected + * bit 5 Cursor = 0 / Stylus = 1 + * bit 4 Reserved + * bit 3 1 if a button on the pointing device has been pressed + * bit 2 P0 (optional) + * bit 1 X15 + * bit 0 X14 + * + * Byte 2 + * bit 7 Always 0 + * bits 6-0 = X13 - X7 + * + * Byte 3 + * bit 7 Always 0 + * bits 6-0 = X6 - X0 + * + * Byte 4 + * bit 7 Always 0 + * bit 6 B3 + * bit 5 B2 + * bit 4 B1 + * bit 3 B0 + * bit 2 P1 (optional) + * bit 1 Y15 + * bit 0 Y14 + * + * Byte 5 + * bit 7 Always 0 + * bits 6-0 = Y13 - Y7 + * + * Byte 6 + * bit 7 Always 0 + * bits 6-0 = Y6 - Y0 + * + * Byte 7 + * bit 7 Always 0 + * bit 6 Sign of pressure data; or wheel-rel for cursor tool + * bit 5 P7; or REL1 for cursor tool + * bit 4 P6; or REL0 for cursor tool + * bit 3 P5 + * bit 2 P4 + * bit 1 P3 + * bit 0 P2 + * + * byte 8 and 9 are optional and present only + * in tilt mode. + * + * Byte 8 + * bit 7 Always 0 + * bit 6 Sign of tilt X + * bit 5 Xt6 + * bit 4 Xt5 + * bit 3 Xt4 + * bit 2 Xt3 + * bit 1 Xt2 + * bit 0 Xt1 + * + * Byte 9 + * bit 7 Always 0 + * bit 6 Sign of tilt Y + * bit 5 Yt6 + * bit 4 Yt5 + * bit 3 Yt4 + * bit 2 Yt3 + * bit 1 Yt2 + * bit 0 Yt1 + */ + +#include <linux/completion.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <linux/slab.h> +#include <linux/string.h> + +MODULE_AUTHOR("Julian Squires <julian@cipht.net>, Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Wacom protocol 4 serial tablet driver"); +MODULE_LICENSE("GPL"); + +#define REQUEST_MODEL_AND_ROM_VERSION "~#" +#define REQUEST_MAX_COORDINATES "~C\r" +#define REQUEST_CONFIGURATION_STRING "~R\r" +#define REQUEST_RESET_TO_PROTOCOL_IV "\r#" +/* + * Note: sending "\r$\r" causes at least the Digitizer II to send + * packets in ASCII instead of binary. "\r#" seems to undo that. + */ + +#define COMMAND_START_SENDING_PACKETS "ST\r" +#define COMMAND_STOP_SENDING_PACKETS "SP\r" +#define COMMAND_MULTI_MODE_INPUT "MU1\r" +#define COMMAND_ORIGIN_IN_UPPER_LEFT "OC1\r" +#define COMMAND_ENABLE_ALL_MACRO_BUTTONS "~M0\r" +#define COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS "~M1\r" +#define COMMAND_TRANSMIT_AT_MAX_RATE "IT0\r" +#define COMMAND_DISABLE_INCREMENTAL_MODE "IN0\r" +#define COMMAND_ENABLE_CONTINUOUS_MODE "SR\r" +#define COMMAND_ENABLE_PRESSURE_MODE "PH1\r" +#define COMMAND_Z_FILTER "ZF1\r" + +/* Note that this is a protocol 4 packet without tilt information. */ +#define PACKET_LENGTH 7 +#define DATA_SIZE 32 + +/* flags */ +#define F_COVERS_SCREEN 0x01 +#define F_HAS_STYLUS2 0x02 +#define F_HAS_SCROLLWHEEL 0x04 + +/* device IDs */ +#define STYLUS_DEVICE_ID 0x02 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A + +enum { STYLUS = 1, ERASER, CURSOR }; + +static const struct { + int device_id; + int input_id; +} tools[] = { + { 0, 0 }, + { STYLUS_DEVICE_ID, BTN_TOOL_PEN }, + { ERASER_DEVICE_ID, BTN_TOOL_RUBBER }, + { CURSOR_DEVICE_ID, BTN_TOOL_MOUSE }, +}; + +struct wacom { + struct input_dev *dev; + struct completion cmd_done; + int result; + u8 expect; + u8 eraser_mask; + unsigned int extra_z_bits; + unsigned int flags; + unsigned int res_x, res_y; + unsigned int max_x, max_y; + unsigned int tool; + unsigned int idx; + u8 data[DATA_SIZE]; + char phys[32]; +}; + +enum { + MODEL_CINTIQ = 0x504C, /* PL */ + MODEL_CINTIQ2 = 0x4454, /* DT */ + MODEL_DIGITIZER_II = 0x5544, /* UD */ + MODEL_GRAPHIRE = 0x4554, /* ET */ + MODEL_PENPARTNER = 0x4354, /* CT */ +}; + +static void wacom_handle_model_response(struct wacom *wacom) +{ + int major_v, minor_v, r = 0; + char *p; + + p = strrchr(wacom->data, 'V'); + if (p) + r = sscanf(p + 1, "%u.%u", &major_v, &minor_v); + if (r != 2) + major_v = minor_v = 0; + + switch (wacom->data[2] << 8 | wacom->data[3]) { + case MODEL_CINTIQ: /* UNTESTED */ + case MODEL_CINTIQ2: + if ((wacom->data[2] << 8 | wacom->data[3]) == MODEL_CINTIQ) { + wacom->dev->name = "Wacom Cintiq"; + wacom->dev->id.version = MODEL_CINTIQ; + } else { + wacom->dev->name = "Wacom Cintiq II"; + wacom->dev->id.version = MODEL_CINTIQ2; + } + wacom->res_x = 508; + wacom->res_y = 508; + + switch (wacom->data[5] << 8 | wacom->data[6]) { + case 0x3731: /* PL-710 */ + wacom->res_x = 2540; + wacom->res_y = 2540; + /* fall through */ + case 0x3535: /* PL-550 */ + case 0x3830: /* PL-800 */ + wacom->extra_z_bits = 2; + } + + wacom->flags = F_COVERS_SCREEN; + break; + + case MODEL_PENPARTNER: + wacom->dev->name = "Wacom Penpartner"; + wacom->dev->id.version = MODEL_PENPARTNER; + wacom->res_x = 1000; + wacom->res_y = 1000; + break; + + case MODEL_GRAPHIRE: + wacom->dev->name = "Wacom Graphire"; + wacom->dev->id.version = MODEL_GRAPHIRE; + wacom->res_x = 1016; + wacom->res_y = 1016; + wacom->max_x = 5103; + wacom->max_y = 3711; + wacom->extra_z_bits = 2; + wacom->eraser_mask = 0x08; + wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL; + break; + + case MODEL_DIGITIZER_II: + wacom->dev->name = "Wacom Digitizer II"; + wacom->dev->id.version = MODEL_DIGITIZER_II; + if (major_v == 1 && minor_v <= 2) + wacom->extra_z_bits = 0; /* UNTESTED */ + break; + + default: + dev_err(&wacom->dev->dev, "Unsupported Wacom model %s\n", + wacom->data); + wacom->result = -ENODEV; + return; + } + + dev_info(&wacom->dev->dev, "%s tablet, version %u.%u\n", + wacom->dev->name, major_v, minor_v); +} + +static void wacom_handle_configuration_response(struct wacom *wacom) +{ + int r, skip; + + dev_dbg(&wacom->dev->dev, "Configuration string: %s\n", wacom->data); + r = sscanf(wacom->data, "~R%x,%u,%u,%u,%u", &skip, &skip, &skip, + &wacom->res_x, &wacom->res_y); + if (r != 5) + dev_warn(&wacom->dev->dev, "could not get resolution\n"); +} + +static void wacom_handle_coordinates_response(struct wacom *wacom) +{ + int r; + + dev_dbg(&wacom->dev->dev, "Coordinates string: %s\n", wacom->data); + r = sscanf(wacom->data, "~C%u,%u", &wacom->max_x, &wacom->max_y); + if (r != 2) + dev_warn(&wacom->dev->dev, "could not get max coordinates\n"); +} + +static void wacom_handle_response(struct wacom *wacom) +{ + if (wacom->data[0] != '~' || wacom->data[1] != wacom->expect) { + dev_err(&wacom->dev->dev, + "Wacom got an unexpected response: %s\n", wacom->data); + wacom->result = -EIO; + } else { + wacom->result = 0; + + switch (wacom->data[1]) { + case '#': + wacom_handle_model_response(wacom); + break; + case 'R': + wacom_handle_configuration_response(wacom); + break; + case 'C': + wacom_handle_coordinates_response(wacom); + break; + } + } + + complete(&wacom->cmd_done); +} + +static void wacom_handle_packet(struct wacom *wacom) +{ + u8 in_proximity_p, stylus_p, button; + unsigned int tool; + int x, y, z; + + in_proximity_p = wacom->data[0] & 0x40; + stylus_p = wacom->data[0] & 0x20; + button = (wacom->data[3] & 0x78) >> 3; + x = (wacom->data[0] & 3) << 14 | wacom->data[1]<<7 | wacom->data[2]; + y = (wacom->data[3] & 3) << 14 | wacom->data[4]<<7 | wacom->data[5]; + + if (in_proximity_p && stylus_p) { + z = wacom->data[6] & 0x7f; + if (wacom->extra_z_bits >= 1) + z = z << 1 | (wacom->data[3] & 0x4) >> 2; + if (wacom->extra_z_bits > 1) + z = z << 1 | (wacom->data[0] & 0x4) >> 2; + z = z ^ (0x40 << wacom->extra_z_bits); + } else { + z = -1; + } + + if (stylus_p) + tool = (button & wacom->eraser_mask) ? ERASER : STYLUS; + else + tool = CURSOR; + + if (tool != wacom->tool && wacom->tool != 0) { + input_report_key(wacom->dev, tools[wacom->tool].input_id, 0); + input_sync(wacom->dev); + } + wacom->tool = tool; + + input_report_key(wacom->dev, tools[tool].input_id, in_proximity_p); + input_report_abs(wacom->dev, ABS_MISC, + in_proximity_p ? tools[tool].device_id : 0); + input_report_abs(wacom->dev, ABS_X, x); + input_report_abs(wacom->dev, ABS_Y, y); + input_report_abs(wacom->dev, ABS_PRESSURE, z); + if (stylus_p) { + input_report_key(wacom->dev, BTN_TOUCH, button & 1); + input_report_key(wacom->dev, BTN_STYLUS, button & 2); + input_report_key(wacom->dev, BTN_STYLUS2, button & 4); + } else { + input_report_key(wacom->dev, BTN_LEFT, button & 1); + input_report_key(wacom->dev, BTN_RIGHT, button & 2); + input_report_key(wacom->dev, BTN_MIDDLE, button & 4); + /* handle relative wheel for non-stylus device */ + z = (wacom->data[6] & 0x30) >> 4; + if (wacom->data[6] & 0x40) + z = -z; + input_report_rel(wacom->dev, REL_WHEEL, z); + } + input_sync(wacom->dev); +} + +static void wacom_clear_data_buf(struct wacom *wacom) +{ + memset(wacom->data, 0, DATA_SIZE); + wacom->idx = 0; +} + +static irqreturn_t wacom_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct wacom *wacom = serio_get_drvdata(serio); + + if (data & 0x80) + wacom->idx = 0; + + /* + * We're either expecting a carriage return-terminated ASCII + * response string, or a seven-byte packet with the MSB set on + * the first byte. + * + * Note however that some tablets (the PenPartner, for + * example) don't send a carriage return at the end of a + * command. We handle these by waiting for timeout. + */ + if (data == '\r' && !(wacom->data[0] & 0x80)) { + wacom_handle_response(wacom); + wacom_clear_data_buf(wacom); + return IRQ_HANDLED; + } + + /* Leave place for 0 termination */ + if (wacom->idx > (DATA_SIZE - 2)) { + dev_dbg(&wacom->dev->dev, + "throwing away %d bytes of garbage\n", wacom->idx); + wacom_clear_data_buf(wacom); + } + wacom->data[wacom->idx++] = data; + + if (wacom->idx == PACKET_LENGTH && (wacom->data[0] & 0x80)) { + wacom_handle_packet(wacom); + wacom_clear_data_buf(wacom); + } + + return IRQ_HANDLED; +} + +static void wacom_disconnect(struct serio *serio) +{ + struct wacom *wacom = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(wacom->dev); + kfree(wacom); +} + +static int wacom_send(struct serio *serio, const u8 *command) +{ + int err = 0; + + for (; !err && *command; command++) + err = serio_write(serio, *command); + + return err; +} + +static int wacom_send_setup_string(struct wacom *wacom, struct serio *serio) +{ + const u8 *cmd; + + switch (wacom->dev->id.version) { + case MODEL_CINTIQ: /* UNTESTED */ + cmd = COMMAND_ORIGIN_IN_UPPER_LEFT + COMMAND_TRANSMIT_AT_MAX_RATE + COMMAND_ENABLE_CONTINUOUS_MODE + COMMAND_START_SENDING_PACKETS; + break; + + case MODEL_PENPARTNER: + cmd = COMMAND_ENABLE_PRESSURE_MODE + COMMAND_START_SENDING_PACKETS; + break; + + default: + cmd = COMMAND_MULTI_MODE_INPUT + COMMAND_ORIGIN_IN_UPPER_LEFT + COMMAND_ENABLE_ALL_MACRO_BUTTONS + COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS + COMMAND_TRANSMIT_AT_MAX_RATE + COMMAND_DISABLE_INCREMENTAL_MODE + COMMAND_ENABLE_CONTINUOUS_MODE + COMMAND_Z_FILTER + COMMAND_START_SENDING_PACKETS; + break; + } + + return wacom_send(serio, cmd); +} + +static int wacom_send_and_wait(struct wacom *wacom, struct serio *serio, + const u8 *cmd, const char *desc) +{ + int err; + unsigned long u; + + wacom->expect = cmd[1]; + init_completion(&wacom->cmd_done); + + err = wacom_send(serio, cmd); + if (err) + return err; + + u = wait_for_completion_timeout(&wacom->cmd_done, HZ); + if (u == 0) { + /* Timeout, process what we've received. */ + wacom_handle_response(wacom); + } + + wacom->expect = 0; + return wacom->result; +} + +static int wacom_setup(struct wacom *wacom, struct serio *serio) +{ + int err; + + /* Note that setting the link speed is the job of inputattach. + * We assume that reset negotiation has already happened, + * here. */ + err = wacom_send_and_wait(wacom, serio, REQUEST_MODEL_AND_ROM_VERSION, + "model and version"); + if (err) + return err; + + if (!(wacom->res_x && wacom->res_y)) { + err = wacom_send_and_wait(wacom, serio, + REQUEST_CONFIGURATION_STRING, + "configuration string"); + if (err) + return err; + } + + if (!(wacom->max_x && wacom->max_y)) { + err = wacom_send_and_wait(wacom, serio, + REQUEST_MAX_COORDINATES, + "coordinates string"); + if (err) + return err; + } + + return wacom_send_setup_string(wacom, serio); +} + +static int wacom_connect(struct serio *serio, struct serio_driver *drv) +{ + struct wacom *wacom; + struct input_dev *input_dev; + int err = -ENOMEM; + + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!wacom || !input_dev) + goto free_device; + + wacom->dev = input_dev; + wacom->extra_z_bits = 1; + wacom->eraser_mask = 0x04; + wacom->tool = wacom->idx = 0; + snprintf(wacom->phys, sizeof(wacom->phys), "%s/input0", serio->phys); + input_dev->phys = wacom->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_WACOM_IV; + input_dev->id.product = serio->id.extra; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = + BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_REL); + set_bit(ABS_MISC, input_dev->absbit); + set_bit(BTN_TOOL_PEN, input_dev->keybit); + set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_STYLUS, input_dev->keybit); + set_bit(BTN_LEFT, input_dev->keybit); + set_bit(BTN_RIGHT, input_dev->keybit); + set_bit(BTN_MIDDLE, input_dev->keybit); + + serio_set_drvdata(serio, wacom); + + err = serio_open(serio, drv); + if (err) + goto free_device; + + err = wacom_setup(wacom, serio); + if (err) + goto close_serio; + + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + if (!(wacom->flags & F_COVERS_SCREEN)) + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + + if (wacom->flags & F_HAS_STYLUS2) + __set_bit(BTN_STYLUS2, input_dev->keybit); + + if (wacom->flags & F_HAS_SCROLLWHEEL) + __set_bit(REL_WHEEL, input_dev->relbit); + + input_abs_set_res(wacom->dev, ABS_X, wacom->res_x); + input_abs_set_res(wacom->dev, ABS_Y, wacom->res_y); + input_set_abs_params(wacom->dev, ABS_X, 0, wacom->max_x, 0, 0); + input_set_abs_params(wacom->dev, ABS_Y, 0, wacom->max_y, 0, 0); + input_set_abs_params(wacom->dev, ABS_PRESSURE, -1, + (1 << (7 + wacom->extra_z_bits)) - 1, 0, 0); + + err = input_register_device(wacom->dev); + if (err) + goto close_serio; + + return 0; + +close_serio: + serio_close(serio); +free_device: + serio_set_drvdata(serio, NULL); + input_free_device(input_dev); + kfree(wacom); + return err; +} + +static struct serio_device_id wacom_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_WACOM_IV, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, wacom_serio_ids); + +static struct serio_driver wacom_drv = { + .driver = { + .name = "wacom_serial4", + }, + .description = "Wacom protocol 4 serial tablet driver", + .id_table = wacom_serio_ids, + .interrupt = wacom_interrupt, + .connect = wacom_connect, + .disconnect = wacom_disconnect, +}; + +module_serio_driver(wacom_drv); diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c deleted file mode 100644 index 2c613cd41dd6..000000000000 --- a/drivers/input/tablet/wacom_sys.c +++ /dev/null @@ -1,1497 +0,0 @@ -/* - * drivers/input/tablet/wacom_sys.c - * - * USB Wacom tablet support - system specific code - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include "wacom_wac.h" -#include "wacom.h" - -/* defines to get HID report descriptor */ -#define HID_DEVICET_HID (USB_TYPE_CLASS | 0x01) -#define HID_DEVICET_REPORT (USB_TYPE_CLASS | 0x02) -#define HID_USAGE_UNDEFINED 0x00 -#define HID_USAGE_PAGE 0x05 -#define HID_USAGE_PAGE_DIGITIZER 0x0d -#define HID_USAGE_PAGE_DESKTOP 0x01 -#define HID_USAGE 0x09 -#define HID_USAGE_X ((HID_USAGE_PAGE_DESKTOP << 16) | 0x30) -#define HID_USAGE_Y ((HID_USAGE_PAGE_DESKTOP << 16) | 0x31) -#define HID_USAGE_PRESSURE ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x30) -#define HID_USAGE_X_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d) -#define HID_USAGE_Y_TILT ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e) -#define HID_USAGE_FINGER ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22) -#define HID_USAGE_STYLUS ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x20) -#define HID_USAGE_CONTACTMAX ((HID_USAGE_PAGE_DIGITIZER << 16) | 0x55) -#define HID_COLLECTION 0xa1 -#define HID_COLLECTION_LOGICAL 0x02 -#define HID_COLLECTION_END 0xc0 - -struct hid_descriptor { - struct usb_descriptor_header header; - __le16 bcdHID; - u8 bCountryCode; - u8 bNumDescriptors; - u8 bDescriptorType; - __le16 wDescriptorLength; -} __attribute__ ((packed)); - -/* defines to get/set USB message */ -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 - -#define WAC_HID_FEATURE_REPORT 0x03 -#define WAC_MSG_RETRIES 5 - -#define WAC_CMD_LED_CONTROL 0x20 -#define WAC_CMD_ICON_START 0x21 -#define WAC_CMD_ICON_XFER 0x23 -#define WAC_CMD_RETRIES 10 - -static int wacom_get_report(struct usb_interface *intf, u8 type, u8 id, - void *buf, size_t size, unsigned int retries) -{ - struct usb_device *dev = interface_to_usbdev(intf); - int retval; - - do { - retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, - (type << 8) + id, - intf->altsetting[0].desc.bInterfaceNumber, - buf, size, 100); - } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); - - return retval; -} - -static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id, - void *buf, size_t size, unsigned int retries) -{ - struct usb_device *dev = interface_to_usbdev(intf); - int retval; - - do { - retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_SET_REPORT, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, - (type << 8) + id, - intf->altsetting[0].desc.bInterfaceNumber, - buf, size, 1000); - } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries); - - return retval; -} - -static void wacom_sys_irq(struct urb *urb) -{ - struct wacom *wacom = urb->context; - struct device *dev = &wacom->intf->dev; - int retval; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dev_dbg(dev, "%s - urb shutting down with status: %d\n", - __func__, urb->status); - return; - default: - dev_dbg(dev, "%s - nonzero urb status received: %d\n", - __func__, urb->status); - goto exit; - } - - wacom_wac_irq(&wacom->wacom_wac, urb->actual_length); - - exit: - usb_mark_last_busy(wacom->usbdev); - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s - usb_submit_urb failed with result %d\n", - __func__, retval); -} - -static int wacom_open(struct input_dev *dev) -{ - struct wacom *wacom = input_get_drvdata(dev); - int retval = 0; - - if (usb_autopm_get_interface(wacom->intf) < 0) - return -EIO; - - mutex_lock(&wacom->lock); - - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - retval = -EIO; - goto out; - } - - wacom->open = true; - wacom->intf->needs_remote_wakeup = 1; - -out: - mutex_unlock(&wacom->lock); - usb_autopm_put_interface(wacom->intf); - return retval; -} - -static void wacom_close(struct input_dev *dev) -{ - struct wacom *wacom = input_get_drvdata(dev); - int autopm_error; - - autopm_error = usb_autopm_get_interface(wacom->intf); - - mutex_lock(&wacom->lock); - usb_kill_urb(wacom->irq); - wacom->open = false; - wacom->intf->needs_remote_wakeup = 0; - mutex_unlock(&wacom->lock); - - if (!autopm_error) - usb_autopm_put_interface(wacom->intf); -} - -/* - * Calculate the resolution of the X or Y axis, given appropriate HID data. - * This function is little more than hidinput_calc_abs_res stripped down. - */ -static int wacom_calc_hid_res(int logical_extents, int physical_extents, - unsigned char unit, unsigned char exponent) -{ - int prev, unit_exponent; - - /* Check if the extents are sane */ - if (logical_extents <= 0 || physical_extents <= 0) - return 0; - - /* Get signed value of nybble-sized twos-compliment exponent */ - unit_exponent = exponent; - if (unit_exponent > 7) - unit_exponent -= 16; - - /* Convert physical_extents to millimeters */ - if (unit == 0x11) { /* If centimeters */ - unit_exponent += 1; - } else if (unit == 0x13) { /* If inches */ - prev = physical_extents; - physical_extents *= 254; - if (physical_extents < prev) - return 0; - unit_exponent -= 1; - } else { - return 0; - } - - /* Apply negative unit exponent */ - for (; unit_exponent < 0; unit_exponent++) { - prev = logical_extents; - logical_extents *= 10; - if (logical_extents < prev) - return 0; - } - /* Apply positive unit exponent */ - for (; unit_exponent > 0; unit_exponent--) { - prev = physical_extents; - physical_extents *= 10; - if (physical_extents < prev) - return 0; - } - - /* Calculate resolution */ - return logical_extents / physical_extents; -} - -static int wacom_parse_logical_collection(unsigned char *report, - struct wacom_features *features) -{ - int length = 0; - - if (features->type == BAMBOO_PT) { - - /* Logical collection is only used by 3rd gen Bamboo Touch */ - features->pktlen = WACOM_PKGLEN_BBTOUCH3; - features->device_type = BTN_TOOL_FINGER; - - features->x_max = features->y_max = - get_unaligned_le16(&report[10]); - - length = 11; - } - return length; -} - -static void wacom_retrieve_report_data(struct usb_interface *intf, - struct wacom_features *features) -{ - int result = 0; - unsigned char *rep_data; - - rep_data = kmalloc(2, GFP_KERNEL); - if (rep_data) { - - rep_data[0] = 12; - result = wacom_get_report(intf, WAC_HID_FEATURE_REPORT, - rep_data[0], rep_data, 2, - WAC_MSG_RETRIES); - - if (result >= 0 && rep_data[1] > 2) - features->touch_max = rep_data[1]; - - kfree(rep_data); - } -} - -/* - * Interface Descriptor of wacom devices can be incomplete and - * inconsistent so wacom_features table is used to store stylus - * device's packet lengths, various maximum values, and tablet - * resolution based on product ID's. - * - * For devices that contain 2 interfaces, wacom_features table is - * inaccurate for the touch interface. Since the Interface Descriptor - * for touch interfaces has pretty complete data, this function exists - * to query tablet for this missing information instead of hard coding in - * an additional table. - * - * A typical Interface Descriptor for a stylus will contain a - * boot mouse application collection that is not of interest and this - * function will ignore it. - * - * It also contains a digitizer application collection that also is not - * of interest since any information it contains would be duplicate - * of what is in wacom_features. Usually it defines a report of an array - * of bytes that could be used as max length of the stylus packet returned. - * If it happens to define a Digitizer-Stylus Physical Collection then - * the X and Y logical values contain valid data but it is ignored. - * - * A typical Interface Descriptor for a touch interface will contain a - * Digitizer-Finger Physical Collection which will define both logical - * X/Y maximum as well as the physical size of tablet. Since touch - * interfaces haven't supported pressure or distance, this is enough - * information to override invalid values in the wacom_features table. - * - * 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical - * Collection. Instead they define a Logical Collection with a single - * Logical Maximum for both X and Y. - * - * Intuos5 touch interface does not contain useful data. We deal with - * this after returning from this function. - */ -static int wacom_parse_hid(struct usb_interface *intf, - struct hid_descriptor *hid_desc, - struct wacom_features *features) -{ - struct usb_device *dev = interface_to_usbdev(intf); - char limit = 0; - /* result has to be defined as int for some devices */ - int result = 0, touch_max = 0; - int i = 0, page = 0, finger = 0, pen = 0; - unsigned char *report; - - report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL); - if (!report) - return -ENOMEM; - - /* retrive report descriptors */ - do { - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, - USB_RECIP_INTERFACE | USB_DIR_IN, - HID_DEVICET_REPORT << 8, - intf->altsetting[0].desc.bInterfaceNumber, /* interface */ - report, - hid_desc->wDescriptorLength, - 5000); /* 5 secs */ - } while (result < 0 && limit++ < WAC_MSG_RETRIES); - - /* No need to parse the Descriptor. It isn't an error though */ - if (result < 0) - goto out; - - for (i = 0; i < hid_desc->wDescriptorLength; i++) { - - switch (report[i]) { - case HID_USAGE_PAGE: - page = report[i + 1]; - i++; - break; - - case HID_USAGE: - switch (page << 16 | report[i + 1]) { - case HID_USAGE_X: - if (finger) { - features->device_type = BTN_TOOL_FINGER; - /* touch device at least supports one touch point */ - touch_max = 1; - switch (features->type) { - case TABLETPC2FG: - features->pktlen = WACOM_PKGLEN_TPC2FG; - break; - - case MTSCREEN: - case WACOM_24HDT: - features->pktlen = WACOM_PKGLEN_MTOUCH; - break; - - case MTTPC: - case MTTPC_B: - features->pktlen = WACOM_PKGLEN_MTTPC; - break; - - case BAMBOO_PT: - features->pktlen = WACOM_PKGLEN_BBTOUCH; - break; - - default: - features->pktlen = WACOM_PKGLEN_GRAPHIRE; - break; - } - - switch (features->type) { - case BAMBOO_PT: - features->x_phy = - get_unaligned_le16(&report[i + 5]); - features->x_max = - get_unaligned_le16(&report[i + 8]); - i += 15; - break; - - case WACOM_24HDT: - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 8]); - features->unit = report[i - 1]; - features->unitExpo = report[i - 3]; - i += 12; - break; - - case MTTPC_B: - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 6]); - features->unit = report[i - 5]; - features->unitExpo = report[i - 3]; - i += 9; - break; - - default: - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 6]); - features->unit = report[i + 9]; - features->unitExpo = report[i + 11]; - i += 12; - break; - } - } else if (pen) { - /* penabled only accepts exact bytes of data */ - if (features->type >= TABLETPC) - features->pktlen = WACOM_PKGLEN_GRAPHIRE; - features->device_type = BTN_TOOL_PEN; - features->x_max = - get_unaligned_le16(&report[i + 3]); - i += 4; - } - break; - - case HID_USAGE_Y: - if (finger) { - switch (features->type) { - case TABLETPC2FG: - case MTSCREEN: - case MTTPC: - features->y_max = - get_unaligned_le16(&report[i + 3]); - features->y_phy = - get_unaligned_le16(&report[i + 6]); - i += 7; - break; - - case WACOM_24HDT: - features->y_max = - get_unaligned_le16(&report[i + 3]); - features->y_phy = - get_unaligned_le16(&report[i - 2]); - i += 7; - break; - - case BAMBOO_PT: - features->y_phy = - get_unaligned_le16(&report[i + 3]); - features->y_max = - get_unaligned_le16(&report[i + 6]); - i += 12; - break; - - case MTTPC_B: - features->y_max = - get_unaligned_le16(&report[i + 3]); - features->y_phy = - get_unaligned_le16(&report[i + 6]); - i += 9; - break; - - default: - features->y_max = - features->x_max; - features->y_phy = - get_unaligned_le16(&report[i + 3]); - i += 4; - break; - } - } else if (pen) { - features->y_max = - get_unaligned_le16(&report[i + 3]); - i += 4; - } - break; - - case HID_USAGE_FINGER: - finger = 1; - i++; - break; - - /* - * Requiring Stylus Usage will ignore boot mouse - * X/Y values and some cases of invalid Digitizer X/Y - * values commonly reported. - */ - case HID_USAGE_STYLUS: - pen = 1; - i++; - break; - - case HID_USAGE_CONTACTMAX: - /* leave touch_max as is if predefined */ - if (!features->touch_max) - wacom_retrieve_report_data(intf, features); - i++; - break; - - case HID_USAGE_PRESSURE: - if (pen) { - features->pressure_max = - get_unaligned_le16(&report[i + 3]); - i += 4; - } - break; - } - break; - - case HID_COLLECTION_END: - /* reset UsagePage and Finger */ - finger = page = 0; - break; - - case HID_COLLECTION: - i++; - switch (report[i]) { - case HID_COLLECTION_LOGICAL: - i += wacom_parse_logical_collection(&report[i], - features); - break; - } - break; - } - } - - out: - if (!features->touch_max && touch_max) - features->touch_max = touch_max; - result = 0; - kfree(report); - return result; -} - -static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int length, int mode) -{ - unsigned char *rep_data; - int error = -ENOMEM, limit = 0; - - rep_data = kzalloc(length, GFP_KERNEL); - if (!rep_data) - return error; - - do { - rep_data[0] = report_id; - rep_data[1] = mode; - - error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, - report_id, rep_data, length, 1); - } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES); - - kfree(rep_data); - - return error < 0 ? error : 0; -} - -/* - * Switch the tablet into its most-capable mode. Wacom tablets are - * typically configured to power-up in a mode which sends mouse-like - * reports to the OS. To get absolute position, pressure data, etc. - * from the tablet, it is necessary to switch the tablet out of this - * mode and into one which sends the full range of tablet data. - */ -static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features) -{ - if (features->device_type == BTN_TOOL_FINGER) { - if (features->type > TABLETPC) { - /* MT Tablet PC touch */ - return wacom_set_device_mode(intf, 3, 4, 4); - } - else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) { - return wacom_set_device_mode(intf, 18, 3, 2); - } - } else if (features->device_type == BTN_TOOL_PEN) { - if (features->type <= BAMBOO_PT && features->type != WIRELESS) { - return wacom_set_device_mode(intf, 2, 2, 2); - } - } - - return 0; -} - -static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, - struct wacom_features *features) -{ - int error = 0; - struct usb_host_interface *interface = intf->cur_altsetting; - struct hid_descriptor *hid_desc; - - /* default features */ - features->device_type = BTN_TOOL_PEN; - features->x_fuzz = 4; - features->y_fuzz = 4; - features->pressure_fuzz = 0; - features->distance_fuzz = 0; - - /* - * The wireless device HID is basic and layout conflicts with - * other tablets (monitor and touch interface can look like pen). - * Skip the query for this type and modify defaults based on - * interface number. - */ - if (features->type == WIRELESS) { - if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - features->device_type = 0; - } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) { - features->device_type = BTN_TOOL_FINGER; - features->pktlen = WACOM_PKGLEN_BBTOUCH3; - } - } - - /* only devices that support touch need to retrieve the info */ - if (features->type < BAMBOO_PT) { - goto out; - } - - error = usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc); - if (error) { - error = usb_get_extra_descriptor(&interface->endpoint[0], - HID_DEVICET_REPORT, &hid_desc); - if (error) { - dev_err(&intf->dev, - "can not retrieve extra class descriptor\n"); - goto out; - } - } - error = wacom_parse_hid(intf, hid_desc, features); - - out: - return error; -} - -struct wacom_usbdev_data { - struct list_head list; - struct kref kref; - struct usb_device *dev; - struct wacom_shared shared; -}; - -static LIST_HEAD(wacom_udev_list); -static DEFINE_MUTEX(wacom_udev_list_lock); - -static struct usb_device *wacom_get_sibling(struct usb_device *dev, int vendor, int product) -{ - int port1; - struct usb_device *sibling; - - if (vendor == 0 && product == 0) - return dev; - - if (dev->parent == NULL) - return NULL; - - usb_hub_for_each_child(dev->parent, port1, sibling) { - struct usb_device_descriptor *d; - if (sibling == NULL) - continue; - - d = &sibling->descriptor; - if (d->idVendor == vendor && d->idProduct == product) - return sibling; - } - - return NULL; -} - -static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev) -{ - struct wacom_usbdev_data *data; - - list_for_each_entry(data, &wacom_udev_list, list) { - if (data->dev == dev) { - kref_get(&data->kref); - return data; - } - } - - return NULL; -} - -static int wacom_add_shared_data(struct wacom_wac *wacom, - struct usb_device *dev) -{ - struct wacom_usbdev_data *data; - int retval = 0; - - mutex_lock(&wacom_udev_list_lock); - - data = wacom_get_usbdev_data(dev); - if (!data) { - data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL); - if (!data) { - retval = -ENOMEM; - goto out; - } - - kref_init(&data->kref); - data->dev = dev; - list_add_tail(&data->list, &wacom_udev_list); - } - - wacom->shared = &data->shared; - -out: - mutex_unlock(&wacom_udev_list_lock); - return retval; -} - -static void wacom_release_shared_data(struct kref *kref) -{ - struct wacom_usbdev_data *data = - container_of(kref, struct wacom_usbdev_data, kref); - - mutex_lock(&wacom_udev_list_lock); - list_del(&data->list); - mutex_unlock(&wacom_udev_list_lock); - - kfree(data); -} - -static void wacom_remove_shared_data(struct wacom_wac *wacom) -{ - struct wacom_usbdev_data *data; - - if (wacom->shared) { - data = container_of(wacom->shared, struct wacom_usbdev_data, shared); - kref_put(&data->kref, wacom_release_shared_data); - wacom->shared = NULL; - } -} - -static int wacom_led_control(struct wacom *wacom) -{ - unsigned char *buf; - int retval; - - buf = kzalloc(9, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (wacom->wacom_wac.features.type >= INTUOS5S && - wacom->wacom_wac.features.type <= INTUOSPL) { - /* - * Touch Ring and crop mark LED luminance may take on - * one of four values: - * 0 = Low; 1 = Medium; 2 = High; 3 = Off - */ - int ring_led = wacom->led.select[0] & 0x03; - int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03; - int crop_lum = 0; - - buf[0] = WAC_CMD_LED_CONTROL; - buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led); - } - else { - int led = wacom->led.select[0] | 0x4; - - if (wacom->wacom_wac.features.type == WACOM_21UX2 || - wacom->wacom_wac.features.type == WACOM_24HD) - led |= (wacom->led.select[1] << 4) | 0x40; - - buf[0] = WAC_CMD_LED_CONTROL; - buf[1] = led; - buf[2] = wacom->led.llv; - buf[3] = wacom->led.hlv; - buf[4] = wacom->led.img_lum; - } - - retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL, - buf, 9, WAC_CMD_RETRIES); - kfree(buf); - - return retval; -} - -static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img) -{ - unsigned char *buf; - int i, retval; - - buf = kzalloc(259, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* Send 'start' command */ - buf[0] = WAC_CMD_ICON_START; - buf[1] = 1; - retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, - buf, 2, WAC_CMD_RETRIES); - if (retval < 0) - goto out; - - buf[0] = WAC_CMD_ICON_XFER; - buf[1] = button_id & 0x07; - for (i = 0; i < 4; i++) { - buf[2] = i; - memcpy(buf + 3, img + i * 256, 256); - - retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_XFER, - buf, 259, WAC_CMD_RETRIES); - if (retval < 0) - break; - } - - /* Send 'stop' */ - buf[0] = WAC_CMD_ICON_START; - buf[1] = 0; - wacom_set_report(wacom->intf, 0x03, WAC_CMD_ICON_START, - buf, 2, WAC_CMD_RETRIES); - -out: - kfree(buf); - return retval; -} - -static ssize_t wacom_led_select_store(struct device *dev, int set_id, - const char *buf, size_t count) -{ - struct wacom *wacom = dev_get_drvdata(dev); - unsigned int id; - int err; - - err = kstrtouint(buf, 10, &id); - if (err) - return err; - - mutex_lock(&wacom->lock); - - wacom->led.select[set_id] = id & 0x3; - err = wacom_led_control(wacom); - - mutex_unlock(&wacom->lock); - - return err < 0 ? err : count; -} - -#define DEVICE_LED_SELECT_ATTR(SET_ID) \ -static ssize_t wacom_led##SET_ID##_select_store(struct device *dev, \ - struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return wacom_led_select_store(dev, SET_ID, buf, count); \ -} \ -static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct wacom *wacom = dev_get_drvdata(dev); \ - return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]); \ -} \ -static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR, \ - wacom_led##SET_ID##_select_show, \ - wacom_led##SET_ID##_select_store) - -DEVICE_LED_SELECT_ATTR(0); -DEVICE_LED_SELECT_ATTR(1); - -static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest, - const char *buf, size_t count) -{ - unsigned int value; - int err; - - err = kstrtouint(buf, 10, &value); - if (err) - return err; - - mutex_lock(&wacom->lock); - - *dest = value & 0x7f; - err = wacom_led_control(wacom); - - mutex_unlock(&wacom->lock); - - return err < 0 ? err : count; -} - -#define DEVICE_LUMINANCE_ATTR(name, field) \ -static ssize_t wacom_##name##_luminance_store(struct device *dev, \ - struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct wacom *wacom = dev_get_drvdata(dev); \ - \ - return wacom_luminance_store(wacom, &wacom->led.field, \ - buf, count); \ -} \ -static DEVICE_ATTR(name##_luminance, S_IWUSR, \ - NULL, wacom_##name##_luminance_store) - -DEVICE_LUMINANCE_ATTR(status0, llv); -DEVICE_LUMINANCE_ATTR(status1, hlv); -DEVICE_LUMINANCE_ATTR(buttons, img_lum); - -static ssize_t wacom_button_image_store(struct device *dev, int button_id, - const char *buf, size_t count) -{ - struct wacom *wacom = dev_get_drvdata(dev); - int err; - - if (count != 1024) - return -EINVAL; - - mutex_lock(&wacom->lock); - - err = wacom_led_putimage(wacom, button_id, buf); - - mutex_unlock(&wacom->lock); - - return err < 0 ? err : count; -} - -#define DEVICE_BTNIMG_ATTR(BUTTON_ID) \ -static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev, \ - struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return wacom_button_image_store(dev, BUTTON_ID, buf, count); \ -} \ -static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR, \ - NULL, wacom_btnimg##BUTTON_ID##_store) - -DEVICE_BTNIMG_ATTR(0); -DEVICE_BTNIMG_ATTR(1); -DEVICE_BTNIMG_ATTR(2); -DEVICE_BTNIMG_ATTR(3); -DEVICE_BTNIMG_ATTR(4); -DEVICE_BTNIMG_ATTR(5); -DEVICE_BTNIMG_ATTR(6); -DEVICE_BTNIMG_ATTR(7); - -static struct attribute *cintiq_led_attrs[] = { - &dev_attr_status_led0_select.attr, - &dev_attr_status_led1_select.attr, - NULL -}; - -static struct attribute_group cintiq_led_attr_group = { - .name = "wacom_led", - .attrs = cintiq_led_attrs, -}; - -static struct attribute *intuos4_led_attrs[] = { - &dev_attr_status0_luminance.attr, - &dev_attr_status1_luminance.attr, - &dev_attr_status_led0_select.attr, - &dev_attr_buttons_luminance.attr, - &dev_attr_button0_rawimg.attr, - &dev_attr_button1_rawimg.attr, - &dev_attr_button2_rawimg.attr, - &dev_attr_button3_rawimg.attr, - &dev_attr_button4_rawimg.attr, - &dev_attr_button5_rawimg.attr, - &dev_attr_button6_rawimg.attr, - &dev_attr_button7_rawimg.attr, - NULL -}; - -static struct attribute_group intuos4_led_attr_group = { - .name = "wacom_led", - .attrs = intuos4_led_attrs, -}; - -static struct attribute *intuos5_led_attrs[] = { - &dev_attr_status0_luminance.attr, - &dev_attr_status_led0_select.attr, - NULL -}; - -static struct attribute_group intuos5_led_attr_group = { - .name = "wacom_led", - .attrs = intuos5_led_attrs, -}; - -static int wacom_initialize_leds(struct wacom *wacom) -{ - int error; - - /* Initialize default values */ - switch (wacom->wacom_wac.features.type) { - case INTUOS4S: - case INTUOS4: - case INTUOS4L: - wacom->led.select[0] = 0; - wacom->led.select[1] = 0; - wacom->led.llv = 10; - wacom->led.hlv = 20; - wacom->led.img_lum = 10; - error = sysfs_create_group(&wacom->intf->dev.kobj, - &intuos4_led_attr_group); - break; - - case WACOM_24HD: - case WACOM_21UX2: - wacom->led.select[0] = 0; - wacom->led.select[1] = 0; - wacom->led.llv = 0; - wacom->led.hlv = 0; - wacom->led.img_lum = 0; - - error = sysfs_create_group(&wacom->intf->dev.kobj, - &cintiq_led_attr_group); - break; - - case INTUOS5S: - case INTUOS5: - case INTUOS5L: - case INTUOSPS: - case INTUOSPM: - case INTUOSPL: - if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) { - wacom->led.select[0] = 0; - wacom->led.select[1] = 0; - wacom->led.llv = 32; - wacom->led.hlv = 0; - wacom->led.img_lum = 0; - - error = sysfs_create_group(&wacom->intf->dev.kobj, - &intuos5_led_attr_group); - } else - return 0; - break; - - default: - return 0; - } - - if (error) { - dev_err(&wacom->intf->dev, - "cannot create sysfs group err: %d\n", error); - return error; - } - wacom_led_control(wacom); - - return 0; -} - -static void wacom_destroy_leds(struct wacom *wacom) -{ - switch (wacom->wacom_wac.features.type) { - case INTUOS4S: - case INTUOS4: - case INTUOS4L: - sysfs_remove_group(&wacom->intf->dev.kobj, - &intuos4_led_attr_group); - break; - - case WACOM_24HD: - case WACOM_21UX2: - sysfs_remove_group(&wacom->intf->dev.kobj, - &cintiq_led_attr_group); - break; - - case INTUOS5S: - case INTUOS5: - case INTUOS5L: - case INTUOSPS: - case INTUOSPM: - case INTUOSPL: - if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) - sysfs_remove_group(&wacom->intf->dev.kobj, - &intuos5_led_attr_group); - break; - } -} - -static enum power_supply_property wacom_battery_props[] = { - POWER_SUPPLY_PROP_SCOPE, - POWER_SUPPLY_PROP_CAPACITY -}; - -static int wacom_battery_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - struct wacom *wacom = container_of(psy, struct wacom, battery); - int ret = 0; - - switch (psp) { - case POWER_SUPPLY_PROP_SCOPE: - val->intval = POWER_SUPPLY_SCOPE_DEVICE; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = - wacom->wacom_wac.battery_capacity * 100 / 31; - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int wacom_initialize_battery(struct wacom *wacom) -{ - int error = 0; - - if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) { - wacom->battery.properties = wacom_battery_props; - wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props); - wacom->battery.get_property = wacom_battery_get_property; - wacom->battery.name = "wacom_battery"; - wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY; - wacom->battery.use_for_apm = 0; - - error = power_supply_register(&wacom->usbdev->dev, - &wacom->battery); - - if (!error) - power_supply_powers(&wacom->battery, - &wacom->usbdev->dev); - } - - return error; -} - -static void wacom_destroy_battery(struct wacom *wacom) -{ - if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR && - wacom->battery.dev) { - power_supply_unregister(&wacom->battery); - wacom->battery.dev = NULL; - } -} - -static int wacom_register_input(struct wacom *wacom) -{ - struct input_dev *input_dev; - struct usb_interface *intf = wacom->intf; - struct usb_device *dev = interface_to_usbdev(intf); - struct wacom_wac *wacom_wac = &(wacom->wacom_wac); - int error; - - input_dev = input_allocate_device(); - if (!input_dev) { - error = -ENOMEM; - goto fail1; - } - - input_dev->name = wacom_wac->name; - input_dev->dev.parent = &intf->dev; - input_dev->open = wacom_open; - input_dev->close = wacom_close; - usb_to_input_id(dev, &input_dev->id); - input_set_drvdata(input_dev, wacom); - - wacom_wac->input = input_dev; - error = wacom_setup_input_capabilities(input_dev, wacom_wac); - if (error) - goto fail1; - - error = input_register_device(input_dev); - if (error) - goto fail2; - - return 0; - -fail2: - input_free_device(input_dev); - wacom_wac->input = NULL; -fail1: - return error; -} - -static void wacom_wireless_work(struct work_struct *work) -{ - struct wacom *wacom = container_of(work, struct wacom, work); - struct usb_device *usbdev = wacom->usbdev; - struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct wacom *wacom1, *wacom2; - struct wacom_wac *wacom_wac1, *wacom_wac2; - int error; - - /* - * Regardless if this is a disconnect or a new tablet, - * remove any existing input and battery devices. - */ - - wacom_destroy_battery(wacom); - - /* Stylus interface */ - wacom1 = usb_get_intfdata(usbdev->config->interface[1]); - wacom_wac1 = &(wacom1->wacom_wac); - if (wacom_wac1->input) - input_unregister_device(wacom_wac1->input); - wacom_wac1->input = NULL; - - /* Touch interface */ - wacom2 = usb_get_intfdata(usbdev->config->interface[2]); - wacom_wac2 = &(wacom2->wacom_wac); - if (wacom_wac2->input) - input_unregister_device(wacom_wac2->input); - wacom_wac2->input = NULL; - - if (wacom_wac->pid == 0) { - dev_info(&wacom->intf->dev, "wireless tablet disconnected\n"); - } else { - const struct usb_device_id *id = wacom_ids; - - dev_info(&wacom->intf->dev, - "wireless tablet connected with PID %x\n", - wacom_wac->pid); - - while (id->match_flags) { - if (id->idVendor == USB_VENDOR_ID_WACOM && - id->idProduct == wacom_wac->pid) - break; - id++; - } - - if (!id->match_flags) { - dev_info(&wacom->intf->dev, - "ignoring unknown PID.\n"); - return; - } - - /* Stylus interface */ - wacom_wac1->features = - *((struct wacom_features *)id->driver_info); - wacom_wac1->features.device_type = BTN_TOOL_PEN; - snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen", - wacom_wac1->features.name); - wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max; - wacom_wac1->shared->type = wacom_wac1->features.type; - error = wacom_register_input(wacom1); - if (error) - goto fail; - - /* Touch interface */ - if (wacom_wac1->features.touch_max || - wacom_wac1->features.type == INTUOSHT) { - wacom_wac2->features = - *((struct wacom_features *)id->driver_info); - wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3; - wacom_wac2->features.device_type = BTN_TOOL_FINGER; - wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096; - if (wacom_wac2->features.touch_max) - snprintf(wacom_wac2->name, WACOM_NAME_MAX, - "%s (WL) Finger",wacom_wac2->features.name); - else - snprintf(wacom_wac2->name, WACOM_NAME_MAX, - "%s (WL) Pad",wacom_wac2->features.name); - error = wacom_register_input(wacom2); - if (error) - goto fail; - - if (wacom_wac1->features.type == INTUOSHT && - wacom_wac1->features.touch_max) - wacom_wac->shared->touch_input = wacom_wac2->input; - } - - error = wacom_initialize_battery(wacom); - if (error) - goto fail; - } - - return; - -fail: - if (wacom_wac2->input) { - input_unregister_device(wacom_wac2->input); - wacom_wac2->input = NULL; - } - - if (wacom_wac1->input) { - input_unregister_device(wacom_wac1->input); - wacom_wac1->input = NULL; - } - return; -} - -/* - * Not all devices report physical dimensions from HID. - * Compute the default from hardcoded logical dimension - * and resolution before driver overwrites them. - */ -static void wacom_set_default_phy(struct wacom_features *features) -{ - if (features->x_resolution) { - features->x_phy = (features->x_max * 100) / - features->x_resolution; - features->y_phy = (features->y_max * 100) / - features->y_resolution; - } -} - -static void wacom_calculate_res(struct wacom_features *features) -{ - features->x_resolution = wacom_calc_hid_res(features->x_max, - features->x_phy, - features->unit, - features->unitExpo); - features->y_resolution = wacom_calc_hid_res(features->y_max, - features->y_phy, - features->unit, - features->unitExpo); -} - -static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_endpoint_descriptor *endpoint; - struct wacom *wacom; - struct wacom_wac *wacom_wac; - struct wacom_features *features; - int error; - - if (!id->driver_info) - return -EINVAL; - - wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); - if (!wacom) - return -ENOMEM; - - wacom_wac = &wacom->wacom_wac; - wacom_wac->features = *((struct wacom_features *)id->driver_info); - features = &wacom_wac->features; - if (features->pktlen > WACOM_PKGLEN_MAX) { - error = -EINVAL; - goto fail1; - } - - wacom_wac->data = usb_alloc_coherent(dev, WACOM_PKGLEN_MAX, - GFP_KERNEL, &wacom->data_dma); - if (!wacom_wac->data) { - error = -ENOMEM; - goto fail1; - } - - wacom->irq = usb_alloc_urb(0, GFP_KERNEL); - if (!wacom->irq) { - error = -ENOMEM; - goto fail2; - } - - wacom->usbdev = dev; - wacom->intf = intf; - mutex_init(&wacom->lock); - INIT_WORK(&wacom->work, wacom_wireless_work); - usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); - strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); - - endpoint = &intf->cur_altsetting->endpoint[0].desc; - - /* set the default size in case we do not get them from hid */ - wacom_set_default_phy(features); - - /* Retrieve the physical and logical size for touch devices */ - error = wacom_retrieve_hid_descriptor(intf, features); - if (error) - goto fail3; - - /* - * Intuos5 has no useful data about its touch interface in its - * HID descriptor. If this is the touch interface (wMaxPacketSize - * of WACOM_PKGLEN_BBTOUCH3), override the table values. - */ - if (features->type >= INTUOS5S && features->type <= INTUOSHT) { - if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) { - features->device_type = BTN_TOOL_FINGER; - features->pktlen = WACOM_PKGLEN_BBTOUCH3; - - features->x_max = 4096; - features->y_max = 4096; - } else { - features->device_type = BTN_TOOL_PEN; - } - } - - wacom_setup_device_quirks(features); - - /* set unit to "100th of a mm" for devices not reported by HID */ - if (!features->unit) { - features->unit = 0x11; - features->unitExpo = 16 - 3; - } - wacom_calculate_res(features); - - strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); - - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) { - struct usb_device *other_dev; - - /* Append the device type to the name */ - if (features->device_type != BTN_TOOL_FINGER) - strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX); - else if (features->touch_max) - strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX); - else - strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX); - - other_dev = wacom_get_sibling(dev, features->oVid, features->oPid); - if (other_dev == NULL || wacom_get_usbdev_data(other_dev) == NULL) - other_dev = dev; - error = wacom_add_shared_data(wacom_wac, other_dev); - if (error) - goto fail3; - } - - usb_fill_int_urb(wacom->irq, dev, - usb_rcvintpipe(dev, endpoint->bEndpointAddress), - wacom_wac->data, features->pktlen, - wacom_sys_irq, wacom, endpoint->bInterval); - wacom->irq->transfer_dma = wacom->data_dma; - wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - error = wacom_initialize_leds(wacom); - if (error) - goto fail4; - - if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) { - error = wacom_register_input(wacom); - if (error) - goto fail5; - } - - /* Note that if query fails it is not a hard failure */ - wacom_query_tablet_data(intf, features); - - usb_set_intfdata(intf, wacom); - - if (features->quirks & WACOM_QUIRK_MONITOR) { - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { - error = -EIO; - goto fail5; - } - } - - if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) { - if (wacom_wac->features.device_type == BTN_TOOL_FINGER) - wacom_wac->shared->touch_input = wacom_wac->input; - } - - return 0; - - fail5: wacom_destroy_leds(wacom); - fail4: wacom_remove_shared_data(wacom_wac); - fail3: usb_free_urb(wacom->irq); - fail2: usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma); - fail1: kfree(wacom); - return error; -} - -static void wacom_disconnect(struct usb_interface *intf) -{ - struct wacom *wacom = usb_get_intfdata(intf); - - usb_set_intfdata(intf, NULL); - - usb_kill_urb(wacom->irq); - cancel_work_sync(&wacom->work); - if (wacom->wacom_wac.input) - input_unregister_device(wacom->wacom_wac.input); - wacom_destroy_battery(wacom); - wacom_destroy_leds(wacom); - usb_free_urb(wacom->irq); - usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX, - wacom->wacom_wac.data, wacom->data_dma); - wacom_remove_shared_data(&wacom->wacom_wac); - kfree(wacom); -} - -static int wacom_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct wacom *wacom = usb_get_intfdata(intf); - - mutex_lock(&wacom->lock); - usb_kill_urb(wacom->irq); - mutex_unlock(&wacom->lock); - - return 0; -} - -static int wacom_resume(struct usb_interface *intf) -{ - struct wacom *wacom = usb_get_intfdata(intf); - struct wacom_features *features = &wacom->wacom_wac.features; - int rv = 0; - - mutex_lock(&wacom->lock); - - /* switch to wacom mode first */ - wacom_query_tablet_data(intf, features); - wacom_led_control(wacom); - - if ((wacom->open || (features->quirks & WACOM_QUIRK_MONITOR)) && - usb_submit_urb(wacom->irq, GFP_NOIO) < 0) - rv = -EIO; - - mutex_unlock(&wacom->lock); - - return rv; -} - -static int wacom_reset_resume(struct usb_interface *intf) -{ - return wacom_resume(intf); -} - -static struct usb_driver wacom_driver = { - .name = "wacom", - .id_table = wacom_ids, - .probe = wacom_probe, - .disconnect = wacom_disconnect, - .suspend = wacom_suspend, - .resume = wacom_resume, - .reset_resume = wacom_reset_resume, - .supports_autosuspend = 1, -}; - -module_usb_driver(wacom_driver); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c deleted file mode 100644 index e73cf2c71f35..000000000000 --- a/drivers/input/tablet/wacom_wac.c +++ /dev/null @@ -1,2485 +0,0 @@ -/* - * drivers/input/tablet/wacom_wac.c - * - * USB Wacom tablet support - Wacom specific code - * - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include "wacom_wac.h" -#include "wacom.h" -#include <linux/input/mt.h> -#include <linux/hid.h> - -/* resolution for penabled devices */ -#define WACOM_PL_RES 20 -#define WACOM_PENPRTN_RES 40 -#define WACOM_VOLITO_RES 50 -#define WACOM_GRAPHIRE_RES 80 -#define WACOM_INTUOS_RES 100 -#define WACOM_INTUOS3_RES 200 - -/* Scale factor relating reported contact size to logical contact area. - * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo - */ -#define WACOM_CONTACT_AREA_SCALE 2607 - -static int wacom_penpartner_irq(struct wacom_wac *wacom) -{ - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - - switch (data[0]) { - case 1: - if (data[5] & 0x80) { - wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; - input_report_key(input, wacom->tool[0], 1); - input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ - input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); - input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); - input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); - input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -127)); - input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); - } else { - input_report_key(input, wacom->tool[0], 0); - input_report_abs(input, ABS_MISC, 0); /* report tool id */ - input_report_abs(input, ABS_PRESSURE, -1); - input_report_key(input, BTN_TOUCH, 0); - } - break; - - case 2: - input_report_key(input, BTN_TOOL_PEN, 1); - input_report_abs(input, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */ - input_report_abs(input, ABS_X, get_unaligned_le16(&data[1])); - input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3])); - input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127); - input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); - input_report_key(input, BTN_STYLUS, (data[5] & 0x40)); - break; - - default: - dev_dbg(input->dev.parent, - "%s: received unknown report #%d\n", __func__, data[0]); - return 0; - } - - return 1; -} - -static int wacom_pl_irq(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - int prox, pressure; - - if (data[0] != WACOM_REPORT_PENABLED) { - dev_dbg(input->dev.parent, - "%s: received unknown report #%d\n", __func__, data[0]); - return 0; - } - - prox = data[1] & 0x40; - - if (prox) { - wacom->id[0] = ERASER_DEVICE_ID; - pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1)); - if (features->pressure_max > 255) - pressure = (pressure << 1) | ((data[4] >> 6) & 1); - pressure += (features->pressure_max + 1) / 2; - - /* - * if going from out of proximity into proximity select between the eraser - * and the pen based on the state of the stylus2 button, choose eraser if - * pressed else choose pen. if not a proximity change from out to in, send - * an out of proximity for previous tool then a in for new tool. - */ - if (!wacom->tool[0]) { - /* Eraser bit set for DTF */ - if (data[1] & 0x10) - wacom->tool[1] = BTN_TOOL_RUBBER; - else - /* Going into proximity select tool */ - wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - } else { - /* was entered with stylus2 pressed */ - if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) { - /* report out proximity for previous tool */ - input_report_key(input, wacom->tool[1], 0); - input_sync(input); - wacom->tool[1] = BTN_TOOL_PEN; - return 0; - } - } - if (wacom->tool[1] != BTN_TOOL_RUBBER) { - /* Unknown tool selected default to pen tool */ - wacom->tool[1] = BTN_TOOL_PEN; - wacom->id[0] = STYLUS_DEVICE_ID; - } - input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */ - input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ - input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14)); - input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14)); - input_report_abs(input, ABS_PRESSURE, pressure); - - input_report_key(input, BTN_TOUCH, data[4] & 0x08); - input_report_key(input, BTN_STYLUS, data[4] & 0x10); - /* Only allow the stylus2 button to be reported for the pen tool. */ - input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20)); - } else { - /* report proximity-out of a (valid) tool */ - if (wacom->tool[1] != BTN_TOOL_RUBBER) { - /* Unknown tool selected default to pen tool */ - wacom->tool[1] = BTN_TOOL_PEN; - } - input_report_key(input, wacom->tool[1], prox); - } - - wacom->tool[0] = prox; /* Save proximity state */ - return 1; -} - -static int wacom_ptu_irq(struct wacom_wac *wacom) -{ - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - - if (data[0] != WACOM_REPORT_PENABLED) { - dev_dbg(input->dev.parent, - "%s: received unknown report #%d\n", __func__, data[0]); - return 0; - } - - if (data[1] & 0x04) { - input_report_key(input, BTN_TOOL_RUBBER, data[1] & 0x20); - input_report_key(input, BTN_TOUCH, data[1] & 0x08); - wacom->id[0] = ERASER_DEVICE_ID; - } else { - input_report_key(input, BTN_TOOL_PEN, data[1] & 0x20); - input_report_key(input, BTN_TOUCH, data[1] & 0x01); - wacom->id[0] = STYLUS_DEVICE_ID; - } - input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ - input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); - input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); - input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6])); - input_report_key(input, BTN_STYLUS, data[1] & 0x02); - input_report_key(input, BTN_STYLUS2, data[1] & 0x10); - return 1; -} - -static int wacom_dtu_irq(struct wacom_wac *wacom) -{ - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - int prox = data[1] & 0x20; - - dev_dbg(input->dev.parent, - "%s: received report #%d", __func__, data[0]); - - if (prox) { - /* Going into proximity select tool */ - wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - if (wacom->tool[0] == BTN_TOOL_PEN) - wacom->id[0] = STYLUS_DEVICE_ID; - else - wacom->id[0] = ERASER_DEVICE_ID; - } - input_report_key(input, BTN_STYLUS, data[1] & 0x02); - input_report_key(input, BTN_STYLUS2, data[1] & 0x10); - input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); - input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); - input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]); - input_report_key(input, BTN_TOUCH, data[1] & 0x05); - if (!prox) /* out-prox */ - wacom->id[0] = 0; - input_report_key(input, wacom->tool[0], prox); - input_report_abs(input, ABS_MISC, wacom->id[0]); - return 1; -} - -static int wacom_dtus_irq(struct wacom_wac *wacom) -{ - char *data = wacom->data; - struct input_dev *input = wacom->input; - unsigned short prox, pressure = 0; - - if (data[0] != WACOM_REPORT_DTUS && data[0] != WACOM_REPORT_DTUSPAD) { - dev_dbg(input->dev.parent, - "%s: received unknown report #%d", __func__, data[0]); - return 0; - } else if (data[0] == WACOM_REPORT_DTUSPAD) { - input_report_key(input, BTN_0, (data[1] & 0x01)); - input_report_key(input, BTN_1, (data[1] & 0x02)); - input_report_key(input, BTN_2, (data[1] & 0x04)); - input_report_key(input, BTN_3, (data[1] & 0x08)); - input_report_abs(input, ABS_MISC, - data[1] & 0x0f ? PAD_DEVICE_ID : 0); - /* - * Serial number is required when expresskeys are - * reported through pen interface. - */ - input_event(input, EV_MSC, MSC_SERIAL, 0xf0); - return 1; - } else { - prox = data[1] & 0x80; - if (prox) { - switch ((data[1] >> 3) & 3) { - case 1: /* Rubber */ - wacom->tool[0] = BTN_TOOL_RUBBER; - wacom->id[0] = ERASER_DEVICE_ID; - break; - - case 2: /* Pen */ - wacom->tool[0] = BTN_TOOL_PEN; - wacom->id[0] = STYLUS_DEVICE_ID; - break; - } - } - - input_report_key(input, BTN_STYLUS, data[1] & 0x20); - input_report_key(input, BTN_STYLUS2, data[1] & 0x40); - input_report_abs(input, ABS_X, get_unaligned_be16(&data[3])); - input_report_abs(input, ABS_Y, get_unaligned_be16(&data[5])); - pressure = ((data[1] & 0x03) << 8) | (data[2] & 0xff); - input_report_abs(input, ABS_PRESSURE, pressure); - input_report_key(input, BTN_TOUCH, pressure > 10); - - if (!prox) /* out-prox */ - wacom->id[0] = 0; - input_report_key(input, wacom->tool[0], prox); - input_report_abs(input, ABS_MISC, wacom->id[0]); - input_event(input, EV_MSC, MSC_SERIAL, 1); - return 1; - } -} - -static int wacom_graphire_irq(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - int prox; - int rw = 0; - int retval = 0; - - if (data[0] != WACOM_REPORT_PENABLED) { - dev_dbg(input->dev.parent, - "%s: received unknown report #%d\n", __func__, data[0]); - goto exit; - } - - prox = data[1] & 0x80; - if (prox || wacom->id[0]) { - if (prox) { - switch ((data[1] >> 5) & 3) { - - case 0: /* Pen */ - wacom->tool[0] = BTN_TOOL_PEN; - wacom->id[0] = STYLUS_DEVICE_ID; - break; - - case 1: /* Rubber */ - wacom->tool[0] = BTN_TOOL_RUBBER; - wacom->id[0] = ERASER_DEVICE_ID; - break; - - case 2: /* Mouse with wheel */ - input_report_key(input, BTN_MIDDLE, data[1] & 0x04); - /* fall through */ - - case 3: /* Mouse without wheel */ - wacom->tool[0] = BTN_TOOL_MOUSE; - wacom->id[0] = CURSOR_DEVICE_ID; - break; - } - } - input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); - input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); - if (wacom->tool[0] != BTN_TOOL_MOUSE) { - input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x03) << 8)); - input_report_key(input, BTN_TOUCH, data[1] & 0x01); - input_report_key(input, BTN_STYLUS, data[1] & 0x02); - input_report_key(input, BTN_STYLUS2, data[1] & 0x04); - } else { - input_report_key(input, BTN_LEFT, data[1] & 0x01); - input_report_key(input, BTN_RIGHT, data[1] & 0x02); - if (features->type == WACOM_G4 || - features->type == WACOM_MO) { - input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f); - rw = (data[7] & 0x04) - (data[7] & 0x03); - } else { - input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f); - rw = -(signed char)data[6]; - } - input_report_rel(input, REL_WHEEL, rw); - } - - if (!prox) - wacom->id[0] = 0; - input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */ - input_report_key(input, wacom->tool[0], prox); - input_event(input, EV_MSC, MSC_SERIAL, 1); - input_sync(input); /* sync last event */ - } - - /* send pad data */ - switch (features->type) { - case WACOM_G4: - prox = data[7] & 0xf8; - if (prox || wacom->id[1]) { - wacom->id[1] = PAD_DEVICE_ID; - input_report_key(input, BTN_BACK, (data[7] & 0x40)); - input_report_key(input, BTN_FORWARD, (data[7] & 0x80)); - rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3); - input_report_rel(input, REL_WHEEL, rw); - if (!prox) - wacom->id[1] = 0; - input_report_abs(input, ABS_MISC, wacom->id[1]); - input_event(input, EV_MSC, MSC_SERIAL, 0xf0); - retval = 1; - } - break; - - case WACOM_MO: - prox = (data[7] & 0xf8) || data[8]; - if (prox || wacom->id[1]) { - wacom->id[1] = PAD_DEVICE_ID; - input_report_key(input, BTN_BACK, (data[7] & 0x08)); - input_report_key(input, BTN_LEFT, (data[7] & 0x20)); - input_report_key(input, BTN_FORWARD, (data[7] & 0x10)); - input_report_key(input, BTN_RIGHT, (data[7] & 0x40)); - input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f)); - if (!prox) - wacom->id[1] = 0; - input_report_abs(input, ABS_MISC, wacom->id[1]); - input_event(input, EV_MSC, MSC_SERIAL, 0xf0); - retval = 1; - } - break; - } -exit: - return retval; -} - -static int wacom_intuos_inout(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - int idx = 0; - - /* tool number */ - if (features->type == INTUOS) - idx = data[1] & 0x01; - - /* Enter report */ - if ((data[1] & 0xfc) == 0xc0) { - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = true; - - /* serial number of the tool */ - wacom->serial[idx] = ((data[3] & 0x0f) << 28) + - (data[4] << 20) + (data[5] << 12) + - (data[6] << 4) + (data[7] >> 4); - - wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) | - ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12); - - switch (wacom->id[idx]) { - case 0x812: /* Inking pen */ - case 0x801: /* Intuos3 Inking pen */ - case 0x120802: /* Intuos4/5 Inking Pen */ - case 0x012: - wacom->tool[idx] = BTN_TOOL_PENCIL; - break; - - case 0x822: /* Pen */ - case 0x842: - case 0x852: - case 0x823: /* Intuos3 Grip Pen */ - case 0x813: /* Intuos3 Classic Pen */ - case 0x885: /* Intuos3 Marker Pen */ - case 0x802: /* Intuos4/5 13HD/24HD General Pen */ - case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */ - case 0x022: - case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */ - case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */ - case 0x160802: /* Cintiq 13HD Pro Pen */ - case 0x180802: /* DTH2242 Pen */ - case 0x100802: /* Intuos4/5 13HD/24HD General Pen */ - wacom->tool[idx] = BTN_TOOL_PEN; - break; - - case 0x832: /* Stroke pen */ - case 0x032: - wacom->tool[idx] = BTN_TOOL_BRUSH; - break; - - case 0x007: /* Mouse 4D and 2D */ - case 0x09c: - case 0x094: - case 0x017: /* Intuos3 2D Mouse */ - case 0x806: /* Intuos4 Mouse */ - wacom->tool[idx] = BTN_TOOL_MOUSE; - break; - - case 0x096: /* Lens cursor */ - case 0x097: /* Intuos3 Lens cursor */ - case 0x006: /* Intuos4 Lens cursor */ - wacom->tool[idx] = BTN_TOOL_LENS; - break; - - case 0x82a: /* Eraser */ - case 0x85a: - case 0x91a: - case 0xd1a: - case 0x0fa: - case 0x82b: /* Intuos3 Grip Pen Eraser */ - case 0x81b: /* Intuos3 Classic Pen Eraser */ - case 0x91b: /* Intuos3 Airbrush Eraser */ - case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */ - case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */ - case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ - case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */ - case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ - case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ - case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */ - case 0x18080a: /* DTH2242 Eraser */ - case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */ - wacom->tool[idx] = BTN_TOOL_RUBBER; - break; - - case 0xd12: - case 0x912: - case 0x112: - case 0x913: /* Intuos3 Airbrush */ - case 0x902: /* Intuos4/5 13HD/24HD Airbrush */ - case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */ - wacom->tool[idx] = BTN_TOOL_AIRBRUSH; - break; - - default: /* Unknown tool */ - wacom->tool[idx] = BTN_TOOL_PEN; - break; - } - return 1; - } - - /* older I4 styli don't work with new Cintiqs */ - if (!((wacom->id[idx] >> 20) & 0x01) && - (features->type == WACOM_21UX2)) - return 1; - - /* Range Report */ - if ((data[1] & 0xfe) == 0x20) { - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_PRESSURE, 0); - input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max); - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = true; - } - - /* Exit report */ - if ((data[1] & 0xfe) == 0x80) { - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = false; - - /* - * Reset all states otherwise we lose the initial states - * when in-prox next time - */ - input_report_abs(input, ABS_X, 0); - input_report_abs(input, ABS_Y, 0); - input_report_abs(input, ABS_DISTANCE, 0); - input_report_abs(input, ABS_TILT_X, 0); - input_report_abs(input, ABS_TILT_Y, 0); - if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { - input_report_key(input, BTN_LEFT, 0); - input_report_key(input, BTN_MIDDLE, 0); - input_report_key(input, BTN_RIGHT, 0); - input_report_key(input, BTN_SIDE, 0); - input_report_key(input, BTN_EXTRA, 0); - input_report_abs(input, ABS_THROTTLE, 0); - input_report_abs(input, ABS_RZ, 0); - } else { - input_report_abs(input, ABS_PRESSURE, 0); - input_report_key(input, BTN_STYLUS, 0); - input_report_key(input, BTN_STYLUS2, 0); - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_WHEEL, 0); - if (features->type >= INTUOS3S) - input_report_abs(input, ABS_Z, 0); - } - input_report_key(input, wacom->tool[idx], 0); - input_report_abs(input, ABS_MISC, 0); /* reset tool id */ - input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); - wacom->id[idx] = 0; - return 2; - } - return 0; -} - -static void wacom_intuos_general(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - unsigned int t; - - /* general pen packet */ - if ((data[1] & 0xb8) == 0xa0) { - t = (data[6] << 2) | ((data[7] >> 6) & 3); - if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) { - t = (t << 1) | (data[1] & 1); - } - input_report_abs(input, ABS_PRESSURE, t); - input_report_abs(input, ABS_TILT_X, - ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); - input_report_key(input, BTN_STYLUS, data[1] & 2); - input_report_key(input, BTN_STYLUS2, data[1] & 4); - input_report_key(input, BTN_TOUCH, t > 10); - } - - /* airbrush second packet */ - if ((data[1] & 0xbc) == 0xb4) { - input_report_abs(input, ABS_WHEEL, - (data[6] << 2) | ((data[7] >> 6) & 3)); - input_report_abs(input, ABS_TILT_X, - ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); - } -} - -static int wacom_intuos_irq(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - unsigned int t; - int idx = 0, result; - - if (data[0] != WACOM_REPORT_PENABLED && - data[0] != WACOM_REPORT_INTUOSREAD && - data[0] != WACOM_REPORT_INTUOSWRITE && - data[0] != WACOM_REPORT_INTUOSPAD && - data[0] != WACOM_REPORT_INTUOS5PAD) { - dev_dbg(input->dev.parent, - "%s: received unknown report #%d\n", __func__, data[0]); - return 0; - } - - /* tool number */ - if (features->type == INTUOS) - idx = data[1] & 0x01; - - /* pad packets. Works as a second tool and is always in prox */ - if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD) { - if (features->type >= INTUOS4S && features->type <= INTUOS4L) { - input_report_key(input, BTN_0, (data[2] & 0x01)); - input_report_key(input, BTN_1, (data[3] & 0x01)); - input_report_key(input, BTN_2, (data[3] & 0x02)); - input_report_key(input, BTN_3, (data[3] & 0x04)); - input_report_key(input, BTN_4, (data[3] & 0x08)); - input_report_key(input, BTN_5, (data[3] & 0x10)); - input_report_key(input, BTN_6, (data[3] & 0x20)); - if (data[1] & 0x80) { - input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f)); - } else { - /* Out of proximity, clear wheel value. */ - input_report_abs(input, ABS_WHEEL, 0); - } - if (features->type != INTUOS4S) { - input_report_key(input, BTN_7, (data[3] & 0x40)); - input_report_key(input, BTN_8, (data[3] & 0x80)); - } - if (data[1] | (data[2] & 0x01) | data[3]) { - input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); - } else { - input_report_abs(input, ABS_MISC, 0); - } - } else if (features->type == DTK) { - input_report_key(input, BTN_0, (data[6] & 0x01)); - input_report_key(input, BTN_1, (data[6] & 0x02)); - input_report_key(input, BTN_2, (data[6] & 0x04)); - input_report_key(input, BTN_3, (data[6] & 0x08)); - input_report_key(input, BTN_4, (data[6] & 0x10)); - input_report_key(input, BTN_5, (data[6] & 0x20)); - if (data[6] & 0x3f) { - input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); - } else { - input_report_abs(input, ABS_MISC, 0); - } - } else if (features->type == WACOM_13HD) { - input_report_key(input, BTN_0, (data[3] & 0x01)); - input_report_key(input, BTN_1, (data[4] & 0x01)); - input_report_key(input, BTN_2, (data[4] & 0x02)); - input_report_key(input, BTN_3, (data[4] & 0x04)); - input_report_key(input, BTN_4, (data[4] & 0x08)); - input_report_key(input, BTN_5, (data[4] & 0x10)); - input_report_key(input, BTN_6, (data[4] & 0x20)); - input_report_key(input, BTN_7, (data[4] & 0x40)); - input_report_key(input, BTN_8, (data[4] & 0x80)); - if ((data[3] & 0x01) | data[4]) { - input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); - } else { - input_report_abs(input, ABS_MISC, 0); - } - } else if (features->type == WACOM_24HD) { - input_report_key(input, BTN_0, (data[6] & 0x01)); - input_report_key(input, BTN_1, (data[6] & 0x02)); - input_report_key(input, BTN_2, (data[6] & 0x04)); - input_report_key(input, BTN_3, (data[6] & 0x08)); - input_report_key(input, BTN_4, (data[6] & 0x10)); - input_report_key(input, BTN_5, (data[6] & 0x20)); - input_report_key(input, BTN_6, (data[6] & 0x40)); - input_report_key(input, BTN_7, (data[6] & 0x80)); - input_report_key(input, BTN_8, (data[8] & 0x01)); - input_report_key(input, BTN_9, (data[8] & 0x02)); - input_report_key(input, BTN_A, (data[8] & 0x04)); - input_report_key(input, BTN_B, (data[8] & 0x08)); - input_report_key(input, BTN_C, (data[8] & 0x10)); - input_report_key(input, BTN_X, (data[8] & 0x20)); - input_report_key(input, BTN_Y, (data[8] & 0x40)); - input_report_key(input, BTN_Z, (data[8] & 0x80)); - - /* - * Three "buttons" are available on the 24HD which are - * physically implemented as a touchstrip. Each button - * is approximately 3 bits wide with a 2 bit spacing. - * The raw touchstrip bits are stored at: - * ((data[3] & 0x1f) << 8) | data[4]) - */ - input_report_key(input, KEY_PROG1, data[4] & 0x07); - input_report_key(input, KEY_PROG2, data[4] & 0xE0); - input_report_key(input, KEY_PROG3, data[3] & 0x1C); - - if (data[1] & 0x80) { - input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f)); - } else { - /* Out of proximity, clear wheel value. */ - input_report_abs(input, ABS_WHEEL, 0); - } - - if (data[2] & 0x80) { - input_report_abs(input, ABS_THROTTLE, (data[2] & 0x7f)); - } else { - /* Out of proximity, clear second wheel value. */ - input_report_abs(input, ABS_THROTTLE, 0); - } - - if (data[1] | data[2] | (data[3] & 0x1f) | data[4] | data[6] | data[8]) { - input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); - } else { - input_report_abs(input, ABS_MISC, 0); - } - } else if (features->type == CINTIQ_HYBRID) { - /* - * Do not send hardware buttons under Android. They - * are already sent to the system through GPIO (and - * have different meaning). - */ - input_report_key(input, BTN_1, (data[4] & 0x01)); - input_report_key(input, BTN_2, (data[4] & 0x02)); - input_report_key(input, BTN_3, (data[4] & 0x04)); - input_report_key(input, BTN_4, (data[4] & 0x08)); - - input_report_key(input, BTN_5, (data[4] & 0x10)); /* Right */ - input_report_key(input, BTN_6, (data[4] & 0x20)); /* Up */ - input_report_key(input, BTN_7, (data[4] & 0x40)); /* Left */ - input_report_key(input, BTN_8, (data[4] & 0x80)); /* Down */ - input_report_key(input, BTN_0, (data[3] & 0x01)); /* Center */ - } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) { - int i; - - /* Touch ring mode switch has no capacitive sensor */ - input_report_key(input, BTN_0, (data[3] & 0x01)); - - /* - * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in - * addition to the mechanical switch. Switch data is - * stored in data[4], capacitive data in data[5]. - */ - for (i = 0; i < 8; i++) - input_report_key(input, BTN_1 + i, data[4] & (1 << i)); - - if (data[2] & 0x80) { - input_report_abs(input, ABS_WHEEL, (data[2] & 0x7f)); - } else { - /* Out of proximity, clear wheel value. */ - input_report_abs(input, ABS_WHEEL, 0); - } - - if (data[2] | (data[3] & 0x01) | data[4] | data[5]) { - input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); - } else { - input_report_abs(input, ABS_MISC, 0); - } - } else { - if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) { - input_report_key(input, BTN_0, (data[5] & 0x01)); - input_report_key(input, BTN_1, (data[6] & 0x01)); - input_report_key(input, BTN_2, (data[6] & 0x02)); - input_report_key(input, BTN_3, (data[6] & 0x04)); - input_report_key(input, BTN_4, (data[6] & 0x08)); - input_report_key(input, BTN_5, (data[6] & 0x10)); - input_report_key(input, BTN_6, (data[6] & 0x20)); - input_report_key(input, BTN_7, (data[6] & 0x40)); - input_report_key(input, BTN_8, (data[6] & 0x80)); - input_report_key(input, BTN_9, (data[7] & 0x01)); - input_report_key(input, BTN_A, (data[8] & 0x01)); - input_report_key(input, BTN_B, (data[8] & 0x02)); - input_report_key(input, BTN_C, (data[8] & 0x04)); - input_report_key(input, BTN_X, (data[8] & 0x08)); - input_report_key(input, BTN_Y, (data[8] & 0x10)); - input_report_key(input, BTN_Z, (data[8] & 0x20)); - input_report_key(input, BTN_BASE, (data[8] & 0x40)); - input_report_key(input, BTN_BASE2, (data[8] & 0x80)); - - if (features->type == WACOM_22HD) { - input_report_key(input, KEY_PROG1, data[9] & 0x01); - input_report_key(input, KEY_PROG2, data[9] & 0x02); - input_report_key(input, KEY_PROG3, data[9] & 0x04); - } - } else { - input_report_key(input, BTN_0, (data[5] & 0x01)); - input_report_key(input, BTN_1, (data[5] & 0x02)); - input_report_key(input, BTN_2, (data[5] & 0x04)); - input_report_key(input, BTN_3, (data[5] & 0x08)); - input_report_key(input, BTN_4, (data[6] & 0x01)); - input_report_key(input, BTN_5, (data[6] & 0x02)); - input_report_key(input, BTN_6, (data[6] & 0x04)); - input_report_key(input, BTN_7, (data[6] & 0x08)); - input_report_key(input, BTN_8, (data[5] & 0x10)); - input_report_key(input, BTN_9, (data[6] & 0x10)); - } - input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); - input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); - - if ((data[5] & 0x1f) | data[6] | (data[1] & 0x1f) | - data[2] | (data[3] & 0x1f) | data[4] | data[8] | - (data[7] & 0x01)) { - input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); - } else { - input_report_abs(input, ABS_MISC, 0); - } - } - input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff); - return 1; - } - - /* process in/out prox events */ - result = wacom_intuos_inout(wacom); - if (result) - return result - 1; - - /* don't proceed if we don't know the ID */ - if (!wacom->id[idx]) - return 0; - - /* Only large Intuos support Lense Cursor */ - if (wacom->tool[idx] == BTN_TOOL_LENS && - (features->type == INTUOS3 || - features->type == INTUOS3S || - features->type == INTUOS4 || - features->type == INTUOS4S || - features->type == INTUOS5 || - features->type == INTUOS5S || - features->type == INTUOSPM || - features->type == INTUOSPS)) { - - return 0; - } - - /* Cintiq doesn't send data when RDY bit isn't set */ - if (features->type == CINTIQ && !(data[1] & 0x40)) - return 0; - - if (features->type >= INTUOS3S) { - input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); - input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); - input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); - } else { - input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2])); - input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4])); - input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f)); - } - - /* process general packets */ - wacom_intuos_general(wacom); - - /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */ - if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) { - - if (data[1] & 0x02) { - /* Rotation packet */ - if (features->type >= INTUOS3S) { - /* I3 marker pen rotation */ - t = (data[6] << 3) | ((data[7] >> 5) & 7); - t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : - ((t-1) / 2 + 450)) : (450 - t / 2) ; - input_report_abs(input, ABS_Z, t); - } else { - /* 4D mouse rotation packet */ - t = (data[6] << 3) | ((data[7] >> 5) & 7); - input_report_abs(input, ABS_RZ, (data[7] & 0x20) ? - ((t - 1) / 2) : -t / 2); - } - - } else if (!(data[1] & 0x10) && features->type < INTUOS3S) { - /* 4D mouse packet */ - input_report_key(input, BTN_LEFT, data[8] & 0x01); - input_report_key(input, BTN_MIDDLE, data[8] & 0x02); - input_report_key(input, BTN_RIGHT, data[8] & 0x04); - - input_report_key(input, BTN_SIDE, data[8] & 0x20); - input_report_key(input, BTN_EXTRA, data[8] & 0x10); - t = (data[6] << 2) | ((data[7] >> 6) & 3); - input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); - - } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) { - /* I4 mouse */ - if (features->type >= INTUOS4S && features->type <= INTUOSPL) { - input_report_key(input, BTN_LEFT, data[6] & 0x01); - input_report_key(input, BTN_MIDDLE, data[6] & 0x02); - input_report_key(input, BTN_RIGHT, data[6] & 0x04); - input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7) - - ((data[7] & 0x40) >> 6)); - input_report_key(input, BTN_SIDE, data[6] & 0x08); - input_report_key(input, BTN_EXTRA, data[6] & 0x10); - - input_report_abs(input, ABS_TILT_X, - ((data[7] << 1) & 0x7e) | (data[8] >> 7)); - input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f); - } else { - /* 2D mouse packet */ - input_report_key(input, BTN_LEFT, data[8] & 0x04); - input_report_key(input, BTN_MIDDLE, data[8] & 0x08); - input_report_key(input, BTN_RIGHT, data[8] & 0x10); - input_report_rel(input, REL_WHEEL, (data[8] & 0x01) - - ((data[8] & 0x02) >> 1)); - - /* I3 2D mouse side buttons */ - if (features->type >= INTUOS3S && features->type <= INTUOS3L) { - input_report_key(input, BTN_SIDE, data[8] & 0x40); - input_report_key(input, BTN_EXTRA, data[8] & 0x20); - } - } - } else if ((features->type < INTUOS3S || features->type == INTUOS3L || - features->type == INTUOS4L || features->type == INTUOS5L || - features->type == INTUOSPL) && - wacom->tool[idx] == BTN_TOOL_LENS) { - /* Lens cursor packets */ - input_report_key(input, BTN_LEFT, data[8] & 0x01); - input_report_key(input, BTN_MIDDLE, data[8] & 0x02); - input_report_key(input, BTN_RIGHT, data[8] & 0x04); - input_report_key(input, BTN_SIDE, data[8] & 0x10); - input_report_key(input, BTN_EXTRA, data[8] & 0x08); - } - } - - input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */ - input_report_key(input, wacom->tool[idx], 1); - input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); - return 1; -} - -static int int_dist(int x1, int y1, int x2, int y2) -{ - int x = x2 - x1; - int y = y2 - y1; - - return int_sqrt(x*x + y*y); -} - -static int wacom_24hdt_irq(struct wacom_wac *wacom) -{ - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int i; - int current_num_contacts = data[61]; - int contacts_to_send = 0; - - /* - * First packet resets the counter since only the first - * packet in series will have non-zero current_num_contacts. - */ - if (current_num_contacts) - wacom->num_contacts_left = current_num_contacts; - - /* There are at most 4 contacts per packet */ - contacts_to_send = min(4, wacom->num_contacts_left); - - for (i = 0; i < contacts_to_send; i++) { - int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1; - bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity; - int slot = input_mt_get_slot_by_key(input, data[offset + 1]); - - if (slot < 0) - continue; - input_mt_slot(input, slot); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - - if (touch) { - int t_x = get_unaligned_le16(&data[offset + 2]); - int c_x = get_unaligned_le16(&data[offset + 4]); - int t_y = get_unaligned_le16(&data[offset + 6]); - int c_y = get_unaligned_le16(&data[offset + 8]); - int w = get_unaligned_le16(&data[offset + 10]); - int h = get_unaligned_le16(&data[offset + 12]); - - input_report_abs(input, ABS_MT_POSITION_X, t_x); - input_report_abs(input, ABS_MT_POSITION_Y, t_y); - input_report_abs(input, ABS_MT_TOUCH_MAJOR, min(w,h)); - input_report_abs(input, ABS_MT_WIDTH_MAJOR, min(w, h) + int_dist(t_x, t_y, c_x, c_y)); - input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); - input_report_abs(input, ABS_MT_ORIENTATION, w > h); - } - } - input_mt_report_pointer_emulation(input, true); - - wacom->num_contacts_left -= contacts_to_send; - if (wacom->num_contacts_left <= 0) - wacom->num_contacts_left = 0; - - return 1; -} - -static int wacom_mt_touch(struct wacom_wac *wacom) -{ - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int i; - int current_num_contacts = data[2]; - int contacts_to_send = 0; - int x_offset = 0; - - /* MTTPC does not support Height and Width */ - if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B) - x_offset = -4; - - /* - * First packet resets the counter since only the first - * packet in series will have non-zero current_num_contacts. - */ - if (current_num_contacts) - wacom->num_contacts_left = current_num_contacts; - - /* There are at most 5 contacts per packet */ - contacts_to_send = min(5, wacom->num_contacts_left); - - for (i = 0; i < contacts_to_send; i++) { - int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; - bool touch = data[offset] & 0x1; - int id = get_unaligned_le16(&data[offset + 1]); - int slot = input_mt_get_slot_by_key(input, id); - - if (slot < 0) - continue; - - input_mt_slot(input, slot); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - if (touch) { - int x = get_unaligned_le16(&data[offset + x_offset + 7]); - int y = get_unaligned_le16(&data[offset + x_offset + 9]); - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); - } - } - input_mt_report_pointer_emulation(input, true); - - wacom->num_contacts_left -= contacts_to_send; - if (wacom->num_contacts_left < 0) - wacom->num_contacts_left = 0; - - return 1; -} - -static int wacom_tpc_mt_touch(struct wacom_wac *wacom) -{ - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int contact_with_no_pen_down_count = 0; - int i; - - for (i = 0; i < 2; i++) { - int p = data[1] & (1 << i); - bool touch = p && !wacom->shared->stylus_in_proximity; - - input_mt_slot(input, i); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - if (touch) { - int x = le16_to_cpup((__le16 *)&data[i * 2 + 2]) & 0x7fff; - int y = le16_to_cpup((__le16 *)&data[i * 2 + 6]) & 0x7fff; - - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); - contact_with_no_pen_down_count++; - } - } - input_mt_report_pointer_emulation(input, true); - - /* keep touch state for pen event */ - wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); - - return 1; -} - -static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) -{ - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - bool prox; - int x = 0, y = 0; - - if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG) - return 0; - - if (!wacom->shared->stylus_in_proximity) { - if (len == WACOM_PKGLEN_TPC1FG) { - prox = data[0] & 0x01; - x = get_unaligned_le16(&data[1]); - y = get_unaligned_le16(&data[3]); - } else if (len == WACOM_PKGLEN_TPC1FG_B) { - prox = data[2] & 0x01; - x = get_unaligned_le16(&data[3]); - y = get_unaligned_le16(&data[5]); - } else { - prox = data[1] & 0x01; - x = le16_to_cpup((__le16 *)&data[2]); - y = le16_to_cpup((__le16 *)&data[4]); - } - } else - /* force touch out when pen is in prox */ - prox = 0; - - if (prox) { - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - } - input_report_key(input, BTN_TOUCH, prox); - - /* keep touch state for pen events */ - wacom->shared->touch_down = prox; - - return 1; -} - -static int wacom_tpc_pen(struct wacom_wac *wacom) -{ - unsigned char *data = wacom->data; - struct input_dev *input = wacom->input; - bool prox = data[1] & 0x20; - - if (!wacom->shared->stylus_in_proximity) /* first in prox */ - /* Going into proximity select tool */ - wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - - /* keep pen state for touch events */ - wacom->shared->stylus_in_proximity = prox; - - /* send pen events only when touch is up or forced out */ - if (!wacom->shared->touch_down) { - input_report_key(input, BTN_STYLUS, data[1] & 0x02); - input_report_key(input, BTN_STYLUS2, data[1] & 0x10); - input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); - input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); - input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x03) << 8) | data[6]); - input_report_key(input, BTN_TOUCH, data[1] & 0x05); - input_report_key(input, wacom->tool[0], prox); - return 1; - } - - return 0; -} - -static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) -{ - unsigned char *data = wacom->data; - - dev_dbg(wacom->input->dev.parent, - "%s: received report #%d\n", __func__, data[0]); - - switch (len) { - case WACOM_PKGLEN_TPC1FG: - return wacom_tpc_single_touch(wacom, len); - - case WACOM_PKGLEN_TPC2FG: - return wacom_tpc_mt_touch(wacom); - - case WACOM_PKGLEN_PENABLED: - return wacom_tpc_pen(wacom); - - default: - switch (data[0]) { - case WACOM_REPORT_TPC1FG: - case WACOM_REPORT_TPCHID: - case WACOM_REPORT_TPCST: - case WACOM_REPORT_TPC1FGE: - return wacom_tpc_single_touch(wacom, len); - - case WACOM_REPORT_TPCMT: - case WACOM_REPORT_TPCMT2: - return wacom_mt_touch(wacom); - - case WACOM_REPORT_PENABLED: - return wacom_tpc_pen(wacom); - } - } - - return 0; -} - -static int wacom_bpt_touch(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int i; - - if (data[0] != 0x02) - return 0; - - for (i = 0; i < 2; i++) { - int offset = (data[1] & 0x80) ? (8 * i) : (9 * i); - bool touch = data[offset + 3] & 0x80; - - /* - * Touch events need to be disabled while stylus is - * in proximity because user's hand is resting on touchpad - * and sending unwanted events. User expects tablet buttons - * to continue working though. - */ - touch = touch && !wacom->shared->stylus_in_proximity; - - input_mt_slot(input, i); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - if (touch) { - int x = get_unaligned_be16(&data[offset + 3]) & 0x7ff; - int y = get_unaligned_be16(&data[offset + 5]) & 0x7ff; - if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { - x <<= 5; - y <<= 5; - } - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); - } - } - - input_mt_report_pointer_emulation(input, true); - - input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); - input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); - input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); - input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); - - input_sync(input); - - return 0; -} - -static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) -{ - struct wacom_features *features = &wacom->features; - struct input_dev *input = wacom->input; - bool touch = data[1] & 0x80; - int slot = input_mt_get_slot_by_key(input, data[0]); - - if (slot < 0) - return; - - touch = touch && !wacom->shared->stylus_in_proximity; - - input_mt_slot(input, slot); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - - if (touch) { - int x = (data[2] << 4) | (data[4] >> 4); - int y = (data[3] << 4) | (data[4] & 0x0f); - int width, height; - - if (features->type >= INTUOSPS && features->type <= INTUOSPL) { - width = data[5] * 100; - height = data[6] * 100; - } else { - /* - * "a" is a scaled-down area which we assume is - * roughly circular and which can be described as: - * a=(pi*r^2)/C. - */ - int a = data[5]; - int x_res = input_abs_get_res(input, ABS_MT_POSITION_X); - int y_res = input_abs_get_res(input, ABS_MT_POSITION_Y); - width = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE); - height = width * y_res / x_res; - } - - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); - input_report_abs(input, ABS_MT_TOUCH_MAJOR, width); - input_report_abs(input, ABS_MT_TOUCH_MINOR, height); - } -} - -static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) -{ - struct input_dev *input = wacom->input; - struct wacom_features *features = &wacom->features; - - if (features->type == INTUOSHT) { - input_report_key(input, BTN_LEFT, (data[1] & 0x02) != 0); - input_report_key(input, BTN_BACK, (data[1] & 0x08) != 0); - } else { - input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); - input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); - } - input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); - input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); -} - -static int wacom_bpt3_touch(struct wacom_wac *wacom) -{ - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int count = data[1] & 0x07; - int i; - - if (data[0] != 0x02) - return 0; - - /* data has up to 7 fixed sized 8-byte messages starting at data[2] */ - for (i = 0; i < count; i++) { - int offset = (8 * i) + 2; - int msg_id = data[offset]; - - if (msg_id >= 2 && msg_id <= 17) - wacom_bpt3_touch_msg(wacom, data + offset); - else if (msg_id == 128) - wacom_bpt3_button_msg(wacom, data + offset); - - } - input_mt_report_pointer_emulation(input, true); - - input_sync(input); - - return 0; -} - -static int wacom_bpt_pen(struct wacom_wac *wacom) -{ - struct wacom_features *features = &wacom->features; - struct input_dev *input = wacom->input; - unsigned char *data = wacom->data; - int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0; - - if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_USB) - return 0; - - if (data[0] == WACOM_REPORT_USB) { - if (features->type == INTUOSHT && features->touch_max) { - input_report_switch(wacom->shared->touch_input, - SW_MUTE_DEVICE, data[8] & 0x40); - input_sync(wacom->shared->touch_input); - } - return 0; - } - - prox = (data[1] & 0x20) == 0x20; - - /* - * All reports shared between PEN and RUBBER tool must be - * forced to a known starting value (zero) when transitioning to - * out-of-prox. - * - * If not reset then, to userspace, it will look like lost events - * if new tool comes in-prox with same values as previous tool sent. - * - * Hardware does report zero in most out-of-prox cases but not all. - */ - if (prox) { - if (!wacom->shared->stylus_in_proximity) { - if (data[1] & 0x08) { - wacom->tool[0] = BTN_TOOL_RUBBER; - wacom->id[0] = ERASER_DEVICE_ID; - } else { - wacom->tool[0] = BTN_TOOL_PEN; - wacom->id[0] = STYLUS_DEVICE_ID; - } - wacom->shared->stylus_in_proximity = true; - } - x = le16_to_cpup((__le16 *)&data[2]); - y = le16_to_cpup((__le16 *)&data[4]); - p = le16_to_cpup((__le16 *)&data[6]); - /* - * Convert distance from out prox to distance from tablet. - * distance will be greater than distance_max once - * touching and applying pressure; do not report negative - * distance. - */ - if (data[8] <= features->distance_max) - d = features->distance_max - data[8]; - - pen = data[1] & 0x01; - btn1 = data[1] & 0x02; - btn2 = data[1] & 0x04; - } - - input_report_key(input, BTN_TOUCH, pen); - input_report_key(input, BTN_STYLUS, btn1); - input_report_key(input, BTN_STYLUS2, btn2); - - input_report_abs(input, ABS_X, x); - input_report_abs(input, ABS_Y, y); - input_report_abs(input, ABS_PRESSURE, p); - input_report_abs(input, ABS_DISTANCE, d); - - if (!prox) { - wacom->id[0] = 0; - wacom->shared->stylus_in_proximity = false; - } - - input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */ - input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */ - - return 1; -} - -static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) -{ - if (len == WACOM_PKGLEN_BBTOUCH) - return wacom_bpt_touch(wacom); - else if (len == WACOM_PKGLEN_BBTOUCH3) - return wacom_bpt3_touch(wacom); - else if (len == WACOM_PKGLEN_BBFUN || len == WACOM_PKGLEN_BBPEN) - return wacom_bpt_pen(wacom); - - return 0; -} - -static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) -{ - unsigned char *data = wacom->data; - int connected; - - if (len != WACOM_PKGLEN_WIRELESS || data[0] != WACOM_REPORT_WL) - return 0; - - connected = data[1] & 0x01; - if (connected) { - int pid, battery; - - if ((wacom->shared->type == INTUOSHT) && - wacom->shared->touch_max) { - input_report_switch(wacom->shared->touch_input, - SW_MUTE_DEVICE, data[5] & 0x40); - input_sync(wacom->shared->touch_input); - } - - pid = get_unaligned_be16(&data[6]); - battery = data[5] & 0x3f; - if (wacom->pid != pid) { - wacom->pid = pid; - wacom_schedule_work(wacom); - } - wacom->battery_capacity = battery; - } else if (wacom->pid != 0) { - /* disconnected while previously connected */ - wacom->pid = 0; - wacom_schedule_work(wacom); - wacom->battery_capacity = 0; - } - - return 0; -} - -void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) -{ - bool sync; - - switch (wacom_wac->features.type) { - case PENPARTNER: - sync = wacom_penpartner_irq(wacom_wac); - break; - - case PL: - sync = wacom_pl_irq(wacom_wac); - break; - - case WACOM_G4: - case GRAPHIRE: - case WACOM_MO: - sync = wacom_graphire_irq(wacom_wac); - break; - - case PTU: - sync = wacom_ptu_irq(wacom_wac); - break; - - case DTU: - sync = wacom_dtu_irq(wacom_wac); - break; - - case DTUS: - sync = wacom_dtus_irq(wacom_wac); - break; - - case INTUOS: - case INTUOS3S: - case INTUOS3: - case INTUOS3L: - case INTUOS4S: - case INTUOS4: - case INTUOS4L: - case CINTIQ: - case WACOM_BEE: - case WACOM_13HD: - case WACOM_21UX2: - case WACOM_22HD: - case WACOM_24HD: - case DTK: - case CINTIQ_HYBRID: - sync = wacom_intuos_irq(wacom_wac); - break; - - case WACOM_24HDT: - sync = wacom_24hdt_irq(wacom_wac); - break; - - case INTUOS5S: - case INTUOS5: - case INTUOS5L: - case INTUOSPS: - case INTUOSPM: - case INTUOSPL: - if (len == WACOM_PKGLEN_BBTOUCH3) - sync = wacom_bpt3_touch(wacom_wac); - else - sync = wacom_intuos_irq(wacom_wac); - break; - - case TABLETPC: - case TABLETPCE: - case TABLETPC2FG: - case MTSCREEN: - case MTTPC: - case MTTPC_B: - sync = wacom_tpc_irq(wacom_wac, len); - break; - - case BAMBOO_PT: - case INTUOSHT: - sync = wacom_bpt_irq(wacom_wac, len); - break; - - case WIRELESS: - sync = wacom_wireless_irq(wacom_wac, len); - break; - - default: - sync = false; - break; - } - - if (sync) - input_sync(wacom_wac->input); -} - -static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) -{ - struct input_dev *input_dev = wacom_wac->input; - - input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); - __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); - __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - __set_bit(BTN_STYLUS2, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_DISTANCE, - 0, wacom_wac->features.distance_max, 0, 0); - input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); - input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); -} - -static void wacom_setup_intuos(struct wacom_wac *wacom_wac) -{ - struct input_dev *input_dev = wacom_wac->input; - - input_set_capability(input_dev, EV_REL, REL_WHEEL); - - wacom_setup_cintiq(wacom_wac); - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(BTN_MIDDLE, input_dev->keybit); - __set_bit(BTN_SIDE, input_dev->keybit); - __set_bit(BTN_EXTRA, input_dev->keybit); - __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); - __set_bit(BTN_TOOL_LENS, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); - input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); -} - -void wacom_setup_device_quirks(struct wacom_features *features) -{ - - /* touch device found but size is not defined. use default */ - if (features->device_type == BTN_TOOL_FINGER && !features->x_max) { - features->x_max = 1023; - features->y_max = 1023; - } - - /* these device have multiple inputs */ - if (features->type >= WIRELESS || - (features->type >= INTUOS5S && features->type <= INTUOSHT) || - (features->oVid && features->oPid)) - features->quirks |= WACOM_QUIRK_MULTI_INPUT; - - /* quirk for bamboo touch with 2 low res touches */ - if (features->type == BAMBOO_PT && - features->pktlen == WACOM_PKGLEN_BBTOUCH) { - features->x_max <<= 5; - features->y_max <<= 5; - features->x_fuzz <<= 5; - features->y_fuzz <<= 5; - features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; - } - - if (features->type == WIRELESS) { - - /* monitor never has input and pen/touch have delayed create */ - features->quirks |= WACOM_QUIRK_NO_INPUT; - - /* must be monitor interface if no device_type set */ - if (!features->device_type) - features->quirks |= WACOM_QUIRK_MONITOR; - } -} - -static void wacom_abs_set_axis(struct input_dev *input_dev, - struct wacom_wac *wacom_wac) -{ - struct wacom_features *features = &wacom_wac->features; - - if (features->device_type == BTN_TOOL_PEN) { - input_set_abs_params(input_dev, ABS_X, features->x_min, - features->x_max, features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_Y, features->y_min, - features->y_max, features->y_fuzz, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, - features->pressure_max, features->pressure_fuzz, 0); - - /* penabled devices have fixed resolution for each model */ - input_abs_set_res(input_dev, ABS_X, features->x_resolution); - input_abs_set_res(input_dev, ABS_Y, features->y_resolution); - } else { - if (features->touch_max == 1) { - input_set_abs_params(input_dev, ABS_X, 0, - features->x_max, features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_Y, 0, - features->y_max, features->y_fuzz, 0); - input_abs_set_res(input_dev, ABS_X, - features->x_resolution); - input_abs_set_res(input_dev, ABS_Y, - features->y_resolution); - } - - if (features->touch_max > 1) { - input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, - features->x_max, features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, - features->y_max, features->y_fuzz, 0); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - features->x_resolution); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - features->y_resolution); - } - } -} - -int wacom_setup_input_capabilities(struct input_dev *input_dev, - struct wacom_wac *wacom_wac) -{ - struct wacom_features *features = &wacom_wac->features; - int i; - - input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - - __set_bit(BTN_TOUCH, input_dev->keybit); - __set_bit(ABS_MISC, input_dev->absbit); - - wacom_abs_set_axis(input_dev, wacom_wac); - - switch (features->type) { - case WACOM_MO: - input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); - /* fall through */ - - case WACOM_G4: - input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - - __set_bit(BTN_BACK, input_dev->keybit); - __set_bit(BTN_FORWARD, input_dev->keybit); - /* fall through */ - - case GRAPHIRE: - input_set_capability(input_dev, EV_REL, REL_WHEEL); - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(BTN_MIDDLE, input_dev->keybit); - - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - __set_bit(BTN_STYLUS2, input_dev->keybit); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - break; - - case WACOM_24HD: - __set_bit(BTN_A, input_dev->keybit); - __set_bit(BTN_B, input_dev->keybit); - __set_bit(BTN_C, input_dev->keybit); - __set_bit(BTN_X, input_dev->keybit); - __set_bit(BTN_Y, input_dev->keybit); - __set_bit(BTN_Z, input_dev->keybit); - - for (i = 6; i < 10; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - - __set_bit(KEY_PROG1, input_dev->keybit); - __set_bit(KEY_PROG2, input_dev->keybit); - __set_bit(KEY_PROG3, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); - /* fall through */ - - case DTK: - for (i = 0; i < 6; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - - wacom_setup_cintiq(wacom_wac); - break; - - case WACOM_22HD: - __set_bit(KEY_PROG1, input_dev->keybit); - __set_bit(KEY_PROG2, input_dev->keybit); - __set_bit(KEY_PROG3, input_dev->keybit); - /* fall through */ - - case WACOM_21UX2: - __set_bit(BTN_A, input_dev->keybit); - __set_bit(BTN_B, input_dev->keybit); - __set_bit(BTN_C, input_dev->keybit); - __set_bit(BTN_X, input_dev->keybit); - __set_bit(BTN_Y, input_dev->keybit); - __set_bit(BTN_Z, input_dev->keybit); - __set_bit(BTN_BASE, input_dev->keybit); - __set_bit(BTN_BASE2, input_dev->keybit); - /* fall through */ - - case WACOM_BEE: - __set_bit(BTN_8, input_dev->keybit); - __set_bit(BTN_9, input_dev->keybit); - /* fall through */ - - case CINTIQ: - for (i = 0; i < 8; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); - input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - - wacom_setup_cintiq(wacom_wac); - break; - - case WACOM_13HD: - for (i = 0; i < 9; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - wacom_setup_cintiq(wacom_wac); - break; - - case INTUOS3: - case INTUOS3L: - __set_bit(BTN_4, input_dev->keybit); - __set_bit(BTN_5, input_dev->keybit); - __set_bit(BTN_6, input_dev->keybit); - __set_bit(BTN_7, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); - /* fall through */ - - case INTUOS3S: - __set_bit(BTN_0, input_dev->keybit); - __set_bit(BTN_1, input_dev->keybit); - __set_bit(BTN_2, input_dev->keybit); - __set_bit(BTN_3, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - /* fall through */ - - case INTUOS: - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - - wacom_setup_intuos(wacom_wac); - break; - - case INTUOS5: - case INTUOS5L: - case INTUOSPM: - case INTUOSPL: - if (features->device_type == BTN_TOOL_PEN) { - __set_bit(BTN_7, input_dev->keybit); - __set_bit(BTN_8, input_dev->keybit); - } - /* fall through */ - - case INTUOS5S: - case INTUOSPS: - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - - if (features->device_type == BTN_TOOL_PEN) { - for (i = 0; i < 7; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_DISTANCE, 0, - features->distance_max, - 0, 0); - - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - - wacom_setup_intuos(wacom_wac); - } else if (features->device_type == BTN_TOOL_FINGER) { - __clear_bit(ABS_MISC, input_dev->absbit); - - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, features->x_max, 0, 0); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, - 0, features->y_max, 0, 0); - input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); - } - break; - - case INTUOS4: - case INTUOS4L: - __set_bit(BTN_7, input_dev->keybit); - __set_bit(BTN_8, input_dev->keybit); - /* fall through */ - - case INTUOS4S: - for (i = 0; i < 7; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - wacom_setup_intuos(wacom_wac); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - break; - - case WACOM_24HDT: - if (features->device_type == BTN_TOOL_FINGER) { - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); - input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0); - input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0); - input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); - } - /* fall through */ - - case MTSCREEN: - case MTTPC: - case MTTPC_B: - case TABLETPC2FG: - if (features->device_type == BTN_TOOL_FINGER && features->touch_max > 1) - input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT); - /* fall through */ - - case TABLETPC: - case TABLETPCE: - __clear_bit(ABS_MISC, input_dev->absbit); - - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - - if (features->device_type != BTN_TOOL_PEN) - break; /* no need to process stylus stuff */ - - /* fall through */ - - case DTUS: - case PL: - case DTU: - if (features->type == DTUS) { - input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - for (i = 0; i < 4; i++) - __set_bit(BTN_0 + i, input_dev->keybit); - } - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - __set_bit(BTN_STYLUS2, input_dev->keybit); - - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - break; - - case PTU: - __set_bit(BTN_STYLUS2, input_dev->keybit); - /* fall through */ - - case PENPARTNER: - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - break; - - case INTUOSHT: - if (features->touch_max && - features->device_type == BTN_TOOL_FINGER) { - input_dev->evbit[0] |= BIT_MASK(EV_SW); - __set_bit(SW_MUTE_DEVICE, input_dev->swbit); - } - /* fall through */ - - case BAMBOO_PT: - __clear_bit(ABS_MISC, input_dev->absbit); - - if (features->device_type == BTN_TOOL_FINGER) { - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_FORWARD, input_dev->keybit); - __set_bit(BTN_BACK, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - - if (features->touch_max) { - if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { - input_set_abs_params(input_dev, - ABS_MT_TOUCH_MAJOR, - 0, features->x_max, 0, 0); - input_set_abs_params(input_dev, - ABS_MT_TOUCH_MINOR, - 0, features->y_max, 0, 0); - } - input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); - } else { - /* buttons/keys only interface */ - __clear_bit(ABS_X, input_dev->absbit); - __clear_bit(ABS_Y, input_dev->absbit); - __clear_bit(BTN_TOUCH, input_dev->keybit); - } - } else if (features->device_type == BTN_TOOL_PEN) { - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - __set_bit(BTN_STYLUS2, input_dev->keybit); - input_set_abs_params(input_dev, ABS_DISTANCE, 0, - features->distance_max, - 0, 0); - } - break; - - case CINTIQ_HYBRID: - __set_bit(BTN_1, input_dev->keybit); - __set_bit(BTN_2, input_dev->keybit); - __set_bit(BTN_3, input_dev->keybit); - __set_bit(BTN_4, input_dev->keybit); - - __set_bit(BTN_5, input_dev->keybit); - __set_bit(BTN_6, input_dev->keybit); - __set_bit(BTN_7, input_dev->keybit); - __set_bit(BTN_8, input_dev->keybit); - __set_bit(BTN_0, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - - wacom_setup_cintiq(wacom_wac); - break; - } - return 0; -} - -static const struct wacom_features wacom_features_0x00 = - { "Wacom Penpartner", WACOM_PKGLEN_PENPRTN, 5040, 3780, 255, - 0, PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; -static const struct wacom_features wacom_features_0x10 = - { "Wacom Graphire", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x11 = - { "Wacom Graphire2 4x5", WACOM_PKGLEN_GRAPHIRE, 10206, 7422, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x12 = - { "Wacom Graphire2 5x7", WACOM_PKGLEN_GRAPHIRE, 13918, 10206, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x13 = - { "Wacom Graphire3", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x14 = - { "Wacom Graphire3 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x15 = - { "Wacom Graphire4 4x5", WACOM_PKGLEN_GRAPHIRE, 10208, 7424, 511, - 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x16 = - { "Wacom Graphire4 6x8", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, - 63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x17 = - { "Wacom BambooFun 4x5", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, - 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x18 = - { "Wacom BambooFun 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 511, - 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x19 = - { "Wacom Bamboo1 Medium", WACOM_PKGLEN_GRAPHIRE, 16704, 12064, 511, - 63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES }; -static const struct wacom_features wacom_features_0x60 = - { "Wacom Volito", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; -static const struct wacom_features wacom_features_0x61 = - { "Wacom PenStation2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 255, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; -static const struct wacom_features wacom_features_0x62 = - { "Wacom Volito2 4x5", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; -static const struct wacom_features wacom_features_0x63 = - { "Wacom Volito2 2x3", WACOM_PKGLEN_GRAPHIRE, 3248, 2320, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; -static const struct wacom_features wacom_features_0x64 = - { "Wacom PenPartner2", WACOM_PKGLEN_GRAPHIRE, 3250, 2320, 511, - 63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES }; -static const struct wacom_features wacom_features_0x65 = - { "Wacom Bamboo", WACOM_PKGLEN_BBFUN, 14760, 9225, 511, - 63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x69 = - { "Wacom Bamboo1", WACOM_PKGLEN_GRAPHIRE, 5104, 3712, 511, - 63, GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES }; -static const struct wacom_features wacom_features_0x6A = - { "Wacom Bamboo1 4x6", WACOM_PKGLEN_GRAPHIRE, 14760, 9225, 1023, - 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x6B = - { "Wacom Bamboo1 5x8", WACOM_PKGLEN_GRAPHIRE, 21648, 13530, 1023, - 63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x20 = - { "Wacom Intuos 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x21 = - { "Wacom Intuos 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x22 = - { "Wacom Intuos 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x23 = - { "Wacom Intuos 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x24 = - { "Wacom Intuos 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x30 = - { "Wacom PL400", WACOM_PKGLEN_GRAPHIRE, 5408, 4056, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x31 = - { "Wacom PL500", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x32 = - { "Wacom PL600", WACOM_PKGLEN_GRAPHIRE, 6126, 4604, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x33 = - { "Wacom PL600SX", WACOM_PKGLEN_GRAPHIRE, 6260, 5016, 255, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x34 = - { "Wacom PL550", WACOM_PKGLEN_GRAPHIRE, 6144, 4608, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x35 = - { "Wacom PL800", WACOM_PKGLEN_GRAPHIRE, 7220, 5780, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x37 = - { "Wacom PL700", WACOM_PKGLEN_GRAPHIRE, 6758, 5406, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x38 = - { "Wacom PL510", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x39 = - { "Wacom DTU710", WACOM_PKGLEN_GRAPHIRE, 34080, 27660, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0xC4 = - { "Wacom DTF521", WACOM_PKGLEN_GRAPHIRE, 6282, 4762, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0xC0 = - { "Wacom DTF720", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0xC2 = - { "Wacom DTF720a", WACOM_PKGLEN_GRAPHIRE, 6858, 5506, 511, - 0, PL, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x03 = - { "Wacom Cintiq Partner", WACOM_PKGLEN_GRAPHIRE, 20480, 15360, 511, - 0, PTU, WACOM_PL_RES, WACOM_PL_RES }; -static const struct wacom_features wacom_features_0x41 = - { "Wacom Intuos2 4x5", WACOM_PKGLEN_INTUOS, 12700, 10600, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x42 = - { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x43 = - { "Wacom Intuos2 9x12", WACOM_PKGLEN_INTUOS, 30480, 24060, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x44 = - { "Wacom Intuos2 12x12", WACOM_PKGLEN_INTUOS, 30480, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x45 = - { "Wacom Intuos2 12x18", WACOM_PKGLEN_INTUOS, 45720, 31680, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xB0 = - { "Wacom Intuos3 4x5", WACOM_PKGLEN_INTUOS, 25400, 20320, 1023, - 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB1 = - { "Wacom Intuos3 6x8", WACOM_PKGLEN_INTUOS, 40640, 30480, 1023, - 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB2 = - { "Wacom Intuos3 9x12", WACOM_PKGLEN_INTUOS, 60960, 45720, 1023, - 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB3 = - { "Wacom Intuos3 12x12", WACOM_PKGLEN_INTUOS, 60960, 60960, 1023, - 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB4 = - { "Wacom Intuos3 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 1023, - 63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB5 = - { "Wacom Intuos3 6x11", WACOM_PKGLEN_INTUOS, 54204, 31750, 1023, - 63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB7 = - { "Wacom Intuos3 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 1023, - 63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB8 = - { "Wacom Intuos4 4x6", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xB9 = - { "Wacom Intuos4 6x9", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xBA = - { "Wacom Intuos4 8x13", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, - 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xBB = - { "Wacom Intuos4 12x19", WACOM_PKGLEN_INTUOS, 97536, 60960, 2047, - 63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xBC = - { "Wacom Intuos4 WL", WACOM_PKGLEN_INTUOS, 40640, 25400, 2047, - 63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0x26 = - { "Wacom Intuos5 touch S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x27 = - { "Wacom Intuos5 touch M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x28 = - { "Wacom Intuos5 touch L", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, - 63, INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x29 = - { "Wacom Intuos5 S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0x2A = - { "Wacom Intuos5 M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0x314 = - { "Wacom Intuos Pro S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047, - 63, INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x315 = - { "Wacom Intuos Pro M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047, - 63, INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x317 = - { "Wacom Intuos Pro L", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047, - 63, INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0xF4 = - { "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104280, 65400, 2047, - 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; -static const struct wacom_features wacom_features_0xF8 = - { "Wacom Cintiq 24HD touch", WACOM_PKGLEN_INTUOS, 104280, 65400, 2047, /* Pen */ - 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 }; -static const struct wacom_features wacom_features_0xF6 = - { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */ - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10 }; -static const struct wacom_features wacom_features_0x3F = - { "Wacom Cintiq 21UX", WACOM_PKGLEN_INTUOS, 87200, 65600, 1023, - 63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xC5 = - { "Wacom Cintiq 20WSX", WACOM_PKGLEN_INTUOS, 86680, 54180, 1023, - 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0xC6 = - { "Wacom Cintiq 12WX", WACOM_PKGLEN_INTUOS, 53020, 33440, 1023, - 63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; -static const struct wacom_features wacom_features_0x304 = - { "Wacom Cintiq 13HD", WACOM_PKGLEN_INTUOS, 59352, 33648, 1023, - 63, WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; -static const struct wacom_features wacom_features_0xC7 = - { "Wacom DTU1931", WACOM_PKGLEN_GRAPHIRE, 37832, 30305, 511, - 0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xCE = - { "Wacom DTU2231", WACOM_PKGLEN_GRAPHIRE, 47864, 27011, 511, - 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xF0 = - { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, - 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xFB = - { "Wacom DTU1031", WACOM_PKGLEN_DTUS, 22096, 13960, 511, - 0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x57 = - { "Wacom DTK2241", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; -static const struct wacom_features wacom_features_0x59 = /* Pen */ - { "Wacom DTH2242", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D }; -static const struct wacom_features wacom_features_0x5D = /* Touch */ - { "Wacom DTH2242", .type = WACOM_24HDT, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10 }; -static const struct wacom_features wacom_features_0xCC = - { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87000, 65400, 2047, - 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; -static const struct wacom_features wacom_features_0xFA = - { "Wacom Cintiq 22HD", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 }; -static const struct wacom_features wacom_features_0x5B = - { "Wacom Cintiq 22HDT", WACOM_PKGLEN_INTUOS, 95640, 54060, 2047, - 63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e }; -static const struct wacom_features wacom_features_0x5E = - { "Wacom Cintiq 22HDT", .type = WACOM_24HDT, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5b, .touch_max = 10 }; -static const struct wacom_features wacom_features_0x90 = - { "Wacom ISDv4 90", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x93 = - { "Wacom ISDv4 93", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x97 = - { "Wacom ISDv4 97", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 511, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x9A = - { "Wacom ISDv4 9A", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x9F = - { "Wacom ISDv4 9F", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xE2 = - { "Wacom ISDv4 E2", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, - 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xE3 = - { "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, - 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xE5 = - { "Wacom ISDv4 E5", WACOM_PKGLEN_MTOUCH, 26202, 16325, 255, - 0, MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xE6 = - { "Wacom ISDv4 E6", WACOM_PKGLEN_TPC2FG, 27760, 15694, 255, - 0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xEC = - { "Wacom ISDv4 EC", WACOM_PKGLEN_GRAPHIRE, 25710, 14500, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xED = - { "Wacom ISDv4 ED", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xEF = - { "Wacom ISDv4 EF", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x100 = - { "Wacom ISDv4 100", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x101 = - { "Wacom ISDv4 101", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x10D = - { "Wacom ISDv4 10D", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x10E = - { "Wacom ISDv4 10E", WACOM_PKGLEN_MTTPC, 27760, 15694, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x10F = - { "Wacom ISDv4 10F", WACOM_PKGLEN_MTTPC, 27760, 15694, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x116 = - { "Wacom ISDv4 116", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255, - 0, TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x4001 = - { "Wacom ISDv4 4001", WACOM_PKGLEN_MTTPC, 26202, 16325, 255, - 0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x4004 = - { "Wacom ISDv4 4004", WACOM_PKGLEN_MTTPC, 11060, 6220, 255, - 0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x5000 = - { "Wacom ISDv4 5000", WACOM_PKGLEN_MTTPC, 27848, 15752, 1023, - 0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x5002 = - { "Wacom ISDv4 5002", WACOM_PKGLEN_MTTPC, 29576, 16724, 1023, - 0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x47 = - { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, - 31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x84 = - { "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0, - 0, WIRELESS, 0, 0, .touch_max = 16 }; -static const struct wacom_features wacom_features_0xD0 = - { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xD1 = - { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xD2 = - { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xD3 = - { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xD4 = - { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xD5 = - { "Wacom Bamboo Pen 6x8", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xD6 = - { "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xD7 = - { "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xD8 = - { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xDA = - { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xDB = - { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 2 }; -static const struct wacom_features wacom_features_0xDD = - { "Wacom Bamboo Connect", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0xDE = - { "Wacom Bamboo 16FG 4x5", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0xDF = - { "Wacom Bamboo 16FG 6x8", WACOM_PKGLEN_BBPEN, 21648, 13700, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x300 = - { "Wacom Bamboo One S", WACOM_PKGLEN_BBPEN, 14720, 9225, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x301 = - { "Wacom Bamboo One M", WACOM_PKGLEN_BBPEN, 21648, 13530, 1023, - 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x302 = - { "Wacom Intuos PT S", WACOM_PKGLEN_BBPEN, 15200, 9500, 1023, - 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x303 = - { "Wacom Intuos PT M", WACOM_PKGLEN_BBPEN, 21600, 13500, 1023, - 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, - .touch_max = 16 }; -static const struct wacom_features wacom_features_0x30E = - { "Wacom Intuos S", WACOM_PKGLEN_BBPEN, 15200, 9500, 1023, - 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x6004 = - { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, - 0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; -static const struct wacom_features wacom_features_0x0307 = - { "Wacom ISDv5 307", WACOM_PKGLEN_INTUOS, 59352, 33648, 2047, - 63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200, - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 }; -static const struct wacom_features wacom_features_0x0309 = - { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */ - .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10 }; - -#define USB_DEVICE_WACOM(prod) \ - USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod - -#define USB_DEVICE_DETAILED(prod, class, sub, proto) \ - USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_WACOM, prod, class, \ - sub, proto), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod - -#define USB_DEVICE_LENOVO(prod) \ - USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ - .driver_info = (kernel_ulong_t)&wacom_features_##prod - -const struct usb_device_id wacom_ids[] = { - { USB_DEVICE_WACOM(0x00) }, - { USB_DEVICE_WACOM(0x10) }, - { USB_DEVICE_WACOM(0x11) }, - { USB_DEVICE_WACOM(0x12) }, - { USB_DEVICE_WACOM(0x13) }, - { USB_DEVICE_WACOM(0x14) }, - { USB_DEVICE_WACOM(0x15) }, - { USB_DEVICE_WACOM(0x16) }, - { USB_DEVICE_WACOM(0x17) }, - { USB_DEVICE_WACOM(0x18) }, - { USB_DEVICE_WACOM(0x19) }, - { USB_DEVICE_WACOM(0x60) }, - { USB_DEVICE_WACOM(0x61) }, - { USB_DEVICE_WACOM(0x62) }, - { USB_DEVICE_WACOM(0x63) }, - { USB_DEVICE_WACOM(0x64) }, - { USB_DEVICE_WACOM(0x65) }, - { USB_DEVICE_WACOM(0x69) }, - { USB_DEVICE_WACOM(0x6A) }, - { USB_DEVICE_WACOM(0x6B) }, - { USB_DEVICE_WACOM(0x20) }, - { USB_DEVICE_WACOM(0x21) }, - { USB_DEVICE_WACOM(0x22) }, - { USB_DEVICE_WACOM(0x23) }, - { USB_DEVICE_WACOM(0x24) }, - { USB_DEVICE_WACOM(0x30) }, - { USB_DEVICE_WACOM(0x31) }, - { USB_DEVICE_WACOM(0x32) }, - { USB_DEVICE_WACOM(0x33) }, - { USB_DEVICE_WACOM(0x34) }, - { USB_DEVICE_WACOM(0x35) }, - { USB_DEVICE_WACOM(0x37) }, - { USB_DEVICE_WACOM(0x38) }, - { USB_DEVICE_WACOM(0x39) }, - { USB_DEVICE_WACOM(0xC4) }, - { USB_DEVICE_WACOM(0xC0) }, - { USB_DEVICE_WACOM(0xC2) }, - { USB_DEVICE_WACOM(0x03) }, - { USB_DEVICE_WACOM(0x41) }, - { USB_DEVICE_WACOM(0x42) }, - { USB_DEVICE_WACOM(0x43) }, - { USB_DEVICE_WACOM(0x44) }, - { USB_DEVICE_WACOM(0x45) }, - { USB_DEVICE_WACOM(0x57) }, - { USB_DEVICE_WACOM(0x59) }, - { USB_DEVICE_DETAILED(0x5D, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_WACOM(0x5B) }, - { USB_DEVICE_DETAILED(0x5E, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_WACOM(0xB0) }, - { USB_DEVICE_WACOM(0xB1) }, - { USB_DEVICE_WACOM(0xB2) }, - { USB_DEVICE_WACOM(0xB3) }, - { USB_DEVICE_WACOM(0xB4) }, - { USB_DEVICE_WACOM(0xB5) }, - { USB_DEVICE_WACOM(0xB7) }, - { USB_DEVICE_WACOM(0xB8) }, - { USB_DEVICE_WACOM(0xB9) }, - { USB_DEVICE_WACOM(0xBA) }, - { USB_DEVICE_WACOM(0xBB) }, - { USB_DEVICE_WACOM(0xBC) }, - { USB_DEVICE_WACOM(0x26) }, - { USB_DEVICE_WACOM(0x27) }, - { USB_DEVICE_WACOM(0x28) }, - { USB_DEVICE_WACOM(0x29) }, - { USB_DEVICE_WACOM(0x2A) }, - { USB_DEVICE_WACOM(0x3F) }, - { USB_DEVICE_WACOM(0xC5) }, - { USB_DEVICE_WACOM(0xC6) }, - { USB_DEVICE_WACOM(0xC7) }, - /* - * DTU-2231 has two interfaces on the same configuration, - * only one is used. - */ - { USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID, - USB_INTERFACE_SUBCLASS_BOOT, - USB_INTERFACE_PROTOCOL_MOUSE) }, - { USB_DEVICE_WACOM(0x84) }, - { USB_DEVICE_WACOM(0xD0) }, - { USB_DEVICE_WACOM(0xD1) }, - { USB_DEVICE_WACOM(0xD2) }, - { USB_DEVICE_WACOM(0xD3) }, - { USB_DEVICE_WACOM(0xD4) }, - { USB_DEVICE_WACOM(0xD5) }, - { USB_DEVICE_WACOM(0xD6) }, - { USB_DEVICE_WACOM(0xD7) }, - { USB_DEVICE_WACOM(0xD8) }, - { USB_DEVICE_WACOM(0xDA) }, - { USB_DEVICE_WACOM(0xDB) }, - { USB_DEVICE_WACOM(0xDD) }, - { USB_DEVICE_WACOM(0xDE) }, - { USB_DEVICE_WACOM(0xDF) }, - { USB_DEVICE_WACOM(0xF0) }, - { USB_DEVICE_WACOM(0xCC) }, - { USB_DEVICE_WACOM(0x90) }, - { USB_DEVICE_WACOM(0x93) }, - { USB_DEVICE_WACOM(0x97) }, - { USB_DEVICE_WACOM(0x9A) }, - { USB_DEVICE_WACOM(0x9F) }, - { USB_DEVICE_WACOM(0xE2) }, - { USB_DEVICE_WACOM(0xE3) }, - { USB_DEVICE_WACOM(0xE5) }, - { USB_DEVICE_WACOM(0xE6) }, - { USB_DEVICE_WACOM(0xEC) }, - { USB_DEVICE_WACOM(0xED) }, - { USB_DEVICE_WACOM(0xEF) }, - { USB_DEVICE_WACOM(0x100) }, - { USB_DEVICE_WACOM(0x101) }, - { USB_DEVICE_WACOM(0x10D) }, - { USB_DEVICE_WACOM(0x10E) }, - { USB_DEVICE_WACOM(0x10F) }, - { USB_DEVICE_WACOM(0x116) }, - { USB_DEVICE_WACOM(0x300) }, - { USB_DEVICE_WACOM(0x301) }, - { USB_DEVICE_DETAILED(0x302, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x303, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x30E, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_WACOM(0x304) }, - { USB_DEVICE_DETAILED(0x314, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x315, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_DETAILED(0x317, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_WACOM(0x4001) }, - { USB_DEVICE_WACOM(0x4004) }, - { USB_DEVICE_WACOM(0x5000) }, - { USB_DEVICE_WACOM(0x5002) }, - { USB_DEVICE_WACOM(0x47) }, - { USB_DEVICE_WACOM(0xF4) }, - { USB_DEVICE_WACOM(0xF8) }, - { USB_DEVICE_DETAILED(0xF6, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_WACOM(0xFA) }, - { USB_DEVICE_WACOM(0xFB) }, - { USB_DEVICE_WACOM(0x0307) }, - { USB_DEVICE_DETAILED(0x0309, USB_CLASS_HID, 0, 0) }, - { USB_DEVICE_LENOVO(0x6004) }, - { } -}; -MODULE_DEVICE_TABLE(usb, wacom_ids); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h deleted file mode 100644 index b2c9a9c1b551..000000000000 --- a/drivers/input/tablet/wacom_wac.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * drivers/input/tablet/wacom_wac.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#ifndef WACOM_WAC_H -#define WACOM_WAC_H - -#include <linux/types.h> - -/* maximum packet length for USB devices */ -#define WACOM_PKGLEN_MAX 68 - -#define WACOM_NAME_MAX 64 - -/* packet length for individual models */ -#define WACOM_PKGLEN_PENPRTN 7 -#define WACOM_PKGLEN_GRAPHIRE 8 -#define WACOM_PKGLEN_BBFUN 9 -#define WACOM_PKGLEN_INTUOS 10 -#define WACOM_PKGLEN_TPC1FG 5 -#define WACOM_PKGLEN_TPC1FG_B 10 -#define WACOM_PKGLEN_TPC2FG 14 -#define WACOM_PKGLEN_BBTOUCH 20 -#define WACOM_PKGLEN_BBTOUCH3 64 -#define WACOM_PKGLEN_BBPEN 10 -#define WACOM_PKGLEN_WIRELESS 32 -#define WACOM_PKGLEN_MTOUCH 62 -#define WACOM_PKGLEN_MTTPC 40 -#define WACOM_PKGLEN_DTUS 68 -#define WACOM_PKGLEN_PENABLED 8 - -/* wacom data size per MT contact */ -#define WACOM_BYTES_PER_MT_PACKET 11 -#define WACOM_BYTES_PER_24HDT_PACKET 14 - -/* device IDs */ -#define STYLUS_DEVICE_ID 0x02 -#define TOUCH_DEVICE_ID 0x03 -#define CURSOR_DEVICE_ID 0x06 -#define ERASER_DEVICE_ID 0x0A -#define PAD_DEVICE_ID 0x0F - -/* wacom data packet report IDs */ -#define WACOM_REPORT_PENABLED 2 -#define WACOM_REPORT_INTUOSREAD 5 -#define WACOM_REPORT_INTUOSWRITE 6 -#define WACOM_REPORT_INTUOSPAD 12 -#define WACOM_REPORT_INTUOS5PAD 3 -#define WACOM_REPORT_DTUSPAD 21 -#define WACOM_REPORT_TPC1FG 6 -#define WACOM_REPORT_TPC2FG 13 -#define WACOM_REPORT_TPCMT 13 -#define WACOM_REPORT_TPCMT2 3 -#define WACOM_REPORT_TPCHID 15 -#define WACOM_REPORT_TPCST 16 -#define WACOM_REPORT_DTUS 17 -#define WACOM_REPORT_TPC1FGE 18 -#define WACOM_REPORT_24HDT 1 -#define WACOM_REPORT_WL 128 -#define WACOM_REPORT_USB 192 - -/* device quirks */ -#define WACOM_QUIRK_MULTI_INPUT 0x0001 -#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 -#define WACOM_QUIRK_NO_INPUT 0x0004 -#define WACOM_QUIRK_MONITOR 0x0008 - -enum { - PENPARTNER = 0, - GRAPHIRE, - WACOM_G4, - PTU, - PL, - DTU, - DTUS, - INTUOS, - INTUOS3S, - INTUOS3, - INTUOS3L, - INTUOS4S, - INTUOS4, - INTUOS4L, - INTUOS5S, - INTUOS5, - INTUOS5L, - INTUOSPS, - INTUOSPM, - INTUOSPL, - INTUOSHT, - WACOM_21UX2, - WACOM_22HD, - DTK, - WACOM_24HD, - CINTIQ_HYBRID, - CINTIQ, - WACOM_BEE, - WACOM_13HD, - WACOM_MO, - WIRELESS, - BAMBOO_PT, - WACOM_24HDT, - TABLETPC, /* add new TPC below */ - TABLETPCE, - TABLETPC2FG, - MTSCREEN, - MTTPC, - MTTPC_B, - MAX_TYPE -}; - -struct wacom_features { - const char *name; - int pktlen; - int x_max; - int y_max; - int pressure_max; - int distance_max; - int type; - int x_resolution; - int y_resolution; - int x_min; - int y_min; - int device_type; - int x_phy; - int y_phy; - unsigned char unit; - unsigned char unitExpo; - int x_fuzz; - int y_fuzz; - int pressure_fuzz; - int distance_fuzz; - unsigned quirks; - unsigned touch_max; - int oVid; - int oPid; -}; - -struct wacom_shared { - bool stylus_in_proximity; - bool touch_down; - /* for wireless device to access USB interfaces */ - unsigned touch_max; - int type; - struct input_dev *touch_input; -}; - -struct wacom_wac { - char name[WACOM_NAME_MAX]; - unsigned char *data; - int tool[2]; - int id[2]; - __u32 serial[2]; - struct wacom_features features; - struct wacom_shared *shared; - struct input_dev *input; - int pid; - int battery_capacity; - int num_contacts_left; -}; - -#endif diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index a23a94bb4bcb..e1d8003d01f8 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -90,6 +90,18 @@ config TOUCHSCREEN_AD7879_SPI To compile this driver as a module, choose M here: the module will be called ad7879-spi. +config TOUCHSCREEN_AR1021_I2C + tristate "Microchip AR1021 i2c touchscreen" + depends on I2C && OF + help + Say Y here if you have the Microchip AR1021 touchscreen controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ar1021_i2c. + config TOUCHSCREEN_ATMEL_MXT tristate "Atmel mXT I2C Touchscreen" depends on I2C @@ -471,6 +483,18 @@ config TOUCHSCREEN_HP7XX To compile this driver as a module, choose M here: the module will be called jornada720_ts. +config TOUCHSCREEN_IPAQ_MICRO + tristate "HP iPAQ Atmel Micro ASIC touchscreen" + depends on MFD_IPAQ_MICRO + help + Say Y here to enable support for the touchscreen attached to + the Atmel Micro peripheral controller on iPAQ h3100/h3600/h3700 + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ipaq-micro-ts. + config TOUCHSCREEN_HTCPEN tristate "HTC Shift X9500 touchscreen" depends on ISA diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 126479d8c29a..090e61cc9171 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o +obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o @@ -46,6 +47,7 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o +obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index fce590677b7b..1eb9d3c20886 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -470,14 +470,10 @@ static int ad7879_gpio_add(struct ad7879 *ts, static void ad7879_gpio_remove(struct ad7879 *ts) { const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev); - int ret; - if (pdata->gpio_export) { - ret = gpiochip_remove(&ts->gc); - if (ret) - dev_err(ts->dev, "failed to remove gpio %d\n", - ts->gc.base); - } + if (pdata->gpio_export) + gpiochip_remove(&ts->gc); + } #else static inline int ad7879_gpio_add(struct ad7879 *ts, diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index da201b8e37dc..e57ba52bf484 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1302,8 +1302,10 @@ static int ads7846_probe(struct spi_device *spi) pdata = dev_get_platdata(&spi->dev); if (!pdata) { pdata = ads7846_probe_dt(&spi->dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); + if (IS_ERR(pdata)) { + err = PTR_ERR(pdata); + goto err_free_mem; + } } ts->model = pdata->model ? : 7846; diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c new file mode 100644 index 000000000000..ba30578e296e --- /dev/null +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -0,0 +1,181 @@ +/* + * Microchip AR1021 driver for I2C + * + * Author: Christian Gmeiner <christian.gmeiner@gmail.com> + * + * License: GPLv2 as published by the FSF. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/of.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/interrupt.h> + +#define AR1021_TOCUH_PKG_SIZE 5 + +#define AR1021_MAX_X 4095 +#define AR1021_MAX_Y 4095 + +struct ar1021_i2c { + struct i2c_client *client; + struct input_dev *input; + u8 data[AR1021_TOCUH_PKG_SIZE]; +}; + +static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id) +{ + struct ar1021_i2c *ar1021 = dev_id; + struct input_dev *input = ar1021->input; + u8 *data = ar1021->data; + unsigned int x, y, button; + int retval; + + retval = i2c_master_recv(ar1021->client, + ar1021->data, sizeof(ar1021->data)); + if (retval != sizeof(ar1021->data)) + goto out; + + /* sync bit set ? */ + if ((data[0] & 0x80) == 0) + goto out; + + button = data[0] & BIT(0); + x = ((data[2] & 0x1f) << 7) | (data[1] & 0x7f); + y = ((data[4] & 0x1f) << 7) | (data[3] & 0x7f); + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_key(input, BTN_TOUCH, button); + input_sync(input); + +out: + return IRQ_HANDLED; +} + +static int ar1021_i2c_open(struct input_dev *dev) +{ + struct ar1021_i2c *ar1021 = input_get_drvdata(dev); + struct i2c_client *client = ar1021->client; + + enable_irq(client->irq); + + return 0; +} + +static void ar1021_i2c_close(struct input_dev *dev) +{ + struct ar1021_i2c *ar1021 = input_get_drvdata(dev); + struct i2c_client *client = ar1021->client; + + disable_irq(client->irq); +} + +static int ar1021_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ar1021_i2c *ar1021; + struct input_dev *input; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c_check_functionality error\n"); + return -ENXIO; + } + + ar1021 = devm_kzalloc(&client->dev, sizeof(*ar1021), GFP_KERNEL); + if (!ar1021) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; + + ar1021->client = client; + ar1021->input = input; + + input->name = "ar1021 I2C Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + input->open = ar1021_i2c_open; + input->close = ar1021_i2c_close; + + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_abs_params(input, ABS_X, 0, AR1021_MAX_X, 0, 0); + input_set_abs_params(input, ABS_Y, 0, AR1021_MAX_Y, 0, 0); + + input_set_drvdata(input, ar1021); + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, ar1021_i2c_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "ar1021_i2c", ar1021); + if (error) { + dev_err(&client->dev, + "Failed to enable IRQ, error: %d\n", error); + return error; + } + + /* Disable the IRQ, we'll enable it in ar1021_i2c_open() */ + disable_irq(client->irq); + + error = input_register_device(ar1021->input); + if (error) { + dev_err(&client->dev, + "Failed to register input device, error: %d\n", error); + return error; + } + + i2c_set_clientdata(client, ar1021); + return 0; +} + +static int __maybe_unused ar1021_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + disable_irq(client->irq); + + return 0; +} + +static int __maybe_unused ar1021_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + enable_irq(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ar1021_i2c_pm, ar1021_i2c_suspend, ar1021_i2c_resume); + +static const struct i2c_device_id ar1021_i2c_id[] = { + { "MICROCHIP_AR1021_I2C", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id); + +static struct of_device_id ar1021_i2c_of_match[] = { + { .compatible = "microchip,ar1021-i2c", }, + { } +}; +MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match); + +static struct i2c_driver ar1021_i2c_driver = { + .driver = { + .name = "ar1021_i2c", + .owner = THIS_MODULE, + .pm = &ar1021_i2c_pm, + .of_match_table = ar1021_i2c_of_match, + }, + + .probe = ar1021_i2c_probe, + .id_table = ar1021_i2c_id, +}; +module_i2c_driver(ar1021_i2c_driver); + +MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>"); +MODULE_DESCRIPTION("Microchip AR1021 I2C Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6e0b4a2120d3..aaacf8bfa61f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2,6 +2,7 @@ * Atmel maXTouch Touchscreen driver * * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2014 Atmel Corporation * Copyright (C) 2012 Google, Inc. * * Author: Joonyoung Shim <jy0922.shim@samsung.com> @@ -22,6 +23,7 @@ #include <linux/i2c/atmel_mxt_ts.h> #include <linux/input/mt.h> #include <linux/interrupt.h> +#include <linux/of.h> #include <linux/slab.h> /* Version */ @@ -29,8 +31,10 @@ #define MXT_VER_21 21 #define MXT_VER_22 22 -/* Firmware */ +/* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" +#define MXT_CFG_NAME "maxtouch.cfg" +#define MXT_CFG_MAGIC "OBP_RAW V1" /* Registers */ #define MXT_INFO 0x00 @@ -44,6 +48,8 @@ #define MXT_OBJECT_START 0x07 #define MXT_OBJECT_SIZE 6 +#define MXT_INFO_CHECKSUM_SIZE 3 +#define MXT_MAX_BLOCK_WRITE 256 /* Object types */ #define MXT_DEBUG_DIAGNOSTIC_T37 37 @@ -74,6 +80,9 @@ #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG 0xff + /* MXT_GEN_COMMAND_T6 field */ #define MXT_COMMAND_RESET 0 #define MXT_COMMAND_BACKUPNV 1 @@ -83,11 +92,20 @@ /* Define for T6 status byte */ #define MXT_T6_STATUS_RESET (1 << 7) +#define MXT_T6_STATUS_OFL (1 << 6) +#define MXT_T6_STATUS_SIGERR (1 << 5) +#define MXT_T6_STATUS_CAL (1 << 4) +#define MXT_T6_STATUS_CFGERR (1 << 3) +#define MXT_T6_STATUS_COMSERR (1 << 2) /* MXT_GEN_POWER_T7 field */ -#define MXT_POWER_IDLEACQINT 0 -#define MXT_POWER_ACTVACQINT 1 -#define MXT_POWER_ACTV2IDLETO 2 +struct t7_config { + u8 idle; + u8 active; +} __packed; + +#define MXT_POWER_CFG_RUN 0 +#define MXT_POWER_CFG_DEEPSLEEP 1 /* MXT_GEN_ACQUIRE_T8 field */ #define MXT_ACQUIRE_CHRGTIME 0 @@ -99,7 +117,6 @@ #define MXT_ACQUIRE_ATCHCALSTHR 7 /* MXT_TOUCH_MULTI_T9 field */ -#define MXT_TOUCH_CTRL 0 #define MXT_T9_ORIENT 9 #define MXT_T9_RANGE 18 @@ -217,11 +234,6 @@ struct mxt_object { u8 num_report_ids; } __packed; -struct mxt_message { - u8 reportid; - u8 message[7]; -}; - /* Each client has this additional data */ struct mxt_data { struct i2c_client *client; @@ -234,15 +246,28 @@ struct mxt_data { unsigned int max_x; unsigned int max_y; bool in_bootloader; + u16 mem_size; + u8 max_reportid; u32 config_crc; + u32 info_crc; u8 bootloader_addr; + u8 *msg_buf; + u8 t6_status; + bool update_input; + u8 last_message_count; + u8 num_touchids; + struct t7_config t7_cfg; /* Cached parameters from object table */ + u16 T5_address; + u8 T5_msg_size; u8 T6_reportid; u16 T6_address; + u16 T7_address; u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; + u16 T44_address; /* for fw update in bootloader */ struct completion bl_completion; @@ -297,42 +322,10 @@ static bool mxt_object_readable(unsigned int type) } } -static bool mxt_object_writable(unsigned int type) -{ - switch (type) { - case MXT_GEN_COMMAND_T6: - case MXT_GEN_POWER_T7: - case MXT_GEN_ACQUIRE_T8: - case MXT_TOUCH_MULTI_T9: - case MXT_TOUCH_KEYARRAY_T15: - case MXT_TOUCH_PROXIMITY_T23: - case MXT_TOUCH_PROXKEY_T52: - case MXT_PROCI_GRIPFACE_T20: - case MXT_PROCG_NOISE_T22: - case MXT_PROCI_ONETOUCH_T24: - case MXT_PROCI_TWOTOUCH_T27: - case MXT_PROCI_GRIP_T40: - case MXT_PROCI_PALM_T41: - case MXT_PROCI_TOUCHSUPPRESSION_T42: - case MXT_PROCI_STYLUS_T47: - case MXT_PROCG_NOISESUPPRESSION_T48: - case MXT_SPT_COMMSCONFIG_T18: - case MXT_SPT_GPIOPWM_T19: - case MXT_SPT_SELFTEST_T25: - case MXT_SPT_CTECONFIG_T28: - case MXT_SPT_DIGITIZER_T43: - case MXT_SPT_CTECONFIG_T46: - return true; - default: - return false; - } -} - -static void mxt_dump_message(struct device *dev, - struct mxt_message *message) +static void mxt_dump_message(struct mxt_data *data, u8 *message) { - dev_dbg(dev, "reportid: %u\tmessage: %*ph\n", - message->reportid, 7, message->message); + dev_dbg(&data->client->dev, "message: %*ph\n", + data->T5_msg_size, message); } static int mxt_wait_for_completion(struct mxt_data *data, @@ -366,7 +359,6 @@ static int mxt_bootloader_read(struct mxt_data *data, msg.buf = val; ret = i2c_transfer(data->client->adapter, &msg, 1); - if (ret == 1) { ret = 0; } else { @@ -401,7 +393,7 @@ static int mxt_bootloader_write(struct mxt_data *data, return ret; } -static int mxt_lookup_bootloader_address(struct mxt_data *data) +static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) { u8 appmode = data->client->addr; u8 bootloader; @@ -409,12 +401,19 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) switch (appmode) { case 0x4a: case 0x4b: + /* Chips after 1664S use different scheme */ + if (retry || data->info.family_id >= 0xa2) { + bootloader = appmode - 0x24; + break; + } + /* Fall through for normal case */ case 0x4c: case 0x4d: case 0x5a: case 0x5b: bootloader = appmode - 0x26; break; + default: dev_err(&data->client->dev, "Appmode i2c address 0x%02x not found\n", @@ -426,6 +425,30 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data) return 0; } +static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) +{ + struct device *dev = &data->client->dev; + int error; + u8 val; + bool crc_failure; + + error = mxt_lookup_bootloader_address(data, alt_address); + if (error) + return error; + + error = mxt_bootloader_read(data, &val, 1); + if (error) + return error; + + /* Check app crc fail mode */ + crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + + dev_err(dev, "Detected bootloader, status:%02X%s\n", + val, crc_failure ? ", APP_CRC_FAIL" : ""); + + return 0; +} + static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) { struct device *dev = &data->client->dev; @@ -447,14 +470,15 @@ static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) } } -static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, + bool wait) { struct device *dev = &data->client->dev; u8 val; int ret; recheck: - if (state != MXT_WAITING_BOOTLOAD_CMD) { + if (wait) { /* * In application update mode, the interrupt * line signals state transitions. We must wait for the @@ -485,6 +509,7 @@ recheck: switch (state) { case MXT_WAITING_BOOTLOAD_CMD: case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: val &= ~MXT_BOOT_STATUS_MASK; break; case MXT_FRAME_CRC_PASS: @@ -508,13 +533,18 @@ recheck: return 0; } -static int mxt_unlock_bootloader(struct mxt_data *data) +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) { int ret; u8 buf[2]; - buf[0] = MXT_UNLOCK_CMD_LSB; - buf[1] = MXT_UNLOCK_CMD_MSB; + if (unlock) { + buf[0] = MXT_UNLOCK_CMD_LSB; + buf[1] = MXT_UNLOCK_CMD_MSB; + } else { + buf[0] = 0x01; + buf[1] = 0x01; + } ret = mxt_bootloader_write(data, buf, 2); if (ret) @@ -605,40 +635,44 @@ mxt_get_object(struct mxt_data *data, u8 type) return object; } - dev_err(&data->client->dev, "Invalid object type T%u\n", type); + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); return NULL; } -static int mxt_read_message(struct mxt_data *data, - struct mxt_message *message) +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) { - struct mxt_object *object; - u16 reg; - - object = mxt_get_object(data, MXT_GEN_MESSAGE_T5); - if (!object) - return -EINVAL; + struct device *dev = &data->client->dev; + u8 status = msg[1]; + u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); - reg = object->start_address; - return __mxt_read_reg(data->client, reg, - sizeof(struct mxt_message), message); -} + complete(&data->crc_completion); -static int mxt_write_object(struct mxt_data *data, - u8 type, u8 offset, u8 val) -{ - struct mxt_object *object; - u16 reg; - - object = mxt_get_object(data, type); - if (!object || offset >= mxt_obj_size(object)) - return -EINVAL; + if (crc != data->config_crc) { + data->config_crc = crc; + dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); + } - reg = object->start_address; - return mxt_write_reg(data->client, reg + offset, val); + /* Detect reset */ + if (status & MXT_T6_STATUS_RESET) + complete(&data->reset_completion); + + /* Output debug if status has changed */ + if (status != data->t6_status) + dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", + status, + status == 0 ? " OK" : "", + status & MXT_T6_STATUS_RESET ? " RESET" : "", + status & MXT_T6_STATUS_OFL ? " OFL" : "", + status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "", + status & MXT_T6_STATUS_CAL ? " CAL" : "", + status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "", + status & MXT_T6_STATUS_COMSERR ? " COMSERR" : ""); + + /* Save current status */ + data->t6_status = status; } -static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) +static void mxt_input_button(struct mxt_data *data, u8 *message) { struct input_dev *input = data->input_dev; const struct mxt_platform_data *pdata = data->pdata; @@ -649,30 +683,33 @@ static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) for (i = 0; i < pdata->t19_num_keys; i++) { if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message->message[0] & (1 << i)); + button = !(message[1] & (1 << i)); input_report_key(input, pdata->t19_keymap[i], button); } } -static void mxt_input_sync(struct input_dev *input_dev) +static void mxt_input_sync(struct mxt_data *data) { - input_mt_report_pointer_emulation(input_dev, false); - input_sync(input_dev); + input_mt_report_pointer_emulation(data->input_dev, + data->pdata->t19_num_keys); + input_sync(data->input_dev); } -static void mxt_input_touchevent(struct mxt_data *data, - struct mxt_message *message, int id) +static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) { struct device *dev = &data->client->dev; - u8 status = message->message[0]; struct input_dev *input_dev = data->input_dev; + int id; + u8 status; int x; int y; int area; int amplitude; - x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf); - y = (message->message[2] << 4) | ((message->message[3] & 0xf)); + id = message[0] - data->T9_reportid_min; + status = message[1]; + x = (message[2] << 4) | ((message[4] >> 4) & 0xf); + y = (message[3] << 4) | ((message[4] & 0xf)); /* Handle 10/12 bit switching */ if (data->max_x < 1024) @@ -680,8 +717,8 @@ static void mxt_input_touchevent(struct mxt_data *data, if (data->max_y < 1024) y >>= 2; - area = message->message[4]; - amplitude = message->message[5]; + area = message[5]; + amplitude = message[6]; dev_dbg(dev, "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", @@ -707,7 +744,7 @@ static void mxt_input_touchevent(struct mxt_data *data, if (status & MXT_T9_RELEASE) { input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); - mxt_input_sync(input_dev); + mxt_input_sync(data); } /* Touch active */ @@ -720,64 +757,184 @@ static void mxt_input_touchevent(struct mxt_data *data, /* Touch no longer active, close out slot */ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); } + + data->update_input = true; +} + +static int mxt_proc_message(struct mxt_data *data, u8 *message) +{ + u8 report_id = message[0]; + + if (report_id == MXT_RPTID_NOMSG) + return 0; + + if (report_id == data->T6_reportid) { + mxt_proc_t6_messages(data, message); + } else if (!data->input_dev) { + /* + * Do not report events if input device + * is not yet registered. + */ + mxt_dump_message(data, message); + } else if (report_id >= data->T9_reportid_min + && report_id <= data->T9_reportid_max) { + mxt_proc_t9_message(data, message); + } else if (report_id == data->T19_reportid) { + mxt_input_button(data, message); + data->update_input = true; + } else { + mxt_dump_message(data, message); + } + + return 1; } -static u16 mxt_extract_T6_csum(const u8 *csum) +static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) { - return csum[0] | (csum[1] << 8) | (csum[2] << 16); + struct device *dev = &data->client->dev; + int ret; + int i; + u8 num_valid = 0; + + /* Safety check for msg_buf */ + if (count > data->max_reportid) + return -EINVAL; + + /* Process remaining messages if necessary */ + ret = __mxt_read_reg(data->client, data->T5_address, + data->T5_msg_size * count, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); + return ret; + } + + for (i = 0; i < count; i++) { + ret = mxt_proc_message(data, + data->msg_buf + data->T5_msg_size * i); + + if (ret == 1) + num_valid++; + } + + /* return number of messages read */ + return num_valid; } -static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) +static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) { - u8 id = msg->reportid; - return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); + struct device *dev = &data->client->dev; + int ret; + u8 count, num_left; + + /* Read T44 and T5 together */ + ret = __mxt_read_reg(data->client, data->T44_address, + data->T5_msg_size + 1, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); + return IRQ_NONE; + } + + count = data->msg_buf[0]; + + if (count == 0) { + /* + * This condition is caused by the CHG line being configured + * in Mode 0. It results in unnecessary I2C operations but it + * is benign. + */ + dev_dbg(dev, "Interrupt triggered but zero messages\n"); + return IRQ_NONE; + } else if (count > data->max_reportid) { + dev_err(dev, "T44 count %d exceeded max report id\n", count); + count = data->max_reportid; + } + + /* Process first message */ + ret = mxt_proc_message(data, data->msg_buf + 1); + if (ret < 0) { + dev_warn(dev, "Unexpected invalid message\n"); + return IRQ_NONE; + } + + num_left = count - 1; + + /* Process remaining messages if necessary */ + if (num_left) { + ret = mxt_read_and_process_messages(data, num_left); + if (ret < 0) + goto end; + else if (ret != num_left) + dev_warn(dev, "Unexpected invalid message\n"); + } + +end: + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } + + return IRQ_HANDLED; } -static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) +static int mxt_process_messages_until_invalid(struct mxt_data *data) { - struct mxt_message message; - const u8 *payload = &message.message[0]; struct device *dev = &data->client->dev; - u8 reportid; - bool update_input = false; - u32 crc; + int count, read; + u8 tries = 2; + count = data->max_reportid; + + /* Read messages until we force an invalid */ do { - if (mxt_read_message(data, &message)) { - dev_err(dev, "Failed to read message\n"); - return IRQ_NONE; - } + read = mxt_read_and_process_messages(data, count); + if (read < count) + return 0; + } while (--tries); - reportid = message.reportid; + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } - if (reportid == data->T6_reportid) { - u8 status = payload[0]; + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; +} - crc = mxt_extract_T6_csum(&payload[1]); - if (crc != data->config_crc) { - data->config_crc = crc; - complete(&data->crc_completion); - } +static irqreturn_t mxt_process_messages(struct mxt_data *data) +{ + int total_handled, num_handled; + u8 count = data->last_message_count; - dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", - status, data->config_crc); - - if (status & MXT_T6_STATUS_RESET) - complete(&data->reset_completion); - } else if (mxt_is_T9_message(data, &message)) { - int id = reportid - data->T9_reportid_min; - mxt_input_touchevent(data, &message, id); - update_input = true; - } else if (message.reportid == data->T19_reportid) { - mxt_input_button(data, &message); - update_input = true; - } else { - mxt_dump_message(dev, &message); - } - } while (reportid != 0xff); + if (count < 1 || count > data->max_reportid) + count = 1; + + /* include final invalid message */ + total_handled = mxt_read_and_process_messages(data, count + 1); + if (total_handled < 0) + return IRQ_NONE; + /* if there were invalid messages, then we are done */ + else if (total_handled <= count) + goto update_count; + + /* keep reading two msgs until one is invalid or reportid limit */ + do { + num_handled = mxt_read_and_process_messages(data, 2); + if (num_handled < 0) + return IRQ_NONE; + + total_handled += num_handled; + + if (num_handled < 2) + break; + } while (total_handled < data->num_touchids); + +update_count: + data->last_message_count = total_handled; - if (update_input) - mxt_input_sync(data->input_dev); + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } return IRQ_HANDLED; } @@ -792,7 +949,14 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } - return mxt_process_messages_until_invalid(data); + if (!data->object_table) + return IRQ_HANDLED; + + if (data->T44_address) { + return mxt_process_messages_t44(data); + } else { + return mxt_process_messages(data); + } } static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, @@ -866,78 +1030,337 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); } -static int mxt_check_reg_init(struct mxt_data *data) +static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) +{ + static const unsigned int crcpoly = 0x80001B; + u32 result; + u32 data_word; + + data_word = (secondbyte << 8) | firstbyte; + result = ((*crc << 1) ^ data_word); + + if (result & 0x1000000) + result ^= crcpoly; + + *crc = result; +} + +static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) +{ + u32 crc = 0; + u8 *ptr = base + start_off; + u8 *last_val = base + end_off - 1; + + if (end_off < start_off) + return -EINVAL; + + while (ptr < last_val) { + mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); + ptr += 2; + } + + /* if len is odd, fill the last byte with 0 */ + if (ptr == last_val) + mxt_calc_crc24(&crc, *ptr, 0); + + /* Mask to 24-bit */ + crc &= 0x00FFFFFF; + + return crc; +} + +static int mxt_prepare_cfg_mem(struct mxt_data *data, + const struct firmware *cfg, + unsigned int data_pos, + unsigned int cfg_start_ofs, + u8 *config_mem, + size_t config_mem_size) { - const struct mxt_platform_data *pdata = data->pdata; - struct mxt_object *object; struct device *dev = &data->client->dev; - int index = 0; - int i, size; + struct mxt_object *object; + unsigned int type, instance, size, byte_offset; + int offset; int ret; + int i; + u16 reg; + u8 val; - if (!pdata->config) { - dev_dbg(dev, "No cfg data defined, skipping reg init\n"); - return 0; + while (data_pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + break; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + return -EINVAL; + } + data_pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d at %d\n", + type, i); + return -EINVAL; + } + data_pos += offset; + } + continue; + } + + if (size > mxt_obj_size(object)) { + /* + * Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited. + */ + dev_warn(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* + * If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. + */ + dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + } + + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + return -EINVAL; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d at %d\n", + type, i); + return -EINVAL; + } + data_pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + byte_offset = reg + i - cfg_start_ofs; + + if (byte_offset >= 0 && byte_offset < config_mem_size) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + return -EINVAL; + } + } } - mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + return 0; +} - if (data->config_crc == pdata->config_crc) { - dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - return 0; +static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start, + u8 *config_mem, size_t config_mem_size) +{ + unsigned int byte_offset = 0; + int error; + + /* Write configuration as blocks */ + while (byte_offset < config_mem_size) { + unsigned int size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + error = __mxt_write_reg(data->client, + cfg_start + byte_offset, + size, config_mem + byte_offset); + if (error) { + dev_err(&data->client->dev, + "Config write error, ret=%d\n", error); + return error; + } + + byte_offset += size; } - dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", - data->config_crc, pdata->config_crc); + return 0; +} - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; +/* + * mxt_update_cfg - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * <TYPE> <INSTANCE> <SIZE> <CONTENTS> + * + * <TYPE> - 2-byte object type as hex + * <INSTANCE> - 2-byte object instance number as hex + * <SIZE> - 2-byte object size as hex + * <CONTENTS> - array of <SIZE> 1-byte hex values + */ +static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) +{ + struct device *dev = &data->client->dev; + struct mxt_info cfg_info; + int ret; + int offset; + int data_pos; + int i; + int cfg_start_ofs; + u32 info_crc, config_crc, calculated_crc; + u8 *config_mem; + size_t config_mem_size; - if (!mxt_object_writable(object->type)) - continue; + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); - size = mxt_obj_size(object) * mxt_obj_instances(object); - if (index + size > pdata->config_length) { - dev_err(dev, "Not enough config data!\n"); + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + return -EINVAL; + } + + data_pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); return -EINVAL; } - ret = __mxt_write_reg(data->client, object->start_address, - size, &pdata->config[index]); - if (ret) - return ret; - index += size; + data_pos += offset; + } + + if (cfg_info.family_id != data->info.family_id) { + dev_err(dev, "Family ID mismatch!\n"); + return -EINVAL; + } + + if (cfg_info.variant_id != data->info.variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + return -EINVAL; + } + + /* Read CRCs */ + ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + return -EINVAL; + } + data_pos += offset; + + ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + return -EINVAL; + } + data_pos += offset; + + /* + * The Info Block CRC is calculated over mxt_info and the object + * table. If it does not match then we are trying to load the + * configuration from a different chip or firmware version, so + * the configuration CRC is invalid anyway. + */ + if (info_crc == data->info_crc) { + if (config_crc == 0 || data->config_crc == 0) { + dev_info(dev, "CRC zero, attempting to apply config\n"); + } else if (config_crc == data->config_crc) { + dev_dbg(dev, "Config CRC 0x%06X: OK\n", + data->config_crc); + return 0; + } else { + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + } + } else { + dev_warn(dev, + "Warning: Info CRC error - device=0x%06X file=0x%06X\n", + data->info_crc, info_crc); + } + + /* Malloc memory to store configuration */ + cfg_start_ofs = MXT_OBJECT_START + + data->info.object_num * sizeof(struct mxt_object) + + MXT_INFO_CHECKSUM_SIZE; + config_mem_size = data->mem_size - cfg_start_ofs; + config_mem = kzalloc(config_mem_size, GFP_KERNEL); + if (!config_mem) { + dev_err(dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs, + config_mem, config_mem_size); + if (ret) + goto release_mem; + + /* Calculate crc of the received configs (not the raw config file) */ + if (data->T7_address < cfg_start_ofs) { + dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n", + data->T7_address, cfg_start_ofs); + ret = 0; + goto release_mem; } + calculated_crc = mxt_calculate_crc(config_mem, + data->T7_address - cfg_start_ofs, + config_mem_size); + + if (config_crc > 0 && config_crc != calculated_crc) + dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + + ret = mxt_upload_cfg_mem(data, cfg_start_ofs, + config_mem, config_mem_size); + if (ret) + goto release_mem; + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); ret = mxt_soft_reset(data); if (ret) - return ret; + goto release_mem; dev_info(dev, "Config successfully updated\n"); - return 0; +release_mem: + kfree(config_mem); + return ret; } -static int mxt_make_highchg(struct mxt_data *data) +static int mxt_acquire_irq(struct mxt_data *data) { - struct device *dev = &data->client->dev; - struct mxt_message message; - int count = 10; int error; - /* Read dummy message to make high CHG pin */ - do { - error = mxt_read_message(data, &message); - if (error) - return error; - } while (message.reportid != 0xff && --count); + enable_irq(data->irq); - if (!count) { - dev_err(dev, "CHG pin isn't cleared\n"); - return -EBUSY; - } + error = mxt_process_messages_until_invalid(data); + if (error) + return error; return 0; } @@ -956,24 +1379,60 @@ static int mxt_get_info(struct mxt_data *data) return 0; } +static void mxt_free_input_device(struct mxt_data *data) +{ + if (data->input_dev) { + input_unregister_device(data->input_dev); + data->input_dev = NULL; + } +} + +static void mxt_free_object_table(struct mxt_data *data) +{ + kfree(data->object_table); + data->object_table = NULL; + kfree(data->msg_buf); + data->msg_buf = NULL; + data->T5_address = 0; + data->T5_msg_size = 0; + data->T6_reportid = 0; + data->T7_address = 0; + data->T9_reportid_min = 0; + data->T9_reportid_max = 0; + data->T19_reportid = 0; + data->T44_address = 0; + data->max_reportid = 0; +} + static int mxt_get_object_table(struct mxt_data *data) { struct i2c_client *client = data->client; size_t table_size; + struct mxt_object *object_table; int error; int i; u8 reportid; + u16 end_address; table_size = data->info.object_num * sizeof(struct mxt_object); + object_table = kzalloc(table_size, GFP_KERNEL); + if (!object_table) { + dev_err(&data->client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + error = __mxt_read_reg(client, MXT_OBJECT_START, table_size, - data->object_table); - if (error) + object_table); + if (error) { + kfree(object_table); return error; + } /* Valid Report IDs start counting from 1 */ reportid = 1; + data->mem_size = 0; for (i = 0; i < data->info.object_num; i++) { - struct mxt_object *object = data->object_table + i; + struct mxt_object *object = object_table + i; u8 min_id, max_id; le16_to_cpus(&object->start_address); @@ -995,31 +1454,74 @@ static int mxt_get_object_table(struct mxt_data *data) min_id, max_id); switch (object->type) { + case MXT_GEN_MESSAGE_T5: + if (data->info.family_id == 0x80 && + data->info.version < 0x20) { + /* + * On mXT224 firmware versions prior to V2.0 + * read and discard unused CRC byte otherwise + * DMA reads are misaligned. + */ + data->T5_msg_size = mxt_obj_size(object); + } else { + /* CRC not enabled, so skip last byte */ + data->T5_msg_size = mxt_obj_size(object) - 1; + } + data->T5_address = object->start_address; + break; case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; data->T6_address = object->start_address; break; + case MXT_GEN_POWER_T7: + data->T7_address = object->start_address; + break; case MXT_TOUCH_MULTI_T9: data->T9_reportid_min = min_id; data->T9_reportid_max = max_id; + data->num_touchids = object->num_report_ids + * mxt_obj_instances(object); + break; + case MXT_SPT_MESSAGECOUNT_T44: + data->T44_address = object->start_address; break; case MXT_SPT_GPIOPWM_T19: data->T19_reportid = min_id; break; } + + end_address = object->start_address + + mxt_obj_size(object) * mxt_obj_instances(object) - 1; + + if (end_address >= data->mem_size) + data->mem_size = end_address + 1; + } + + /* Store maximum reportid */ + data->max_reportid = reportid; + + /* If T44 exists, T5 position has to be directly after */ + if (data->T44_address && (data->T5_address != data->T44_address + 1)) { + dev_err(&client->dev, "Invalid T44 position\n"); + error = -EINVAL; + goto free_object_table; + } + + data->msg_buf = kcalloc(data->max_reportid, + data->T5_msg_size, GFP_KERNEL); + if (!data->msg_buf) { + dev_err(&client->dev, "Failed to allocate message buffer\n"); + error = -ENOMEM; + goto free_object_table; } + data->object_table = object_table; + return 0; -} -static void mxt_free_object_table(struct mxt_data *data) -{ - kfree(data->object_table); - data->object_table = NULL; - data->T6_reportid = 0; - data->T9_reportid_min = 0; - data->T9_reportid_max = 0; - data->T19_reportid = 0; +free_object_table: + mxt_free_object_table(data); + return error; } static int mxt_read_t9_resolution(struct mxt_data *data) @@ -1070,55 +1572,259 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } -static int mxt_initialize(struct mxt_data *data) +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static int mxt_initialize_t9_input_device(struct mxt_data *data) { - struct i2c_client *client = data->client; - struct mxt_info *info = &data->info; + struct device *dev = &data->client->dev; + const struct mxt_platform_data *pdata = data->pdata; + struct input_dev *input_dev; int error; + unsigned int num_mt_slots; + unsigned int mt_flags = 0; + int i; - error = mxt_get_info(data); + error = mxt_read_t9_resolution(data); if (error) - return error; + dev_warn(dev, "Failed to initialize T9 resolution\n"); - data->object_table = kcalloc(info->object_num, - sizeof(struct mxt_object), - GFP_KERNEL); - if (!data->object_table) { - dev_err(&client->dev, "Failed to allocate memory\n"); + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(dev, "Failed to allocate memory\n"); return -ENOMEM; } + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + if (pdata->t19_num_keys) { + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); + + mt_flags |= INPUT_MT_POINTER; + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; + } + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, 255, 0, 0); + + /* For multi touch */ + num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->max_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg); + +static void mxt_config_cb(const struct firmware *cfg, void *ctx) +{ + mxt_configure_objects(ctx, cfg); + release_firmware(cfg); +} + +static int mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int recovery_attempts = 0; + int error; + + while (1) { + error = mxt_get_info(data); + if (!error) + break; + + /* Check bootloader state */ + error = mxt_probe_bootloader(data, false); + if (error) { + dev_info(&client->dev, "Trying alternate bootloader address\n"); + error = mxt_probe_bootloader(data, true); + if (error) { + /* Chip is not in appmode or bootloader mode */ + return error; + } + } + + /* OK, we are in bootloader, see if we can recover */ + if (++recovery_attempts > 1) { + dev_err(&client->dev, "Could not recover from bootloader mode\n"); + /* + * We can reflash from this state, so do not + * abort initialization. + */ + data->in_bootloader = true; + return 0; + } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); + } + /* Get object table information */ error = mxt_get_object_table(data); if (error) { dev_err(&client->dev, "Error %d reading object table\n", error); - goto err_free_object_table; + return error; } - /* Check register init values */ - error = mxt_check_reg_init(data); + error = mxt_acquire_irq(data); + if (error) + goto err_free_object_table; + + error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME, + &client->dev, GFP_KERNEL, data, + mxt_config_cb); if (error) { - dev_err(&client->dev, "Error %d initializing configuration\n", + dev_err(&client->dev, "Failed to invoke firmware loader: %d\n", error); goto err_free_object_table; } - error = mxt_read_t9_resolution(data); + return 0; + +err_free_object_table: + mxt_free_object_table(data); + return error; +} + +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ + struct device *dev = &data->client->dev; + int error; + struct t7_config *new_config; + struct t7_config deepsleep = { .active = 0, .idle = 0 }; + + if (sleep == MXT_POWER_CFG_DEEPSLEEP) + new_config = &deepsleep; + else + new_config = &data->t7_cfg; + + error = __mxt_write_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), new_config); + if (error) + return error; + + dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", + new_config->active, new_config->idle); + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + bool retry = false; + +recheck: + error = __mxt_read_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), &data->t7_cfg); + if (error) + return error; + + if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { + if (!retry) { + dev_dbg(dev, "T7 cfg zero, resetting\n"); + mxt_soft_reset(data); + retry = true; + goto recheck; + } else { + dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); + data->t7_cfg.active = 20; + data->t7_cfg.idle = 100; + return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + } + } + + dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", + data->t7_cfg.active, data->t7_cfg.idle); + return 0; +} + +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg) +{ + struct device *dev = &data->client->dev; + struct mxt_info *info = &data->info; + int error; + + if (cfg) { + error = mxt_update_cfg(data, cfg); + if (error) + dev_warn(dev, "Error %d updating config\n", error); + } + + error = mxt_init_t7_power_cfg(data); if (error) { - dev_err(&client->dev, "Failed to initialize T9 resolution\n"); - goto err_free_object_table; + dev_err(dev, "Failed to initialize power cfg\n"); + return error; } - dev_info(&client->dev, + error = mxt_initialize_t9_input_device(data); + if (error) + return error; + + dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", info->family_id, info->variant_id, info->version >> 4, info->version & 0xf, info->build, info->object_num); return 0; - -err_free_object_table: - mxt_free_object_table(data); - return error; } /* Firmware Version is returned as Major.Minor.Build */ @@ -1246,30 +1952,47 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto release_firmware; - ret = mxt_lookup_bootloader_address(data); - if (ret) - goto release_firmware; + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; - /* Change to the bootloader mode */ - data->in_bootloader = true; + ret = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; - ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); - if (ret) - goto release_firmware; + msleep(MXT_RESET_TIME); - msleep(MXT_RESET_TIME); + /* Do not need to scan since we know family ID */ + ret = mxt_lookup_bootloader_address(data, 0); + if (ret) + goto release_firmware; + + mxt_free_input_device(data); + mxt_free_object_table(data); + } else { + enable_irq(data->irq); + } reinit_completion(&data->bl_completion); - ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); - if (ret) - goto disable_irq; + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (ret) { + /* Bootloader may still be unlocked from previous attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); + if (ret) + goto disable_irq; + } else { + dev_info(dev, "Unlocking bootloader\n"); - /* Unlock bootloader */ - mxt_unlock_bootloader(data); + /* Unlock bootloader */ + ret = mxt_send_bootloader_cmd(data, true); + if (ret) + goto disable_irq; + } while (pos < fw->size) { - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); if (ret) goto disable_irq; @@ -1283,7 +2006,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto disable_irq; - ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); if (ret) { retry++; @@ -1343,13 +2066,7 @@ static ssize_t mxt_update_fw_store(struct device *dev, } else { dev_info(dev, "The firmware update succeeded\n"); - mxt_free_object_table(data); - - mxt_initialize(data); - - enable_irq(data->irq); - - error = mxt_make_highchg(data); + error = mxt_initialize(data); if (error) return error; } @@ -1376,16 +2093,15 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { - /* Touch enable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + /* Recalibrate since chip has been in deep sleep */ + mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); } static void mxt_stop(struct mxt_data *data) { - /* Touch disable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); } static int mxt_input_open(struct input_dev *dev) @@ -1404,138 +2120,113 @@ static void mxt_input_close(struct input_dev *dev) mxt_stop(data); } -static int mxt_probe(struct i2c_client *client, - const struct i2c_device_id *id) +#ifdef CONFIG_OF +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + struct mxt_platform_data *pdata; + u32 *keymap; + u32 keycode; + int proplen, i, ret; + + if (!client->dev.of_node) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (of_find_property(client->dev.of_node, "linux,gpio-keymap", + &proplen)) { + pdata->t19_num_keys = proplen / sizeof(u32); + + keymap = devm_kzalloc(&client->dev, + pdata->t19_num_keys * sizeof(keymap[0]), + GFP_KERNEL); + if (!keymap) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < pdata->t19_num_keys; i++) { + ret = of_property_read_u32_index(client->dev.of_node, + "linux,gpio-keymap", i, &keycode); + if (ret) + keycode = KEY_RESERVED; + + keymap[i] = keycode; + } + + pdata->t19_keymap = keymap; + } + + return pdata; +} +#else +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + dev_dbg(&client->dev, "No platform data specified\n"); + return ERR_PTR(-EINVAL); +} +#endif + +static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); struct mxt_data *data; - struct input_dev *input_dev; + const struct mxt_platform_data *pdata; int error; - unsigned int num_mt_slots; - unsigned int mt_flags = 0; - int i; - if (!pdata) - return -EINVAL; + pdata = dev_get_platdata(&client->dev); + if (!pdata) { + pdata = mxt_parse_dt(client); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!data || !input_dev) { + if (!data) { dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; + return -ENOMEM; } - input_dev->name = "Atmel maXTouch Touchscreen"; snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); - input_dev->phys = data->phys; - - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; - input_dev->open = mxt_input_open; - input_dev->close = mxt_input_close; - data->client = client; - data->input_dev = input_dev; data->pdata = pdata; data->irq = client->irq; + i2c_set_clientdata(client, data); init_completion(&data->bl_completion); init_completion(&data->reset_completion); init_completion(&data->crc_completion); - error = mxt_initialize(data); - if (error) - goto err_free_mem; - - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - - if (pdata->t19_num_keys) { - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - - for (i = 0; i < pdata->t19_num_keys; i++) - if (pdata->t19_keymap[i] != KEY_RESERVED) - input_set_capability(input_dev, EV_KEY, - pdata->t19_keymap[i]); - - mt_flags |= INPUT_MT_POINTER; - - input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - MXT_PIXELS_PER_MM); - - input_dev->name = "Atmel maXTouch Touchpad"; - } - - /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, 255, 0, 0); - - /* For multi touch */ - num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; - error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); - if (error) - goto err_free_object; - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, MXT_MAX_AREA, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, 255, 0, 0); - - input_set_drvdata(input_dev, data); - i2c_set_clientdata(client, data); - error = request_threaded_irq(client->irq, NULL, mxt_interrupt, pdata->irqflags | IRQF_ONESHOT, client->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_object; + goto err_free_mem; } - error = mxt_make_highchg(data); - if (error) - goto err_free_irq; + disable_irq(client->irq); - error = input_register_device(input_dev); - if (error) { - dev_err(&client->dev, "Error %d registering input device\n", - error); + error = mxt_initialize(data); + if (error) goto err_free_irq; - } error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { dev_err(&client->dev, "Failure %d creating sysfs group\n", error); - goto err_unregister_device; + goto err_free_object; } return 0; -err_unregister_device: - input_unregister_device(input_dev); - input_dev = NULL; +err_free_object: + mxt_free_input_device(data); + mxt_free_object_table(data); err_free_irq: free_irq(client->irq, data); -err_free_object: - kfree(data->object_table); err_free_mem: - input_free_device(input_dev); kfree(data); return error; } @@ -1546,8 +2237,8 @@ static int mxt_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); - input_unregister_device(data->input_dev); - kfree(data->object_table); + mxt_free_input_device(data); + mxt_free_object_table(data); kfree(data); return 0; @@ -1576,8 +2267,6 @@ static int mxt_resume(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; - mxt_soft_reset(data); - mutex_lock(&input_dev->mutex); if (input_dev->users) @@ -1591,6 +2280,12 @@ static int mxt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); +static const struct of_device_id mxt_of_match[] = { + { .compatible = "atmel,maxtouch", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mxt_of_match); + static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, @@ -1604,6 +2299,7 @@ static struct i2c_driver mxt_driver = { .driver = { .name = "atmel_mxt_ts", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mxt_of_match), .pm = &mxt_pm_ops, }, .probe = mxt_probe, diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d4f33992ad8c..ee3434f1e949 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -262,7 +262,6 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, case M06: wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; wrbuf[2] = value; wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; return edt_ft5x06_ts_readwrite(tsdata->client, 4, @@ -733,8 +732,7 @@ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) { - if (tsdata->debug_dir) - debugfs_remove_recursive(tsdata->debug_dir); + debugfs_remove_recursive(tsdata->debug_dir); kfree(tsdata->raw_buffer); } @@ -814,7 +812,7 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, /* if we find something consistent, stay with that assumption * at least M09 won't send 3 bytes here */ - if (!(strnicmp(rdbuf + 1, "EP0", 3))) { + if (!(strncasecmp(rdbuf + 1, "EP0", 3))) { tsdata->version = M06; /* remove last '$' end marker */ diff --git a/drivers/input/touchscreen/ipaq-micro-ts.c b/drivers/input/touchscreen/ipaq-micro-ts.c new file mode 100644 index 000000000000..62c8976e616f --- /dev/null +++ b/drivers/input/touchscreen/ipaq-micro-ts.c @@ -0,0 +1,166 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * h3600 atmel micro companion support, touchscreen subdevice + * Author : Alessandro Gardich <gremlin@gremlin.it> + * Author : Dmitry Artamonow <mad_soft@inbox.ru> + * Author : Linus Walleij <linus.walleij@linaro.org> + * + */ + +#include <asm/byteorder.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/mfd/ipaq-micro.h> + +struct touchscreen_data { + struct input_dev *input; + struct ipaq_micro *micro; +}; + +static void micro_ts_receive(void *data, int len, unsigned char *msg) +{ + struct touchscreen_data *ts = data; + + if (len == 4) { + input_report_abs(ts->input, ABS_X, + be16_to_cpup((__be16 *) &msg[2])); + input_report_abs(ts->input, ABS_Y, + be16_to_cpup((__be16 *) &msg[0])); + input_report_key(ts->input, BTN_TOUCH, 1); + input_sync(ts->input); + } else if (len == 0) { + input_report_abs(ts->input, ABS_X, 0); + input_report_abs(ts->input, ABS_Y, 0); + input_report_key(ts->input, BTN_TOUCH, 0); + input_sync(ts->input); + } +} + +static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable) +{ + struct ipaq_micro *micro = ts->micro; + + spin_lock_irq(µ->lock); + + if (enable) { + micro->ts = micro_ts_receive; + micro->ts_data = ts; + } else { + micro->ts = NULL; + micro->ts_data = NULL; + } + + spin_unlock_irq(&ts->micro->lock); +} + +static int micro_ts_open(struct input_dev *input) +{ + struct touchscreen_data *ts = input_get_drvdata(input); + + micro_ts_toggle_receive(ts, true); + + return 0; +} + +static void micro_ts_close(struct input_dev *input) +{ + struct touchscreen_data *ts = input_get_drvdata(input); + + micro_ts_toggle_receive(ts, false); +} + +static int micro_ts_probe(struct platform_device *pdev) +{ + struct ipaq_micro *micro = dev_get_drvdata(pdev->dev.parent); + struct touchscreen_data *ts; + int error; + + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->micro = micro; + + ts->input = devm_input_allocate_device(&pdev->dev); + if (!ts->input) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + ts->input->name = "ipaq micro ts"; + ts->input->open = micro_ts_open; + ts->input->close = micro_ts_close; + + input_set_drvdata(ts->input, ts); + + input_set_capability(ts->input, EV_KEY, BTN_TOUCH); + input_set_capability(ts->input, EV_ABS, ABS_X); + input_set_capability(ts->input, EV_ABS, ABS_Y); + input_set_abs_params(ts->input, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, 1023, 0, 0); + + error = input_register_device(ts->input); + if (error) { + dev_err(&pdev->dev, "error registering touch input\n"); + return error; + } + + platform_set_drvdata(pdev, ts); + + dev_info(&pdev->dev, "iPAQ micro touchscreen\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int micro_ts_suspend(struct device *dev) +{ + struct touchscreen_data *ts = dev_get_drvdata(dev); + + micro_ts_toggle_receive(ts, false); + + return 0; +} + +static int micro_ts_resume(struct device *dev) +{ + struct touchscreen_data *ts = dev_get_drvdata(dev); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (input->users) + micro_ts_toggle_receive(ts, true); + + mutex_unlock(&input->mutex); + + return 0; +} +#endif + +static const struct dev_pm_ops micro_ts_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(micro_ts_suspend, micro_ts_resume) +}; + +static struct platform_driver micro_ts_device_driver = { + .driver = { + .name = "ipaq-micro-ts", + .pm = µ_ts_dev_pm_ops, + }, + .probe = micro_ts_probe, +}; +module_platform_driver(micro_ts_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen"); +MODULE_ALIAS("platform:ipaq-micro-ts"); diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index 7324c5c0fb86..651ec71a5c68 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -36,22 +36,21 @@ struct jornada_ts { static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts) { + /* 3 low word X samples */ + jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY); - /* 3 low word X samples */ - jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY); - jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY); - jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY); + /* 3 low word Y samples */ + jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY); - /* 3 low word Y samples */ - jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY); - jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY); - jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY); + /* combined x samples bits */ + jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY); - /* combined x samples bits */ - jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY); - - /* combined y samples bits */ - jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY); + /* combined y samples bits */ + jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY); } static int jornada720_ts_average(int coords[4]) @@ -104,13 +103,13 @@ static int jornada720_ts_probe(struct platform_device *pdev) struct input_dev *input_dev; int error; - jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL); - input_dev = input_allocate_device(); + jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL); + if (!jornada_ts) + return -ENOMEM; - if (!jornada_ts || !input_dev) { - error = -ENOMEM; - goto fail1; - } + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; platform_set_drvdata(pdev, jornada_ts); @@ -126,36 +125,18 @@ static int jornada720_ts_probe(struct platform_device *pdev) input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0); input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0); - error = request_irq(IRQ_GPIO9, - jornada720_ts_interrupt, - IRQF_TRIGGER_RISING, - "HP7XX Touchscreen driver", pdev); + error = devm_request_irq(&pdev->dev, IRQ_GPIO9, + jornada720_ts_interrupt, + IRQF_TRIGGER_RISING, + "HP7XX Touchscreen driver", pdev); if (error) { - printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n"); - goto fail1; + dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n"); + return error; } error = input_register_device(jornada_ts->dev); if (error) - goto fail2; - - return 0; - - fail2: - free_irq(IRQ_GPIO9, pdev); - fail1: - input_free_device(input_dev); - kfree(jornada_ts); - return error; -} - -static int jornada720_ts_remove(struct platform_device *pdev) -{ - struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); - - free_irq(IRQ_GPIO9, pdev); - input_unregister_device(jornada_ts->dev); - kfree(jornada_ts); + return error; return 0; } @@ -165,7 +146,6 @@ MODULE_ALIAS("platform:jornada_ts"); static struct platform_driver jornada720_ts_driver = { .probe = jornada720_ts_probe, - .remove = jornada720_ts_remove, .driver = { .name = "jornada_ts", .owner = THIS_MODULE, diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 00510a9836b3..8b47e1fecb25 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -248,8 +248,7 @@ static int mcs5000_ts_probe(struct i2c_client *client, return 0; } -#ifdef CONFIG_PM -static int mcs5000_ts_suspend(struct device *dev) +static int __maybe_unused mcs5000_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -259,7 +258,7 @@ static int mcs5000_ts_suspend(struct device *dev) return 0; } -static int mcs5000_ts_resume(struct device *dev) +static int __maybe_unused mcs5000_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct mcs5000_ts_data *data = i2c_get_clientdata(client); @@ -269,7 +268,6 @@ static int mcs5000_ts_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 19c6c0fdc94b..fc49c75317d1 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -23,22 +23,51 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/input.h> +#include <linux/input/mt.h> #include <linux/input/pixcir_ts.h> #include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> + +#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */ struct pixcir_i2c_ts_data { struct i2c_client *client; struct input_dev *input; - const struct pixcir_ts_platform_data *chip; + const struct pixcir_ts_platform_data *pdata; bool running; + int max_fingers; /* Max fingers supported in this instance */ +}; + +struct pixcir_touch { + int x; + int y; + int id; +}; + +struct pixcir_report_data { + int num_touches; + struct pixcir_touch touches[PIXCIR_MAX_SLOTS]; }; -static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) +static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, + struct pixcir_report_data *report) { - struct pixcir_i2c_ts_data *tsdata = data; - u8 rdbuf[10], wrbuf[1] = { 0 }; + u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5]; + u8 wrbuf[1] = { 0 }; + u8 *bufptr; u8 touch; - int ret; + int ret, i; + int readsize; + const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip; + + memset(report, 0, sizeof(struct pixcir_report_data)); + + i = chip->has_hw_ids ? 1 : 0; + readsize = 2 + tsdata->max_fingers * (4 + i); + if (readsize > sizeof(rdbuf)) + readsize = sizeof(rdbuf); ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf)); if (ret != sizeof(wrbuf)) { @@ -48,7 +77,7 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) return; } - ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf)); + ret = i2c_master_recv(tsdata->client, rdbuf, readsize); if (ret != sizeof(rdbuf)) { dev_err(&tsdata->client->dev, "%s: i2c_master_recv failed(), ret=%d\n", @@ -56,45 +85,103 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data) return; } - touch = rdbuf[0]; - if (touch) { - u16 posx1 = (rdbuf[3] << 8) | rdbuf[2]; - u16 posy1 = (rdbuf[5] << 8) | rdbuf[4]; - u16 posx2 = (rdbuf[7] << 8) | rdbuf[6]; - u16 posy2 = (rdbuf[9] << 8) | rdbuf[8]; - - input_report_key(tsdata->input, BTN_TOUCH, 1); - input_report_abs(tsdata->input, ABS_X, posx1); - input_report_abs(tsdata->input, ABS_Y, posy1); - - input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1); - input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1); - input_mt_sync(tsdata->input); - - if (touch == 2) { - input_report_abs(tsdata->input, - ABS_MT_POSITION_X, posx2); - input_report_abs(tsdata->input, - ABS_MT_POSITION_Y, posy2); - input_mt_sync(tsdata->input); + touch = rdbuf[0] & 0x7; + if (touch > tsdata->max_fingers) + touch = tsdata->max_fingers; + + report->num_touches = touch; + bufptr = &rdbuf[2]; + + for (i = 0; i < touch; i++) { + report->touches[i].x = (bufptr[1] << 8) | bufptr[0]; + report->touches[i].y = (bufptr[3] << 8) | bufptr[2]; + + if (chip->has_hw_ids) { + report->touches[i].id = bufptr[4]; + bufptr = bufptr + 5; + } else { + bufptr = bufptr + 4; + } + } +} + +static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, + struct pixcir_report_data *report) +{ + struct input_mt_pos pos[PIXCIR_MAX_SLOTS]; + int slots[PIXCIR_MAX_SLOTS]; + struct pixcir_touch *touch; + int n, i, slot; + struct device *dev = &ts->client->dev; + const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip; + + n = report->num_touches; + if (n > PIXCIR_MAX_SLOTS) + n = PIXCIR_MAX_SLOTS; + + if (!chip->has_hw_ids) { + for (i = 0; i < n; i++) { + touch = &report->touches[i]; + pos[i].x = touch->x; + pos[i].y = touch->y; + } + + input_mt_assign_slots(ts->input, slots, pos, n); + } + + for (i = 0; i < n; i++) { + touch = &report->touches[i]; + + if (chip->has_hw_ids) { + slot = input_mt_get_slot_by_key(ts->input, touch->id); + if (slot < 0) { + dev_dbg(dev, "no free slot for id 0x%x\n", + touch->id); + continue; + } + } else { + slot = slots[i]; } - } else { - input_report_key(tsdata->input, BTN_TOUCH, 0); + + input_mt_slot(ts->input, slot); + input_mt_report_slot_state(ts->input, + MT_TOOL_FINGER, true); + + input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x); + input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y); + + dev_dbg(dev, "%d: slot %d, x %d, y %d\n", + i, slot, touch->x, touch->y); } - input_sync(tsdata->input); + input_mt_sync_frame(ts->input); + input_sync(ts->input); } static irqreturn_t pixcir_ts_isr(int irq, void *dev_id) { struct pixcir_i2c_ts_data *tsdata = dev_id; - const struct pixcir_ts_platform_data *pdata = tsdata->chip; + const struct pixcir_ts_platform_data *pdata = tsdata->pdata; + struct pixcir_report_data report; while (tsdata->running) { - pixcir_ts_poscheck(tsdata); - - if (gpio_get_value(pdata->gpio_attb)) + /* parse packet */ + pixcir_ts_parse(tsdata, &report); + + /* report it */ + pixcir_ts_report(tsdata, &report); + + if (gpio_get_value(pdata->gpio_attb)) { + if (report.num_touches) { + /* + * Last report with no finger up? + * Do it now then. + */ + input_mt_sync_frame(tsdata->input); + input_sync(tsdata->input); + } break; + } msleep(20); } @@ -323,16 +410,69 @@ unlock: static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume); +#ifdef CONFIG_OF +static const struct of_device_id pixcir_of_match[]; + +static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev) +{ + struct pixcir_ts_platform_data *pdata; + struct device_node *np = dev->of_node; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(pixcir_of_match), dev); + if (!match) + return ERR_PTR(-EINVAL); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data; + + pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0); + /* gpio_attb validity is checked in probe */ + + if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) { + dev_err(dev, "Failed to get touchscreen-size-x property\n"); + return ERR_PTR(-EINVAL); + } + pdata->x_max -= 1; + + if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) { + dev_err(dev, "Failed to get touchscreen-size-y property\n"); + return ERR_PTR(-EINVAL); + } + pdata->y_max -= 1; + + dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__, + pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb); + + return pdata; +} +#else +static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + static int pixcir_i2c_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct pixcir_ts_platform_data *pdata = dev_get_platdata(&client->dev); struct device *dev = &client->dev; + struct device_node *np = dev->of_node; struct pixcir_i2c_ts_data *tsdata; struct input_dev *input; int error; + if (np && !pdata) { + pdata = pixcir_parse_dt(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + if (!pdata) { dev_err(&client->dev, "platform data not defined\n"); return -EINVAL; @@ -343,6 +483,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return -EINVAL; } + if (!pdata->chip.max_fingers) { + dev_err(dev, "Invalid max_fingers in pdata\n"); + return -EINVAL; + } + tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); if (!tsdata) return -ENOMEM; @@ -355,7 +500,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, tsdata->client = client; tsdata->input = input; - tsdata->chip = pdata; + tsdata->pdata = pdata; input->name = client->name; input->id.bustype = BUS_I2C; @@ -371,6 +516,20 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); + tsdata->max_fingers = tsdata->pdata->chip.max_fingers; + if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) { + tsdata->max_fingers = PIXCIR_MAX_SLOTS; + dev_info(dev, "Limiting maximum fingers to %d\n", + tsdata->max_fingers); + } + + error = input_mt_init_slots(input, tsdata->max_fingers, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(dev, "Error initializing Multi-Touch slots\n"); + return error; + } + input_set_drvdata(input, tsdata); error = devm_gpio_request_one(dev, pdata->gpio_attb, @@ -419,15 +578,36 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client) static const struct i2c_device_id pixcir_i2c_ts_id[] = { { "pixcir_ts", 0 }, + { "pixcir_tangoc", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id); +#ifdef CONFIG_OF +static const struct pixcir_i2c_chip_data pixcir_ts_data = { + .max_fingers = 2, + /* no hw id support */ +}; + +static const struct pixcir_i2c_chip_data pixcir_tangoc_data = { + .max_fingers = 5, + .has_hw_ids = true, +}; + +static const struct of_device_id pixcir_of_match[] = { + { .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data }, + { .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data }, + { } +}; +MODULE_DEVICE_TABLE(of, pixcir_of_match); +#endif + static struct i2c_driver pixcir_i2c_ts_driver = { .driver = { .owner = THIS_MODULE, .name = "pixcir_ts", .pm = &pixcir_dev_pm_ops, + .of_match_table = of_match_ptr(pixcir_of_match), }, .probe = pixcir_i2c_ts_probe, .remove = pixcir_i2c_ts_remove, diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 19cb247dbb86..5a69ded9b53c 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -264,7 +264,7 @@ static int s3c2410ts_probe(struct platform_device *pdev) return -ENOENT; } - clk_enable(ts.clock); + clk_prepare_enable(ts.clock); dev_dbg(dev, "got and enabled clocks\n"); ts.irq_tc = ret = platform_get_irq(pdev, 0); @@ -369,7 +369,7 @@ static int s3c2410ts_remove(struct platform_device *pdev) free_irq(ts.irq_tc, ts.input); del_timer_sync(&touch_timer); - clk_disable(ts.clock); + clk_disable_unprepare(ts.clock); clk_put(ts.clock); input_unregister_device(ts.input); diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c index 16b52115c27f..705ffa1e064a 100644 --- a/drivers/input/touchscreen/wm9712.c +++ b/drivers/input/touchscreen/wm9712.c @@ -41,7 +41,7 @@ */ static int rpu = 8; module_param(rpu, int, 0); -MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); +MODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect."); /* * Set current used for pressure measurement. diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c index 7405353199d7..572a5a64face 100644 --- a/drivers/input/touchscreen/wm9713.c +++ b/drivers/input/touchscreen/wm9713.c @@ -41,7 +41,7 @@ */ static int rpu = 8; module_param(rpu, int, 0); -MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); +MODULE_PARM_DESC(rpu, "Set internal pull up resistor for pen detect."); /* * Set current used for pressure measurement. diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index d0ef91fc87d1..b1ae77995968 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -70,11 +70,11 @@ * Documentation/input/input-programming.txt for more details. */ -static int abs_x[3] = {350, 3900, 5}; +static int abs_x[3] = {150, 4000, 5}; module_param_array(abs_x, int, NULL, 0); MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); -static int abs_y[3] = {320, 3750, 40}; +static int abs_y[3] = {200, 4000, 40}; module_param_array(abs_y, int, NULL, 0); MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index feea85b52fa8..8ba48f5eff7b 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -29,6 +29,8 @@ #include <linux/sysfs.h> #include <linux/input/mt.h> #include <linux/platform_data/zforce_ts.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> #include <linux/of.h> #include <linux/of_gpio.h> @@ -117,6 +119,8 @@ struct zforce_ts { const struct zforce_ts_platdata *pdata; char phys[32]; + struct regulator *reg_vdd; + bool suspending; bool suspended; bool boot_complete; @@ -690,6 +694,11 @@ static void zforce_reset(void *data) struct zforce_ts *ts = data; gpio_set_value(ts->pdata->gpio_rst, 0); + + udelay(10); + + if (!IS_ERR(ts->reg_vdd)) + regulator_disable(ts->reg_vdd); } static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev) @@ -765,10 +774,32 @@ static int zforce_probe(struct i2c_client *client, return ret; } + ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd"); + if (IS_ERR(ts->reg_vdd)) { + ret = PTR_ERR(ts->reg_vdd); + if (ret == -EPROBE_DEFER) + return ret; + } else { + ret = regulator_enable(ts->reg_vdd); + if (ret) + return ret; + + /* + * according to datasheet add 100us grace time after regular + * regulator enable delay. + */ + udelay(100); + } + ret = devm_add_action(&client->dev, zforce_reset, ts); if (ret) { dev_err(&client->dev, "failed to register reset action, %d\n", ret); + + /* hereafter the regulator will be disabled by the action */ + if (!IS_ERR(ts->reg_vdd)) + regulator_disable(ts->reg_vdd); + return ret; } |