diff options
Diffstat (limited to 'drivers/media/rc/img-ir/img-ir-hw.c')
-rw-r--r-- | drivers/media/rc/img-ir/img-ir-hw.c | 84 |
1 files changed, 74 insertions, 10 deletions
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 2fd47c9bf5d..7bb71bc9f53 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -42,6 +42,12 @@ static struct img_ir_decoder *img_ir_decoders[] = { #ifdef CONFIG_IR_IMG_SANYO &img_ir_sanyo, #endif +#ifdef CONFIG_IR_IMG_RC5 + &img_ir_rc5, +#endif +#ifdef CONFIG_IR_IMG_RC6 + &img_ir_rc6, +#endif NULL }; @@ -52,6 +58,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 +553,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; @@ -806,20 +818,24 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) struct img_ir_priv_hw *hw = &priv->hw; const struct img_ir_decoder *dec = hw->decoder; int ret = IMG_IR_SCANCODE; - u32 scancode; - enum rc_type protocol = RC_TYPE_UNKNOWN; + struct img_ir_scancode_req request; + + request.protocol = RC_TYPE_UNKNOWN; + request.toggle = 0; if (dec->scancode) - ret = dec->scancode(len, raw, &protocol, &scancode, hw->enabled_protocols); + ret = dec->scancode(len, raw, hw->enabled_protocols, &request); else if (len >= 32) - scancode = (u32)raw; + request.scancode = (u32)raw; else if (len < 32) - scancode = (u32)raw & ((1 << len)-1); + request.scancode = (u32)raw & ((1 << len)-1); dev_dbg(priv->dev, "data (%u bits) = %#llx\n", len, (unsigned long long)raw); if (ret == IMG_IR_SCANCODE) { - dev_dbg(priv->dev, "decoded scan code %#x\n", scancode); - rc_keydown(hw->rdev, protocol, scancode, 0); + dev_dbg(priv->dev, "decoded scan code %#x, toggle %u\n", + request.scancode, request.toggle); + rc_keydown(hw->rdev, request.protocol, request.scancode, + request.toggle); img_ir_end_repeat(priv); } else if (ret == IMG_IR_REPEATCODE) { if (hw->mode == IMG_IR_M_REPEATING) { @@ -857,6 +873,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) @@ -922,15 +961,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; @@ -972,7 +1034,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; } @@ -991,6 +1053,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)) { |