diff options
Diffstat (limited to 'drivers/media/usb/em28xx/em28xx-input.c')
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-input.c | 219 |
1 files changed, 134 insertions, 85 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index edcd6978f2e1..72cb0cfad8d0 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -62,13 +62,17 @@ struct em28xx_IR { char name[32]; char phys[32]; - /* poll external decoder */ + /* poll decoder */ int polling; struct delayed_work work; unsigned int full_code:1; unsigned int last_readcount; u64 rc_type; + /* external device (if used) */ + struct i2c_client *i2c_dev; + + int (*get_key_i2c)(struct i2c_client *, u32 *, u32 *); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -76,12 +80,13 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) + if (1 != i2c_master_recv(i2c_dev, &b, 1)) return -EIO; /* it seems that 0xFE indicates that a button is still hold @@ -100,14 +105,15 @@ static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; u16 code; int size; /* poll IR chip */ - size = i2c_master_recv(ir->c, buf, sizeof(buf)); + size = i2c_master_recv(i2c_dev, buf, sizeof(buf)); if (size != 2) return -EIO; @@ -144,14 +150,14 @@ static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char buf[3]; /* poll IR chip */ - if (3 != i2c_master_recv(ir->c, buf, 3)) + if (3 != i2c_master_recv(i2c_dev, buf, 3)) return -EIO; if (buf[0] != 0x00) @@ -163,24 +169,24 @@ static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } -static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char subaddr, keydetect, key; - struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1}, + struct i2c_msg msg[] = { { .addr = i2c_dev->addr, .flags = 0, .buf = &subaddr, .len = 1}, - { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; subaddr = 0x10; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) return -EIO; if (keydetect == 0x00) return 0; subaddr = 0x00; msg[1].buf = &key; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) return -EIO; if (key == 0x00) return 0; @@ -280,6 +286,24 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, Polling code for em28xx **********************************************************/ +static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) +{ + static u32 ir_key, ir_raw; + int rc; + + rc = ir->get_key_i2c(ir->i2c_dev, &ir_key, &ir_raw); + if (rc < 0) { + dprintk("ir->get_key_i2c() failed: %d\n", rc); + return rc; + } + + if (rc) { + dprintk("%s: keycode = 0x%04x\n", __func__, ir_key); + rc_keydown(ir->rc, ir_key, 0); + } + return 0; +} + static void em28xx_ir_handle_key(struct em28xx_IR *ir) { int result; @@ -288,7 +312,7 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) /* read the registers containing the IR status */ result = ir->get_key(ir, &poll_result); if (unlikely(result < 0)) { - dprintk("ir->get_key() failed %d\n", result); + dprintk("ir->get_key() failed: %d\n", result); return; } @@ -318,6 +342,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } } +static void em28xx_i2c_ir_work(struct work_struct *work) +{ + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); + + em28xx_i2c_ir_handle_key(ir); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + static void em28xx_ir_work(struct work_struct *work) { struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); @@ -330,7 +362,10 @@ static int em28xx_ir_start(struct rc_dev *rc) { struct em28xx_IR *ir = rc->priv; - INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); + if (ir->i2c_dev) /* external i2c device */ + INIT_DELAYED_WORK(&ir->work, em28xx_i2c_ir_work); + else /* internal device */ + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); return 0; @@ -427,49 +462,33 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) } } -static void em28xx_register_i2c_ir(struct em28xx *dev) +static struct i2c_client *em28xx_probe_i2c_ir(struct em28xx *dev) { + int i = 0; + struct i2c_client *i2c_dev = NULL; /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ /* at address 0x18, so if that address is needed for another board in */ /* the future, please put it after 0x1f. */ - struct i2c_board_info info; const unsigned short addr_list[] = { 0x1f, 0x30, 0x47, I2C_CLIENT_END }; - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "Terratec Cinergy 200/250"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "Pinnacle USB2"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "WinTV USB2"; - dev->init_data.type = RC_BIT_RC5; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "Winfast TV USBII Deluxe"; - break; + while (addr_list[i] != I2C_CLIENT_END) { + if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) { + i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); + if (i2c_dev) { + i2c_dev->addr = addr_list[i]; + i2c_dev->adapter = &dev->i2c_adap; + /* NOTE: as long as we don't register the device + * at the i2c subsystem, no other fields need to + * be set up */ + } + break; + } + i++; } - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); + return i2c_dev; } /********************************************************** @@ -565,19 +584,21 @@ static int em28xx_ir_init(struct em28xx *dev) struct rc_dev *rc; int err = -ENOMEM; u64 rc_type; + struct i2c_client *i2c_rc_dev = NULL; if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); if (dev->board.has_ir_i2c) { - em28xx_register_i2c_ir(dev); -#if defined(CONFIG_MODULES) && defined(MODULE) - request_module("ir-kbd-i2c"); -#endif - return 0; + i2c_rc_dev = em28xx_probe_i2c_ir(dev); + if (!i2c_rc_dev) { + dev->board.has_ir_i2c = 0; + em28xx_warn("No i2c IR remote control device found.\n"); + return -ENODEV; + } } - if (dev->board.ir_codes == NULL) { + if (dev->board.ir_codes == NULL && !dev->board.has_ir_i2c) { /* No remote control support */ em28xx_warn("Remote control support is not available for " "this card.\n"); @@ -594,45 +615,70 @@ static int em28xx_ir_init(struct em28xx *dev) dev->ir = ir; ir->rc = rc; - /* - * em2874 supports more protocols. For now, let's just announce - * the two protocols that were already tested - */ - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; rc->priv = ir; - rc->change_protocol = em28xx_ir_change_protocol; rc->open = em28xx_ir_start; rc->close = em28xx_ir_stop; - switch (dev->chip_id) { - case CHIP_ID_EM2860: - case CHIP_ID_EM2883: - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; - ir->get_key = default_polling_getkey; - break; - case CHIP_ID_EM2884: - case CHIP_ID_EM2874: - case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; - break; - default: - err = -ENODEV; - goto error; + if (dev->board.has_ir_i2c) { /* external i2c device */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + rc->map_name = RC_MAP_EM_TERRATEC; + ir->get_key_i2c = em28xx_get_key_terratec; + break; + case EM2820_BOARD_PINNACLE_USB_2: + rc->map_name = RC_MAP_PINNACLE_GREY; + ir->get_key_i2c = em28xx_get_key_pinnacle_usb_grey; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + rc->map_name = RC_MAP_HAUPPAUGE; + ir->get_key_i2c = em28xx_get_key_em_haup; + rc->allowed_protos = RC_BIT_RC5; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + rc->map_name = RC_MAP_WINFAST_USBII_DELUXE; + ir->get_key_i2c = em28xx_get_key_winfast_usbii_deluxe; + break; + default: + err = -ENODEV; + goto error; + } + + ir->i2c_dev = i2c_rc_dev; + } else { /* internal device */ + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + ir->get_key = default_polling_getkey; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + ir->get_key = em2874_polling_getkey; + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | + RC_BIT_RC6_0; + break; + default: + err = -ENODEV; + goto error; + } + + rc->change_protocol = em28xx_ir_change_protocol; + rc->map_name = dev->board.ir_codes; + + /* By default, keep protocol field untouched */ + rc_type = RC_BIT_UNKNOWN; + err = em28xx_ir_change_protocol(rc, &rc_type); + if (err) + goto error; } - /* By default, keep protocol field untouched */ - rc_type = RC_BIT_UNKNOWN; - err = em28xx_ir_change_protocol(rc, &rc_type); - if (err) - goto error; - /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ /* init input device */ - snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", - dev->name); + snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", dev->name); usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); @@ -644,7 +690,6 @@ static int em28xx_ir_init(struct em28xx *dev) rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); rc->dev.parent = &dev->udev->dev; - rc->map_name = dev->board.ir_codes; rc->driver_name = MODULE_NAME; /* all done */ @@ -655,6 +700,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; error: + if (ir && ir->i2c_dev) + kfree(ir->i2c_dev); dev->ir = NULL; rc_free_device(rc); kfree(ir); @@ -674,6 +721,8 @@ static int em28xx_ir_fini(struct em28xx *dev) if (ir->rc) rc_unregister_device(ir->rc); + kfree(ir->i2c_dev); + /* done */ kfree(ir); dev->ir = NULL; |