]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - sound/soc/soc-dapm.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / sound / soc / soc-dapm.c
index c721502833bcbf424855c87bcf1fddf2805bbafe..1790f83ee6651b8051ff4950d2bf7540b4ea8011 100644 (file)
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include <sound/soc-dapm.h>
+#include <sound/soc.h>
 #include <sound/initval.h>
 
+#include <trace/events/asoc.h>
+
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
        [snd_soc_dapm_pre] = 0,
@@ -54,12 +56,14 @@ static int dapm_up_seq[] = {
        [snd_soc_dapm_aif_out] = 3,
        [snd_soc_dapm_mic] = 4,
        [snd_soc_dapm_mux] = 5,
+       [snd_soc_dapm_virt_mux] = 5,
        [snd_soc_dapm_value_mux] = 5,
        [snd_soc_dapm_dac] = 6,
        [snd_soc_dapm_mixer] = 7,
        [snd_soc_dapm_mixer_named_ctl] = 7,
        [snd_soc_dapm_pga] = 8,
        [snd_soc_dapm_adc] = 9,
+       [snd_soc_dapm_out_drv] = 10,
        [snd_soc_dapm_hp] = 10,
        [snd_soc_dapm_spk] = 10,
        [snd_soc_dapm_post] = 11,
@@ -70,6 +74,7 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_adc] = 1,
        [snd_soc_dapm_hp] = 2,
        [snd_soc_dapm_spk] = 2,
+       [snd_soc_dapm_out_drv] = 2,
        [snd_soc_dapm_pga] = 4,
        [snd_soc_dapm_mixer_named_ctl] = 5,
        [snd_soc_dapm_mixer] = 5,
@@ -77,6 +82,7 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_mic] = 7,
        [snd_soc_dapm_micbias] = 8,
        [snd_soc_dapm_mux] = 9,
+       [snd_soc_dapm_virt_mux] = 9,
        [snd_soc_dapm_value_mux] = 9,
        [snd_soc_dapm_aif_in] = 10,
        [snd_soc_dapm_aif_out] = 10,
@@ -90,17 +96,24 @@ static void pop_wait(u32 pop_time)
                schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
 }
 
-static void pop_dbg(u32 pop_time, const char *fmt, ...)
+static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
 {
        va_list args;
+       char *buf;
 
-       va_start(args, fmt);
+       if (!pop_time)
+               return;
 
-       if (pop_time) {
-               vprintk(fmt, args);
-       }
+       buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (buf == NULL)
+               return;
 
+       va_start(args, fmt);
+       vsnprintf(buf, PAGE_SIZE, fmt, args);
+       dev_info(dev, "%s", buf);
        va_end(args);
+
+       kfree(buf);
 }
 
 /* create a new dapm widget */
@@ -120,36 +133,45 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
  * Returns 0 for success else error.
  */
 static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card,
-               struct snd_soc_codec *codec, enum snd_soc_bias_level level)
+                                      struct snd_soc_dapm_context *dapm,
+                                      enum snd_soc_bias_level level)
 {
        int ret = 0;
 
        switch (level) {
        case SND_SOC_BIAS_ON:
-               dev_dbg(codec->dev, "Setting full bias\n");
+               dev_dbg(dapm->dev, "Setting full bias\n");
                break;
        case SND_SOC_BIAS_PREPARE:
-               dev_dbg(codec->dev, "Setting bias prepare\n");
+               dev_dbg(dapm->dev, "Setting bias prepare\n");
                break;
        case SND_SOC_BIAS_STANDBY:
-               dev_dbg(codec->dev, "Setting standby bias\n");
+               dev_dbg(dapm->dev, "Setting standby bias\n");
                break;
        case SND_SOC_BIAS_OFF:
-               dev_dbg(codec->dev, "Setting bias off\n");
+               dev_dbg(dapm->dev, "Setting bias off\n");
                break;
        default:
-               dev_err(codec->dev, "Setting invalid bias %d\n", level);
+               dev_err(dapm->dev, "Setting invalid bias %d\n", level);
                return -EINVAL;
        }
 
+       trace_snd_soc_bias_level_start(card, level);
+
        if (card && card->set_bias_level)
                ret = card->set_bias_level(card, level);
        if (ret == 0) {
-               if (codec->driver->set_bias_level)
-                       ret = codec->driver->set_bias_level(codec, level);
+               if (dapm->codec && dapm->codec->driver->set_bias_level)
+                       ret = dapm->codec->driver->set_bias_level(dapm->codec, level);
                else
-                       codec->bias_level = level;
+                       dapm->bias_level = level;
        }
+       if (ret == 0) {
+               if (card && card->set_bias_level_post)
+                       ret = card->set_bias_level_post(card, level);
+       }
+
+       trace_snd_soc_bias_level_done(card, level);
 
        return ret;
 }
@@ -196,6 +218,20 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
                }
        }
        break;
+       case snd_soc_dapm_virt_mux: {
+               struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+
+               p->connect = 0;
+               /* since a virtual mux has no backing registers to
+                * decide which path to connect, it will try to match
+                * with the first enumeration.  This is to ensure
+                * that the default mux choice (the first) will be
+                * correctly powered up during initialization.
+                */
+               if (!strcmp(p->name, e->texts[0]))
+                       p->connect = 1;
+       }
+       break;
        case snd_soc_dapm_value_mux: {
                struct soc_enum *e = (struct soc_enum *)
                        w->kcontrols[i].private_value;
@@ -217,6 +253,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        break;
        /* does not effect routing - always connected */
        case snd_soc_dapm_pga:
+       case snd_soc_dapm_out_drv:
        case snd_soc_dapm_output:
        case snd_soc_dapm_adc:
        case snd_soc_dapm_input:
@@ -241,7 +278,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
 }
 
 /* connect mux widget to its interconnecting audio paths */
-static int dapm_connect_mux(struct snd_soc_codec *codec,
+static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
        struct snd_soc_dapm_path *path, const char *control_name,
        const struct snd_kcontrol_new *kcontrol)
