Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[pandora-kernel.git] / sound / soc / soc-dapm.c
index 81c4052..32ab7fc 100644 (file)
@@ -187,7 +187,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        case snd_soc_dapm_mixer_named_ctl: {
                int val;
                struct soc_mixer_control *mc = (struct soc_mixer_control *)
-                       w->kcontrols[i].private_value;
+                       w->kcontrol_news[i].private_value;
                unsigned int reg = mc->reg;
                unsigned int shift = mc->shift;
                int max = mc->max;
@@ -204,7 +204,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        }
        break;
        case snd_soc_dapm_mux: {
-               struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+               struct soc_enum *e = (struct soc_enum *)
+                       w->kcontrol_news[i].private_value;
                int val, item, bitmask;
 
                for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
@@ -220,7 +221,8 @@ 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;
+               struct soc_enum *e = (struct soc_enum *)
+                       w->kcontrol_news[i].private_value;
 
                p->connect = 0;
                /* since a virtual mux has no backing registers to
@@ -235,7 +237,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        break;
        case snd_soc_dapm_value_mux: {
                struct soc_enum *e = (struct soc_enum *)
-                       w->kcontrols[i].private_value;
+                       w->kcontrol_news[i].private_value;
                int val, item;
 
                val = snd_soc_read(w->codec, e->reg);
@@ -310,11 +312,11 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
 
        /* search for mixer kcontrol */
        for (i = 0; i < dest->num_kcontrols; i++) {
-               if (!strcmp(control_name, dest->kcontrols[i].name)) {
+               if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
                        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;
+                       path->name = dest->kcontrol_news[i].name;
                        dapm_set_path_status(dest, path, i);
                        return 0;
                }
@@ -322,54 +324,42 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
        return -ENODEV;
 }
 
-/* update dapm codec register bits */
-static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
+static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
+       struct snd_soc_dapm_widget *kcontrolw,
+       const struct snd_kcontrol_new *kcontrol_new,
+       struct snd_kcontrol **kcontrol)
 {
-       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 ||
-               widget->id == snd_soc_dapm_output ||
-               widget->id == snd_soc_dapm_hp ||
-               widget->id == snd_soc_dapm_mic ||
-               widget->id == snd_soc_dapm_line ||
-               widget->id == snd_soc_dapm_spk)
-               return 0;
-
-       power = widget->power;
-       if (widget->invert)
-               power = (power ? 0:1);
+       struct snd_soc_dapm_widget *w;
+       int i;
 
-       old = snd_soc_read(codec, widget->reg);
-       new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
+       *kcontrol = NULL;
 
-       change = old != new;
-       if (change) {
-               pop_dbg(dapm->dev, card->pop_time,
-                       "pop test %s : %s in %d ms\n",
-                       widget->name, widget->power ? "on" : "off",
-                       card->pop_time);
-               pop_wait(card->pop_time);
-               snd_soc_write(codec, widget->reg, new);
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (w == kcontrolw || w->dapm != kcontrolw->dapm)
+                       continue;
+               for (i = 0; i < w->num_kcontrols; i++) {
+                       if (&w->kcontrol_news[i] == kcontrol_new) {
+                               if (w->kcontrols)
+                                       *kcontrol = w->kcontrols[i];
+                               return 1;
+                       }
+               }
        }
-       dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg,
-               old, new, change);
-       return change;
+
+       return 0;
 }
 
 /* create new dapm mixer control */
