Commit ee0be4a9 authored by Ben Zhang's avatar Ben Zhang Committed by Mark Brown

ASoC: rt5677: Disable irq at suspend

The irq is disabled at suspend to avoid running the threaded irq
handler after the codec has been powered off. At resume, codec irq is
re-enabled and the interrupt status register is checked to see if
headphone has been pluggnd/unplugged while the device is suspended.

There is still a chance that the headphone gets enabled or disabled
after the codec is suspended. disable_irq syncs the threaded irq
handler, but soc-jack's threaded irq handler schedules a delayed
work to poll gpios (for debounce). This is still OK. The codec won't
be powered back on again because all audio paths have been suspended,
and there are no force enabled supply widgets (MICBIAS1 is disabled).
The gpio status read after codec power off could be wrong, so the
gpio values are checked again after resume.
Signed-off-by: default avatarBen Zhang <benzh@chromium.org>
Signed-off-by: default avatarCurtis Malainey <cujomalainey@chromium.org>
Link: https://lore.kernel.org/r/20191106011335.223061-8-cujomalainey@chromium.orgSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 3f81068d
...@@ -4949,6 +4949,11 @@ static int rt5677_suspend(struct snd_soc_component *component) ...@@ -4949,6 +4949,11 @@ static int rt5677_suspend(struct snd_soc_component *component)
{ {
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
if (rt5677->irq) {
cancel_delayed_work_sync(&rt5677->resume_irq_check);
disable_irq(rt5677->irq);
}
if (!rt5677->dsp_vad_en) { if (!rt5677->dsp_vad_en) {
regcache_cache_only(rt5677->regmap, true); regcache_cache_only(rt5677->regmap, true);
regcache_mark_dirty(rt5677->regmap); regcache_mark_dirty(rt5677->regmap);
...@@ -4977,6 +4982,11 @@ static int rt5677_resume(struct snd_soc_component *component) ...@@ -4977,6 +4982,11 @@ static int rt5677_resume(struct snd_soc_component *component)
regcache_sync(rt5677->regmap); regcache_sync(rt5677->regmap);
} }
if (rt5677->irq) {
enable_irq(rt5677->irq);
schedule_delayed_work(&rt5677->resume_irq_check, 0);
}
return 0; return 0;
} }
#else #else
...@@ -5375,6 +5385,39 @@ static irqreturn_t rt5677_irq(int unused, void *data) ...@@ -5375,6 +5385,39 @@ static irqreturn_t rt5677_irq(int unused, void *data)
return IRQ_NONE; return IRQ_NONE;
} }
static void rt5677_resume_irq_check(struct work_struct *work)
{
int i, virq;
struct rt5677_priv *rt5677 =
container_of(work, struct rt5677_priv, resume_irq_check.work);
/* This is needed to check and clear the interrupt status register
* at resume. If the headset is plugged/unplugged when the device is
* fully suspended, there won't be a rising edge at resume to trigger
* the interrupt. Without this, we miss the next unplug/plug event.
*/
rt5677_irq(0, rt5677);
/* Call all enabled jack detect irq handlers again. This is needed in
* addition to the above check for a corner case caused by jack gpio
* debounce. After codec irq is disabled at suspend, the delayed work
* scheduled by soc-jack may run and read wrong jack gpio values, since
* the regmap is in cache only mode. At resume, there is no irq because
* rt5677_irq has already ran and cleared the irq status at suspend.
* Without this explicit check, unplug the headset right after suspend
* starts, then after resume the headset is still shown as plugged in.
*/
mutex_lock(&rt5677->irq_lock);
for (i = 0; i < RT5677_IRQ_NUM; i++) {
if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) {
virq = irq_find_mapping(rt5677->domain, i);
if (virq)
handle_nested_irq(virq);
}
}
mutex_unlock(&rt5677->irq_lock);
}
static void rt5677_irq_bus_lock(struct irq_data *data) static void rt5677_irq_bus_lock(struct irq_data *data)
{ {
struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data); struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
...@@ -5450,6 +5493,7 @@ static int rt5677_init_irq(struct i2c_client *i2c) ...@@ -5450,6 +5493,7 @@ static int rt5677_init_irq(struct i2c_client *i2c)
} }
mutex_init(&rt5677->irq_lock); mutex_init(&rt5677->irq_lock);
INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check);
/* /*
* Select RC as the debounce clock so that GPIO works even when * Select RC as the debounce clock so that GPIO works even when
...@@ -5495,6 +5539,8 @@ static int rt5677_init_irq(struct i2c_client *i2c) ...@@ -5495,6 +5539,8 @@ static int rt5677_init_irq(struct i2c_client *i2c)
if (ret) if (ret)
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
rt5677->irq = i2c->irq;
return ret; return ret;
} }
......
...@@ -1856,6 +1856,8 @@ struct rt5677_priv { ...@@ -1856,6 +1856,8 @@ struct rt5677_priv {
struct irq_domain *domain; struct irq_domain *domain;
struct mutex irq_lock; struct mutex irq_lock;
unsigned int irq_en; unsigned int irq_en;
struct delayed_work resume_irq_check;
int irq;
int (*set_dsp_vad)(struct snd_soc_component *component, bool on); int (*set_dsp_vad)(struct snd_soc_component *component, bool on);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment