diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-11-12 09:41:29 -0200 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-11-12 09:41:29 -0200 |
commit | ae39f27bbdb9e06fee01813e98aef9499e409e59 (patch) | |
tree | c6839da4916b7a0ac2f18c6b9e37d7dab3a68a54 /linux/drivers/media | |
parent | 9fb707ad2afd41671c63bd89eed17ee20d37546e (diff) | |
download | mediapointer-dvb-s2-ae39f27bbdb9e06fee01813e98aef9499e409e59.tar.gz mediapointer-dvb-s2-ae39f27bbdb9e06fee01813e98aef9499e409e59.tar.bz2 |
em28xx: Properly implement poll support for IR's
From: Mauro Carvalho Chehab <mchehab@redhat.com>
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 <mchehab@redhat.com>
Diffstat (limited to 'linux/drivers/media')
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx-cards.c | 2 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx-input.c | 260 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx-reg.h | 7 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx-video.c | 4 | ||||
-rw-r--r-- | linux/drivers/media/video/em28xx/em28xx.h | 6 |
5 files changed, 264 insertions, 15 deletions
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 75024a252..8d92cb05b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1848,4 +1848,6 @@ void em28xx_card_setup(struct em28xx *dev) #endif em28xx_config_tuner(dev); + + em28xx_ir_init(dev); } diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index 8d21eaad9..c3c9cba30 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -39,12 +39,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) { @@ -52,7 +82,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; } @@ -60,7 +90,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; @@ -74,7 +104,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]; @@ -103,7 +132,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 */ @@ -120,11 +149,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; @@ -134,6 +163,215 @@ 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); +} + +#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) #else @@ -232,9 +470,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev) } return; } - -/* ---------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 50d1790d8..f67955a1b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -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 --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 19684ad45..09886d567 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1659,6 +1659,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); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index aa1588c22..1bccb9ee4 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -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 @@ 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, @@ -559,6 +560,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 {\ |