#include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
+#include <linux/async.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/bitops.h>
        }
 }
 
+/* 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.
        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);
                }
        }
 
-       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);
        /* 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);