V4L/DVB (9607): em28xx: Properly implement poll support for IR's
authorMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 12 Nov 2008 11:41:29 +0000 (08:41 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 29 Dec 2008 19:53:33 +0000 (17:53 -0200)
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.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/em28xx/em28xx-cards.c
drivers/media/video/em28xx/em28xx-input.c
drivers/media/video/em28xx/em28xx-reg.h
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h

index e4251117cbe3422bbd6442a7624a2bfa1ec56955..1d38bfaa3e7cb445bfa61ac5987268acaccf0564 100644 (file)
@@ -1843,4 +1843,6 @@ void em28xx_card_setup(struct em28xx *dev)
 #endif
 
        em28xx_config_tuner(dev);
+
+       em28xx_ir_init(dev);
 }
index eab3d9511af3397c8aaa42be2cd116264d1ca892..afd67607c028e57bb2616ea5542d51d4666391f1 100644 (file)
@@ -38,12 +38,42 @@ static unsigned int ir_debug;
 module_param(ir_debug, int, 0644);
 MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
 
-#define dprintk(fmt, arg...) \
+#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->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)
 {
@@ -51,7 +81,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
 
        /* poll IR chip */
        if (1 != i2c_master_recv(&ir->c, &b, 1)) {
-               dprintk("read error\n");
+               i2cdprintk("read error\n");
                return -EIO;
        }
 
@@ -59,7 +89,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
           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_i2c *ir, u32 *ir_key, u32 *ir_raw)
        return 1;
 }
 
-
 int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
 {
        unsigned char buf[2];
@@ -97,7 +126,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
                 ((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 */
@@ -114,11 +143,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
        /* 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;
 
@@ -128,6 +157,188 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
        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);
+}
+
+static void em28xx_ir_work(struct work_struct *work)
+{
+       struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
+
+       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);
+       INIT_WORK(&ir->work, em28xx_ir_work);
+       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 (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);
+
+       input_dev->dev.parent = &dev->udev->dev;
+       /* 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
+ **********************************************************/
+
 static void em28xx_query_sbutton(struct work_struct *work)
 {
        /* Poll the register and see if the button is depressed */
@@ -210,9 +421,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev)
        }
        return;
 }
-
-/* ----------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
index 50d1790d84fca013286916e88873d0db4cf28042..f67955a1b818fd309451815db9101ef6e05e92cb 100644 (file)
 #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
index 129bd06a2ea698f57046f6a901ec43c184feb39a..154a99c61ee64b9607a54058e1fec554a5048e09 100644 (file)
@@ -1603,6 +1603,10 @@ static void em28xx_release_resources(struct em28xx *dev)
        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);
index 1350a9cea7c5e667f4f8f44dabd3e4ec461d9867..73fd9e9e040342c0f57f56467d247b8bf7160202 100644 (file)
@@ -384,6 +384,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;
 
@@ -544,7 +546,6 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
 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,
@@ -552,6 +553,9 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
 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 {\