-static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
-       struct snd_soc_dapm_widget *w)
+static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
 {
+       struct snd_soc_dapm_context *dapm = w->dapm;
        int i, ret = 0;
        size_t name_len, prefix_len;
        struct snd_soc_dapm_path *path;
        struct snd_card *card = dapm->card->snd_card;
        const char *prefix;
+       struct snd_soc_dapm_widget_list *wlist;
+       size_t wlistsize;
 
        if (dapm->codec)
                prefix = dapm->codec->name_prefix;
@@ -388,23 +378,37 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
                list_for_each_entry(path, &w->sources, list_sink) {
 
                        /* mixer/mux paths name must match control name */
-                       if (path->name != (char*)w->kcontrols[i].name)
+                       if (path->name != (char *)w->kcontrol_news[i].name)
                                continue;
 
+                       wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+                                   sizeof(struct snd_soc_dapm_widget *),
+                       wlist = kzalloc(wlistsize, GFP_KERNEL);
+                       if (wlist == NULL) {
+                               dev_err(dapm->dev,
+                                       "asoc: can't allocate widget list for %s\n",
+                                       w->name);
+                               return -ENOMEM;
+                       }
+                       wlist->num_widgets = 1;
+                       wlist->widgets[0] = w;
+
                        /* add dapm control with long name.
                         * for dapm_mixer this is the concatenation of the
                         * mixer and kcontrol name.
                         * for dapm_mixer_named_ctl this is simply the
                         * kcontrol name.
                         */
-                       name_len = strlen(w->kcontrols[i].name) + 1;
+                       name_len = strlen(w->kcontrol_news[i].name) + 1;
                        if (w->id != snd_soc_dapm_mixer_named_ctl)
                                name_len += 1 + strlen(w->name);
 
                        path->long_name = kmalloc(name_len, GFP_KERNEL);
 
-                       if (path->long_name == NULL)
+                       if (path->long_name == NULL) {
+                               kfree(wlist);
                                return -ENOMEM;
+                       }
 
                        switch (w->id) {
                        default:
@@ -416,82 +420,122 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
                                 */
                                snprintf(path->long_name, name_len, "%s %s",
                                         w->name + prefix_len,
-                                        w->kcontrols[i].name);
+                                        w->kcontrol_news[i].name);
                                break;
                        case snd_soc_dapm_mixer_named_ctl:
                                snprintf(path->long_name, name_len, "%s",
-                                        w->kcontrols[i].name);
+                                        w->kcontrol_news[i].name);
                                break;
                        }
 
                        path->long_name[name_len - 1] = '\0';
 
-                       path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
-                                                     path->long_name, prefix);
+                       path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
+                                                     wlist, path->long_name,
+                                                     prefix);
                        ret = snd_ctl_add(card, path->kcontrol);
                        if (ret < 0) {
                                dev_err(dapm->dev,
                                        "asoc: failed to add dapm kcontrol %s: %d\n",
                                        path->long_name, ret);
+                               kfree(wlist);
                                kfree(path->long_name);
                                path->long_name = NULL;
                                return ret;
                        }
+                       w->kcontrols[i] = path->kcontrol;
                }
        }
        return ret;
 }
 
 /* create new dapm mux control */
-static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
-       struct snd_soc_dapm_widget *w)
+static int dapm_new_mux(struct snd_soc_dapm_widget *w)
 {
+       struct snd_soc_dapm_context *dapm = w->dapm;
        struct snd_soc_dapm_path *path = NULL;
        struct snd_kcontrol *kcontrol;
        struct snd_card *card = dapm->card->snd_card;
        const char *prefix;
        size_t prefix_len;
-       int ret = 0;
-
-       if (!w->num_kcontrols) {
-               dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name);
+       int ret;
+       struct snd_soc_dapm_widget_list *wlist;
+       int shared, wlistentries;
+       size_t wlistsize;
+       char *name;
+
+       if (w->num_kcontrols != 1) {
+               dev_err(dapm->dev,
+                       "asoc: mux %s has incorrect number of controls\n",
+                       w->name);
                return -EINVAL;
        }
 
-       if (dapm->codec)
-               prefix = dapm->codec->name_prefix;
-       else
-               prefix = NULL;
+       shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0],
+                                        &kcontrol);
+       if (kcontrol) {
+               wlist = kcontrol->private_data;
+               wlistentries = wlist->num_widgets + 1;
+       } else {
+               wlist = NULL;
+               wlistentries = 1;
+       }
+       wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+               wlistentries * sizeof(struct snd_soc_dapm_widget *),
+       wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
+       if (wlist == NULL) {
+               dev_err(dapm->dev,
+                       "asoc: can't allocate widget list for %s\n", w->name);
+               return -ENOMEM;
+       }
+       wlist->num_widgets = wlistentries;
+       wlist->widgets[wlistentries - 1] = w;
 