@@ -251,7 +288,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec,
 
        for (i = 0; i < e->max; i++) {
                if (!(strcmp(control_name, e->texts[i]))) {
-                       list_add(&path->list, &codec->dapm_paths);
+                       list_add(&path->list, &dapm->card->paths);
                        list_add(&path->list_sink, &dest->sources);
                        list_add(&path->list_source, &src->sinks);
                        path->name = (char*)e->texts[i];
@@ -264,7 +301,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec,
 }
 
 /* connect mixer widget to its interconnecting audio paths */
-static int dapm_connect_mixer(struct snd_soc_codec *codec,
+static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
        struct snd_soc_dapm_path *path, const char *control_name)
 {
@@ -273,7 +310,7 @@ static int dapm_connect_mixer(struct snd_soc_codec *codec,
        /* search for mixer kcontrol */
        for (i = 0; i < dest->num_kcontrols; i++) {
                if (!strcmp(control_name, dest->kcontrols[i].name)) {
-                       list_add(&path->list, &codec->dapm_paths);
+                       list_add(&path->list, &dapm->card->paths);
                        list_add(&path->list_sink, &dest->sources);
                        list_add(&path->list_source, &src->sinks);
                        path->name = dest->kcontrols[i].name;
@@ -290,6 +327,8 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
        int change, power;
        unsigned int old, new;
        struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_dapm_context *dapm = widget->dapm;
+       struct snd_soc_card *card = dapm->card;
 
        /* check for valid widgets */
        if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
@@ -309,24 +348,26 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
 
        change = old != new;
        if (change) {
-               pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n",
+               pop_dbg(dapm->dev, card->pop_time,
+                       "pop test %s : %s in %d ms\n",
                        widget->name, widget->power ? "on" : "off",
-                       codec->pop_time);
-               pop_wait(codec->pop_time);
+                       card->pop_time);
+               pop_wait(card->pop_time);
                snd_soc_write(codec, widget->reg, new);
        }
-       pr_debug("reg %x old %x new %x change %d\n", widget->reg,
-                old, new, change);
+       dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg,
+               old, new, change);
        return change;
 }
 
 /* create new dapm mixer control */
-static int dapm_new_mixer(struct snd_soc_codec *codec,
+static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *w)
 {
        int i, ret = 0;
        size_t name_len;
        struct snd_soc_dapm_path *path;
+       struct snd_card *card = dapm->codec->card->snd_card;
 
        /* add kcontrol */
        for (i = 0; i < w->num_kcontrols; i++) {
@@ -368,11 +409,11 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
 
                        path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
                                path->long_name);
-                       ret = snd_ctl_add(codec->card->snd_card, path->kcontrol);
+                       ret = snd_ctl_add(card, path->kcontrol);
                        if (ret < 0) {
-                               printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n",
-                                      path->long_name,
-                                      ret);
+                               dev_err(dapm->dev,
+                                       "asoc: failed to add dapm kcontrol %s: %d\n",
+                                       path->long_name, ret);
                                kfree(path->long_name);
                                path->long_name = NULL;
                                return ret;
@@ -383,20 +424,22 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
 }
 
 /* create new dapm mux control */
-static int dapm_new_mux(struct snd_soc_codec *codec,
+static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *w)
 {
        struct snd_soc_dapm_path *path = NULL;
        struct snd_kcontrol *kcontrol;
+       struct snd_card *card = dapm->codec->card->snd_card;
        int ret = 0;
 
        if (!w->num_kcontrols) {
-               printk(KERN_ERR "asoc: mux %s has no controls\n", w->name);
+               dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name);
                return -EINVAL;
        }
 
        kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
-       ret = snd_ctl_add(codec->card->snd_card, kcontrol);
+       ret = snd_ctl_add(card, kcontrol);
+
        if (ret < 0)
                goto err;
 
@@ -406,26 +449,27 @@ static int dapm_new_mux(struct snd_soc_codec *codec,
        return ret;
 
 err:
-       printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
+       dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name);
        return ret;
 }
 
 /* create new dapm volume control */
-static int dapm_new_pga(struct snd_soc_codec *codec,
+static int dapm_new_pga(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *w)
 {
        if (w->num_kcontrols)
-               pr_err("asoc: PGA controls not supported: '%s'\n", w->name);
+               dev_err(w->dapm->dev,
+                       "asoc: PGA controls not supported: '%s'\n", w->name);
 
        return 0;
 }
 
 /* reset 'walked' bit for each dapm path */
-static inline void dapm_clear_walk(struct snd_soc_codec *codec)
+static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm)
 {
        struct snd_soc_dapm_path *p;
 
-       list_for_each_entry(p, &codec->dapm_paths, list)
+       list_for_each_entry(p, &dapm->card->paths, list)
                p->walked = 0;
 }
 
@@ -435,13 +479,14 @@ static inline void dapm_clear_walk(struct snd_soc_codec *codec)
  */
 static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
 {
-       int level = snd_power_get_state(widget->codec->card->snd_card);
+       int level = snd_power_get_state(widget->dapm->codec->card->snd_card);
 
        switch (level) {
        case SNDRV_CTL_POWER_D3hot:
        case SNDRV_CTL_POWER_D3cold:
                if (widget->ignore_suspend)
-                       pr_debug("%s ignoring suspend\n", widget->name);
+                       dev_dbg(widget->dapm->dev, "%s ignoring suspend\n",
+                               widget->name);
                return widget->ignore_suspend;
        default:
                return 1;
@@ -572,7 +617,7 @@ static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
 
        /* call any power change event handlers */
        if (w->event)
-               pr_debug("power %s event for %s flags %x\n",
+               dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n",
                         w->power ? "on" : "off",
                         w->name, w->event_flags);
 
@@ -621,9 +666,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
        int in, out;
 
        in = is_connected_input_ep(w);
-       dapm_clear_walk(w->codec);
+       dapm_clear_walk(w->dapm);
        out = is_connected_output_ep(w);
-       dapm_clear_walk(w->codec);
+       dapm_clear_walk(w->dapm);
        return out != 0 && in != 0;
 }
 
@@ -634,7 +679,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
 
        if (w->active) {
                in = is_connected_input_ep(w);
-               dapm_clear_walk(w->codec);
+               dapm_clear_walk(w->dapm);
                return in != 0;
        } else {
                return dapm_generic_check_power(w);
@@ -648,7 +693,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
 
        if (w->active) {
                out = is_connected_output_ep(w);
-               dapm_clear_walk(w->codec);
+               dapm_clear_walk(w->dapm);
                return out != 0;
        } else {
                return dapm_generic_check_power(w);
@@ -667,14 +712,22 @@ 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;
                }
        }
 
-       dapm_clear_walk(w->codec);
+       dapm_clear_walk(w->dapm);
 
        return power;
 }
@@ -687,8 +740,8 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
                return sort[a->id] - sort[b->id];
        if (a->reg != b->reg)
                return a->reg - b->reg;
-       if (a->codec != b->codec)
-               return (unsigned long)a->codec - (unsigned long)b->codec;
+       if (a->dapm != b->dapm)
+               return (unsigned long)a->dapm - (unsigned long)b->dapm;
 
        return 0;
 }
