summaryrefslogtreecommitdiff
path: root/drivers/media/usb/em28xx/em28xx-input.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/em28xx/em28xx-input.c')
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c219
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;