ASoC: dapm: Implement and instantiate DAI widgets
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 17 Feb 2012 03:37:51 +0000 (19:37 -0800)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Fri, 17 Feb 2012 16:10:10 +0000 (08:10 -0800)
In order to allow us to do smarter things with DAI links create DAPM
widgets which directly represent the DAIs in the DAPM graph. These are
automatically created from the DAIs as we probe the card with references
held in both directions between the widget and the DAI.

The widgets are not made available for direct instantiation by drivers,
they are created automatically from the DAIs.  Drivers should be updated
to create stream routes using DAPM maps rather than by annotating AIF
and DAC widgets with streams.

In order to ease transition to this model from existing drivers we
automatically create DAPM routes between the DAI widgets and the existing
stream widgets which are started and stopped by the DAI widgets, though
the old stream handling mechanism is still in place.  This also has the
nice effect of removing non-DAPM devices as any device with a DAI
acquires a widget automatically which will allow future simplifications
to the core DAPM logic.

The intention is that in future the AIF and DAI widgets will gain the
ability to interact such that we are able to manage activity on
individual channels independantly rather than powering up and down the
entire AIF as we do currently.

Currently we only generate these for CODECs, mostly as I have no systems
with non-CODEC DAPM to integrate with. It should be a simple matter of
programming to add the additional hookup for these.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@ti.com>
include/sound/soc-dai.h
include/sound/soc-dapm.h
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index 2413acc54883aad471cf0256b3fc2f8fc6d7ae11..adb07fcd712c162351a576c5a229fa3ebef438af 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/list.h>
 
 struct snd_pcm_substream;
+struct snd_soc_dapm_widget;
 
 /*
  * DAI hardware audio formats.
@@ -238,6 +239,9 @@ struct snd_soc_dai {
        unsigned char pop_wait:1;
        unsigned char probed:1;
 
+       struct snd_soc_dapm_widget *playback_widget;
+       struct snd_soc_dapm_widget *capture_widget;
+
        /* DAI DMA data */
        void *playback_dma_data;
        void *capture_dma_data;
index 91eb8126c00e8884b05bdfb1ce9118cc21715927..e46107fffeb48b8c5832eea797bb7ec2b69c92a0 100644 (file)
@@ -355,6 +355,9 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
        const struct snd_soc_dapm_widget *widget,
        int num);
+int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
+                                struct snd_soc_dai *dai);
+int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
 
 /* dapm path setup */
 int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
@@ -425,6 +428,7 @@ enum snd_soc_dapm_type {
        snd_soc_dapm_aif_in,            /* audio interface input */
        snd_soc_dapm_aif_out,           /* audio interface output */
        snd_soc_dapm_siggen,            /* signal generator */
+       snd_soc_dapm_dai,               /* link to DAI structure */
 };
 
 /*
index 77d230cba61a98834bf274584f08732f01dd9ca0..32ca75e2002412efa54022c255ab23ef816dd9f0 100644 (file)
@@ -1010,6 +1010,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
 {
        int ret = 0;
        const struct snd_soc_codec_driver *driver = codec->driver;
+       struct snd_soc_dai *dai;
 
        codec->card = card;
        codec->dapm.card = card;
@@ -1024,6 +1025,14 @@ static int soc_probe_codec(struct snd_soc_card *card,
                snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
                                          driver->num_dapm_widgets);
 
+       /* Create DAPM widgets for each DAI stream */
+       list_for_each_entry(dai, &dai_list, list) {
+               if (dai->dev != codec->dev)
+                       continue;
+
+               snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
+       }
+
        codec->dapm.idle_bias_off = driver->idle_bias_off;
 
        if (driver->probe) {
@@ -1500,6 +1509,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
+       snd_soc_dapm_link_dai_widgets(card);
+
        if (card->controls)
                snd_soc_add_card_controls(card, card->controls, card->num_controls);
 
index 97915eb711cc80eb92ebe2749955f0618a4c7386..a4707d0fdf3dc5aa787cdcb3d091da990a097b5b 100644 (file)
@@ -52,6 +52,7 @@ static int dapm_up_seq[] = {
        [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,
@@ -86,6 +87,7 @@ 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,
@@ -365,6 +367,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        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:
@@ -522,17 +525,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,
@@ -566,7 +569,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,
@@ -702,6 +705,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
        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;
@@ -773,6 +777,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
        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;
@@ -892,6 +897,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)
 {
@@ -2049,6 +2061,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        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);
@@ -2732,10 +2745,10 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                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:
@@ -2771,6 +2784,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        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;
@@ -2822,6 +2838,109 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
 
+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;
+
+       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;
+
+               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);
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
                                  int stream, struct snd_soc_dai *dai,
                                  int event)