[alsa-devel] [PATCH] ASoC: dapm: Add API call to query valid DAPM paths.
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 Dynamic PCM core to determine the runtime DAI mappings between FE and BE DAIs in order to run PCM operations.
This also adds some extra debug to help with graph debugging.
Signed-off-by: Liam Girdwood lrg@ti.com --- include/sound/soc-dapm.h | 5 ++ sound/soc/soc-dapm.c | 127 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 122 insertions(+), 10 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d3e45a0..8d58880 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -323,6 +323,7 @@ struct snd_soc_dapm_path; struct snd_soc_dapm_pin; struct snd_soc_dapm_route; struct snd_soc_dapm_context; +struct snd_soc_dapm_widget_list;
int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -401,6 +402,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 */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8320fd7..c7928db 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -703,11 +703,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; @@ -763,9 +803,26 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) if (path->walked) continue;
+ dev_vdbg(widget->dapm->dev," %c : %s -> %s -> %s\n", + path->sink && path->connect ? '*' : ' ', + widget->name, path->name, path->sink->name); + 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); } }
@@ -778,7 +835,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; @@ -846,9 +904,26 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) if (path->walked) continue;
+ dev_vdbg(widget->dapm->dev," %c : %s <- %s <- %s\n", + path->source && path->connect ? '*' : ' ', + widget->name, path->name, path->source->name); + 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); } }
@@ -857,6 +932,38 @@ 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; + + 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); + + dev_dbg(dai->dev, "%s: found %d paths\n", + stream ? "capture" : "playback", paths); + dapm_clear_walk(&card->dapm); + + return paths; +} + /* * Handler for generic register modifier widget. */ @@ -913,9 +1020,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; } @@ -935,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 { @@ -951,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 { @@ -1587,9 +1694,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",
On Wed, Mar 07, 2012 at 05:55:52PM +0000, Liam Girdwood wrote:
+struct snd_soc_dapm_widget_list;
I might be blind but I think the hunk that actually declares the widget list got dropped from the header... probably in some other part of the series you haven't pushed out yet?
dev_vdbg(widget->dapm->dev," %c : %s -> %s -> %s\n",
path->sink && path->connect ? '*' : ' ',
widget->name, path->name, path->sink->name);
This and the input user look like good candidates for a tracepoint, it's probably pretty useful to have them around and more accessible than vdbg is. It's the sort of information people often look for, and it's real time unlike the debugfs picture which isn't capturing stuff when DAPM is looking at it.
- dapm_reset(card);
This function isn't in mainline, another patch series reordering thing I expect. It does also look like we need some locking somewhere along the line here (even if the only thing here is a big scary comment saying the relevant locks need to be held).
- dev_dbg(dai->dev, "%s: found %d paths\n",
stream ? "capture" : "playback", paths);
Tracepoint again? Much less clear for this one. I do think we should be dumping the stats we've been collecting for the neighbour walks, very useful if performance blows up again.
On Wed, 2012-03-07 at 19:15 +0000, Mark Brown wrote:
On Wed, Mar 07, 2012 at 05:55:52PM +0000, Liam Girdwood wrote:
+struct snd_soc_dapm_widget_list;
I might be blind but I think the hunk that actually declares the widget list got dropped from the header... probably in some other part of the series you haven't pushed out yet?
I had to double check myself to be sure and Stephen added it a while back :-
fafd2176f72148e83c64a1f818ff33fceed83d08
dev_vdbg(widget->dapm->dev," %c : %s -> %s -> %s\n",
path->sink && path->connect ? '*' : ' ',
widget->name, path->name, path->sink->name);
This and the input user look like good candidates for a tracepoint, it's probably pretty useful to have them around and more accessible than vdbg is. It's the sort of information people often look for, and it's real time unlike the debugfs picture which isn't capturing stuff when DAPM is looking at it.
- dapm_reset(card);
This function isn't in mainline, another patch series reordering thing I expect.
It's in 6c120e19fa587710d80757a6e364961a017fb6c3
It does also look like we need some locking somewhere along the line here (even if the only thing here is a big scary comment saying the relevant locks need to be held).
Oh yeah, this was post the mutex patches and got missed.
- dev_dbg(dai->dev, "%s: found %d paths\n",
stream ? "capture" : "playback", paths);
Tracepoint again? Much less clear for this one. I do think we should be dumping the stats we've been collecting for the neighbour walks, very useful if performance blows up again.
Yep, Tracepoint it is then.
Liam
On Thu, Mar 08, 2012 at 11:42:30AM +0000, Liam Girdwood wrote:
On Wed, 2012-03-07 at 19:15 +0000, Mark Brown wrote:
I might be blind but I think the hunk that actually declares the widget list got dropped from the header... probably in some other part of the series you haven't pushed out yet?
I had to double check myself to be sure and Stephen added it a while back :-
fafd2176f72148e83c64a1f818ff33fceed83d08
Oh, sorry about that - I didn't check the actual code for that one as I saw you adding the forward declaration and normally we're fairly good about always adding those so I figured it was a missing diff hunk.
- dapm_reset(card);
This function isn't in mainline, another patch series reordering thing I expect.
It's in 6c120e19fa587710d80757a6e364961a017fb6c3
This one I definitely did check, must've been on a separate branch and didn't notice properly.
participants (2)
-
Liam Girdwood
-
Mark Brown