@@ -709,12 +762,57 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
        list_add_tail(&new_widget->power_list, list);
 }
 
+static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm,
+                                struct snd_soc_dapm_widget *w, int event)
+{
+       struct snd_soc_card *card = dapm->card;
+       const char *ev_name;
+       int power, ret;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               ev_name = "PRE_PMU";
+               power = 1;
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               ev_name = "POST_PMU";
+               power = 1;
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               ev_name = "PRE_PMD";
+               power = 0;
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               ev_name = "POST_PMD";
+               power = 0;
+               break;
+       default:
+               BUG();
+               return;
+       }
+
+       if (w->power != power)
+               return;
+
+       if (w->event && (w->event_flags & event)) {
+               pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n",
+                       w->name, ev_name);
+               trace_snd_soc_dapm_widget_event_start(w, event);
+               ret = w->event(w, NULL, event);
+               trace_snd_soc_dapm_widget_event_done(w, event);
+               if (ret < 0)
+                       pr_err("%s: %s event failed: %d\n",
+                              ev_name, w->name, ret);
+       }
+}
+
 /* Apply the coalesced changes from a DAPM sequence */
-static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
+static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
                                   struct list_head *pending)
 {
+       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
-       int reg, power, ret;
+       int reg, power;
        unsigned int value = 0;
        unsigned int mask = 0;
        unsigned int cur_mask;
@@ -735,64 +833,26 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
                if (power)
                        value |= cur_mask;
 
-               pop_dbg(codec->pop_time,
+               pop_dbg(dapm->dev, card->pop_time,
                        "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
                        w->name, reg, value, mask);
 
-               /* power up pre event */
-               if (w->power && w->event &&
-                   (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
-                       pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
-                               w->name);
-                       ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
-                       if (ret < 0)
-                               pr_err("%s: pre event failed: %d\n",
-                                      w->name, ret);
-               }
-
-               /* power down pre event */
-               if (!w->power && w->event &&
-                   (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
-                       pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
-                               w->name);
-                       ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
-                       if (ret < 0)
-                               pr_err("%s: pre event failed: %d\n",
-                                      w->name, ret);
-               }
+               /* Check for events */
+               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU);
+               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD);
        }
 
        if (reg >= 0) {
-               pop_dbg(codec->pop_time,
+               pop_dbg(dapm->dev, card->pop_time,
                        "pop test : Applying 0x%x/0x%x to %x in %dms\n",
-                       value, mask, reg, codec->pop_time);
-               pop_wait(codec->pop_time);
-               snd_soc_update_bits(codec, reg, mask, value);
+                       value, mask, reg, card->pop_time);
+               pop_wait(card->pop_time);
+               snd_soc_update_bits(dapm->codec, reg, mask, value);
        }
 
        list_for_each_entry(w, pending, power_list) {
-               /* power up post event */
-               if (w->power && w->event &&
-                   (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
-                       pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
-                               w->name);
-                       ret = w->event(w,
-                                      NULL, SND_SOC_DAPM_POST_PMU);
-                       if (ret < 0)
-                               pr_err("%s: post event failed: %d\n",
-                                      w->name, ret);
-               }
-
-               /* power down post event */
-               if (!w->power && w->event &&
-                   (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
-                       pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
-                               w->name);
-                       ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
-                       if (ret < 0)
-                               pr_err("%s: post event failed: %d\n",
-                                      w->name, ret);
-               }
+               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU);
+               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD);
        }
 }
 
@@ -804,26 +864,29 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
  * Currently anything that requires more than a single write is not
  * handled.
  */
-static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
-                        int event, int sort[])
+static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
+                        struct list_head *list, int event, int sort[])
 {
        struct snd_soc_dapm_widget *w, *n;
        LIST_HEAD(pending);
        int cur_sort = -1;
        int cur_reg = SND_SOC_NOPM;
+       struct snd_soc_dapm_context *cur_dapm = NULL;
        int ret;
 
        list_for_each_entry_safe(w, n, list, power_list) {
                ret = 0;
 
                /* Do we need to apply any queued changes? */
-               if (sort[w->id] != cur_sort || w->reg != cur_reg) {
+               if (sort[w->id] != cur_sort || w->reg != cur_reg ||
+                   w->dapm != cur_dapm) {
                        if (!list_empty(&pending))
-                               dapm_seq_run_coalesced(codec, &pending);
+                               dapm_seq_run_coalesced(cur_dapm, &pending);
 
                        INIT_LIST_HEAD(&pending);
                        cur_sort = -1;
                        cur_reg = SND_SOC_NOPM;
+                       cur_dapm = NULL;
                }
 
                switch (w->id) {
@@ -867,19 +930,55 @@ static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
                        /* Queue it up for application */
                        cur_sort = sort[w->id];
                        cur_reg = w->reg;
+                       cur_dapm = w->dapm;
                        list_move(&w->power_list, &pending);
                        break;
                }
 
                if (ret < 0)
-                       pr_err("Failed to apply widget power: %d\n",
-                              ret);
+                       dev_err(w->dapm->dev,
+                               "Failed to apply widget power: %d\n", ret);
        }
 
        if (!list_empty(&pending))
-               dapm_seq_run_coalesced(codec, &pending);
+               dapm_seq_run_coalesced(cur_dapm, &pending);
 }
 
+static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
+{
+       struct snd_soc_dapm_update *update = dapm->update;
+       struct snd_soc_dapm_widget *w;
+       int ret;
+
+       if (!update)
+               return;
+
+       w = update->widget;
+
+       if (w->event &&
+           (w->event_flags & SND_SOC_DAPM_PRE_REG)) {
+               ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);
+               if (ret != 0)
+                       pr_err("%s DAPM pre-event failed: %d\n",
+                              w->name, ret);
+       }
+
+       ret = snd_soc_update_bits(w->codec, update->reg, update->mask,
+                                 update->val);
+       if (ret < 0)
+               pr_err("%s DAPM update failed: %d\n", w->name, ret);
+
+       if (w->event &&
+           (w->event_flags & SND_SOC_DAPM_POST_REG)) {
+               ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);
+               if (ret != 0)
+                       pr_err("%s DAPM post-event failed: %d\n",
+                              w->name, ret);
+       }
+}
+
+
+
 /*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
@@ -889,20 +988,26 @@ static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
  *  o Input pin to Output pin (bypass, sidetone)
  *  o DAC to ADC (loopback).
  */
