diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/saa7134/saa7134-input.c | 226 | ||||
-rw-r--r-- | drivers/media/video/saa7134/saa7134.h | 2 |
2 files changed, 221 insertions, 7 deletions
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 72562b8..fbb2ff1 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -54,8 +54,11 @@ MODULE_PARM_DESC(disable_other_ir, "disable full codes of " #define i2cdprintk(fmt, arg...) if (ir_debug) \ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg) -/* Helper function for raw decoding at GPIO16 or GPIO18 */ +/* Helper functions for RC5 and NEC decoding at GPIO16 or GPIO18 */ +static int saa7134_rc5_irq(struct saa7134_dev *dev); +static int saa7134_nec_irq(struct saa7134_dev *dev); static int saa7134_raw_decode_irq(struct saa7134_dev *dev); +static void nec_task(unsigned long data); /* -------------------- GPIO generic keycode builder -------------------- */ @@ -394,8 +397,12 @@ void saa7134_input_irq(struct saa7134_dev *dev) if (!ir->running) return; - if (!ir->polling && !ir->raw_decode) { + if (ir->nec_gpio) { + saa7134_nec_irq(dev); + } else if (!ir->polling && !ir->rc5_gpio && !ir->raw_decode) { build_key(dev); + } else if (ir->rc5_gpio) { + saa7134_rc5_irq(dev); } else if (ir->raw_decode) { saa7134_raw_decode_irq(dev); } @@ -441,6 +448,17 @@ static int __saa7134_ir_start(void *priv) (unsigned long)dev); ir->timer.expires = jiffies + HZ; add_timer(&ir->timer); + } else if (ir->rc5_gpio) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = ir_rc5_timer_end; + ir->timer_end.data = (unsigned long)ir; + ir->shift_by = 2; + ir->start = 0x2; + ir->addr = 0x17; + ir->rc5_remote_gap = ir_rc5_remote_gap; + } else if (ir->nec_gpio) { + tasklet_init(&ir->tlet, nec_task, (unsigned long)dev); } else if (ir->raw_decode) { /* set timer_end for code completion */ init_timer(&ir->timer_end); @@ -468,6 +486,10 @@ static void __saa7134_ir_stop(void *priv) return; if (dev->remote->polling) del_timer_sync(&dev->remote->timer); + else if (ir->rc5_gpio) + del_timer_sync(&ir->timer_end); + else if (ir->nec_gpio) + tasklet_kill(&ir->tlet); else if (ir->raw_decode) { del_timer_sync(&ir->timer_end); ir->active = 0; @@ -509,6 +531,40 @@ static void saa7134_ir_close(struct rc_dev *rc) __saa7134_ir_stop(dev); } + +static int saa7134_ir_change_protocol(struct rc_dev *rc, u64 ir_type) +{ + struct saa7134_dev *dev = rc->priv; + struct card_ir *ir = dev->remote; + u32 nec_gpio, rc5_gpio; + + if (ir_type == IR_TYPE_RC5) { + dprintk("Changing protocol to RC5\n"); + nec_gpio = 0; + rc5_gpio = 1; + } else if (ir_type == IR_TYPE_NEC) { + dprintk("Changing protocol to NEC\n"); + nec_gpio = 1; + rc5_gpio = 0; + } else { + dprintk("IR protocol type %ud is not supported\n", + (unsigned)ir_type); + return -EINVAL; + } + + if (ir->running) { + saa7134_ir_stop(dev); + ir->nec_gpio = nec_gpio; + ir->rc5_gpio = rc5_gpio; + saa7134_ir_start(dev); + } else { + ir->nec_gpio = nec_gpio; + ir->rc5_gpio = rc5_gpio; + } + + return 0; +} + int saa7134_input_init1(struct saa7134_dev *dev) { struct card_ir *ir; @@ -518,7 +574,10 @@ int saa7134_input_init1(struct saa7134_dev *dev) u32 mask_keydown = 0; u32 mask_keyup = 0; int polling = 0; + int rc5_gpio = 0; + int nec_gpio = 0; int raw_decode = 0; + int allow_protocol_change = 0; int err; if (dev->has_remote != SAA7134_REMOTE_GPIO) @@ -708,10 +767,9 @@ int saa7134_input_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_ENCORE_ENLTV_FM53: ir_codes = RC_MAP_ENCORE_ENLTV_FM53; - mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ - mask_keyup = 0x0040000; - mask_keycode = 0xffff; - raw_decode = 1; + mask_keydown = 0x0040000; + mask_keycode = 0x00007f; + nec_gpio = 1; break; case SAA7134_BOARD_10MOONSTVMASTER3: ir_codes = RC_MAP_ENCORE_ENLTV; @@ -771,6 +829,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) ir->mask_keydown = mask_keydown; ir->mask_keyup = mask_keyup; ir->polling = polling; + ir->rc5_gpio = rc5_gpio; + ir->nec_gpio = nec_gpio; ir->raw_decode = raw_decode; /* init input device */ @@ -785,6 +845,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) if (raw_decode) rc->driver_type = RC_DRIVER_IR_RAW; + if (!raw_decode && allow_protocol_change) { + rc->allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; + rc->change_protocol = saa7134_ir_change_protocol; + } + rc->input_name = ir->name; rc->input_phys = ir->phys; rc->input_id.bustype = BUS_PCI; @@ -958,3 +1023,152 @@ static int saa7134_raw_decode_irq(struct saa7134_dev *dev) return 1; } + +static int saa7134_rc5_irq(struct saa7134_dev *dev) +{ + struct card_ir *ir = dev->remote; + struct timeval tv; + u32 gap; + unsigned long current_jiffies, timeout; + + /* get time of bit */ + current_jiffies = jiffies; + do_gettimeofday(&tv); + + /* avoid overflow with gap >1s */ + if (tv.tv_sec - ir->base_time.tv_sec > 1) { + gap = 200000; + } else { + gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + + tv.tv_usec - ir->base_time.tv_usec; + } + + /* active code => add bit */ + if (ir->active) { + /* only if in the code (otherwise spurious IRQ or timer + late) */ + if (ir->last_bit < 28) { + ir->last_bit = (gap - ir_rc5_remote_gap / 2) / + ir_rc5_remote_gap; + ir->code |= 1 << ir->last_bit; + } + /* starting new code */ + } else { + ir->active = 1; + ir->code = 0; + ir->base_time = tv; + ir->last_bit = 0; + + timeout = current_jiffies + (500 + 30 * HZ) / 1000; + mod_timer(&ir->timer_end, timeout); + } + + return 1; +} + +static void nec_task(unsigned long data) +{ + struct saa7134_dev *dev = (struct saa7134_dev *) data; + struct card_ir *ir; + struct timeval tv; + int count, pulse, oldpulse, gap; + u32 ircode = 0, not_code = 0; + int ngap = 0; + + if (!data) { + printk(KERN_ERR "saa713x/ir: Can't recover dev struct\n"); + /* GPIO will be kept disabled */ + return; + } + + ir = dev->remote; + + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + oldpulse = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown; + pulse = oldpulse; + + do_gettimeofday(&tv); + ir->base_time = tv; + + /* Decode NEC pulsecode. This code can take up to 76.5 ms to run. + Unfortunately, using IRQ to decode pulse didn't work, since it uses + a pulse train of 38KHz. This means one pulse on each 52 us + */ + do { + /* Wait until the end of pulse/space or 5 ms */ + for (count = 0; count < 500; count++) { + udelay(10); + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + pulse = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) + & ir->mask_keydown; + if (pulse != oldpulse) + break; + } + + do_gettimeofday(&tv); + gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + + tv.tv_usec - ir->base_time.tv_usec; + + if (!pulse) { + /* Bit 0 has 560 us, while bit 1 has 1120 us. + Do something only if bit == 1 + */ + if (ngap && (gap > 560 + 280)) { + unsigned int shift = ngap - 1; + + /* Address first, then command */ + if (shift < 8) { + shift += 8; + ircode |= 1 << shift; + } else if (shift < 16) { + not_code |= 1 << shift; + } else if (shift < 24) { + shift -= 16; + ircode |= 1 << shift; + } else { + shift -= 24; + not_code |= 1 << shift; + } + } + ngap++; + } + + + ir->base_time = tv; + + /* TIMEOUT - Long pulse */ + if (gap >= 5000) + break; + oldpulse = pulse; + } while (ngap < 32); + + if (ngap == 32) { + /* FIXME: should check if not_code == ~ircode */ + ir->code = ir_extract_bits(ircode, ir->mask_keycode); + + dprintk("scancode = 0x%02x (code = 0x%02x, notcode= 0x%02x)\n", + ir->code, ircode, not_code); + + ir_keydown(ir->dev, ir->code, 0); + } else { + dprintk("Repeat last key\n"); + ir_repeat(ir->dev); + } + + saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); +} + +static int saa7134_nec_irq(struct saa7134_dev *dev) +{ + struct card_ir *ir = dev->remote; + + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); + tasklet_schedule(&ir->tlet); + + return 1; +} diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index a6c726f..4e37b8b 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -37,7 +37,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <media/tuner.h> -#include <media/ir-core.h> +#include <media/ir-common.h> #include <media/ir-kbd-i2c.h> #include <media/videobuf-dma-sg.h> #include <sound/core.h> |