[media] rc: img-ir: biphase enabled with workaround
authorSifan Naeem <sifan.naeem@imgtec.com>
Thu, 11 Dec 2014 20:06:24 +0000 (17:06 -0300)
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>
Tue, 23 Dec 2014 17:15:26 +0000 (15:15 -0200)
Biphase decoding in the current img-ir has got a quirk, where multiple
Interrupts are generated when an incomplete IR code is received by the
decoder.

Patch adds a work around for the quirk and enables biphase decoding.

Signed-off-by: Sifan Naeem <sifan.naeem@imgtec.com>
Acked-by: James Hogan <james.hogan@imgtec.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
drivers/media/rc/img-ir/img-ir-hw.c
drivers/media/rc/img-ir/img-ir-hw.h

index 9cecda73f98064ce24738c08731569ee9c95ea1b..5c32f05b32ec60f2268ea07aedc9cd1afa02b4d9 100644 (file)
@@ -52,6 +52,11 @@ static struct img_ir_decoder *img_ir_decoders[] = {
 
 #define IMG_IR_QUIRK_CODE_BROKEN       0x1     /* Decode is broken */
 #define IMG_IR_QUIRK_CODE_LEN_INCR     0x2     /* Bit length needs increment */
+/*
+ * The decoder generates rapid interrupts without actually having
+ * received any new data after an incomplete IR code is decoded.
+ */
+#define IMG_IR_QUIRK_CODE_IRQ          0x4
 
 /* functions for preprocessing timings, ensuring max is set */
 
@@ -542,6 +547,7 @@ static void img_ir_set_decoder(struct img_ir_priv *priv,
         */
        spin_unlock_irq(&priv->lock);
        del_timer_sync(&hw->end_timer);
+       del_timer_sync(&hw->suspend_timer);
        spin_lock_irq(&priv->lock);
 
        hw->stopping = false;
@@ -861,6 +867,29 @@ static void img_ir_end_timer(unsigned long arg)
        spin_unlock_irq(&priv->lock);
 }
 
+/*
+ * Timer function to re-enable the current protocol after it had been
+ * cleared when invalid interrupts were generated due to a quirk in the
+ * img-ir decoder.
+ */
+static void img_ir_suspend_timer(unsigned long arg)
+{
+       struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+       spin_lock_irq(&priv->lock);
+       /*
+        * Don't overwrite enabled valid/match IRQs if they have already been
+        * changed by e.g. a filter change.
+        */
+       if ((priv->hw.quirk_suspend_irq & IMG_IR_IRQ_EDGE) ==
+                               img_ir_read(priv, IMG_IR_IRQ_ENABLE))
+               img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+                                       priv->hw.quirk_suspend_irq);
+       /* enable */
+       img_ir_write(priv, IMG_IR_CONTROL, priv->hw.reg_timings.ctrl);
+       spin_unlock_irq(&priv->lock);
+}
+
 #ifdef CONFIG_COMMON_CLK
 static void img_ir_change_frequency(struct img_ir_priv *priv,
                                    struct clk_notifier_data *change)
@@ -926,15 +955,38 @@ void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
        if (!hw->decoder)
                return;
 
+       ct = hw->decoder->control.code_type;
+
        ir_status = img_ir_read(priv, IMG_IR_STATUS);
-       if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)))
+       if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) {
+               if (!(priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_IRQ) ||
+                               hw->stopping)
+                       return;
+               /*
+                * The below functionality is added as a work around to stop
+                * multiple Interrupts generated when an incomplete IR code is
+                * received by the decoder.
+                * The decoder generates rapid interrupts without actually
+                * having received any new data. After a single interrupt it's
+                * expected to clear up, but instead multiple interrupts are
+                * rapidly generated. only way to get out of this loop is to
+                * reset the control register after a short delay.
+                */
+               img_ir_write(priv, IMG_IR_CONTROL, 0);
+               hw->quirk_suspend_irq = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+               img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+                            hw->quirk_suspend_irq & IMG_IR_IRQ_EDGE);
+
+               /* Timer activated to re-enable the protocol. */
+               mod_timer(&hw->suspend_timer,
+                         jiffies + msecs_to_jiffies(5));
                return;
+       }
        ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
        img_ir_write(priv, IMG_IR_STATUS, ir_status);
 
        len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT;
        /* some versions report wrong length for certain code types */
-       ct = hw->decoder->control.code_type;
        if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR)
                ++len;
 
@@ -976,7 +1028,7 @@ static void img_ir_probe_hw_caps(struct img_ir_priv *priv)
        hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN]
                |= IMG_IR_QUIRK_CODE_LEN_INCR;
        hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE]
-               |= IMG_IR_QUIRK_CODE_BROKEN;
+               |= IMG_IR_QUIRK_CODE_IRQ;
        hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS]
                |= IMG_IR_QUIRK_CODE_BROKEN;
 }
@@ -995,6 +1047,8 @@ int img_ir_probe_hw(struct img_ir_priv *priv)
 
        /* Set up the end timer */
        setup_timer(&hw->end_timer, img_ir_end_timer, (unsigned long)priv);
+       setup_timer(&hw->suspend_timer, img_ir_suspend_timer,
+                               (unsigned long)priv);
 
        /* Register a clock notifier */
        if (!IS_ERR(priv->clk)) {
index beac3a6c4660e53cde54b0b173828ca07210d7bf..b31ffc947e4120d261758178dc80778e977e67e0 100644 (file)
@@ -218,6 +218,7 @@ enum img_ir_mode {
  * @rdev:              Remote control device
  * @clk_nb:            Notifier block for clock notify events.
  * @end_timer:         Timer until repeat timeout.
+ * @suspend_timer:     Timer to re-enable protocol.
  * @decoder:           Current decoder settings.
  * @enabled_protocols: Currently enabled protocols.
  * @clk_hz:            Current core clock rate in Hz.
@@ -228,12 +229,14 @@ enum img_ir_mode {
  * @stopping:          Indicates that decoder is being taken down and timers
  *                     should not be restarted.
  * @suspend_irqen:     Saved IRQ enable mask over suspend.
+ * @quirk_suspend_irq: Saved IRQ enable mask over quirk suspend timer.
  */
 struct img_ir_priv_hw {
        unsigned int                    ct_quirks[4];
        struct rc_dev                   *rdev;
        struct notifier_block           clk_nb;
        struct timer_list               end_timer;
+       struct timer_list               suspend_timer;
        const struct img_ir_decoder     *decoder;
        u64                             enabled_protocols;
        unsigned long                   clk_hz;
@@ -244,6 +247,7 @@ struct img_ir_priv_hw {
        enum img_ir_mode                mode;
        bool                            stopping;
        u32                             suspend_irqen;
+       u32                             quirk_suspend_irq;
 };
 
 static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)