-static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
+static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
 {
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = dapm->codec->card;
        struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_context *d;
        LIST_HEAD(up_list);
        LIST_HEAD(down_list);
        int ret = 0;
        int power;
-       int sys_power = 0;
+
+       trace_snd_soc_dapm_start(card);
+
+       list_for_each_entry(d, &card->dapm_list, list)
+               if (d->n_widgets)
+                       d->dev_power = 0;
 
        /* Check which widgets we need to power and store them in
         * lists indicating if they should be powered up or down.
         */
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       list_for_each_entry(w, &card->widgets, list) {
                switch (w->id) {
                case snd_soc_dapm_pre:
                        dapm_seq_insert(w, &down_list, dapm_down_seq);
@@ -920,11 +1025,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                        else
                                power = 1;
                        if (power)
-                               sys_power = 1;
+                               w->dapm->dev_power = 1;
 
                        if (w->power == power)
                                continue;
 
+                       trace_snd_soc_dapm_widget_power(w, power);
+
                        if (power)
                                dapm_seq_insert(w, &up_list, dapm_up_seq);
                        else
@@ -938,26 +1045,26 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
        /* If there are no DAPM widgets then try to figure out power from the
         * event type.
         */
-       if (list_empty(&codec->dapm_widgets)) {
+       if (!dapm->n_widgets) {
                switch (event) {
                case SND_SOC_DAPM_STREAM_START:
                case SND_SOC_DAPM_STREAM_RESUME:
-                       sys_power = 1;
+                       dapm->dev_power = 1;
                        break;
                case SND_SOC_DAPM_STREAM_STOP:
-                       sys_power = !!codec->active;
+                       dapm->dev_power = !!dapm->codec->active;
                        break;
                case SND_SOC_DAPM_STREAM_SUSPEND:
-                       sys_power = 0;
+                       dapm->dev_power = 0;
                        break;
                case SND_SOC_DAPM_STREAM_NOP:
-                       switch (codec->bias_level) {
+                       switch (dapm->bias_level) {
                                case SND_SOC_BIAS_STANDBY:
                                case SND_SOC_BIAS_OFF:
-                                       sys_power = 0;
+                                       dapm->dev_power = 0;
                                        break;
                                default:
-                                       sys_power = 1;
+                                       dapm->dev_power = 1;
                                        break;
                        }
                        break;
@@ -966,52 +1073,71 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                }
        }
 
-       if (sys_power && codec->bias_level == SND_SOC_BIAS_OFF) {
-               ret = snd_soc_dapm_set_bias_level(card, codec,
-                                                 SND_SOC_BIAS_STANDBY);
-               if (ret != 0)
-                       pr_err("Failed to turn on bias: %d\n", ret);
-       }
+       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 ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
-           (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
-               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_PREPARE);
-               if (ret != 0)
-                       pr_err("Failed to prepare 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);
+               }
        }
 
        /* Power down widgets first; try to avoid amplifying pops. */
-       dapm_seq_run(codec, &down_list, event, dapm_down_seq);
+       dapm_seq_run(dapm, &down_list, event, dapm_down_seq);
+
+       dapm_widget_update(dapm);
 
        /* Now power up. */
-       dapm_seq_run(codec, &up_list, event, dapm_up_seq);
+       dapm_seq_run(dapm, &up_list, event, dapm_up_seq);
+
+       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 just powered the last thing off drop to standby bias */
-       if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
-               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_STANDBY);
-               if (ret != 0)
-                       pr_err("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're in standby and can support bias off then do that */
-       if (codec->bias_level == SND_SOC_BIAS_STANDBY &&
-           codec->idle_bias_off) {
-               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_OFF);
-               if (ret != 0)
-                       pr_err("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);
+               }
        }
 
-       /* If we just powered up then move to active bias */
-       if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
-               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_ON);
-               if (ret != 0)
-                       pr_err("Failed to apply active bias: %d\n", ret);
-       }
+       pop_dbg(dapm->dev, card->pop_time,
+               "DAPM sequencing finished, waiting %dms\n", card->pop_time);
+       pop_wait(card->pop_time);
 
-       pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
-               codec->pop_time);
-       pop_wait(codec->pop_time);
+       trace_snd_soc_dapm_done(card);
 
        return 0;
 }
@@ -1038,9 +1164,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                return -ENOMEM;
 
        in = is_connected_input_ep(w);
-       dapm_clear_walk(w->codec);
+       dapm_clear_walk(w->dapm);
        out = is_connected_output_ep(w);
-       dapm_clear_walk(w->codec);
+       dapm_clear_walk(w->dapm);
 
        ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d",
                       w->name, w->power ? "On" : "Off", in, out);
@@ -1090,29 +1216,29 @@ static const struct file_operations dapm_widget_power_fops = {
        .llseek = default_llseek,
 };
 