-       if (prefix)
-               prefix_len = strlen(prefix) + 1;
-       else
-               prefix_len = 0;
+       if (!kcontrol) {
+               if (dapm->codec)
+                       prefix = dapm->codec->name_prefix;
+               else
+                       prefix = NULL;
+
+               if (shared) {
+                       name = w->kcontrol_news[0].name;
+                       prefix_len = 0;
+               } else {
+                       name = w->name;
+                       if (prefix)
+                               prefix_len = strlen(prefix) + 1;
+                       else
+                               prefix_len = 0;
+               }
 
-       /* The control will get a prefix from the control creation
-        * process but we're also using the same prefix for widgets so
-        * cut the prefix off the front of the widget name.
-        */
-       kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len,
-                               prefix);
-       ret = snd_ctl_add(card, kcontrol);
+               /*
+                * The control will get a prefix from the control creation
+                * process but we're also using the same prefix for widgets so
+                * cut the prefix off the front of the widget name.
+                */
+               kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,
+                                       name + prefix_len, prefix);
+               ret = snd_ctl_add(card, kcontrol);
+               if (ret < 0) {
+                       dev_err(dapm->dev,
+                               "asoc: failed to add kcontrol %s\n", w->name);
+                       kfree(wlist);
+                       return ret;
+               }
+       }
 
-       if (ret < 0)
-               goto err;
+       kcontrol->private_data = wlist;
+
+       w->kcontrols[0] = kcontrol;
 
        list_for_each_entry(path, &w->sources, list_sink)
                path->kcontrol = kcontrol;
 
-       return ret;
-
-err:
-       dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name);
-       return ret;
+       return 0;
 }
 
 /* create new dapm volume control */
