ASoC: dapm: Add API call to query valid DAPM paths
authorLiam Girdwood <lrg@ti.com>
Wed, 18 Apr 2012 10:41:11 +0000 (11:41 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 18 Apr 2012 17:23:00 +0000 (18:23 +0100)
In preparation for ASoC DSP support.

Add a DAPM API call to determine whether a DAPM audio path is valid between
source and sink widgets. This also takes into account all kcontrol mux and mixer
settings in between the source and sink widgets to validate the audio path.

This will be used by the DSP core to determine the runtime DAI mappings
between FE and BE DAIs in order to run PCM operations.

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

index bea0c8658aa0e063352a7c5ce62573c7e5ccf7e0..e3833d9f1914ce8d529e27c319d9038bddb8ef3b 100644 (file)
@@ -321,6 +321,7 @@ struct snd_soc_dapm_pin;
 struct snd_soc_dapm_route;
 struct snd_soc_dapm_context;
 struct regulator;
+struct snd_soc_dapm_widget_list;
 
 int dapm_reg_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
@@ -403,6 +404,10 @@ void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec);
 /* Mostly internal - should not normally be used */
 void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason);
 
+/* dapm path query */
+int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
+       struct snd_soc_dapm_widget_list **list);
+
 /* dapm widget types */
 enum snd_soc_dapm_type {
        snd_soc_dapm_input = 0,         /* input pin */
index ab26f8aa3c781819ca442268acf5b9f81ff8d6c7..6d8efb1cc8ce0b3985fa70b88460c9a3ba5d049c 100644 (file)
@@ -7,6 +7,8 @@
 #include <linux/ktime.h>
 #include <linux/tracepoint.h>
 
+#define DAPM_DIRECT "(direct)"
+
 struct snd_soc_jack;
 struct snd_soc_codec;
 struct snd_soc_platform;
@@ -241,6 +243,84 @@ TRACE_EVENT(snd_soc_dapm_walk_done,
                  (int)__entry->path_checks, (int)__entry->neighbour_checks)
 );
 
+TRACE_EVENT(snd_soc_dapm_output_path,
+
+       TP_PROTO(struct snd_soc_dapm_widget *widget,
+               struct snd_soc_dapm_path *path),
+
+       TP_ARGS(widget, path),
+
+       TP_STRUCT__entry(
+               __string(       wname,  widget->name            )
+               __string(       pname,  path->name ? path->name : DAPM_DIRECT)
+               __string(       psname, path->sink->name        )
+               __field(        int,    path_sink               )
+               __field(        int,    path_connect            )
+       ),
+
+       TP_fast_assign(
+               __assign_str(wname, widget->name);
+               __assign_str(pname, path->name ? path->name : DAPM_DIRECT);
+               __assign_str(psname, path->sink->name);
+               __entry->path_connect = path->connect;
+               __entry->path_sink = (int)path->sink;
+       ),
+
+       TP_printk("%c%s -> %s -> %s\n",
+               (int) __entry->path_sink &&
+               (int) __entry->path_connect ? '*' : ' ',
+               __get_str(wname), __get_str(pname), __get_str(psname))
+);
+
+TRACE_EVENT(snd_soc_dapm_input_path,
+
+       TP_PROTO(struct snd_soc_dapm_widget *widget,
+               struct snd_soc_dapm_path *path),
+
+       TP_ARGS(widget, path),
+
+       TP_STRUCT__entry(
+               __string(       wname,  widget->name            )
+               __string(       pname,  path->name ? path->name : DAPM_DIRECT)
+               __string(       psname, path->source->name      )
+               __field(        int,    path_source             )
+               __field(        int,    path_connect            )
+       ),
+
+       TP_fast_assign(
+               __assign_str(wname, widget->name);
+               __assign_str(pname, path->name ? path->name : DAPM_DIRECT);
+               __assign_str(psname, path->source->name);
+               __entry->path_connect = path->connect;
+               __entry->path_source = (int)path->source;
+       ),
+
+       TP_printk("%c%s <- %s <- %s\n",
+               (int) __entry->path_source &&
+               (int) __entry->path_connect ? '*' : ' ',
+               __get_str(wname), __get_str(pname), __get_str(psname))
+);
+
+TRACE_EVENT(snd_soc_dapm_connected,
+
+       TP_PROTO(int paths, int stream),
+
+       TP_ARGS(paths, stream),
+
+       TP_STRUCT__entry(
+               __field(        int,    paths           )
+               __field(        int,    stream          )
+       ),
+
+       TP_fast_assign(
+               __entry->paths = paths;
+               __entry->stream = stream;
+       ),
+
+       TP_printk("%s: found %d paths\n",
+               __entry->stream ? "capture" : "playback", __entry->paths)
+);
+
 TRACE_EVENT(snd_soc_jack_irq,
 
        TP_PROTO(const char *name),
index d7ee73a60ca556a1d41ee9c576d1ca0ae50cb127..214323f539561cb0f09b779ec5733ee16246b0c4 100644 (file)
@@ -705,11 +705,51 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
        }
 }
 
