ASoC: dapm: Remove bodges for no-widget CODECs
[deliverable/linux.git] / sound / soc / soc-dapm.c
index 1315663c1c0990787f226c8358700bce5e0d7963..69e9452386e6d8306f6a7dc62c361143a90aa7a8 100644 (file)
  *      dynamic configuration of codec internal audio paths and active
  *      DACs/ADCs.
  *    o Platform power domain - can support external components i.e. amps and
- *      mic/meadphone insertion events.
+ *      mic/headphone insertion events.
  *    o Automatic Mic Bias support
  *    o Jack insertion power event initiation - e.g. hp insertion will enable
  *      sinks, dacs, etc
- *    o Delayed powerdown of audio susbsystem to reduce pops between a quick
+ *    o Delayed power down of audio subsystem to reduce pops between a quick
  *      device reopen.
  *
- *  Todo:
- *    o DAPM power change sequencing - allow for configurable per
- *      codec sequences.
- *    o Support for analogue bias optimisation.
- *    o Support for reduced codec oversampling rates.
- *    o Support for reduced codec bias currents.
  */
 
 #include <linux/module.h>
@@ -40,6 +34,7 @@
 #include <linux/jiffies.h>
 #include <linux/debugfs.h>
 #include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -55,7 +50,9 @@
 static int dapm_up_seq[] = {
        [snd_soc_dapm_pre] = 0,
        [snd_soc_dapm_supply] = 1,
+       [snd_soc_dapm_regulator_supply] = 1,
        [snd_soc_dapm_micbias] = 2,
+       [snd_soc_dapm_dai] = 3,
        [snd_soc_dapm_aif_in] = 3,
        [snd_soc_dapm_aif_out] = 3,
        [snd_soc_dapm_mic] = 4,
@@ -90,6 +87,8 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_value_mux] = 9,
        [snd_soc_dapm_aif_in] = 10,
        [snd_soc_dapm_aif_out] = 10,
+       [snd_soc_dapm_dai] = 10,
+       [snd_soc_dapm_regulator_supply] = 11,
        [snd_soc_dapm_supply] = 11,
        [snd_soc_dapm_post] = 12,
 };
@@ -172,6 +171,19 @@ static inline struct snd_soc_card *dapm_get_soc_card(
        return NULL;
 }
 
+static void dapm_reset(struct snd_soc_card *card)
+{
+       struct snd_soc_dapm_widget *w;
+
+       memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
+
+       list_for_each_entry(w, &card->widgets, list) {
+               w->power_checked = false;
+               w->inputs = -1;
+               w->outputs = -1;
+       }
+}
+
 static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg)
 {
        if (w->codec)
@@ -194,24 +206,53 @@ static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
        return -1;
 }
 