-void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
 {
        struct snd_soc_dapm_widget *w;
        struct dentry *d;
 
-       if (!codec->debugfs_dapm)
+       if (!dapm->debugfs_dapm)
                return;
 
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
-               if (!w->name)
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (!w->name || w->dapm != dapm)
                        continue;
 
                d = debugfs_create_file(w->name, 0444,
-                                       codec->debugfs_dapm, w,
+                                       dapm->debugfs_dapm, w,
                                        &dapm_widget_power_fops);
                if (!d)
-                       printk(KERN_WARNING
-                              "ASoC: Failed to create %s debugfs file\n",
-                              w->name);
+                       dev_warn(w->dapm->dev,
+                               "ASoC: Failed to create %s debugfs file\n",
+                               w->name);
        }
 }
 #else
-void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
 {
 }
 #endif
@@ -1126,6 +1252,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
        int found = 0;
 
        if (widget->id != snd_soc_dapm_mux &&
+           widget->id != snd_soc_dapm_virt_mux &&
            widget->id != snd_soc_dapm_value_mux)
                return -ENODEV;
 
@@ -1133,7 +1260,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
                return 0;
 
        /* find dapm widget path assoc with kcontrol */
-       list_for_each_entry(path, &widget->codec->dapm_paths, list) {
+       list_for_each_entry(path, &widget->dapm->card->paths, list) {
                if (path->kcontrol != kcontrol)
                        continue;
 
@@ -1149,7 +1276,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
        }
 
        if (found)
-               dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+               dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
 
        return 0;
 }
@@ -1167,7 +1294,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
                return -ENODEV;
 
        /* find dapm widget path assoc with kcontrol */
-       list_for_each_entry(path, &widget->codec->dapm_paths, list) {
+       list_for_each_entry(path, &widget->dapm->card->paths, list) {
                if (path->kcontrol != kcontrol)
                        continue;
 
@@ -1178,7 +1305,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
        }
 
        if (found)
-               dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
+               dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
 
        return 0;
 }
@@ -1194,7 +1321,9 @@ static ssize_t dapm_widget_show(struct device *dev,
        int count = 0;
        char *state = "not set";
 
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       list_for_each_entry(w, &codec->card->widgets, list) {
+               if (w->dapm != &codec->dapm)
+                       continue;
 
                /* only display widgets that burnm power */
                switch (w->id) {
@@ -1206,6 +1335,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                case snd_soc_dapm_dac:
                case snd_soc_dapm_adc:
                case snd_soc_dapm_pga:
+               case snd_soc_dapm_out_drv:
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
                case snd_soc_dapm_supply:
@@ -1218,7 +1348,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                }
        }
 
-       switch (codec->bias_level) {
+       switch (codec->dapm.bias_level) {
        case SND_SOC_BIAS_ON:
                state = "On";
                break;
@@ -1250,31 +1380,50 @@ static void snd_soc_dapm_sys_remove(struct device *dev)
 }
 
 /* free all dapm widgets and resources */
-static void dapm_free_widgets(struct snd_soc_codec *codec)
+static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
 {
        struct snd_soc_dapm_widget *w, *next_w;
        struct snd_soc_dapm_path *p, *next_p;
 
-       list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
+       list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) {
+               if (w->dapm != dapm)
+                       continue;
                list_del(&w->list);
+               /*
+                * remove source and sink paths associated to this widget.
+                * While removing the path, remove reference to it from both
+                * source and sink widgets so that path is removed only once.
+                */
+               list_for_each_entry_safe(p, next_p, &w->sources, list_sink) {
+                       list_del(&p->list_sink);
+                       list_del(&p->list_source);
+                       list_del(&p->list);
+                       kfree(p->long_name);
+                       kfree(p);
+               }
+               list_for_each_entry_safe(p, next_p, &w->sinks, list_source) {
+                       list_del(&p->list_sink);
+                       list_del(&p->list_source);
+                       list_del(&p->list);
+                       kfree(p->long_name);
+                       kfree(p);
+               }
+               kfree(w->name);
                kfree(w);
        }
-
-       list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
-               list_del(&p->list);
-               kfree(p->long_name);
-               kfree(p);
-       }
 }
 
-static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
+static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
                                const char *pin, int status)
 {
        struct snd_soc_dapm_widget *w;
 
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (w->dapm != dapm)
+                       continue;
                if (!strcmp(w->name, pin)) {
-                       pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+                       dev_dbg(w->dapm->dev, "dapm: pin %s = %d\n",
+                               pin, status);
                        w->connected = status;
                        /* Allow disabling of forced pins */
                        if (status == 0)
@@ -1283,46 +1432,72 @@ static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
                }
        }
 
-       pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+       dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
        return -EINVAL;
 }
 
 /**
  * snd_soc_dapm_sync - scan and power dapm paths
- * @codec: audio codec
+ * @dapm: DAPM context
  *
  * Walks all dapm audio paths and powers widgets according to their
  * stream or path usage.
  *
  * Returns 0 for success.
  */
-int snd_soc_dapm_sync(struct snd_soc_codec *codec)
+int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 {
-       return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+       return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
-static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
+static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
                                  const struct snd_soc_dapm_route *route)
 {
        struct snd_soc_dapm_path *path;
        struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
-       const char *sink = route->sink;
+       struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
+       const char *sink;
        const char *control = route->control;
-       const char *source = route->source;
+       const char *source;
+       char prefixed_sink[80];
+       char prefixed_source[80];
        int ret = 0;
 
-       /* find src and dest widgets */
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       if (dapm->codec->name_prefix) {
+               snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
+                        dapm->codec->name_prefix, route->sink);
+               sink = prefixed_sink;
+               snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
+                        dapm->codec->name_prefix, route->source);
+               source = prefixed_source;
+       } else {
+               sink = route->sink;
+               source = route->source;
+       }
 
+       /*
+        * find src and dest widgets over all widgets but favor a widget from
+        * current DAPM context
+        */
+       list_for_each_entry(w, &dapm->card->widgets, list) {
                if (!wsink && !(strcmp(w->name, sink))) {
-                       wsink = w;
+                       wtsink = w;
+                       if (w->dapm == dapm)
+                               wsink = w;
                        continue;
                }
                if (!wsource && !(strcmp(w->name, source))) {
-                       wsource = w;
+                       wtsource = w;
+                       if (w->dapm == dapm)
+                               wsource = w;
                }
        }
