The patch number 9607 was added via Mauro Carvalho Chehab <[EMAIL PROTECTED]>
to http://linuxtv.org/hg/v4l-dvb master development tree.
Kernel patches in this development tree may be modified to be backward
compatible with older kernels. Compatibility modifications will be
removed before inclusion into the mainstream Kernel
If anyone has any objections, please let us know by sending a message to:
[EMAIL PROTECTED]
------
From: Mauro Carvalho Chehab <[EMAIL PROTECTED]>
em28xx: Properly implement poll support for IR's
The first em28xx were based on i2c IR's. However, some newer designs
are coming with a polling-based IR. Those are done by reading a register
set at em28xx.
This patch adds core polling support for those devices. Later patches will
add support for some device-specific IR's.
This patch adds the same basic IR polling code used by bttv, cx88 and saa7134,
and
shares the common getkey masks defined at ir-common.
Priority: normal
Signed-off-by: Mauro Carvalho Chehab <[EMAIL PROTECTED]>
---
linux/drivers/media/video/em28xx/em28xx-cards.c | 2
linux/drivers/media/video/em28xx/em28xx-input.c | 264 +++++++++++++++-
linux/drivers/media/video/em28xx/em28xx-reg.h | 7
linux/drivers/media/video/em28xx/em28xx-video.c | 4
linux/drivers/media/video/em28xx/em28xx.h | 6
5 files changed, 266 insertions(+), 17 deletions(-)
diff -r c95684f09ecb -r f28c3e7bca61
linux/drivers/media/video/em28xx/em28xx-cards.c
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c Wed Nov 12 02:28:57
2008 -0200
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c Wed Nov 12 09:41:29
2008 -0200
@@ -1848,4 +1848,6 @@ void em28xx_card_setup(struct em28xx *de
#endif
em28xx_config_tuner(dev);
+
+ em28xx_ir_init(dev);
}
diff -r c95684f09ecb -r f28c3e7bca61
linux/drivers/media/video/em28xx/em28xx-input.c
--- a/linux/drivers/media/video/em28xx/em28xx-input.c Wed Nov 12 02:28:57
2008 -0200
+++ b/linux/drivers/media/video/em28xx/em28xx-input.c Wed Nov 12 09:41:29
2008 -0200
@@ -39,12 +39,42 @@ module_param(ir_debug, int, 0644);
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
+#define i2cdprintk(fmt, arg...) \
+ if (ir_debug) { \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
+ }
+
#define dprintk(fmt, arg...) \
if (ir_debug) { \
- printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
- }
-
-/* ----------------------------------------------------------------------- */
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ }
+
+/**********************************************************
+ Polling structure used by em28xx IR's
+ **********************************************************/
+
+struct em28xx_IR {
+ struct em28xx *dev;
+ struct input_dev *input;
+ struct ir_input_state ir;
+ char name[32];
+ char phys[32];
+
+ /* poll external decoder */
+ int polling;
+ struct work_struct work;
+ struct timer_list timer;
+ u32 last_gpio;
+ u32 mask_keycode;
+ u32 mask_keydown;
+ u32 mask_keyup;
+
+ int (*get_key)(struct em28xx_IR *);
+};
+
+/**********************************************************
+ I2C IR based get keycodes - should be used with ir-kbd-i2c
+ **********************************************************/
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
@@ -52,7 +82,7 @@ int em28xx_get_key_terratec(struct IR_i2
/* poll IR chip */
if (1 != i2c_master_recv(&ir->c, &b, 1)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
@@ -60,7 +90,7 @@ int em28xx_get_key_terratec(struct IR_i2
down, while 0xff indicates that no button is hold
down. 0xfe sequences are sometimes interrupted by 0xFF */
- dprintk("key %02x\n", b);
+ i2cdprintk("key %02x\n", b);
if (b == 0xff)
return 0;
@@ -73,7 +103,6 @@ int em28xx_get_key_terratec(struct IR_i2
*ir_raw = b;
return 1;
}
-
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
@@ -103,7 +132,7 @@ int em28xx_get_key_em_haup(struct IR_i2c
((buf[0]&0x10)>>3) | /* 0000 0010 */
((buf[0]&0x20)>>5); /* 0000 0001 */
- dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
+ i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
code, buf[0]);
/* return key */
@@ -120,11 +149,11 @@ int em28xx_get_key_pinnacle_usb_grey(str
/* poll IR chip */
if (3 != i2c_master_recv(&ir->c, buf, 3)) {
- dprintk("read error\n");
+ i2cdprintk("read error\n");
return -EIO;
}
- dprintk("key %02x\n", buf[2]&0x3f);
+ i2cdprintk("key %02x\n", buf[2]&0x3f);
if (buf[0] != 0x00)
return 0;
@@ -133,6 +162,215 @@ int em28xx_get_key_pinnacle_usb_grey(str
return 1;
}
+
+/**********************************************************
+ Poll based get keycode functions
+ **********************************************************/
+
+static int default_polling_getkey(struct em28xx_IR *ir)
+{
+ struct em28xx *dev = ir->dev;
+ int rc;
+ u32 msg;
+
+ /* Read key toggle, brand, and key code */
+ rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
+ (u8 *)&msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ return (int)(msg & 0x7fffffffl);
+}
+
+/**********************************************************
+ Polling code for em28xx
+ **********************************************************/
+
+static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+{
+ int gpio;
+ u32 data;
+
+ /* read gpio value */
+ gpio = ir->get_key(ir);
+ if (gpio < 0)
+ return;
+
+ if (gpio == ir->last_gpio)
+ return;
+ ir->last_gpio = gpio;
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ dprintk("irq gpio=0x%x code=%d | poll%s%s\n",
+ gpio, data,
+ (gpio & ir->mask_keydown) ? " down" : "",
+ (gpio & ir->mask_keyup) ? " up" : "");
+
+ /* Generate keyup/keydown events */
+ if (ir->mask_keydown) {
+ /* bit set on keydown */
+ if (gpio & ir->mask_keydown)
+ ir_input_keydown(ir->input, &ir->ir, data, data);
+ else
+ ir_input_nokey(ir->input, &ir->ir);
+ } else if (ir->mask_keyup) {
+ /* bit cleared on keydown */
+ if (!(gpio & ir->mask_keyup))
+ ir_input_keydown(ir->input, &ir->ir, data, data);
+ else
+ ir_input_nokey(ir->input, &ir->ir);
+ } else {
+ /* can't distinguish keydown/up :-/ */
+ ir_input_keydown(ir->input, &ir->ir, data, data);
+ ir_input_nokey(ir->input, &ir->ir);
+ }
+}
+
+static void ir_timer(unsigned long data)
+{
+ struct em28xx_IR *ir = (struct em28xx_IR *)data;
+
+ schedule_work(&ir->work);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void em28xx_ir_work(void *data)
+#else
+static void em28xx_ir_work(struct work_struct *work)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+ struct em28xx_IR *ir = data;
+#else
+ struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
+#endif
+
+ em28xx_ir_handle_key(ir);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+void em28xx_ir_start(struct em28xx_IR *ir)
+{
+ setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&ir->work, em28xx_ir_work, ir);
+#else
+ INIT_WORK(&ir->work, em28xx_ir_work);
+#endif
+ schedule_work(&ir->work);
+}
+
+static void em28xx_ir_stop(struct em28xx_IR *ir)
+{
+ del_timer_sync(&ir->timer);
+ flush_scheduled_work();
+}
+
+int em28xx_ir_init(struct em28xx *dev)
+{
+ struct em28xx_IR *ir;
+ struct input_dev *input_dev;
+ IR_KEYTAB_TYPE *ir_codes = NULL;
+ int ir_type = IR_TYPE_OTHER;
+ int err = -ENOMEM;
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ir || !input_dev)
+ goto err_out_free;
+
+ ir->input = input_dev;
+
+ /* */
+ ir->get_key = default_polling_getkey;
+ ir->polling = 50; /* ms */
+
+ /* detect & configure */
+ switch (dev->model) {
+#if 0
+ /* dummy entry, just as a reference, while we don't add
+ other entries here
+ */
+ case EM2820_BOARD_UNKNOWN:
+ ir_type = IR_TYPE_OTHER;
+ ir_codes = ir_codes_empty;
+ ir->mask_keycode = 0x007f0000;
+ break;
+ }
+#endif
+ }
+
+ if (NULL == ir_codes) {
+ err = -ENODEV;
+ goto err_out_free;
+ }
+
+ /* Get the current key status, to avoid adding an
+ unexistent key code */
+ ir->last_gpio = ir->get_key(ir);
+
+ /* init input device */
+ 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));
+
+ ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+ input_dev->id.bustype = BUS_USB;
+ input_dev->id.version = 1;
+ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ input_dev->dev.parent = &dev->udev->dev;
+#else
+ input_dev->cdev.dev = &dev->udev->dev;
+#endif
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+
+ em28xx_ir_start(ir);
+
+ /* all done */
+ err = input_register_device(ir->input);
+ if (err)
+ goto err_out_stop;
+
+ return 0;
+ err_out_stop:
+ em28xx_ir_stop(ir);
+ dev->ir = NULL;
+ err_out_free:
+ input_free_device(input_dev);
+ kfree(ir);
+ return err;
+}
+
+int em28xx_ir_fini(struct em28xx *dev)
+{
+ struct em28xx_IR *ir = dev->ir;
+
+ /* skip detach on non attached boards */
+ if (!ir)
+ return 0;
+
+ em28xx_ir_stop(ir);
+ input_unregister_device(ir->input);
+ kfree(ir);
+
+ /* done */
+ dev->ir = NULL;
+ return 0;
+}
+
+/**********************************************************
+ Handle Webcam snapshot button
+ **********************************************************/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
static void em28xx_query_sbutton(void *data)
@@ -232,9 +470,3 @@ void em28xx_deregister_snapshot_button(s
}
return;
}
-
-/* ----------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff -r c95684f09ecb -r f28c3e7bca61
linux/drivers/media/video/em28xx/em28xx-reg.h
--- a/linux/drivers/media/video/em28xx/em28xx-reg.h Wed Nov 12 02:28:57
2008 -0200
+++ b/linux/drivers/media/video/em28xx/em28xx-reg.h Wed Nov 12 09:41:29
2008 -0200
@@ -71,6 +71,13 @@
#define EM28XX_R42_AC97ADDR 0x42
#define EM28XX_R43_AC97BUSY 0x43
+#define EM28XX_R45_IR 0x45
+ /* 0x45 bit 7 - parity bit
+ bits 6-0 - count
+ 0x46 IR brand
+ 0x47 IR data
+ */
+
/* em202 registers */
#define EM28XX_R02_MASTER_AC97 0x02
#define EM28XX_R10_LINE_IN_AC97 0x10
diff -r c95684f09ecb -r f28c3e7bca61
linux/drivers/media/video/em28xx/em28xx-video.c
--- a/linux/drivers/media/video/em28xx/em28xx-video.c Wed Nov 12 02:28:57
2008 -0200
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c Wed Nov 12 09:41:29
2008 -0200
@@ -1659,6 +1659,10 @@ static void em28xx_release_resources(str
list_del(&dev->devlist);
if (dev->sbutton_input_dev)
em28xx_deregister_snapshot_button(dev);
+
+ if (dev->ir)
+ em28xx_ir_fini(dev);
+
if (dev->radio_dev) {
if (-1 != dev->radio_dev->minor)
video_unregister_device(dev->radio_dev);
diff -r c95684f09ecb -r f28c3e7bca61 linux/drivers/media/video/em28xx/em28xx.h
--- a/linux/drivers/media/video/em28xx/em28xx.h Wed Nov 12 02:28:57 2008 -0200
+++ b/linux/drivers/media/video/em28xx/em28xx.h Wed Nov 12 09:41:29 2008 -0200
@@ -390,6 +390,8 @@ struct em28xx {
unsigned int has_snapshot_button:1;
unsigned int valid:1; /* report for validated boards */
+ struct em28xx_IR *ir;
+
/* Some older em28xx chips needs a waiting time after writing */
unsigned int wait_after_write;
@@ -551,7 +553,6 @@ int em28xx_tuner_callback(void *ptr, int
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
/* Provided by em28xx-input.c */
-/* TODO: Check if the standard get_key handlers on ir-common can be used */
int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
@@ -559,6 +560,9 @@ void em28xx_register_snapshot_button(str
void em28xx_register_snapshot_button(struct em28xx *dev);
void em28xx_deregister_snapshot_button(struct em28xx *dev);
+int em28xx_ir_init(struct em28xx *dev);
+int em28xx_ir_fini(struct em28xx *dev);
+
/* printk macros */
#define em28xx_err(fmt, arg...) do {\
---
Patch is available at:
http://linuxtv.org/hg/v4l-dvb/rev/f28c3e7bca61d6eba42870b538e08dfe179331fc
_______________________________________________
linuxtv-commits mailing list
[email protected]
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits