diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-31 21:31:14 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-31 21:31:14 +0400 |
commit | b399c46ea0070671f3abbe1915d26076101a42f2 (patch) | |
tree | 8945606976fc46c3446c09f8a9e0d4f45f6c408e /drivers/media/usb/em28xx/em28xx-input.c | |
parent | b890eb4ecc718907223a3b7b7b069b59b33f28ef (diff) | |
parent | 6c3df5da67f1f53df78c7e20cd53a481dc28eade (diff) | |
download | linux-b399c46ea0070671f3abbe1915d26076101a42f2.tar.xz |
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- a new jpeg codec driver for Samsung Exynos (jpeg-hw-exynos4)
- a new dvb frontend for ds2103 chipset (m88ds2103)
- a new sensor driver for Samsung S5K5BAF UXGA (s5k5baf)
- new drivers for R-Car VSP1
- a new radio driver: radio-raremono
- a new tuner driver for ts2022 chipset (m88ts2022)
- the analog part of em28xx is now a separate module that only
load/runs if the device is not a pure digital TV device
- added a staging driver for bcm2048 radio devices
- the omap 2 video driver (omap24xx) was moved to staging. This driver
is for an old hardware and uses a deprecated Kernel internal API. If
nobody cares enough to fix it, it would be removed on a couple Kernel
releases
- the sn9c102 driver was moved to staging. This driver was replaced by
gspca, and disabled on some distros, as almost all devices are known
to work properly with gspca. It should be removed from kernel on a
couple Kernel releases
- lots of driver fixes, improvements and cleanups
* 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (421 commits)
[media] media: v4l2-dev: fix video device index assignment
[media] rc-core: reuse device numbers
[media] em28xx-cards: properly initialize the device bitmap
[media] Staging: media: Fix line length exceeding 80 characters in as102_drv.c
[media] Staging: media: Fix line length exceeding 80 characters in as102_fe.c
[media] Staging: media: Fix quoted string split across line in as102_fe.c
[media] media: st-rc: Add reset support
[media] m2m-deinterlace: fix allocated struct type
[media] radio-usb-si4713: fix sparse non static symbol warnings
[media] em28xx-audio: remove needless check before usb_free_coherent()
[media] au0828: Fix sparse non static symbol warning
Revert "[media] go7007-usb: only use go->dev after allocated"
[media] em28xx-audio: provide an error code when URB submit fails
[media] em28xx: fix check for audio only usb interfaces when changing the usb alternate setting
[media] em28xx: fix usb alternate setting for analog and digital video endpoints > 0
[media] em28xx: make 'em28xx_ctrl_ops' static
em28xx-alsa: Fix error patch for init/fini
[media] em28xx-audio: flush work at .fini
[media] drxk: remove the option to load firmware asynchronously
[media] em28xx: adjust period size at runtime
...
Diffstat (limited to 'drivers/media/usb/em28xx/em28xx-input.c')
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-input.c | 209 |
1 files changed, 167 insertions, 42 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ea181e4b68c5..18f65d89d4bc 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -30,8 +30,9 @@ #include "em28xx.h" -#define EM28XX_SNAPSHOT_KEY KEY_CAMERA -#define EM28XX_SBUTTON_QUERY_INTERVAL 500 +#define EM28XX_SNAPSHOT_KEY KEY_CAMERA +#define EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL 500 /* [ms] */ +#define EM28XX_BUTTONS_VOLATILE_QUERY_INTERVAL 100 /* [ms] */ static unsigned int ir_debug; module_param(ir_debug, int, 0644); @@ -442,6 +443,7 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: + case CHIP_ID_EM28178: return em2874_ir_change_protocol(rc_dev, rc_type); default: printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", @@ -470,54 +472,98 @@ static int em28xx_probe_i2c_ir(struct em28xx *dev) } /********************************************************** - Handle Webcam snapshot button + Handle buttons **********************************************************/ -static void em28xx_query_sbutton(struct work_struct *work) +static void em28xx_query_buttons(struct work_struct *work) { - /* Poll the register and see if the button is depressed */ struct em28xx *dev = - container_of(work, struct em28xx, sbutton_query_work.work); - int ret; - - ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); - - if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { - u8 cleared; - /* Button is depressed, clear the register */ - cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; - em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); - - /* Not emulate the keypress */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 1); - /* Now unpress the key */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 0); + container_of(work, struct em28xx, buttons_query_work.work); + u8 i, j; + int regval; + bool is_pressed, was_pressed; + const struct em28xx_led *led; + + /* Poll and evaluate all addresses */ + for (i = 0; i < dev->num_button_polling_addresses; i++) { + /* Read value from register */ + regval = em28xx_read_reg(dev, dev->button_polling_addresses[i]); + if (regval < 0) + continue; + /* Check states of the buttons and act */ + j = 0; + while (dev->board.buttons[j].role >= 0 && + dev->board.buttons[j].role < EM28XX_NUM_BUTTON_ROLES) { + struct em28xx_button *button = &dev->board.buttons[j]; + /* Check if button uses the current address */ + if (button->reg_r != dev->button_polling_addresses[i]) { + j++; + continue; + } + /* Determine if button is and was pressed last time */ + is_pressed = regval & button->mask; + was_pressed = dev->button_polling_last_values[i] + & button->mask; + if (button->inverted) { + is_pressed = !is_pressed; + was_pressed = !was_pressed; + } + /* Clear button state (if needed) */ + if (is_pressed && button->reg_clearing) + em28xx_write_reg(dev, button->reg_clearing, + (~regval & button->mask) + | (regval & ~button->mask)); + /* Handle button state */ + if (!is_pressed || was_pressed) { + j++; + continue; + } + switch (button->role) { + case EM28XX_BUTTON_SNAPSHOT: + /* Emulate the keypress */ + input_report_key(dev->sbutton_input_dev, + EM28XX_SNAPSHOT_KEY, 1); + /* Unpress the key */ + input_report_key(dev->sbutton_input_dev, + EM28XX_SNAPSHOT_KEY, 0); + break; + case EM28XX_BUTTON_ILLUMINATION: + led = em28xx_find_led(dev, + EM28XX_LED_ILLUMINATION); + /* Switch illumination LED on/off */ + if (led) + em28xx_toggle_reg_bits(dev, + led->gpio_reg, + led->gpio_mask); + break; + default: + WARN_ONCE(1, "BUG: unhandled button role."); + } + /* Next button */ + j++; + } + /* Save current value for comparison during the next polling */ + dev->button_polling_last_values[i] = regval; } - /* Schedule next poll */ - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + schedule_delayed_work(&dev->buttons_query_work, + msecs_to_jiffies(dev->button_polling_interval)); } -static void em28xx_register_snapshot_button(struct em28xx *dev) +static int em28xx_register_snapshot_button(struct em28xx *dev) { struct input_dev *input_dev; int err; em28xx_info("Registering snapshot button...\n"); input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); - return; - } + if (!input_dev) + return -ENOMEM; usb_make_path(dev->udev, dev->snapshot_button_path, sizeof(dev->snapshot_button_path)); strlcat(dev->snapshot_button_path, "/sbutton", sizeof(dev->snapshot_button_path)); - INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); input_dev->name = "em28xx snapshot button"; input_dev->phys = dev->snapshot_button_path; @@ -535,25 +581,86 @@ static void em28xx_register_snapshot_button(struct em28xx *dev) if (err) { em28xx_errdev("input_register_device failed\n"); input_free_device(input_dev); - return; + return err; } dev->sbutton_input_dev = input_dev; - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); - return; + return 0; +} +static void em28xx_init_buttons(struct em28xx *dev) +{ + u8 i = 0, j = 0; + bool addr_new = 0; + + dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL; + while (dev->board.buttons[i].role >= 0 && + dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) { + struct em28xx_button *button = &dev->board.buttons[i]; + /* Check if polling address is already on the list */ + addr_new = 1; + for (j = 0; j < dev->num_button_polling_addresses; j++) { + if (button->reg_r == dev->button_polling_addresses[j]) { + addr_new = 0; + break; + } + } + /* Check if max. number of polling addresses is exceeded */ + if (addr_new && dev->num_button_polling_addresses + >= EM28XX_NUM_BUTTON_ADDRESSES_MAX) { + WARN_ONCE(1, "BUG: maximum number of button polling addresses exceeded."); + goto next_button; + } + /* Button role specific checks and actions */ + if (button->role == EM28XX_BUTTON_SNAPSHOT) { + /* Register input device */ + if (em28xx_register_snapshot_button(dev) < 0) + goto next_button; + } else if (button->role == EM28XX_BUTTON_ILLUMINATION) { + /* Check sanity */ + if (!em28xx_find_led(dev, EM28XX_LED_ILLUMINATION)) { + em28xx_errdev("BUG: illumination button defined, but no illumination LED.\n"); + goto next_button; + } + } + /* Add read address to list of polling addresses */ + if (addr_new) { + unsigned int index = dev->num_button_polling_addresses; + dev->button_polling_addresses[index] = button->reg_r; + dev->num_button_polling_addresses++; + } + /* Reduce polling interval if necessary */ + if (!button->reg_clearing) + dev->button_polling_interval = + EM28XX_BUTTONS_VOLATILE_QUERY_INTERVAL; +next_button: + /* Next button */ + i++; + } + + /* Start polling */ + if (dev->num_button_polling_addresses) { + memset(dev->button_polling_last_values, 0, + EM28XX_NUM_BUTTON_ADDRESSES_MAX); + INIT_DELAYED_WORK(&dev->buttons_query_work, + em28xx_query_buttons); + schedule_delayed_work(&dev->buttons_query_work, + msecs_to_jiffies(dev->button_polling_interval)); + } } -static void em28xx_deregister_snapshot_button(struct em28xx *dev) +static void em28xx_shutdown_buttons(struct em28xx *dev) { + /* Cancel polling */ + cancel_delayed_work_sync(&dev->buttons_query_work); + /* Clear polling addresses list */ + dev->num_button_polling_addresses = 0; + /* Deregister input devices */ if (dev->sbutton_input_dev != NULL) { em28xx_info("Deregistering snapshot button\n"); - cancel_delayed_work_sync(&dev->sbutton_query_work); input_unregister_device(dev->sbutton_input_dev); dev->sbutton_input_dev = NULL; } - return; } static int em28xx_ir_init(struct em28xx *dev) @@ -564,8 +671,13 @@ static int em28xx_ir_init(struct em28xx *dev) u64 rc_type; u16 i2c_rc_dev_addr = 0; - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + + if (dev->board.buttons) + em28xx_init_buttons(dev); if (dev->board.has_ir_i2c) { i2c_rc_dev_addr = em28xx_probe_i2c_ir(dev); @@ -583,6 +695,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; } + em28xx_info("Registering input extension\n"); + ir = kzalloc(sizeof(*ir), GFP_KERNEL); rc = rc_allocate_device(); if (!ir || !rc) @@ -633,6 +747,7 @@ static int em28xx_ir_init(struct em28xx *dev) case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: + case CHIP_ID_EM28178: ir->get_key = em2874_polling_getkey; rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; @@ -675,6 +790,8 @@ static int em28xx_ir_init(struct em28xx *dev) if (err) goto error; + em28xx_info("Input extension successfully initalized\n"); + return 0; error: @@ -688,7 +805,14 @@ static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; - em28xx_deregister_snapshot_button(dev); + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + + em28xx_info("Closing input extension"); + + em28xx_shutdown_buttons(dev); /* skip detach on non attached boards */ if (!ir) @@ -722,7 +846,8 @@ static void __exit em28xx_rc_unregister(void) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); -MODULE_DESCRIPTION("Em28xx Input driver"); +MODULE_DESCRIPTION(DRIVER_DESC " - input interface"); +MODULE_VERSION(EM28XX_VERSION); module_init(em28xx_rc_register); module_exit(em28xx_rc_unregister); |