+       /* use widget from another DAPM context if not found from this */
+       if (!wsink)
+               wsink = wtsink;
+       if (!wsource)
+               wsource = wtsource;
 
        if (wsource == NULL || wsink == NULL)
                return -ENODEV;
@@ -1356,7 +1531,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
 
        /* connect static paths */
        if (control == NULL) {
-               list_add(&path->list, &codec->dapm_paths);
+               list_add(&path->list, &dapm->card->paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
                path->connect = 1;
@@ -1368,6 +1543,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        case snd_soc_dapm_adc:
        case snd_soc_dapm_dac:
        case snd_soc_dapm_pga:
+       case snd_soc_dapm_out_drv:
        case snd_soc_dapm_input:
        case snd_soc_dapm_output:
        case snd_soc_dapm_micbias:
@@ -1377,14 +1553,15 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        case snd_soc_dapm_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
-               list_add(&path->list, &codec->dapm_paths);
+               list_add(&path->list, &dapm->card->paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
                path->connect = 1;
                return 0;
        case snd_soc_dapm_mux:
+       case snd_soc_dapm_virt_mux:
        case snd_soc_dapm_value_mux:
-               ret = dapm_connect_mux(codec, wsource, wsink, path, control,
+               ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
                        &wsink->kcontrols[0]);
                if (ret != 0)
                        goto err;
@@ -1392,7 +1569,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        case snd_soc_dapm_switch:
        case snd_soc_dapm_mixer:
        case snd_soc_dapm_mixer_named_ctl:
-               ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
+               ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
                if (ret != 0)
                        goto err;
                break;
@@ -1400,7 +1577,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        case snd_soc_dapm_mic:
        case snd_soc_dapm_line:
        case snd_soc_dapm_spk:
-               list_add(&path->list, &codec->dapm_paths);
+               list_add(&path->list, &dapm->card->paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
                path->connect = 0;
@@ -1409,15 +1586,15 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
        return 0;
 
 err:
-       printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
-               control, sink);
+       dev_warn(dapm->dev, "asoc: no dapm match for %s --> %s --> %s\n",
+                source, control, sink);
        kfree(path);
        return ret;
 }
 
 /**
  * snd_soc_dapm_add_routes - Add routes between DAPM widgets
- * @codec: codec
+ * @dapm: DAPM context
  * @route: audio routes
  * @num: number of routes
  *
@@ -1428,17 +1605,16 @@ err:
  * Returns 0 for success else error. On error all resources can be freed
  * with a call to snd_soc_card_free().
  */
-int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
+int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
                            const struct snd_soc_dapm_route *route, int num)
 {
        int i, ret;
 
        for (i = 0; i < num; i++) {
-               ret = snd_soc_dapm_add_route(codec, route);
+               ret = snd_soc_dapm_add_route(dapm, route);
                if (ret < 0) {
-                       printk(KERN_ERR "Failed to add route %s->%s\n",
-                              route->source,
-                              route->sink);
+                       dev_err(dapm->dev, "Failed to add route %s->%s\n",
+                               route->source, route->sink);
                        return ret;
                }
                route++;
@@ -1450,17 +1626,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 
 /**
  * snd_soc_dapm_new_widgets - add new dapm widgets
- * @codec: audio codec
+ * @dapm: DAPM context
  *
  * Checks the codec for any new dapm widgets and creates them if found.
  *
  * Returns 0 for success.
  */
-int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
+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, &codec->dapm_widgets, list)
+       list_for_each_entry(w, &dapm->card->widgets, list)
        {
                if (w->new)
                        continue;
@@ -1470,12 +1647,13 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
                        w->power_check = dapm_generic_check_power;
-                       dapm_new_mixer(codec, w);
+                       dapm_new_mixer(dapm, w);
                        break;
                case snd_soc_dapm_mux:
+               case snd_soc_dapm_virt_mux:
                case snd_soc_dapm_value_mux:
                        w->power_check = dapm_generic_check_power;
-                       dapm_new_mux(codec, w);
+                       dapm_new_mux(dapm, w);
                        break;
                case snd_soc_dapm_adc:
                case snd_soc_dapm_aif_out:
@@ -1486,8 +1664,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                        w->power_check = dapm_dac_check_power;
                        break;
                case snd_soc_dapm_pga:
+               case snd_soc_dapm_out_drv:
                        w->power_check = dapm_generic_check_power;
-                       dapm_new_pga(codec, w);
+                       dapm_new_pga(dapm, w);
                        break;
                case snd_soc_dapm_input:
                case snd_soc_dapm_output:
@@ -1505,10 +1684,22 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
                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;
        }
 
-       dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
+       dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -1569,32 +1760,25 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned int val, val2, val_mask;
-       int connect;
-       int ret;
+       unsigned int val;
+       int connect, change;
+       struct snd_soc_dapm_update update;
 
        val = (ucontrol->value.integer.value[0] & mask);
 
        if (invert)
                val = max - val;
-       val_mask = mask << shift;
+       mask = mask << shift;
        val = val << shift;
-       if (shift != rshift) {
-               val2 = (ucontrol->value.integer.value[1] & mask);
-               if (invert)
-                       val2 = max - val2;
-               val_mask |= mask << rshift;
-               val |= val2 << rshift;
-       }
 
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
 
-       if (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 */
                        connect = invert ? 0:1;
@@ -1602,28 +1786,20 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                        /* old connection must be powered down */
                        connect = invert ? 1:0;
 
+               update.kcontrol = kcontrol;
+               update.widget = widget;
+               update.reg = reg;
+               update.mask = mask;
+               update.val = val;
+               widget->dapm->update = &update;
+
                dapm_mixer_update_power(widget, kcontrol, connect);
+
+               widget->dapm->update = NULL;
        }
 
-       if (widget->event) {
-               if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-                       ret = widget->event(widget, kcontrol,
-                                               SND_SOC_DAPM_PRE_REG);
-                       if (ret < 0) {
-                               ret = 1;
-                               goto out;
-                       }
-               }
-               ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
-               if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-                       ret = widget->event(widget, kcontrol,
-                                               SND_SOC_DAPM_POST_REG);
-       } else
-               ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
-
-out:
        mutex_unlock(&widget->codec->mutex);
-       return ret;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
 
@@ -1671,7 +1847,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask, bitmask;
-       int ret = 0;
+       struct snd_soc_dapm_update update;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
                ;
@@ -1690,24 +1866,20 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
        change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
-       dapm_mux_update_power(widget, kcontrol, change, mux, e);
 
-       if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-               ret = widget->event(widget,
-                                   kcontrol, SND_SOC_DAPM_PRE_REG);
-               if (ret < 0)
-                       goto out;
-       }
+       update.kcontrol = kcontrol;
+       update.widget = widget;
+       update.reg = e->reg;
+       update.mask = mask;
+       update.val = val;
+       widget->dapm->update = &update;
 