-static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
+static inline void soc_widget_lock(struct snd_soc_dapm_widget *w)
+{
+       if (w->codec && !w->codec->using_regmap)
+               mutex_lock(&w->codec->mutex);
+       else if (w->platform)
+               mutex_lock(&w->platform->mutex);
+}
+
+static inline void soc_widget_unlock(struct snd_soc_dapm_widget *w)
+{
+       if (w->codec && !w->codec->using_regmap)
+               mutex_unlock(&w->codec->mutex);
+       else if (w->platform)
+               mutex_unlock(&w->platform->mutex);
+}
+
+static int soc_widget_update_bits_locked(struct snd_soc_dapm_widget *w,
        unsigned short reg, unsigned int mask, unsigned int value)
 {
-       int change;
+       bool change;
        unsigned int old, new;
        int ret;
 
-       ret = soc_widget_read(w, reg);
-       if (ret < 0)
-               return ret;
-
-       old = ret;
-       new = (old & ~mask) | (value & mask);
-       change = old != new;
-       if (change) {
-               ret = soc_widget_write(w, reg, new);
-               if (ret < 0)
+       if (w->codec && w->codec->using_regmap) {
+               ret = regmap_update_bits_check(w->codec->control_data,
+                                              reg, mask, value, &change);
+               if (ret != 0)
+                       return ret;
+       } else {
+               soc_widget_lock(w);
+               ret = soc_widget_read(w, reg);
+               if (ret < 0) {
+                       soc_widget_unlock(w);
                        return ret;
+               }
+
+               old = ret;
+               new = (old & ~mask) | (value & mask);
+               change = old != new;
+               if (change) {
+                       ret = soc_widget_write(w, reg, new);
+                       if (ret < 0) {
+                               soc_widget_unlock(w);
+                               return ret;
+                       }
+               }
+               soc_widget_unlock(w);
        }
 
        return change;
@@ -345,8 +386,10 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        case snd_soc_dapm_micbias:
        case snd_soc_dapm_vmid:
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_dai:
        case snd_soc_dapm_hp:
        case snd_soc_dapm_mic:
        case snd_soc_dapm_spk:
@@ -504,17 +547,17 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
                                 * for widgets so cut the prefix off
                                 * the front of the widget name.
                                 */
-                               snprintf(path->long_name, name_len, "%s %s",
-                                        w->name + prefix_len,
+                               snprintf((char *)path->long_name, name_len,
+                                        "%s %s", w->name + prefix_len,
                                         w->kcontrol_news[i].name);
                                break;
                        case snd_soc_dapm_mixer_named_ctl:
-                               snprintf(path->long_name, name_len, "%s",
-                                        w->kcontrol_news[i].name);
+                               snprintf((char *)path->long_name, name_len,
+                                        "%s", w->kcontrol_news[i].name);
                                break;
                        }
 
-                       path->long_name[name_len - 1] = '\0';
+                       ((char *)path->long_name)[name_len - 1] = '\0';
 
                        path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
                                                      wlist, path->long_name,
@@ -548,7 +591,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
        struct snd_soc_dapm_widget_list *wlist;
        int shared, wlistentries;
        size_t wlistsize;
-       char *name;
+       const char *name;
 
        if (w->num_kcontrols != 1) {
                dev_err(dapm->dev,
@@ -673,12 +716,18 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       if (widget->id == snd_soc_dapm_supply)
+       switch (widget->id) {
+       case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                return 0;
+       default:
+               break;
+       }
 
        switch (widget->id) {
        case snd_soc_dapm_adc:
        case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_dai:
                if (widget->active) {
                        widget->outputs = snd_soc_dapm_suspend_check(widget);
                        return widget->outputs;
@@ -738,13 +787,19 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       if (widget->id == snd_soc_dapm_supply)
+       switch (widget->id) {
+       case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                return 0;
+       default:
+               break;
+       }
 
        /* active stream ? */
        switch (widget->id) {
        case snd_soc_dapm_dac:
        case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_dai:
                if (widget->active) {
                        widget->inputs = snd_soc_dapm_suspend_check(widget);
                        return widget->inputs;
@@ -814,13 +869,26 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
        else
                val = w->off_val;
 
-       soc_widget_update_bits(w, -(w->reg + 1),
+       soc_widget_update_bits_locked(w, -(w->reg + 1),
                            w->mask << w->shift, val << w->shift);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(dapm_reg_event);
 
+/*
+ * Handler for regulator supply widget.
+ */
+int dapm_regulator_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               return regulator_enable(w->regulator);
+       else
+               return regulator_disable_deferred(w->regulator, w->shift);
+}
+EXPORT_SYMBOL_GPL(dapm_regulator_event);
+
 static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
 {
        if (w->power_checked)
@@ -851,6 +919,13 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
        return out != 0 && in != 0;
 }
 
+static int dapm_dai_check_power(struct snd_soc_dapm_widget *w)
+{
+       DAPM_UPDATE_STAT(w, power_checks);
+
+       return w->active;
+}
+
 /* Check to see if an ADC has power */
 static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
 {
@@ -1052,7 +1127,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
                        "pop test : Applying 0x%x/0x%x to %x in %dms\n",
                        value, mask, reg, card->pop_time);
                pop_wait(card->pop_time);
-               soc_widget_update_bits(w, reg, mask, value);
+               soc_widget_update_bits_locked(w, reg, mask, value);
        }
 
        list_for_each_entry(w, pending, power_list) {
@@ -1182,7 +1257,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
                               w->name, ret);
        }
 
-       ret = snd_soc_update_bits(w->codec, update->reg, update->mask,
+       ret = soc_widget_update_bits_locked(w, update->reg, update->mask,
                                  update->val);
        if (ret < 0)
                pr_err("%s DAPM update failed: %d\n", w->name, ret);
@@ -1251,7 +1326,7 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
                        dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
 
                if (d->dev)
-                       pm_runtime_put_sync(d->dev);
+                       pm_runtime_put(d->dev);
        }
 
        /* If we just powered up then move to active bias */
@@ -1301,6 +1376,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
        }
        switch (w->id) {
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                /* Supplies can't affect their outputs, only their inputs */
                break;
        default:
@@ -1365,21 +1441,13 @@ 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 || d->codec == NULL) {
-                       if (d->idle_bias_off)
-                               d->target_bias_level = SND_SOC_BIAS_OFF;
-                       else
-                               d->target_bias_level = SND_SOC_BIAS_STANDBY;
-               }
+               if (d->idle_bias_off)
+                       d->target_bias_level = SND_SOC_BIAS_OFF;
+               else
+                       d->target_bias_level = SND_SOC_BIAS_STANDBY;
        }
 
-       memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
-
-       list_for_each_entry(w, &card->widgets, list) {
-               w->power_checked = false;
-               w->inputs = -1;
-               w->outputs = -1;
-       }
+       dapm_reset(card);
 
        /* Check which widgets we need to power and store them in
         * lists indicating if they should be powered up or down.  We
@@ -1400,10 +1468,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                        /* Supplies and micbiases only bring the
                         * context up to STANDBY as unless something
                         * else is active and passing audio they
-                        * generally don't require full power.
+                        * generally don't require full power.  Signal
+                        * generators are virtual pins and have no
+                        * power impact themselves.
                         */
                        switch (w->id) {
+                       case snd_soc_dapm_siggen:
+                               break;
                        case snd_soc_dapm_supply:
+                       case snd_soc_dapm_regulator_supply:
                        case snd_soc_dapm_micbias:
                                if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
                                        d->target_bias_level = SND_SOC_BIAS_STANDBY;
@@ -1416,32 +1489,6 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
 
        }
 
-       /* If there are no DAPM widgets then try to figure out power from the
-        * event type.
-        */
-       if (!dapm->n_widgets) {
-               switch (event) {
-               case SND_SOC_DAPM_STREAM_START:
-               case SND_SOC_DAPM_STREAM_RESUME:
-                       dapm->target_bias_level = SND_SOC_BIAS_ON;
-                       break;
-               case SND_SOC_DAPM_STREAM_STOP:
-                       if (dapm->codec && dapm->codec->active)
-                               dapm->target_bias_level = SND_SOC_BIAS_ON;
-                       else
-                               dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
-                       break;
-               case SND_SOC_DAPM_STREAM_SUSPEND:
-                       dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
-                       break;
-               case SND_SOC_DAPM_STREAM_NOP:
-                       dapm->target_bias_level = dapm->bias_level;
-                       break;
-               default:
-                       break;
-               }
-       }
-
        /* Force all contexts in the card to the same bias state if
         * they're not ground referenced.
         */
@@ -1475,6 +1522,12 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                                        &async_domain);
        async_synchronize_full_domain(&async_domain);
 
+       /* do we need to notify any clients that DAPM event is complete */
+       list_for_each_entry(d, &card->dapm_list, list) {
+               if (d->stream_event)
+                       d->stream_event(d, event);
+       }
+
        pop_dbg(dapm->dev, card->pop_time,
                "DAPM sequencing finished, waiting %dms\n", card->pop_time);
        pop_wait(card->pop_time);
@@ -1510,8 +1563,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        out = is_connected_output_ep(w);
        dapm_clear_walk(w->dapm);
 
-       ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d",
-                      w->name, w->power ? "On" : "Off", in, out);
+       ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
+                      w->name, w->power ? "On" : "Off",
+                      w->force ? " (forced)" : "", in, out);
 
        if (w->reg >= 0)
                ret += snprintf(buf + ret, PAGE_SIZE - ret,
@@ -1607,7 +1661,7 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
        dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
 
        if (!dapm->debugfs_dapm) {
-               printk(KERN_WARNING
+               dev_warn(dapm->dev,
                       "Failed to create DAPM debugfs directory\n");
                return;
        }
@@ -1659,9 +1713,8 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
 #endif
 
 /* test and update the power status of a mux widget */
-static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
-                                struct snd_kcontrol *kcontrol, int change,
-                                int mux, struct soc_enum *e)
+static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+                                struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
@@ -1671,9 +1724,6 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
            widget->id != snd_soc_dapm_value_mux)
                return -ENODEV;
 
-       if (!change)
-               return 0;
-
        /* find dapm widget path assoc with kcontrol */
        list_for_each_entry(path, &widget->dapm->card->paths, list) {
                if (path->kcontrol != kcontrol)
@@ -1703,8 +1753,21 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
        return 0;
 }
 
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+               struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
+{
+       struct snd_soc_card *card = widget->dapm->card;
+       int ret;
+
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+       mutex_unlock(&card->dapm_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
+
 /* test and update the power status of a mixer or switch widget */
-static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
                                   struct snd_kcontrol *kcontrol, int connect)
 {
        struct snd_soc_dapm_path *path;
@@ -1734,6 +1797,19 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
        return 0;
 }
 
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+                               struct snd_kcontrol *kcontrol, int connect)
+{
+       struct snd_soc_card *card = widget->dapm->card;
+       int ret;
+
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
+       mutex_unlock(&card->dapm_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
+
 /* show dapm widget status in sys fs */
 static ssize_t dapm_widget_show(struct device *dev,
        struct device_attribute *attr, char *buf)
@@ -1762,6 +1838,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
                case snd_soc_dapm_supply:
+               case snd_soc_dapm_regulator_supply:
                        if (w->name)
                                count += sprintf(buf + count, "%s: %s\n",
                                        w->name, w->power ? "On":"Off");
@@ -1869,10 +1946,12 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
                return -EINVAL;
        }
 
+       if (w->connected != status)
+               dapm_mark_dirty(w, "pin configuration");
+
        w->connected = status;
        if (status == 0)
                w->force = 0;
-       dapm_mark_dirty(w, "pin configuration");
 
        return 0;
 }
@@ -1888,6 +1967,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
  */
 int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 {
+       int ret;
+
        /*
         * Suppress early reports (eg, jacks syncing their state) to avoid
         * silly DAPM runs during card startup.
@@ -1895,7 +1976,10 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
        if (!dapm->card || !dapm->card->instantiated)
                return 0;
 
-       return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+       mutex_unlock(&dapm->card->dapm_mutex);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
@@ -2000,8 +2084,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        case snd_soc_dapm_pre:
        case snd_soc_dapm_post:
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_dai:
                list_add(&path->list, &dapm->card->paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
@@ -2059,6 +2145,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 {
        int i, ret;
 
+       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
        for (i = 0; i < num; i++) {
                ret = snd_soc_dapm_add_route(dapm, route);
                if (ret < 0) {
@@ -2068,6 +2155,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
                }
                route++;
        }
+       mutex_unlock(&dapm->card->dapm_mutex);
 
        return 0;
 }
@@ -2140,12 +2228,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
        int i, err;
        int ret = 0;
 
+       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
        for (i = 0; i < num; i++) {
                err = snd_soc_dapm_weak_route(dapm, route);
                if (err)
                        ret = err;
                route++;
        }
+       mutex_unlock(&dapm->card->dapm_mutex);
 
        return ret;
 }
@@ -2164,6 +2254,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
        struct snd_soc_dapm_widget *w;
        unsigned int val;
 
+       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+
        list_for_each_entry(w, &dapm->card->widgets, list)
        {
                if (w->new)
@@ -2173,8 +2265,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
                        w->kcontrols = kzalloc(w->num_kcontrols *
                                                sizeof(struct snd_kcontrol *),
                                                GFP_KERNEL);
-                       if (!w->kcontrols)
+                       if (!w->kcontrols) {
+                               mutex_unlock(&dapm->card->dapm_mutex);
                                return -ENOMEM;
+                       }
                }
 
                switch(w->id) {
@@ -2214,6 +2308,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
        }
 
        dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+       mutex_unlock(&dapm->card->dapm_mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -2273,6 +2368,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *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 snd_soc_card *card = codec->card;
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int reg = mc->reg;
@@ -2299,7 +2395,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                /* old connection must be powered down */
                connect = invert ? 1 : 0;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        change = snd_soc_test_bits(widget->codec, reg, mask, val);
        if (change) {
@@ -2315,13 +2411,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                        update.val = val;
                        widget->dapm->update = &update;
 
-                       dapm_mixer_update_power(widget, kcontrol, connect);
+                       soc_dapm_mixer_update_power(widget, kcontrol, connect);
 
                        widget->dapm->update = NULL;
                }
        }
 
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&card->dapm_mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@@ -2370,6 +2466,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *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 snd_soc_card *card = codec->card;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask, bitmask;
@@ -2390,7 +2487,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
                mask |= (bitmask - 1) << e->shift_r;
        }
 