-static int dapm_new_pga(struct snd_soc_dapm_context *dapm,
-       struct snd_soc_dapm_widget *w)
+static int dapm_new_pga(struct snd_soc_dapm_widget *w)
 {
        if (w->num_kcontrols)
                dev_err(w->dapm->dev,
@@ -644,57 +688,6 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(dapm_reg_event);
 
-/* Standard power change method, used to apply power changes to most
- * widgets.
- */
-static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
-{
-       int ret;
-
-       /* call any power change event handlers */
-       if (w->event)
-               dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n",
-                        w->power ? "on" : "off",
-                        w->name, w->event_flags);
-
-       /* power up pre event */
-       if (w->power && w->event &&
-           (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
-               ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* power down pre event */
-       if (!w->power && w->event &&
-           (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
-               ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
-               if (ret < 0)
-                       return ret;
-       }
-
-       dapm_update_bits(w);
-
-       /* power up post event */
-       if (w->power && w->event &&
-           (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
-               ret = w->event(w,
-                              NULL, SND_SOC_DAPM_POST_PMU);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* power down post event */
-       if (!w->power && w->event &&
-           (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
-               ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
-               if (ret < 0)
-                       return ret;
-       }
-
-       return 0;
-}
-
 /* Generic check to see if a widget should be powered.
  */
 static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
@@ -981,16 +974,6 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
                                               NULL, SND_SOC_DAPM_POST_PMD);
                        break;
 
-               case snd_soc_dapm_input:
-               case snd_soc_dapm_output:
-               case snd_soc_dapm_hp:
-               case snd_soc_dapm_mic:
-               case snd_soc_dapm_line:
-               case snd_soc_dapm_spk:
-                       /* No register support currently */
-                       ret = dapm_generic_apply_power(w);
-                       break;
-
                default:
                        /* Queue it up for application */
                        cur_sort = sort[w->id];
@@ -1129,7 +1112,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
        trace_snd_soc_dapm_start(card);
 
        list_for_each_entry(d, &card->dapm_list, list)
-               if (d->n_widgets)
+               if (d->n_widgets || d->codec == NULL)
                        d->dev_power = 0;
 
        /* Check which widgets we need to power and store them in
@@ -1201,6 +1184,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                }
        }
 
+       /* Force all contexts in the card to the same bias state */
+       power = 0;
+       list_for_each_entry(d, &card->dapm_list, list)
+               if (d->dev_power)
+                       power = 1;
+       list_for_each_entry(d, &card->dapm_list, list)
+               d->dev_power = power;
+
+
        /* 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,
@@ -1304,31 +1296,104 @@ static const struct file_operations dapm_widget_power_fops = {
        .llseek = default_llseek,
 };
 
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+static int dapm_bias_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct snd_soc_dapm_context *dapm = file->private_data;
+       char *level;
+
+       switch (dapm->bias_level) {
+       case SND_SOC_BIAS_ON:
+               level = "On\n";
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               level = "Prepare\n";
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               level = "Standby\n";
+               break;
+       case SND_SOC_BIAS_OFF:
+               level = "Off\n";
+               break;
+       default:
+               BUG();
+               level = "Unknown\n";
+               break;
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, level,
+                                      strlen(level));
+}
+
+static const struct file_operations dapm_bias_fops = {
+       .open = dapm_bias_open_file,
+       .read = dapm_bias_read_file,
+       .llseek = default_llseek,
+};
+
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+       struct dentry *parent)
 {
-       struct snd_soc_dapm_widget *w;
        struct dentry *d;
 
-       if (!dapm->debugfs_dapm)
+       dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
+
+       if (!dapm->debugfs_dapm) {
+               printk(KERN_WARNING
+                      "Failed to create DAPM debugfs directory\n");
                return;
+       }
 
-       list_for_each_entry(w, &dapm->card->widgets, list) {
-               if (!w->name || w->dapm != dapm)
-                       continue;
+       d = debugfs_create_file("bias_level", 0444,
+                               dapm->debugfs_dapm, dapm,
+                               &dapm_bias_fops);
+       if (!d)
+               dev_warn(dapm->dev,
+                        "ASoC: Failed to create bias level debugfs file\n");
+}
 
-               d = debugfs_create_file(w->name, 0444,
-                                       dapm->debugfs_dapm, w,
-                                       &dapm_widget_power_fops);
-               if (!d)
-                       dev_warn(w->dapm->dev,
-                               "ASoC: Failed to create %s debugfs file\n",
-                               w->name);
-       }
+static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct dentry *d;
+
+       if (!dapm->debugfs_dapm || !w->name)
+               return;
+
+       d = debugfs_create_file(w->name, 0444,
+                               dapm->debugfs_dapm, w,
+                               &dapm_widget_power_fops);
+       if (!d)
+               dev_warn(w->dapm->dev,
+                       "ASoC: Failed to create %s debugfs file\n",
+                       w->name);
+}
+
+static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
+{
+       debugfs_remove_recursive(dapm->debugfs_dapm);
 }
+
 #else
-void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
+void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
+       struct dentry *parent)
+{
+}
+
+static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
+{
+}
+
+static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
 {
 }
+
 #endif
 
 /* test and update the power status of a mux widget */
@@ -1496,32 +1561,49 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
                        kfree(p->long_name);
                        kfree(p);
                }
+               kfree(w->kcontrols);
                kfree(w->name);
                kfree(w);
        }
 }
 
-static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
-                               const char *pin, int status)
+static struct snd_soc_dapm_widget *dapm_find_widget(
+                       struct snd_soc_dapm_context *dapm, const char *pin,
+                       bool search_other_contexts)
 {
        struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_widget *fallback = NULL;
 
        list_for_each_entry(w, &dapm->card->widgets, list) {
-               if (w->dapm != dapm)
-                       continue;
                if (!strcmp(w->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)
-                               w->force = 0;
-                       return 0;
+                       if (w->dapm == dapm)
+                               return w;
+                       else
+                               fallback = w;
                }
        }
 
-       dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
-       return -EINVAL;
+       if (search_other_contexts)
+               return fallback;
+
+       return NULL;
+}
+
+static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
+                               const char *pin, int status)
+{
+       struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
+
+       if (!w) {
+               dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+               return -EINVAL;
+       }
+
+       w->connected = status;
+       if (status == 0)
+               w->force = 0;
+
+       return 0;
 }
 
 /**
@@ -1627,7 +1709,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        }
 
        /* connect dynamic paths */
-       switch(wsink->id) {
+       switch (wsink->id) {
        case snd_soc_dapm_adc:
        case snd_soc_dapm_dac:
        case snd_soc_dapm_pga:
@@ -1650,7 +1732,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        case snd_soc_dapm_virt_mux:
        case snd_soc_dapm_value_mux:
                ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
-                       &wsink->kcontrols[0]);
+                       &wsink->kcontrol_news[0]);
                if (ret != 0)
                        goto err;
                break;
@@ -1730,18 +1812,26 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
                if (w->new)
                        continue;
 
+               if (w->num_kcontrols) {
+                       w->kcontrols = kzalloc(w->num_kcontrols *
+                                               sizeof(struct snd_kcontrol *),
+                                               GFP_KERNEL);
+                       if (!w->kcontrols)
+                               return -ENOMEM;
+               }
+
                switch(w->id) {
                case snd_soc_dapm_switch:
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
                        w->power_check = dapm_generic_check_power;
-                       dapm_new_mixer(dapm, w);
+                       dapm_new_mixer(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(dapm, w);
+                       dapm_new_mux(w);
                        break;
                case snd_soc_dapm_adc:
                case snd_soc_dapm_aif_out:
@@ -1754,7 +1844,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
                case snd_soc_dapm_pga:
                case snd_soc_dapm_out_drv:
                        w->power_check = dapm_generic_check_power;
-                       dapm_new_pga(dapm, w);
+                       dapm_new_pga(w);
                        break;
                case snd_soc_dapm_input:
                case snd_soc_dapm_output:
@@ -1785,6 +1875,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
                }
 
                w->new = 1;
+
+               dapm_debugfs_add_widget(w);
        }
 
        dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
@@ -1804,7 +1896,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
 int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int reg = mc->reg;
@@ -1843,7 +1936,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = widget->codec;
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int reg = mc->reg;
@@ -1854,6 +1949,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        unsigned int val;
        int connect, change;
        struct snd_soc_dapm_update update;
+       int wi;
 
        val = (ucontrol->value.integer.value[0] & mask);
 
@@ -1862,31 +1958,36 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        mask = mask << shift;
        val = val << shift;
 
-       mutex_lock(&widget->codec->mutex);
-       widget->value = val;
+       if (val)
+               /* new connection */
+               connect = invert ? 0 : 1;
+       else
+               /* old connection must be powered down */
+               connect = invert ? 1 : 0;
+
+       mutex_lock(&codec->mutex);
 
        change = snd_soc_test_bits(widget->codec, reg, mask, val);
        if (change) {
-               if (val)
-                       /* new connection */
-                       connect = invert ? 0:1;
-               else
-                       /* old connection must be powered down */
-                       connect = invert ? 1:0;
+               for (wi = 0; wi < wlist->num_widgets; wi++) {
+                       widget = wlist->widgets[wi];
 
-               update.kcontrol = kcontrol;
-               update.widget = widget;
-               update.reg = reg;
-               update.mask = mask;
-               update.val = val;
-               widget->dapm->update = &update;
+                       widget->value = val;
 
-               dapm_mixer_update_power(widget, kcontrol, connect);
+                       update.kcontrol = kcontrol;
+                       update.widget = widget;
+                       update.reg = reg;
+                       update.mask = mask;
+                       update.val = val;
+                       widget->dapm->update = &update;
 
-               widget->dapm->update = NULL;
+                       dapm_mixer_update_power(widget, kcontrol, connect);
+
+                       widget->dapm->update = NULL;
+               }
        }
 
-       mutex_unlock(&widget->codec->mutex);
+       mutex_unlock(&codec->mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@@ -1903,7 +2004,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
 int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, bitmask;
 
@@ -1931,11 +2033,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = widget->codec;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask, bitmask;
        struct snd_soc_dapm_update update;
+       int wi;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
                ;
@@ -1951,22 +2056,29 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
                mask |= (bitmask - 1) << e->shift_r;
        }
 
-       mutex_lock(&widget->codec->mutex);
-       widget->value = val;
+       mutex_lock(&codec->mutex);
+
        change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       if (change) {
+               for (wi = 0; wi < wlist->num_widgets; wi++) {
+                       widget = wlist->widgets[wi];
 
-       update.kcontrol = kcontrol;
-       update.widget = widget;
-       update.reg = e->reg;
-       update.mask = mask;
-       update.val = val;
-       widget->dapm->update = &update;
+                       widget->value = val;
 
-       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+                       update.kcontrol = kcontrol;
+                       update.widget = widget;
+                       update.reg = e->reg;
+                       update.mask = mask;
+                       update.val = val;
+                       widget->dapm->update = &update;
 
-       widget->dapm->update = NULL;
+                       dapm_mux_update_power(widget, kcontrol, change, mux, e);
 
-       mutex_unlock(&widget->codec->mutex);
+                       widget->dapm->update = NULL;
+               }
+       }
+
+       mutex_unlock(&codec->mutex);
        return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@@ -1981,7 +2093,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
 
        ucontrol->value.enumerated.item[0] = widget->value;
 
@@ -1999,22 +2112,33 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
 int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = widget->codec;
        struct soc_enum *e =
                (struct soc_enum *)kcontrol->private_value;
        int change;
        int ret = 0;
+       int wi;
 
        if (ucontrol->value.enumerated.item[0] >= e->max)
                return -EINVAL;
 
-       mutex_lock(&widget->codec->mutex);
+       mutex_lock(&codec->mutex);
 
        change = widget->value != ucontrol->value.enumerated.item[0];
-       widget->value = ucontrol->value.enumerated.item[0];
-       dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+       if (change) {
+               for (wi = 0; wi < wlist->num_widgets; wi++) {
+                       widget = wlist->widgets[wi];
 
-       mutex_unlock(&widget->codec->mutex);
+                       widget->value = ucontrol->value.enumerated.item[0];
+
+                       dapm_mux_update_power(widget, kcontrol, change,
+                                             widget->value, e);
+               }
+       }
+
+       mutex_unlock(&codec->mutex);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@@ -2035,7 +2159,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
 int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int reg_val, val, mux;
 
@@ -2075,11 +2200,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
 int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = widget->codec;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask;
        struct snd_soc_dapm_update update;
+       int wi;
 
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
                return -EINVAL;
@@ -2093,22 +2221,29 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
                mask |= e->mask << e->shift_r;
        }
 
