Commit 9d0624a7 authored by Mark Brown's avatar Mark Brown

ASoC: Run bias level changes for all DAPM contexts in parallel

As bias level changes can be quite time consuming and the bias changes
for multiple devices aren't strongly tied to each other (if anything it
can be advantageous to bring different devices up together) we can improve
the state transition time for multi-component systems by running the bias
level changes for all the devices in parallel. This is very simple to
achieve using the kernel async functionality so use that to schedule the
work.

This should have no practical effect for the overwhelming majority of
systems which have a single DAPM context - we'll bounce into another
thread to do the bias level change but otherwise everything will happen
in exactly the same order as it did before.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
parent ed5a4c47
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/async.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -1014,7 +1015,62 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) ...@@ -1014,7 +1015,62 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
} }
} }
/* Async callback run prior to DAPM sequences - brings to _PREPARE if
* they're changing state.
*/
static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
{
struct snd_soc_dapm_context *d = data;
int ret;
if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
if (ret != 0)
dev_err(d->dev,
"Failed to turn on bias: %d\n", ret);
}
/* If we're changing to all on or all off then prepare */
if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
(!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);
if (ret != 0)
dev_err(d->dev,
"Failed to prepare bias: %d\n", ret);
}
}
/* Async callback run prior to DAPM sequences - brings to their final
* state.
*/
static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
{
struct snd_soc_dapm_context *d = data;
int ret;
/* If we just powered the last thing off drop to standby bias */
if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
if (ret != 0)
dev_err(d->dev, "Failed to apply standby bias: %d\n",
ret);
}
/* If we're in standby and can support bias off then do that */
if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
if (ret != 0)
dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
}
/* If we just powered up then move to active bias */
if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);
if (ret != 0)
dev_err(d->dev, "Failed to apply active bias: %d\n",
ret);
}
}
/* /*
* Scan each dapm widget for complete audio path. * Scan each dapm widget for complete audio path.
...@@ -1032,7 +1088,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) ...@@ -1032,7 +1088,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
struct snd_soc_dapm_context *d; struct snd_soc_dapm_context *d;
LIST_HEAD(up_list); LIST_HEAD(up_list);
LIST_HEAD(down_list); LIST_HEAD(down_list);
int ret = 0; LIST_HEAD(async_domain);
int power; int power;
trace_snd_soc_dapm_start(card); trace_snd_soc_dapm_start(card);
...@@ -1110,25 +1166,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) ...@@ -1110,25 +1166,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
} }
} }
list_for_each_entry(d, &dapm->card->dapm_list, list) { /* Run all the bias changes in parallel */
if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { list_for_each_entry(d, &dapm->card->dapm_list, list)
ret = snd_soc_dapm_set_bias_level(d, async_schedule_domain(dapm_pre_sequence_async, d,
SND_SOC_BIAS_STANDBY); &async_domain);
if (ret != 0) async_synchronize_full_domain(&async_domain);
dev_err(d->dev,
"Failed to turn on bias: %d\n", ret);
}
/* If we're changing to all on or all off then prepare */
if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
(!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
ret = snd_soc_dapm_set_bias_level(d,
SND_SOC_BIAS_PREPARE);
if (ret != 0)
dev_err(d->dev,
"Failed to prepare bias: %d\n", ret);
}
}
/* Power down widgets first; try to avoid amplifying pops. */ /* Power down widgets first; try to avoid amplifying pops. */
dapm_seq_run(dapm, &down_list, event, false); dapm_seq_run(dapm, &down_list, event, false);
...@@ -1138,37 +1180,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) ...@@ -1138,37 +1180,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
/* Now power up. */ /* Now power up. */
dapm_seq_run(dapm, &up_list, event, true); dapm_seq_run(dapm, &up_list, event, true);
list_for_each_entry(d, &dapm->card->dapm_list, list) { /* Run all the bias changes in parallel */
/* If we just powered the last thing off drop to standby bias */ list_for_each_entry(d, &dapm->card->dapm_list, list)
if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { async_schedule_domain(dapm_post_sequence_async, d,
ret = snd_soc_dapm_set_bias_level(d, &async_domain);
SND_SOC_BIAS_STANDBY); async_synchronize_full_domain(&async_domain);
if (ret != 0)
dev_err(d->dev,
"Failed to apply standby bias: %d\n",
ret);
}
/* If we're in standby and can support bias off then do that */
if (d->bias_level == SND_SOC_BIAS_STANDBY &&
d->idle_bias_off) {
ret = snd_soc_dapm_set_bias_level(d,
SND_SOC_BIAS_OFF);
if (ret != 0)
dev_err(d->dev,
"Failed to turn off bias: %d\n", ret);
}
/* If we just powered up then move to active bias */
if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
ret = snd_soc_dapm_set_bias_level(d,
SND_SOC_BIAS_ON);
if (ret != 0)
dev_err(d->dev,
"Failed to apply active bias: %d\n",
ret);
}
}
pop_dbg(dapm->dev, card->pop_time, pop_dbg(dapm->dev, card->pop_time,
"DAPM sequencing finished, waiting %dms\n", card->pop_time); "DAPM sequencing finished, waiting %dms\n", card->pop_time);
......
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