diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1a932bbe8251..200ae7cd2f00 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -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. @@ -1032,7 +1088,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) struct snd_soc_dapm_context *d; LIST_HEAD(up_list); LIST_HEAD(down_list); - int ret = 0; + LIST_HEAD(async_domain); int power; trace_snd_soc_dapm_start(card); @@ -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) { - 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); - } - } + /* Run all the bias changes in parallel */ + list_for_each_entry(d, &dapm->card->dapm_list, list) + async_schedule_domain(dapm_pre_sequence_async, d, + &async_domain); + async_synchronize_full_domain(&async_domain); /* Power down widgets first; try to avoid amplifying pops. */ 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) /* Now power up. */ dapm_seq_run(dapm, &up_list, event, true); - list_for_each_entry(d, &dapm->card->dapm_list, list) { - /* 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); - } - } + /* Run all the bias changes in parallel */ + list_for_each_entry(d, &dapm->card->dapm_list, list) + async_schedule_domain(dapm_post_sequence_async, d, + &async_domain); + async_synchronize_full_domain(&async_domain); pop_dbg(dapm->dev, card->pop_time, "DAPM sequencing finished, waiting %dms\n", card->pop_time);