ASoC: Get the card directly from the DAPM context
[pandora-kernel.git] / sound / soc / soc-dapm.c
index 37b376f..8240ab8 100644 (file)
@@ -32,6 +32,7 @@
 #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>
@@ -125,17 +126,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
 
 /**
  * snd_soc_dapm_set_bias_level - set the bias level for the system
- * @card: audio device
+ * @dapm: DAPM context
  * @level: level to configure
  *
  * Configure the bias (power) levels for the SoC audio device.
  *
  * Returns 0 for success else error.
  */
-static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card,
-                                      struct snd_soc_dapm_context *dapm,
+static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
                                       enum snd_soc_bias_level level)
 {
+       struct snd_soc_card *card = dapm->card;
        int ret = 0;
 
        switch (level) {
@@ -367,7 +368,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
        int i, ret = 0;
        size_t name_len;
        struct snd_soc_dapm_path *path;
-       struct snd_card *card = dapm->codec->card->snd_card;
+       struct snd_card *card = dapm->card->snd_card;
 
        /* add kcontrol */
        for (i = 0; i < w->num_kcontrols; i++) {
@@ -429,7 +430,7 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
 {
        struct snd_soc_dapm_path *path = NULL;
        struct snd_kcontrol *kcontrol;
-       struct snd_card *card = dapm->codec->card->snd_card;
+       struct snd_card *card = dapm->card->snd_card;
        int ret = 0;
 
        if (!w->num_kcontrols) {
@@ -479,7 +480,7 @@ static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm)
  */
 static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
 {
-       int level = snd_power_get_state(widget->dapm->codec->card->snd_card);
+       int level = snd_power_get_state(widget->dapm->card->snd_card);
 
        switch (level) {
        case SNDRV_CTL_POWER_D3hot:
@@ -712,7 +713,15 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
                    !path->connected(path->source, path->sink))
                        continue;
 
-               if (path->sink && path->sink->power_check &&
+               if (!path->sink)
+                       continue;
+
+               if (path->sink->force) {
+                       power = 1;
+                       break;
+               }
+
+               if (path->sink->power_check &&
                    path->sink->power_check(path->sink)) {
                        power = 1;
                        break;
@@ -899,7 +908,8 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
                                for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
                                        if (sort[i] == cur_sort)
                                                cur_dapm->seq_notifier(cur_dapm,
-                                                                      i);
+                                                                      i,
+                                                                      cur_subseq);
                        }
 
                        INIT_LIST_HEAD(&pending);
@@ -968,7 +978,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
                for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
                        if (sort[i] == cur_sort)
                                cur_dapm->seq_notifier(cur_dapm,
-                                                      i);
+                                                      i, cur_subseq);
        }
 }
 
@@ -1005,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.
@@ -1018,12 +1083,12 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
  */
 static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
 {
-       struct snd_soc_card *card = dapm->codec->card;
+       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
        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);
@@ -1101,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(card, 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(card, 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);
@@ -1129,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(card, 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(card, 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(card, 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);
@@ -1217,7 +1242,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
 
                if (p->connect)
                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
-                                       " in  %s %s\n",
+                                       " in  \"%s\" \"%s\"\n",
                                        p->name ? p->name : "static",
                                        p->source->name);
        }
@@ -1227,7 +1252,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
 
                if (p->connect)
                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
-                                       " out %s %s\n",
+                                       " out \"%s\" \"%s\"\n",
                                        p->name ? p->name : "static",
                                        p->sink->name);
        }
@@ -1663,6 +1688,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
 {
        struct snd_soc_dapm_widget *w;
+       unsigned int val;
 
        list_for_each_entry(w, &dapm->card->widgets, list)
        {
@@ -1711,6 +1737,18 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
                case snd_soc_dapm_post:
                        break;
                }
+
+               /* Read the initial power state from the device */
+               if (w->reg >= 0) {
+                       val = snd_soc_read(w->codec, w->reg);
+                       val &= 1 << w->shift;
+                       if (w->invert)
+                               val = !val;
+
+                       if (val)
+                               w->power = 1;
+               }
+
                w->new = 1;
        }
 
@@ -1778,7 +1816,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned int val, val_mask;
+       unsigned int val;
        int connect, change;
        struct snd_soc_dapm_update update;
 
@@ -1786,13 +1824,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 
        if (invert)
                val = max - val;
-       val_mask = mask << shift;
+       mask = mask << shift;
        val = val << shift;
 
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
 
-       change = snd_soc_test_bits(widget->codec, reg, val_mask, val);
+       change = snd_soc_test_bits(widget->codec, reg, mask, val);
        if (change) {
                if (val)
                        /* new connection */
@@ -2241,7 +2279,6 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
        mutex_unlock(&codec->mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
 /**
  * snd_soc_dapm_enable_pin - enable pin.
@@ -2418,9 +2455,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
         * standby.
         */
        if (powerdown) {
-               snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE);
+               snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE);
                dapm_seq_run(dapm, &down_list, 0, false);
-               snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY);
+               snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY);
        }
 }
 
@@ -2433,7 +2470,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card)
 
        list_for_each_entry(codec, &card->codec_dev_list, list) {
                soc_dapm_shutdown_codec(&codec->dapm);
-               snd_soc_dapm_set_bias_level(card, &codec->dapm, SND_SOC_BIAS_OFF);
+               snd_soc_dapm_set_bias_level(&codec->dapm, SND_SOC_BIAS_OFF);
        }
 }