-       mutex_lock(&codec->mutex);
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
        if (change) {
@@ -2406,13 +2503,13 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
                        update.val = val;
                        widget->dapm->update = &update;
 
-                       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+                       soc_dapm_mux_update_power(widget, kcontrol, mux, e);
 
                        widget->dapm->update = NULL;
                }
        }
 
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&card->dapm_mutex);
        return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@@ -2449,6 +2546,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *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 snd_soc_card *card = codec->card;
        struct soc_enum *e =
                (struct soc_enum *)kcontrol->private_value;
        int change;
@@ -2458,7 +2556,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
        if (ucontrol->value.enumerated.item[0] >= e->max)
                return -EINVAL;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        change = widget->value != ucontrol->value.enumerated.item[0];
        if (change) {
@@ -2467,12 +2565,11 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
 
                        widget->value = ucontrol->value.enumerated.item[0];
 
-                       dapm_mux_update_power(widget, kcontrol, change,
-                                             widget->value, e);
+                       soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
                }
        }
 
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&card->dapm_mutex);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@@ -2537,6 +2634,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *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 snd_soc_card *card = codec->card;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask;
@@ -2555,7 +2653,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
                mask |= e->mask << e->shift_r;
        }
 
-       mutex_lock(&codec->mutex);
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
        if (change) {
@@ -2571,13 +2669,13 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
                        update.val = val;
                        widget->dapm->update = &update;
 
-                       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+                       soc_dapm_mux_update_power(widget, kcontrol, mux, e);
 
                        widget->dapm->update = NULL;
                }
        }
 
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&card->dapm_mutex);
        return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
