mirror of
https://github.com/adulau/aha.git
synced 2024-12-29 12:16:20 +00:00
V4L/DVB (8625): saa7134: Add NEC prococol IR decoding capability
This patch adds the capability of decoding NEC protocol, received via GPIO18 line. This GPIO port can trigger saa7134 IRQ. A future improvement would be to make it a little more generic to work also with GPIO16 line. A pure IRQ code didn't work, since some delays were introduced on the tests we did. A possible approach would be to use polling at a rate of 2.5 ms or less. If a new code were taken, a code similar to nec_task() could be used. However, this would add an extra overhead to kernel, and will consume more power. Due to that, we took an hybrid approach: an IRQ upper half to trigger when a new key is received and a bottom half to convert pulse-distance into a keycode. The bottom half is polling based, to improve performance. During the bottom half proccess, GPIO18 IRQ line is disabled, preventing IRQ reentrancy and improving performance a little bit. Thanks to Sistema Fenix (http://www.sistemafenix.com.br/) for sponsoring this development. Signed-off-by: Gilberto <gilberto@sistemafenix.com.br> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
d56dc61265
commit
622ecb3003
2 changed files with 139 additions and 6 deletions
|
@ -62,8 +62,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->c.name , ## arg)
|
||||
|
||||
/** rc5 functions */
|
||||
/* 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 void nec_task(unsigned long data);
|
||||
static void saa7134_nec_timer(unsigned long data);
|
||||
|
||||
/* -------------------- GPIO generic keycode builder -------------------- */
|
||||
|
||||
|
@ -280,7 +283,9 @@ void saa7134_input_irq(struct saa7134_dev *dev)
|
|||
{
|
||||
struct card_ir *ir = dev->remote;
|
||||
|
||||
if (!ir->polling && !ir->rc5_gpio) {
|
||||
if (ir->nec_gpio) {
|
||||
saa7134_nec_irq(dev);
|
||||
} else if (!ir->polling && !ir->rc5_gpio) {
|
||||
build_key(dev);
|
||||
} else if (ir->rc5_gpio) {
|
||||
saa7134_rc5_irq(dev);
|
||||
|
@ -316,6 +321,10 @@ void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir)
|
|||
ir->addr = 0x17;
|
||||
ir->rc5_key_timeout = ir_rc5_key_timeout;
|
||||
ir->rc5_remote_gap = ir_rc5_remote_gap;
|
||||
} else if (ir->nec_gpio) {
|
||||
setup_timer(&ir->timer_keyup, saa7134_nec_timer,
|
||||
(unsigned long)dev);
|
||||
tasklet_init(&ir->tlet, nec_task, (unsigned long)dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,6 +344,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
|
|||
u32 mask_keyup = 0;
|
||||
int polling = 0;
|
||||
int rc5_gpio = 0;
|
||||
int nec_gpio = 0;
|
||||
int ir_type = IR_TYPE_OTHER;
|
||||
int err;
|
||||
|
||||
|
@ -533,6 +543,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
|
|||
ir->mask_keyup = mask_keyup;
|
||||
ir->polling = polling;
|
||||
ir->rc5_gpio = rc5_gpio;
|
||||
ir->nec_gpio = nec_gpio;
|
||||
|
||||
/* init input device */
|
||||
snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
|
||||
|
@ -675,8 +686,125 @@ static int saa7134_rc5_irq(struct saa7134_dev *dev)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
|
||||
/* On NEC protocol, One has 2.25 ms, and zero has 1.125 ms
|
||||
The first pulse (start) has 9 + 4.5 ms
|
||||
*/
|
||||
|
||||
static void saa7134_nec_timer(unsigned long data)
|
||||
{
|
||||
struct saa7134_dev *dev = (struct saa7134_dev *) data;
|
||||
struct card_ir *ir = dev->remote;
|
||||
|
||||
dprintk("Cancel key repeat\n");
|
||||
|
||||
ir_input_nokey(ir->dev, &ir->ir);
|
||||
}
|
||||
|
||||
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_input_keydown(ir->dev, &ir->ir, ir->code, ir->code);
|
||||
} else
|
||||
dprintk("Repeat last key\n");
|
||||
|
||||
/* Keep repeating the last key */
|
||||
mod_timer(&ir->timer_keyup, jiffies + msecs_to_jiffies(150));
|
||||
|
||||
saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18);
|
||||
}
|
||||
|
||||
static int saa7134_nec_irq(struct saa7134_dev *dev)
|
||||
{
|
||||
struct card_ir *ir = dev->remote;
|
||||
|
||||
saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18);
|
||||
tasklet_schedule(&ir->tlet);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <linux/input.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define IR_TYPE_RC5 1
|
||||
#define IR_TYPE_PD 2 /* Pulse distance encoded IR */
|
||||
|
@ -85,6 +86,10 @@ struct card_ir {
|
|||
u32 code; /* raw code under construction */
|
||||
struct timeval base_time; /* time of last seen code */
|
||||
int active; /* building raw code */
|
||||
|
||||
/* NEC decoding */
|
||||
u32 nec_gpio;
|
||||
struct tasklet_struct tlet;
|
||||
};
|
||||
|
||||
void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
|
||||
|
|
Loading…
Reference in a new issue