summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/codecs/rt5677.c71
1 files changed, 45 insertions, 26 deletions
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index b5ae61ff87af..202af7135f07 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5072,38 +5072,57 @@ static const struct rt5677_irq_desc rt5677_irq_descs[] = {
static irqreturn_t rt5677_irq(int unused, void *data)
{
struct rt5677_priv *rt5677 = data;
- int ret = 0, i, reg_irq, virq;
+ int ret = 0, loop, i, reg_irq, virq;
bool irq_fired = false;
mutex_lock(&rt5677->irq_lock);
- /* Read interrupt status */
- ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, &reg_irq);
- if (ret) {
- dev_err(rt5677->dev, "failed reading IRQ status: %d\n", ret);
- goto exit;
- }
- for (i = 0; i < RT5677_IRQ_NUM; i++) {
- if (reg_irq & rt5677_irq_descs[i].status_mask) {
- irq_fired = true;
- virq = irq_find_mapping(rt5677->domain, i);
- if (virq)
- handle_nested_irq(virq);
-
- /* Clear the interrupt by flipping the polarity of the
- * interrupt source line that fired
- */
- reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ /*
+ * Loop to handle interrupts until the last i2c read shows no pending
+ * irqs. The interrupt line is shared by multiple interrupt sources.
+ * After the regmap_read() below, a new interrupt source line may
+ * become high before the regmap_write() finishes, so there isn't a
+ * rising edge on the shared interrupt line for the new interrupt. Thus,
+ * the loop is needed to avoid missing irqs.
+ *
+ * A safeguard of 20 loops is used to avoid hanging in the irq handler
+ * if there is something wrong with the interrupt status update. The
+ * interrupt sources here are audio jack plug/unplug events which
+ * shouldn't happen at a high frequency for a long period of time.
+ * Empirically, more than 3 loops have never been seen.
+ */
+ for (loop = 0; loop < 20; loop++) {
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, &reg_irq);
+ if (ret) {
+ dev_err(rt5677->dev, "failed reading IRQ status: %d\n",
+ ret);
+ goto exit;
}
- }
- if (!irq_fired)
- goto exit;
-
- ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
- if (ret) {
- dev_err(rt5677->dev, "failed updating IRQ status: %d\n", ret);
- goto exit;
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ irq_fired = true;
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+
+ /* Clear the interrupt by flipping the polarity
+ * of the interrupt source line that fired
+ */
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ }
+ }
+ if (!irq_fired)
+ goto exit;
+
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(rt5677->dev, "failed updating IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
}
exit:
mutex_unlock(&rt5677->irq_lock);