@@ -2611,15 +2709,15 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch);
 int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
        const char *pin = (const char *)kcontrol->private_value;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        ucontrol->value.integer.value[0] =
-               snd_soc_dapm_get_pin_status(&codec->dapm, pin);
+               snd_soc_dapm_get_pin_status(&card->dapm, pin);
 
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&card->dapm_mutex);
 
        return 0;
 }
@@ -2634,41 +2732,47 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch);
 int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
        const char *pin = (const char *)kcontrol->private_value;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        if (ucontrol->value.integer.value[0])
-               snd_soc_dapm_enable_pin(&codec->dapm, pin);
+               snd_soc_dapm_enable_pin(&card->dapm, pin);
        else
-               snd_soc_dapm_disable_pin(&codec->dapm, pin);
+               snd_soc_dapm_disable_pin(&card->dapm, pin);
 
-       snd_soc_dapm_sync(&codec->dapm);
-
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&card->dapm_mutex);
 
+       snd_soc_dapm_sync(&card->dapm);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
 
-/**
- * snd_soc_dapm_new_control - create new dapm control
- * @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_dapm_context *dapm,
-       const struct snd_soc_dapm_widget *widget)
+static struct snd_soc_dapm_widget *
+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;
+       int ret;
 
        if ((w = dapm_cnew_widget(widget)) == NULL)
-               return -ENOMEM;
+               return NULL;
+
+       switch (w->id) {
+       case snd_soc_dapm_regulator_supply:
+               w->regulator = devm_regulator_get(dapm->dev, w->name);
+               if (IS_ERR(w->regulator)) {
+                       ret = PTR_ERR(w->regulator);
+                       dev_err(dapm->dev, "Failed to request %s: %d\n",
+                               w->name, ret);
+                       return NULL;
+               }
+               break;
+       default:
+               break;
+       }
 
        name_len = strlen(widget->name) + 1;
        if (dapm->codec && dapm->codec->name_prefix)
@@ -2676,13 +2780,13 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        w->name = kmalloc(name_len, GFP_KERNEL);
        if (w->name == NULL) {
                kfree(w);
-               return -ENOMEM;
+               return NULL;
        }
        if (dapm->codec && dapm->codec->name_prefix)
-               snprintf(w->name, name_len, "%s %s",
+               snprintf((char *)w->name, name_len, "%s %s",
                        dapm->codec->name_prefix, widget->name);
        else
-               snprintf(w->name, name_len, "%s", widget->name);
+               snprintf((char *)w->name, name_len, "%s", widget->name);
 
        switch (w->id) {
        case snd_soc_dapm_switch:
@@ -2715,8 +2819,12 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                w->power_check = dapm_generic_check_power;
                break;
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                w->power_check = dapm_supply_check_power;
                break;
+       case snd_soc_dapm_dai:
+               w->power_check = dapm_dai_check_power;
+               break;
        default:
                w->power_check = dapm_always_on_check_power;
                break;
@@ -2734,9 +2842,8 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 
        /* machine layer set ups unconnected pins and insertions */
        w->connected = 1;
