From b595076a180a56d1bb170e6eceda6eb9d76f4cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 1 Nov 2010 15:38:34 -0400 Subject: tree-wide: fix comment/printk typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "gadget", "through", "command", "maintain", "maintain", "controller", "address", "between", "initiali[zs]e", "instead", "function", "select", "already", "equal", "access", "management", "hierarchy", "registration", "interest", "relative", "memory", "offset", "already", Signed-off-by: Uwe Kleine-König Signed-off-by: Jiri Kosina --- drivers/input/touchscreen/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 06ea8da95c62..6675bd7dde19 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -610,7 +610,7 @@ config TOUCHSCREEN_USB_ZYTRONIC config TOUCHSCREEN_USB_ETT_TC45USB default y - bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED + bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_NEXIO -- cgit v1.2.3 From de7fd0fc989727d657d40f6ddf2fb99639bd6262 Mon Sep 17 00:00:00 2001 From: Michael Witten Date: Wed, 27 Oct 2010 23:11:10 +0000 Subject: Kconfig: typo: boad -> board Signed-off-by: Michael Witten Signed-off-by: Jiri Kosina --- drivers/input/serio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 6256233d2bfb..44a351cdc641 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -230,7 +230,7 @@ config SERIO_PS2MULT tristate "TQC PS/2 multiplexer" help Say Y here if you have the PS/2 line multiplexer like the one - present on TQC boads. + present on TQC boards. To compile this driver as a module, choose M here: the module will be called ps2mult. -- cgit v1.2.3 From 49cc69b6789b57d2d8ed78843c4219525b433b58 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 11 Nov 2010 21:39:11 -0800 Subject: Input: xpad - return proper error in error path In current implementation, xpad_probe return 0 when usb_alloc_urb failed for xpad->bulk_out and kzalloc failed for xpad->bdata. This patch removes the initialization for error variable, assign the error code at the place the error happens instead. Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index f9fb7fa10af3..39c0265ca156 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -543,21 +543,25 @@ exit: static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { struct usb_endpoint_descriptor *ep_irq_out; - int error = -ENOMEM; + int error; if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX) return 0; xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, GFP_KERNEL, &xpad->odata_dma); - if (!xpad->odata) + if (!xpad->odata) { + error = -ENOMEM; goto fail1; + } mutex_init(&xpad->odata_mutex); xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); - if (!xpad->irq_out) + if (!xpad->irq_out) { + error = -ENOMEM; goto fail2; + } ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; usb_fill_int_urb(xpad->irq_out, xpad->udev, @@ -789,8 +793,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 i; - int error = -ENOMEM; + int i, error; for (i = 0; xpad_device[i].idVendor; i++) { if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) && @@ -800,17 +803,23 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL); input_dev = input_allocate_device(); - if (!xpad || !input_dev) + if (!xpad || !input_dev) { + error = -ENOMEM; goto fail1; + } xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN, GFP_KERNEL, &xpad->idata_dma); - if (!xpad->idata) + if (!xpad->idata) { + error = -ENOMEM; goto fail1; + } xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL); - if (!xpad->irq_in) + if (!xpad->irq_in) { + error = -ENOMEM; goto fail2; + } xpad->udev = udev; xpad->mapping = xpad_device[i].mapping; @@ -929,12 +938,16 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id * controller when it shows up */ xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); - if(!xpad->bulk_out) + if (!xpad->bulk_out) { + error = -ENOMEM; goto fail5; + } xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); - if(!xpad->bdata) + if (!xpad->bdata) { + error = -ENOMEM; goto fail6; + } xpad->bdata[2] = 0x08; switch (intf->cur_altsetting->desc.bInterfaceNumber) { -- cgit v1.2.3 From 6ff92a6db2083ecd1a8e2742d9397159fd880987 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 11 Nov 2010 21:43:17 -0800 Subject: Input: xpad - fix a memory leak In xpad_led_disconnect(), what we really want is to kfree(xpad_led). In xpad_disconnect(), add a missing kfree(xpad->bdata) to fix the memory leak. Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 39c0265ca156..e8b2ece3e01e 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -732,7 +732,7 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) if (xpad_led) { led_classdev_unregister(&xpad_led->led_cdev); - kfree(xpad_led->name); + kfree(xpad_led); } } #else @@ -921,7 +921,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id usb_set_intfdata(intf, xpad); /* - * Submit the int URB immediatly rather than waiting for open + * Submit the int URB immediately rather than waiting for open * because we get status messages from the device whether * or not any controllers are attached. In fact, it's * exactly the message that a controller has arrived that @@ -1000,6 +1000,7 @@ static void xpad_disconnect(struct usb_interface *intf) usb_free_urb(xpad->irq_in); usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); + kfree(xpad->bdata); kfree(xpad); } } -- cgit v1.2.3 From 161feb2417dd0c4324c2e8da24aaebd30a436d45 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 11 Nov 2010 21:47:42 -0800 Subject: Input: xpad - fix resource reclaim in xpad_probe error path Properly free the resources in error path by the reverse order of resource allocation. Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index e8b2ece3e01e..5fce72433932 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -760,8 +760,9 @@ static void xpad_close(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); - if(xpad->xtype != XTYPE_XBOX360W) + if (xpad->xtype != XTYPE_XBOX360W) usb_kill_urb(xpad->irq_in); + xpad_stop_output(xpad); } @@ -896,15 +897,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id error = xpad_init_output(intf, xpad); if (error) - goto fail2; + goto fail3; error = xpad_init_ff(xpad); if (error) - goto fail3; + goto fail4; error = xpad_led_probe(xpad); if (error) - goto fail3; + goto fail5; ep_irq_in = &intf->cur_altsetting->endpoint[0].desc; usb_fill_int_urb(xpad->irq_in, udev, @@ -916,7 +917,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id error = input_register_device(xpad->dev); if (error) - goto fail4; + goto fail6; usb_set_intfdata(intf, xpad); @@ -931,7 +932,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->irq_in->dev = xpad->udev; error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); if (error) - goto fail4; + goto fail7; /* * Setup the message to set the LEDs on the @@ -940,13 +941,13 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->bulk_out) { error = -ENOMEM; - goto fail5; + goto fail8; } xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); if (!xpad->bdata) { error = -ENOMEM; - goto fail6; + goto fail9; } xpad->bdata[2] = 0x08; @@ -972,10 +973,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id return 0; - fail6: usb_free_urb(xpad->bulk_out); - fail5: usb_kill_urb(xpad->irq_in); - fail4: usb_free_urb(xpad->irq_in); - fail3: xpad_deinit_output(xpad); + fail9: usb_free_urb(xpad->bulk_out); + fail8: usb_kill_urb(xpad->irq_in); + fail7: input_unregister_device(input_dev); + input_dev = NULL; + fail6: xpad_led_disconnect(xpad); + fail5: if (input_dev) + input_ff_destroy(input_dev); + fail4: xpad_deinit_output(xpad); + fail3: usb_free_urb(xpad->irq_in); fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); fail1: input_free_device(input_dev); kfree(xpad); -- cgit v1.2.3 From 2a0591596b302adc654a1caf6bd3d0063407ea4b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 11 Nov 2010 21:52:18 -0800 Subject: Input: xpad - remove useless check in xpad_remove ixpad can never be NULL here; if it is NULL we would not have been bound to the interface and then why would we be called? Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 5fce72433932..4875de9a3f88 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -993,22 +993,24 @@ static void xpad_disconnect(struct usb_interface *intf) { struct usb_xpad *xpad = usb_get_intfdata (intf); - usb_set_intfdata(intf, NULL); - if (xpad) { - xpad_led_disconnect(xpad); - input_unregister_device(xpad->dev); - xpad_deinit_output(xpad); - if (xpad->xtype == XTYPE_XBOX360W) { - usb_kill_urb(xpad->bulk_out); - usb_free_urb(xpad->bulk_out); - usb_kill_urb(xpad->irq_in); - } - usb_free_urb(xpad->irq_in); - usb_free_coherent(xpad->udev, XPAD_PKT_LEN, - xpad->idata, xpad->idata_dma); - kfree(xpad->bdata); - kfree(xpad); + xpad_led_disconnect(xpad); + input_unregister_device(xpad->dev); + xpad_deinit_output(xpad); + + if (xpad->xtype == XTYPE_XBOX360W) { + usb_kill_urb(xpad->bulk_out); + usb_free_urb(xpad->bulk_out); + usb_kill_urb(xpad->irq_in); } + + usb_free_urb(xpad->irq_in); + usb_free_coherent(xpad->udev, XPAD_PKT_LEN, + xpad->idata, xpad->idata_dma); + + kfree(xpad->bdata); + kfree(xpad); + + usb_set_intfdata(intf, NULL); } static struct usb_driver xpad_driver = { @@ -1020,10 +1022,7 @@ static struct usb_driver xpad_driver = { static int __init usb_xpad_init(void) { - int result = usb_register(&xpad_driver); - if (result == 0) - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); - return result; + return usb_register(&xpad_driver); } static void __exit usb_xpad_exit(void) -- cgit v1.2.3 From 10ee2ded629b1571cef1182728d6f65dbe4c7f79 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 11 Nov 2010 01:03:00 -0800 Subject: Input: pcf_keypad - convert to dev_op_ops Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pcf8574_keypad.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c index d1583aea1721..08be1a355956 100644 --- a/drivers/input/misc/pcf8574_keypad.c +++ b/drivers/input/misc/pcf8574_keypad.c @@ -169,19 +169,29 @@ static int __devexit pcf8574_kp_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int pcf8574_kp_resume(struct i2c_client *client) +static int pcf8574_kp_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); + enable_irq(client->irq); return 0; } -static int pcf8574_kp_suspend(struct i2c_client *client, pm_message_t mesg) +static int pcf8574_kp_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); + disable_irq(client->irq); return 0; } + +static const struct dev_pm_ops pcf8574_kp_pm_ops = { + .suspend = pcf8574_kp_suspend, + .resume = pcf8574_kp_resume, +}; + #else # define pcf8574_kp_resume NULL # define pcf8574_kp_suspend NULL @@ -197,11 +207,12 @@ static struct i2c_driver pcf8574_kp_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &pcf8574_kp_pm_ops, +#endif }, .probe = pcf8574_kp_probe, .remove = __devexit_p(pcf8574_kp_remove), - .suspend = pcf8574_kp_suspend, - .resume = pcf8574_kp_resume, .id_table = pcf8574_kp_id, }; -- cgit v1.2.3 From ca94ec43540ce5d93fd30a3bf88321b6f11ed51a Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 11 Nov 2010 22:19:57 -0800 Subject: Input: hgpk - support GlideSensor and PenTablet modes Add a "hgpk_mode" sysfs attribute that allows selection between 3 options: Mouse (the existing option), GlideSensor and PenTablet. GlideSensor is an enhanced protocol for the regular touchpad mode that additionally reports pressure and uses absolute coordinates. We suspect that it may be more reliable than mouse mode in some environments. PenTablet mode puts the touchpad into resistive mode, you must then use a stylus as an input. We suspect this is the most reliable way to drive the touchpad. The GlideSensor and PenTablet devices expose themselves with the intention of being combined with the synaptics X11 input driver. Based on earlier work by Paul Fox. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/hgpk.c | 467 +++++++++++++++++++++++++++++++++---- drivers/input/mouse/hgpk.h | 16 ++ drivers/input/mouse/psmouse-base.c | 1 + 3 files changed, 441 insertions(+), 43 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 1d2205b24800..3d33d958a122 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -69,6 +69,32 @@ module_param(post_interrupt_delay, int, 0644); MODULE_PARM_DESC(post_interrupt_delay, "delay (ms) before recal after recal interrupt detected"); +static char hgpk_mode_name[16]; +module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644); +MODULE_PARM_DESC(hgpk_mode, + "default hgpk mode: mouse, glidesensor or pentablet"); + +static int hgpk_default_mode = HGPK_MODE_MOUSE; + +static const char * const hgpk_mode_names[] = { + [HGPK_MODE_MOUSE] = "Mouse", + [HGPK_MODE_GLIDESENSOR] = "GlideSensor", + [HGPK_MODE_PENTABLET] = "PenTablet", +}; + +static int hgpk_mode_from_name(const char *buf, int len) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) { + const char *name = hgpk_mode_names[i]; + if (strlen(name) == len && !strncasecmp(name, buf, len)) + return i; + } + + return HGPK_MODE_INVALID; +} + /* * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" * above the pad and still have it send packets. This causes a jump cursor @@ -143,23 +169,139 @@ static void hgpk_spewing_hack(struct psmouse *psmouse, * swr/swl are the left/right buttons. * x-neg/y-neg are the x and y delta negative bits * x-over/y-over are the x and y overflow bits + * + * --- + * + * HGPK Advanced Mode - single-mode format + * + * byte 0(PT): 1 1 0 0 1 1 1 1 + * byte 0(GS): 1 1 1 1 1 1 1 1 + * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 + * byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0 + * byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw + * byte 3: 0 y9 y8 y7 1 0 swr swl + * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 + * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 + * + * ?'s are not defined in the protocol spec, may vary between models. + * + * swr/swl are the left/right buttons. + * + * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a + * pen/finger */ -static int hgpk_validate_byte(unsigned char *packet) +static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet) { - return (packet[0] & 0x0C) != 0x08; + struct hgpk_data *priv = psmouse->private; + int pktcnt = psmouse->pktcnt; + bool valid; + + switch (priv->mode) { + case HGPK_MODE_MOUSE: + valid = (packet[0] & 0x0C) == 0x08; + break; + + case HGPK_MODE_GLIDESENSOR: + valid = pktcnt == 1 ? + packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80); + break; + + case HGPK_MODE_PENTABLET: + valid = pktcnt == 1 ? + packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80); + break; + + default: + valid = false; + break; + } + + if (!valid) + hgpk_dbg(psmouse, + "bad data, mode %d (%d) %02x %02x %02x %02x %02x %02x\n", + priv->mode, pktcnt, + psmouse->packet[0], psmouse->packet[1], + psmouse->packet[2], psmouse->packet[3], + psmouse->packet[4], psmouse->packet[5]); + + return valid; } -static void hgpk_process_packet(struct psmouse *psmouse) +static void hgpk_process_advanced_packet(struct psmouse *psmouse) { - struct input_dev *dev = psmouse->dev; + struct hgpk_data *priv = psmouse->private; + struct input_dev *idev = psmouse->dev; unsigned char *packet = psmouse->packet; - int x, y, left, right; + int down = !!(packet[2] & 2); + int left = !!(packet[3] & 1); + int right = !!(packet[3] & 2); + int x = packet[1] | ((packet[2] & 0x78) << 4); + int y = packet[4] | ((packet[3] & 0x70) << 3); + + if (priv->mode == HGPK_MODE_GLIDESENSOR) { + int pt_down = !!(packet[2] & 1); + int finger_down = !!(packet[2] & 2); + int z = packet[5]; + + input_report_abs(idev, ABS_PRESSURE, z); + if (tpdebug) + hgpk_dbg(psmouse, "pd=%d fd=%d z=%d", + pt_down, finger_down, z); + } else { + /* + * PenTablet mode does not report pressure, so we don't + * report it here + */ + if (tpdebug) + hgpk_dbg(psmouse, "pd=%d ", down); + } - left = packet[0] & 1; - right = (packet[0] >> 1) & 1; + if (tpdebug) + hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); - x = packet[1] - ((packet[0] << 4) & 0x100); - y = ((packet[0] << 3) & 0x100) - packet[2]; + input_report_key(idev, BTN_TOUCH, down); + input_report_key(idev, BTN_LEFT, left); + input_report_key(idev, BTN_RIGHT, right); + + /* + * If this packet says that the finger was removed, reset our position + * tracking so that we don't erroneously detect a jump on next press. + */ + if (!down) + priv->abs_x = priv->abs_y = -1; + + /* + * Report position if finger/pen is down, but weed out duplicate + * packets (we get quite a few in this mode, and they mess up our + * jump detection. + */ + if (down && (x != priv->abs_x || y != priv->abs_y)) { + + /* Don't apply hacks in PT mode, it seems reliable */ + if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { + hgpk_jumpy_hack(psmouse, + priv->abs_x - x, priv->abs_y - y); + hgpk_spewing_hack(psmouse, left, right, + priv->abs_x - x, priv->abs_y - y); + } + + input_report_abs(idev, ABS_X, x); + input_report_abs(idev, ABS_Y, y); + priv->abs_x = x; + priv->abs_y = y; + } + + input_sync(idev); +} + +static void hgpk_process_simple_packet(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + unsigned char *packet = psmouse->packet; + int left = packet[0] & 1; + int right = (packet[0] >> 1) & 1; + int x = packet[1] - ((packet[0] << 4) & 0x100); + int y = ((packet[0] << 3) & 0x100) - packet[2]; hgpk_jumpy_hack(psmouse, x, y); hgpk_spewing_hack(psmouse, left, right, x, y); @@ -180,15 +322,14 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) { struct hgpk_data *priv = psmouse->private; - if (hgpk_validate_byte(psmouse->packet)) { - hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n", - __func__, psmouse->pktcnt, psmouse->packet[0], - psmouse->packet[1], psmouse->packet[2]); + if (!hgpk_is_byte_valid(psmouse, psmouse->packet)) return PSMOUSE_BAD_DATA; - } if (psmouse->pktcnt >= psmouse->pktsize) { - hgpk_process_packet(psmouse); + if (priv->mode == HGPK_MODE_MOUSE) + hgpk_process_simple_packet(psmouse); + else + hgpk_process_advanced_packet(psmouse); return PSMOUSE_FULL_PACKET; } @@ -210,10 +351,161 @@ static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) return PSMOUSE_GOOD_DATA; } +static int hgpk_select_mode(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct hgpk_data *priv = psmouse->private; + int i; + int cmd; + + /* + * 4 disables to enable advanced mode + * then 3 0xf2 bytes as the preamble for GS/PT selection + */ + const int advanced_init[] = { + PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, + PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, + 0xf2, 0xf2, 0xf2, + }; + + switch (priv->mode) { + case HGPK_MODE_MOUSE: + psmouse->pktsize = 3; + break; + + case HGPK_MODE_GLIDESENSOR: + case HGPK_MODE_PENTABLET: + psmouse->pktsize = 6; + + /* Switch to 'Advanced mode.', four disables in a row. */ + for (i = 0; i < ARRAY_SIZE(advanced_init); i++) + if (ps2_command(ps2dev, NULL, advanced_init[i])) + return -EIO; + + /* select between GlideSensor (mouse) or PenTablet */ + cmd = priv->mode == HGPK_MODE_GLIDESENSOR ? + PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21; + + if (ps2_command(ps2dev, NULL, cmd)) + return -EIO; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void hgpk_setup_input_device(struct input_dev *input, + struct input_dev *old_input, + enum hgpk_mode mode) +{ + if (old_input) { + input->name = old_input->name; + input->phys = old_input->phys; + input->id = old_input->id; + input->dev.parent = old_input->dev.parent; + } + + memset(input->evbit, 0, sizeof(input->evbit)); + memset(input->relbit, 0, sizeof(input->relbit)); + memset(input->keybit, 0, sizeof(input->keybit)); + + /* All modes report left and right buttons */ + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + + switch (mode) { + case HGPK_MODE_MOUSE: + __set_bit(EV_REL, input->evbit); + __set_bit(REL_X, input->relbit); + __set_bit(REL_Y, input->relbit); + break; + + case HGPK_MODE_GLIDESENSOR: + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + __set_bit(EV_ABS, input->evbit); + + /* GlideSensor has pressure sensor, PenTablet does not */ + input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0); + + /* From device specs */ + input_set_abs_params(input, ABS_X, 0, 399, 0, 0); + input_set_abs_params(input, ABS_Y, 0, 290, 0, 0); + + /* Calculated by hand based on usable size (52mm x 38mm) */ + input_abs_set_res(input, ABS_X, 8); + input_abs_set_res(input, ABS_Y, 8); + break; + + case HGPK_MODE_PENTABLET: + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + __set_bit(EV_ABS, input->evbit); + + /* From device specs */ + input_set_abs_params(input, ABS_X, 0, 999, 0, 0); + input_set_abs_params(input, ABS_Y, 5, 239, 0, 0); + + /* Calculated by hand based on usable size (156mm x 38mm) */ + input_abs_set_res(input, ABS_X, 6); + input_abs_set_res(input, ABS_Y, 8); + break; + + default: + BUG(); + } +} + +static void hgpk_reset_hack_state(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + priv->abs_x = priv->abs_y = -1; +} + +static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) +{ + int err; + + psmouse_reset(psmouse); + + if (recalibrate) { + struct ps2dev *ps2dev = &psmouse->ps2dev; + + /* send the recalibrate request */ + if (ps2_command(ps2dev, NULL, 0xf5) || + ps2_command(ps2dev, NULL, 0xf5) || + ps2_command(ps2dev, NULL, 0xe6) || + ps2_command(ps2dev, NULL, 0xf5)) { + return -1; + } + + /* according to ALPS, 150mS is required for recalibration */ + msleep(150); + } + + err = hgpk_select_mode(psmouse); + if (err) { + hgpk_err(psmouse, "failed to select mode\n"); + return err; + } + + hgpk_reset_hack_state(psmouse); + + return 0; +} + static int hgpk_force_recalibrate(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; struct hgpk_data *priv = psmouse->private; + int err; /* C-series touchpads added the recalibrate command */ if (psmouse->model < HGPK_MODEL_C) @@ -223,20 +515,12 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); /* start by resetting the device */ - psmouse_reset(psmouse); - - /* send the recalibrate request */ - if (ps2_command(ps2dev, NULL, 0xf5) || - ps2_command(ps2dev, NULL, 0xf5) || - ps2_command(ps2dev, NULL, 0xe6) || - ps2_command(ps2dev, NULL, 0xf5)) { - return -1; - } - - /* according to ALPS, 150mS is required for recalibration */ - msleep(150); + err = hgpk_reset_device(psmouse, true); + if (err) + return err; - /* XXX: If a finger is down during this delay, recalibration will + /* + * XXX: If a finger is down during this delay, recalibration will * detect capacitance incorrectly. This is a hardware bug, and * we don't have a good way to deal with it. The 2s window stuff * (below) is our best option for now. @@ -247,12 +531,13 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - /* After we recalibrate, we shouldn't get any packets for 2s. If + /* + * After we recalibrate, we shouldn't get any packets for 2s. If * we do, it's likely that someone's finger was on the touchpad. * If someone's finger *was* on the touchpad, it's probably * miscalibrated. So, we should schedule another recalibration */ - priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time); + priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time); return 0; } @@ -266,6 +551,7 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable) { struct ps2dev *ps2dev = &psmouse->ps2dev; int timeo; + int err; /* Added on D-series touchpads */ if (psmouse->model < HGPK_MODEL_D) @@ -288,7 +574,11 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable) msleep(50); } - psmouse_reset(psmouse); + err = hgpk_reset_device(psmouse, false); + if (err) { + hgpk_err(psmouse, "Failed to reset device!\n"); + return err; + } /* should be all set, enable the touchpad */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); @@ -319,17 +609,17 @@ static int hgpk_poll(struct psmouse *psmouse) static int hgpk_reconnect(struct psmouse *psmouse) { - /* During suspend/resume the ps2 rails remain powered. We don't want + /* + * During suspend/resume the ps2 rails remain powered. We don't want * to do a reset because it's flush data out of buffers; however, - * earlier prototypes (B1) had some brokenness that required a reset. */ + * earlier prototypes (B1) had some brokenness that required a reset. + */ if (olpc_board_at_least(olpc_board(0xb2))) if (psmouse->ps2dev.serio->dev.power.power_state.event != PM_EVENT_ON) return 0; - psmouse_reset(psmouse); - - return 0; + return hgpk_reset_device(psmouse, false); } static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) @@ -366,6 +656,65 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, hgpk_show_powered, hgpk_set_powered, false); +static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf) +{ + struct hgpk_data *priv = psmouse->private; + + return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]); +} + +static ssize_t attr_set_mode(struct psmouse *psmouse, void *data, + const char *buf, size_t len) +{ + struct hgpk_data *priv = psmouse->private; + enum hgpk_mode old_mode = priv->mode; + enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len); + struct input_dev *old_dev = psmouse->dev; + struct input_dev *new_dev; + int err; + + if (new_mode == HGPK_MODE_INVALID) + return -EINVAL; + + if (old_mode == new_mode) + return len; + + new_dev = input_allocate_device(); + if (!new_dev) + return -ENOMEM; + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + + /* Switch device into the new mode */ + priv->mode = new_mode; + err = hgpk_reset_device(psmouse, false); + if (err) + goto err_try_restore; + + hgpk_setup_input_device(new_dev, old_dev, new_mode); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + + err = input_register_device(new_dev); + if (err) + goto err_try_restore; + + psmouse->dev = new_dev; + input_unregister_device(old_dev); + + return len; + +err_try_restore: + input_free_device(new_dev); + priv->mode = old_mode; + hgpk_reset_device(psmouse, false); + + return err; +} + +PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL, + attr_show_mode, attr_set_mode); + static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse, void *data, char *buf) { @@ -401,6 +750,8 @@ static void hgpk_disconnect(struct psmouse *psmouse) device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_powered.dattr); + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_hgpk_mode.dattr); if (psmouse->model >= HGPK_MODEL_C) device_remove_file(&psmouse->ps2dev.serio->dev, @@ -424,6 +775,7 @@ static void hgpk_recalib_work(struct work_struct *work) static int hgpk_register(struct psmouse *psmouse) { + struct hgpk_data *priv = psmouse->private; int err; /* register handlers */ @@ -431,13 +783,14 @@ static int hgpk_register(struct psmouse *psmouse) psmouse->poll = hgpk_poll; psmouse->disconnect = hgpk_disconnect; psmouse->reconnect = hgpk_reconnect; - psmouse->pktsize = 3; /* Disable the idle resync. */ psmouse->resync_time = 0; /* Reset after a lot of bad bytes. */ psmouse->resetafter = 1024; + hgpk_setup_input_device(psmouse->dev, NULL, priv->mode); + err = device_create_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_powered.dattr); if (err) { @@ -445,6 +798,13 @@ static int hgpk_register(struct psmouse *psmouse) return err; } + err = device_create_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_hgpk_mode.dattr); + if (err) { + hgpk_err(psmouse, "Failed creating 'hgpk_mode' sysfs node\n"); + goto err_remove_powered; + } + /* C-series touchpads added the recalibrate command */ if (psmouse->model >= HGPK_MODEL_C) { err = device_create_file(&psmouse->ps2dev.serio->dev, @@ -452,30 +812,40 @@ static int hgpk_register(struct psmouse *psmouse) if (err) { hgpk_err(psmouse, "Failed creating 'recalibrate' sysfs node\n"); - device_remove_file(&psmouse->ps2dev.serio->dev, - &psmouse_attr_powered.dattr); - return err; + goto err_remove_mode; } } return 0; + +err_remove_mode: + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_hgpk_mode.dattr); +err_remove_powered: + device_remove_file(&psmouse->ps2dev.serio->dev, + &psmouse_attr_powered.dattr); + return err; } int hgpk_init(struct psmouse *psmouse) { struct hgpk_data *priv; - int err = -ENOMEM; + int err; priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); - if (!priv) + if (!priv) { + err = -ENOMEM; goto alloc_fail; + } psmouse->private = priv; + priv->psmouse = psmouse; priv->powered = true; + priv->mode = hgpk_default_mode; INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); - err = psmouse_reset(psmouse); + err = hgpk_reset_device(psmouse, false); if (err) goto init_fail; @@ -531,3 +901,14 @@ int hgpk_detect(struct psmouse *psmouse, bool set_properties) return 0; } + +void hgpk_module_init(void) +{ + hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name, + strlen(hgpk_mode_name)); + if (hgpk_default_mode == HGPK_MODE_INVALID) { + hgpk_default_mode = HGPK_MODE_MOUSE; + strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE], + sizeof(hgpk_mode_name)); + } +} diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h index d61cfd3ee9cb..01c983bb8465 100644 --- a/drivers/input/mouse/hgpk.h +++ b/drivers/input/mouse/hgpk.h @@ -5,6 +5,9 @@ #ifndef _HGPK_H #define _HGPK_H +#define HGPK_GS 0xff /* The GlideSensor */ +#define HGPK_PT 0xcf /* The PenTablet */ + enum hgpk_model_t { HGPK_MODEL_PREA = 0x0a, /* pre-B1s */ HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ @@ -13,12 +16,21 @@ enum hgpk_model_t { HGPK_MODEL_D = 0x50, /* C1, mass production */ }; +enum hgpk_mode { + HGPK_MODE_MOUSE, + HGPK_MODE_GLIDESENSOR, + HGPK_MODE_PENTABLET, + HGPK_MODE_INVALID +}; + struct hgpk_data { struct psmouse *psmouse; + enum hgpk_mode mode; bool powered; int count, x_tally, y_tally; /* hardware workaround stuff */ unsigned long recalib_window; struct delayed_work recalib_wq; + int abs_x, abs_y; }; #define hgpk_dbg(psmouse, format, arg...) \ @@ -33,9 +45,13 @@ struct hgpk_data { dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg) #ifdef CONFIG_MOUSE_PS2_OLPC +void hgpk_module_init(void); int hgpk_detect(struct psmouse *psmouse, bool set_properties); int hgpk_init(struct psmouse *psmouse); #else +static inline void hgpk_module_init(void) +{ +} static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties) { return -ENODEV; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index cd9d0c97e429..3f74baee102b 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1711,6 +1711,7 @@ static int __init psmouse_init(void) lifebook_module_init(); synaptics_module_init(); + hgpk_module_init(); kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); if (!kpsmoused_wq) { -- cgit v1.2.3 From c0dc8342656a1425c31dcc505072f2387f0f0c92 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 11 Nov 2010 22:20:02 -0800 Subject: Input: hgpk - rework spew detection The old implementation of spew detection simply tracked the overall position delta of the cursor over every 100 packets. We found that this causes occasional false positives in spew detection, and also that the conditions of the spewy packets are perhaps more fixed than we once thought. Rework the spew detection to look for packets of specific small delta, and only recalibrating if the overall movement delta stays within expected bounds. Also discard duplicate packets in the advanced mode, which appear to be very common. If we don't, the spew detection kicks in far too early. If we get a large spew of duplicates, request a recalibration straight up. Based on earlier work by Paul Fox. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/hgpk.c | 122 +++++++++++++++++++++++++++++++++------------ drivers/input/mouse/hgpk.h | 13 ++++- 2 files changed, 103 insertions(+), 32 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 3d33d958a122..222594742c39 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -54,7 +54,7 @@ module_param(jumpy_delay, int, 0644); MODULE_PARM_DESC(jumpy_delay, "delay (ms) before recal after jumpiness detected"); -static int spew_delay = 1000; +static int spew_delay = 1; module_param(spew_delay, int, 0644); MODULE_PARM_DESC(spew_delay, "delay (ms) before recal after packet spew detected"); @@ -117,6 +117,23 @@ static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) } } +static void hgpk_reset_spew_detection(struct hgpk_data *priv) +{ + priv->spew_count = 0; + priv->dupe_count = 0; + priv->x_tally = 0; + priv->y_tally = 0; + priv->spew_flag = NO_SPEW; +} + +static void hgpk_reset_hack_state(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; + + priv->abs_x = priv->abs_y = -1; + hgpk_reset_spew_detection(priv); +} + /* * We have no idea why this particular hardware bug occurs. The touchpad * will randomly start spewing packets without anything touching the @@ -142,20 +159,57 @@ static void hgpk_spewing_hack(struct psmouse *psmouse, if (l || r) return; + /* don't track spew if the workaround feature has been turned off */ + if (!spew_delay) + return; + + if (abs(x) > 3 || abs(y) > 3) { + /* no spew, or spew ended */ + hgpk_reset_spew_detection(priv); + return; + } + + /* Keep a tally of the overall delta to the cursor position caused by + * the spew */ priv->x_tally += x; priv->y_tally += y; - if (++priv->count > 100) { + switch (priv->spew_flag) { + case NO_SPEW: + /* we're not spewing, but this packet might be the start */ + priv->spew_flag = MAYBE_SPEWING; + + /* fall-through */ + + case MAYBE_SPEWING: + priv->spew_count++; + + if (priv->spew_count < SPEW_WATCH_COUNT) + break; + + /* excessive spew detected, request recalibration */ + priv->spew_flag = SPEW_DETECTED; + + /* fall-through */ + + case SPEW_DETECTED: + /* only recalibrate when the overall delta to the cursor + * is really small. if the spew is causing significant cursor + * movement, it is probably a case of the user moving the + * cursor very slowly across the screen. */ if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { - hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n", + hgpk_err(psmouse, "packet spew detected (%d,%d)\n", priv->x_tally, priv->y_tally); + priv->spew_flag = RECALIBRATING; psmouse_queue_work(psmouse, &priv->recalib_wq, msecs_to_jiffies(spew_delay)); } - /* reset every 100 packets */ - priv->count = 0; - priv->x_tally = 0; - priv->y_tally = 0; + + break; + case RECALIBRATING: + /* we already detected a spew and requested a recalibration, + * just wait for the queue to kick into action. */ + break; } } @@ -267,30 +321,43 @@ static void hgpk_process_advanced_packet(struct psmouse *psmouse) * If this packet says that the finger was removed, reset our position * tracking so that we don't erroneously detect a jump on next press. */ - if (!down) - priv->abs_x = priv->abs_y = -1; + if (!down) { + hgpk_reset_hack_state(priv); + goto done; + } /* - * Report position if finger/pen is down, but weed out duplicate - * packets (we get quite a few in this mode, and they mess up our - * jump detection. + * Weed out duplicate packets (we get quite a few, and they mess up + * our jump detection) */ - if (down && (x != priv->abs_x || y != priv->abs_y)) { - - /* Don't apply hacks in PT mode, it seems reliable */ - if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { - hgpk_jumpy_hack(psmouse, - priv->abs_x - x, priv->abs_y - y); - hgpk_spewing_hack(psmouse, left, right, - priv->abs_x - x, priv->abs_y - y); + if (x == priv->abs_x && y == priv->abs_y) { + if (++priv->dupe_count > SPEW_WATCH_COUNT) { + if (tpdebug) + hgpk_dbg(psmouse, "hard spew detected\n"); + priv->spew_flag = RECALIBRATING; + psmouse_queue_work(psmouse, &priv->recalib_wq, + msecs_to_jiffies(spew_delay)); } + goto done; + } - input_report_abs(idev, ABS_X, x); - input_report_abs(idev, ABS_Y, y); - priv->abs_x = x; - priv->abs_y = y; + /* not a duplicate, continue with position reporting */ + priv->dupe_count = 0; + + /* Don't apply hacks in PT mode, it seems reliable */ + if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { + hgpk_jumpy_hack(psmouse, + priv->abs_x - x, priv->abs_y - y); + hgpk_spewing_hack(psmouse, left, right, + priv->abs_x - x, priv->abs_y - y); } + input_report_abs(idev, ABS_X, x); + input_report_abs(idev, ABS_Y, y); + priv->abs_x = x; + priv->abs_y = y; + +done: input_sync(idev); } @@ -462,13 +529,6 @@ static void hgpk_setup_input_device(struct input_dev *input, } } -static void hgpk_reset_hack_state(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - - priv->abs_x = priv->abs_y = -1; -} - static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) { int err; diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h index 01c983bb8465..bccdb26dca49 100644 --- a/drivers/input/mouse/hgpk.h +++ b/drivers/input/mouse/hgpk.h @@ -16,6 +16,15 @@ enum hgpk_model_t { HGPK_MODEL_D = 0x50, /* C1, mass production */ }; +enum hgpk_spew_flag { + NO_SPEW, + MAYBE_SPEWING, + SPEW_DETECTED, + RECALIBRATING, +}; + +#define SPEW_WATCH_COUNT 42 /* at 12ms/packet, this is 1/2 second */ + enum hgpk_mode { HGPK_MODE_MOUSE, HGPK_MODE_GLIDESENSOR, @@ -27,10 +36,12 @@ struct hgpk_data { struct psmouse *psmouse; enum hgpk_mode mode; bool powered; - int count, x_tally, y_tally; /* hardware workaround stuff */ + enum hgpk_spew_flag spew_flag; + int spew_count, x_tally, y_tally; /* spew detection */ unsigned long recalib_window; struct delayed_work recalib_wq; int abs_x, abs_y; + int dupe_count; }; #define hgpk_dbg(psmouse, format, arg...) \ -- cgit v1.2.3 From a309cdc778b9eece59b34e9e1c26e41476dbbcd6 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 11 Nov 2010 22:20:03 -0800 Subject: Input: hgpk - extend jumpiness detection In addition to forcing recalibrations upon detection of cursor jumps (and performing them quicker than before), detect and discard errant 'jump' packets caused by a firmware bug, which are then repeated with each one being approximately half the delta of the one previously (as if it is averaging out) Based on original work by Paul Fox. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/hgpk.c | 106 ++++++++++++++++++++++++++++++++++++--------- drivers/input/mouse/hgpk.h | 2 + 2 files changed, 88 insertions(+), 20 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 222594742c39..b54f074ec307 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -40,6 +40,8 @@ #include "psmouse.h" #include "hgpk.h" +#define ILLEGAL_XY 999999 + static bool tpdebug; module_param(tpdebug, bool, 0644); MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); @@ -47,9 +49,10 @@ MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); static int recalib_delta = 100; module_param(recalib_delta, int, 0644); MODULE_PARM_DESC(recalib_delta, - "packets containing a delta this large will cause a recalibration."); + "packets containing a delta this large will be discarded, and a " + "recalibration may be scheduled."); -static int jumpy_delay = 1000; +static int jumpy_delay = 20; module_param(jumpy_delay, int, 0644); MODULE_PARM_DESC(jumpy_delay, "delay (ms) before recal after jumpiness detected"); @@ -96,25 +99,76 @@ static int hgpk_mode_from_name(const char *buf, int len) } /* - * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" - * above the pad and still have it send packets. This causes a jump cursor - * when one places their finger on the pad. We can probably detect the - * jump as we see a large deltas (>= 100px). In mouse mode, I've been - * unable to even come close to 100px deltas during normal usage, so I think - * this threshold is safe. If a large delta occurs, trigger a recalibration. + * see if new value is within 20% of half of old value + */ +static int approx_half(int curr, int prev) +{ + int belowhalf, abovehalf; + + if (curr < 5 || prev < 5) + return 0; + + belowhalf = (prev * 8) / 20; + abovehalf = (prev * 12) / 20; + + return belowhalf < curr && curr <= abovehalf; +} + +/* + * Throw out oddly large delta packets, and any that immediately follow whose + * values are each approximately half of the previous. It seems that the ALPS + * firmware emits errant packets, and they get averaged out slowly. */ -static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) +static int hgpk_discard_decay_hack(struct psmouse *psmouse, int x, int y) { struct hgpk_data *priv = psmouse->private; + int avx, avy; + bool do_recal = false; + + avx = abs(x); + avy = abs(y); + + /* discard if too big, or half that but > 4 times the prev delta */ + if (avx > recalib_delta || + (avx > recalib_delta / 2 && ((avx / 4) > priv->xlast))) { + hgpk_err(psmouse, "detected %dpx jump in x\n", x); + priv->xbigj = avx; + } else if (approx_half(avx, priv->xbigj)) { + hgpk_err(psmouse, "detected secondary %dpx jump in x\n", x); + priv->xbigj = avx; + priv->xsaw_secondary++; + } else { + if (priv->xbigj && priv->xsaw_secondary > 1) + do_recal = true; + priv->xbigj = 0; + priv->xsaw_secondary = 0; + } - if (abs(x) > recalib_delta || abs(y) > recalib_delta) { - hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n", - recalib_delta, x, y); - /* My car gets forty rods to the hogshead and that's the - * way I likes it! */ + if (avy > recalib_delta || + (avy > recalib_delta / 2 && ((avy / 4) > priv->ylast))) { + hgpk_err(psmouse, "detected %dpx jump in y\n", y); + priv->ybigj = avy; + } else if (approx_half(avy, priv->ybigj)) { + hgpk_err(psmouse, "detected secondary %dpx jump in y\n", y); + priv->ybigj = avy; + priv->ysaw_secondary++; + } else { + if (priv->ybigj && priv->ysaw_secondary > 1) + do_recal = true; + priv->ybigj = 0; + priv->ysaw_secondary = 0; + } + + priv->xlast = avx; + priv->ylast = avy; + + if (do_recal && jumpy_delay) { + hgpk_err(psmouse, "scheduling recalibration\n"); psmouse_queue_work(psmouse, &priv->recalib_wq, msecs_to_jiffies(jumpy_delay)); } + + return priv->xbigj || priv->ybigj; } static void hgpk_reset_spew_detection(struct hgpk_data *priv) @@ -131,6 +185,9 @@ static void hgpk_reset_hack_state(struct psmouse *psmouse) struct hgpk_data *priv = psmouse->private; priv->abs_x = priv->abs_y = -1; + priv->xlast = priv->ylast = ILLEGAL_XY; + priv->xbigj = priv->ybigj = 0; + priv->xsaw_secondary = priv->ysaw_secondary = 0; hgpk_reset_spew_detection(priv); } @@ -322,7 +379,7 @@ static void hgpk_process_advanced_packet(struct psmouse *psmouse) * tracking so that we don't erroneously detect a jump on next press. */ if (!down) { - hgpk_reset_hack_state(priv); + hgpk_reset_hack_state(psmouse); goto done; } @@ -346,10 +403,14 @@ static void hgpk_process_advanced_packet(struct psmouse *psmouse) /* Don't apply hacks in PT mode, it seems reliable */ if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { - hgpk_jumpy_hack(psmouse, - priv->abs_x - x, priv->abs_y - y); - hgpk_spewing_hack(psmouse, left, right, - priv->abs_x - x, priv->abs_y - y); + int x_diff = priv->abs_x - x; + int y_diff = priv->abs_y - y; + if (hgpk_discard_decay_hack(psmouse, x_diff, y_diff)) { + if (tpdebug) + hgpk_dbg(psmouse, "discarding\n"); + goto done; + } + hgpk_spewing_hack(psmouse, left, right, x_diff, y_diff); } input_report_abs(idev, ABS_X, x); @@ -370,7 +431,12 @@ static void hgpk_process_simple_packet(struct psmouse *psmouse) int x = packet[1] - ((packet[0] << 4) & 0x100); int y = ((packet[0] << 3) & 0x100) - packet[2]; - hgpk_jumpy_hack(psmouse, x, y); + if (hgpk_discard_decay_hack(psmouse, x, y)) { + if (tpdebug) + hgpk_dbg(psmouse, "discarding\n"); + return; + } + hgpk_spewing_hack(psmouse, left, right, x, y); if (tpdebug) diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h index bccdb26dca49..311c0e87fcbf 100644 --- a/drivers/input/mouse/hgpk.h +++ b/drivers/input/mouse/hgpk.h @@ -42,6 +42,8 @@ struct hgpk_data { struct delayed_work recalib_wq; int abs_x, abs_y; int dupe_count; + int xbigj, ybigj, xlast, ylast; /* jumpiness detection */ + int xsaw_secondary, ysaw_secondary; /* jumpiness detection */ }; #define hgpk_dbg(psmouse, format, arg...) \ -- cgit v1.2.3 From 67f56bb0f4997b55291c162077e02e4f29639fc2 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 15 Nov 2010 01:28:54 -0800 Subject: Input: hgpk - detect simple mode overflows Based on work by Paul Fox. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/hgpk.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index b54f074ec307..7f6cb32d177a 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -431,6 +431,11 @@ static void hgpk_process_simple_packet(struct psmouse *psmouse) int x = packet[1] - ((packet[0] << 4) & 0x100); int y = ((packet[0] << 3) & 0x100) - packet[2]; + if (packet[0] & 0xc0) + hgpk_dbg(psmouse, + "overflow -- 0x%02x 0x%02x 0x%02x\n", + packet[0], packet[1], packet[2]); + if (hgpk_discard_decay_hack(psmouse, x, y)) { if (tpdebug) hgpk_dbg(psmouse, "discarding\n"); -- cgit v1.2.3 From 34caed2082105a6d9f5aaba1cf4e02760cbee14e Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 15 Nov 2010 01:33:22 -0800 Subject: Input: hgpk - recalibration tweaks Disable the recalibration guard where new recalibrations are triggered if we detect a packet too soon after calibrating - we found that this results in erroneous recalibrations, and if the recalibration failed then the rest of our badness-detection code will request another. Add a module option disabling all of the recalibration code, in case an OLPC deployment thinks all of the workarounds we have are doing more damage than good and wants to experiment with them all disabled. Based on work by Paul Fox. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/hgpk.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 7f6cb32d177a..1beb5da4f0f3 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -62,16 +62,20 @@ module_param(spew_delay, int, 0644); MODULE_PARM_DESC(spew_delay, "delay (ms) before recal after packet spew detected"); -static int recal_guard_time = 2000; +static int recal_guard_time; module_param(recal_guard_time, int, 0644); MODULE_PARM_DESC(recal_guard_time, "interval (ms) during which recal will be restarted if packet received"); -static int post_interrupt_delay = 1000; +static int post_interrupt_delay = 40; module_param(post_interrupt_delay, int, 0644); MODULE_PARM_DESC(post_interrupt_delay, "delay (ms) before recal after recal interrupt detected"); +static bool autorecal = true; +module_param(autorecal, bool, 0644); +MODULE_PARM_DESC(autorecal, "enable recalibration in the driver"); + static char hgpk_mode_name[16]; module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644); MODULE_PARM_DESC(hgpk_mode, @@ -642,6 +646,13 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) if (psmouse->model < HGPK_MODEL_C) return 0; + if (!autorecal) { + hgpk_dbg(psmouse, "recalibrations disabled, ignoring\n"); + return 0; + } + + hgpk_dbg(psmouse, "recalibrating touchpad..\n"); + /* we don't want to race with the irq handler, nor with resyncs */ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); @@ -662,13 +673,17 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + if (tpdebug) + hgpk_dbg(psmouse, "touchpad reactivated\n"); + /* - * After we recalibrate, we shouldn't get any packets for 2s. If - * we do, it's likely that someone's finger was on the touchpad. - * If someone's finger *was* on the touchpad, it's probably - * miscalibrated. So, we should schedule another recalibration + * If we get packets right away after recalibrating, it's likely + * that a finger was on the touchpad. If so, it's probably + * miscalibrated, so we optionally schedule another. */ - priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time); + if (recal_guard_time) + priv->recalib_window = jiffies + + msecs_to_jiffies(recal_guard_time); return 0; } @@ -898,8 +913,6 @@ static void hgpk_recalib_work(struct work_struct *work) struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); struct psmouse *psmouse = priv->psmouse; - hgpk_dbg(psmouse, "recalibrating touchpad..\n"); - if (hgpk_force_recalibrate(psmouse)) hgpk_err(psmouse, "recalibration failed!\n"); } -- cgit v1.2.3 From 20a4c261ad9cec39942257b1f91765a4b238db05 Mon Sep 17 00:00:00 2001 From: Paul Fox Date: Mon, 15 Nov 2010 01:33:24 -0800 Subject: Input: hgpk - fix powersave mode Recent testing of this codepath showed that it wasn't working, perhaps due to changes within the input layer. This fixes it. Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/hgpk.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c index 1beb5da4f0f3..95577c15ae56 100644 --- a/drivers/input/mouse/hgpk.c +++ b/drivers/input/mouse/hgpk.c @@ -689,11 +689,15 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) } /* - * This kills power to the touchpad; according to ALPS, current consumption - * goes down to 50uA after running this. To turn power back on, we drive - * MS-DAT low. + * This puts the touchpad in a power saving mode; according to ALPS, current + * consumption goes down to 50uA after running this. To turn power back on, + * we drive MS-DAT low. Measuring with a 1mA resolution ammeter says that + * the current on the SUS_3.3V rail drops from 3mA or 4mA to 0 when we do this. + * + * We have no formal spec that details this operation -- the low-power + * sequence came from a long-lost email trail. */ -static int hgpk_toggle_power(struct psmouse *psmouse, int enable) +static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable) { struct ps2dev *ps2dev = &psmouse->ps2dev; int timeo; @@ -711,13 +715,13 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable) * the controller. Once we get an ACK back from it, it * means we can continue with the touchpad re-init. ALPS * tells us that 1s should be long enough, so set that as - * the upper bound. + * the upper bound. (in practice, it takes about 3 loops.) */ for (timeo = 20; timeo > 0; timeo--) { if (!ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) break; - msleep(50); + msleep(25); } err = hgpk_reset_device(psmouse, false); @@ -729,10 +733,9 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable) /* should be all set, enable the touchpad */ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - + hgpk_dbg(psmouse, "Touchpad powered up.\n"); } else { hgpk_dbg(psmouse, "Powering off touchpad.\n"); - psmouse_set_state(psmouse, PSMOUSE_IGNORE); if (ps2_command(ps2dev, NULL, 0xec) || ps2_command(ps2dev, NULL, 0xec) || @@ -740,6 +743,8 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable) return -1; } + psmouse_set_state(psmouse, PSMOUSE_IGNORE); + /* probably won't see an ACK, the touchpad will be off */ ps2_sendbyte(&psmouse->ps2dev, 0xec, 20); } @@ -755,6 +760,8 @@ static int hgpk_poll(struct psmouse *psmouse) static int hgpk_reconnect(struct psmouse *psmouse) { + struct hgpk_data *priv = psmouse->private; + /* * During suspend/resume the ps2 rails remain powered. We don't want * to do a reset because it's flush data out of buffers; however, @@ -765,6 +772,7 @@ static int hgpk_reconnect(struct psmouse *psmouse) PM_EVENT_ON) return 0; + priv->powered = 1; return hgpk_reset_device(psmouse, false); } @@ -791,7 +799,7 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, * hgpk_toggle_power will deal w/ state so * we're not racing w/ irq */ - err = hgpk_toggle_power(psmouse, value); + err = hgpk_toggle_powersave(psmouse, value); if (!err) priv->powered = value; } -- cgit v1.2.3 From 6021afcf19d8c6f5db6d11cadcfb6a22d0c28a48 Mon Sep 17 00:00:00 2001 From: "Edgar (gimli) Hucek" Date: Tue, 9 Nov 2010 17:38:42 +0100 Subject: input: bcm5974: Add support for MacBookAir3 This patch adds support for the MacBookAir3,1 and MacBookAir3,2 models. [rydberg@euromail.se: touchpad range calibration] Cc: stable@kernel.org Signed-off-by: Edgar (gimli) Hucek Signed-off-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/input/mouse/bcm5974.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index b95231763911..ee82851afe3e 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -55,6 +55,14 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI 0x0236 #define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO 0x0237 #define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238 +/* MacbookAir3,2 (unibody), aka wellspring5 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI 0x023f +#define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO 0x0240 +#define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS 0x0241 +/* MacbookAir3,1 (unibody), aka wellspring4 */ +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 +#define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ @@ -80,6 +88,14 @@ static const struct usb_device_id bcm5974_table[] = { BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS), + /* MacbookAir3,2 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS), + /* MacbookAir3,1 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), /* Terminating entry */ {} }; @@ -234,6 +250,30 @@ static const struct bcm5974_config bcm5974_config_table[] = { { DIM_X, DIM_X / SN_COORD, -4460, 5166 }, { DIM_Y, DIM_Y / SN_COORD, -75, 6700 } }, + { + USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING4_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING4_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4620, 5140 }, + { DIM_Y, DIM_Y / SN_COORD, -150, 6600 } + }, + { + USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4616, 5112 }, + { DIM_Y, DIM_Y / SN_COORD, -142, 5234 } + }, {} }; -- cgit v1.2.3 From 6f07d31e46639e4b1b23de6ee88c9e079a7bf32d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Nov 2010 13:33:25 -0800 Subject: Input: aiptek - tighten up permissions on sysfs attributes Sysfs attributes affecting device behavior should not be, by default, world-writeable. If distributions want to allow console users access these attributes they need to employ udev and friends to adjust permissions as needed. Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/aiptek.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 57b25b84d1fc..0a619c558bfb 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -1097,7 +1097,7 @@ store_tabletPointerMode(struct device *dev, struct device_attribute *attr, const } static DEVICE_ATTR(pointer_mode, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletPointerMode, store_tabletPointerMode); /*********************************************************************** @@ -1134,7 +1134,7 @@ store_tabletCoordinateMode(struct device *dev, struct device_attribute *attr, co } static DEVICE_ATTR(coordinate_mode, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletCoordinateMode, store_tabletCoordinateMode); /*********************************************************************** @@ -1176,7 +1176,7 @@ store_tabletToolMode(struct device *dev, struct device_attribute *attr, const ch } static DEVICE_ATTR(tool_mode, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletToolMode, store_tabletToolMode); /*********************************************************************** @@ -1219,7 +1219,7 @@ store_tabletXtilt(struct device *dev, struct device_attribute *attr, const char } static DEVICE_ATTR(xtilt, - S_IRUGO | S_IWUGO, show_tabletXtilt, store_tabletXtilt); + S_IRUGO | S_IWUSR, show_tabletXtilt, store_tabletXtilt); /*********************************************************************** * support routines for the 'ytilt' file. Note that this file @@ -1261,7 +1261,7 @@ store_tabletYtilt(struct device *dev, struct device_attribute *attr, const char } static DEVICE_ATTR(ytilt, - S_IRUGO | S_IWUGO, show_tabletYtilt, store_tabletYtilt); + S_IRUGO | S_IWUSR, show_tabletYtilt, store_tabletYtilt); /*********************************************************************** * support routines for the 'jitter' file. Note that this file @@ -1288,7 +1288,7 @@ store_tabletJitterDelay(struct device *dev, struct device_attribute *attr, const } static DEVICE_ATTR(jitter, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletJitterDelay, store_tabletJitterDelay); /*********************************************************************** @@ -1317,7 +1317,7 @@ store_tabletProgrammableDelay(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(delay, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletProgrammableDelay, store_tabletProgrammableDelay); /*********************************************************************** @@ -1406,7 +1406,7 @@ store_tabletStylusUpper(struct device *dev, struct device_attribute *attr, const } static DEVICE_ATTR(stylus_upper, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletStylusUpper, store_tabletStylusUpper); /*********************************************************************** @@ -1437,7 +1437,7 @@ store_tabletStylusLower(struct device *dev, struct device_attribute *attr, const } static DEVICE_ATTR(stylus_lower, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletStylusLower, store_tabletStylusLower); /*********************************************************************** @@ -1475,7 +1475,7 @@ store_tabletMouseLeft(struct device *dev, struct device_attribute *attr, const c } static DEVICE_ATTR(mouse_left, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletMouseLeft, store_tabletMouseLeft); /*********************************************************************** @@ -1505,7 +1505,7 @@ store_tabletMouseMiddle(struct device *dev, struct device_attribute *attr, const } static DEVICE_ATTR(mouse_middle, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletMouseMiddle, store_tabletMouseMiddle); /*********************************************************************** @@ -1535,7 +1535,7 @@ store_tabletMouseRight(struct device *dev, struct device_attribute *attr, const } static DEVICE_ATTR(mouse_right, - S_IRUGO | S_IWUGO, + S_IRUGO | S_IWUSR, show_tabletMouseRight, store_tabletMouseRight); /*********************************************************************** @@ -1567,7 +1567,7 @@ store_tabletWheel(struct device *dev, struct device_attribute *attr, const char } static DEVICE_ATTR(wheel, - S_IRUGO | S_IWUGO, show_tabletWheel, store_tabletWheel); + S_IRUGO | S_IWUSR, show_tabletWheel, store_tabletWheel); /*********************************************************************** * support routines for the 'execute' file. Note that this file @@ -1600,7 +1600,7 @@ store_tabletExecute(struct device *dev, struct device_attribute *attr, const cha } static DEVICE_ATTR(execute, - S_IRUGO | S_IWUGO, show_tabletExecute, store_tabletExecute); + S_IRUGO | S_IWUSR, show_tabletExecute, store_tabletExecute); /*********************************************************************** * support routines for the 'odm_code' file. Note that this file -- cgit v1.2.3 From 451a3c24b0135bce54542009b5fde43846c7cf67 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 17 Nov 2010 16:26:55 +0100 Subject: BKL: remove extraneous #include The big kernel lock has been removed from all these files at some point, leaving only the #include. Remove this too as a cleanup. Signed-off-by: Arnd Bergmann Signed-off-by: Linus Torvalds --- drivers/input/input.c | 1 - drivers/input/serio/serio_raw.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 7f26ca6ecf75..90b7ecfb6257 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "input-compat.h" MODULE_AUTHOR("Vojtech Pavlik "); diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index cd82bb125915..b7ba4597f7f0 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From e3f0f0a6c11b049f1be603dcfec82d2a8643f5fd Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 17 Nov 2010 23:59:34 -0800 Subject: Input: xpad - ensure xpad->bulk_out is initialized before submitting urb As pointed out by Oliver Neukum: xpad->irq_in is currently submitted before xpad->bulk_out is allocated. That however is a race, because the callback for irq_in can call xpad360w_process_packet(), which will in turn submit the bulk URB. This patch moves initialization for xpad->bulk_out earlier, so we can ensure xpad->bulk_out is initialized before submitting urb. Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 4875de9a3f88..56abf3d0e911 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -921,19 +921,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id usb_set_intfdata(intf, xpad); - /* - * Submit the int URB immediately rather than waiting for open - * because we get status messages from the device whether - * or not any controllers are attached. In fact, it's - * exactly the message that a controller has arrived that - * we're waiting for. - */ if (xpad->xtype == XTYPE_XBOX360W) { - xpad->irq_in->dev = xpad->udev; - error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); - if (error) - goto fail7; - /* * Setup the message to set the LEDs on the * controller when it shows up @@ -941,13 +929,13 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->bulk_out) { error = -ENOMEM; - goto fail8; + goto fail7; } xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); if (!xpad->bdata) { error = -ENOMEM; - goto fail9; + goto fail8; } xpad->bdata[2] = 0x08; @@ -969,12 +957,24 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id usb_fill_bulk_urb(xpad->bulk_out, udev, usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); + + /* + * Submit the int URB immediately rather than waiting for open + * because we get status messages from the device whether + * or not any controllers are attached. In fact, it's + * exactly the message that a controller has arrived that + * we're waiting for. + */ + xpad->irq_in->dev = xpad->udev; + error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); + if (error) + goto fail9; } return 0; - fail9: usb_free_urb(xpad->bulk_out); - fail8: usb_kill_urb(xpad->irq_in); + fail9: kfree(xpad->bdata); + fail8: usb_free_urb(xpad->bulk_out); fail7: input_unregister_device(input_dev); input_dev = NULL; fail6: xpad_led_disconnect(xpad); -- cgit v1.2.3 From 8b5fce06f8795d9a7f8d1f03f743fc8befa66b3b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 18 Nov 2010 00:14:03 -0800 Subject: Input: qt602240_ts - convert to using dev_pm_ops Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/qt602240_ts.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c index 66b26ad3032a..125e50ddef7f 100644 --- a/drivers/input/touchscreen/qt602240_ts.c +++ b/drivers/input/touchscreen/qt602240_ts.c @@ -1324,8 +1324,9 @@ static int __devexit qt602240_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg) +static int qt602240_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct qt602240_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; @@ -1339,8 +1340,9 @@ static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int qt602240_resume(struct i2c_client *client) +static int qt602240_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct qt602240_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; @@ -1359,9 +1361,11 @@ static int qt602240_resume(struct i2c_client *client) return 0; } -#else -#define qt602240_suspend NULL -#define qt602240_resume NULL + +static const struct dev_pm_ops qt602240_pm_ops = { + .suspend = qt602240_suspend, + .resume = qt602240_resume, +}; #endif static const struct i2c_device_id qt602240_id[] = { @@ -1374,11 +1378,12 @@ static struct i2c_driver qt602240_driver = { .driver = { .name = "qt602240_ts", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &qt602240_pm_ops, +#endif }, .probe = qt602240_probe, .remove = __devexit_p(qt602240_remove), - .suspend = qt602240_suspend, - .resume = qt602240_resume, .id_table = qt602240_id, }; -- cgit v1.2.3 From ce16a474f6305dd631c885ba970d5746e4d5c803 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Wed, 17 Nov 2010 23:59:54 -0800 Subject: Input: qt602240_ts - fix wrong sizeof in object table allocation The kcalloc call for the object table is using sizeof(struct qt602240_data) when it should be using sizeof(struct qt6602240_object), resulting in a larger allocation than is required. Signed-off-by: Chris Leech Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/qt602240_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c index 125e50ddef7f..4dcb0e872f6a 100644 --- a/drivers/input/touchscreen/qt602240_ts.c +++ b/drivers/input/touchscreen/qt602240_ts.c @@ -969,7 +969,7 @@ static int qt602240_initialize(struct qt602240_data *data) return error; data->object_table = kcalloc(info->object_num, - sizeof(struct qt602240_data), + sizeof(struct qt602240_object), GFP_KERNEL); if (!data->object_table) { dev_err(&client->dev, "Failed to allocate memory\n"); -- cgit v1.2.3 From 8ee294cd9def0004887da7f44b80563493b0a097 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Nov 2010 01:39:57 -0800 Subject: Input: serio - convert to common workqueue instead of a thread Instead of creating an exclusive thread to handle serio events (which happen rarely), let's switch to using common workqueue. With the arrival of concurrency-managed workqueue infrastructure we are not concerned that our callers or callees also using workqueue (no deadlocks anymore) and it should reduce total number of threads in the system. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/serio.c | 155 ++++++++++++++++++++------------------------ 1 file changed, 69 insertions(+), 86 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 405bf214527c..db5b0bca1a1a 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -32,10 +32,9 @@ #include #include #include -#include #include #include -#include +#include #include MODULE_AUTHOR("Vojtech Pavlik "); @@ -44,7 +43,7 @@ MODULE_LICENSE("GPL"); /* * serio_mutex protects entire serio subsystem and is taken every time - * serio port or driver registrered or unregistered. + * serio port or driver registered or unregistered. */ static DEFINE_MUTEX(serio_mutex); @@ -165,58 +164,22 @@ struct serio_event { static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */ static LIST_HEAD(serio_event_list); -static DECLARE_WAIT_QUEUE_HEAD(serio_wait); -static struct task_struct *serio_task; -static int serio_queue_event(void *object, struct module *owner, - enum serio_event_type event_type) +static struct serio_event *serio_get_event(void) { + struct serio_event *event = NULL; unsigned long flags; - struct serio_event *event; - int retval = 0; spin_lock_irqsave(&serio_event_lock, flags); - /* - * Scan event list for the other events for the same serio port, - * starting with the most recent one. If event is the same we - * do not need add new one. If event is of different type we - * need to add this event and should not look further because - * we need to preseve sequence of distinct events. - */ - list_for_each_entry_reverse(event, &serio_event_list, node) { - if (event->object == object) { - if (event->type == event_type) - goto out; - break; - } - } - - event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC); - if (!event) { - pr_err("Not enough memory to queue event %d\n", event_type); - retval = -ENOMEM; - goto out; - } - - if (!try_module_get(owner)) { - pr_warning("Can't get module reference, dropping event %d\n", - event_type); - kfree(event); - retval = -EINVAL; - goto out; + if (!list_empty(&serio_event_list)) { + event = list_first_entry(&serio_event_list, + struct serio_event, node); + list_del_init(&event->node); } - event->type = event_type; - event->object = object; - event->owner = owner; - - list_add_tail(&event->node, &serio_event_list); - wake_up(&serio_wait); - -out: spin_unlock_irqrestore(&serio_event_lock, flags); - return retval; + return event; } static void serio_free_event(struct serio_event *event) @@ -250,25 +213,7 @@ static void serio_remove_duplicate_events(struct serio_event *event) spin_unlock_irqrestore(&serio_event_lock, flags); } - -static struct serio_event *serio_get_event(void) -{ - struct serio_event *event = NULL; - unsigned long flags; - - spin_lock_irqsave(&serio_event_lock, flags); - - if (!list_empty(&serio_event_list)) { - event = list_first_entry(&serio_event_list, - struct serio_event, node); - list_del_init(&event->node); - } - - spin_unlock_irqrestore(&serio_event_lock, flags); - return event; -} - -static void serio_handle_event(void) +static void serio_handle_event(struct work_struct *work) { struct serio_event *event; @@ -307,6 +252,59 @@ static void serio_handle_event(void) mutex_unlock(&serio_mutex); } +static DECLARE_WORK(serio_event_work, serio_handle_event); + +static int serio_queue_event(void *object, struct module *owner, + enum serio_event_type event_type) +{ + unsigned long flags; + struct serio_event *event; + int retval = 0; + + spin_lock_irqsave(&serio_event_lock, flags); + + /* + * Scan event list for the other events for the same serio port, + * starting with the most recent one. If event is the same we + * do not need add new one. If event is of different type we + * need to add this event and should not look further because + * we need to preseve sequence of distinct events. + */ + list_for_each_entry_reverse(event, &serio_event_list, node) { + if (event->object == object) { + if (event->type == event_type) + goto out; + break; + } + } + + event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC); + if (!event) { + pr_err("Not enough memory to queue event %d\n", event_type); + retval = -ENOMEM; + goto out; + } + + if (!try_module_get(owner)) { + pr_warning("Can't get module reference, dropping event %d\n", + event_type); + kfree(event); + retval = -EINVAL; + goto out; + } + + event->type = event_type; + event->object = object; + event->owner = owner; + + list_add_tail(&event->node, &serio_event_list); + schedule_work(&serio_event_work); + +out: + spin_unlock_irqrestore(&serio_event_lock, flags); + return retval; +} + /* * Remove all events that have been submitted for a given * object, be it serio port or driver. @@ -356,18 +354,6 @@ static struct serio *serio_get_pending_child(struct serio *parent) return child; } -static int serio_thread(void *nothing) -{ - do { - serio_handle_event(); - wait_event_interruptible(serio_wait, - kthread_should_stop() || !list_empty(&serio_event_list)); - } while (!kthread_should_stop()); - - return 0; -} - - /* * Serio port operations */ @@ -1040,21 +1026,18 @@ static int __init serio_init(void) return error; } - serio_task = kthread_run(serio_thread, NULL, "kseriod"); - if (IS_ERR(serio_task)) { - bus_unregister(&serio_bus); - error = PTR_ERR(serio_task); - pr_err("Failed to start kseriod, error: %d\n", error); - return error; - } - return 0; } static void __exit serio_exit(void) { bus_unregister(&serio_bus); - kthread_stop(serio_task); + + /* + * There should not be any outstanding events but work may + * still be scheduled so simply cancel it. + */ + cancel_work_sync(&serio_event_work); } subsys_initcall(serio_init); -- cgit v1.2.3 From c44f242064093e640a068741b05ee6acdd49bc22 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Nov 2010 01:39:57 -0800 Subject: Input: gameport - convert to use common workqueue instead of a thread Instead of creating an exclusive thread to handle gameport events (which happen rarely), let's switch to common workqueue. With the arrival of concurrency-managed workqueue infrastructure we are not concerned that our callers or callees also using workqueue (no deadlocks anymore) and it should reduce total number of threads in the system. Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/gameport.c | 152 +++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 84 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 46239e47a260..dbf741c95835 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -18,13 +18,11 @@ #include #include #include -#include #include #include -#include +#include #include /* HZ */ #include -#include /*#include */ @@ -234,58 +232,22 @@ struct gameport_event { static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */ static LIST_HEAD(gameport_event_list); -static DECLARE_WAIT_QUEUE_HEAD(gameport_wait); -static struct task_struct *gameport_task; -static int gameport_queue_event(void *object, struct module *owner, - enum gameport_event_type event_type) +static struct gameport_event *gameport_get_event(void) { + struct gameport_event *event = NULL; unsigned long flags; - struct gameport_event *event; - int retval = 0; spin_lock_irqsave(&gameport_event_lock, flags); - /* - * Scan event list for the other events for the same gameport port, - * starting with the most recent one. If event is the same we - * do not need add new one. If event is of different type we - * need to add this event and should not look further because - * we need to preseve sequence of distinct events. - */ - list_for_each_entry_reverse(event, &gameport_event_list, node) { - if (event->object == object) { - if (event->type == event_type) - goto out; - break; - } - } - - event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); - if (!event) { - pr_err("Not enough memory to queue event %d\n", event_type); - retval = -ENOMEM; - goto out; - } - - if (!try_module_get(owner)) { - pr_warning("Can't get module reference, dropping event %d\n", - event_type); - kfree(event); - retval = -EINVAL; - goto out; + if (!list_empty(&gameport_event_list)) { + event = list_first_entry(&gameport_event_list, + struct gameport_event, node); + list_del_init(&event->node); } - event->type = event_type; - event->object = object; - event->owner = owner; - - list_add_tail(&event->node, &gameport_event_list); - wake_up(&gameport_wait); - -out: spin_unlock_irqrestore(&gameport_event_lock, flags); - return retval; + return event; } static void gameport_free_event(struct gameport_event *event) @@ -319,24 +281,8 @@ static void gameport_remove_duplicate_events(struct gameport_event *event) spin_unlock_irqrestore(&gameport_event_lock, flags); } -static struct gameport_event *gameport_get_event(void) -{ - struct gameport_event *event = NULL; - unsigned long flags; - - spin_lock_irqsave(&gameport_event_lock, flags); - - if (!list_empty(&gameport_event_list)) { - event = list_first_entry(&gameport_event_list, - struct gameport_event, node); - list_del_init(&event->node); - } - - spin_unlock_irqrestore(&gameport_event_lock, flags); - return event; -} -static void gameport_handle_event(void) +static void gameport_handle_events(struct work_struct *work) { struct gameport_event *event; @@ -368,6 +314,59 @@ static void gameport_handle_event(void) mutex_unlock(&gameport_mutex); } +static DECLARE_WORK(gameport_event_work, gameport_handle_events); + +static int gameport_queue_event(void *object, struct module *owner, + enum gameport_event_type event_type) +{ + unsigned long flags; + struct gameport_event *event; + int retval = 0; + + spin_lock_irqsave(&gameport_event_lock, flags); + + /* + * Scan event list for the other events for the same gameport port, + * starting with the most recent one. If event is the same we + * do not need add new one. If event is of different type we + * need to add this event and should not look further because + * we need to preserve sequence of distinct events. + */ + list_for_each_entry_reverse(event, &gameport_event_list, node) { + if (event->object == object) { + if (event->type == event_type) + goto out; + break; + } + } + + event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC); + if (!event) { + pr_err("Not enough memory to queue event %d\n", event_type); + retval = -ENOMEM; + goto out; + } + + if (!try_module_get(owner)) { + pr_warning("Can't get module reference, dropping event %d\n", + event_type); + kfree(event); + retval = -EINVAL; + goto out; + } + + event->type = event_type; + event->object = object; + event->owner = owner; + + list_add_tail(&event->node, &gameport_event_list); + schedule_work(&gameport_event_work); + +out: + spin_unlock_irqrestore(&gameport_event_lock, flags); + return retval; +} + /* * Remove all events that have been submitted for a given object, * be it a gameport port or a driver. @@ -419,19 +418,6 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent) return child; } -static int gameport_thread(void *nothing) -{ - set_freezable(); - do { - gameport_handle_event(); - wait_event_freezable(gameport_wait, - kthread_should_stop() || !list_empty(&gameport_event_list)); - } while (!kthread_should_stop()); - - return 0; -} - - /* * Gameport port operations */ @@ -814,13 +800,6 @@ static int __init gameport_init(void) return error; } - gameport_task = kthread_run(gameport_thread, NULL, "kgameportd"); - if (IS_ERR(gameport_task)) { - bus_unregister(&gameport_bus); - error = PTR_ERR(gameport_task); - pr_err("Failed to start kgameportd, error: %d\n", error); - return error; - } return 0; } @@ -828,7 +807,12 @@ static int __init gameport_init(void) static void __exit gameport_exit(void) { bus_unregister(&gameport_bus); - kthread_stop(gameport_task); + + /* + * There should not be any outstanding events but work may + * still be scheduled so simply cancel it. + */ + cancel_work_sync(&gameport_event_work); } subsys_initcall(gameport_init); -- cgit v1.2.3 From de391d12500ede13faa7ecadbe900a8de1f76cfa Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Thu, 18 Nov 2010 09:06:43 -0800 Subject: Input: fix typo in keycode validation supporting large scancodes Check the input_keymap_entry keycode size (u32) instead of the device's (void*) when validating that keycode value can be stored in the keymap. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=22722 Signed-off-by: Mattia Dongili Tested-by: Norbert Preining Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 7f26ca6ecf75..5edc41aa08f8 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -753,7 +753,7 @@ static int input_default_setkeycode(struct input_dev *dev, if (index >= dev->keycodemax) return -EINVAL; - if (dev->keycodesize < sizeof(dev->keycode) && + if (dev->keycodesize < sizeof(ke->keycode) && (ke->keycode >> (dev->keycodesize * 8))) return -EINVAL; -- cgit v1.2.3 From bc95df78c4566327086d44f1bfab984a70dc4d6b Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Fri, 19 Nov 2010 12:41:19 -0800 Subject: Input: add support for keyboards on ST SPEAr platform Signed-off-by: Rajeev Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 9 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/spear-keyboard.c | 344 ++++++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 drivers/input/keyboard/spear-keyboard.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b8c51b9781db..ee85e5bf4b2a 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -443,6 +443,15 @@ config KEYBOARD_OMAP4 To compile this driver as a module, choose M here: the module will be called omap4-keypad. +config KEYBOARD_SPEAR + tristate "ST SPEAR keyboard support" + depends on PLAT_SPEAR + help + Say Y here if you want to use the SPEAR keyboard. + + To compile this driver as a module, choose M here: the + module will be called spear-keboard. + config KEYBOARD_TNETV107X tristate "TI TNETV107X keypad support" depends on ARCH_DAVINCI_TNETV107X diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e8ebe2..8449c730892c 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o +obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c new file mode 100644 index 000000000000..bee03d64c453 --- /dev/null +++ b/drivers/input/keyboard/spear-keyboard.c @@ -0,0 +1,344 @@ +/* + * SPEAr Keyboard Driver + * Based on omap-keypad driver + * + * Copyright (C) 2010 ST Microelectronics + * Rajeev Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Keyboard Registers */ +#define MODE_REG 0x00 /* 16 bit reg */ +#define STATUS_REG 0x0C /* 2 bit reg */ +#define DATA_REG 0x10 /* 8 bit reg */ +#define INTR_MASK 0x54 + +/* Register Values */ +/* + * pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode + * control register as 1010010(82MHZ) + */ +#define PCLK_FREQ_MSK 0xA400 /* 82 MHz */ +#define START_SCAN 0x0100 +#define SCAN_RATE_10 0x0000 +#define SCAN_RATE_20 0x0004 +#define SCAN_RATE_40 0x0008 +#define SCAN_RATE_80 0x000C +#define MODE_KEYBOARD 0x0002 +#define DATA_AVAIL 0x2 + +#define KEY_MASK 0xFF000000 +#define KEY_VALUE 0x00FFFFFF +#define ROW_MASK 0xF0 +#define COLUMN_MASK 0x0F +#define ROW_SHIFT 4 + +struct spear_kbd { + struct input_dev *input; + struct resource *res; + void __iomem *io_base; + struct clk *clk; + unsigned int irq; + unsigned short last_key; + unsigned short keycodes[256]; +}; + +static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) +{ + struct spear_kbd *kbd = dev_id; + struct input_dev *input = kbd->input; + unsigned int key; + u8 sts, val; + + sts = readb(kbd->io_base + STATUS_REG); + if (sts & DATA_AVAIL) + return IRQ_NONE; + + if (kbd->last_key != KEY_RESERVED) { + input_report_key(input, kbd->last_key, 0); + kbd->last_key = KEY_RESERVED; + } + + /* following reads active (row, col) pair */ + val = readb(kbd->io_base + DATA_REG); + key = kbd->keycodes[val]; + + input_event(input, EV_MSC, MSC_SCAN, val); + input_report_key(input, key, 1); + input_sync(input); + + kbd->last_key = key; + + /* clear interrupt */ + writeb(0, kbd->io_base + STATUS_REG); + + return IRQ_HANDLED; +} + +static int spear_kbd_open(struct input_dev *dev) +{ + struct spear_kbd *kbd = input_get_drvdata(dev); + int error; + u16 val; + + kbd->last_key = KEY_RESERVED; + + error = clk_enable(kbd->clk); + if (error) + return error; + + /* program keyboard */ + val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK; + writew(val, kbd->io_base + MODE_REG); + writeb(1, kbd->io_base + STATUS_REG); + + /* start key scan */ + val = readw(kbd->io_base + MODE_REG); + val |= START_SCAN; + writew(val, kbd->io_base + MODE_REG); + + return 0; +} + +static void spear_kbd_close(struct input_dev *dev) +{ + struct spear_kbd *kbd = input_get_drvdata(dev); + u16 val; + + /* stop key scan */ + val = readw(kbd->io_base + MODE_REG); + val &= ~START_SCAN; + writew(val, kbd->io_base + MODE_REG); + + clk_disable(kbd->clk); + + kbd->last_key = KEY_RESERVED; +} + +static int __devinit spear_kbd_probe(struct platform_device *pdev) +{ + const struct kbd_platform_data *pdata = pdev->dev.platform_data; + const struct matrix_keymap_data *keymap; + struct spear_kbd *kbd; + struct input_dev *input_dev; + struct resource *res; + int irq; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "Invalid platform data\n"); + return -EINVAL; + } + + keymap = pdata->keymap; + if (!keymap) { + dev_err(&pdev->dev, "no keymap defined\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no keyboard resource defined\n"); + return -EBUSY; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "not able to get irq for the device\n"); + return irq; + } + + kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbd || !input_dev) { + dev_err(&pdev->dev, "out of memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + kbd->input = input_dev; + kbd->irq = irq; + kbd->res = request_mem_region(res->start, resource_size(res), + pdev->name); + if (!kbd->res) { + dev_err(&pdev->dev, "keyboard region already claimed\n"); + error = -EBUSY; + goto err_free_mem; + } + + kbd->io_base = ioremap(res->start, resource_size(res)); + if (!kbd->io_base) { + dev_err(&pdev->dev, "ioremap failed for kbd_region\n"); + error = -ENOMEM; + goto err_release_mem_region; + } + + kbd->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(kbd->clk)) { + error = PTR_ERR(kbd->clk); + goto err_iounmap; + } + + input_dev->name = "Spear Keyboard"; + input_dev->phys = "keyboard/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->open = spear_kbd_open; + input_dev->close = spear_kbd_close; + + __set_bit(EV_KEY, input_dev->evbit); + if (pdata->rep) + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = kbd->keycodes; + input_dev->keycodesize = sizeof(kbd->keycodes[0]); + input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes); + + matrix_keypad_build_keymap(keymap, ROW_SHIFT, + input_dev->keycode, input_dev->keybit); + + input_set_drvdata(input_dev, kbd); + + error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd); + if (error) { + dev_err(&pdev->dev, "request_irq fail\n"); + goto err_put_clk; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "Unable to register keyboard device\n"); + goto err_free_irq; + } + + device_init_wakeup(&pdev->dev, 1); + platform_set_drvdata(pdev, kbd); + + return 0; + +err_free_irq: + free_irq(kbd->irq, kbd); +err_put_clk: + clk_put(kbd->clk); +err_iounmap: + iounmap(kbd->io_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(kbd); + + return error; +} + +static int __devexit spear_kbd_remove(struct platform_device *pdev) +{ + struct spear_kbd *kbd = platform_get_drvdata(pdev); + + free_irq(kbd->irq, kbd); + input_unregister_device(kbd->input); + clk_put(kbd->clk); + iounmap(kbd->io_base); + release_mem_region(kbd->res->start, resource_size(kbd->res)); + kfree(kbd); + + device_init_wakeup(&pdev->dev, 1); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int spear_kbd_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spear_kbd *kbd = platform_get_drvdata(pdev); + struct input_dev *input_dev = kbd->input; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + clk_enable(kbd->clk); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(kbd->irq); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int spear_kbd_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spear_kbd *kbd = platform_get_drvdata(pdev); + struct input_dev *input_dev = kbd->input; + + mutex_lock(&input_dev->mutex); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(kbd->irq); + + if (input_dev->users) + clk_enable(kbd->clk); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops spear_kbd_pm_ops = { + .suspend = spear_kbd_suspend, + .resume = spear_kbd_resume, +}; +#endif + +static struct platform_driver spear_kbd_driver = { + .probe = spear_kbd_probe, + .remove = __devexit_p(spear_kbd_remove), + .driver = { + .name = "keyboard", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &spear_kbd_pm_ops, +#endif + }, +}; + +static int __init spear_kbd_init(void) +{ + return platform_driver_register(&spear_kbd_driver); +} +module_init(spear_kbd_init); + +static void __exit spear_kbd_exit(void) +{ + platform_driver_unregister(&spear_kbd_driver); +} +module_exit(spear_kbd_exit); + +MODULE_AUTHOR("Rajeev Kumar"); +MODULE_DESCRIPTION("SPEAr Keyboard Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 39de52104dd92bc0548a20201350111dc9317df9 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sat, 20 Nov 2010 13:36:49 -0800 Subject: Input: serio HIL MLC - don't deref null, don't leak and return proper error While reviewing various users of kernel memory allocation functions I came across drivers/input/serio/hil_mlc.c::hil_mlc_register() and noticed that: - it calls kzalloc() but fails to check for a NULL return before use. - it makes several allocations and if one fails it doesn't free the previous ones. - It doesn't return -ENOMEM in the failed memory allocation case (it just crashes). This patch corrects all of the above and also reworks the only caller of this function that I could find (drivers/input/serio/hp_sdc_mlc.c::hp_sdc_mlc_out()) so that it now checks the return value of hil_mlc_register() and properly propagates it on failure and I also restructured the code to remove some labels and goto's to make it, IMHO nicer to read. Signed-off-by: Jesper Juhl Tested-by: Helge Deller Acked-by: Helge Deller Signed-off-by: Dmitry Torokhov --- drivers/input/serio/hil_mlc.c | 5 +++++ drivers/input/serio/hp_sdc_mlc.c | 18 +++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c index e5624d8f1709..bfd3865d886b 100644 --- a/drivers/input/serio/hil_mlc.c +++ b/drivers/input/serio/hil_mlc.c @@ -932,6 +932,11 @@ int hil_mlc_register(hil_mlc *mlc) hil_mlc_copy_di_scratch(mlc, i); mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL); mlc->serio[i] = mlc_serio; + if (!mlc->serio[i]) { + for (; i >= 0; i--) + kfree(mlc->serio[i]); + return -ENOMEM; + } snprintf(mlc_serio->name, sizeof(mlc_serio->name)-1, "HIL_SERIO%d", i); snprintf(mlc_serio->phys, sizeof(mlc_serio->phys)-1, "HIL%d", i); mlc_serio->id = hil_mlc_serio_id; diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c index 7d2b820ef58d..d50f0678bf47 100644 --- a/drivers/input/serio/hp_sdc_mlc.c +++ b/drivers/input/serio/hp_sdc_mlc.c @@ -305,6 +305,7 @@ static void hp_sdc_mlc_out(hil_mlc *mlc) static int __init hp_sdc_mlc_init(void) { hil_mlc *mlc = &hp_sdc_mlc; + int err; #ifdef __mc68000__ if (!MACH_IS_HP300) @@ -323,22 +324,21 @@ static int __init hp_sdc_mlc_init(void) mlc->out = &hp_sdc_mlc_out; mlc->priv = &hp_sdc_mlc_priv; - if (hil_mlc_register(mlc)) { + err = hil_mlc_register(mlc); + if (err) { printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n"); - goto err0; + return err; } if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) { printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n"); - goto err1; + if (hil_mlc_unregister(mlc)) + printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" + "This is bad. Could cause an oops.\n"); + return -EBUSY; } + return 0; - err1: - if (hil_mlc_unregister(mlc)) - printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n" - "This is bad. Could cause an oops.\n"); - err0: - return -EBUSY; } static void __exit hp_sdc_mlc_exit(void) -- cgit v1.2.3 From 0e86eb29def648664c2c0fa605f5b5bad84247cb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 21 Nov 2010 22:23:06 -0800 Subject: Input: ps2mult - fix wrong kfree in ps2mult_connect error path Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/serio/ps2mult.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c index 6bce22e4e495..15aa81c9f1fb 100644 --- a/drivers/input/serio/ps2mult.c +++ b/drivers/input/serio/ps2mult.c @@ -207,7 +207,7 @@ static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) err_out: while (--i >= 0) kfree(psm->ports[i].serio); - kfree(serio); + kfree(psm); return error; } -- cgit v1.2.3 From ebcc019926269e7e123d55ec92ff00c2688ca343 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 21 Nov 2010 22:27:09 -0800 Subject: Input: ams_delta_serio - fix wrong kfree in ams_delta_serio_exit serio_unregister_port() will call put_device() to free the memory. Thus remove kfree(ams_delta_serio) after serio_unregister_port(ams_delta_serio). Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/serio/ams_delta_serio.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c index 8f1770e1e08b..ebe955325677 100644 --- a/drivers/input/serio/ams_delta_serio.c +++ b/drivers/input/serio/ams_delta_serio.c @@ -172,6 +172,5 @@ static void __exit ams_delta_serio_exit(void) free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); - kfree(ams_delta_serio); } module_exit(ams_delta_serio_exit); -- cgit v1.2.3 From f09830ab15bfb7eb4e832e44189b5b5883309811 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 21 Nov 2010 22:27:13 -0800 Subject: Input: ct82c710 - remove a redundant serio_register_port() We already call serio_register_port() in ct82c710_probe(), thus remove a redundant serio_register_port() in ct82c710_init(). Looks like this bug is introduced by 916d83cfe5da1cda454d8b0ae233f06b58bd7f91 "Input: ct82c710 - convert to the new platform device interface" [dtor@mail.ru: also move printk to where we register port] Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/serio/ct82c710.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 4a3084695c00..448c7724beb9 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -191,6 +191,9 @@ static int __devinit ct82c710_probe(struct platform_device *dev) serio_register_port(ct82c710_port); + printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n", + (unsigned long long)CT82C710_DATA, CT82C710_IRQ); + return 0; } @@ -237,11 +240,6 @@ static int __init ct82c710_init(void) if (error) goto err_free_device; - serio_register_port(ct82c710_port); - - printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n", - (unsigned long long)CT82C710_DATA, CT82C710_IRQ); - return 0; err_free_device: -- cgit v1.2.3 From 09910509ebc74643e94fe6f3feb4c23b019aeb34 Mon Sep 17 00:00:00 2001 From: Glenn Sommer Date: Mon, 22 Nov 2010 12:00:05 -0800 Subject: Input: usbtouchscreen - add support for LG Flatron T1710B I've recently got my hands on a LG Flatron T1710B touchscreen. As other LG products, this seems to use the ITM panel. Signed-off-by: Glenn Sommer Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index f45f80f6d336..73fd6642b681 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -178,6 +178,7 @@ static const struct usb_device_id usbtouch_devices[] = { #ifdef CONFIG_TOUCHSCREEN_USB_ITM {USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM}, + {USB_DEVICE(0x16e3, 0xf9e9), .driver_info = DEVTYPE_ITM}, #endif #ifdef CONFIG_TOUCHSCREEN_USB_ETURBO -- cgit v1.2.3 From ebde50d5a49122c164f81958a03993e1c947c0b6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Nov 2010 12:39:28 -0800 Subject: Input: clean up Makefile (use input-core-y) The proper way to specify multi-source object is to use -y instead of -obj (which is deprecated) as it allows conditional inclusion of modules in the list. Signed-off-by: Dmitry Torokhov --- drivers/input/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 7ad212d31f99..9fd1cf2b705e 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -5,7 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT) += input-core.o -input-core-objs := input.o input-compat.o ff-core.o +input-core-y := input.o input-compat.o ff-core.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o -- cgit v1.2.3 From 33e808c383477e821163f133c2e3e671879c28b6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 22 Nov 2010 12:53:23 -0800 Subject: Input: iforce - clean up Makefile Use -y notation to specify list of objects comprising iforce module and conditionally pull in USB and RS232 support. Also remove custom compiler flags and rely on general makefile rules for enabling warnings. Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/iforce/Makefile | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile index 74daff49ab6e..bc5bda22f15e 100644 --- a/drivers/input/joystick/iforce/Makefile +++ b/drivers/input/joystick/iforce/Makefile @@ -4,17 +4,8 @@ # By Johann Deneux # -# Goal definition -iforce-objs := iforce-ff.o iforce-main.o iforce-packets.o - obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o -ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y) - iforce-objs += iforce-serio.o -endif - -ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y) - iforce-objs += iforce-usb.o -endif - -EXTRA_CFLAGS = -Werror-implicit-function-declaration +iforce-y := iforce-ff.o iforce-main.o iforce-packets.o +iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o +iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o -- cgit v1.2.3 From 9632f9890e76aa7a78f719b0b3b572f65e3e968b Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Tue, 16 Nov 2010 14:26:57 -0800 Subject: OMAP1: drop AMS_DELTA_FIQ config option This patches removes a config option that was used to select a FIQ handler to be build for Amstrad Delta, as required by the on-board serio interface driver. Not having any problem reports received since it was introduced in 2.6.35, the FIQ handler can now be built and initialized by default, thus reqiring no extra config option. Signed-off-by: Janusz Krzysztofik Signed-off-by: Tony Lindgren --- drivers/input/serio/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 6256233d2bfb..bcb1fdedb595 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -214,7 +214,6 @@ config SERIO_AMS_DELTA tristate "Amstrad Delta (E3) mailboard support" depends on MACH_AMS_DELTA default y - select AMS_DELTA_FIQ ---help--- Say Y here if you have an E3 and want to use its mailboard, or any standard AT keyboard connected to the mailboard port. -- cgit v1.2.3 From b029ffafe89cf4b97cf39e0225a5205cbbf9e02f Mon Sep 17 00:00:00 2001 From: Hemanth V Date: Tue, 30 Nov 2010 23:03:54 -0800 Subject: Input: add CMA3000 accelerometer driver Add support for CMA3000 Tri-axis accelerometer, which supports Motion detect, Measurement and Free fall modes. CMA3000 supports both I2C/SPI bus for communication, currently the driver supports I2C based communication. Signed-off-by: Hemanth V Reviewed-by: Jonathan Cameron Reviewed-by: Sergio Aguirre Reviewed-by: Shubhrajyoti Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 24 +++ drivers/input/misc/Makefile | 2 + drivers/input/misc/cma3000_d0x.c | 398 +++++++++++++++++++++++++++++++++++ drivers/input/misc/cma3000_d0x.h | 42 ++++ drivers/input/misc/cma3000_d0x_i2c.c | 141 +++++++++++++ 5 files changed, 607 insertions(+) create mode 100644 drivers/input/misc/cma3000_d0x.c create mode 100644 drivers/input/misc/cma3000_d0x.h create mode 100644 drivers/input/misc/cma3000_d0x_i2c.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index b99b8cbde02f..f0d90172a65f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -448,4 +448,28 @@ config INPUT_ADXL34X_SPI To compile this driver as a module, choose M here: the module will be called adxl34x-spi. +config INPUT_CMA3000 + tristate "VTI CMA3000 Tri-axis accelerometer" + help + Say Y here if you want to use VTI CMA3000_D0x Accelerometer + driver + + This driver currently only supports I2C interface to the + controller. Also select the I2C method. + + If unsure, say N + + To compile this driver as a module, choose M here: the + module will be called cma3000_d0x. + +config INPUT_CMA3000_I2C + tristate "Support I2C bus connection" + depends on INPUT_CMA3000 && I2C + help + Say Y here if you want to use VTI CMA3000_D0x Accelerometer + through I2C interface. + + To compile this driver as a module, choose M here: the + module will be called cma3000_d0x_i2c. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1fe1f6c8b737..35bcfe46555e 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o obj-$(CONFIG_INPUT_CM109) += cm109.o +obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o +obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c new file mode 100644 index 000000000000..1633b6342267 --- /dev/null +++ b/drivers/input/misc/cma3000_d0x.c @@ -0,0 +1,398 @@ +/* + * VTI CMA3000_D0x Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Hemanth V + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "cma3000_d0x.h" + +#define CMA3000_WHOAMI 0x00 +#define CMA3000_REVID 0x01 +#define CMA3000_CTRL 0x02 +#define CMA3000_STATUS 0x03 +#define CMA3000_RSTR 0x04 +#define CMA3000_INTSTATUS 0x05 +#define CMA3000_DOUTX 0x06 +#define CMA3000_DOUTY 0x07 +#define CMA3000_DOUTZ 0x08 +#define CMA3000_MDTHR 0x09 +#define CMA3000_MDFFTMR 0x0A +#define CMA3000_FFTHR 0x0B + +#define CMA3000_RANGE2G (1 << 7) +#define CMA3000_RANGE8G (0 << 7) +#define CMA3000_BUSI2C (0 << 4) +#define CMA3000_MODEMASK (7 << 1) +#define CMA3000_GRANGEMASK (1 << 7) + +#define CMA3000_STATUS_PERR 1 +#define CMA3000_INTSTATUS_FFDET (1 << 2) + +/* Settling time delay in ms */ +#define CMA3000_SETDELAY 30 + +/* Delay for clearing interrupt in us */ +#define CMA3000_INTDELAY 44 + + +/* + * Bit weights in mg for bit 0, other bits need + * multipy factor 2^n. Eight bit is the sign bit. + */ +#define BIT_TO_2G 18 +#define BIT_TO_8G 71 + +struct cma3000_accl_data { + const struct cma3000_bus_ops *bus_ops; + const struct cma3000_platform_data *pdata; + + struct device *dev; + struct input_dev *input_dev; + + int bit_to_mg; + int irq; + + int g_range; + u8 mode; + + struct mutex mutex; + bool opened; + bool suspended; +}; + +#define CMA3000_READ(data, reg, msg) \ + (data->bus_ops->read(data->dev, reg, msg)) +#define CMA3000_SET(data, reg, val, msg) \ + ((data)->bus_ops->write(data->dev, reg, val, msg)) + +/* + * Conversion for each of the eight modes to g, depending + * on G range i.e 2G or 8G. Some modes always operate in + * 8G. + */ + +static int mode_to_mg[8][2] = { + { 0, 0 }, + { BIT_TO_8G, BIT_TO_2G }, + { BIT_TO_8G, BIT_TO_2G }, + { BIT_TO_8G, BIT_TO_8G }, + { BIT_TO_8G, BIT_TO_8G }, + { BIT_TO_8G, BIT_TO_2G }, + { BIT_TO_8G, BIT_TO_2G }, + { 0, 0}, +}; + +static void decode_mg(struct cma3000_accl_data *data, int *datax, + int *datay, int *dataz) +{ + /* Data in 2's complement, convert to mg */ + *datax = ((s8)*datax) * data->bit_to_mg; + *datay = ((s8)*datay) * data->bit_to_mg; + *dataz = ((s8)*dataz) * data->bit_to_mg; +} + +static irqreturn_t cma3000_thread_irq(int irq, void *dev_id) +{ + struct cma3000_accl_data *data = dev_id; + int datax, datay, dataz; + u8 ctrl, mode, range, intr_status; + + intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status"); + if (intr_status < 0) + return IRQ_NONE; + + /* Check if free fall is detected, report immediately */ + if (intr_status & CMA3000_INTSTATUS_FFDET) { + input_report_abs(data->input_dev, ABS_MISC, 1); + input_sync(data->input_dev); + } else { + input_report_abs(data->input_dev, ABS_MISC, 0); + } + + datax = CMA3000_READ(data, CMA3000_DOUTX, "X"); + datay = CMA3000_READ(data, CMA3000_DOUTY, "Y"); + dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z"); + + ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl"); + mode = (ctrl & CMA3000_MODEMASK) >> 1; + range = (ctrl & CMA3000_GRANGEMASK) >> 7; + + data->bit_to_mg = mode_to_mg[mode][range]; + + /* Interrupt not for this device */ + if (data->bit_to_mg == 0) + return IRQ_NONE; + + /* Decode register values to milli g */ + decode_mg(data, &datax, &datay, &dataz); + + input_report_abs(data->input_dev, ABS_X, datax); + input_report_abs(data->input_dev, ABS_Y, datay); + input_report_abs(data->input_dev, ABS_Z, dataz); + input_sync(data->input_dev); + + return IRQ_HANDLED; +} + +static int cma3000_reset(struct cma3000_accl_data *data) +{ + int val; + + /* Reset sequence */ + CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset"); + CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset"); + CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset"); + + /* Settling time delay */ + mdelay(10); + + val = CMA3000_READ(data, CMA3000_STATUS, "Status"); + if (val < 0) { + dev_err(data->dev, "Reset failed\n"); + return val; + } + + if (val & CMA3000_STATUS_PERR) { + dev_err(data->dev, "Parity Error\n"); + return -EIO; + } + + return 0; +} + +static int cma3000_poweron(struct cma3000_accl_data *data) +{ + const struct cma3000_platform_data *pdata = data->pdata; + u8 ctrl = 0; + int ret; + + if (data->g_range == CMARANGE_2G) { + ctrl = (data->mode << 1) | CMA3000_RANGE2G; + } else if (data->g_range == CMARANGE_8G) { + ctrl = (data->mode << 1) | CMA3000_RANGE8G; + } else { + dev_info(data->dev, + "Invalid G range specified, assuming 8G\n"); + ctrl = (data->mode << 1) | CMA3000_RANGE8G; + } + + ctrl |= data->bus_ops->ctrl_mod; + + CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr, + "Motion Detect Threshold"); + CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr, + "Time register"); + CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr, + "Free fall threshold"); + ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting"); + if (ret < 0) + return -EIO; + + msleep(CMA3000_SETDELAY); + + return 0; +} + +static int cma3000_poweroff(struct cma3000_accl_data *data) +{ + int ret; + + ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting"); + msleep(CMA3000_SETDELAY); + + return ret; +} + +static int cma3000_open(struct input_dev *input_dev) +{ + struct cma3000_accl_data *data = input_get_drvdata(input_dev); + + mutex_lock(&data->mutex); + + if (!data->suspended) + cma3000_poweron(data); + + data->opened = true; + + mutex_unlock(&data->mutex); + + return 0; +} + +static void cma3000_close(struct input_dev *input_dev) +{ + struct cma3000_accl_data *data = input_get_drvdata(input_dev); + + mutex_lock(&data->mutex); + + if (!data->suspended) + cma3000_poweroff(data); + + data->opened = false; + + mutex_unlock(&data->mutex); +} + +void cma3000_suspend(struct cma3000_accl_data *data) +{ + mutex_lock(&data->mutex); + + if (!data->suspended && data->opened) + cma3000_poweroff(data); + + data->suspended = true; + + mutex_unlock(&data->mutex); +} +EXPORT_SYMBOL(cma3000_suspend); + + +void cma3000_resume(struct cma3000_accl_data *data) +{ + mutex_lock(&data->mutex); + + if (data->suspended && data->opened) + cma3000_poweron(data); + + data->suspended = false; + + mutex_unlock(&data->mutex); +} +EXPORT_SYMBOL(cma3000_resume); + +struct cma3000_accl_data *cma3000_init(struct device *dev, int irq, + const struct cma3000_bus_ops *bops) +{ + const struct cma3000_platform_data *pdata = dev->platform_data; + struct cma3000_accl_data *data; + struct input_dev *input_dev; + int rev; + int error; + + if (!pdata) { + dev_err(dev, "platform data not found\n"); + error = -EINVAL; + goto err_out; + } + + + /* if no IRQ return error */ + if (irq == 0) { + error = -EINVAL; + goto err_out; + } + + data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + data->dev = dev; + data->input_dev = input_dev; + data->bus_ops = bops; + data->pdata = pdata; + data->irq = irq; + mutex_init(&data->mutex); + + data->mode = pdata->mode; + if (data->mode < CMAMODE_DEFAULT || data->mode > CMAMODE_POFF) { + data->mode = CMAMODE_MOTDET; + dev_warn(dev, + "Invalid mode specified, assuming Motion Detect\n"); + } + + data->g_range = pdata->g_range; + if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) { + dev_info(dev, + "Invalid G range specified, assuming 8G\n"); + data->g_range = CMARANGE_8G; + } + + input_dev->name = "cma3000-accelerometer"; + input_dev->id.bustype = bops->bustype; + input_dev->open = cma3000_open; + input_dev->close = cma3000_close; + + __set_bit(EV_ABS, input_dev->evbit); + + input_set_abs_params(input_dev, ABS_X, + -data->g_range, data->g_range, pdata->fuzz_x, 0); + input_set_abs_params(input_dev, ABS_Y, + -data->g_range, data->g_range, pdata->fuzz_y, 0); + input_set_abs_params(input_dev, ABS_Z, + -data->g_range, data->g_range, pdata->fuzz_z, 0); + input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0); + + input_set_drvdata(input_dev, data); + + error = cma3000_reset(data); + if (error) + goto err_free_mem; + + rev = CMA3000_READ(data, CMA3000_REVID, "Revid"); + if (rev < 0) { + error = rev; + goto err_free_mem; + } + + pr_info("CMA3000 Accelerometer: Revision %x\n", rev); + + error = request_threaded_irq(irq, NULL, cma3000_thread_irq, + pdata->irqflags | IRQF_ONESHOT, + "cma3000_d0x", data); + if (error) { + dev_err(dev, "request_threaded_irq failed\n"); + goto err_free_mem; + } + + error = input_register_device(data->input_dev); + if (error) { + dev_err(dev, "Unable to register input device\n"); + goto err_free_irq; + } + + return data; + +err_free_irq: + free_irq(irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); +err_out: + return ERR_PTR(error); +} +EXPORT_SYMBOL(cma3000_init); + +void cma3000_exit(struct cma3000_accl_data *data) +{ + free_irq(data->irq, data); + input_unregister_device(data->input_dev); + kfree(data); +} +EXPORT_SYMBOL(cma3000_exit); + +MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hemanth V "); diff --git a/drivers/input/misc/cma3000_d0x.h b/drivers/input/misc/cma3000_d0x.h new file mode 100644 index 000000000000..2304ce306e1c --- /dev/null +++ b/drivers/input/misc/cma3000_d0x.h @@ -0,0 +1,42 @@ +/* + * VTI CMA3000_D0x Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Hemanth V + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _INPUT_CMA3000_H +#define _INPUT_CMA3000_H + +#include +#include + +struct device; +struct cma3000_accl_data; + +struct cma3000_bus_ops { + u16 bustype; + u8 ctrl_mod; + int (*read)(struct device *, u8, char *); + int (*write)(struct device *, u8, u8, char *); +}; + +struct cma3000_accl_data *cma3000_init(struct device *dev, int irq, + const struct cma3000_bus_ops *bops); +void cma3000_exit(struct cma3000_accl_data *); +void cma3000_suspend(struct cma3000_accl_data *); +void cma3000_resume(struct cma3000_accl_data *); + +#endif diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c new file mode 100644 index 000000000000..c52d278a7942 --- /dev/null +++ b/drivers/input/misc/cma3000_d0x_i2c.c @@ -0,0 +1,141 @@ +/* + * Implements I2C interface for VTI CMA300_D0x Accelerometer driver + * + * Copyright (C) 2010 Texas Instruments + * Author: Hemanth V + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include "cma3000_d0x.h" + +static int cma3000_i2c_set(struct device *dev, + u8 reg, u8 val, char *msg) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret < 0) + dev_err(&client->dev, + "%s failed (%s, %d)\n", __func__, msg, ret); + return ret; +} + +static int cma3000_i2c_read(struct device *dev, u8 reg, char *msg) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) + dev_err(&client->dev, + "%s failed (%s, %d)\n", __func__, msg, ret); + return ret; +} + +static const struct cma3000_bus_ops cma3000_i2c_bops = { + .bustype = BUS_I2C, +#define CMA3000_BUSI2C (0 << 4) + .ctrl_mod = CMA3000_BUSI2C, + .read = cma3000_i2c_read, + .write = cma3000_i2c_set, +}; + +static int __devinit cma3000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cma3000_accl_data *data; + + data = cma3000_init(&client->dev, client->irq, &cma3000_i2c_bops); + if (IS_ERR(data)) + return PTR_ERR(data); + + i2c_set_clientdata(client, data); + + return 0; +} + +static int __devexit cma3000_i2c_remove(struct i2c_client *client) +{ + struct cma3000_accl_data *data = i2c_get_clientdata(client); + + cma3000_exit(data); + + return 0; +} + +#ifdef CONFIG_PM +static int cma3000_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cma3000_accl_data *data = i2c_get_clientdata(client); + + cma3000_suspend(data); + + return 0; +} + +static int cma3000_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cma3000_accl_data *data = i2c_get_clientdata(client); + + cma3000_resume(data); + + return 0; +} + +static const struct dev_pm_ops cma3000_i2c_pm_ops = { + .suspend = cma3000_i2c_suspend, + .resume = cma3000_i2c_resume, +}; +#endif + +static const struct i2c_device_id cma3000_i2c_id[] = { + { "cma3000_d01", 0 }, + { }, +}; + +static struct i2c_driver cma3000_i2c_driver = { + .probe = cma3000_i2c_probe, + .remove = __devexit_p(cma3000_i2c_remove), + .id_table = cma3000_i2c_id, + .driver = { + .name = "cma3000_i2c_accl", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &cma3000_i2c_pm_ops, +#endif + }, +}; + +static int __init cma3000_i2c_init(void) +{ + return i2c_add_driver(&cma3000_i2c_driver); +} + +static void __exit cma3000_i2c_exit(void) +{ + i2c_del_driver(&cma3000_i2c_driver); +} + +module_init(cma3000_i2c_init); +module_exit(cma3000_i2c_exit); + +MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hemanth V "); -- cgit v1.2.3 From 4eb3c30b2e034b673df3e8f21b497e39f3911a02 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 29 Nov 2010 23:33:07 -0800 Subject: Input: i8042 - use pr_, pr_fmt, fix dbg and __FILE__ use Standardized message logging prefixes. Removed \n from dbg macro, added \n to each dbg call site. Removed direct use of __FILE__ from dbg, converted to pr_fmt(fmt) Added non-debug printf argument verification of dbg calls Removed "i8042.c" from printks, converted to pr_ Signed-off-by: Joe Perches Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 30 +++++------- drivers/input/serio/i8042.c | 92 ++++++++++++++++------------------- drivers/input/serio/i8042.h | 14 ++++-- 3 files changed, 63 insertions(+), 73 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index a5475b577086..cd9ff808bd23 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -752,7 +752,7 @@ static int __init i8042_pnp_init(void) #endif if (i8042_nopnp) { - printk(KERN_INFO "i8042: PNP detection disabled\n"); + pr_info("PNP detection disabled\n"); return 0; } @@ -769,7 +769,7 @@ static int __init i8042_pnp_init(void) #if defined(__ia64__) return -ENODEV; #else - printk(KERN_INFO "PNP: No PS/2 controller found. Probing ports directly.\n"); + pr_info("PNP: No PS/2 controller found. Probing ports directly.\n"); return 0; #endif } @@ -781,7 +781,7 @@ static int __init i8042_pnp_init(void) snprintf(aux_irq_str, sizeof(aux_irq_str), "%d", i8042_pnp_aux_irq); - printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n", + pr_info("PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n", i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "", i8042_pnp_aux_name, i8042_pnp_data_reg, i8042_pnp_command_reg, @@ -798,9 +798,7 @@ static int __init i8042_pnp_init(void) if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) && i8042_pnp_data_reg != i8042_data_reg) || !i8042_pnp_data_reg) { - printk(KERN_WARNING - "PNP: PS/2 controller has invalid data port %#x; " - "using default %#x\n", + pr_warn("PNP: PS/2 controller has invalid data port %#x; using default %#x\n", i8042_pnp_data_reg, i8042_data_reg); i8042_pnp_data_reg = i8042_data_reg; pnp_data_busted = true; @@ -809,33 +807,27 @@ static int __init i8042_pnp_init(void) if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) && i8042_pnp_command_reg != i8042_command_reg) || !i8042_pnp_command_reg) { - printk(KERN_WARNING - "PNP: PS/2 controller has invalid command port %#x; " - "using default %#x\n", + pr_warn("PNP: PS/2 controller has invalid command port %#x; using default %#x\n", i8042_pnp_command_reg, i8042_command_reg); i8042_pnp_command_reg = i8042_command_reg; pnp_data_busted = true; } if (!i8042_nokbd && !i8042_pnp_kbd_irq) { - printk(KERN_WARNING - "PNP: PS/2 controller doesn't have KBD irq; " - "using default %d\n", i8042_kbd_irq); + pr_warn("PNP: PS/2 controller doesn't have KBD irq; using default %d\n", + i8042_kbd_irq); i8042_pnp_kbd_irq = i8042_kbd_irq; pnp_data_busted = true; } if (!i8042_noaux && !i8042_pnp_aux_irq) { if (!pnp_data_busted && i8042_pnp_kbd_irq) { - printk(KERN_WARNING - "PNP: PS/2 appears to have AUX port disabled, " - "if this is incorrect please boot with " - "i8042.nopnp\n"); + pr_warn("PNP: PS/2 appears to have AUX port disabled, " + "if this is incorrect please boot with i8042.nopnp\n"); i8042_noaux = true; } else { - printk(KERN_WARNING - "PNP: PS/2 controller doesn't have AUX irq; " - "using default %d\n", i8042_aux_irq); + pr_warn("PNP: PS/2 controller doesn't have AUX irq; using default %d\n", + i8042_aux_irq); i8042_pnp_aux_irq = i8042_aux_irq; } } diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 18db5a8c7478..c04ff00a3663 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -10,6 +10,8 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -225,8 +227,8 @@ static int i8042_flush(void) udelay(50); data = i8042_read_data(); i++; - dbg("%02x <- i8042 (flush, %s)", data, - str & I8042_STR_AUXDATA ? "aux" : "kbd"); + dbg("%02x <- i8042 (flush, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); } spin_unlock_irqrestore(&i8042_lock, flags); @@ -253,32 +255,32 @@ static int __i8042_command(unsigned char *param, int command) if (error) return error; - dbg("%02x -> i8042 (command)", command & 0xff); + dbg("%02x -> i8042 (command)\n", command & 0xff); i8042_write_command(command & 0xff); for (i = 0; i < ((command >> 12) & 0xf); i++) { error = i8042_wait_write(); if (error) return error; - dbg("%02x -> i8042 (parameter)", param[i]); + dbg("%02x -> i8042 (parameter)\n", param[i]); i8042_write_data(param[i]); } for (i = 0; i < ((command >> 8) & 0xf); i++) { error = i8042_wait_read(); if (error) { - dbg(" -- i8042 (timeout)"); + dbg(" -- i8042 (timeout)\n"); return error; } if (command == I8042_CMD_AUX_LOOP && !(i8042_read_status() & I8042_STR_AUXDATA)) { - dbg(" -- i8042 (auxerr)"); + dbg(" -- i8042 (auxerr)\n"); return -1; } param[i] = i8042_read_data(); - dbg("%02x <- i8042 (return)", param[i]); + dbg("%02x <- i8042 (return)\n", param[i]); } return 0; @@ -309,7 +311,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c) spin_lock_irqsave(&i8042_lock, flags); if (!(retval = i8042_wait_write())) { - dbg("%02x -> i8042 (kbd-data)", c); + dbg("%02x -> i8042 (kbd-data)\n", c); i8042_write_data(c); } @@ -355,17 +357,14 @@ static void i8042_port_close(struct serio *serio) i8042_ctr &= ~irq_bit; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) - printk(KERN_WARNING - "i8042.c: Can't write CTR while closing %s port.\n", - port_name); + pr_warn("Can't write CTR while closing %s port\n", port_name); udelay(50); i8042_ctr &= ~disable_bit; i8042_ctr |= irq_bit; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) - printk(KERN_ERR "i8042.c: Can't reactivate %s port.\n", - port_name); + pr_err("Can't reactivate %s port\n", port_name); /* * See if there is any data appeared while we were messing with @@ -456,7 +455,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) str = i8042_read_status(); if (unlikely(~str & I8042_STR_OBF)) { spin_unlock_irqrestore(&i8042_lock, flags); - if (irq) dbg("Interrupt %d, without any data", irq); + if (irq) + dbg("Interrupt %d, without any data\n", irq); ret = 0; goto out; } @@ -469,7 +469,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) dfl = 0; if (str & I8042_STR_MUXERR) { - dbg("MUX error, status is %02x, data is %02x", str, data); + dbg("MUX error, status is %02x, data is %02x\n", + str, data); /* * When MUXERR condition is signalled the data register can only contain * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately @@ -512,7 +513,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) port = &i8042_ports[port_no]; serio = port->exists ? port->serio : NULL; - dbg("%02x <- i8042 (interrupt, %d, %d%s%s)", + dbg("%02x <- i8042 (interrupt, %d, %d%s%s)\n", data, port_no, irq, dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_TIMEOUT ? ", timeout" : ""); @@ -540,7 +541,7 @@ static int i8042_enable_kbd_port(void) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { i8042_ctr &= ~I8042_CTR_KBDINT; i8042_ctr |= I8042_CTR_KBDDIS; - printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n"); + pr_err("Failed to enable KBD port\n"); return -EIO; } @@ -559,7 +560,7 @@ static int i8042_enable_aux_port(void) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { i8042_ctr &= ~I8042_CTR_AUXINT; i8042_ctr |= I8042_CTR_AUXDIS; - printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n"); + pr_err("Failed to enable AUX port\n"); return -EIO; } @@ -641,7 +642,7 @@ static int __init i8042_check_mux(void) if (i8042_set_mux_mode(true, &mux_version)) return -1; - printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n", + pr_info("Detected active multiplexing controller, rev %d.%d\n", (mux_version >> 4) & 0xf, mux_version & 0xf); /* @@ -651,7 +652,7 @@ static int __init i8042_check_mux(void) i8042_ctr &= ~I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n"); + pr_err("Failed to disable AUX port, can't use MUX\n"); return -EIO; } @@ -676,8 +677,8 @@ static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id) str = i8042_read_status(); if (str & I8042_STR_OBF) { data = i8042_read_data(); - dbg("%02x <- i8042 (aux_test_irq, %s)", - data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); + dbg("%02x <- i8042 (aux_test_irq, %s)\n", + data, str & I8042_STR_AUXDATA ? "aux" : "kbd"); if (i8042_irq_being_tested && data == 0xa5 && (str & I8042_STR_AUXDATA)) complete(&i8042_aux_irq_delivered); @@ -770,8 +771,8 @@ static int __init i8042_check_aux(void) */ if (i8042_toggle_aux(false)) { - printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); - printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n"); + pr_warn("Failed to disable AUX port, but continuing anyway... Is this a SiS?\n"); + pr_warn("If AUX port is really absent please use the 'i8042.noaux' option\n"); } if (i8042_toggle_aux(true)) @@ -819,7 +820,7 @@ static int __init i8042_check_aux(void) * AUX IRQ was never delivered so we need to flush the controller to * get rid of the byte we put there; otherwise keyboard may not work. */ - dbg(" -- i8042 (aux irq test timeout)"); + dbg(" -- i8042 (aux irq test timeout)\n"); i8042_flush(); retval = -1; } @@ -845,7 +846,7 @@ static int __init i8042_check_aux(void) static int i8042_controller_check(void) { if (i8042_flush() == I8042_BUFFER_SIZE) { - printk(KERN_ERR "i8042.c: No controller found.\n"); + pr_err("No controller found\n"); return -ENODEV; } @@ -864,15 +865,15 @@ static int i8042_controller_selftest(void) do { if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { - printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n"); + pr_err("i8042 controller self test timeout\n"); return -ENODEV; } if (param == I8042_RET_CTL_TEST) return 0; - printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n", - param, I8042_RET_CTL_TEST); + pr_err("i8042 controller selftest failed. (%#x != %#x)\n", + param, I8042_RET_CTL_TEST); msleep(50); } while (i++ < 5); @@ -883,8 +884,7 @@ static int i8042_controller_selftest(void) * and user will still get a working keyboard. This is especially * important on netbooks. On other arches we trust hardware more. */ - printk(KERN_INFO - "i8042: giving up on controller selftest, continuing anyway...\n"); + pr_info("giving up on controller selftest, continuing anyway...\n"); return 0; #else return -EIO; @@ -909,8 +909,7 @@ static int i8042_controller_init(void) do { if (n >= 10) { - printk(KERN_ERR - "i8042.c: Unable to get stable CTR read.\n"); + pr_err("Unable to get stable CTR read\n"); return -EIO; } @@ -918,8 +917,7 @@ static int i8042_controller_init(void) udelay(50); if (i8042_command(&ctr[n++ % 2], I8042_CMD_CTL_RCTR)) { - printk(KERN_ERR - "i8042.c: Can't read CTR while initializing i8042.\n"); + pr_err("Can't read CTR while initializing i8042\n"); return -EIO; } @@ -943,7 +941,7 @@ static int i8042_controller_init(void) if (i8042_unlock) i8042_ctr |= I8042_CTR_IGNKEYLOCK; else - printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); + pr_warn("Warning: Keylock active\n"); } spin_unlock_irqrestore(&i8042_lock, flags); @@ -970,7 +968,7 @@ static int i8042_controller_init(void) */ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n"); + pr_err("Can't write CTR while initializing i8042\n"); return -EIO; } @@ -1000,7 +998,7 @@ static void i8042_controller_reset(void) i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT); if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) - printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n"); + pr_warn("Can't write CTR while resetting\n"); /* * Disable MUX mode if present. @@ -1021,7 +1019,7 @@ static void i8042_controller_reset(void) */ if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR)) - printk(KERN_WARNING "i8042.c: Can't restore CTR.\n"); + pr_warn("Can't restore CTR\n"); } @@ -1045,14 +1043,14 @@ static long i8042_panic_blink(int state) led = (state) ? 0x01 | 0x04 : 0; while (i8042_read_status() & I8042_STR_IBF) DELAY; - dbg("%02x -> i8042 (panic blink)", 0xed); + dbg("%02x -> i8042 (panic blink)\n", 0xed); i8042_suppress_kbd_ack = 2; i8042_write_data(0xed); /* set leds */ DELAY; while (i8042_read_status() & I8042_STR_IBF) DELAY; DELAY; - dbg("%02x -> i8042 (panic blink)", led); + dbg("%02x -> i8042 (panic blink)\n", led); i8042_write_data(led); DELAY; return delay; @@ -1068,9 +1066,7 @@ static void i8042_dritek_enable(void) error = i8042_command(¶m, 0x1059); if (error) - printk(KERN_WARNING - "Failed to enable DRITEK extension: %d\n", - error); + pr_warn("Failed to enable DRITEK extension: %d\n", error); } #endif @@ -1105,10 +1101,10 @@ static int i8042_controller_resume(bool force_reset) i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS; i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT); if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_WARNING "i8042: Can't write CTR to resume, retrying...\n"); + pr_warn("Can't write CTR to resume, retrying...\n"); msleep(50); if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - printk(KERN_ERR "i8042: CTR write retry failed\n"); + pr_err("CTR write retry failed\n"); return -EIO; } } @@ -1121,9 +1117,7 @@ static int i8042_controller_resume(bool force_reset) if (i8042_mux_present) { if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports()) - printk(KERN_WARNING - "i8042: failed to resume active multiplexor, " - "mouse won't work.\n"); + pr_warn("failed to resume active multiplexor, mouse won't work\n"); } else if (i8042_ports[I8042_AUX_PORT_NO].serio) i8042_enable_aux_port(); diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h index cbc1beb66574..ac1d759d0f55 100644 --- a/drivers/input/serio/i8042.h +++ b/drivers/input/serio/i8042.h @@ -89,15 +89,19 @@ #ifdef DEBUG static unsigned long i8042_start_time; #define dbg_init() do { i8042_start_time = jiffies; } while (0) -#define dbg(format, arg...) \ - do { \ +#define dbg(format, arg...) \ + do { \ if (i8042_debug) \ - printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" , \ - ## arg, (int) (jiffies - i8042_start_time)); \ + printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format, \ + (int) (jiffies - i8042_start_time), ##arg); \ } while (0) #else #define dbg_init() do { } while (0) -#define dbg(format, arg...) do {} while (0) +#define dbg(format, arg...) \ + do { \ + if (0) \ + printk(KERN_DEBUG pr_fmt(format), ##arg); \ + } while (0) #endif #endif /* _I8042_H */ -- cgit v1.2.3 From da0c490115de026618a7fdcd886602da44392a50 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 29 Nov 2010 23:33:07 -0800 Subject: Input: use pr_fmt and pr_ Signed-off-by: Joe Perches Signed-off-by: Dmitry Torokhov --- drivers/input/apm-power.c | 14 ++++++-------- drivers/input/evbug.c | 18 ++++++++++-------- drivers/input/evdev.c | 15 ++++++++------- drivers/input/ff-core.c | 11 +++++------ drivers/input/ff-memless.c | 31 +++++++++++++++---------------- drivers/input/input-polldev.c | 5 +++-- drivers/input/input.c | 22 +++++++++++----------- drivers/input/joydev.c | 4 +++- drivers/input/mousedev.c | 10 ++++++---- drivers/input/xen-kbdfront.c | 10 +++++----- 10 files changed, 72 insertions(+), 68 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/apm-power.c b/drivers/input/apm-power.c index 7d61a9660806..e90ee3d30613 100644 --- a/drivers/input/apm-power.c +++ b/drivers/input/apm-power.c @@ -9,6 +9,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -23,8 +25,7 @@ static void system_power_event(unsigned int keycode) switch (keycode) { case KEY_SUSPEND: apm_queue_event(APM_USER_SUSPEND); - - printk(KERN_INFO "apm-power: Requesting system suspend...\n"); + pr_info("Requesting system suspend...\n"); break; default: break; @@ -65,18 +66,15 @@ static int apmpower_connect(struct input_handler *handler, error = input_register_handle(handle); if (error) { - printk(KERN_ERR - "apm-power: Failed to register input power handler, " - "error %d\n", error); + pr_err("Failed to register input power handler, error %d\n", + error); kfree(handle); return error; } error = input_open_device(handle); if (error) { - printk(KERN_ERR - "apm-power: Failed to open input power device, " - "error %d\n", error); + pr_err("Failed to open input power device, error %d\n", error); input_unregister_handle(handle); kfree(handle); return error; diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index f7c5c14ec12a..cd4e6679d61a 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -26,6 +26,8 @@ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -38,8 +40,8 @@ MODULE_LICENSE("GPL"); static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { - printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", - dev_name(&handle->dev->dev), type, code, value); + printk(KERN_DEBUG pr_fmt("Event. Dev: %s, Type: %d, Code: %d, Value: %d\n"), + dev_name(&handle->dev->dev), type, code, value); } static int evbug_connect(struct input_handler *handler, struct input_dev *dev, @@ -64,10 +66,10 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev, if (error) goto err_unregister_handle; - printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n", - dev_name(&dev->dev), - dev->name ?: "unknown", - dev->phys ?: "unknown"); + printk(KERN_DEBUG pr_fmt("Connected device: %s (%s at %s)\n"), + dev_name(&dev->dev), + dev->name ?: "unknown", + dev->phys ?: "unknown"); return 0; @@ -80,8 +82,8 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev, static void evbug_disconnect(struct input_handle *handle) { - printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", - dev_name(&handle->dev->dev)); + printk(KERN_DEBUG pr_fmt("Disconnected device: %s\n"), + dev_name(&handle->dev->dev)); input_close_device(handle); input_unregister_handle(handle); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index e3f7fc6f9565..17660b1c8591 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -8,6 +8,8 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define EVDEV_MINOR_BASE 64 #define EVDEV_MINORS 32 #define EVDEV_MIN_BUFFER_SIZE 64U @@ -522,12 +524,11 @@ static int handle_eviocgbit(struct input_dev *dev, if (type == EV_KEY && size == OLD_KEY_MAX) { len = OLD_KEY_MAX; if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000)) - printk(KERN_WARNING - "evdev.c(EVIOCGBIT): Suspicious buffer size %u, " - "limiting output to %zu bytes. See " - "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n", - OLD_KEY_MAX, - BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long)); + pr_warning("(EVIOCGBIT): Suspicious buffer size %u, " + "limiting output to %zu bytes. See " + "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n", + OLD_KEY_MAX, + BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long)); } return bits_to_user(bits, len, size, p, compat_mode); @@ -894,7 +895,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, break; if (minor == EVDEV_MINORS) { - printk(KERN_ERR "evdev: no more free evdev devices\n"); + pr_err("no more free evdev devices\n"); return -ENFILE; } diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 03078c08309a..3367f760d75a 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -23,7 +23,7 @@ /* #define DEBUG */ -#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg) +#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt #include #include @@ -116,7 +116,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX || !test_bit(effect->type, dev->ffbit)) { - debug("invalid or not supported effect type in upload"); + pr_debug("invalid or not supported effect type in upload\n"); return -EINVAL; } @@ -124,7 +124,7 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, (effect->u.periodic.waveform < FF_WAVEFORM_MIN || effect->u.periodic.waveform > FF_WAVEFORM_MAX || !test_bit(effect->u.periodic.waveform, dev->ffbit))) { - debug("invalid or not supported wave form in upload"); + pr_debug("invalid or not supported wave form in upload\n"); return -EINVAL; } @@ -246,7 +246,7 @@ static int flush_effects(struct input_dev *dev, struct file *file) struct ff_device *ff = dev->ff; int i; - debug("flushing now"); + pr_debug("flushing now\n"); mutex_lock(&ff->mutex); @@ -315,8 +315,7 @@ int input_ff_create(struct input_dev *dev, int max_effects) int i; if (!max_effects) { - printk(KERN_ERR - "ff-core: cannot allocate device without any effects\n"); + pr_err("cannot allocate device without any effects\n"); return -EINVAL; } diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 1d881c96ba8f..117a59aaa70e 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -23,7 +23,7 @@ /* #define DEBUG */ -#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg) +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include @@ -129,7 +129,7 @@ static void ml_schedule_timer(struct ml_device *ml) int events = 0; int i; - debug("calculating next timer"); + pr_debug("calculating next timer\n"); for (i = 0; i < FF_MEMLESS_EFFECTS; i++) { @@ -149,10 +149,10 @@ static void ml_schedule_timer(struct ml_device *ml) } if (!events) { - debug("no actions"); + pr_debug("no actions\n"); del_timer(&ml->timer); } else { - debug("timer set"); + pr_debug("timer set\n"); mod_timer(&ml->timer, earliest); } } @@ -173,8 +173,8 @@ static int apply_envelope(struct ml_effect_state *state, int value, if (envelope->attack_length && time_before(now, state->play_at + msecs_to_jiffies(envelope->attack_length))) { - debug("value = 0x%x, attack_level = 0x%x", value, - envelope->attack_level); + pr_debug("value = 0x%x, attack_level = 0x%x\n", + value, envelope->attack_level); time_from_level = jiffies_to_msecs(now - state->play_at); time_of_envelope = envelope->attack_length; envelope_level = min_t(__s16, envelope->attack_level, 0x7fff); @@ -191,13 +191,13 @@ static int apply_envelope(struct ml_effect_state *state, int value, difference = abs(value) - envelope_level; - debug("difference = %d", difference); - debug("time_from_level = 0x%x", time_from_level); - debug("time_of_envelope = 0x%x", time_of_envelope); + pr_debug("difference = %d\n", difference); + pr_debug("time_from_level = 0x%x\n", time_from_level); + pr_debug("time_of_envelope = 0x%x\n", time_of_envelope); difference = difference * time_from_level / time_of_envelope; - debug("difference = %d", difference); + pr_debug("difference = %d\n", difference); return value < 0 ? -(difference + envelope_level) : (difference + envelope_level); @@ -215,8 +215,7 @@ static int get_compatible_type(struct ff_device *ff, int effect_type) if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit)) return FF_RUMBLE; - printk(KERN_ERR - "ff-memless: invalid type in get_compatible_type()\n"); + pr_err("invalid type in get_compatible_type()\n"); return 0; } @@ -312,7 +311,7 @@ static void ml_combine_effects(struct ff_effect *effect, break; default: - printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n"); + pr_err("invalid type in ml_combine_effects()\n"); break; } @@ -406,7 +405,7 @@ static void ml_effect_timer(unsigned long timer_data) struct ml_device *ml = dev->ff->private; unsigned long flags; - debug("timer: updating effects"); + pr_debug("timer: updating effects\n"); spin_lock_irqsave(&dev->event_lock, flags); ml_play_effects(ml); @@ -438,7 +437,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) struct ml_effect_state *state = &ml->states[effect_id]; if (value > 0) { - debug("initiated play"); + pr_debug("initiated play\n"); __set_bit(FF_EFFECT_STARTED, &state->flags); state->count = value; @@ -449,7 +448,7 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) state->adj_at = state->play_at; } else { - debug("initiated stop"); + pr_debug("initiated stop\n"); if (test_bit(FF_EFFECT_PLAYING, &state->flags)) __set_bit(FF_EFFECT_ABORTING, &state->flags); diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 10c9b0a845f0..0559e309bac9 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -8,6 +8,8 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -33,8 +35,7 @@ static int input_polldev_start_workqueue(void) if (!polldev_users) { polldev_wq = create_singlethread_workqueue("ipolldevd"); if (!polldev_wq) { - printk(KERN_ERR "input-polldev: failed to create " - "ipolldevd workqueue\n"); + pr_err("failed to create ipolldevd workqueue\n"); retval = -ENOMEM; goto out; } diff --git a/drivers/input/input.c b/drivers/input/input.c index 7f26ca6ecf75..c7a1e826c580 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -10,6 +10,8 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_BASENAME ": " fmt + #include #include #include @@ -959,10 +961,8 @@ static int input_attach_handler(struct input_dev *dev, struct input_handler *han error = handler->connect(handler, dev, id); if (error && error != -ENODEV) - printk(KERN_ERR - "input: failed to attach handler %s to device %s, " - "error: %d\n", - handler->name, kobject_name(&dev->dev.kobj), error); + pr_err("failed to attach handler %s to device %s, error: %d\n", + handler->name, kobject_name(&dev->dev.kobj), error); return error; } @@ -1820,9 +1820,8 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int break; default: - printk(KERN_ERR - "input_set_capability: unknown type %u (code %u)\n", - type, code); + pr_err("input_set_capability: unknown type %u (code %u)\n", + type, code); dump_stack(); return; } @@ -1904,8 +1903,9 @@ int input_register_device(struct input_dev *dev) return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); - printk(KERN_INFO "input: %s as %s\n", - dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); + pr_info("%s as %s\n", + dev->name ? dev->name : "Unspecified device", + path ? path : "N/A"); kfree(path); error = mutex_lock_interruptible(&input_mutex); @@ -2187,7 +2187,7 @@ static int __init input_init(void) err = class_register(&input_class); if (err) { - printk(KERN_ERR "input: unable to register input_dev class\n"); + pr_err("unable to register input_dev class\n"); return err; } @@ -2197,7 +2197,7 @@ static int __init input_init(void) err = register_chrdev(INPUT_MAJOR, "input", &input_fops); if (err) { - printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); + pr_err("unable to register char major %d", INPUT_MAJOR); goto fail2; } diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 9d424cebfd2c..3182c9cd1b0e 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -10,6 +10,8 @@ * (at your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -806,7 +808,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, break; if (minor == JOYDEV_MINORS) { - printk(KERN_ERR "joydev: no more free joydev devices\n"); + pr_err("no more free joydev devices\n"); return -ENFILE; } diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 2a00ddf4f23a..7630273e9474 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -9,6 +9,8 @@ * the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define MOUSEDEV_MINOR_BASE 32 #define MOUSEDEV_MINORS 32 #define MOUSEDEV_MIX 31 @@ -977,7 +979,7 @@ static int mousedev_connect(struct input_handler *handler, break; if (minor == MOUSEDEV_MINORS) { - printk(KERN_ERR "mousedev: no more free mousedev devices\n"); + pr_err("no more free mousedev devices\n"); return -ENFILE; } @@ -1087,13 +1089,13 @@ static int __init mousedev_init(void) #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX error = misc_register(&psaux_mouse); if (error) - printk(KERN_WARNING "mice: could not register psaux device, " - "error: %d\n", error); + pr_warning("could not register psaux device, error: %d\n", + error); else psaux_registered = 1; #endif - printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); + pr_info("PS/2 mouse device common for all mice\n"); return 0; } diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index e0c024db2ca5..7f85a862ad11 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -17,6 +17,8 @@ * Switch to grant tables together with xen-fbfront.c. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -84,9 +86,8 @@ static irqreturn_t input_handler(int rq, void *dev_id) input_report_key(dev, event->key.keycode, event->key.pressed); else - printk(KERN_WARNING - "xenkbd: unhandled keycode 0x%x\n", - event->key.keycode); + pr_warning("unhandled keycode 0x%x\n", + event->key.keycode); break; case XENKBD_TYPE_POS: input_report_abs(dev, ABS_X, event->pos.abs_x); @@ -292,8 +293,7 @@ InitWait: ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, "request-abs-pointer", "1"); if (ret) - printk(KERN_WARNING - "xenkbd: can't request abs-pointer"); + pr_warning("can't request abs-pointer\n"); } xenbus_switch_state(dev, XenbusStateConnected); break; -- cgit v1.2.3 From a318e6b1a04c9d8882712508eb9c8a061796c06b Mon Sep 17 00:00:00 2001 From: David Foley Date: Tue, 30 Nov 2010 23:45:46 -0800 Subject: Input: wacom - add IDs for two new Bamboo PTs Add two new Bamboo Pen & Touch models: Bamboo Comic Medium (CTH661/S1; Product ID = 0xd8) Bamboo P & T Special Edition Small (CTH461/L; Product ID = 0xdA) Tested-by: IRIE Shinsuke Tested-by: Andrea Cadeddu Signed-off-by: David Foley Reviewed-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index b3252ef1e279..db747e4c4c4c 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1436,6 +1436,10 @@ static struct wacom_features wacom_features_0xD2 = { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xD3 = { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD8 = + { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xDA = + { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -1504,6 +1508,8 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xD1) }, { USB_DEVICE_WACOM(0xD2) }, { USB_DEVICE_WACOM(0xD3) }, + { USB_DEVICE_WACOM(0xD8) }, + { USB_DEVICE_WACOM(0xDA) }, { USB_DEVICE_WACOM(0xF0) }, { USB_DEVICE_WACOM(0xCC) }, { USB_DEVICE_WACOM(0x90) }, -- cgit v1.2.3 From 3bfa321e662edf90fb8123a02c987c2965fa50bb Mon Sep 17 00:00:00 2001 From: Yan Li Date: Tue, 30 Nov 2010 23:51:03 -0800 Subject: Input: synaptics - fix handling of 2-button ClickPads Lenovo S10-3t's ClickPad is a 2-button ClickPad that reports BTN_LEFT and BTN_RIGHT as normal touchpad, unlike the 1-button ClickPad used in HP mini 210 that reports solely BTN_MIDDLE. In 0xc0-cap response, the 1-button ClickPad has the 20-bit set while 2-button ClickPad has the 8-bit set. This patch makes the kernel only handle 1-button ClickPad specially, and treat 2-button ClickPad in the same fashion as regular touchpads. This fixes kernel bug #18122 and MeeGo bug #4807. Signed-off-by: Yan Li Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 613a3652f98f..0aefaa885871 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -51,7 +51,8 @@ #define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) -#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100) +#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */ +#define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */ #define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000) /* synaptics modes query bits */ -- cgit v1.2.3 From 8ed9e0e1b602a0bcdc3bef52ec05fdab5b484341 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 1 Dec 2010 09:19:45 -0800 Subject: Input: turbografx - fix reference counting The ref-count of parport gained from parport_find_number() was not released in normal path. Signed-off-by: Namhyung Kim Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/turbografx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index d53b9e900234..27b6a3ce18ca 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -245,6 +245,7 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs) goto err_free_tgfx; } + parport_put_port(pp); return tgfx; err_free_dev: -- cgit v1.2.3 From 7bfbeae9c78fb9404ccbd62a25c82f5860432ac9 Mon Sep 17 00:00:00 2001 From: Guy Martin Date: Tue, 2 Nov 2010 15:23:08 +0000 Subject: parisc: Fix GSC PS/2 driver name for keyboard and mouse Fix kernel warnings caused by the driver name of GSC PS/2 containing '/'. The following warnings are observed on a K410 system : [ 10.700000] name 'GSC PS/2 keyboard' [ 10.732000] ------------[ cut here ]------------ [ 10.772000] WARNING: at fs/proc/generic.c:323 [ 10.828000] Modules linked in: [ 10.916000] [ 10.916000] YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI [ 10.936000] PSW: 00000000000001000000000000001111 Not tainted [ 10.992000] r00-03 0004000f 104fe3e0 10201ea0 00000000 [ 11.060000] r04-07 4fc405c8 00000006 4fc405c8 4fc40694 [ 11.124000] r08-11 4fc40708 10438aa0 00000001 1043bfc8 [ 11.184000] r12-15 104ff2a0 104ff2a0 4fc38634 104ff2a0 [ 11.248000] r16-19 f0001570 10479af0 f000006c 1044fe50 [ 11.308000] r20-23 00000000 00000028 104cd858 00000000 [ 11.372000] r24-27 ffffffff 0000000e 1044fe10 1043bbe0 [ 11.436000] r28-31 0000002b 00000078 4fc40800 0000000d [ 11.496000] sr00-03 00000000 00000000 00000000 00000000 [ 11.560000] sr04-07 00000000 00000000 00000000 00000000 [ 11.624000] [ 11.688000] IASQ: 00000000 00000000 IAOQ: 10201ea0 10201ea4 [ 11.704000] IIR: 03ffe01f ISR: 00000000 IOR: 0000000d [ 11.772000] CPU: 0 CR30: 4fc40000 CR31: f01043b0 [ 11.836000] ORIG_R28: 4fc40940 [ 11.904000] IAOQ[0]: __xlate_proc_name+0x90/0xd0 [ 11.940000] IAOQ[1]: __xlate_proc_name+0x94/0xd0 [ 11.996000] RP(r2): __xlate_proc_name+0x90/0xd0 [ 12.052000] Backtrace: [ 12.108000] [<10257790>] vsnprintf+0x290/0x4f4 [ 12.136000] [ 12.188000] ---[ end trace 91bf6ece17e322dd ]--- [ 12.208000] serio: GSC PS/2 keyboard port at 0x0001c000 irq 19 @ 10:12:7 [ 12.264000] name 'GSC PS/2 mouse' [ 12.344000] ------------[ cut here ]------------ [ 12.384000] WARNING: at fs/proc/generic.c:323 [ 12.436000] Modules linked in: [ 12.524000] [ 12.528000] YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI [ 12.544000] PSW: 00000000000001000000000000001111 Tainted: G W [ 12.600000] r00-03 0004000f 104fe3e0 10201ea0 00000000 [ 12.680000] r04-07 4fc405c8 00000006 4fc405c8 4fc40694 [ 12.740000] r08-11 4fc40708 10438aa0 00000001 1043bfc8 [ 12.804000] r12-15 104ff2a0 104ff2a0 4fc38634 104ff2a0 [ 12.868000] r16-19 f0001570 10479af0 f000006c 1044fe50 [ 12.928000] r20-23 00000000 00000025 104cd858 00000000 [ 12.992000] r24-27 ffffffff 0000000e 1044fe10 1043bbe0 [ 13.056000] r28-31 00000028 00000078 4fc40800 0000000d [ 13.116000] sr00-03 00000000 00000000 00000000 00000000 [ 13.180000] sr04-07 00000000 00000000 00000000 00000000 [ 13.244000] [ 13.308000] IASQ: 00000000 00000000 IAOQ: 10201ea0 10201ea4 [ 13.324000] IIR: 03ffe01f ISR: 00000000 IOR: 0000000d [ 13.392000] CPU: 0 CR30: 4fc40000 CR31: f01043b0 [ 13.456000] ORIG_R28: 4fc40940 [ 13.524000] IAOQ[0]: __xlate_proc_name+0x90/0xd0 [ 13.560000] IAOQ[1]: __xlate_proc_name+0x94/0xd0 [ 13.616000] RP(r2): __xlate_proc_name+0x90/0xd0 [ 13.672000] Backtrace: [ 13.728000] [<10257790>] vsnprintf+0x290/0x4f4 [ 13.756000] [ 13.808000] ---[ end trace 91bf6ece17e322de ]--- [ 13.828000] serio: GSC PS/2 mouse port at 0x00020100 irq 19 @ 10:12:8 Signed-off-by: Guy Martin Acked-by: Helge Deller Signed-off-by: Kyle McMartin --- drivers/input/serio/gscps2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 3c287dd879d3..4225f5d6b15f 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -358,7 +358,7 @@ static int __devinit gscps2_probe(struct parisc_device *dev) gscps2_reset(ps2port); ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f; - snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s", + snprintf(serio->name, sizeof(serio->name), "gsc-ps2-%s", (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse"); strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys)); serio->id.type = SERIO_8042; -- cgit v1.2.3 From 0e7d0c860a0dee49dacb7bbb248d1eba637075ad Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 6 Dec 2010 17:14:47 -0800 Subject: Input: add input driver for polled GPIO buttons The existing gpio-keys driver can be usable only for GPIO lines with interrupt support. Several devices have buttons connected to a GPIO line which is not capable to generate interrupts. This patch adds a new input driver using the generic GPIO layer and the input-polldev to support such buttons. [Ben Gardiner Signed-off-by: Ben Gardiner Tested-by: Ben Gardiner Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 16 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/gpio_keys_polled.c | 261 ++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 drivers/input/keyboard/gpio_keys_polled.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b8c51b9781db..3a87f3ba5f75 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -179,6 +179,22 @@ config KEYBOARD_GPIO To compile this driver as a module, choose M here: the module will be called gpio_keys. +config KEYBOARD_GPIO_POLLED + tristate "Polled GPIO buttons" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver implements support for buttons connected + to GPIO pins that are not capable of generating interrupts. + + Say Y here if your device has buttons connected + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. + + To compile this driver as a module, choose M here: the + module will be called gpio_keys_polled. + config KEYBOARD_TCA6416 tristate "TCA6416 Keypad Support" depends on I2C diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e8ebe2..622de73a445d 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o +obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c new file mode 100644 index 000000000000..4c17aff20657 --- /dev/null +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -0,0 +1,261 @@ +/* + * Driver for buttons on GPIO lines not capable of generating interrupts + * + * Copyright (C) 2007-2010 Gabor Juhos + * Copyright (C) 2010 Nuno Goncalves + * + * This file was based on: /drivers/input/misc/cobalt_btns.c + * Copyright (C) 2007 Yoichi Yuasa + * + * also was based on: /drivers/input/keyboard/gpio_keys.c + * Copyright 2005 Phil Blundell + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "gpio-keys-polled" + +struct gpio_keys_button_data { + int last_state; + int count; + int threshold; + int can_sleep; +}; + +struct gpio_keys_polled_dev { + struct input_polled_dev *poll_dev; + struct device *dev; + struct gpio_keys_platform_data *pdata; + struct gpio_keys_button_data data[0]; +}; + +static void gpio_keys_polled_check_state(struct input_dev *input, + struct gpio_keys_button *button, + struct gpio_keys_button_data *bdata) +{ + int state; + + if (bdata->can_sleep) + state = !!gpio_get_value_cansleep(button->gpio); + else + state = !!gpio_get_value(button->gpio); + + if (state != bdata->last_state) { + unsigned int type = button->type ?: EV_KEY; + + input_event(input, type, button->code, + !!(state ^ button->active_low)); + input_sync(input); + bdata->count = 0; + bdata->last_state = state; + } +} + +static void gpio_keys_polled_poll(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + struct input_dev *input = dev->input; + int i; + + for (i = 0; i < bdev->pdata->nbuttons; i++) { + struct gpio_keys_button_data *bdata = &bdev->data[i]; + + if (bdata->count < bdata->threshold) + bdata->count++; + else + gpio_keys_polled_check_state(input, &pdata->buttons[i], + bdata); + } +} + +static void gpio_keys_polled_open(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + + if (pdata->enable) + pdata->enable(bdev->dev); +} + +static void gpio_keys_polled_close(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + + if (pdata->disable) + pdata->disable(bdev->dev); +} + +static int __devinit gpio_keys_polled_probe(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct gpio_keys_polled_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + int error; + int i; + + if (!pdata || !pdata->poll_interval) + return -EINVAL; + + bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) + + pdata->nbuttons * sizeof(struct gpio_keys_button_data), + GFP_KERNEL); + if (!bdev) { + dev_err(dev, "no memory for private data\n"); + return -ENOMEM; + } + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + error = -ENOMEM; + goto err_free_bdev; + } + + poll_dev->private = bdev; + poll_dev->poll = gpio_keys_polled_poll; + poll_dev->poll_interval = pdata->poll_interval; + poll_dev->open = gpio_keys_polled_open; + poll_dev->close = gpio_keys_polled_close; + + input = poll_dev->input; + + input->evbit[0] = BIT(EV_KEY); + input->name = pdev->name; + input->phys = DRV_NAME"/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_keys_button_data *bdata = &bdev->data[i]; + unsigned int gpio = button->gpio; + unsigned int type = button->type ?: EV_KEY; + + if (button->wakeup) { + dev_err(dev, DRV_NAME " does not support wakeup\n"); + error = -EINVAL; + goto err_free_gpio; + } + + error = gpio_request(gpio, + button->desc ? button->desc : DRV_NAME); + if (error) { + dev_err(dev, "unable to claim gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + error = gpio_direction_input(gpio); + if (error) { + dev_err(dev, + "unable to set direction on gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + bdata->can_sleep = gpio_cansleep(gpio); + bdata->last_state = -1; + bdata->threshold = DIV_ROUND_UP(button->debounce_interval, + pdata->poll_interval); + + input_set_capability(input, type, button->code); + } + + bdev->poll_dev = poll_dev; + bdev->dev = dev; + bdev->pdata = pdata; + platform_set_drvdata(pdev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + goto err_free_gpio; + } + + /* report initial state of the buttons */ + for (i = 0; i < pdata->nbuttons; i++) + gpio_keys_polled_check_state(input, &pdata->buttons[i], + &bdev->data[i]); + + return 0; + +err_free_gpio: + while (--i >= 0) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(poll_dev); + +err_free_bdev: + kfree(bdev); + + platform_set_drvdata(pdev, NULL); + return error; +} + +static int __devexit gpio_keys_polled_remove(struct platform_device *pdev) +{ + struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev); + struct gpio_keys_platform_data *pdata = bdev->pdata; + int i; + + input_unregister_polled_device(bdev->poll_dev); + + for (i = 0; i < pdata->nbuttons; i++) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(bdev->poll_dev); + + kfree(bdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver gpio_keys_polled_driver = { + .probe = gpio_keys_polled_probe, + .remove = __devexit_p(gpio_keys_polled_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_keys_polled_init(void) +{ + return platform_driver_register(&gpio_keys_polled_driver); +} + +static void __exit gpio_keys_polled_exit(void) +{ + platform_driver_unregister(&gpio_keys_polled_driver); +} + +module_init(gpio_keys_polled_init); +module_exit(gpio_keys_polled_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_DESCRIPTION("Polled GPIO Buttons driver"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.3 From 47d092352c132a2d0ee4156b5dca263eaad2c17f Mon Sep 17 00:00:00 2001 From: David Foley Date: Tue, 7 Dec 2010 21:05:59 -0800 Subject: Input: wacom - add new Bamboo PT (0xdb) Adds new Bamboo Pen & Touch model - Bamboo P & T Special Edition Medium (CTH661/L; Product ID = 0xdb). Tested-by: Tobias Verbeke Signed-off-by: David Foley Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index db747e4c4c4c..4852b440960a 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1440,6 +1440,8 @@ static struct wacom_features wacom_features_0xD8 = { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xDA = { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xDB = + { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -1510,6 +1512,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xD3) }, { USB_DEVICE_WACOM(0xD8) }, { USB_DEVICE_WACOM(0xDA) }, + { USB_DEVICE_WACOM(0xDB) }, { USB_DEVICE_WACOM(0xF0) }, { USB_DEVICE_WACOM(0xCC) }, { USB_DEVICE_WACOM(0x90) }, -- cgit v1.2.3 From b8a3d6bcbc85d7636d9f2adede8479ce2999c232 Mon Sep 17 00:00:00 2001 From: Tony SIM Date: Tue, 7 Dec 2010 02:54:00 -0800 Subject: Input: tca6416-keypad - add support for tca6408a Support 8-bit tca6408a I/O expander as a keypad. Signed-off-by: Tony SIM Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 ++++++---- drivers/input/keyboard/tca6416-keypad.c | 13 ++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ee85e5bf4b2a..a378e959368e 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -180,20 +180,22 @@ config KEYBOARD_GPIO module will be called gpio_keys. config KEYBOARD_TCA6416 - tristate "TCA6416 Keypad Support" + tristate "TCA6416/TCA6408A Keypad Support" depends on I2C help This driver implements basic keypad functionality - for keys connected through TCA6416 IO expander + for keys connected through TCA6416/TCA6408A IO expanders. Say Y here if your device has keys connected to - TCA6416 IO expander. Your board-specific setup logic + TCA6416/TCA6408A IO expander. Your board-specific setup logic must also provide pin-mask details(of which TCA6416 pins are used for keypad). - If enabled the complete TCA6416 device will be managed through + If enabled the entire TCA6416 device will be managed through this driver. + To compile this driver as a module, choose M here: the + module will be called tca6416_keypad. config KEYBOARD_MATRIX tristate "GPIO driven matrix keypad support" diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 00137bebcf97..800fbccf1f0f 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -29,6 +29,7 @@ static const struct i2c_device_id tca6416_id[] = { { "tca6416-keys", 16, }, + { "tca6408-keys", 8, }, { } }; MODULE_DEVICE_TABLE(i2c, tca6416_id); @@ -46,8 +47,9 @@ struct tca6416_keypad_chip { struct i2c_client *client; struct input_dev *input; struct delayed_work dwork; - u16 pinmask; + int io_size; int irqnum; + u16 pinmask; bool use_polling; struct tca6416_button buttons[0]; }; @@ -56,7 +58,9 @@ static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) { int error; - error = i2c_smbus_write_word_data(chip->client, reg << 1, val); + error = chip->io_size > 8 ? + i2c_smbus_write_word_data(chip->client, reg << 1, val) : + i2c_smbus_write_byte_data(chip->client, reg, val); if (error < 0) { dev_err(&chip->client->dev, "%s failed, reg: %d, val: %d, error: %d\n", @@ -71,7 +75,9 @@ static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) { int retval; - retval = i2c_smbus_read_word_data(chip->client, reg << 1); + retval = chip->io_size > 8 ? + i2c_smbus_read_word_data(chip->client, reg << 1) : + i2c_smbus_read_byte_data(chip->client, reg); if (retval < 0) { dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", __func__, reg, retval); @@ -224,6 +230,7 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client, chip->client = client; chip->input = input; + chip->io_size = id->driver_data; chip->pinmask = pdata->pinmask; chip->use_polling = pdata->use_polling; -- cgit v1.2.3 From 356c6f654d1c03e2ae45ef58b267e83dfd1a17f2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 7 Dec 2010 22:11:09 -0800 Subject: Input: cma3000_d0x_i2c - add MODULE_DEVICE_TABLE This should allow loading this module automatically on systems that have such device. Signed-off-by: Dmitry Torokhov --- drivers/input/misc/cma3000_d0x_i2c.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c index c52d278a7942..d100cc5c5783 100644 --- a/drivers/input/misc/cma3000_d0x_i2c.c +++ b/drivers/input/misc/cma3000_d0x_i2c.c @@ -110,6 +110,8 @@ static const struct i2c_device_id cma3000_i2c_id[] = { { }, }; +MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id); + static struct i2c_driver cma3000_i2c_driver = { .probe = cma3000_i2c_probe, .remove = __devexit_p(cma3000_i2c_remove), -- cgit v1.2.3 From 57a7872fa0f03e90be0fa224b9ea533f5b03ee4f Mon Sep 17 00:00:00 2001 From: Kevin Granade Date: Fri, 10 Dec 2010 23:04:02 -0800 Subject: Input: wacom - add another Bamboo Pen ID (0xd4) Add the features struct and device table entry to enable yet another version of Wacom Bamboo Pen (CTL460, Product ID 0xD4). Signed-off-by: Kevin Granade Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 4852b440960a..435b0af401e4 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1436,6 +1436,8 @@ static struct wacom_features wacom_features_0xD2 = { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xD3 = { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; +static const struct wacom_features wacom_features_0xD4 = + { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 255, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xD8 = { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xDA = @@ -1510,6 +1512,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xD1) }, { USB_DEVICE_WACOM(0xD2) }, { USB_DEVICE_WACOM(0xD3) }, + { USB_DEVICE_WACOM(0xD4) }, { USB_DEVICE_WACOM(0xD8) }, { USB_DEVICE_WACOM(0xDA) }, { USB_DEVICE_WACOM(0xDB) }, -- cgit v1.2.3 From ab4e0192196b8d4e43a3945742d4996da934a86f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 14 Dec 2010 23:53:21 -0800 Subject: Input: define separate EVIOCGKEYCODE_V2/EVIOCSKEYCODE_V2 The desire to keep old names for the EVIOCGKEYCODE/EVIOCSKEYCODE while extending them to support large scancodes was a mistake. While we tried to keep ABI intact (and we succeeded in doing that, programs compiled on older kernels will work on newer ones) there is still a problem with recompiling existing software with newer kernel headers. New kernel headers will supply updated ioctl numbers and kernel will expect that userspace will use struct input_keymap_entry to set and retrieve keymap data. But since the names of ioctls are still the same userspace will happily compile even if not adjusted to make use of the new structure and will start miraculously fail in the field. To avoid this issue let's revert EVIOCGKEYCODE/EVIOCSKEYCODE definitions and add EVIOCGKEYCODE_V2/EVIOCSKEYCODE_V2 so that userspace can explicitly select the style of ioctls it wants to employ. Reviewed-by: Henrik Rydberg Acked-by: Jarod Wilson Acked-by: Mauro Carvalho Chehab Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 113 ++++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 55 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index e3f7fc6f9565..68f09a868434 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -534,76 +534,73 @@ static int handle_eviocgbit(struct input_dev *dev, } #undef OLD_KEY_MAX -static int evdev_handle_get_keycode(struct input_dev *dev, - void __user *p, size_t size) +static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p) { - struct input_keymap_entry ke; + struct input_keymap_entry ke = { + .len = sizeof(unsigned int), + .flags = 0, + }; + int __user *ip = (int __user *)p; int error; - memset(&ke, 0, sizeof(ke)); - - if (size == sizeof(unsigned int[2])) { - /* legacy case */ - int __user *ip = (int __user *)p; + /* legacy case */ + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; - if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) - return -EFAULT; + error = input_get_keycode(dev, &ke); + if (error) + return error; - ke.len = sizeof(unsigned int); - ke.flags = 0; + if (put_user(ke.keycode, ip + 1)) + return -EFAULT; - error = input_get_keycode(dev, &ke); - if (error) - return error; + return 0; +} - if (put_user(ke.keycode, ip + 1)) - return -EFAULT; +static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke; + int error; - } else { - size = min(size, sizeof(ke)); + if (copy_from_user(&ke, p, sizeof(ke))) + return -EFAULT; - if (copy_from_user(&ke, p, size)) - return -EFAULT; + error = input_get_keycode(dev, &ke); + if (error) + return error; - error = input_get_keycode(dev, &ke); - if (error) - return error; + if (copy_to_user(p, &ke, sizeof(ke))) + return -EFAULT; - if (copy_to_user(p, &ke, size)) - return -EFAULT; - } return 0; } -static int evdev_handle_set_keycode(struct input_dev *dev, - void __user *p, size_t size) +static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p) { - struct input_keymap_entry ke; - - memset(&ke, 0, sizeof(ke)); + struct input_keymap_entry ke = { + .len = sizeof(unsigned int), + .flags = 0, + }; + int __user *ip = (int __user *)p; - if (size == sizeof(unsigned int[2])) { - /* legacy case */ - int __user *ip = (int __user *)p; + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; - if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) - return -EFAULT; + if (get_user(ke.keycode, ip + 1)) + return -EFAULT; - if (get_user(ke.keycode, ip + 1)) - return -EFAULT; + return input_set_keycode(dev, &ke); +} - ke.len = sizeof(unsigned int); - ke.flags = 0; +static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) +{ + struct input_keymap_entry ke; - } else { - size = min(size, sizeof(ke)); + if (copy_from_user(&ke, p, sizeof(ke))) + return -EFAULT; - if (copy_from_user(&ke, p, size)) - return -EFAULT; - - if (ke.len > sizeof(ke.scancode)) - return -EINVAL; - } + if (ke.len > sizeof(ke.scancode)) + return -EINVAL; return input_set_keycode(dev, &ke); } @@ -669,6 +666,18 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return evdev_grab(evdev, client); else return evdev_ungrab(evdev, client); + + case EVIOCGKEYCODE: + return evdev_handle_get_keycode(dev, p); + + case EVIOCSKEYCODE: + return evdev_handle_set_keycode(dev, p); + + case EVIOCGKEYCODE_V2: + return evdev_handle_get_keycode_v2(dev, p); + + case EVIOCSKEYCODE_V2: + return evdev_handle_set_keycode_v2(dev, p); } size = _IOC_SIZE(cmd); @@ -708,12 +717,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return -EFAULT; return error; - - case EVIOC_MASK_SIZE(EVIOCGKEYCODE): - return evdev_handle_get_keycode(dev, p, size); - - case EVIOC_MASK_SIZE(EVIOCSKEYCODE): - return evdev_handle_set_keycode(dev, p, size); } /* Multi-number variable-length handlers */ -- cgit v1.2.3 From 56a8bd6dcf81693e61a712097216904f3a4ab536 Mon Sep 17 00:00:00 2001 From: Tony SIM Date: Wed, 15 Dec 2010 23:39:25 -0800 Subject: Input: add ST1232 touchscreen controller driver This patch introduces support for Sitronix ST1232 integrated capacitive touchscreen with LCD module. The touchscreen is multitouch capable and can report coordinates of up to two contact points. Signed-off-by: Tony SIM Reviewed-by: Trilok Soni Acked-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 22 ++- drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/st1232.c | 274 +++++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+), 5 deletions(-) create mode 100644 drivers/input/touchscreen/st1232.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 06ea8da95c62..07ac77d393a4 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -659,17 +659,17 @@ config TOUCHSCREEN_PCAP To compile this driver as a module, choose M here: the module will be called pcap_ts. -config TOUCHSCREEN_TPS6507X - tristate "TPS6507x based touchscreens" +config TOUCHSCREEN_ST1232 + tristate "Sitronix ST1232 touchscreen controllers" depends on I2C help - Say Y here if you have a TPS6507x based touchscreen - controller. + Say Y here if you want to support Sitronix ST1232 + touchscreen controller. If unsure, say N. To compile this driver as a module, choose M here: the - module will be called tps6507x_ts. + module will be called st1232_ts. config TOUCHSCREEN_STMPE tristate "STMicroelectronics STMPE touchscreens" @@ -681,4 +681,16 @@ config TOUCHSCREEN_STMPE To compile this driver as a module, choose M here: the module will be called stmpe-ts. +config TOUCHSCREEN_TPS6507X + tristate "TPS6507x based touchscreens" + depends on I2C + help + Say Y here if you have a TPS6507x based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tps6507x_ts. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 7cc1b4f4b677..718bcc814952 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o +obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c new file mode 100644 index 000000000000..4ab371358b33 --- /dev/null +++ b/drivers/input/touchscreen/st1232.c @@ -0,0 +1,274 @@ +/* + * ST1232 Touchscreen Controller Driver + * + * Copyright (C) 2010 Renesas Solutions Corp. + * Tony SIM + * + * Using code from: + * - android.git.kernel.org: projects/kernel/common.git: synaptics_i2c_rmi.c + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include + +#define ST1232_TS_NAME "st1232-ts" + +#define MIN_X 0x00 +#define MIN_Y 0x00 +#define MAX_X 0x31f /* (800 - 1) */ +#define MAX_Y 0x1df /* (480 - 1) */ +#define MAX_AREA 0xff +#define MAX_FINGERS 2 + +struct st1232_ts_finger { + u16 x; + u16 y; + u8 t; + bool is_valid; +}; + +struct st1232_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct st1232_ts_finger finger[MAX_FINGERS]; +}; + +static int st1232_ts_read_data(struct st1232_ts_data *ts) +{ + struct st1232_ts_finger *finger = ts->finger; + struct i2c_client *client = ts->client; + struct i2c_msg msg[2]; + int error; + u8 start_reg; + u8 buf[10]; + + /* read touchscreen data from ST1232 */ + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start_reg; + start_reg = 0x10; + + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = sizeof(buf); + msg[1].buf = buf; + + error = i2c_transfer(client->adapter, msg, 2); + if (error < 0) + return error; + + /* get "valid" bits */ + finger[0].is_valid = buf[2] >> 7; + finger[1].is_valid = buf[5] >> 7; + + /* get xy coordinate */ + if (finger[0].is_valid) { + finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3]; + finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4]; + finger[0].t = buf[8]; + } + + if (finger[1].is_valid) { + finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6]; + finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7]; + finger[1].t = buf[9]; + } + + return 0; +} + +static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) +{ + struct st1232_ts_data *ts = dev_id; + struct st1232_ts_finger *finger = ts->finger; + struct input_dev *input_dev = ts->input_dev; + int count = 0; + int i, ret; + + ret = st1232_ts_read_data(ts); + if (ret < 0) + goto end; + + /* multi touch protocol */ + for (i = 0; i < MAX_FINGERS; i++) { + if (!finger[i].is_valid) + continue; + + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t); + input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y); + input_mt_sync(input_dev); + count++; + } + + /* SYN_MT_REPORT only if no contact */ + if (!count) + input_mt_sync(input_dev); + + /* SYN_REPORT */ + input_sync(input_dev); + +end: + return IRQ_HANDLED; +} + +static int __devinit st1232_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct st1232_ts_data *ts; + struct input_dev *input_dev; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + return -EIO; + } + + if (!client->irq) { + dev_err(&client->dev, "no IRQ?\n"); + return -EINVAL; + } + + + ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->input_dev = input_dev; + + input_dev->name = "st1232-touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); + + error = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler, + IRQF_ONESHOT, client->name, ts); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(ts->input_dev); + if (error) { + dev_err(&client->dev, "Unable to register %s input device\n", + input_dev->name); + goto err_free_irq; + } + + i2c_set_clientdata(client, ts); + device_init_wakeup(&client->dev, 1); + + return 0; + +err_free_irq: + free_irq(client->irq, ts); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return error; +} + +static int __devexit st1232_ts_remove(struct i2c_client *client) +{ + struct st1232_ts_data *ts = i2c_get_clientdata(client); + + device_init_wakeup(&client->dev, 0); + free_irq(client->irq, ts); + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +#ifdef CONFIG_PM +static int st1232_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + else + disable_irq(client->irq); + + return 0; +} + +static int st1232_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + else + enable_irq(client->irq); + + return 0; +} + +static const struct dev_pm_ops st1232_ts_pm_ops = { + .suspend = st1232_ts_suspend, + .resume = st1232_ts_resume, +}; +#endif + +static const struct i2c_device_id st1232_ts_id[] = { + { ST1232_TS_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, st1232_ts_id); + +static struct i2c_driver st1232_ts_driver = { + .probe = st1232_ts_probe, + .remove = __devexit_p(st1232_ts_remove), + .id_table = st1232_ts_id, + .driver = { + .name = ST1232_TS_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &st1232_ts_pm_ops, +#endif + }, +}; + +static int __init st1232_ts_init(void) +{ + return i2c_add_driver(&st1232_ts_driver); +} +module_init(st1232_ts_init); + +static void __exit st1232_ts_exit(void) +{ + i2c_del_driver(&st1232_ts_driver); +} +module_exit(st1232_ts_exit); + +MODULE_AUTHOR("Tony SIM "); +MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 47c78e891323513e9909729b44033e2c6649e2b7 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 27 Nov 2010 09:16:48 +0100 Subject: input: mt: Break out slots handling In preparation for common code to handle a larger set of MT slots devices, move the slots handling over to a separate file. Signed-off-by: Henrik Rydberg --- drivers/input/Makefile | 2 +- drivers/input/input-mt.c | 58 +++++++++++++++++++++++++++++++++ drivers/input/input.c | 48 +-------------------------- drivers/input/misc/uinput.c | 1 + drivers/input/tablet/wacom_wac.c | 1 + drivers/input/touchscreen/wacom_w8001.c | 2 +- 6 files changed, 63 insertions(+), 49 deletions(-) create mode 100644 drivers/input/input-mt.c (limited to 'drivers/input') diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 7ad212d31f99..569938b3cc04 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -5,7 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT) += input-core.o -input-core-objs := input.o input-compat.o ff-core.o +input-core-objs := input.o input-compat.o ff-core.o input-mt.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c new file mode 100644 index 000000000000..463a4d7d54f2 --- /dev/null +++ b/drivers/input/input-mt.c @@ -0,0 +1,58 @@ +/* + * Input Multitouch Library + * + * Copyright (c) 2008-2010 Henrik Rydberg + * + * 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 +#include + +/** + * input_mt_create_slots() - create MT input slots + * @dev: input device supporting MT events and finger tracking + * @num_slots: number of slots used by the device + * + * This function allocates all necessary memory for MT slot handling in the + * input device, and adds ABS_MT_SLOT to the device capabilities. All slots + * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. + */ +int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) +{ + int i; + + if (!num_slots) + return 0; + + dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); + if (!dev->mt) + return -ENOMEM; + + dev->mtsize = num_slots; + input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + + /* Mark slots as 'unused' */ + for (i = 0; i < num_slots; i++) + input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1); + + return 0; +} +EXPORT_SYMBOL(input_mt_create_slots); + +/** + * input_mt_destroy_slots() - frees the MT slots of the input device + * @dev: input device with allocated MT slots + * + * This function is only needed in error path as the input core will + * automatically free the MT slots when the device is destroyed. + */ +void input_mt_destroy_slots(struct input_dev *dev) +{ + kfree(dev->mt); + dev->mt = NULL; + dev->mtsize = 0; +} +EXPORT_SYMBOL(input_mt_destroy_slots); diff --git a/drivers/input/input.c b/drivers/input/input.c index d092ef9291da..37708d1d86ec 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include @@ -1690,52 +1690,6 @@ void input_free_device(struct input_dev *dev) } EXPORT_SYMBOL(input_free_device); -/** - * input_mt_create_slots() - create MT input slots - * @dev: input device supporting MT events and finger tracking - * @num_slots: number of slots used by the device - * - * This function allocates all necessary memory for MT slot handling in the - * input device, and adds ABS_MT_SLOT to the device capabilities. All slots - * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. - */ -int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) -{ - int i; - - if (!num_slots) - return 0; - - dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); - if (!dev->mt) - return -ENOMEM; - - dev->mtsize = num_slots; - input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); - - /* Mark slots as 'unused' */ - for (i = 0; i < num_slots; i++) - dev->mt[i].abs[ABS_MT_TRACKING_ID - ABS_MT_FIRST] = -1; - - return 0; -} -EXPORT_SYMBOL(input_mt_create_slots); - -/** - * input_mt_destroy_slots() - frees the MT slots of the input device - * @dev: input device with allocated MT slots - * - * This function is only needed in error path as the input core will - * automatically free the MT slots when the device is destroyed. - */ -void input_mt_destroy_slots(struct input_dev *dev) -{ - kfree(dev->mt); - dev->mt = NULL; - dev->mtsize = 0; -} -EXPORT_SYMBOL(input_mt_destroy_slots); - /** * input_set_capability - mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index b9410784e6a1..8f374143190e 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "../input-compat.h" static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index b3252ef1e279..bde612c6d36d 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -14,6 +14,7 @@ #include "wacom_wac.h" #include "wacom.h" +#include static int wacom_penpartner_irq(struct wacom_wac *wacom) { diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 9ae4c7b16ba7..5d4f50e52a28 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 8cde81001626c4c60b26ef2eb5fc522885ed9fd0 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 27 Nov 2010 10:50:54 +0100 Subject: input: mt: Collect slots initialization code The MT slots devices all follow the same initialization pattern of creating slots and hinting about buffer size. Let drivers call an initialization function instead, and make sure it can be called repeatedly without side effects. Signed-off-by: Henrik Rydberg --- drivers/input/input-mt.c | 19 +++++++++++++------ drivers/input/misc/uinput.c | 3 +-- drivers/input/tablet/wacom_wac.c | 2 +- drivers/input/touchscreen/wacom_w8001.c | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 463a4d7d54f2..f400e47092c4 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -12,20 +12,25 @@ #include /** - * input_mt_create_slots() - create MT input slots + * input_mt_init_slots() - initialize MT input slots * @dev: input device supporting MT events and finger tracking * @num_slots: number of slots used by the device * - * This function allocates all necessary memory for MT slot handling in the - * input device, and adds ABS_MT_SLOT to the device capabilities. All slots - * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. + * This function allocates all necessary memory for MT slot handling + * in the input device, adds ABS_MT_SLOT to the device capabilities + * and sets up appropriate event buffers. All slots are initially + * marked as unused by setting ABS_MT_TRACKING_ID to -1. May be called + * repeatedly. Returns -EINVAL if attempting to reinitialize with a + * different number of slots. */ -int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) +int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) { int i; if (!num_slots) return 0; + if (dev->mt) + return dev->mtsize != num_slots ? -EINVAL : 0; dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); if (!dev->mt) @@ -33,6 +38,7 @@ int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) dev->mtsize = num_slots; input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + input_set_events_per_packet(dev, 6 * num_slots); /* Mark slots as 'unused' */ for (i = 0; i < num_slots; i++) @@ -40,7 +46,7 @@ int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) return 0; } -EXPORT_SYMBOL(input_mt_create_slots); +EXPORT_SYMBOL(input_mt_init_slots); /** * input_mt_destroy_slots() - frees the MT slots of the input device @@ -54,5 +60,6 @@ void input_mt_destroy_slots(struct input_dev *dev) kfree(dev->mt); dev->mt = NULL; dev->mtsize = 0; + dev->slot = 0; } EXPORT_SYMBOL(input_mt_destroy_slots); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 8f374143190e..bea89722c4e9 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -407,8 +407,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu goto exit; if (test_bit(ABS_MT_SLOT, dev->absbit)) { int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_create_slots(dev, nslot); - input_set_events_per_packet(dev, 6 * nslot); + input_mt_init_slots(dev, nslot); } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { input_set_events_per_packet(dev, 60); } diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index bde612c6d36d..f26e2238f6ca 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1273,7 +1273,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOOL_FINGER, input_dev->keybit); __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - input_mt_create_slots(input_dev, 2); + input_mt_init_slots(input_dev, 2); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, features->x_max, features->x_fuzz, 0); diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 5d4f50e52a28..4a2e8cf4c8ef 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -318,7 +318,7 @@ static int w8001_setup(struct w8001 *w8001) case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; - input_mt_create_slots(dev, 2); + input_mt_init_slots(dev, 2); input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, MAX_TRACKING_ID, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, -- cgit v1.2.3 From c5f4dec1ceb6ab773bbbefbe64a7c990c7d6b17f Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 15 Dec 2010 13:50:34 +0100 Subject: input: mt: Move tracking and pointer emulation to input-mt The drivers using the type B protocol all report tracking information the same way. The contact id is semantically equivalent to ABS_MT_SLOT, and the handling of ABS_MT_TRACKING_ID only complicates the driver. The situation can be improved upon by providing a common pointer emulation code, thereby removing the need for the tracking id in the driver. This patch moves all tracking event handling over to the input core, simplifying both the existing drivers and the ones currently in preparation. Acked-by: Ping Cheng Acked-by: Jiri Kosina Signed-off-by: Henrik Rydberg --- drivers/input/input-mt.c | 115 ++++++++++++++++++++++++++++++-- drivers/input/tablet/wacom_wac.c | 23 ++----- drivers/input/tablet/wacom_wac.h | 4 -- drivers/input/touchscreen/wacom_w8001.c | 21 ++---- 4 files changed, 120 insertions(+), 43 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index f400e47092c4..c48c81f0308d 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -11,17 +11,18 @@ #include #include +#define TRKID_SGN ((TRKID_MAX + 1) >> 1) + /** * input_mt_init_slots() - initialize MT input slots * @dev: input device supporting MT events and finger tracking * @num_slots: number of slots used by the device * * This function allocates all necessary memory for MT slot handling - * in the input device, adds ABS_MT_SLOT to the device capabilities - * and sets up appropriate event buffers. All slots are initially - * marked as unused by setting ABS_MT_TRACKING_ID to -1. May be called - * repeatedly. Returns -EINVAL if attempting to reinitialize with a - * different number of slots. + * in the input device, prepares the ABS_MT_SLOT and + * ABS_MT_TRACKING_ID events for use and sets up appropriate buffers. + * May be called repeatedly. Returns -EINVAL if attempting to + * reinitialize with a different number of slots. */ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) { @@ -38,6 +39,7 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots) dev->mtsize = num_slots; input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); input_set_events_per_packet(dev, 6 * num_slots); /* Mark slots as 'unused' */ @@ -61,5 +63,108 @@ void input_mt_destroy_slots(struct input_dev *dev) dev->mt = NULL; dev->mtsize = 0; dev->slot = 0; + dev->trkid = 0; } EXPORT_SYMBOL(input_mt_destroy_slots); + +/** + * input_mt_report_slot_state() - report contact state + * @dev: input device with allocated MT slots + * @tool_type: the tool type to use in this slot + * @active: true if contact is active, false otherwise + * + * Reports a contact via ABS_MT_TRACKING_ID, and optionally + * ABS_MT_TOOL_TYPE. If active is true and the slot is currently + * inactive, or if the tool type is changed, a new tracking id is + * assigned to the slot. The tool type is only reported if the + * corresponding absbit field is set. + */ +void input_mt_report_slot_state(struct input_dev *dev, + unsigned int tool_type, bool active) +{ + struct input_mt_slot *mt; + int id; + + if (!dev->mt || !active) { + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); + return; + } + + mt = &dev->mt[dev->slot]; + id = input_mt_get_value(mt, ABS_MT_TRACKING_ID); + if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type) + id = input_mt_new_trkid(dev); + + input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); + input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type); +} +EXPORT_SYMBOL(input_mt_report_slot_state); + +/** + * input_mt_report_finger_count() - report contact count + * @dev: input device with allocated MT slots + * @count: the number of contacts + * + * Reports the contact count via BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, + * BTN_TOOL_TRIPLETAP and BTN_TOOL_QUADTAP. + * + * The input core ensures only the KEY events already setup for + * this device will produce output. + */ +void input_mt_report_finger_count(struct input_dev *dev, int count) +{ + input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1); + input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2); + input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3); + input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4); +} +EXPORT_SYMBOL(input_mt_report_finger_count); + +/** + * input_mt_report_pointer_emulation() - common pointer emulation + * @dev: input device with allocated MT slots + * @use_count: report number of active contacts as finger count + * + * Performs legacy pointer emulation via BTN_TOUCH, ABS_X, ABS_Y and + * ABS_PRESSURE. Touchpad finger count is emulated if use_count is true. + * + * The input core ensures only the KEY and ABS axes already setup for + * this device will produce output. + */ +void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) +{ + struct input_mt_slot *oldest = 0; + int oldid = dev->trkid; + int count = 0; + int i; + + for (i = 0; i < dev->mtsize; ++i) { + struct input_mt_slot *ps = &dev->mt[i]; + int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID); + + if (id < 0) + continue; + if ((id - oldid) & TRKID_SGN) { + oldest = ps; + oldid = id; + } + count++; + } + + input_event(dev, EV_KEY, BTN_TOUCH, count > 0); + if (use_count) + input_mt_report_finger_count(dev, count); + + if (oldest) { + int x = input_mt_get_value(oldest, ABS_MT_POSITION_X); + int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y); + int p = input_mt_get_value(oldest, ABS_MT_PRESSURE); + + input_event(dev, EV_ABS, ABS_X, x); + input_event(dev, EV_ABS, ABS_Y, y); + input_event(dev, EV_ABS, ABS_PRESSURE, p); + } else { + input_event(dev, EV_ABS, ABS_PRESSURE, 0); + } +} +EXPORT_SYMBOL(input_mt_report_pointer_emulation); diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index f26e2238f6ca..0b0525486711 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -863,19 +863,21 @@ 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 sp = 0, sx = 0, sy = 0, count = 0; int i; for (i = 0; i < 2; i++) { int p = data[9 * i + 2]; + bool touch = p && !wacom->shared->stylus_in_proximity; + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); /* * 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. */ - if (p && !wacom->shared->stylus_in_proximity) { + if (touch) { int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff; int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff; if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { @@ -885,23 +887,10 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_PRESSURE, p); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - if (wacom->id[i] < 0) - wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID; - if (!count++) - sp = p, sx = x, sy = y; - } else { - wacom->id[i] = -1; } - input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]); } - input_report_key(input, BTN_TOUCH, count > 0); - input_report_key(input, BTN_TOOL_FINGER, count == 1); - input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2); - - input_report_abs(input, ABS_PRESSURE, sp); - input_report_abs(input, ABS_X, sx); - input_report_abs(input, ABS_Y, sy); + 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); @@ -1283,8 +1272,6 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, features->pressure_max, features->pressure_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, - MAX_TRACKING_ID, 0, 0); } else if (features->device_type == BTN_TOOL_PEN) { __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 00ca01541d89..b1310ec9720c 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -42,9 +42,6 @@ #define WACOM_QUIRK_MULTI_INPUT 0x0001 #define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 -/* largest reported tracking id */ -#define MAX_TRACKING_ID 0xfff - enum { PENPARTNER = 0, GRAPHIRE, @@ -100,7 +97,6 @@ struct wacom_wac { int id[3]; __u32 serial[2]; int last_finger; - int trk_id; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 4a2e8cf4c8ef..2a0bec12d12a 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -48,8 +48,6 @@ MODULE_LICENSE("GPL"); #define W8001_PKTLEN_TPCCTL 11 /* control packet */ #define W8001_PKTLEN_TOUCH2FG 13 -#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */ - struct w8001_coord { u8 rdy; u8 tsw; @@ -87,7 +85,6 @@ struct w8001 { char phys[32]; int type; unsigned int pktlen; - int trkid[2]; }; static void parse_data(u8 *data, struct w8001_coord *coord) @@ -116,28 +113,23 @@ static void parse_data(u8 *data, struct w8001_coord *coord) static void parse_touch(struct w8001 *w8001) { - static int trkid; struct input_dev *dev = w8001->dev; unsigned char *data = w8001->data; int i; for (i = 0; i < 2; i++) { - input_mt_slot(dev, i); + bool touch = data[0] & (1 << i); - if (data[0] & (1 << i)) { + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); + if (touch) { int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); /* data[5,6] and [11,12] is finger capacity */ input_report_abs(dev, ABS_MT_POSITION_X, x); input_report_abs(dev, ABS_MT_POSITION_Y, y); - input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); - if (w8001->trkid[i] < 0) - w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; - } else { - w8001->trkid[i] = -1; } - input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); } input_sync(dev); @@ -319,14 +311,12 @@ static int w8001_setup(struct w8001 *w8001) w8001->pktlen = W8001_PKTLEN_TOUCH2FG; input_mt_init_slots(dev, 2); - input_set_abs_params(dev, ABS_MT_TRACKING_ID, - 0, MAX_TRACKING_ID, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch.x, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, touch.y, 0, 0); input_set_abs_params(dev, ABS_MT_TOOL_TYPE, - 0, 0, 0, 0); + 0, MT_TOOL_MAX, 0, 0); break; } } @@ -372,7 +362,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) w8001->serio = serio; w8001->id = serio->id.id; w8001->dev = input_dev; - w8001->trkid[0] = w8001->trkid[1] = -1; init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); -- cgit v1.2.3 From 85b7720039fc000b561c20fe2aaa3b54cddae4a7 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Dec 2010 20:51:13 +0100 Subject: Input: introduce device properties Today, userspace sets up an input device based on the data it emits. This is not always enough; a tablet and a touchscreen may emit exactly the same data, for instance, but the former should be set up with a pointer whereas the latter does not need to. Recently, a new type of touchpad has emerged where the buttons are under the pad, which changes logic without changing the emitted data. This patch introduces a new ioctl, EVIOCGPROP, which enables user access to a set of device properties useful during setup. The properties are given as a bitmap in the same fashion as the event types, and are also made available via sysfs, uevent and /proc/bus/input/devices. Acked-by: Ping Cheng Acked-by: Chase Douglas Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/evdev.c | 4 ++++ drivers/input/input.c | 19 +++++++++++++++++++ drivers/input/misc/uinput.c | 4 ++++ 3 files changed, 27 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index e3f7fc6f9565..0cd97e8f0c9a 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -677,6 +677,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, #define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) switch (EVIOC_MASK_SIZE(cmd)) { + case EVIOCGPROP(0): + return bits_to_user(dev->propbit, INPUT_PROP_MAX, + size, p, compat_mode); + case EVIOCGKEY(0): return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); diff --git a/drivers/input/input.c b/drivers/input/input.c index 37708d1d86ec..9ea713f4192b 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1095,6 +1095,8 @@ static int input_devices_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%s ", handle->name); seq_putc(seq, '\n'); + input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX); + input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX); if (test_bit(EV_KEY, dev->evbit)) input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX); @@ -1318,11 +1320,26 @@ static ssize_t input_dev_show_modalias(struct device *dev, } static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); +static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, + int max, int add_cr); + +static ssize_t input_dev_show_properties(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct input_dev *input_dev = to_input_dev(dev); + int len = input_print_bitmap(buf, PAGE_SIZE, input_dev->propbit, + INPUT_PROP_MAX, true); + return min_t(int, len, PAGE_SIZE); +} +static DEVICE_ATTR(properties, S_IRUGO, input_dev_show_properties, NULL); + static struct attribute *input_dev_attrs[] = { &dev_attr_name.attr, &dev_attr_phys.attr, &dev_attr_uniq.attr, &dev_attr_modalias.attr, + &dev_attr_properties.attr, NULL }; @@ -1522,6 +1539,8 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) if (dev->uniq) INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq); + INPUT_ADD_HOTPLUG_BM_VAR("PROP=", dev->propbit, INPUT_PROP_MAX); + INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX); if (test_bit(EV_KEY, dev->evbit)) INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index bea89722c4e9..82542a1c1098 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -680,6 +680,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, retval = uinput_set_bit(arg, swbit, SW_MAX); break; + case UI_SET_PROPBIT: + retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); + break; + case UI_SET_PHYS: if (udev->state == UIST_CREATED) { retval = -EINVAL; -- cgit v1.2.3 From fcd3027abbbcc26248714eddae40af3fb3c8a82e Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Dec 2010 20:28:26 +0100 Subject: Input: fix double equality sign in uevent Looking at the uevent stream for input devices, all properties are on the form "A=B" except the bitmap values, which are on the form "A==B". This bug has been around at least since 2007, and the input uevent code has been untouched since. The recent addition of device properties suggests this is a good time for a remedy. Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 9ea713f4192b..3bb6907f26d6 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1473,7 +1473,7 @@ static int input_add_uevent_bm_var(struct kobj_uevent_env *env, { int len; - if (add_uevent_var(env, "%s=", name)) + if (add_uevent_var(env, "%s", name)) return -ENOMEM; len = input_print_bitmap(&env->buf[env->buflen - 1], -- cgit v1.2.3 From c14890a8e54977f895773d393d6a640d6d698fb8 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 16 Dec 2010 09:52:23 +0100 Subject: Input: synaptics - report clickpad property With the new input property interface, it is possible to report the special quirks of a device using ioctl/sysfs. This patch sets up the device as a pointer, and reports the clickpad functionality via the INPUT_PROP_BUTTONPAD property. Acked-by: Chase Douglas Signed-off-by: Henrik Rydberg --- drivers/input/mouse/synaptics.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2e300a460556..8997cbc69dcc 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -622,6 +622,8 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) { int i; + __set_bit(INPUT_PROP_POINTER, dev->propbit); + __set_bit(EV_ABS, dev->evbit); input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0); @@ -663,6 +665,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) input_abs_set_res(dev, ABS_Y, priv->y_res); if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { + __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); /* Clickpads report only left button */ __clear_bit(BTN_RIGHT, dev->keybit); __clear_bit(BTN_MIDDLE, dev->keybit); -- cgit v1.2.3 From fec6e5252b542e748871c88f8455e69ae73ea156 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Tue, 21 Dec 2010 18:11:25 +0100 Subject: Input: synaptics - add multi-finger and semi-mt support The Synaptics 2.7 series of touchpads support a mode for reporting two sets of X/Y/Pressure data (advanced gesture mode). By default, these devices report only single finger data, depriving userspace of the nowadays ubiquitous two-finger scroll gesture. Enabling advanced gesture mode also enables the multi-finger report, although the device does not claim that capability. Up to three fingers can be reported this way. While two or three fingers are touching, the normal packet is prepended by a reduced finger packet of lower resolution. From the two packets (which do not represent the actual fingers), the bounding rectangle of the individual contacts can be extracted. This information is sufficient to perform scaling gestures and a limited form of rotation gesture. The behavior has been coined semi-mt capability, and is signaled to userspace via the INPUT_PROP_SEMI_MT device property. Work to decode the advanced gesture packet: Takashi Iwai. Cleanup and testing of the original patch: Chase Douglas. Minor cleanup and testing: Chris Bagwell. Finalization and semi-mt support: Henrik Rydberg. Reported-by: Tobyn Bertram Signed-off-by: Takashi Iwai Signed-off-by: Chase Douglas Signed-off-by: Chris Bagwell Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/mouse/synaptics.c | 88 +++++++++++++++++++++++++++++++++++++++-- drivers/input/mouse/synaptics.h | 3 ++ 2 files changed, 88 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 8997cbc69dcc..6514928fd35f 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include @@ -279,6 +279,25 @@ static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate) synaptics_mode_cmd(psmouse, priv->mode); } +static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) +{ + static unsigned char param = 0xc8; + struct synaptics_data *priv = psmouse->private; + + if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) + return 0; + + if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL)) + return -1; + if (ps2_command(&psmouse->ps2dev, ¶m, PSMOUSE_CMD_SETRATE)) + return -1; + + /* Advanced gesture mode also sends multi finger data */ + priv->capabilities |= BIT(1); + + return 0; +} + /***************************************************************************** * Synaptics pass-through PS/2 port support ****************************************************************************/ @@ -380,7 +399,9 @@ static void synaptics_pt_create(struct psmouse *psmouse) * Functions to interpret the absolute mode packets ****************************************************************************/ -static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) +static int synaptics_parse_hw_state(const unsigned char buf[], + struct synaptics_data *priv, + struct synaptics_hw_state *hw) { memset(hw, 0, sizeof(struct synaptics_hw_state)); @@ -397,6 +418,14 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data ((buf[0] & 0x04) >> 1) | ((buf[3] & 0x04) >> 2)); + if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) { + /* Gesture packet: (x, y, z) at half resolution */ + priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1; + priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1; + priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1; + return 1; + } + hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; @@ -452,6 +481,36 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; } + + return 0; +} + +static void set_slot(struct input_dev *dev, int slot, bool active, 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, + YMAX_NOMINAL + YMIN_NOMINAL - y); + } +} + +static void synaptics_report_semi_mt_data(struct input_dev *dev, + const struct synaptics_hw_state *a, + const struct synaptics_hw_state *b, + int num_fingers) +{ + if (num_fingers >= 2) { + set_slot(dev, 0, true, min(a->x, b->x), min(a->y, b->y)); + set_slot(dev, 1, true, max(a->x, b->x), max(a->y, b->y)); + } else if (num_fingers == 1) { + set_slot(dev, 0, true, a->x, a->y); + set_slot(dev, 1, false, 0, 0); + } else { + set_slot(dev, 0, false, 0, 0); + set_slot(dev, 1, false, 0, 0); + } } /* @@ -466,7 +525,8 @@ static void synaptics_process_packet(struct psmouse *psmouse) int finger_width; int i; - synaptics_parse_hw_state(psmouse->packet, priv, &hw); + if (synaptics_parse_hw_state(psmouse->packet, priv, &hw)) + return; if (hw.scroll) { priv->scroll += hw.scroll; @@ -512,6 +572,9 @@ static void synaptics_process_packet(struct psmouse *psmouse) finger_width = 0; } + if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) + synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers); + /* Post events * BTN_TOUCH has to be first as mousedev relies on it when doing * absolute -> relative conversion @@ -631,6 +694,15 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); + input_mt_init_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_POSITION_X, XMIN_NOMINAL, + priv->x_max ?: XMAX_NOMINAL, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, YMIN_NOMINAL, + priv->y_max ?: YMAX_NOMINAL, 0, 0); + } + if (SYN_CAP_PALMDETECT(priv->capabilities)) input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); @@ -705,6 +777,11 @@ static int synaptics_reconnect(struct psmouse *psmouse) return -1; } + if (synaptics_set_advanced_gesture_mode(psmouse)) { + printk(KERN_ERR "Advanced gesture mode reconnect failed.\n"); + return -1; + } + return 0; } @@ -772,6 +849,11 @@ int synaptics_init(struct psmouse *psmouse) goto init_fail; } + if (synaptics_set_advanced_gesture_mode(psmouse)) { + printk(KERN_ERR "Advanced gesture mode init failed.\n"); + goto init_fail; + } + priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n", diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 613a3652f98f..50e20e9da442 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -53,6 +53,7 @@ #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100) #define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000) +#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000) /* synaptics modes query bits */ #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) @@ -112,6 +113,8 @@ struct synaptics_data { int scroll; struct serio *pt_port; /* Pass-through serio port */ + + struct synaptics_hw_state mt; /* current gesture packet */ }; void synaptics_module_init(void); -- cgit v1.2.3 From 4f56ce929cab45a3a6e1a81700da52bb9bdbfc0f Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 18 Dec 2010 15:42:30 +0100 Subject: Input: synaptics - ignore bogus mt packet In multitouch mode, at least one device (fw: 7.4 id: 0x1c0b1) sometimes sends a final main packet with x == 1. Since the normal values are above 1472, this is clearly bogus. At the same time, a two-finger touch is signaled, even though only one finger was on the pad to begin with. This patch ignores the packet altogether, removing the problem. Acked-by: Chris Bagwell Acked-by: Chase Douglas Acked-by: Dmitry Torokhov Signed-off-by: Henrik Rydberg --- drivers/input/mouse/synaptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 6514928fd35f..720729afe6dc 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -548,7 +548,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) return; } - if (hw.z > 0) { + if (hw.z > 0 && hw.x > 1) { num_fingers = 1; finger_width = 5; if (SYN_CAP_EXTENDED(priv->capabilities)) { @@ -582,7 +582,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1); if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0); - if (hw.z > 0) { + if (num_fingers > 0) { input_report_abs(dev, ABS_X, hw.x); input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y); } -- cgit v1.2.3 From da1f026b532ce944d74461497dc6d8c16456466e Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Mon, 20 Dec 2010 21:09:22 +0000 Subject: Keyboard: omap-keypad: use matrix_keypad.h Most keypad drivers make use of the defined macros, structures and inline functions. Convert omap-keypad driver to use those as well, as suggested by a compile time warning, hardcoded into the OMAP . Created against linux-2.6.37-rc5. Tested on Amstrad Delta. Compile tested with omap1_defconfig and omap2plus_defconfig shrinked to board-h4. Signed-off-by: Janusz Krzysztofik Reviewed-by: Aaro Koskinen Acked-by: Dmitry Torokhov Signed-off-by: Tony Lindgren --- drivers/input/keyboard/omap-keypad.c | 41 +++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index a72e61ddca91..0e2a19cb43d8 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -65,7 +65,6 @@ struct omap_kp { static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); -static int *keymap; static unsigned int *row_gpios; static unsigned int *col_gpios; @@ -162,20 +161,11 @@ static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) } } -static inline int omap_kp_find_key(int col, int row) -{ - int i, key; - - key = KEY(col, row, 0); - for (i = 0; keymap[i] != 0; i++) - if ((keymap[i] & 0xff000000) == key) - return keymap[i] & 0x00ffffff; - return -1; -} - static void omap_kp_tasklet(unsigned long data) { struct omap_kp *omap_kp_data = (struct omap_kp *) data; + unsigned short *keycodes = omap_kp_data->input->keycode; + unsigned int row_shift = get_count_order(omap_kp_data->cols); unsigned char new_state[8], changed, key_down = 0; int col, row; int spurious = 0; @@ -199,7 +189,7 @@ static void omap_kp_tasklet(unsigned long data) row, (new_state[col] & (1 << row)) ? "pressed" : "released"); #else - key = omap_kp_find_key(col, row); + key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)]; if (key < 0) { printk(KERN_WARNING "omap-keypad: Spurious key event %d-%d\n", @@ -298,13 +288,18 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) struct input_dev *input_dev; struct omap_kp_platform_data *pdata = pdev->dev.platform_data; int i, col_idx, row_idx, irq_idx, ret; + unsigned int row_shift, keycodemax; - if (!pdata->rows || !pdata->cols || !pdata->keymap) { - printk(KERN_ERR "No rows, cols or keymap from pdata\n"); + if (!pdata->rows || !pdata->cols || !pdata->keymap_data) { + printk(KERN_ERR "No rows, cols or keymap_data from pdata\n"); return -EINVAL; } - omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL); + row_shift = get_count_order(pdata->cols); + keycodemax = pdata->rows << row_shift; + + omap_kp = kzalloc(sizeof(struct omap_kp) + + keycodemax * sizeof(unsigned short), GFP_KERNEL); input_dev = input_allocate_device(); if (!omap_kp || !input_dev) { kfree(omap_kp); @@ -320,7 +315,9 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) if (!cpu_is_omap24xx()) omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - keymap = pdata->keymap; + input_dev->keycode = &omap_kp[1]; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = keycodemax; if (pdata->rep) __set_bit(EV_REP, input_dev->evbit); @@ -374,8 +371,8 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) /* setup input device */ __set_bit(EV_KEY, input_dev->evbit); - for (i = 0; keymap[i] != 0; i++) - __set_bit(keymap[i] & KEY_MAX, input_dev->keybit); + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); input_dev->name = "omap-keypad"; input_dev->phys = "omap-keypad/input0"; input_dev->dev.parent = &pdev->dev; @@ -416,7 +413,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) return 0; err5: for (i = irq_idx - 1; i >=0; i--) - free_irq(row_gpios[i], 0); + free_irq(row_gpios[i], NULL); err4: input_unregister_device(omap_kp->input); input_dev = NULL; @@ -447,11 +444,11 @@ static int __devexit omap_kp_remove(struct platform_device *pdev) gpio_free(col_gpios[i]); for (i = 0; i < omap_kp->rows; i++) { gpio_free(row_gpios[i]); - free_irq(gpio_to_irq(row_gpios[i]), 0); + free_irq(gpio_to_irq(row_gpios[i]), NULL); } } else { omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - free_irq(omap_kp->irq, 0); + free_irq(omap_kp->irq, NULL); } del_timer_sync(&omap_kp->timer); -- cgit v1.2.3 From 7ee99161a4febe53c906cb9becc596075fd6193e Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 23 Dec 2010 01:18:28 -0800 Subject: Input: psmouse - fix up Synaptics comment Minor comment fixup for typos and grammar. Noticed while adding a separate workaround. Signed-off-by: Andres Salomon Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2e300a460556..53c65a634b26 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -802,8 +802,8 @@ int synaptics_init(struct psmouse *psmouse) /* * Toshiba's KBC seems to have trouble handling data from - * Synaptics as full rate, switch to lower rate which is roughly - * thye same as rate of standard PS/2 mouse. + * Synaptics at full rate. Switch to a lower rate (roughly + * the same rate as a standard PS/2 mouse). */ if (psmouse->rate >= 80 && impaired_toshiba_kbc) { printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", -- cgit v1.2.3 From ef8313bb1a22e7d2125d9d758aa8a81f1de91d81 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 23 Dec 2010 01:19:38 -0800 Subject: Input: psmouse - disable the synaptics extension on OLPC machines OLPC has switched to a Synaptics touchpad. It turns out that it's pretty useless in absolute mode. This patch looks for an OLPC system (via DMI tables), and refuses to init Synaptics mode in that scenario (falling back to relative mode). Signed-off-by: Andres Salomon Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 53c65a634b26..a977ef4c6625 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -744,15 +744,45 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = { #endif }; +static bool broken_olpc_ec; + +static const struct dmi_system_id __initconst olpc_dmi_table[] = { +#if defined(CONFIG_DMI) && defined(CONFIG_OLPC) + { + /* OLPC XO-1 or XO-1.5 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OLPC"), + DMI_MATCH(DMI_PRODUCT_NAME, "XO"), + }, + }, + { } +#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); } int synaptics_init(struct psmouse *psmouse) { struct synaptics_data *priv; + /* + * The OLPC XO has issues with Synaptics' absolute mode; similarly to + * the HGPK, it quickly degrades and the hardware becomes jumpy and + * overly sensitive. Not only that, but the constant packet spew + * (even at a lowered 40pps rate) overloads the EC such that key + * presses on the keyboard are missed. Given all of that, don't + * even attempt to use Synaptics mode. Relative mode seems to work + * just fine. + */ + if (broken_olpc_ec) { + printk(KERN_INFO "synaptics: OLPC XO detected, not enabling Synaptics protocol.\n"); + return -ENODEV; + } + psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) return -ENOMEM; -- cgit v1.2.3 From 7b4b30689d688d9ca2e5c3859db6bbe1c35e6014 Mon Sep 17 00:00:00 2001 From: Ajay Ramaswamy Date: Thu, 23 Dec 2010 01:19:39 -0800 Subject: Input: wacom - add support for digitizer in Lenovo W700 Signed-off-by: Ajay Ramaswamy Reviewed-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom.h | 1 + drivers/input/tablet/wacom_wac.c | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index de5adb109030..23317bd09c82 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -103,6 +103,7 @@ 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; diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 435b0af401e4..f72df2c4a649 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1444,11 +1444,17 @@ static struct wacom_features wacom_features_0xDA = { "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xDB = { "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; +static const struct wacom_features wacom_features_0x6004 = + { "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255, 0, TABLETPC }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ .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) }, @@ -1525,6 +1531,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xE2) }, { USB_DEVICE_WACOM(0xE3) }, { USB_DEVICE_WACOM(0x47) }, + { USB_DEVICE_LENOVO(0x6004) }, { } }; MODULE_DEVICE_TABLE(usb, wacom_ids); -- cgit v1.2.3 From a06a09c802c869426cfe8c405c381c985c3b25b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Mon, 27 Dec 2010 17:21:45 -0800 Subject: Input: i8042 - add Acer Aspire 5100 to the Dritek list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds notebook Acer Aspire 5100 to the list of Dritek HW. Acer Aspire 5100 needs Dritek keyboard extension to support all Fn keys. Signed-off-by: Pali Rohár Tested-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index cd9ff808bd23..5ae0fc4578fe 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -552,6 +552,13 @@ static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { * have turned up in 2007 that also need this again. */ static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { + { + /* Acer Aspire 5100 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"), + }, + }, { /* Acer Aspire 5610 */ .matches = { -- cgit v1.2.3 From ef11e701f32fb0cd5c5f0f6fb9a9e28fab151219 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 27 Dec 2010 17:20:01 -0800 Subject: Input: bu21013_ts - fix misuse of logical operation in place of bitop Signed-off-by: David Sterba Acked-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/bu21013_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 2ca9e5d66460..f7fa9ef4cd65 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -365,7 +365,7 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) } retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, - BU21013_TH_OFF_4 || BU21013_TH_OFF_3); + BU21013_TH_OFF_4 | BU21013_TH_OFF_3); if (retval < 0) { dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n"); return retval; -- cgit v1.2.3 From 202b6ca149dc90d3d81772413e1e1c0b65e83012 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 24 Dec 2010 13:13:25 -0800 Subject: Input: wacom_w8001 - use __set_bit to set keybits This makes code safer and easier to read. Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 2a0bec12d12a..49bce13838be 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -374,11 +374,11 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) input_dev->dev.parent = &serio->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); - input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); - input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); - input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); + __set_bit(BTN_TOUCH, 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); serio_set_drvdata(serio, w8001); err = serio_open(serio, drv); -- cgit v1.2.3 From 5b2e303f6df1e0b1a903950c5d613a20c8c71a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Fri, 29 Oct 2010 16:08:28 -0300 Subject: [media] rc-core: convert winbond-cir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move winbond-cir from drivers/input/misc/ into drivers/media/rc/ and convert it to use rc-core. Signed-off-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/input/misc/Kconfig | 18 - drivers/input/misc/Makefile | 1 - drivers/input/misc/winbond-cir.c | 1608 -------------------------------------- 3 files changed, 1627 deletions(-) delete mode 100644 drivers/input/misc/winbond-cir.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index b99b8cbde02f..c1a81bcdb319 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -294,24 +294,6 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. -config INPUT_WINBOND_CIR - tristate "Winbond IR remote control" - depends on X86 && PNP - select NEW_LEDS - select LEDS_CLASS - select LEDS_TRIGGERS - select BITREVERSE - help - Say Y here if you want to use the IR remote functionality found - in some Winbond SuperI/O chips. Currently only the WPCD376I - chip is supported (included in some Intel Media series motherboards). - - IR Receive and wake-on-IR from suspend and power-off is currently - supported. - - To compile this driver as a module, choose M here: the module will be - called winbond_cir. - config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1fe1f6c8b737..06b2b5154038 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -38,7 +38,6 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o -obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/input/misc/winbond-cir.c b/drivers/input/misc/winbond-cir.c deleted file mode 100644 index 64f1de7960c6..000000000000 --- a/drivers/input/misc/winbond-cir.c +++ /dev/null @@ -1,1608 +0,0 @@ -/* - * winbond-cir.c - Driver for the Consumer IR functionality of Winbond - * SuperI/O chips. - * - * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but - * could probably support others (Winbond WEC102X, NatSemi, etc) - * with minor modifications. - * - * Original Author: David Härdeman - * Copyright (C) 2009 David Härdeman - * - * Dedicated to Matilda, my newborn daughter, without whose loving attention - * this driver would have been finished in half the time and with a fraction - * of the bugs. - * - * Written using: - * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel - * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff) - * o DSDT dumps - * - * Supported features: - * o RC6 - * o Wake-On-CIR functionality - * - * To do: - * o Test NEC and RC5 - * - * Left as an exercise for the reader: - * o Learning (I have neither the hardware, nor the need) - * o IR Transmit (ibid) - * - * 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. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRVNAME "winbond-cir" - -/* CEIR Wake-Up Registers, relative to data->wbase */ -#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */ -#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */ -#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */ -#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */ -#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */ -#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */ -#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */ -#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */ -#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */ -#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */ - -/* CEIR Enhanced Functionality Registers, relative to data->ebase */ -#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */ -#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */ -#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */ -#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */ -#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */ - -/* SP3 Banked Registers, relative to data->sbase */ -#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */ - /* Bank 0 */ -#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */ -#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */ -#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */ -#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */ -#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */ -#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */ -#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */ -#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */ -#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */ - /* Bank 2 */ -#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */ -#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */ -#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */ -#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */ -#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */ -#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */ - /* Bank 3 */ -#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */ -#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */ -#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */ - /* Bank 4 */ -#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */ - /* Bank 5 */ -#define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */ - /* Bank 6 */ -#define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */ -#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */ - /* Bank 7 */ -#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */ -#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */ -#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */ -#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */ -#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */ - -/* - * Magic values follow - */ - -/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ -#define WBCIR_IRQ_NONE 0x00 -/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ -#define WBCIR_IRQ_RX 0x01 -/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ -#define WBCIR_IRQ_ERR 0x04 -/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ -#define WBCIR_LED_ENABLE 0x80 -/* RX data available bit for WBCIR_REG_SP3_LSR */ -#define WBCIR_RX_AVAIL 0x01 -/* RX disable bit for WBCIR_REG_SP3_ASCR */ -#define WBCIR_RX_DISABLE 0x20 -/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ -#define WBCIR_EXT_ENABLE 0x01 -/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ -#define WBCIR_REGSEL_COMPARE 0x10 -/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ -#define WBCIR_REGSEL_MASK 0x20 -/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ -#define WBCIR_REG_ADDR0 0x00 - -/* Valid banks for the SP3 UART */ -enum wbcir_bank { - WBCIR_BANK_0 = 0x00, - WBCIR_BANK_1 = 0x80, - WBCIR_BANK_2 = 0xE0, - WBCIR_BANK_3 = 0xE4, - WBCIR_BANK_4 = 0xE8, - WBCIR_BANK_5 = 0xEC, - WBCIR_BANK_6 = 0xF0, - WBCIR_BANK_7 = 0xF4, -}; - -/* Supported IR Protocols */ -enum wbcir_protocol { - IR_PROTOCOL_RC5 = 0x0, - IR_PROTOCOL_NEC = 0x1, - IR_PROTOCOL_RC6 = 0x2, -}; - -/* Misc */ -#define WBCIR_NAME "Winbond CIR" -#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ -#define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */ -#define IR_KEYPRESS_TIMEOUT 250 /* FIXME: should be per-protocol? */ -#define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */ -#define WAKEUP_IOMEM_LEN 0x10 /* Wake-Up I/O Reg Len */ -#define EHFUNC_IOMEM_LEN 0x10 /* Enhanced Func I/O Reg Len */ -#define SP_IOMEM_LEN 0x08 /* Serial Port 3 (IR) Reg Len */ -#define WBCIR_MAX_IDLE_BYTES 10 - -static DEFINE_SPINLOCK(wbcir_lock); -static DEFINE_RWLOCK(keytable_lock); - -struct wbcir_key { - u32 scancode; - unsigned int keycode; -}; - -struct wbcir_keyentry { - struct wbcir_key key; - struct list_head list; -}; - -static struct wbcir_key rc6_def_keymap[] = { - { 0x800F0400, KEY_NUMERIC_0 }, - { 0x800F0401, KEY_NUMERIC_1 }, - { 0x800F0402, KEY_NUMERIC_2 }, - { 0x800F0403, KEY_NUMERIC_3 }, - { 0x800F0404, KEY_NUMERIC_4 }, - { 0x800F0405, KEY_NUMERIC_5 }, - { 0x800F0406, KEY_NUMERIC_6 }, - { 0x800F0407, KEY_NUMERIC_7 }, - { 0x800F0408, KEY_NUMERIC_8 }, - { 0x800F0409, KEY_NUMERIC_9 }, - { 0x800F041D, KEY_NUMERIC_STAR }, - { 0x800F041C, KEY_NUMERIC_POUND }, - { 0x800F0410, KEY_VOLUMEUP }, - { 0x800F0411, KEY_VOLUMEDOWN }, - { 0x800F0412, KEY_CHANNELUP }, - { 0x800F0413, KEY_CHANNELDOWN }, - { 0x800F040E, KEY_MUTE }, - { 0x800F040D, KEY_VENDOR }, /* Vista Logo Key */ - { 0x800F041E, KEY_UP }, - { 0x800F041F, KEY_DOWN }, - { 0x800F0420, KEY_LEFT }, - { 0x800F0421, KEY_RIGHT }, - { 0x800F0422, KEY_OK }, - { 0x800F0423, KEY_ESC }, - { 0x800F040F, KEY_INFO }, - { 0x800F040A, KEY_CLEAR }, - { 0x800F040B, KEY_ENTER }, - { 0x800F045B, KEY_RED }, - { 0x800F045C, KEY_GREEN }, - { 0x800F045D, KEY_YELLOW }, - { 0x800F045E, KEY_BLUE }, - { 0x800F045A, KEY_TEXT }, - { 0x800F0427, KEY_SWITCHVIDEOMODE }, - { 0x800F040C, KEY_POWER }, - { 0x800F0450, KEY_RADIO }, - { 0x800F0448, KEY_PVR }, - { 0x800F0447, KEY_AUDIO }, - { 0x800F0426, KEY_EPG }, - { 0x800F0449, KEY_CAMERA }, - { 0x800F0425, KEY_TV }, - { 0x800F044A, KEY_VIDEO }, - { 0x800F0424, KEY_DVD }, - { 0x800F0416, KEY_PLAY }, - { 0x800F0418, KEY_PAUSE }, - { 0x800F0419, KEY_STOP }, - { 0x800F0414, KEY_FASTFORWARD }, - { 0x800F041A, KEY_NEXT }, - { 0x800F041B, KEY_PREVIOUS }, - { 0x800F0415, KEY_REWIND }, - { 0x800F0417, KEY_RECORD }, -}; - -/* Registers and other state is protected by wbcir_lock */ -struct wbcir_data { - unsigned long wbase; /* Wake-Up Baseaddr */ - unsigned long ebase; /* Enhanced Func. Baseaddr */ - unsigned long sbase; /* Serial Port Baseaddr */ - unsigned int irq; /* Serial Port IRQ */ - - struct input_dev *input_dev; - struct timer_list timer_keyup; - struct led_trigger *rxtrigger; - struct led_trigger *txtrigger; - struct led_classdev led; - - u32 last_scancode; - unsigned int last_keycode; - u8 last_toggle; - u8 keypressed; - unsigned long keyup_jiffies; - unsigned int idle_count; - - /* RX irdata and parsing state */ - unsigned long irdata[30]; - unsigned int irdata_count; - unsigned int irdata_idle; - unsigned int irdata_off; - unsigned int irdata_error; - - /* Protected by keytable_lock */ - struct list_head keytable; -}; - -static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; -module_param(protocol, uint, 0444); -MODULE_PARM_DESC(protocol, "IR protocol to use " - "(0 = RC5, 1 = NEC, 2 = RC6A, default)"); - -static int invert; /* default = 0 */ -module_param(invert, bool, 0444); -MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); - -static unsigned int wake_sc = 0x800F040C; -module_param(wake_sc, uint, 0644); -MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); - -static unsigned int wake_rc6mode = 6; -module_param(wake_rc6mode, uint, 0644); -MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command " - "(0 = 0, 6 = 6A, default)"); - - - -/***************************************************************************** - * - * UTILITY FUNCTIONS - * - *****************************************************************************/ - -/* Caller needs to hold wbcir_lock */ -static void -wbcir_set_bits(unsigned long addr, u8 bits, u8 mask) -{ - u8 val; - - val = inb(addr); - val = ((val & ~mask) | (bits & mask)); - outb(val, addr); -} - -/* Selects the register bank for the serial port */ -static inline void -wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) -{ - outb(bank, data->sbase + WBCIR_REG_SP3_BSR); -} - -static enum led_brightness -wbcir_led_brightness_get(struct led_classdev *led_cdev) -{ - struct wbcir_data *data = container_of(led_cdev, - struct wbcir_data, - led); - - if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE) - return LED_FULL; - else - return LED_OFF; -} - -static void -wbcir_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct wbcir_data *data = container_of(led_cdev, - struct wbcir_data, - led); - - wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, - brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE, - WBCIR_LED_ENABLE); -} - -/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */ -static u8 -wbcir_to_rc6cells(u8 val) -{ - u8 coded = 0x00; - int i; - - val &= 0x0F; - for (i = 0; i < 4; i++) { - if (val & 0x01) - coded |= 0x02 << (i * 2); - else - coded |= 0x01 << (i * 2); - val >>= 1; - } - - return coded; -} - - - -/***************************************************************************** - * - * INPUT FUNCTIONS - * - *****************************************************************************/ - -static unsigned int -wbcir_do_getkeycode(struct wbcir_data *data, u32 scancode) -{ - struct wbcir_keyentry *keyentry; - unsigned int keycode = KEY_RESERVED; - unsigned long flags; - - read_lock_irqsave(&keytable_lock, flags); - - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode == scancode) { - keycode = keyentry->key.keycode; - break; - } - } - - read_unlock_irqrestore(&keytable_lock, flags); - return keycode; -} - -static int -wbcir_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct wbcir_data *data = input_get_drvdata(dev); - - *keycode = wbcir_do_getkeycode(data, scancode); - return 0; -} - -static int -wbcir_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct wbcir_data *data = input_get_drvdata(dev); - struct wbcir_keyentry *keyentry; - struct wbcir_keyentry *new_keyentry; - unsigned long flags; - unsigned int old_keycode = KEY_RESERVED; - - new_keyentry = kmalloc(sizeof(*new_keyentry), GFP_KERNEL); - if (!new_keyentry) - return -ENOMEM; - - write_lock_irqsave(&keytable_lock, flags); - - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode != scancode) - continue; - - old_keycode = keyentry->key.keycode; - keyentry->key.keycode = keycode; - - if (keyentry->key.keycode == KEY_RESERVED) { - list_del(&keyentry->list); - kfree(keyentry); - } - - break; - } - - set_bit(keycode, dev->keybit); - - if (old_keycode == KEY_RESERVED) { - new_keyentry->key.scancode = scancode; - new_keyentry->key.keycode = keycode; - list_add(&new_keyentry->list, &data->keytable); - } else { - kfree(new_keyentry); - clear_bit(old_keycode, dev->keybit); - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.keycode == old_keycode) { - set_bit(old_keycode, dev->keybit); - break; - } - } - } - - write_unlock_irqrestore(&keytable_lock, flags); - return 0; -} - -/* - * Timer function to report keyup event some time after keydown is - * reported by the ISR. - */ -static void -wbcir_keyup(unsigned long cookie) -{ - struct wbcir_data *data = (struct wbcir_data *)cookie; - unsigned long flags; - - /* - * data->keyup_jiffies is used to prevent a race condition if a - * hardware interrupt occurs at this point and the keyup timer - * event is moved further into the future as a result. - * - * The timer will then be reactivated and this function called - * again in the future. We need to exit gracefully in that case - * to allow the input subsystem to do its auto-repeat magic or - * a keyup event might follow immediately after the keydown. - */ - - spin_lock_irqsave(&wbcir_lock, flags); - - if (time_is_after_eq_jiffies(data->keyup_jiffies) && data->keypressed) { - data->keypressed = 0; - led_trigger_event(data->rxtrigger, LED_OFF); - input_report_key(data->input_dev, data->last_keycode, 0); - input_sync(data->input_dev); - } - - spin_unlock_irqrestore(&wbcir_lock, flags); -} - -static void -wbcir_keydown(struct wbcir_data *data, u32 scancode, u8 toggle) -{ - unsigned int keycode; - - /* Repeat? */ - if (data->last_scancode == scancode && - data->last_toggle == toggle && - data->keypressed) - goto set_timer; - data->last_scancode = scancode; - - /* Do we need to release an old keypress? */ - if (data->keypressed) { - input_report_key(data->input_dev, data->last_keycode, 0); - input_sync(data->input_dev); - data->keypressed = 0; - } - - /* Report scancode */ - input_event(data->input_dev, EV_MSC, MSC_SCAN, (int)scancode); - - /* Do we know this scancode? */ - keycode = wbcir_do_getkeycode(data, scancode); - if (keycode == KEY_RESERVED) - goto set_timer; - - /* Register a keypress */ - input_report_key(data->input_dev, keycode, 1); - data->keypressed = 1; - data->last_keycode = keycode; - data->last_toggle = toggle; - -set_timer: - input_sync(data->input_dev); - led_trigger_event(data->rxtrigger, - data->keypressed ? LED_FULL : LED_OFF); - data->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); - mod_timer(&data->timer_keyup, data->keyup_jiffies); -} - - - -/***************************************************************************** - * - * IR PARSING FUNCTIONS - * - *****************************************************************************/ - -/* Resets all irdata */ -static void -wbcir_reset_irdata(struct wbcir_data *data) -{ - memset(data->irdata, 0, sizeof(data->irdata)); - data->irdata_count = 0; - data->irdata_off = 0; - data->irdata_error = 0; - data->idle_count = 0; -} - -/* Adds one bit of irdata */ -static void -add_irdata_bit(struct wbcir_data *data, int set) -{ - if (data->irdata_count >= sizeof(data->irdata) * 8) { - data->irdata_error = 1; - return; - } - - if (set) - __set_bit(data->irdata_count, data->irdata); - data->irdata_count++; -} - -/* Gets count bits of irdata */ -static u16 -get_bits(struct wbcir_data *data, int count) -{ - u16 val = 0x0; - - if (data->irdata_count - data->irdata_off < count) { - data->irdata_error = 1; - return 0x0; - } - - while (count > 0) { - val <<= 1; - if (test_bit(data->irdata_off, data->irdata)) - val |= 0x1; - count--; - data->irdata_off++; - } - - return val; -} - -/* Reads 16 cells and converts them to a byte */ -static u8 -wbcir_rc6cells_to_byte(struct wbcir_data *data) -{ - u16 raw = get_bits(data, 16); - u8 val = 0x00; - int bit; - - for (bit = 0; bit < 8; bit++) { - switch (raw & 0x03) { - case 0x01: - break; - case 0x02: - val |= (0x01 << bit); - break; - default: - data->irdata_error = 1; - break; - } - raw >>= 2; - } - - return val; -} - -/* Decodes a number of bits from raw RC5 data */ -static u8 -wbcir_get_rc5bits(struct wbcir_data *data, unsigned int count) -{ - u16 raw = get_bits(data, count * 2); - u8 val = 0x00; - int bit; - - for (bit = 0; bit < count; bit++) { - switch (raw & 0x03) { - case 0x01: - val |= (0x01 << bit); - break; - case 0x02: - break; - default: - data->irdata_error = 1; - break; - } - raw >>= 2; - } - - return val; -} - -static void -wbcir_parse_rc6(struct device *dev, struct wbcir_data *data) -{ - /* - * Normal bits are manchester coded as follows: - * cell0 + cell1 = logic "0" - * cell1 + cell0 = logic "1" - * - * The IR pulse has the following components: - * - * Leader - 6 * cell1 - discarded - * Gap - 2 * cell0 - discarded - * Start bit - Normal Coding - always "1" - * Mode Bit 2 - 0 - Normal Coding - * Toggle bit - Normal Coding with double bit time, - * e.g. cell0 + cell0 + cell1 + cell1 - * means logic "0". - * - * The rest depends on the mode, the following modes are known: - * - * MODE 0: - * Address Bit 7 - 0 - Normal Coding - * Command Bit 7 - 0 - Normal Coding - * - * MODE 6: - * The above Toggle Bit is used as a submode bit, 0 = A, 1 = B. - * Submode B is for pointing devices, only remotes using submode A - * are supported. - * - * Customer range bit - 0 => Customer = 7 bits, 0...127 - * 1 => Customer = 15 bits, 32768...65535 - * Customer Bits - Normal Coding - * - * Customer codes are allocated by Philips. The rest of the bits - * are customer dependent. The following is commonly used (and the - * only supported config): - * - * Toggle Bit - Normal Coding - * Address Bit 6 - 0 - Normal Coding - * Command Bit 7 - 0 - Normal Coding - * - * All modes are followed by at least 6 * cell0. - * - * MODE 0 msglen: - * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (toggle) + - * 8 * 2 (address) + 8 * 2 (command) = - * 44 cells - * - * MODE 6A msglen: - * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (submode) + - * 1 * 2 (customer range bit) + 7/15 * 2 (customer bits) + - * 1 * 2 (toggle bit) + 7 * 2 (address) + 8 * 2 (command) = - * 60 - 76 cells - */ - u8 mode; - u8 toggle; - u16 customer = 0x0; - u8 address; - u8 command; - u32 scancode; - - /* Leader mark */ - while (get_bits(data, 1) && !data->irdata_error) - /* Do nothing */; - - /* Leader space */ - if (get_bits(data, 1)) { - dev_dbg(dev, "RC6 - Invalid leader space\n"); - return; - } - - /* Start bit */ - if (get_bits(data, 2) != 0x02) { - dev_dbg(dev, "RC6 - Invalid start bit\n"); - return; - } - - /* Mode */ - mode = get_bits(data, 6); - switch (mode) { - case 0x15: /* 010101 = b000 */ - mode = 0; - break; - case 0x29: /* 101001 = b110 */ - mode = 6; - break; - default: - dev_dbg(dev, "RC6 - Invalid mode\n"); - return; - } - - /* Toggle bit / Submode bit */ - toggle = get_bits(data, 4); - switch (toggle) { - case 0x03: - toggle = 0; - break; - case 0x0C: - toggle = 1; - break; - default: - dev_dbg(dev, "RC6 - Toggle bit error\n"); - break; - } - - /* Customer */ - if (mode == 6) { - if (toggle != 0) { - dev_dbg(dev, "RC6B - Not Supported\n"); - return; - } - - customer = wbcir_rc6cells_to_byte(data); - - if (customer & 0x80) { - /* 15 bit customer value */ - customer <<= 8; - customer |= wbcir_rc6cells_to_byte(data); - } - } - - /* Address */ - address = wbcir_rc6cells_to_byte(data); - if (mode == 6) { - toggle = address >> 7; - address &= 0x7F; - } - - /* Command */ - command = wbcir_rc6cells_to_byte(data); - - /* Create scancode */ - scancode = command; - scancode |= address << 8; - scancode |= customer << 16; - - /* Last sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "RC6 - Cell error(s)\n"); - return; - } - - dev_dbg(dev, "IR-RC6 ad 0x%02X cm 0x%02X cu 0x%04X " - "toggle %u mode %u scan 0x%08X\n", - address, - command, - customer, - (unsigned int)toggle, - (unsigned int)mode, - scancode); - - wbcir_keydown(data, scancode, toggle); -} - -static void -wbcir_parse_rc5(struct device *dev, struct wbcir_data *data) -{ - /* - * Bits are manchester coded as follows: - * cell1 + cell0 = logic "0" - * cell0 + cell1 = logic "1" - * (i.e. the reverse of RC6) - * - * Start bit 1 - "1" - discarded - * Start bit 2 - Must be inverted to get command bit 6 - * Toggle bit - * Address Bit 4 - 0 - * Command Bit 5 - 0 - */ - u8 toggle; - u8 address; - u8 command; - u32 scancode; - - /* Start bit 1 */ - if (!get_bits(data, 1)) { - dev_dbg(dev, "RC5 - Invalid start bit\n"); - return; - } - - /* Start bit 2 */ - if (!wbcir_get_rc5bits(data, 1)) - command = 0x40; - else - command = 0x00; - - toggle = wbcir_get_rc5bits(data, 1); - address = wbcir_get_rc5bits(data, 5); - command |= wbcir_get_rc5bits(data, 6); - scancode = address << 7 | command; - - /* Last sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "RC5 - Invalid message\n"); - return; - } - - dev_dbg(dev, "IR-RC5 ad %u cm %u t %u s %u\n", - (unsigned int)address, - (unsigned int)command, - (unsigned int)toggle, - (unsigned int)scancode); - - wbcir_keydown(data, scancode, toggle); -} - -static void -wbcir_parse_nec(struct device *dev, struct wbcir_data *data) -{ - /* - * Each bit represents 560 us. - * - * Leader - 9 ms burst - * Gap - 4.5 ms silence - * Address1 bit 0 - 7 - Address 1 - * Address2 bit 0 - 7 - Address 2 - * Command1 bit 0 - 7 - Command 1 - * Command2 bit 0 - 7 - Command 2 - * - * Note the bit order! - * - * With the old NEC protocol, Address2 was the inverse of Address1 - * and Command2 was the inverse of Command1 and were used as - * an error check. - * - * With NEC extended, Address1 is the LSB of the Address and - * Address2 is the MSB, Command parsing remains unchanged. - * - * A repeat message is coded as: - * Leader - 9 ms burst - * Gap - 2.25 ms silence - * Repeat - 560 us active - */ - u8 address1; - u8 address2; - u8 command1; - u8 command2; - u16 address; - u32 scancode; - - /* Leader mark */ - while (get_bits(data, 1) && !data->irdata_error) - /* Do nothing */; - - /* Leader space */ - if (get_bits(data, 4)) { - dev_dbg(dev, "NEC - Invalid leader space\n"); - return; - } - - /* Repeat? */ - if (get_bits(data, 1)) { - if (!data->keypressed) { - dev_dbg(dev, "NEC - Stray repeat message\n"); - return; - } - - dev_dbg(dev, "IR-NEC repeat s %u\n", - (unsigned int)data->last_scancode); - - wbcir_keydown(data, data->last_scancode, data->last_toggle); - return; - } - - /* Remaining leader space */ - if (get_bits(data, 3)) { - dev_dbg(dev, "NEC - Invalid leader space\n"); - return; - } - - address1 = bitrev8(get_bits(data, 8)); - address2 = bitrev8(get_bits(data, 8)); - command1 = bitrev8(get_bits(data, 8)); - command2 = bitrev8(get_bits(data, 8)); - - /* Sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "NEC - Invalid message\n"); - return; - } - - /* Check command validity */ - if (command1 != ~command2) { - dev_dbg(dev, "NEC - Command bytes mismatch\n"); - return; - } - - /* Check for extended NEC protocol */ - address = address1; - if (address1 != ~address2) - address |= address2 << 8; - - scancode = address << 8 | command1; - - dev_dbg(dev, "IR-NEC ad %u cm %u s %u\n", - (unsigned int)address, - (unsigned int)command1, - (unsigned int)scancode); - - wbcir_keydown(data, scancode, !data->last_toggle); -} - - - -/***************************************************************************** - * - * INTERRUPT FUNCTIONS - * - *****************************************************************************/ - -static irqreturn_t -wbcir_irq_handler(int irqno, void *cookie) -{ - struct pnp_dev *device = cookie; - struct wbcir_data *data = pnp_get_drvdata(device); - struct device *dev = &device->dev; - u8 status; - unsigned long flags; - u8 irdata[8]; - int i; - unsigned int hw; - - spin_lock_irqsave(&wbcir_lock, flags); - - wbcir_select_bank(data, WBCIR_BANK_0); - - status = inb(data->sbase + WBCIR_REG_SP3_EIR); - - if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { - spin_unlock_irqrestore(&wbcir_lock, flags); - return IRQ_NONE; - } - - if (status & WBCIR_IRQ_ERR) - data->irdata_error = 1; - - if (!(status & WBCIR_IRQ_RX)) - goto out; - - /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ - insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); - - for (i = 0; i < sizeof(irdata); i++) { - hw = hweight8(irdata[i]); - if (hw > 4) - add_irdata_bit(data, 0); - else - add_irdata_bit(data, 1); - - if (hw == 8) - data->idle_count++; - else - data->idle_count = 0; - } - - if (data->idle_count > WBCIR_MAX_IDLE_BYTES) { - /* Set RXINACTIVE... */ - outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); - - /* ...and drain the FIFO */ - while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) - inb(data->sbase + WBCIR_REG_SP3_RXDATA); - - dev_dbg(dev, "IRDATA:\n"); - for (i = 0; i < data->irdata_count; i += BITS_PER_LONG) - dev_dbg(dev, "0x%08lX\n", data->irdata[i/BITS_PER_LONG]); - - switch (protocol) { - case IR_PROTOCOL_RC5: - wbcir_parse_rc5(dev, data); - break; - case IR_PROTOCOL_RC6: - wbcir_parse_rc6(dev, data); - break; - case IR_PROTOCOL_NEC: - wbcir_parse_nec(dev, data); - break; - } - - wbcir_reset_irdata(data); - } - -out: - spin_unlock_irqrestore(&wbcir_lock, flags); - return IRQ_HANDLED; -} - - - -/***************************************************************************** - * - * SETUP/INIT/SUSPEND/RESUME FUNCTIONS - * - *****************************************************************************/ - -static void -wbcir_shutdown(struct pnp_dev *device) -{ - struct device *dev = &device->dev; - struct wbcir_data *data = pnp_get_drvdata(device); - int do_wake = 1; - u8 match[11]; - u8 mask[11]; - u8 rc6_csl = 0; - int i; - - memset(match, 0, sizeof(match)); - memset(mask, 0, sizeof(mask)); - - if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) { - do_wake = 0; - goto finish; - } - - switch (protocol) { - case IR_PROTOCOL_RC5: - if (wake_sc > 0xFFF) { - do_wake = 0; - dev_err(dev, "RC5 - Invalid wake scancode\n"); - break; - } - - /* Mask = 13 bits, ex toggle */ - mask[0] = 0xFF; - mask[1] = 0x17; - - match[0] = (wake_sc & 0x003F); /* 6 command bits */ - match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */ - match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */ - if (!(wake_sc & 0x0040)) /* 2nd start bit */ - match[1] |= 0x10; - - break; - - case IR_PROTOCOL_NEC: - if (wake_sc > 0xFFFFFF) { - do_wake = 0; - dev_err(dev, "NEC - Invalid wake scancode\n"); - break; - } - - mask[0] = mask[1] = mask[2] = mask[3] = 0xFF; - - match[1] = bitrev8((wake_sc & 0xFF)); - match[0] = ~match[1]; - - match[3] = bitrev8((wake_sc & 0xFF00) >> 8); - if (wake_sc > 0xFFFF) - match[2] = bitrev8((wake_sc & 0xFF0000) >> 16); - else - match[2] = ~match[3]; - - break; - - case IR_PROTOCOL_RC6: - - if (wake_rc6mode == 0) { - if (wake_sc > 0xFFFF) { - do_wake = 0; - dev_err(dev, "RC6 - Invalid wake scancode\n"); - break; - } - - /* Command */ - match[0] = wbcir_to_rc6cells(wake_sc >> 0); - mask[0] = 0xFF; - match[1] = wbcir_to_rc6cells(wake_sc >> 4); - mask[1] = 0xFF; - - /* Address */ - match[2] = wbcir_to_rc6cells(wake_sc >> 8); - mask[2] = 0xFF; - match[3] = wbcir_to_rc6cells(wake_sc >> 12); - mask[3] = 0xFF; - - /* Header */ - match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */ - mask[4] = 0xF0; - match[5] = 0x09; /* start bit = 1, mode2 = 0 */ - mask[5] = 0x0F; - - rc6_csl = 44; - - } else if (wake_rc6mode == 6) { - i = 0; - - /* Command */ - match[i] = wbcir_to_rc6cells(wake_sc >> 0); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 4); - mask[i++] = 0xFF; - - /* Address + Toggle */ - match[i] = wbcir_to_rc6cells(wake_sc >> 8); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 12); - mask[i++] = 0x3F; - - /* Customer bits 7 - 0 */ - match[i] = wbcir_to_rc6cells(wake_sc >> 16); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 20); - mask[i++] = 0xFF; - - if (wake_sc & 0x80000000) { - /* Customer range bit and bits 15 - 8 */ - match[i] = wbcir_to_rc6cells(wake_sc >> 24); - mask[i++] = 0xFF; - match[i] = wbcir_to_rc6cells(wake_sc >> 28); - mask[i++] = 0xFF; - rc6_csl = 76; - } else if (wake_sc <= 0x007FFFFF) { - rc6_csl = 60; - } else { - do_wake = 0; - dev_err(dev, "RC6 - Invalid wake scancode\n"); - break; - } - - /* Header */ - match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */ - mask[i++] = 0xFF; - match[i] = 0x0A; /* start bit = 1, mode2 = 1 */ - mask[i++] = 0x0F; - - } else { - do_wake = 0; - dev_err(dev, "RC6 - Invalid wake mode\n"); - } - - break; - - default: - do_wake = 0; - break; - } - -finish: - if (do_wake) { - /* Set compare and compare mask */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, - WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0, - 0x3F); - outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11); - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, - WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0, - 0x3F); - outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11); - - /* RC6 Compare String Len */ - outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL); - - /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); - - /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07); - - /* Set CEIR_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01); - - } else { - /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); - - /* Clear CEIR_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); - } - - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - /* - * ACPI will set the HW disable bit for SP3 which means that the - * output signals are left in an undefined state which may cause - * spurious interrupts which we need to ignore until the hardware - * is reinitialized. - */ - disable_irq(data->irq); -} - -static int -wbcir_suspend(struct pnp_dev *device, pm_message_t state) -{ - wbcir_shutdown(device); - return 0; -} - -static void -wbcir_init_hw(struct wbcir_data *data) -{ - u8 tmp; - - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ - tmp = protocol << 4; - if (invert) - tmp |= 0x08; - outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL); - - /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); - - /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); - - /* Set RC5 cell time to correspond to 36 kHz */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F); - - /* Set IRTX_INV */ - if (invert) - outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL); - else - outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); - - /* - * Clear IR LED, set SP3 clock to 24Mhz - * set SP3_IRRX_SW to binary 01, helpfully not documented - */ - outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); - - /* Enable extended mode */ - wbcir_select_bank(data, WBCIR_BANK_2); - outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1); - - /* - * Configure baud generator, IR data will be sampled at - * a bitrate of: (24Mhz * prescaler) / (divisor * 16). - * - * The ECIR registers include a flag to change the - * 24Mhz clock freq to 48Mhz. - * - * It's not documented in the specs, but fifo levels - * other than 16 seems to be unsupported. - */ - - /* prescaler 1.0, tx/rx fifo lvl 16 */ - outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); - - /* Set baud divisor to generate one byte per bit/cell */ - switch (protocol) { - case IR_PROTOCOL_RC5: - outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL); - break; - case IR_PROTOCOL_RC6: - outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL); - break; - case IR_PROTOCOL_NEC: - outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL); - break; - } - outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); - - /* Set CEIR mode */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR); - inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ - inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ - - /* Disable RX demod, run-length encoding/decoding, set freq span */ - wbcir_select_bank(data, WBCIR_BANK_7); - outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG); - - /* Disable timer */ - wbcir_select_bank(data, WBCIR_BANK_4); - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); - - /* Enable MSR interrupt, Clear AUX_IRX */ - wbcir_select_bank(data, WBCIR_BANK_5); - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); - - /* Disable CRC */ - wbcir_select_bank(data, WBCIR_BANK_6); - outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); - - /* Set RX/TX (de)modulation freq, not really used */ - wbcir_select_bank(data, WBCIR_BANK_7); - outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); - outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); - - /* Set invert and pin direction */ - if (invert) - outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4); - else - outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4); - - /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(0x97, data->sbase + WBCIR_REG_SP3_FCR); - - /* Clear AUX status bits */ - outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); - - /* Enable interrupts */ - wbcir_reset_irdata(data); - outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); -} - -static int -wbcir_resume(struct pnp_dev *device) -{ - struct wbcir_data *data = pnp_get_drvdata(device); - - wbcir_init_hw(data); - enable_irq(data->irq); - - return 0; -} - -static int __devinit -wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) -{ - struct device *dev = &device->dev; - struct wbcir_data *data; - int err; - - if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN && - pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN && - pnp_port_len(device, 2) == SP_IOMEM_LEN)) { - dev_err(dev, "Invalid resources\n"); - return -ENODEV; - } - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit; - } - - pnp_set_drvdata(device, data); - - data->ebase = pnp_port_start(device, 0); - data->wbase = pnp_port_start(device, 1); - data->sbase = pnp_port_start(device, 2); - data->irq = pnp_irq(device, 0); - - if (data->wbase == 0 || data->ebase == 0 || - data->sbase == 0 || data->irq == 0) { - err = -ENODEV; - dev_err(dev, "Invalid resources\n"); - goto exit_free_data; - } - - dev_dbg(&device->dev, "Found device " - "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n", - data->wbase, data->ebase, data->sbase, data->irq); - - if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { - dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", - data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); - err = -EBUSY; - goto exit_free_data; - } - - if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { - dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", - data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1); - err = -EBUSY; - goto exit_release_wbase; - } - - if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) { - dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", - data->sbase, data->sbase + SP_IOMEM_LEN - 1); - err = -EBUSY; - goto exit_release_ebase; - } - - err = request_irq(data->irq, wbcir_irq_handler, - IRQF_DISABLED, DRVNAME, device); - if (err) { - dev_err(dev, "Failed to claim IRQ %u\n", data->irq); - err = -EBUSY; - goto exit_release_sbase; - } - - led_trigger_register_simple("cir-tx", &data->txtrigger); - if (!data->txtrigger) { - err = -ENOMEM; - goto exit_free_irq; - } - - led_trigger_register_simple("cir-rx", &data->rxtrigger); - if (!data->rxtrigger) { - err = -ENOMEM; - goto exit_unregister_txtrigger; - } - - data->led.name = "cir::activity"; - data->led.default_trigger = "cir-rx"; - data->led.brightness_set = wbcir_led_brightness_set; - data->led.brightness_get = wbcir_led_brightness_get; - err = led_classdev_register(&device->dev, &data->led); - if (err) - goto exit_unregister_rxtrigger; - - data->input_dev = input_allocate_device(); - if (!data->input_dev) { - err = -ENOMEM; - goto exit_unregister_led; - } - - data->input_dev->evbit[0] = BIT(EV_KEY); - data->input_dev->name = WBCIR_NAME; - data->input_dev->phys = "wbcir/cir0"; - data->input_dev->id.bustype = BUS_HOST; - data->input_dev->id.vendor = PCI_VENDOR_ID_WINBOND; - data->input_dev->id.product = WBCIR_ID_FAMILY; - data->input_dev->id.version = WBCIR_ID_CHIP; - data->input_dev->getkeycode = wbcir_getkeycode; - data->input_dev->setkeycode = wbcir_setkeycode; - input_set_capability(data->input_dev, EV_MSC, MSC_SCAN); - input_set_drvdata(data->input_dev, data); - - err = input_register_device(data->input_dev); - if (err) - goto exit_free_input; - - data->last_scancode = INVALID_SCANCODE; - INIT_LIST_HEAD(&data->keytable); - setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data); - - /* Load default keymaps */ - if (protocol == IR_PROTOCOL_RC6) { - int i; - for (i = 0; i < ARRAY_SIZE(rc6_def_keymap); i++) { - err = wbcir_setkeycode(data->input_dev, - (int)rc6_def_keymap[i].scancode, - (int)rc6_def_keymap[i].keycode); - if (err) - goto exit_unregister_keys; - } - } - - device_init_wakeup(&device->dev, 1); - - wbcir_init_hw(data); - - return 0; - -exit_unregister_keys: - if (!list_empty(&data->keytable)) { - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; - - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } - } - input_unregister_device(data->input_dev); - /* Can't call input_free_device on an unregistered device */ - data->input_dev = NULL; -exit_free_input: - input_free_device(data->input_dev); -exit_unregister_led: - led_classdev_unregister(&data->led); -exit_unregister_rxtrigger: - led_trigger_unregister_simple(data->rxtrigger); -exit_unregister_txtrigger: - led_trigger_unregister_simple(data->txtrigger); -exit_free_irq: - free_irq(data->irq, device); -exit_release_sbase: - release_region(data->sbase, SP_IOMEM_LEN); -exit_release_ebase: - release_region(data->ebase, EHFUNC_IOMEM_LEN); -exit_release_wbase: - release_region(data->wbase, WAKEUP_IOMEM_LEN); -exit_free_data: - kfree(data); - pnp_set_drvdata(device, NULL); -exit: - return err; -} - -static void __devexit -wbcir_remove(struct pnp_dev *device) -{ - struct wbcir_data *data = pnp_get_drvdata(device); - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; - - /* Disable interrupts */ - wbcir_select_bank(data, WBCIR_BANK_0); - outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - - del_timer_sync(&data->timer_keyup); - - free_irq(data->irq, device); - - /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); - - /* Clear CEIR_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); - - /* Clear BUFF_EN, END_EN, MATCH_EN */ - wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); - - /* This will generate a keyup event if necessary */ - input_unregister_device(data->input_dev); - - led_trigger_unregister_simple(data->rxtrigger); - led_trigger_unregister_simple(data->txtrigger); - led_classdev_unregister(&data->led); - - /* This is ok since &data->led isn't actually used */ - wbcir_led_brightness_set(&data->led, LED_OFF); - - release_region(data->wbase, WAKEUP_IOMEM_LEN); - release_region(data->ebase, EHFUNC_IOMEM_LEN); - release_region(data->sbase, SP_IOMEM_LEN); - - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } - - kfree(data); - - pnp_set_drvdata(device, NULL); -} - -static const struct pnp_device_id wbcir_ids[] = { - { "WEC1022", 0 }, - { "", 0 } -}; -MODULE_DEVICE_TABLE(pnp, wbcir_ids); - -static struct pnp_driver wbcir_driver = { - .name = WBCIR_NAME, - .id_table = wbcir_ids, - .probe = wbcir_probe, - .remove = __devexit_p(wbcir_remove), - .suspend = wbcir_suspend, - .resume = wbcir_resume, - .shutdown = wbcir_shutdown -}; - -static int __init -wbcir_init(void) -{ - int ret; - - switch (protocol) { - case IR_PROTOCOL_RC5: - case IR_PROTOCOL_NEC: - case IR_PROTOCOL_RC6: - break; - default: - printk(KERN_ERR DRVNAME ": Invalid protocol argument\n"); - return -EINVAL; - } - - ret = pnp_register_driver(&wbcir_driver); - if (ret) - printk(KERN_ERR DRVNAME ": Unable to register driver\n"); - - return ret; -} - -static void __exit -wbcir_exit(void) -{ - pnp_unregister_driver(&wbcir_driver); -} - -MODULE_AUTHOR("David Härdeman "); -MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver"); -MODULE_LICENSE("GPL"); - -module_init(wbcir_init); -module_exit(wbcir_exit); - - -- cgit v1.2.3 From 09c730a488c32c2cadb31cdb8dcc4df528441197 Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Tue, 21 Dec 2010 15:53:31 +0530 Subject: input/tc3589x: add tc3589x keypad support Add support for the keypad controller module found on the TC3589X devices. This driver default adds the support for TC35893 device. Signed-off-by: Sundar Iyer Acked-by: Dmitry Torokhov [Some minor fixups for compilation] Signed-off-by: Linus Walleij --- drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/tc3589x-keypad.c | 472 ++++++++++++++++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 drivers/input/keyboard/tc3589x-keypad.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b8c51b9781db..85af3c3f7bcb 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -443,6 +443,16 @@ config KEYBOARD_OMAP4 To compile this driver as a module, choose M here: the module will be called omap4-keypad. +config KEYBOARD_TC3589X + tristate "TC3589X Keypad support" + depends on MFD_TC3589X + help + Say Y here if you want to use the keypad controller on + TC35892/3 I/O expander. + + To compile this driver as a module, choose M here: the + module will be called tc3589x-keypad. + config KEYBOARD_TNETV107X tristate "TI TNETV107X keypad support" depends on ARCH_DAVINCI_TNETV107X diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e8ebe2..4411c70db3b5 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c new file mode 100644 index 000000000000..69dc0cb20a00 --- /dev/null +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -0,0 +1,472 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jayeeta Banerjee + * Author: Sundar Iyer + * + * License Terms: GNU General Public License, version 2 + * + * TC35893 MFD Keypad Controller driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Maximum supported keypad matrix row/columns size */ +#define TC3589x_MAX_KPROW 8 +#define TC3589x_MAX_KPCOL 12 + +/* keypad related Constants */ +#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF +#define DEDICATED_KEY_VAL 0xFF + +/* Pull up/down masks */ +#define TC3589x_NO_PULL_MASK 0x0 +#define TC3589x_PULL_DOWN_MASK 0x1 +#define TC3589x_PULL_UP_MASK 0x2 +#define TC3589x_PULLUP_ALL_MASK 0xAA +#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2)) + +/* Bit masks for IOCFG register */ +#define IOCFG_BALLCFG 0x01 +#define IOCFG_IG 0x08 + +#define KP_EVCODE_COL_MASK 0x0F +#define KP_EVCODE_ROW_MASK 0x70 +#define KP_RELEASE_EVT_MASK 0x80 + +#define KP_ROW_SHIFT 4 + +#define KP_NO_VALID_KEY_MASK 0x7F + +/* bit masks for RESTCTRL register */ +#define TC3589x_KBDRST 0x2 +#define TC3589x_IRQRST 0x10 +#define TC3589x_RESET_ALL 0x1B + +/* KBDMFS register bit mask */ +#define TC3589x_KBDMFS_EN 0x1 + +/* CLKEN register bitmask */ +#define KPD_CLK_EN 0x1 + +/* RSTINTCLR register bit mask */ +#define IRQ_CLEAR 0x1 + +/* bit masks for keyboard interrupts*/ +#define TC3589x_EVT_LOSS_INT 0x8 +#define TC3589x_EVT_INT 0x4 +#define TC3589x_KBD_LOSS_INT 0x2 +#define TC3589x_KBD_INT 0x1 + +/* bit masks for keyboard interrupt clear*/ +#define TC3589x_EVT_INT_CLR 0x2 +#define TC3589x_KBD_INT_CLR 0x1 + +#define TC3589x_KBD_KEYMAP_SIZE 64 + +/** + * struct tc_keypad - data structure used by keypad driver + * @input: pointer to input device object + * @board: keypad platform device + * @krow: number of rows + * @kcol: number of coloumns + * @keymap: matrix scan code table for keycodes + */ +struct tc_keypad { + struct tc3589x *tc3589x; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *board; + unsigned int krow; + unsigned int kcol; + unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE]; + bool keypad_stopped; +}; + +static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad) +{ + int ret; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 settle_time = keypad->board->settle_time; + u8 dbounce_period = keypad->board->debounce_period; + u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */ + u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */ + + /* validate platform configurations */ + if (keypad->board->kcol > TC3589x_MAX_KPCOL || + keypad->board->krow > TC3589x_MAX_KPROW || + keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE || + keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE) + return -EINVAL; + + /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE, + (rows << KP_ROW_SHIFT) | column); + if (ret < 0) + return ret; + + /* configure dedicated key config, no dedicated key selected */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL); + if (ret < 0) + return ret; + + /* Configure settle time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time); + if (ret < 0) + return ret; + + /* Configure debounce time */ + ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period); + if (ret < 0) + return ret; + + /* Start of initialise keypad GPIOs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all row GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + /* Configure pull-up resistors for all column GPIOs */ + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB, + TC3589x_PULLUP_ALL_MASK); + if (ret < 0) + return ret; + + ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB, + TC3589x_PULLUP_ALL_MASK); + + return ret; +} + +#define TC35893_DATA_REGS 4 +#define TC35893_KEYCODE_FIFO_EMPTY 0x7f +#define TC35893_KEYCODE_FIFO_CLEAR 0xff +#define TC35893_KEYPAD_ROW_SHIFT 0x3 + +static irqreturn_t tc3589x_keypad_irq(int irq, void *dev) +{ + struct tc_keypad *keypad = dev; + struct tc3589x *tc3589x = keypad->tc3589x; + u8 i, row_index, col_index, kbd_code, up; + u8 code; + + for (i = 0; i < TC35893_DATA_REGS * 2; i++) { + kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO); + + /* loop till fifo is empty and no more keys are pressed */ + if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY || + kbd_code == TC35893_KEYCODE_FIFO_CLEAR) + continue; + + /* valid key is found */ + col_index = kbd_code & KP_EVCODE_COL_MASK; + row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT; + code = MATRIX_SCAN_CODE(row_index, col_index, + TC35893_KEYPAD_ROW_SHIFT); + up = kbd_code & KP_RELEASE_EVT_MASK; + + input_event(keypad->input, EV_MSC, MSC_SCAN, code); + input_report_key(keypad->input, keypad->keymap[code], !up); + input_sync(keypad->input); + } + + /* clear IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + /* enable IRQ */ + tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + 0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + + return IRQ_HANDLED; +} + +static int tc3589x_keypad_enable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* pull the keypad module out of reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0); + if (ret < 0) + return ret; + + /* configure KBDMFS */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN); + if (ret < 0) + return ret; + + /* enable the keypad clock */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN); + if (ret < 0) + return ret; + + /* clear pending IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1); + if (ret < 0) + return ret; + + /* enable the IRQs */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0, + TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); + if (ret < 0) + return ret; + + keypad->keypad_stopped = false; + + return ret; +} + +static int tc3589x_keypad_disable(struct tc_keypad *keypad) +{ + struct tc3589x *tc3589x = keypad->tc3589x; + int ret; + + /* clear IRQ */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC, + 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); + if (ret < 0) + return ret; + + /* disable all interrupts */ + ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, + ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0); + if (ret < 0) + return ret; + + /* disable the keypad module */ + ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0); + if (ret < 0) + return ret; + + /* put the keypad module into reset */ + ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1); + + keypad->keypad_stopped = true; + + return ret; +} + +static int tc3589x_keypad_open(struct input_dev *input) +{ + int error; + struct tc_keypad *keypad = input_get_drvdata(input); + + /* enable the keypad module */ + error = tc3589x_keypad_enable(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to enable keypad module\n"); + return error; + } + + error = tc3589x_keypad_init_key_hardware(keypad); + if (error < 0) { + dev_err(&input->dev, "failed to configure keypad module\n"); + return error; + } + + return 0; +} + +static void tc3589x_keypad_close(struct input_dev *input) +{ + struct tc_keypad *keypad = input_get_drvdata(input); + + /* disable the keypad module */ + tc3589x_keypad_disable(keypad); +} + +static int __devinit tc3589x_keypad_probe(struct platform_device *pdev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); + struct tc_keypad *keypad; + struct input_dev *input; + const struct tc3589x_keypad_platform_data *plat; + int error, irq; + + plat = tc3589x->pdata->keypad; + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL); + input = input_allocate_device(); + if (!keypad || !input) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + keypad->board = plat; + keypad->input = input; + keypad->tc3589x = tc3589x; + + input->id.bustype = BUS_I2C; + input->name = pdev->name; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input->open = tc3589x_keypad_open; + input->close = tc3589x_keypad_close; + + input_set_drvdata(input, keypad); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, 0x3, + input->keycode, input->keybit); + + error = request_threaded_irq(irq, NULL, + tc3589x_keypad_irq, plat->irqtype, + "tc3589x-keypad", keypad); + if (error < 0) { + dev_err(&pdev->dev, + "Could not allocate irq %d,error %d\n", + irq, error); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "Could not register input device\n"); + goto err_free_irq; + } + + /* let platform decide if keypad is a wakeup source or not */ + device_init_wakeup(&pdev->dev, plat->enable_wakeup); + device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup); + + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_irq: + free_irq(irq, keypad); +err_free_mem: + input_free_device(input); + kfree(keypad); + return error; +} + +static int __devexit tc3589x_keypad_remove(struct platform_device *pdev) +{ + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + tc3589x_keypad_disable(keypad); + + free_irq(irq, keypad); + + input_unregister_device(keypad->input); + + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int tc3589x_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + /* keypad is already off; we do nothing */ + if (keypad->keypad_stopped) + return 0; + + /* if device is not a wakeup source, disable it for powersave */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_disable(keypad); + else + enable_irq_wake(irq); + + return 0; +} + +static int tc3589x_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tc_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (!keypad->keypad_stopped) + return 0; + + /* enable the device to resume normal operations */ + if (!device_may_wakeup(&pdev->dev)) + tc3589x_keypad_enable(keypad); + else + disable_irq_wake(irq); + + return 0; +} + +static const SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops, + tc3589x_keypad_suspend, tc3589x_keypad_resume); +#endif + +static struct platform_driver tc3589x_keypad_driver = { + .driver.name = "tc3589x-keypad", + .driver.owner = THIS_MODULE, +#ifdef CONFIG_PM + .driver.pm = &tc3589x_keypad_dev_pm_ops, +#endif + .probe = tc3589x_keypad_probe, + .remove = __devexit_p(tc3589x_keypad_remove), +}; + +static int __init tc3589x_keypad_init(void) +{ + return platform_driver_register(&tc3589x_keypad_driver); +} +module_init(tc3589x_keypad_init); + +static void __exit tc3589x_keypad_exit(void) +{ + return platform_driver_unregister(&tc3589x_keypad_driver); +} +module_exit(tc3589x_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer"); +MODULE_DESCRIPTION("TC35893 Keypad Driver"); +MODULE_ALIAS("platform:tc3589x-keypad") -- cgit v1.2.3 From a6d38f889750ed6290728a19d9dad577b147c6d0 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 24 Dec 2010 13:16:53 -0800 Subject: Input: wacom_w8001 - support pen or touch only devices Not all penabled devices support touch. The same holds true for touch devices, so we should be setting up devices according to the results returned when we query the hardware. Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 44 ++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 49bce13838be..8ed53aded2d3 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -19,6 +19,7 @@ #include #include #include +#include #define DRIVER_DESC "Wacom W8001 serial touchscreen driver" @@ -37,6 +38,7 @@ MODULE_LICENSE("GPL"); #define W8001_QUERY_PACKET 0x20 +#define W8001_CMD_STOP '0' #define W8001_CMD_START '1' #define W8001_CMD_QUERY '*' #define W8001_CMD_TOUCHQUERY '%' @@ -279,24 +281,46 @@ static int w8001_setup(struct w8001 *w8001) struct w8001_coord coord; int error; - error = w8001_command(w8001, W8001_CMD_QUERY, true); + error = w8001_command(w8001, W8001_CMD_STOP, false); if (error) return error; - parse_data(w8001->response, &coord); + msleep(250); /* wait 250ms before querying the device */ - input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); - input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); - input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); - input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); - input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); + /* penabled? */ + error = w8001_command(w8001, W8001_CMD_QUERY, true); + if (!error) { + __set_bit(BTN_TOOL_PEN, dev->keybit); + __set_bit(BTN_TOOL_RUBBER, dev->keybit); + __set_bit(BTN_STYLUS, dev->keybit); + __set_bit(BTN_STYLUS2, dev->keybit); + parse_data(w8001->response, &coord); + + input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); + input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); + if (coord.tilt_x && coord.tilt_y) { + input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); + input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); + } + } + /* Touch enabled? */ error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); - if (!error) { + + /* + * Some non-touch devices may reply to the touch query. But their + * second byte is empty, which indicates touch is not supported. + */ + if (!error && w8001->response[1]) { struct w8001_touch_query touch; parse_touchquery(w8001->response, &touch); + input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + switch (touch.sensor_id) { case 0: case 2: @@ -375,10 +399,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); __set_bit(BTN_TOUCH, 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); serio_set_drvdata(serio, w8001); err = serio_open(serio, drv); -- cgit v1.2.3 From 55ee4ef30241a62b700f79517e6d5ef2ddbefa67 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 16 Dec 2010 12:15:15 -0600 Subject: gameport: use this_cpu_read instead of lookup Signed-off-by: Christoph Lameter Acked-by: "H. Peter Anvin" Signed-off-by: Tejun Heo --- drivers/input/gameport/gameport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 46239e47a260..88d13f170432 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -123,7 +123,7 @@ static int gameport_measure_speed(struct gameport *gameport) } gameport_close(gameport); - return (cpu_data(raw_smp_processor_id()).loops_per_jiffy * + return (this_cpu_read(cpu_info.loops_per_jiffy) * (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx); #else -- cgit v1.2.3 From d5dc9ac3f6daf7df45c49e5a861c733a5f794c6b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Jan 2011 23:01:02 -0800 Subject: Input: ad7879 - convert I2C to dev_pm_ops There is a general move towards the use of dev_pm_ops rather than bus specific suspend APIs as this simplifies both the bus and PM core implementations. Convert the ad7879-ts I2C support over. Compile tested only. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879-i2c.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index d82a38ee9a3e..4e4e58cec6c8 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -10,14 +10,16 @@ #include #include #include +#include #include "ad7879.h" #define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */ #ifdef CONFIG_PM -static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message) +static int ad7879_i2c_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct ad7879 *ts = i2c_get_clientdata(client); ad7879_suspend(ts); @@ -25,17 +27,17 @@ static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message) return 0; } -static int ad7879_i2c_resume(struct i2c_client *client) +static int ad7879_i2c_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct ad7879 *ts = i2c_get_clientdata(client); ad7879_resume(ts); return 0; } -#else -# define ad7879_i2c_suspend NULL -# define ad7879_i2c_resume NULL + +static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume); #endif /* All registers are word-sized. @@ -117,11 +119,12 @@ static struct i2c_driver ad7879_i2c_driver = { .driver = { .name = "ad7879", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ad7879_i2c_pm, +#endif }, .probe = ad7879_i2c_probe, .remove = __devexit_p(ad7879_i2c_remove), - .suspend = ad7879_i2c_suspend, - .resume = ad7879_i2c_resume, .id_table = ad7879_id, }; -- cgit v1.2.3 From 85012fff38302e0262f838431e853f8c09e754d5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Jan 2011 23:01:02 -0800 Subject: Input: eeti_ts - convert to dev_pm_ops There is a general move towards the use of dev_pm_ops rather than bus specific suspend APIs as this simplifies both the bus and PM core implementations. Convert the eeti_ts driver over. Compile tested only by me, but Sven Neumann reports that the new code works. Signed-off-by: Mark Brown Tested-by: Sven Neumann Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/eeti_ts.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 7a3a916f84a8..7f8f538a9806 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -261,8 +261,9 @@ static int __devexit eeti_ts_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg) +static int eeti_ts_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct eeti_ts_priv *priv = i2c_get_clientdata(client); struct input_dev *input_dev = priv->input; @@ -279,8 +280,9 @@ static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int eeti_ts_resume(struct i2c_client *client) +static int eeti_ts_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct eeti_ts_priv *priv = i2c_get_clientdata(client); struct input_dev *input_dev = priv->input; @@ -296,9 +298,8 @@ static int eeti_ts_resume(struct i2c_client *client) return 0; } -#else -#define eeti_ts_suspend NULL -#define eeti_ts_resume NULL + +static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume); #endif static const struct i2c_device_id eeti_ts_id[] = { @@ -310,11 +311,12 @@ MODULE_DEVICE_TABLE(i2c, eeti_ts_id); static struct i2c_driver eeti_ts_driver = { .driver = { .name = "eeti_ts", +#ifdef CONFIG_PM + .pm = &eeti_ts_pm, +#endif }, .probe = eeti_ts_probe, .remove = __devexit_p(eeti_ts_remove), - .suspend = eeti_ts_suspend, - .resume = eeti_ts_resume, .id_table = eeti_ts_id, }; -- cgit v1.2.3 From 92b672e217ddf87e4181454f8200f0f06139cdbc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Jan 2011 23:01:03 -0800 Subject: Input: mcs5000_ts - convert to dev_pm_ops There is a general move towards the use of dev_pm_ops rather than bus specific suspend APIs as this simplifies both the bus and PM core implementations. Convert the mcs5000_ts driver over. Compile tested only. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mcs5000_ts.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 6ee9940aaf5b..2d84c80ceb66 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -261,25 +261,27 @@ static int __devexit mcs5000_ts_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg) +static int mcs5000_ts_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); + /* Touch sleep mode */ i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); return 0; } -static int mcs5000_ts_resume(struct i2c_client *client) +static int mcs5000_ts_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct mcs5000_ts_data *data = i2c_get_clientdata(client); mcs5000_ts_phys_init(data); return 0; } -#else -#define mcs5000_ts_suspend NULL -#define mcs5000_ts_resume NULL + +static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume); #endif static const struct i2c_device_id mcs5000_ts_id[] = { @@ -291,10 +293,11 @@ MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); static struct i2c_driver mcs5000_ts_driver = { .probe = mcs5000_ts_probe, .remove = __devexit_p(mcs5000_ts_remove), - .suspend = mcs5000_ts_suspend, - .resume = mcs5000_ts_resume, .driver = { .name = "mcs5000_ts", +#ifdef CONFIG_PM + .pm = &mcs5000_ts_pm, +#endif }, .id_table = mcs5000_ts_id, }; -- cgit v1.2.3 From d72e64ec0b134d26a89c736394ee0b638513bed8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Jan 2011 23:01:03 -0800 Subject: Input: migor_ts - convert to dev_pm_ops There is a general move towards the use of dev_pm_ops rather than bus specific suspend APIs as this simplifies both the bus and PM core implementations. Convert the migor_ts driver over. Compile tested only. Signed-off-by: Mark Brown Acked-by: Magnus Damm Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/migor_ts.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c index defe5dd3627c..5803bd0c1cca 100644 --- a/drivers/input/touchscreen/migor_ts.c +++ b/drivers/input/touchscreen/migor_ts.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -226,8 +227,9 @@ static int migor_ts_remove(struct i2c_client *client) return 0; } -static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg) +static int migor_ts_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct migor_ts_priv *priv = dev_get_drvdata(&client->dev); if (device_may_wakeup(&client->dev)) @@ -236,8 +238,9 @@ static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int migor_ts_resume(struct i2c_client *client) +static int migor_ts_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct migor_ts_priv *priv = dev_get_drvdata(&client->dev); if (device_may_wakeup(&client->dev)) @@ -246,6 +249,8 @@ static int migor_ts_resume(struct i2c_client *client) return 0; } +static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume); + static const struct i2c_device_id migor_ts_id[] = { { "migor_ts", 0 }, { } @@ -255,11 +260,10 @@ MODULE_DEVICE_TABLE(i2c, migor_ts); static struct i2c_driver migor_ts_driver = { .driver = { .name = "migor_ts", + .pm = &migor_ts_pm, }, .probe = migor_ts_probe, .remove = migor_ts_remove, - .suspend = migor_ts_suspend, - .resume = migor_ts_resume, .id_table = migor_ts_id, }; -- cgit v1.2.3 From 93f38e914183c9b7af248fab793153772996987c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Jan 2011 23:01:03 -0800 Subject: Input: cy8ctmg110_ts - Convert to dev_pm_ops There is a general move towards the use of dev_pm_ops rather than bus specific suspend APIs as this simplifies both the bus and PM core implementations. Convert the cy8ctmg110_ts driver over. Compile tested only. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cy8ctmg110_ts.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index d0c3a7229adf..a93c5c26ab3f 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -280,8 +280,9 @@ err_free_mem: } #ifdef CONFIG_PM -static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) +static int cy8ctmg110_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct cy8ctmg110 *ts = i2c_get_clientdata(client); if (device_may_wakeup(&client->dev)) @@ -293,8 +294,9 @@ static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) return 0; } -static int cy8ctmg110_resume(struct i2c_client *client) +static int cy8ctmg110_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct cy8ctmg110 *ts = i2c_get_clientdata(client); if (device_may_wakeup(&client->dev)) @@ -305,6 +307,8 @@ static int cy8ctmg110_resume(struct i2c_client *client) } return 0; } + +static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume); #endif static int __devexit cy8ctmg110_remove(struct i2c_client *client) @@ -335,14 +339,13 @@ static struct i2c_driver cy8ctmg110_driver = { .driver = { .owner = THIS_MODULE, .name = CY8CTMG110_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &cy8ctmg110_pm, +#endif }, .id_table = cy8ctmg110_idtable, .probe = cy8ctmg110_probe, .remove = __devexit_p(cy8ctmg110_remove), -#ifdef CONFIG_PM - .suspend = cy8ctmg110_suspend, - .resume = cy8ctmg110_resume, -#endif }; static int __init cy8ctmg110_init(void) -- cgit v1.2.3 From aa58abc20fa85328a9f048e2626c0893691ff284 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 7 Jan 2011 20:47:37 +0100 Subject: input/tc3589x: fix compile error There was a semi-colon missing and it broke the compile. Signed-off-by: Dan Carpenter Cc: Dmitry Torokhov Signed-off-by: Linus Walleij Signed-off-by: Linus Torvalds --- drivers/input/keyboard/tc3589x-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 69dc0cb20a00..dbbe761778d2 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -469,4 +469,4 @@ module_exit(tc3589x_keypad_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer"); MODULE_DESCRIPTION("TC35893 Keypad Driver"); -MODULE_ALIAS("platform:tc3589x-keypad") +MODULE_ALIAS("platform:tc3589x-keypad"); -- cgit v1.2.3 From f8313ef1f448006207f12c107123522c8bc00f15 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Sat, 8 Jan 2011 01:37:26 -0800 Subject: Input: i8042 - introduce 'notimeout' blacklist for Dell Vostro V13 i8042 controller present in Dell Vostro V13 errorneously signals spurious timeouts. Introduce i8042.notimeout parameter for ignoring i8042-signalled timeouts and apply this quirk automatically for Dell Vostro V13, based on DMI match. In addition to that, this machine also needs to be added to nomux blacklist. Signed-off-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 21 +++++++++++++++++++++ drivers/input/serio/i8042.c | 6 +++++- 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 5ae0fc4578fe..bb9f5d31f0d0 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -424,6 +424,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), }, }, + { + /* Dell Vostro V13 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + }, + }, { } }; @@ -545,6 +552,17 @@ static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = { }; #endif +static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = { + { + /* Dell Vostro V13 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"), + }, + }, + { } +}; + /* * Some Wistron based laptops need us to explicitly enable the 'Dritek * keyboard extension' to make their extra keys start generating scancodes. @@ -896,6 +914,9 @@ static int __init i8042_platform_init(void) if (dmi_check_system(i8042_dmi_nomux_table)) i8042_nomux = true; + if (dmi_check_system(i8042_dmi_notimeout_table)) + i8042_notimeout = true; + if (dmi_check_system(i8042_dmi_dritek_table)) i8042_dritek = true; #endif /* CONFIG_X86 */ diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index c04ff00a3663..ac4c93689ab9 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -63,6 +63,10 @@ static bool i8042_noloop; module_param_named(noloop, i8042_noloop, bool, 0); MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port"); +static bool i8042_notimeout; +module_param_named(notimeout, i8042_notimeout, bool, 0); +MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042"); + #ifdef CONFIG_X86 static bool i8042_dritek; module_param_named(dritek, i8042_dritek, bool, 0); @@ -504,7 +508,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) } else { dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | - ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); + ((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0); port_no = (str & I8042_STR_AUXDATA) ? I8042_AUX_PORT_NO : I8042_KBD_PORT_NO; -- cgit v1.2.3 From 048fc018c81d2acd86db2c3d1e226ea28f646729 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Sat, 8 Jan 2011 01:37:42 -0800 Subject: Input: remove aaed2000 keyboard driver The only platform using this driver (mach-aaec2000) is no longer in the kernel so remove the driver. Signed-off-by: Jamie Iles Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 12 --- drivers/input/keyboard/Makefile | 1 - drivers/input/keyboard/aaed2000_kbd.c | 186 ---------------------------------- 3 files changed, 199 deletions(-) delete mode 100644 drivers/input/keyboard/aaed2000_kbd.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a378e959368e..05ab6f41ee1e 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -12,18 +12,6 @@ menuconfig INPUT_KEYBOARD if INPUT_KEYBOARD -config KEYBOARD_AAED2000 - tristate "AAED-2000 keyboard" - depends on MACH_AAED2000 - select INPUT_POLLDEV - default y - help - Say Y here to enable the keyboard on the Agilent AAED-2000 - development board. - - To compile this driver as a module, choose M here: the - module will be called aaed2000_kbd. - config KEYBOARD_ADP5520 tristate "Keypad Support for ADP5520 PMIC" depends on PMIC_ADP5520 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 8449c730892c..ee14f85a716f 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -4,7 +4,6 @@ # Each configuration option enables a list of files. -obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c deleted file mode 100644 index 18222a689a03..000000000000 --- a/drivers/input/keyboard/aaed2000_kbd.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Keyboard driver for the AAED-2000 dev board - * - * Copyright (c) 2006 Nicolas Bellido Y Ortega - * - * Based on corgikbd.c - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define KB_ROWS 12 -#define KB_COLS 8 -#define KB_ROWMASK(r) (1 << (r)) -#define SCANCODE(r,c) (((c) * KB_ROWS) + (r)) -#define NR_SCANCODES (KB_COLS * KB_ROWS) - -#define SCAN_INTERVAL (50) /* ms */ -#define KB_ACTIVATE_DELAY (20) /* us */ - -static unsigned char aaedkbd_keycode[NR_SCANCODES] = { - KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0, - KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0, - KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0, - KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0, - KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK, - KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB, - KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE, - 0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL -}; - -struct aaedkbd { - unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)]; - struct input_polled_dev *poll_dev; - int kbdscan_state[KB_COLS]; - int kbdscan_count[KB_COLS]; -}; - -#define KBDSCAN_STABLE_COUNT 2 - -static void aaedkbd_report_col(struct aaedkbd *aaedkbd, - unsigned int col, unsigned int rowd) -{ - unsigned int scancode, pressed; - unsigned int row; - - for (row = 0; row < KB_ROWS; row++) { - scancode = SCANCODE(row, col); - pressed = rowd & KB_ROWMASK(row); - - input_report_key(aaedkbd->poll_dev->input, - aaedkbd->keycode[scancode], pressed); - } -} - -/* Scan the hardware keyboard and push any changes up through the input layer */ -static void aaedkbd_poll(struct input_polled_dev *dev) -{ - struct aaedkbd *aaedkbd = dev->private; - unsigned int col, rowd; - - col = 0; - do { - AAEC_GPIO_KSCAN = col + 8; - udelay(KB_ACTIVATE_DELAY); - rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN; - - if (rowd != aaedkbd->kbdscan_state[col]) { - aaedkbd->kbdscan_count[col] = 0; - aaedkbd->kbdscan_state[col] = rowd; - } else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) { - aaedkbd_report_col(aaedkbd, col, rowd); - col++; - } - } while (col < KB_COLS); - - AAEC_GPIO_KSCAN = 0x07; - input_sync(dev->input); -} - -static int __devinit aaedkbd_probe(struct platform_device *pdev) -{ - struct aaedkbd *aaedkbd; - struct input_polled_dev *poll_dev; - struct input_dev *input_dev; - int i; - int error; - - aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL); - poll_dev = input_allocate_polled_device(); - if (!aaedkbd || !poll_dev) { - error = -ENOMEM; - goto fail; - } - - platform_set_drvdata(pdev, aaedkbd); - - aaedkbd->poll_dev = poll_dev; - memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode)); - - poll_dev->private = aaedkbd; - poll_dev->poll = aaedkbd_poll; - poll_dev->poll_interval = SCAN_INTERVAL; - - input_dev = poll_dev->input; - input_dev->name = "AAED-2000 Keyboard"; - input_dev->phys = "aaedkbd/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0x0001; - input_dev->id.product = 0x0001; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &pdev->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_dev->keycode = aaedkbd->keycode; - input_dev->keycodesize = sizeof(unsigned char); - input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode); - - for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++) - set_bit(aaedkbd->keycode[i], input_dev->keybit); - clear_bit(0, input_dev->keybit); - - error = input_register_polled_device(aaedkbd->poll_dev); - if (error) - goto fail; - - return 0; - - fail: kfree(aaedkbd); - input_free_polled_device(poll_dev); - return error; -} - -static int __devexit aaedkbd_remove(struct platform_device *pdev) -{ - struct aaedkbd *aaedkbd = platform_get_drvdata(pdev); - - input_unregister_polled_device(aaedkbd->poll_dev); - input_free_polled_device(aaedkbd->poll_dev); - kfree(aaedkbd); - - return 0; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:aaed2000-keyboard"); - -static struct platform_driver aaedkbd_driver = { - .probe = aaedkbd_probe, - .remove = __devexit_p(aaedkbd_remove), - .driver = { - .name = "aaed2000-keyboard", - .owner = THIS_MODULE, - }, -}; - -static int __init aaedkbd_init(void) -{ - return platform_driver_register(&aaedkbd_driver); -} - -static void __exit aaedkbd_exit(void) -{ - platform_driver_unregister(&aaedkbd_driver); -} - -module_init(aaedkbd_init); -module_exit(aaedkbd_exit); - -MODULE_AUTHOR("Nicolas Bellido Y Ortega"); -MODULE_DESCRIPTION("AAED-2000 Keyboard Driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 9d084a3d5dffd076a9a006164ea0dbd9c495f2b0 Mon Sep 17 00:00:00 2001 From: Fabien Marteau Date: Mon, 10 Jan 2011 11:01:13 -0800 Subject: Input: add Austria Microsystem AS5011 joystick driver This is driver for EasyPoint AS5011 2 axis joystick chip. This chip is plugged on an I2C bus. Tested on ARM processor (i.MX27). Signed-off-by: Fabien Marteau Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/Kconfig | 10 ++ drivers/input/joystick/Makefile | 1 + drivers/input/joystick/as5011.c | 367 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100644 drivers/input/joystick/as5011.c (limited to 'drivers/input') diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 5b596165b571..56eb471b5576 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -255,6 +255,16 @@ config JOYSTICK_AMIGA To compile this driver as a module, choose M here: the module will be called amijoy. +config JOYSTICK_AS5011 + tristate "Austria Microsystem AS5011 joystick" + depends on I2C + help + Say Y here if you have an AS5011 digital joystick connected to your + system. + + To compile this driver as a module, choose M here: the + module will be called as5011. + config JOYSTICK_JOYDUMP tristate "Gameport data dumper" select GAMEPORT diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index f3a8cbe2abb6..92dc0de9dfed 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_JOYSTICK_A3D) += a3d.o obj-$(CONFIG_JOYSTICK_ADI) += adi.o obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o +obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o obj-$(CONFIG_JOYSTICK_DB9) += db9.o diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c new file mode 100644 index 000000000000..f6732b57ca07 --- /dev/null +++ b/drivers/input/joystick/as5011.c @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2010, 2011 Fabien Marteau + * Sponsored by ARMadeus Systems + * + * 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Driver for Austria Microsystems joysticks AS5011 + * + * TODO: + * - Power on the chip when open() and power down when close() + * - Manage power mode + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick" +#define MODULE_DEVICE_ALIAS "as5011" + +MODULE_AUTHOR("Fabien Marteau "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* registers */ +#define AS5011_CTRL1 0x76 +#define AS5011_CTRL2 0x75 +#define AS5011_XP 0x43 +#define AS5011_XN 0x44 +#define AS5011_YP 0x53 +#define AS5011_YN 0x54 +#define AS5011_X_REG 0x41 +#define AS5011_Y_REG 0x42 +#define AS5011_X_RES_INT 0x51 +#define AS5011_Y_RES_INT 0x52 + +/* CTRL1 bits */ +#define AS5011_CTRL1_LP_PULSED 0x80 +#define AS5011_CTRL1_LP_ACTIVE 0x40 +#define AS5011_CTRL1_LP_CONTINUE 0x20 +#define AS5011_CTRL1_INT_WUP_EN 0x10 +#define AS5011_CTRL1_INT_ACT_EN 0x08 +#define AS5011_CTRL1_EXT_CLK_EN 0x04 +#define AS5011_CTRL1_SOFT_RST 0x02 +#define AS5011_CTRL1_DATA_VALID 0x01 + +/* CTRL2 bits */ +#define AS5011_CTRL2_EXT_SAMPLE_EN 0x08 +#define AS5011_CTRL2_RC_BIAS_ON 0x04 +#define AS5011_CTRL2_INV_SPINNING 0x02 + +#define AS5011_MAX_AXIS 80 +#define AS5011_MIN_AXIS (-80) +#define AS5011_FUZZ 8 +#define AS5011_FLAT 40 + +struct as5011_device { + struct input_dev *input_dev; + struct i2c_client *i2c_client; + unsigned int button_gpio; + unsigned int button_irq; + unsigned int axis_irq; +}; + +static int as5011_i2c_write(struct i2c_client *client, + uint8_t aregaddr, + uint8_t avalue) +{ + uint8_t data[2] = { aregaddr, avalue }; + struct i2c_msg msg = { + client->addr, I2C_M_IGNORE_NAK, 2, (uint8_t *)data + }; + int error; + + error = i2c_transfer(client->adapter, &msg, 1); + return error < 0 ? error : 0; +} + +static int as5011_i2c_read(struct i2c_client *client, + uint8_t aregaddr, signed char *value) +{ + uint8_t data[2] = { aregaddr }; + struct i2c_msg msg_set[2] = { + { client->addr, I2C_M_REV_DIR_ADDR, 1, (uint8_t *)data }, + { client->addr, I2C_M_RD | I2C_M_NOSTART, 1, (uint8_t *)data } + }; + int error; + + error = i2c_transfer(client->adapter, msg_set, 2); + if (error < 0) + return error; + + *value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0]; + return 0; +} + +static irqreturn_t as5011_button_interrupt(int irq, void *dev_id) +{ + struct as5011_device *as5011 = dev_id; + int val = gpio_get_value_cansleep(as5011->button_gpio); + + input_report_key(as5011->input_dev, BTN_JOYSTICK, !val); + input_sync(as5011->input_dev); + + return IRQ_HANDLED; +} + +static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id) +{ + struct as5011_device *as5011 = dev_id; + int error; + signed char x, y; + + error = as5011_i2c_read(as5011->i2c_client, AS5011_X_RES_INT, &x); + if (error < 0) + goto out; + + error = as5011_i2c_read(as5011->i2c_client, AS5011_Y_RES_INT, &y); + if (error < 0) + goto out; + + input_report_abs(as5011->input_dev, ABS_X, x); + input_report_abs(as5011->input_dev, ABS_Y, y); + input_sync(as5011->input_dev); + +out: + return IRQ_HANDLED; +} + +static int __devinit as5011_configure_chip(struct as5011_device *as5011, + const struct as5011_platform_data *plat_dat) +{ + struct i2c_client *client = as5011->i2c_client; + int error; + signed char value; + + /* chip soft reset */ + error = as5011_i2c_write(client, AS5011_CTRL1, + AS5011_CTRL1_SOFT_RST); + if (error < 0) { + dev_err(&client->dev, "Soft reset failed\n"); + return error; + } + + mdelay(10); + + error = as5011_i2c_write(client, AS5011_CTRL1, + AS5011_CTRL1_LP_PULSED | + AS5011_CTRL1_LP_ACTIVE | + AS5011_CTRL1_INT_ACT_EN); + if (error < 0) { + dev_err(&client->dev, "Power config failed\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_CTRL2, + AS5011_CTRL2_INV_SPINNING); + if (error < 0) { + dev_err(&client->dev, "Can't invert spinning\n"); + return error; + } + + /* write threshold */ + error = as5011_i2c_write(client, AS5011_XP, plat_dat->xp); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_XN, plat_dat->xn); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_YP, plat_dat->yp); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + error = as5011_i2c_write(client, AS5011_YN, plat_dat->yn); + if (error < 0) { + dev_err(&client->dev, "Can't write threshold\n"); + return error; + } + + /* to free irq gpio in chip */ + error = as5011_i2c_read(client, AS5011_X_RES_INT, &value); + if (error < 0) { + dev_err(&client->dev, "Can't read i2c X resolution value\n"); + return error; + } + + return 0; +} + +static int __devinit as5011_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct as5011_platform_data *plat_data; + struct as5011_device *as5011; + struct input_dev *input_dev; + int irq; + int error; + + plat_data = client->dev.platform_data; + if (!plat_data) + return -EINVAL; + + if (!plat_data->axis_irq) { + dev_err(&client->dev, "No axis IRQ?\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_PROTOCOL_MANGLING)) { + dev_err(&client->dev, + "need i2c bus that supports protocol mangling\n"); + return -ENODEV; + } + + as5011 = kmalloc(sizeof(struct as5011_device), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!as5011 || !input_dev) { + dev_err(&client->dev, + "Can't allocate memory for device structure\n"); + error = -ENOMEM; + goto err_free_mem; + } + + as5011->i2c_client = client; + as5011->input_dev = input_dev; + as5011->button_gpio = plat_data->button_gpio; + as5011->axis_irq = plat_data->axis_irq; + + input_dev->name = "Austria Microsystem as5011 joystick"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_JOYSTICK, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, + AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); + input_set_abs_params(as5011->input_dev, ABS_Y, + AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT); + + error = gpio_request(as5011->button_gpio, "AS5011 button"); + if (error < 0) { + dev_err(&client->dev, "Failed to request button gpio\n"); + goto err_free_mem; + } + + irq = gpio_to_irq(as5011->button_gpio); + if (irq < 0) { + dev_err(&client->dev, + "Failed to get irq number for button gpio\n"); + goto err_free_button_gpio; + } + + as5011->button_irq = irq; + + error = request_threaded_irq(as5011->button_irq, + NULL, as5011_button_interrupt, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "as5011_button", as5011); + if (error < 0) { + dev_err(&client->dev, + "Can't allocate button irq %d\n", as5011->button_irq); + goto err_free_button_gpio; + } + + error = as5011_configure_chip(as5011, plat_data); + if (error) + goto err_free_button_irq; + + error = request_threaded_irq(as5011->axis_irq, NULL, + as5011_axis_interrupt, + plat_data->axis_irqflags, + "as5011_joystick", as5011); + if (error) { + dev_err(&client->dev, + "Can't allocate axis irq %d\n", plat_data->axis_irq); + goto err_free_button_irq; + } + + error = input_register_device(as5011->input_dev); + if (error) { + dev_err(&client->dev, "Failed to register input device\n"); + goto err_free_axis_irq; + } + + i2c_set_clientdata(client, as5011); + + return 0; + +err_free_axis_irq: + free_irq(as5011->axis_irq, as5011); +err_free_button_irq: + free_irq(as5011->button_irq, as5011); +err_free_button_gpio: + gpio_free(as5011->button_gpio); +err_free_mem: + input_free_device(input_dev); + kfree(as5011); + + return error; +} + +static int __devexit as5011_remove(struct i2c_client *client) +{ + struct as5011_device *as5011 = i2c_get_clientdata(client); + + free_irq(as5011->axis_irq, as5011); + free_irq(as5011->button_irq, as5011); + gpio_free(as5011->button_gpio); + + input_unregister_device(as5011->input_dev); + kfree(as5011); + + return 0; +} + +static const struct i2c_device_id as5011_id[] = { + { MODULE_DEVICE_ALIAS, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, as5011_id); + +static struct i2c_driver as5011_driver = { + .driver = { + .name = "as5011", + }, + .probe = as5011_probe, + .remove = __devexit_p(as5011_remove), + .id_table = as5011_id, +}; + +static int __init as5011_init(void) +{ + return i2c_add_driver(&as5011_driver); +} +module_init(as5011_init); + +static void __exit as5011_exit(void) +{ + i2c_del_driver(&as5011_driver); +} +module_exit(as5011_exit); -- cgit v1.2.3 From 5fca6cac9feed75efc0b2c454305a5f538d887f5 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 11 Jan 2011 01:06:58 -0800 Subject: Input: wacom_w8001 - add single-touch support Emulate single-touch compatible events for the 2-finger panels so that they can be used with single-touch legacy clients. Assign device ids as Wacom USB vendor ID and product ID. Name the device to reflect its specific features. Scale touch coordinates to pen maximum if pen supported. Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 182 ++++++++++++++++++++++++++------ 1 file changed, 149 insertions(+), 33 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 8ed53aded2d3..5cb8449c909d 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -3,6 +3,7 @@ * * Copyright (c) 2008 Jaya Kumar * Copyright (c) 2010 Red Hat, Inc. + * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for @@ -64,11 +65,11 @@ struct w8001_coord { /* touch query reply packet */ struct w8001_touch_query { + u16 x; + u16 y; u8 panel_res; u8 capacity_res; u8 sensor_id; - u16 x; - u16 y; }; /* @@ -87,9 +88,14 @@ struct w8001 { char phys[32]; int type; unsigned int pktlen; + u16 max_touch_x; + u16 max_touch_y; + u16 max_pen_x; + u16 max_pen_y; + char name[64]; }; -static void parse_data(u8 *data, struct w8001_coord *coord) +static void parse_pen_data(u8 *data, struct w8001_coord *coord) { memset(coord, 0, sizeof(*coord)); @@ -113,11 +119,30 @@ static void parse_data(u8 *data, struct w8001_coord *coord) coord->tilt_y = data[8] & 0x7F; } -static void parse_touch(struct w8001 *w8001) +static void parse_single_touch(u8 *data, struct w8001_coord *coord) +{ + coord->x = (data[1] << 7) | data[2]; + coord->y = (data[3] << 7) | data[4]; + coord->tsw = data[0] & 0x01; +} + +static void scale_touch_coordinates(struct w8001 *w8001, + unsigned int *x, unsigned int *y) +{ + if (w8001->max_pen_x && w8001->max_touch_x) + *x = *x * w8001->max_pen_x / w8001->max_touch_x; + + if (w8001->max_pen_y && w8001->max_touch_y) + *y = *y * w8001->max_pen_y / w8001->max_touch_y; +} + +static void parse_multi_touch(struct w8001 *w8001) { struct input_dev *dev = w8001->dev; unsigned char *data = w8001->data; + unsigned int x, y; int i; + int count = 0; for (i = 0; i < 2; i++) { bool touch = data[0] & (1 << i); @@ -125,15 +150,29 @@ static void parse_touch(struct w8001 *w8001) input_mt_slot(dev, i); input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); if (touch) { - int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); - int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); + x = (data[6 * i + 1] << 7) | data[6 * i + 2]; + y = (data[6 * i + 3] << 7) | data[6 * i + 4]; /* data[5,6] and [11,12] is finger capacity */ + /* scale to pen maximum */ + scale_touch_coordinates(w8001, &x, &y); + input_report_abs(dev, ABS_MT_POSITION_X, x); input_report_abs(dev, ABS_MT_POSITION_Y, y); + count++; } } + /* emulate single touch events when stylus is out of proximity. + * This is to make single touch backward support consistent + * across all Wacom single touch devices. + */ + if (w8001->type != BTN_TOOL_PEN && + w8001->type != BTN_TOOL_RUBBER) { + w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED; + input_mt_report_pointer_emulation(dev, true); + } + input_sync(dev); } @@ -152,6 +191,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query) query->y = data[5] << 9; query->y |= data[6] << 2; query->y |= (data[2] >> 3) & 0x3; + + /* Early days' single-finger touch models need the following defaults */ + if (!query->x && !query->y) { + query->x = 1024; + query->y = 1024; + if (query->panel_res) + query->x = query->y = (1 << query->panel_res); + query->panel_res = 10; + } } static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) @@ -161,16 +209,15 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) /* * We have 1 bit for proximity (rdy) and 3 bits for tip, side, * side2/eraser. If rdy && f2 are set, this can be either pen + side2, - * or eraser. assume + * or eraser. Assume: * - if dev is already in proximity and f2 is toggled → pen + side2 * - if dev comes into proximity with f2 set → eraser * If f2 disappears after assuming eraser, fake proximity out for * eraser and in for pen. */ - if (!w8001->type) { - w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; - } else if (w8001->type == BTN_TOOL_RUBBER) { + switch (w8001->type) { + case BTN_TOOL_RUBBER: if (!coord->f2) { input_report_abs(dev, ABS_PRESSURE, 0); input_report_key(dev, BTN_TOUCH, 0); @@ -180,8 +227,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) input_sync(dev); w8001->type = BTN_TOOL_PEN; } - } else { + break; + + case BTN_TOOL_FINGER: + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_TOOL_FINGER, 0); + input_sync(dev); + /* fall through */ + + case KEY_RESERVED: + w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + break; + + default: input_report_key(dev, BTN_STYLUS2, coord->f2); + break; } input_report_abs(dev, ABS_X, coord->x); @@ -193,7 +253,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) input_sync(dev); if (!coord->rdy) - w8001->type = 0; + w8001->type = KEY_RESERVED; +} + +static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) +{ + struct input_dev *dev = w8001->dev; + unsigned int x = coord->x; + unsigned int y = coord->y; + + /* scale to pen maximum */ + scale_touch_coordinates(w8001, &x, &y); + + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + input_report_key(dev, BTN_TOUCH, coord->tsw); + input_report_key(dev, BTN_TOOL_FINGER, coord->tsw); + + input_sync(dev); + + w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED; } static irqreturn_t w8001_interrupt(struct serio *serio, @@ -214,9 +293,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio, case W8001_PKTLEN_TOUCH93 - 1: case W8001_PKTLEN_TOUCH9A - 1: - /* ignore one-finger touch packet. */ - if (w8001->pktlen == w8001->idx) + tmp = w8001->data[0] & W8001_TOUCH_BYTE; + if (tmp != W8001_TOUCH_BYTE) + break; + + if (w8001->pktlen == w8001->idx) { w8001->idx = 0; + if (w8001->type != BTN_TOOL_PEN && + w8001->type != BTN_TOOL_RUBBER) { + parse_single_touch(w8001->data, &coord); + report_single_touch(w8001, &coord); + } + } break; /* Pen coordinates packet */ @@ -225,18 +313,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio, if (unlikely(tmp == W8001_TAB_BYTE)) break; - tmp = (w8001->data[0] & W8001_TOUCH_BYTE); + tmp = w8001->data[0] & W8001_TOUCH_BYTE; if (tmp == W8001_TOUCH_BYTE) break; w8001->idx = 0; - parse_data(w8001->data, &coord); + parse_pen_data(w8001->data, &coord); report_pen_events(w8001, &coord); break; /* control packet */ case W8001_PKTLEN_TPCCTL - 1: - tmp = (w8001->data[0] & W8001_TOUCH_MASK); + tmp = w8001->data[0] & W8001_TOUCH_MASK; if (tmp == W8001_TOUCH_BYTE) break; @@ -249,7 +337,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio, /* 2 finger touch packet */ case W8001_PKTLEN_TOUCH2FG - 1: w8001->idx = 0; - parse_touch(w8001); + parse_multi_touch(w8001); break; } @@ -279,6 +367,7 @@ static int w8001_setup(struct w8001 *w8001) { struct input_dev *dev = w8001->dev; struct w8001_coord coord; + struct w8001_touch_query touch; int error; error = w8001_command(w8001, W8001_CMD_STOP, false); @@ -287,14 +376,21 @@ static int w8001_setup(struct w8001 *w8001) msleep(250); /* wait 250ms before querying the device */ + dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); + /* penabled? */ error = w8001_command(w8001, W8001_CMD_QUERY, true); if (!error) { + __set_bit(BTN_TOUCH, dev->keybit); __set_bit(BTN_TOOL_PEN, dev->keybit); __set_bit(BTN_TOOL_RUBBER, dev->keybit); __set_bit(BTN_STYLUS, dev->keybit); __set_bit(BTN_STYLUS2, dev->keybit); - parse_data(w8001->response, &coord); + + parse_pen_data(w8001->response, &coord); + w8001->max_pen_x = coord.x; + w8001->max_pen_y = coord.y; input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); @@ -303,6 +399,8 @@ static int w8001_setup(struct w8001 *w8001) input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); } + w8001->id = 0x90; + strlcat(w8001->name, " Penabled", sizeof(w8001->name)); } /* Touch enabled? */ @@ -313,24 +411,38 @@ static int w8001_setup(struct w8001 *w8001) * second byte is empty, which indicates touch is not supported. */ if (!error && w8001->response[1]) { - struct w8001_touch_query touch; + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); parse_touchquery(w8001->response, &touch); + w8001->max_touch_x = touch.x; + w8001->max_touch_y = touch.y; + + /* scale to pen maximum */ + if (w8001->max_pen_x && w8001->max_pen_y) { + touch.x = w8001->max_pen_x; + touch.y = w8001->max_pen_y; + } input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); - __set_bit(BTN_TOOL_FINGER, dev->keybit); switch (touch.sensor_id) { case 0: case 2: w8001->pktlen = W8001_PKTLEN_TOUCH93; + w8001->id = 0x93; + strlcat(w8001->name, " 1FG", sizeof(w8001->name)); break; + case 1: case 3: case 4: w8001->pktlen = W8001_PKTLEN_TOUCH9A; + strlcat(w8001->name, " 1FG", sizeof(w8001->name)); + w8001->id = 0x9a; break; + case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; @@ -341,10 +453,18 @@ static int w8001_setup(struct w8001 *w8001) 0, touch.y, 0, 0); input_set_abs_params(dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); + + strlcat(w8001->name, " 2FG", sizeof(w8001->name)); + if (w8001->max_pen_x && w8001->max_pen_y) + w8001->id = 0xE3; + else + w8001->id = 0xE2; break; } } + strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); + return w8001_command(w8001, W8001_CMD_START, false); } @@ -384,22 +504,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) } w8001->serio = serio; - w8001->id = serio->id.id; w8001->dev = input_dev; init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); - input_dev->name = "Wacom W8001 Penabled Serial TouchScreen"; - input_dev->phys = w8001->phys; - input_dev->id.bustype = BUS_RS232; - input_dev->id.vendor = SERIO_W8001; - input_dev->id.product = w8001->id; - input_dev->id.version = 0x0100; - input_dev->dev.parent = &serio->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - __set_bit(BTN_TOUCH, input_dev->keybit); - serio_set_drvdata(serio, w8001); err = serio_open(serio, drv); if (err) @@ -409,6 +517,14 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) if (err) goto fail3; + input_dev->name = w8001->name; + input_dev->phys = w8001->phys; + input_dev->id.product = w8001->id; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = 0x056a; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + err = input_register_device(w8001->dev); if (err) goto fail3; -- cgit v1.2.3 From a0640925880a5801ae0aac232fae6900a2c44b27 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 17 Jan 2011 20:40:32 -0800 Subject: Input: tnetv107x-keypad - don't treat NULL clk as an error We should use IS_ERR() when checking whether clk_get() succeeded or not since it returns errors by encoding error codes with ERR_PTR(). Signed-off-by: Jamie Iles Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tnetv107x-keypad.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index b4a81ebfab92..c8f097a15d89 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -219,9 +220,9 @@ static int __devinit keypad_probe(struct platform_device *pdev) } kp->clk = clk_get(dev, NULL); - if (!kp->clk) { + if (IS_ERR(kp->clk)) { dev_err(dev, "cannot claim device clock\n"); - error = -EINVAL; + error = PTR_ERR(kp->clk); goto error_clk; } -- cgit v1.2.3 From ba555461833aa1b5083004492ba97c92d5fccf46 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 17 Jan 2011 20:40:55 -0800 Subject: Input: tnetv107x-ts - don't treat NULL clk as an error We should use IS_ERR() when checking whether clk_get() succeeded or not since it returns errors by encoding error codes with ERR_PTR(). Signed-off-by: Jamie Iles Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tnetv107x-ts.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c index cf1dba2e267c..22a3411e93c5 100644 --- a/drivers/input/touchscreen/tnetv107x-ts.c +++ b/drivers/input/touchscreen/tnetv107x-ts.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -289,9 +290,9 @@ static int __devinit tsc_probe(struct platform_device *pdev) } ts->clk = clk_get(dev, NULL); - if (!ts->clk) { + if (IS_ERR(ts->clk)) { dev_err(dev, "cannot claim device clock\n"); - error = -EINVAL; + error = PTR_ERR(ts->clk); goto error_clk; } -- cgit v1.2.3 From d2763b4f44e16f44cc4156c9591e74df9dcd88be Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Mon, 17 Jan 2011 20:40:58 -0800 Subject: Input: bu21013_ts - remove duplicate resolution parameters Remove duplicate display resolution parameters from platform data as one pair is quite enough. Signed-off-by: Naveen Kumar Gaddipati Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/bu21013_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index f7fa9ef4cd65..3c7e60b2f778 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -485,9 +485,9 @@ static int __devinit bu21013_probe(struct i2c_client *client, __set_bit(EV_ABS, in_dev->evbit); input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, - pdata->x_max_res, 0, 0); + pdata->touch_x_max, 0, 0); input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, - pdata->y_max_res, 0, 0); + pdata->touch_y_max, 0, 0); input_set_drvdata(in_dev, bu21013_data); error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, -- cgit v1.2.3 From 81e78deafb21ba867eb244ab1117726c68d817f8 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Mon, 17 Jan 2011 20:47:31 -0800 Subject: Input: bu21013_ts - added regulator support Add regulator support in ROHM BU21013 touch panel driver. Signed-off-by: Naveen Kumar Gaddipati Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/bu21013_ts.c | 35 +++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 3c7e60b2f778..1507ce108d5b 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -12,6 +12,7 @@ #include #include #include +#include #define PEN_DOWN_INTR 0 #define MAX_FINGERS 2 @@ -139,6 +140,7 @@ * @chip: pointer to the touch panel controller * @in_dev: pointer to the input device structure * @intr_pin: interrupt pin value + * @regulator: pointer to the Regulator used for touch screen * * Touch panel device data structure */ @@ -149,6 +151,7 @@ struct bu21013_ts_data { const struct bu21013_platform_device *chip; struct input_dev *in_dev; unsigned int intr_pin; + struct regulator *regulator; }; /** @@ -456,6 +459,20 @@ static int __devinit bu21013_probe(struct i2c_client *client, bu21013_data->in_dev = in_dev; bu21013_data->chip = pdata; bu21013_data->client = client; + + bu21013_data->regulator = regulator_get(&client->dev, "V-TOUCH"); + if (IS_ERR(bu21013_data->regulator)) { + dev_err(&client->dev, "regulator_get failed\n"); + error = PTR_ERR(bu21013_data->regulator); + goto err_free_mem; + } + + error = regulator_enable(bu21013_data->regulator); + if (error < 0) { + dev_err(&client->dev, "regulator enable failed\n"); + goto err_put_regulator; + } + bu21013_data->touch_stopped = false; init_waitqueue_head(&bu21013_data->wait); @@ -464,7 +481,7 @@ static int __devinit bu21013_probe(struct i2c_client *client, error = pdata->cs_en(pdata->cs_pin); if (error < 0) { dev_err(&client->dev, "chip init failed\n"); - goto err_free_mem; + goto err_disable_regulator; } } @@ -513,6 +530,10 @@ err_free_irq: bu21013_free_irq(bu21013_data); err_cs_disable: pdata->cs_dis(pdata->cs_pin); +err_disable_regulator: + regulator_disable(bu21013_data->regulator); +err_put_regulator: + regulator_put(bu21013_data->regulator); err_free_mem: input_free_device(in_dev); kfree(bu21013_data); @@ -535,6 +556,10 @@ static int __devexit bu21013_remove(struct i2c_client *client) bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); input_unregister_device(bu21013_data->in_dev); + + regulator_disable(bu21013_data->regulator); + regulator_put(bu21013_data->regulator); + kfree(bu21013_data); device_init_wakeup(&client->dev, false); @@ -561,6 +586,8 @@ static int bu21013_suspend(struct device *dev) else disable_irq(bu21013_data->chip->irq); + regulator_disable(bu21013_data->regulator); + return 0; } @@ -577,6 +604,12 @@ static int bu21013_resume(struct device *dev) struct i2c_client *client = bu21013_data->client; int retval; + retval = regulator_enable(bu21013_data->regulator); + if (retval < 0) { + dev_err(&client->dev, "bu21013 regulator enable failed\n"); + return retval; + } + retval = bu21013_init_chip(bu21013_data); if (retval < 0) { dev_err(&client->dev, "bu21013 controller config failed\n"); -- cgit v1.2.3 From b0f05aadf1516c166ba301b7a535bc9429ce1961 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 17 Jan 2011 20:48:18 -0800 Subject: Input: ct82710c - return proper error code for ct82c710_open If request_irq() fails we should return the proper error instead of -1. Signed-off-by: Davidlohr Bueso Signed-off-by: Dmitry Torokhov --- drivers/input/serio/ct82c710.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 448c7724beb9..852816567241 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -111,9 +111,11 @@ static void ct82c710_close(struct serio *serio) static int ct82c710_open(struct serio *serio) { unsigned char status; + int err; - if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL)) - return -1; + err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL); + if (err) + return err; status = inb_p(CT82C710_STATUS); @@ -131,7 +133,7 @@ static int ct82c710_open(struct serio *serio) status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON); outb_p(status, CT82C710_STATUS); free_irq(CT82C710_IRQ, NULL); - return -1; + return -EBUSY; } return 0; -- cgit v1.2.3 From 6a108a14fa356ef607be308b68337939e56ea94e Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Thu, 20 Jan 2011 14:44:16 -0800 Subject: kconfig: rename CONFIG_EMBEDDED to CONFIG_EXPERT The meaning of CONFIG_EMBEDDED has long since been obsoleted; the option is used to configure any non-standard kernel with a much larger scope than only small devices. This patch renames the option to CONFIG_EXPERT in init/Kconfig and fixes references to the option throughout the kernel. A new CONFIG_EMBEDDED option is added that automatically selects CONFIG_EXPERT when enabled and can be used in the future to isolate options that should only be considered for embedded systems (RISC architectures, SLOB, etc). Calling the option "EXPERT" more accurately represents its intention: only expert users who understand the impact of the configuration changes they are making should enable it. Reviewed-by: Ingo Molnar Acked-by: David Woodhouse Signed-off-by: David Rientjes Cc: Greg KH Cc: "David S. Miller" Cc: Jens Axboe Cc: Arnd Bergmann Cc: Robin Holt Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/input/Kconfig | 6 +++--- drivers/input/keyboard/Kconfig | 4 ++-- drivers/input/mouse/Kconfig | 10 +++++----- drivers/input/serio/Kconfig | 6 +++--- drivers/input/touchscreen/Kconfig | 30 +++++++++++++++--------------- 5 files changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 07c2cd43109c..1903c0f5b925 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -6,7 +6,7 @@ menu "Input device support" depends on !S390 config INPUT - tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED + tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT default y help Say Y here if you have any input device (mouse, keyboard, tablet, @@ -67,7 +67,7 @@ config INPUT_SPARSEKMAP comment "Userland interfaces" config INPUT_MOUSEDEV - tristate "Mouse interface" if EMBEDDED + tristate "Mouse interface" if EXPERT default y help Say Y here if you want your mouse to be accessible as char devices @@ -150,7 +150,7 @@ config INPUT_EVBUG module will be called evbug. config INPUT_APMPOWER - tristate "Input Power Event -> APM Bridge" if EMBEDDED + tristate "Input Power Event -> APM Bridge" if EXPERT depends on INPUT && APM_EMULATION help Say Y here if you want suspend key events to trigger a user diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 7b3c0b8fa432..417507348bab 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # menuconfig INPUT_KEYBOARD - bool "Keyboards" if EMBEDDED || !X86 + bool "Keyboards" if EXPERT || !X86 default y help Say Y here, and a list of supported keyboards will be displayed. @@ -57,7 +57,7 @@ config KEYBOARD_ATARI module will be called atakbd. config KEYBOARD_ATKBD - tristate "AT keyboard" if EMBEDDED || !X86 + tristate "AT keyboard" if EXPERT || !X86 default y select SERIO select SERIO_LIBPS2 diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index bf5fd7f6a313..9c1e6ee83531 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -39,7 +39,7 @@ config MOUSE_PS2 module will be called psmouse. config MOUSE_PS2_ALPS - bool "ALPS PS/2 mouse protocol extension" if EMBEDDED + bool "ALPS PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -49,7 +49,7 @@ config MOUSE_PS2_ALPS If unsure, say Y. config MOUSE_PS2_LOGIPS2PP - bool "Logitech PS/2++ mouse protocol extension" if EMBEDDED + bool "Logitech PS/2++ mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -59,7 +59,7 @@ config MOUSE_PS2_LOGIPS2PP If unsure, say Y. config MOUSE_PS2_SYNAPTICS - bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED + bool "Synaptics PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -69,7 +69,7 @@ config MOUSE_PS2_SYNAPTICS If unsure, say Y. config MOUSE_PS2_LIFEBOOK - bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED + bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 && X86 && DMI help @@ -79,7 +79,7 @@ config MOUSE_PS2_LIFEBOOK If unsure, say Y. config MOUSE_PS2_TRACKPOINT - bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED + bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 307eef77a172..55f2c2293ec6 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # config SERIO - tristate "Serial I/O support" if EMBEDDED || !X86 + tristate "Serial I/O support" if EXPERT || !X86 default y help Say Yes here if you have any input device that uses serial I/O to @@ -19,7 +19,7 @@ config SERIO if SERIO config SERIO_I8042 - tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 + tristate "i8042 PC Keyboard controller" if EXPERT || !X86 default y depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \ (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN @@ -168,7 +168,7 @@ config SERIO_MACEPS2 module will be called maceps2. config SERIO_LIBPS2 - tristate "PS/2 driver library" if EMBEDDED + tristate "PS/2 driver library" if EXPERT depends on SERIO_I8042 || SERIO_I8042=n help Say Y here if you are using a driver for device connected diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 0c9f4b158ff0..61834ae282e1 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -540,62 +540,62 @@ config TOUCHSCREEN_MC13783 config TOUCHSCREEN_USB_EGALAX default y - bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED + bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_PANJIT default y - bool "PanJit device support" if EMBEDDED + bool "PanJit device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_3M default y - bool "3M/Microtouch EX II series device support" if EMBEDDED + bool "3M/Microtouch EX II series device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ITM default y - bool "ITM device support" if EMBEDDED + bool "ITM device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ETURBO default y - bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED + bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GUNZE default y - bool "Gunze AHL61 device support" if EMBEDDED + bool "Gunze AHL61 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_DMC_TSC10 default y - bool "DMC TSC-10/25 device support" if EMBEDDED + bool "DMC TSC-10/25 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_IRTOUCH default y - bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED + bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_IDEALTEK default y - bool "IdealTEK URTC1000 device support" if EMBEDDED + bool "IdealTEK URTC1000 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GENERAL_TOUCH default y - bool "GeneralTouch Touchscreen device support" if EMBEDDED + bool "GeneralTouch Touchscreen device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GOTOP default y - bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED + bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_JASTEC default y - bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED + bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_E2I @@ -605,17 +605,17 @@ config TOUCHSCREEN_USB_E2I config TOUCHSCREEN_USB_ZYTRONIC default y - bool "Zytronic controller" if EMBEDDED + bool "Zytronic controller" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ETT_TC45USB default y - bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EMBEDDED + bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_NEXIO default y - bool "NEXIO/iNexio device support" if EMBEDDED + bool "NEXIO/iNexio device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_TOUCHIT213 -- cgit v1.2.3 From 48c27016e18f8608c12b7516515ad773093198d8 Mon Sep 17 00:00:00 2001 From: David Engraf Date: Thu, 20 Jan 2011 23:05:17 -0800 Subject: Input: serio - allow registered drivers to get status flag Parse and pass the status byte information to the registered serio drivers as well as the data bytes. Signed-off-by: David Engraf Signed-off-by: Dmitry Torokhov --- drivers/input/serio/serport.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 6e362de3f412..8755f5f3ad37 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -116,14 +116,15 @@ static void serport_ldisc_close(struct tty_struct *tty) /* * serport_ldisc_receive() is called by the low level tty driver when characters - * are ready for us. We forward the characters, one by one to the 'interrupt' - * routine. + * are ready for us. We forward the characters and flags, one by one to the + * 'interrupt' routine. */ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct serport *serport = (struct serport*) tty->disc_data; unsigned long flags; + unsigned int ch_flags; int i; spin_lock_irqsave(&serport->lock, flags); @@ -131,8 +132,23 @@ static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *c if (!test_bit(SERPORT_ACTIVE, &serport->flags)) goto out; - for (i = 0; i < count; i++) - serio_interrupt(serport->serio, cp[i], 0); + for (i = 0; i < count; i++) { + switch (fp[i]) { + case TTY_FRAME: + ch_flags = SERIO_FRAME; + break; + + case TTY_PARITY: + ch_flags = SERIO_PARITY; + break; + + default: + ch_flags = 0; + break; + } + + serio_interrupt(serport->serio, cp[i], ch_flags); + } out: spin_unlock_irqrestore(&serport->lock, flags); -- cgit v1.2.3 From 94a8cab8caaa56824981c17b6898b73627e8382f Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 20 Jan 2011 23:09:30 -0800 Subject: Input: gpio_keys - switch to using request_any_context_irq The driver does not require hardirq context and can work with threaded interrupts as well, so let's switch to request_any_context_irq which will select the context that is available for us. Signed-off-by: Philippe Langlais Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 6069abe31e42..eb3006361ee4 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -322,7 +322,7 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; + int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) ^ button->active_low; input_event(input, type, button->code, !!state); input_sync(input); @@ -410,8 +410,8 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, if (!button->can_disable) irqflags |= IRQF_SHARED; - error = request_irq(irq, gpio_keys_isr, irqflags, desc, bdata); - if (error) { + error = request_any_context_irq(irq, gpio_keys_isr, irqflags, desc, bdata); + if (error < 0) { dev_err(dev, "Unable to claim irq %d; error %d\n", irq, error); goto fail3; -- cgit v1.2.3 From 11f5b30dacdd77bee1028764d91ad58b6c73d50c Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Wed, 19 Jan 2011 23:38:47 -0800 Subject: Input: tegra-kbc - add tegra keyboard driver This patch adds support for the internal matrix keyboard controller for Nvidia Tegra platforms. Signed-off-by: Rakesh Iyer Reviewed-by: Trilok Soni Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/tegra-kbc.c | 727 +++++++++++++++++++++++++++++++++++++ 3 files changed, 738 insertions(+) create mode 100644 drivers/input/keyboard/tegra-kbc.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 89bd912489e4..b7dca74eb9be 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -343,6 +343,16 @@ config KEYBOARD_NOMADIK To compile this driver as a module, choose M here: the module will be called nmk-ske-keypad. +config KEYBOARD_TEGRA + tristate "NVIDIA Tegra internal matrix keyboard controller support" + depends on ARCH_TEGRA + help + Say Y here if you want to use a matrix keyboard connected directly + to the internal keyboard controller on Tegra SoCs. + + To compile this driver as a module, choose M here: the + module will be called tegra-kbc. + config KEYBOARD_OPENCORES tristate "OpenCores Keyboard Controller" help diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 4dd15cf3f781..3456b931e254 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o +obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c new file mode 100644 index 000000000000..939476659ad6 --- /dev/null +++ b/drivers/input/keyboard/tegra-kbc.c @@ -0,0 +1,727 @@ +/* + * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix + * keyboard controller + * + * Copyright (c) 2009-2011, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KBC_MAX_DEBOUNCE_CNT 0x3ffu + +/* KBC row scan time and delay for beginning the row scan. */ +#define KBC_ROW_SCAN_TIME 16 +#define KBC_ROW_SCAN_DLY 5 + +/* KBC uses a 32KHz clock so a cycle = 1/32Khz */ +#define KBC_CYCLE_USEC 32 + +/* KBC Registers */ + +/* KBC Control Register */ +#define KBC_CONTROL_0 0x0 +#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) +#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) +#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KBC_EN (1 << 0) + +/* KBC Interrupt Register */ +#define KBC_INT_0 0x4 +#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) + +#define KBC_ROW_CFG0_0 0x8 +#define KBC_COL_CFG0_0 0x18 +#define KBC_INIT_DLY_0 0x28 +#define KBC_RPT_DLY_0 0x2c +#define KBC_KP_ENT0_0 0x30 +#define KBC_KP_ENT1_0 0x34 +#define KBC_ROW0_MASK_0 0x38 + +#define KBC_ROW_SHIFT 3 + +struct tegra_kbc { + void __iomem *mmio; + struct input_dev *idev; + unsigned int irq; + unsigned int wake_enable_rows; + unsigned int wake_enable_cols; + spinlock_t lock; + unsigned int repoll_dly; + unsigned long cp_dly_jiffies; + const struct tegra_kbc_platform_data *pdata; + unsigned short keycode[KBC_MAX_KEY]; + unsigned short current_keys[KBC_MAX_KPENT]; + unsigned int num_pressed_keys; + struct timer_list timer; + struct clk *clk; +}; + +static const u32 tegra_kbc_default_keymap[] = { + KEY(0, 2, KEY_W), + KEY(0, 3, KEY_S), + KEY(0, 4, KEY_A), + KEY(0, 5, KEY_Z), + KEY(0, 7, KEY_FN), + + KEY(1, 7, KEY_MENU), + + KEY(2, 6, KEY_RIGHTALT), + KEY(2, 7, KEY_LEFTALT), + + KEY(3, 0, KEY_5), + KEY(3, 1, KEY_4), + KEY(3, 2, KEY_R), + KEY(3, 3, KEY_E), + KEY(3, 4, KEY_F), + KEY(3, 5, KEY_D), + KEY(3, 6, KEY_X), + + KEY(4, 0, KEY_7), + KEY(4, 1, KEY_6), + KEY(4, 2, KEY_T), + KEY(4, 3, KEY_H), + KEY(4, 4, KEY_G), + KEY(4, 5, KEY_V), + KEY(4, 6, KEY_C), + KEY(4, 7, KEY_SPACE), + + KEY(5, 0, KEY_9), + KEY(5, 1, KEY_8), + KEY(5, 2, KEY_U), + KEY(5, 3, KEY_Y), + KEY(5, 4, KEY_J), + KEY(5, 5, KEY_N), + KEY(5, 6, KEY_B), + KEY(5, 7, KEY_BACKSLASH), + + KEY(6, 0, KEY_MINUS), + KEY(6, 1, KEY_0), + KEY(6, 2, KEY_O), + KEY(6, 3, KEY_I), + KEY(6, 4, KEY_L), + KEY(6, 5, KEY_K), + KEY(6, 6, KEY_COMMA), + KEY(6, 7, KEY_M), + + KEY(7, 1, KEY_EQUAL), + KEY(7, 2, KEY_RIGHTBRACE), + KEY(7, 3, KEY_ENTER), + KEY(7, 7, KEY_MENU), + + KEY(8, 4, KEY_RIGHTSHIFT), + KEY(8, 5, KEY_LEFTSHIFT), + + KEY(9, 5, KEY_RIGHTCTRL), + KEY(9, 7, KEY_LEFTCTRL), + + KEY(11, 0, KEY_LEFTBRACE), + KEY(11, 1, KEY_P), + KEY(11, 2, KEY_APOSTROPHE), + KEY(11, 3, KEY_SEMICOLON), + KEY(11, 4, KEY_SLASH), + KEY(11, 5, KEY_DOT), + + KEY(12, 0, KEY_F10), + KEY(12, 1, KEY_F9), + KEY(12, 2, KEY_BACKSPACE), + KEY(12, 3, KEY_3), + KEY(12, 4, KEY_2), + KEY(12, 5, KEY_UP), + KEY(12, 6, KEY_PRINT), + KEY(12, 7, KEY_PAUSE), + + KEY(13, 0, KEY_INSERT), + KEY(13, 1, KEY_DELETE), + KEY(13, 3, KEY_PAGEUP), + KEY(13, 4, KEY_PAGEDOWN), + KEY(13, 5, KEY_RIGHT), + KEY(13, 6, KEY_DOWN), + KEY(13, 7, KEY_LEFT), + + KEY(14, 0, KEY_F11), + KEY(14, 1, KEY_F12), + KEY(14, 2, KEY_F8), + KEY(14, 3, KEY_Q), + KEY(14, 4, KEY_F4), + KEY(14, 5, KEY_F3), + KEY(14, 6, KEY_1), + KEY(14, 7, KEY_F7), + + KEY(15, 0, KEY_ESC), + KEY(15, 1, KEY_GRAVE), + KEY(15, 2, KEY_F5), + KEY(15, 3, KEY_TAB), + KEY(15, 4, KEY_F1), + KEY(15, 5, KEY_F2), + KEY(15, 6, KEY_CAPSLOCK), + KEY(15, 7, KEY_F6), +}; + +static const struct matrix_keymap_data tegra_kbc_default_keymap_data = { + .keymap = tegra_kbc_default_keymap, + .keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap), +}; + +static void tegra_kbc_report_released_keys(struct input_dev *input, + unsigned short old_keycodes[], + unsigned int old_num_keys, + unsigned short new_keycodes[], + unsigned int new_num_keys) +{ + unsigned int i, j; + + for (i = 0; i < old_num_keys; i++) { + for (j = 0; j < new_num_keys; j++) + if (old_keycodes[i] == new_keycodes[j]) + break; + + if (j == new_num_keys) + input_report_key(input, old_keycodes[i], 0); + } +} + +static void tegra_kbc_report_pressed_keys(struct input_dev *input, + unsigned char scancodes[], + unsigned short keycodes[], + unsigned int num_pressed_keys) +{ + unsigned int i; + + for (i = 0; i < num_pressed_keys; i++) { + input_event(input, EV_MSC, MSC_SCAN, scancodes[i]); + input_report_key(input, keycodes[i], 1); + } +} + +static void tegra_kbc_report_keys(struct tegra_kbc *kbc) +{ + unsigned char scancodes[KBC_MAX_KPENT]; + unsigned short keycodes[KBC_MAX_KPENT]; + u32 val = 0; + unsigned int i; + unsigned int num_down = 0; + unsigned long flags; + + spin_lock_irqsave(&kbc->lock, flags); + for (i = 0; i < KBC_MAX_KPENT; i++) { + if ((i % 4) == 0) + val = readl(kbc->mmio + KBC_KP_ENT0_0 + i); + + if (val & 0x80) { + unsigned int col = val & 0x07; + unsigned int row = (val >> 3) & 0x0f; + unsigned char scancode = + MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); + + scancodes[num_down] = scancode; + keycodes[num_down++] = kbc->keycode[scancode]; + } + + val >>= 8; + } + spin_unlock_irqrestore(&kbc->lock, flags); + + tegra_kbc_report_released_keys(kbc->idev, + kbc->current_keys, kbc->num_pressed_keys, + keycodes, num_down); + tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down); + input_sync(kbc->idev); + + memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys)); + kbc->num_pressed_keys = num_down; +} + +static void tegra_kbc_keypress_timer(unsigned long data) +{ + struct tegra_kbc *kbc = (struct tegra_kbc *)data; + unsigned long flags; + u32 val; + unsigned int i; + + val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; + if (val) { + unsigned long dly; + + tegra_kbc_report_keys(kbc); + + /* + * If more than one keys are pressed we need not wait + * for the repoll delay. + */ + dly = (val == 1) ? kbc->repoll_dly : 1; + mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); + } else { + /* Release any pressed keys and exit the polling loop */ + for (i = 0; i < kbc->num_pressed_keys; i++) + input_report_key(kbc->idev, kbc->current_keys[i], 0); + input_sync(kbc->idev); + + kbc->num_pressed_keys = 0; + + /* All keys are released so enable the keypress interrupt */ + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val |= KBC_CONTROL_FIFO_CNT_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); + } +} + +static irqreturn_t tegra_kbc_isr(int irq, void *args) +{ + struct tegra_kbc *kbc = args; + u32 val, ctl; + + /* + * Until all keys are released, defer further processing to + * the polling loop in tegra_kbc_keypress_timer + */ + ctl = readl(kbc->mmio + KBC_CONTROL_0); + ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN; + writel(ctl, kbc->mmio + KBC_CONTROL_0); + + /* + * Quickly bail out & reenable interrupts if the fifo threshold + * count interrupt wasn't the interrupt source + */ + val = readl(kbc->mmio + KBC_INT_0); + writel(val, kbc->mmio + KBC_INT_0); + + if (val & KBC_INT_FIFO_CNT_INT_STATUS) { + /* + * Schedule timer to run when hardware is in continuous + * polling mode. + */ + mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); + } else { + ctl |= KBC_CONTROL_FIFO_CNT_INT_EN; + writel(ctl, kbc->mmio + KBC_CONTROL_0); + } + + return IRQ_HANDLED; +} + +static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + int i; + unsigned int rst_val; + + BUG_ON(pdata->wake_cnt > KBC_MAX_KEY); + rst_val = (filter && pdata->wake_cnt) ? ~0 : 0; + + for (i = 0; i < KBC_MAX_ROW; i++) + writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); + + if (filter) { + for (i = 0; i < pdata->wake_cnt; i++) { + u32 val, addr; + addr = pdata->wake_cfg[i].row * 4 + KBC_ROW0_MASK_0; + val = readl(kbc->mmio + addr); + val &= ~(1 << pdata->wake_cfg[i].col); + writel(val, kbc->mmio + addr); + } + } +} + +static void tegra_kbc_config_pins(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + int i; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + u32 r_shft = 5 * (i % 6); + u32 c_shft = 4 * (i % 8); + u32 r_mask = 0x1f << r_shift; + u32 c_mask = 0x0f << c_shift; + u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; + u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; + u32 row_cfg = readl(kbc->mmio + r_offs); + u32 col_cfg = readl(kbc->mmio + c_offs); + + row_cfg &= ~r_mask; + col_cfg &= ~c_mask; + + if (pdata->pin_cfg[i].is_row) + row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft; + else + col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft; + + writel(row_cfg, kbc->mmio + r_offs); + writel(col_cfg, kbc->mmio + c_offs); + } +} + +static int tegra_kbc_start(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + unsigned long flags; + unsigned int debounce_cnt; + u32 val = 0; + + clk_enable(kbc->clk); + + /* Reset the KBC controller to clear all previous status.*/ + tegra_periph_reset_assert(kbc->clk); + udelay(100); + tegra_periph_reset_deassert(kbc->clk); + udelay(100); + + tegra_kbc_config_pins(kbc); + tegra_kbc_setup_wakekeys(kbc, false); + + writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); + + /* Keyboard debounce count is maximum of 12 bits. */ + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); + val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ + val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ + val |= KBC_CONTROL_KBC_EN; /* enable */ + writel(val, kbc->mmio + KBC_CONTROL_0); + + /* + * Compute the delay(ns) from interrupt mode to continuous polling + * mode so the timer routine is scheduled appropriately. + */ + val = readl(kbc->mmio + KBC_INIT_DLY_0); + kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); + + kbc->num_pressed_keys = 0; + + /* + * Atomically clear out any remaining entries in the key FIFO + * and enable keyboard interrupts. + */ + spin_lock_irqsave(&kbc->lock, flags); + while (1) { + val = readl(kbc->mmio + KBC_INT_0); + val >>= 4; + if (!val) + break; + + val = readl(kbc->mmio + KBC_KP_ENT0_0); + val = readl(kbc->mmio + KBC_KP_ENT1_0); + } + writel(0x7, kbc->mmio + KBC_INT_0); + spin_unlock_irqrestore(&kbc->lock, flags); + + enable_irq(kbc->irq); + + return 0; +} + +static void tegra_kbc_stop(struct tegra_kbc *kbc) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&kbc->lock, flags); + val = readl(kbc->mmio + KBC_CONTROL_0); + val &= ~1; + writel(val, kbc->mmio + KBC_CONTROL_0); + spin_unlock_irqrestore(&kbc->lock, flags); + + disable_irq(kbc->irq); + del_timer_sync(&kbc->timer); + + clk_disable(kbc->clk); +} + +static int tegra_kbc_open(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + + return tegra_kbc_start(kbc); +} + +static void tegra_kbc_close(struct input_dev *dev) +{ + struct tegra_kbc *kbc = input_get_drvdata(dev); + + return tegra_kbc_stop(kbc); +} + +static bool __devinit +tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, + struct device *dev, unsigned int *num_rows) +{ + int i; + + *num_rows = 0; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; + + if (pin_cfg->is_row) { + if (pin_cfg->num >= KBC_MAX_ROW) { + dev_err(dev, + "pin_cfg[%d]: invalid row number %d\n", + i, pin_cfg->num); + return false; + } + (*num_rows)++; + } else { + if (pin_cfg->num >= KBC_MAX_COL) { + dev_err(dev, + "pin_cfg[%d]: invalid column number %d\n", + i, pin_cfg->num); + return false; + } + } + } + + return true; +} + +static int __devinit tegra_kbc_probe(struct platform_device *pdev) +{ + const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; + const struct matrix_keymap_data *keymap_data; + struct tegra_kbc *kbc; + struct input_dev *input_dev; + struct resource *res; + int irq; + int err; + int i; + int num_rows = 0; + unsigned int debounce_cnt; + unsigned int scan_time_rows; + + if (!pdata) + return -EINVAL; + + if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + return -ENXIO; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); + return -ENXIO; + } + + kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!kbc || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + kbc->pdata = pdata; + kbc->idev = input_dev; + kbc->irq = irq; + spin_lock_init(&kbc->lock); + setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + err = -EBUSY; + goto err_free_mem; + } + + kbc->mmio = ioremap(res->start, resource_size(res)); + if (!kbc->mmio) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + err = -ENXIO; + goto err_free_mem_region; + } + + kbc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(kbc->clk)) { + dev_err(&pdev->dev, "failed to get keyboard clock\n"); + err = PTR_ERR(kbc->clk); + goto err_iounmap; + } + + kbc->wake_enable_rows = 0; + kbc->wake_enable_cols = 0; + for (i = 0; i < pdata->wake_cnt; i++) { + kbc->wake_enable_rows |= (1 << pdata->wake_cfg[i].row); + kbc->wake_enable_cols |= (1 << pdata->wake_cfg[i].col); + } + + /* + * The time delay between two consecutive reads of the FIFO is + * the sum of the repeat time and the time taken for scanning + * the rows. There is an additional delay before the row scanning + * starts. The repoll delay is computed in milliseconds. + */ + debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; + kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; + kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000; + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_dev->open = tegra_kbc_open; + input_dev->close = tegra_kbc_close; + + input_set_drvdata(input_dev, kbc); + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = kbc->keycode; + input_dev->keycodesize = sizeof(kbc->keycode[0]); + input_dev->keycodemax = ARRAY_SIZE(kbc->keycode); + + keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; + matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, + input_dev->keycode, input_dev->keybit); + + err = request_irq(kbc->irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, + pdev->name, kbc); + if (err) { + dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); + goto err_put_clk; + } + + disable_irq(kbc->irq); + + err = input_register_device(kbc->idev); + if (err) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, kbc); + device_init_wakeup(&pdev->dev, pdata->wakeup); + + return 0; + +err_free_irq: + free_irq(kbc->irq, pdev); +err_put_clk: + clk_put(kbc->clk); +err_iounmap: + iounmap(kbc->mmio); +err_free_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(kbc->idev); + kfree(kbc); + + return err; +} + +static int __devexit tegra_kbc_remove(struct platform_device *pdev) +{ + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(kbc->irq, pdev); + clk_put(kbc->clk); + + input_unregister_device(kbc->idev); + iounmap(kbc->mmio); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(kbc); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_kbc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) { + tegra_kbc_setup_wakekeys(kbc, true); + enable_irq_wake(kbc->irq); + /* Forcefully clear the interrupt status */ + writel(0x7, kbc->mmio + KBC_INT_0); + msleep(30); + } else { + mutex_lock(&kbc->idev->mutex); + if (kbc->idev->users) + tegra_kbc_stop(kbc); + mutex_unlock(&kbc->idev->mutex); + } + + return 0; +} + +static int tegra_kbc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_kbc *kbc = platform_get_drvdata(pdev); + int err = 0; + + if (device_may_wakeup(&pdev->dev)) { + disable_irq_wake(kbc->irq); + tegra_kbc_setup_wakekeys(kbc, false); + } else { + mutex_lock(&kbc->idev->mutex); + if (kbc->idev->users) + err = tegra_kbc_start(kbc); + mutex_unlock(&kbc->idev->mutex); + } + + return err; +} +#endif + +static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume); + +static struct platform_driver tegra_kbc_driver = { + .probe = tegra_kbc_probe, + .remove = __devexit_p(tegra_kbc_remove), + .driver = { + .name = "tegra-kbc", + .owner = THIS_MODULE, + .pm = &tegra_kbc_pm_ops, + }, +}; + +static void __exit tegra_kbc_exit(void) +{ + platform_driver_unregister(&tegra_kbc_driver); +} +module_exit(tegra_kbc_exit); + +static int __init tegra_kbc_init(void) +{ + return platform_driver_register(&tegra_kbc_driver); +} +module_init(tegra_kbc_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Rakesh Iyer "); +MODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); +MODULE_ALIAS("platform:tegra-kbc"); -- cgit v1.2.3 From cb1b145929b5b7e1bdc5f76dd2905df9f89c038e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 20 Jan 2011 23:05:17 -0800 Subject: Input: sparse-keymap - fix KEY_VSW handling in sparse_keymap_setup We were forgetting to set up device capabilities for KEY_VSW entries. Reported-by: Tapio Vihuri Tested-by: Tapio Vihuri Signed-off-by: Dmitry Torokhov --- drivers/input/sparse-keymap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index a29a7812bd46..7729e547ba65 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -201,6 +201,7 @@ int sparse_keymap_setup(struct input_dev *dev, break; case KE_SW: + case KE_VSW: __set_bit(EV_SW, dev->evbit); __set_bit(entry->sw.code, dev->swbit); break; -- cgit v1.2.3 From d38acb49b7368ac4e8ec0db0055155be0805db5d Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Mon, 24 Jan 2011 09:32:50 -0800 Subject: Input: wacom - add 2 Bamboo Pen and touch models Reported-by: David Foley Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 518782999fea..f44c822caac8 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1426,6 +1426,10 @@ static struct wacom_features wacom_features_0xD3 = { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; static const struct wacom_features wacom_features_0xD4 = { "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 255, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD6 = + { "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD7 = + { "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xD8 = { "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; static struct wacom_features wacom_features_0xDA = @@ -1507,6 +1511,8 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xD2) }, { USB_DEVICE_WACOM(0xD3) }, { USB_DEVICE_WACOM(0xD4) }, + { USB_DEVICE_WACOM(0xD6) }, + { USB_DEVICE_WACOM(0xD7) }, { USB_DEVICE_WACOM(0xD8) }, { USB_DEVICE_WACOM(0xDA) }, { USB_DEVICE_WACOM(0xDB) }, -- cgit v1.2.3 From 409550f2902470f0387fe40a7db441526e16b2c0 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Tue, 25 Jan 2011 18:03:13 -0800 Subject: Input: wacom - pass touch resolution to clients through input_absinfo Also remove fake ABS_RX/ABS_RY "axes" that were used to report physical dimensions now that we have better way. Signed-off-by: Ping Cheng Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index f44c822caac8..367fa82a607e 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1101,6 +1101,13 @@ void wacom_setup_device_quirks(struct wacom_features *features) } } +static unsigned int wacom_calculate_touch_res(unsigned int logical_max, + unsigned int physical_max) +{ + /* Touch physical dimensions are in 100th of mm */ + return (logical_max * 100) / physical_max; +} + void wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { @@ -1228,8 +1235,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, case TABLETPC: if (features->device_type == BTN_TOOL_DOUBLETAP || features->device_type == BTN_TOOL_TRIPLETAP) { - input_set_abs_params(input_dev, ABS_RX, 0, features->x_phy, 0, 0); - input_set_abs_params(input_dev, ABS_RY, 0, features->y_phy, 0, 0); + input_abs_set_res(input_dev, ABS_X, + wacom_calculate_touch_res(features->x_max, + features->x_phy)); + input_abs_set_res(input_dev, ABS_Y, + wacom_calculate_touch_res(features->y_max, + features->y_phy)); __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); } @@ -1272,6 +1283,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, features->pressure_max, features->pressure_fuzz, 0); + input_abs_set_res(input_dev, ABS_X, + wacom_calculate_touch_res(features->x_max, + features->x_phy)); + input_abs_set_res(input_dev, ABS_Y, + wacom_calculate_touch_res(features->y_max, + features->y_phy)); } else if (features->device_type == BTN_TOOL_PEN) { __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); -- cgit v1.2.3 From 7530c4a197f3dd63f1381c1cdf93e4d2b8429ef5 Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Fri, 28 Jan 2011 22:05:14 -0800 Subject: Input: tegra-kbc - fix build error Fix build error introduced by variable name change. Signed-off-by: Rakesh Iyer Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 939476659ad6..3d3d649be9bc 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -355,8 +355,8 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc) for (i = 0; i < KBC_MAX_GPIO; i++) { u32 r_shft = 5 * (i % 6); u32 c_shft = 4 * (i % 8); - u32 r_mask = 0x1f << r_shift; - u32 c_mask = 0x0f << c_shift; + u32 r_mask = 0x1f << r_shft; + u32 c_mask = 0x0f << c_shft; u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; u32 row_cfg = readl(kbc->mmio + r_offs); -- cgit v1.2.3 From e7acc84a27fe53b198cd98cc7deaabb5dd0f20c4 Mon Sep 17 00:00:00 2001 From: Rakesh Iyer Date: Fri, 28 Jan 2011 22:05:14 -0800 Subject: Input: tegra-kbc - fix keymap entry for LeftMeta key Correct key mapping for Left Meta key. Signed-off-by: Rakesh Iyer Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 3d3d649be9bc..ac471b77c18e 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -86,7 +86,7 @@ static const u32 tegra_kbc_default_keymap[] = { KEY(0, 5, KEY_Z), KEY(0, 7, KEY_FN), - KEY(1, 7, KEY_MENU), + KEY(1, 7, KEY_LEFTMETA), KEY(2, 6, KEY_RIGHTALT), KEY(2, 7, KEY_LEFTALT), -- cgit v1.2.3 From baddf58963241307b29e027a9fb28dfe55939db8 Mon Sep 17 00:00:00 2001 From: Alexandre Peixoto Ferreira Date: Fri, 28 Jan 2011 22:05:14 -0800 Subject: Input: synaptics - fix reconnect logic on MT devices synaptics_set_advanced_gesture_mode() affect capabilities bits we should perform comparison after calling this function, otherwise they will never match and we will be forced to perform full reconnect. Signed-off-by: Alexandre Peixoto Ferreira Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index da392c22fc6c..fdf089c0c707 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -766,12 +766,6 @@ static int synaptics_reconnect(struct psmouse *psmouse) return -1; } - if (old_priv.identity != priv->identity || - old_priv.model_id != priv->model_id || - old_priv.capabilities != priv->capabilities || - old_priv.ext_cap != priv->ext_cap) - return -1; - if (synaptics_set_absolute_mode(psmouse)) { printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); return -1; @@ -782,6 +776,19 @@ static int synaptics_reconnect(struct psmouse *psmouse) return -1; } + if (old_priv.identity != priv->identity || + old_priv.model_id != priv->model_id || + old_priv.capabilities != priv->capabilities || + old_priv.ext_cap != priv->ext_cap) { + printk(KERN_ERR "Synaptics hardware appears to be different: " + "id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n", + old_priv.identity, priv->identity, + old_priv.model_id, priv->model_id, + old_priv.capabilities, priv->capabilities, + old_priv.ext_cap, priv->ext_cap); + return -1; + } + return 0; } -- cgit v1.2.3 From c63fe0a41fd2374be0ebe93c053820a0eed9cefe Mon Sep 17 00:00:00 2001 From: Alexandre Peixoto Ferreira Date: Fri, 28 Jan 2011 22:05:14 -0800 Subject: Input: synaptics - retry failed resets when reconnecting On some machines, like Dell Studio XPS 16 (1640), touchpad fails to respond to the standard query after first reset but may start responding later, so let's repeat reset sequence several (3) times. Signed-off-by: Alexandre Peixoto Ferreira Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index fdf089c0c707..aa186cf6c514 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -755,12 +755,21 @@ static int synaptics_reconnect(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; struct synaptics_data old_priv = *priv; + int retry = 0; + int error; - psmouse_reset(psmouse); + do { + psmouse_reset(psmouse); + error = synaptics_detect(psmouse, 0); + } while (error && ++retry < 3); - if (synaptics_detect(psmouse, 0)) + if (error) return -1; + if (retry > 1) + printk(KERN_DEBUG "Synaptics reconnected after %d tries\n", + retry); + if (synaptics_query_hardware(psmouse)) { printk(KERN_ERR "Unable to query Synaptics hardware.\n"); return -1; -- cgit v1.2.3 From e66a022a80d73b1a5d2e02c9db2c42e8b9853b40 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 11 Dec 2010 20:17:54 +0000 Subject: arm/ixp4xx: Rename FREQ macro to avoid collisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FREQ is a ridiculously short name for a platform-specific macro in a generic header, and it now conflicts with an enumeration in the gspca/ov519 driver. Also delete conditional reference to ixp4xx_get_board_tick_rate() which is not defined anywhere. Signed-off-by: Ben Hutchings Signed-off-by: Krzysztof HaÅ‚asa --- drivers/input/misc/ixp4xx-beeper.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 9dfd6e5f786f..1f38302a5951 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -69,11 +69,7 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned } if (value > 20 && value < 32767) -#ifndef FREQ - count = (ixp4xx_get_board_tick_rate() / (value * 4)) - 1; -#else - count = (FREQ / (value * 4)) - 1; -#endif + count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1; ixp4xx_spkr_control(pin, count); -- cgit v1.2.3