-       mutex_lock(&widget->codec->mutex);
-       widget->value = val;
+       mutex_lock(&codec->mutex);
+
        change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       if (change) {
+               for (wi = 0; wi < wlist->num_widgets; wi++) {
+                       widget = wlist->widgets[wi];
 
-       update.kcontrol = kcontrol;
-       update.widget = widget;
-       update.reg = e->reg;
-       update.mask = mask;
-       update.val = val;
-       widget->dapm->update = &update;
+                       widget->value = val;
 
-       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+                       update.kcontrol = kcontrol;
+                       update.widget = widget;
+                       update.reg = e->reg;
+                       update.mask = mask;
+                       update.val = val;
+                       widget->dapm->update = &update;
 
-       widget->dapm->update = NULL;
+                       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+                       widget->dapm->update = NULL;
+               }
+       }
 
-       mutex_unlock(&widget->codec->mutex);
+       mutex_unlock(&codec->mutex);
        return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
@@ -2346,22 +2481,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
 int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
                                  const char *pin)
 {
-       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
 
-       list_for_each_entry(w, &dapm->card->widgets, list) {
-               if (w->dapm != dapm)
-                       continue;
-               if (!strcmp(w->name, pin)) {
-                       dev_dbg(w->dapm->dev,
-                               "dapm: force enable pin %s\n", pin);
-                       w->connected = 1;
-                       w->force = 1;
-                       return 0;
-               }
+       if (!w) {
+               dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+               return -EINVAL;
        }
 
-       dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
-       return -EINVAL;
+       dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin);
+       w->connected = 1;
+       w->force = 1;
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
 
@@ -2413,14 +2544,10 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
 int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm,
                                const char *pin)
 {
-       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
 
-       list_for_each_entry(w, &dapm->card->widgets, list) {
-               if (w->dapm != dapm)
-                       continue;
-               if (!strcmp(w->name, pin))
-                       return w->connected;
-       }
+       if (w)
+               return w->connected;
 
        return 0;
 }
@@ -2440,19 +2567,16 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
 int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
                                const char *pin)
 {
-       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false);
 
-       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;
-               }
+       if (!w) {
+               dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
+               return -EINVAL;
        }
 
-       dev_err(dapm->dev, "dapm: unknown pin %s\n", pin);
-       return -EINVAL;
+       w->ignore_suspend = 1;
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
 
@@ -2465,6 +2589,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
 void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
 {
        snd_soc_dapm_sys_remove(dapm->dev);
+       dapm_debugfs_cleanup(dapm);
        dapm_free_widgets(dapm);
        list_del(&dapm->list);
 }