The patch number 8625 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]>
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.

Priority: normal

Signed-off-by: Gilberto <[EMAIL PROTECTED]>
Signed-off-by: Mauro Carvalho Chehab <[EMAIL PROTECTED]>


---

 linux/drivers/media/video/saa7134/saa7134-input.c |  144 +++++++++++++-
 linux/include/media/ir-common.h                   |    5 
 2 files changed, 143 insertions(+), 6 deletions(-)

diff -r 7c739269c8b3 -r 0514410c6ed4 
linux/drivers/media/video/saa7134/saa7134-input.c
--- a/linux/drivers/media/video/saa7134/saa7134-input.c Tue Aug 05 10:00:02 
2008 -0300
+++ b/linux/drivers/media/video/saa7134/saa7134-input.c Tue Aug 05 10:03:17 
2008 -0300
@@ -62,8 +62,11 @@ MODULE_PARM_DESC(disable_other_ir, "disa
 #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_de
 {
        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
                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_d
        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_d
        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)",
@@ -679,8 +690,129 @@ static int saa7134_rc5_irq(struct saa713
        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) {
+#if 0
+                       if (!ngap && gap < 4000)
+                               break;
+#endif
+                       /* 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;
+}
diff -r 7c739269c8b3 -r 0514410c6ed4 linux/include/media/ir-common.h
--- a/linux/include/media/ir-common.h   Tue Aug 05 10:00:02 2008 -0300
+++ b/linux/include/media/ir-common.h   Tue Aug 05 10:03:17 2008 -0300
@@ -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,


---

Patch is available at: 
http://linuxtv.org/hg/v4l-dvb/rev/0514410c6ed410f949dd97f1fa7291a79108f9dd

_______________________________________________
linuxtv-commits mailing list
linuxtv-commits@linuxtv.org
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linuxtv-commits

Reply via email to