-       return 0;
+       return w;
 }
-EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
 
 /**
  * snd_soc_dapm_new_controls - create new dapm controls
@@ -2752,56 +2859,183 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
        const struct snd_soc_dapm_widget *widget,
        int num)
 {
-       int i, ret;
+       struct snd_soc_dapm_widget *w;
+       int i;
 
+       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
        for (i = 0; i < num; i++) {
-               ret = snd_soc_dapm_new_control(dapm, widget);
-               if (ret < 0) {
+               w = snd_soc_dapm_new_control(dapm, widget);
+               if (!w) {
                        dev_err(dapm->dev,
-                               "ASoC: Failed to create DAPM control %s: %d\n",
-                               widget->name, ret);
-                       return ret;
+                               "ASoC: Failed to create DAPM control %s\n",
+                               widget->name);
+                       return -ENOMEM;
                }
                widget++;
        }
+       mutex_unlock(&dapm->card->dapm_mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
 
-static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
-       const char *stream, int event)
+int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
+                                struct snd_soc_dai *dai)
 {
+       struct snd_soc_dapm_widget template;
        struct snd_soc_dapm_widget *w;
 
-       list_for_each_entry(w, &dapm->card->widgets, list)
-       {
-               if (!w->sname || w->dapm != dapm)
+       WARN_ON(dapm->dev != dai->dev);
+
+       memset(&template, 0, sizeof(template));
+       template.reg = SND_SOC_NOPM;
+
+       if (dai->driver->playback.stream_name) {
+               template.id = snd_soc_dapm_dai;
+               template.name = dai->driver->playback.stream_name;
+               template.sname = dai->driver->playback.stream_name;
+
+               dev_dbg(dai->dev, "adding %s widget\n",
+                       template.name);
+
+               w = snd_soc_dapm_new_control(dapm, &template);
+               if (!w) {
+                       dev_err(dapm->dev, "Failed to create %s widget\n",
+                               dai->driver->playback.stream_name);
+               }
+
+               w->priv = dai;
+               dai->playback_widget = w;
+       }
+
+       if (dai->driver->capture.stream_name) {
+               template.id = snd_soc_dapm_dai;
+               template.name = dai->driver->capture.stream_name;
+               template.sname = dai->driver->capture.stream_name;
+
+               dev_dbg(dai->dev, "adding %s widget\n",
+                       template.name);
+
+               w = snd_soc_dapm_new_control(dapm, &template);
+               if (!w) {
+                       dev_err(dapm->dev, "Failed to create %s widget\n",
+                               dai->driver->capture.stream_name);
+               }
+
+               w->priv = dai;
+               dai->capture_widget = w;
+       }
+
+       return 0;
+}
+
+int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
+{
+       struct snd_soc_dapm_widget *dai_w, *w;
+       struct snd_soc_dai *dai;
+       struct snd_soc_dapm_route r;
+
+       memset(&r, 0, sizeof(r));
+
+       /* For each DAI widget... */
+       list_for_each_entry(dai_w, &card->widgets, list) {
+               if (dai_w->id != snd_soc_dapm_dai)
                        continue;
-               dev_vdbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n",
-                       w->name, w->sname, stream, event);
-               if (strstr(w->sname, stream)) {
-                       dapm_mark_dirty(w, "stream event");
-                       switch(event) {
-                       case SND_SOC_DAPM_STREAM_START:
-                               w->active = 1;
-                               break;
-                       case SND_SOC_DAPM_STREAM_STOP:
-                               w->active = 0;
-                               break;
-                       case SND_SOC_DAPM_STREAM_SUSPEND:
-                       case SND_SOC_DAPM_STREAM_RESUME:
-                       case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
-                       case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
-                               break;
+
+               dai = dai_w->priv;
+
+               /* ...find all widgets with the same stream and link them */
+               list_for_each_entry(w, &card->widgets, list) {
+                       if (w->dapm != dai_w->dapm)
+                               continue;
+
+                       if (w->id == snd_soc_dapm_dai)
+                               continue;
+
+                       if (!w->sname)
+                               continue;
+
+                       if (dai->driver->playback.stream_name &&
+                           strstr(w->sname,
+                                  dai->driver->playback.stream_name)) {
+                               r.source = dai->playback_widget->name;
+                               r.sink = w->name;
+                               dev_dbg(dai->dev, "%s -> %s\n",
+                                        r.source, r.sink);
+
+                               snd_soc_dapm_add_route(w->dapm, &r);
+                       }
+
+                       if (dai->driver->capture.stream_name &&
+                           strstr(w->sname,
+                                  dai->driver->capture.stream_name)) {
+                               r.source = w->name;
+                               r.sink = dai->capture_widget->name;
+                               dev_dbg(dai->dev, "%s -> %s\n",
+                                       r.source, r.sink);
+
+                               snd_soc_dapm_add_route(w->dapm, &r);
                        }
                }
        }
 
-       dapm_power_widgets(dapm, event);
+       return 0;
+}
+
+static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+       int event)
+{
+
+       struct snd_soc_dapm_widget *w_cpu, *w_codec;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
 
-       /* do we need to notify any clients that DAPM stream is complete */
-       if (dapm->stream_event)
-               dapm->stream_event(dapm, event);
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               w_cpu = cpu_dai->playback_widget;
+               w_codec = codec_dai->playback_widget;
+       } else {
+               w_cpu = cpu_dai->capture_widget;
+               w_codec = codec_dai->capture_widget;
+       }
+
+       if (w_cpu) {
+
+               dapm_mark_dirty(w_cpu, "stream event");
+
+               switch (event) {
+               case SND_SOC_DAPM_STREAM_START:
+                       w_cpu->active = 1;
+                       break;
+               case SND_SOC_DAPM_STREAM_STOP:
+                       w_cpu->active = 0;
+                       break;
+               case SND_SOC_DAPM_STREAM_SUSPEND:
+               case SND_SOC_DAPM_STREAM_RESUME:
+               case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+               case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+                       break;
+               }
+       }
+
+       if (w_codec) {
+
+               dapm_mark_dirty(w_codec, "stream event");
+
+               switch (event) {
+               case SND_SOC_DAPM_STREAM_START:
+                       w_codec->active = 1;
+                       break;
+               case SND_SOC_DAPM_STREAM_STOP:
+                       w_codec->active = 0;
+                       break;
+               case SND_SOC_DAPM_STREAM_SUSPEND:
+               case SND_SOC_DAPM_STREAM_RESUME:
+               case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
+               case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
+                       break;
+               }
+       }
+
+       dapm_power_widgets(&rtd->card->dapm, event);
 }
 
 /**
@@ -2815,18 +3049,14 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
  *
  * Returns 0 for success else error.
  */
-int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
-       const char *stream, int event)
+void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
+                             int event)
 {
-       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_card *card = rtd->card;
 
-       if (stream == NULL)
-               return 0;
-
-       mutex_lock(&codec->mutex);
-       soc_dapm_stream_event(&codec->dapm, stream, event);
-       mutex_unlock(&codec->mutex);
-       return 0;
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       soc_dapm_stream_event(rtd, stream, event);
+       mutex_unlock(&card->dapm_mutex);
 }
 
 /**
This page took 0.038799 seconds and 5 git commands to generate.