-       ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+       dapm_mux_update_power(widget, kcontrol, change, mux, e);
 
-       if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-               ret = widget->event(widget,
-                                   kcontrol, SND_SOC_DAPM_POST_REG);
+       widget->dapm->update = NULL;
 
-out:
        mutex_unlock(&widget->codec->mutex);
-       return ret;
+       return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 
@@ -1819,7 +1991,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask;
-       int ret = 0;
+       struct snd_soc_dapm_update update;
 
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
                return -EINVAL;
@@ -1836,24 +2008,20 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
        change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
-       dapm_mux_update_power(widget, kcontrol, change, mux, e);
 
-       if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-               ret = widget->event(widget,
-                                   kcontrol, SND_SOC_DAPM_PRE_REG);
-               if (ret < 0)
-                       goto out;
-       }
+       update.kcontrol = kcontrol;
+       update.widget = widget;
+       update.reg = e->reg;
+       update.mask = mask;
+       update.val = val;
+       widget->dapm->update = &update;
 
-       ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+       dapm_mux_update_power(widget, kcontrol, change, mux, e);
 
-       if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-               ret = widget->event(widget,
-                                   kcontrol, SND_SOC_DAPM_POST_REG);
+       widget->dapm->update = NULL;
 
-out:
        mutex_unlock(&widget->codec->mutex);
-       return ret;
+       return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
 
@@ -1892,7 +2060,7 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
        mutex_lock(&codec->mutex);
 
        ucontrol->value.integer.value[0] =
-               snd_soc_dapm_get_pin_status(codec, pin);
+               snd_soc_dapm_get_pin_status(&codec->dapm, pin);
 
        mutex_unlock(&codec->mutex);
 
@@ -1915,11 +2083,11 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
        mutex_lock(&codec->mutex);
 
        if (ucontrol->value.integer.value[0])
-               snd_soc_dapm_enable_pin(codec, pin);
+               snd_soc_dapm_enable_pin(&codec->dapm, pin);
        else
-               snd_soc_dapm_disable_pin(codec, pin);
+               snd_soc_dapm_disable_pin(&codec->dapm, pin);
 
-       snd_soc_dapm_sync(codec);
+       snd_soc_dapm_sync(&codec->dapm);
 
        mutex_unlock(&codec->mutex);
 
@@ -1929,26 +2097,43 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
 
 /**
  * snd_soc_dapm_new_control - create new dapm control
- * @codec: audio codec
+ * @dapm: DAPM context
  * @widget: widget template
  *
  * Creates a new dapm control based upon the template.
  *
  * Returns 0 for success else error.
  */
-int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
+int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        const struct snd_soc_dapm_widget *widget)
 {
        struct snd_soc_dapm_widget *w;
+       size_t name_len;
 
        if ((w = dapm_cnew_widget(widget)) == NULL)
                return -ENOMEM;
 
-       w->codec = codec;
+       name_len = strlen(widget->name) + 1;
+       if (dapm->codec->name_prefix)
+               name_len += 1 + strlen(dapm->codec->name_prefix);
+       w->name = kmalloc(name_len, GFP_KERNEL);
+       if (w->name == NULL) {
+               kfree(w);
+               return -ENOMEM;
+       }
+       if (dapm->codec->name_prefix)
+               snprintf(w->name, name_len, "%s %s",
+                       dapm->codec->name_prefix, widget->name);
+       else
+               snprintf(w->name, name_len, "%s", widget->name);
+
+       dapm->n_widgets++;
+       w->dapm = dapm;
+       w->codec = dapm->codec;
        INIT_LIST_HEAD(&w->sources);
        INIT_LIST_HEAD(&w->sinks);
        INIT_LIST_HEAD(&w->list);
-       list_add(&w->list, &codec->dapm_widgets);
+       list_add(&w->list, &dapm->card->widgets);
 
        /* machine layer set ups unconnected pins and insertions */
        w->connected = 1;
@@ -1958,7 +2143,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
 
 /**
  * snd_soc_dapm_new_controls - create new dapm controls
- * @codec: audio codec
+ * @dapm: DAPM context
  * @widget: widget array
  * @num: number of widgets
  *
@@ -1966,18 +2151,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
  *
  * Returns 0 for success else error.
  */
-int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
+int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
        const struct snd_soc_dapm_widget *widget,
        int num)
 {
        int i, ret;
 
        for (i = 0; i < num; i++) {
-               ret = snd_soc_dapm_new_control(codec, widget);
+               ret = snd_soc_dapm_new_control(dapm, widget);
                if (ret < 0) {
-                       printk(KERN_ERR
-                              "ASoC: Failed to create DAPM control %s: %d\n",
-                              widget->name, ret);
+                       dev_err(dapm->dev,
+                               "ASoC: Failed to create DAPM control %s: %d\n",
+                               widget->name, ret);
                        return ret;
                }
                widget++;
@@ -1986,34 +2171,17 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
 
-
-/**
- * snd_soc_dapm_stream_event - send a stream event to the dapm core
- * @codec: audio codec
- * @stream: stream name
- * @event: stream event
- *
- * Sends a stream event to the dapm core. The core then makes any
- * necessary widget power changes.
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
+static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
        const char *stream, int event)
 {
-       struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_widget *w;
 
-       if (stream == NULL)
-               return 0;
-
-       mutex_lock(&codec->mutex);
-       list_for_each_entry(w, &codec->dapm_widgets, list)
+       list_for_each_entry(w, &dapm->card->widgets, list)
        {
-               if (!w->sname)
+               if (!w->sname || w->dapm != dapm)
                        continue;
-               pr_debug("widget %s\n %s stream %s event %d\n",
-                        w->name, w->sname, stream, event);
+               dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n",
+                       w->name, w->sname, stream, event);
                if (strstr(w->sname, stream)) {
                        switch(event) {
                        case SND_SOC_DAPM_STREAM_START:
@@ -2031,7 +2199,30 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
                }
        }
 
-       dapm_power_widgets(codec, event);
+       dapm_power_widgets(dapm, event);
+}
+
+/**
+ * snd_soc_dapm_stream_event - send a stream event to the dapm core
+ * @rtd: PCM runtime data
+ * @stream: stream name
+ * @event: stream event
+ *
+ * Sends a stream event to the dapm core. The core then makes any
+ * necessary widget power changes.
+ *
+ * Returns 0 for success else error.
+ */
+int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
+       const char *stream, int event)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+
+       if (stream == NULL)
+               return 0;
+
+       mutex_lock(&codec->mutex);
+       soc_dapm_stream_event(&codec->dapm, stream, event);
        mutex_unlock(&codec->mutex);
        return 0;
 }