+/* add widget to list if it's not already in the list */
+static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list,
+       struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_widget_list *wlist;
+       int wlistsize, wlistentries, i;
+
+       if (*list == NULL)
+               return -EINVAL;
+
+       wlist = *list;
+
+       /* is this widget already in the list */
+       for (i = 0; i < wlist->num_widgets; i++) {
+               if (wlist->widgets[i] == w)
+                       return 0;
+       }
+
+       /* allocate some new space */
+       wlistentries = wlist->num_widgets + 1;
+       wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
+                       wlistentries * sizeof(struct snd_soc_dapm_widget *);
+       *list = krealloc(wlist, wlistsize, GFP_KERNEL);
+       if (*list == NULL) {
+               dev_err(w->dapm->dev, "can't allocate widget list for %s\n",
+                       w->name);
+               return -ENOMEM;
+       }
+       wlist = *list;
+
+       /* insert the widget */
+       dev_dbg(w->dapm->dev, "added %s in widget list pos %d\n",
+                       w->name, wlist->num_widgets);
+
+       wlist->widgets[wlist->num_widgets] = w;
+       wlist->num_widgets++;
+       return 1;
+}
+
 /*
  * Recursively check for a completed path to an active or physically connected
  * output widget. Returns number of complete paths.
  */
-static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
+       struct snd_soc_dapm_widget_list **list)
 {
        struct snd_soc_dapm_path *path;
        int con = 0;
@@ -765,9 +805,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
                if (path->walked)
                        continue;
 
+               trace_snd_soc_dapm_output_path(widget, path);
+
                if (path->sink && path->connect) {
                        path->walked = 1;
-                       con += is_connected_output_ep(path->sink);
+
+                       /* do we need to add this widget to the list ? */
+                       if (list) {
+                               int err;
+                               err = dapm_list_add_widget(list, path->sink);
+                               if (err < 0) {
+                                       dev_err(widget->dapm->dev, "could not add widget %s\n",
+                                               widget->name);
+                                       return con;
+                               }
+                       }
+
+                       con += is_connected_output_ep(path->sink, list);
                }
        }
 
@@ -780,7 +834,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
  * Recursively check for a completed path to an active or physically connected
  * input widget. Returns number of complete paths.
  */
-static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
+static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
+       struct snd_soc_dapm_widget_list **list)
 {
        struct snd_soc_dapm_path *path;
        int con = 0;
@@ -848,9 +903,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
                if (path->walked)
                        continue;
 
+               trace_snd_soc_dapm_input_path(widget, path);
+
                if (path->source && path->connect) {
                        path->walked = 1;
-                       con += is_connected_input_ep(path->source);
+
+                       /* do we need to add this widget to the list ? */
+                       if (list) {
+                               int err;
+                               err = dapm_list_add_widget(list, path->sink);
+                               if (err < 0) {
+                                       dev_err(widget->dapm->dev, "could not add widget %s\n",
+                                               widget->name);
+                                       return con;
+                               }
+                       }
+
+                       con += is_connected_input_ep(path->source, list);
                }
        }
 
@@ -859,6 +928,39 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
        return con;
 }
 
+/**
+ * snd_soc_dapm_get_connected_widgets - query audio path and it's widgets.
+ * @dai: the soc DAI.
+ * @stream: stream direction.
+ * @list: list of active widgets for this stream.
+ *
+ * Queries DAPM graph as to whether an valid audio stream path exists for
+ * the initial stream specified by name. This takes into account
+ * current mixer and mux kcontrol settings. Creates list of valid widgets.
+ *
+ * Returns the number of valid paths or negative error.
+ */
+int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
+       struct snd_soc_dapm_widget_list **list)
+{
+       struct snd_soc_card *card = dai->card;
+       int paths;
+
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       dapm_reset(card);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               paths = is_connected_output_ep(dai->playback_widget, list);
+       else
+               paths = is_connected_input_ep(dai->playback_widget, list);
+
+       trace_snd_soc_dapm_connected(paths, stream);
+       dapm_clear_walk(&card->dapm);
+       mutex_unlock(&card->dapm_mutex);
+
+       return paths;
+}
+
 /*
  * Handler for generic register modifier widget.
  */
@@ -915,9 +1017,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
 
        DAPM_UPDATE_STAT(w, power_checks);
 
-       in = is_connected_input_ep(w);
+       in = is_connected_input_ep(w, NULL);
        dapm_clear_walk(w->dapm);
-       out = is_connected_output_ep(w);
+       out = is_connected_output_ep(w, NULL);
        dapm_clear_walk(w->dapm);
        return out != 0 && in != 0;
 }
@@ -940,7 +1042,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
        DAPM_UPDATE_STAT(w, power_checks);
 
        if (w->active) {
-               in = is_connected_input_ep(w);
+               in = is_connected_input_ep(w, NULL);
                dapm_clear_walk(w->dapm);
                return in != 0;
        } else {
@@ -956,7 +1058,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
        DAPM_UPDATE_STAT(w, power_checks);
 
        if (w->active) {
-               out = is_connected_output_ep(w);
+               out = is_connected_output_ep(w, NULL);
                dapm_clear_walk(w->dapm);
                return out != 0;
        } else {
@@ -1558,9 +1660,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        if (!buf)
                return -ENOMEM;
 
-       in = is_connected_input_ep(w);
+       in = is_connected_input_ep(w, NULL);
        dapm_clear_walk(w->dapm);
-       out = is_connected_output_ep(w);
+       out = is_connected_output_ep(w, NULL);
        dapm_clear_walk(w->dapm);
 
        ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",