|
@@ -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)) {
|