@@ -2039,7 +2230,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
 /**
  * snd_soc_dapm_enable_pin - enable pin.
- * @codec: SoC codec
+ * @dapm: DAPM context
  * @pin: pin name
  *
  * Enables input/output pin and its parents or children widgets iff there is
@@ -2047,15 +2238,15 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin)
+int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin)
 {
-       return snd_soc_dapm_set_pin(codec, pin, 1);
+       return snd_soc_dapm_set_pin(dapm, pin, 1);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
 
 /**
  * snd_soc_dapm_force_enable_pin - force a pin to be enabled
- * @codec: SoC codec
+ * @dapm: DAPM context
  * @pin: pin name
  *
  * Enables input/output pin regardless of any other state.  This is
@@ -2065,42 +2256,47 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_force_enable_pin(struct snd_soc_codec *codec, const char *pin)
+int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
+                                 const char *pin)
 {
        struct snd_soc_dapm_widget *w;
 
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (w->dapm != dapm)
+                       continue;
                if (!strcmp(w->name, pin)) {
-                       pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+                       dev_dbg(w->dapm->dev,
+                               "dapm: force enable pin %s\n", pin);
                        w->connected = 1;
                        w->force = 1;
                        return 0;
                }
        }
 
-       pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+       dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
        return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
 
 /**
  * snd_soc_dapm_disable_pin - disable pin.
- * @codec: SoC codec
+ * @dapm: DAPM context
  * @pin: pin name
  *
  * Disables input/output pin and its parents or children widgets.
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin)
+int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
+                            const char *pin)
 {
-       return snd_soc_dapm_set_pin(codec, pin, 0);
+       return snd_soc_dapm_set_pin(dapm, pin, 0);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
 
 /**
  * snd_soc_dapm_nc_pin - permanently disable pin.
- * @codec: SoC codec
+ * @dapm: DAPM context
  * @pin: pin name
  *
  * Marks the specified pin as being not connected, disabling it along
@@ -2112,26 +2308,29 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
-int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin)
+int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin)
 {
-       return snd_soc_dapm_set_pin(codec, pin, 0);
+       return snd_soc_dapm_set_pin(dapm, pin, 0);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
 
 /**
  * snd_soc_dapm_get_pin_status - get audio pin status
- * @codec: audio codec
+ * @dapm: DAPM context
  * @pin: audio signal pin endpoint (or start point)
  *
  * Get audio pin status - connected or disconnected.
  *
  * Returns 1 for connected otherwise 0.
  */
-int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin)
+int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,
+                               const char *pin)
 {
        struct snd_soc_dapm_widget *w;
 
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (w->dapm != dapm)
+                       continue;
                if (!strcmp(w->name, pin))
                        return w->connected;
        }
@@ -2142,7 +2341,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
 
 /**
  * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint
- * @codec: audio codec
+ * @dapm: DAPM context
  * @pin: audio signal pin endpoint (or start point)
  *
  * Mark the given endpoint or pin as ignoring suspend.  When the
@@ -2151,18 +2350,21 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
  * normal means at suspend time, it will not be turned on if it was not
  * already enabled.
  */
-int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin)
+int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
+                               const char *pin)
 {
        struct snd_soc_dapm_widget *w;
 
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (w->dapm != dapm)
+                       continue;
                if (!strcmp(w->name, pin)) {
                        w->ignore_suspend = 1;
                        return 0;
                }
        }
 
-       pr_err("Unknown DAPM pin: %s\n", pin);
+       dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
        return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
@@ -2173,20 +2375,23 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
  *
  * Free all dapm widgets and resources.
  */
-void snd_soc_dapm_free(struct snd_soc_codec *codec)
+void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
 {
-       snd_soc_dapm_sys_remove(codec->dev);
-       dapm_free_widgets(codec);
+       snd_soc_dapm_sys_remove(dapm->dev);
+       dapm_free_widgets(dapm);
+       list_del(&dapm->list);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
 
-static void soc_dapm_shutdown_codec(struct snd_soc_codec *codec)
+static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
 {
        struct snd_soc_dapm_widget *w;
        LIST_HEAD(down_list);
        int powerdown = 0;
 
-       list_for_each_entry(w, &codec->dapm_widgets, list) {
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (w->dapm != dapm)
+                       continue;
                if (w->power) {
                        dapm_seq_insert(w, &down_list, dapm_down_seq);
                        w->power = 0;
@@ -2198,9 +2403,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_codec *codec)
         * standby.
         */
        if (powerdown) {
-               snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_PREPARE);
-               dapm_seq_run(codec, &down_list, 0, dapm_down_seq);
-               snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_STANDBY);
+               snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE);
+               dapm_seq_run(dapm, &down_list, 0, dapm_down_seq);
+               snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY);
        }
 }
 
@@ -2211,10 +2416,10 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card)
 {
        struct snd_soc_codec *codec;
 
-       list_for_each_entry(codec, &card->codec_dev_list, list)
-               soc_dapm_shutdown_codec(codec);
-
-       snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_OFF);
+       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);
+       }
 }
 
 /* Module information */