From b97e26980f6c13afad4c249b60a8dca7f5f86116 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 21 Jul 2015 18:11:07 +0200 Subject: ASoC: dapm: Add helper function to free a widget snd_soc_tplg_widget_remove_all() has a verbatim copy of an older version of the widget freeing code from dapm_free_widgets(). Add a new helper function that takes care of freeing a widget and use it in both places. This removes the duplicated code and also makes sure that future changes to the widget freeing code only have to be made in one location. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 38 ++++++++++++++++++++++---------------- sound/soc/soc-topology.c | 25 +------------------------ 3 files changed, 24 insertions(+), 40 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 37d95a898275..cadc7fc5d727 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -397,6 +397,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w); /* dapm events */ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e0de8072c514..24ea692bd49e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2312,30 +2312,36 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) kfree(path); } +void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p, *next_p; + + list_del(&w->list); + /* + * remove source and sink paths associated to this widget. + * While removing the path, remove reference to it from both + * source and sink widgets so that path is removed only once. + */ + list_for_each_entry_safe(p, next_p, &w->sources, list_sink) + dapm_free_path(p); + + list_for_each_entry_safe(p, next_p, &w->sinks, list_source) + dapm_free_path(p); + + kfree(w->kcontrols); + kfree(w->name); + kfree(w); +} + /* free all dapm widgets and resources */ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w, *next_w; - struct snd_soc_dapm_path *p, *next_p; list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { if (w->dapm != dapm) continue; - list_del(&w->list); - /* - * remove source and sink paths associated to this widget. - * While removing the path, remove reference to it from both - * source and sink widgets so that path is removed only once. - */ - list_for_each_entry_safe(p, next_p, &w->sources, list_sink) - dapm_free_path(p); - - list_for_each_entry_safe(p, next_p, &w->sinks, list_source) - dapm_free_path(p); - - kfree(w->kcontrols); - kfree(w->name); - kfree(w); + snd_soc_dapm_free_widget(w); } } diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index d0960683c409..56dd108d6f5f 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1734,7 +1734,6 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, u32 index) { struct snd_soc_dapm_widget *w, *next_w; - struct snd_soc_dapm_path *p, *next_p; list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { @@ -1746,31 +1745,9 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, if (w->dobj.index != index && w->dobj.index != SND_SOC_TPLG_INDEX_ALL) continue; - - list_del(&w->list); - - /* - * remove source and sink paths associated to this widget. - * While removing the path, remove reference to it from both - * source and sink widgets so that path is removed only once. - */ - list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { - list_del(&p->list_sink); - list_del(&p->list_source); - list_del(&p->list); - kfree(p); - } - list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { - list_del(&p->list_sink); - list_del(&p->list_source); - list_del(&p->list); - kfree(p); - } /* check and free and dynamic widget kcontrols */ snd_soc_tplg_widget_remove(w); - kfree(w->kcontrols); - kfree(w->name); - kfree(w); + snd_soc_dapm_free_widget(w); } } EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); -- cgit v1.2.3 From 480689617510381391b3d906549477b948d9c4bc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 21 Jul 2015 18:11:08 +0200 Subject: ASoC: dapm: Avoid duplicating immutable strings When creating a new widget from a template the name string of the template is duplicated for the newly created widget. This is necessary because in some cases the string might be stored on the stack or other volatile memory locations. But most of the time the string is static const data, which means it is possible to use it directly without having to worry that it might get freed or changed. Use kstrdup_const() to handle duplicating the string. This function is capable of detecting whether a string is immutable and if it is returns the input without duplicating it. This will slightly reduce the runtime memory footprint of DAPM and also speed up initialization. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 24ea692bd49e..cb4bc1cd5049 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2329,7 +2329,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) dapm_free_path(p); kfree(w->kcontrols); - kfree(w->name); + kfree_const(w->name); kfree(w); } @@ -3350,7 +3350,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, if (prefix) w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name); else - w->name = kasprintf(GFP_KERNEL, "%s", widget->name); + w->name = kstrdup_const(widget->name, GFP_KERNEL); if (w->name == NULL) { kfree(w); return NULL; -- cgit v1.2.3 From 787126ebdb9821f1a19b1dfd1ab1bbb74b8c80b8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 26 Jul 2015 19:04:58 +0200 Subject: ASoC: dapm: Drop always true checks list_first_entry() always returns non NULL and since the code previously checked that list is not empty it will also be a valid pointer. Furthermore a path has always a sink or a source widget. So both checks are redundant and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index cb4bc1cd5049..f80b7ded0721 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3482,11 +3482,6 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path, list_source); - if (WARN_ON(!source_p || !sink_p) || - WARN_ON(!sink_p->source || !source_p->sink) || - WARN_ON(!source_p->source || !sink_p->sink)) - return -EINVAL; - source = source_p->source->priv; sink = sink_p->sink->priv; -- cgit v1.2.3 From 1ce43acff0c078fd560ee0f2a4ae10b8da28e388 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 26 Jul 2015 19:04:59 +0200 Subject: ASoC: dapm: Simplify list creation in dapm_dai_get_connected_widgets() When running dapm_dai_get_connected_widgets() currently in is_connected_{input,output}_ep() for each widget that gets added the array is resized and the code also loops over all existing entries to avoid adding a widget multiple times. The former can be avoided by collecting the widgets in a linked list and only once we have all widgets allocate the array. The later can be avoided by changing when the widget is added. Currently it is added when walking the neighbor lists of a widget. Since a widget can be neighbors with multiple other widgets it could get added twice and hence the check is necessary. But the main body of is_connected_{input,output}_ep is guaranteed to be only executed at most once per widget. So adding the widget to the list at the beginning of the function automatically makes sure that each widget gets only added once. The only difference is that using this method the starting point itself will also end up on the list, but it can easily be skipped when creating the array. Overall this reduces the code size and speeds things slightly up. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 101 ++++++++++++++++++--------------------------------- sound/soc/soc-pcm.c | 11 +----- 2 files changed, 38 insertions(+), 74 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f80b7ded0721..4586f95c676b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1032,43 +1032,27 @@ 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) +static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, + struct list_head *widgets) { - struct snd_soc_dapm_widget_list *wlist; - int wlistsize, wlistentries, i; - - if (*list == NULL) - return -EINVAL; - - wlist = *list; + struct snd_soc_dapm_widget *w; + struct list_head *it; + unsigned int size = 0; + unsigned int i = 0; - /* is this widget already in the list */ - for (i = 0; i < wlist->num_widgets; i++) { - if (wlist->widgets[i] == w) - return 0; - } + list_for_each(it, widgets) + size++; - /* 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, "ASoC: can't allocate widget list for %s\n", - w->name); + *list = kzalloc(sizeof(**list) + size * sizeof(*w), GFP_KERNEL); + if (*list == NULL) return -ENOMEM; - } - wlist = *list; - /* insert the widget */ - dev_dbg(w->dapm->dev, "ASoC: added %s in widget list pos %d\n", - w->name, wlist->num_widgets); + list_for_each_entry(w, widgets, work_list) + (*list)->widgets[i++] = w; - wlist->widgets[wlist->num_widgets] = w; - wlist->num_widgets++; - return 1; + (*list)->num_widgets = i; + + return 0; } /* @@ -1076,7 +1060,7 @@ static int dapm_list_add_widget(struct snd_soc_dapm_widget_list **list, * output widget. Returns number of complete paths. */ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, - struct snd_soc_dapm_widget_list **list) + struct list_head *list) { struct snd_soc_dapm_path *path; int con = 0; @@ -1086,6 +1070,10 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); + /* do we need to add this widget to the list ? */ + if (list) + list_add_tail(&widget->work_list, list); + if (widget->is_sink && widget->connected) { widget->outputs = snd_soc_dapm_suspend_check(widget); return widget->outputs; @@ -1104,22 +1092,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, if (path->connect) { path->walking = 1; - - /* 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, - "ASoC: could not add widget %s\n", - widget->name); - path->walking = 0; - return con; - } - } - con += is_connected_output_ep(path->sink, list); - path->walking = 0; } } @@ -1134,7 +1107,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, * input widget. Returns number of complete paths. */ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, - struct snd_soc_dapm_widget_list **list) + struct list_head *list) { struct snd_soc_dapm_path *path; int con = 0; @@ -1144,6 +1117,10 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, DAPM_UPDATE_STAT(widget, path_checks); + /* do we need to add this widget to the list ? */ + if (list) + list_add_tail(&widget->work_list, list); + if (widget->is_source && widget->connected) { widget->inputs = snd_soc_dapm_suspend_check(widget); return widget->inputs; @@ -1162,22 +1139,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, if (path->connect) { path->walking = 1; - - /* do we need to add this widget to the list ? */ - if (list) { - int err; - err = dapm_list_add_widget(list, path->source); - if (err < 0) { - dev_err(widget->dapm->dev, - "ASoC: could not add widget %s\n", - widget->name); - path->walking = 0; - return con; - } - } - con += is_connected_input_ep(path->source, list); - path->walking = 0; } } @@ -1204,7 +1166,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, { struct snd_soc_card *card = dai->component->card; struct snd_soc_dapm_widget *w; + LIST_HEAD(widgets); int paths; + int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); @@ -1218,9 +1182,16 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, } if (stream == SNDRV_PCM_STREAM_PLAYBACK) - paths = is_connected_output_ep(dai->playback_widget, list); + paths = is_connected_output_ep(dai->playback_widget, &widgets); else - paths = is_connected_input_ep(dai->capture_widget, list); + paths = is_connected_input_ep(dai->capture_widget, &widgets); + + /* Drop starting point */ + list_del(widgets.next); + + ret = dapm_widget_list_create(list, &widgets); + if (ret) + return ret; trace_snd_soc_dapm_connected(paths, stream); mutex_unlock(&card->dapm_mutex); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 256b9c91aa94..7aed170f31bc 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1231,24 +1231,17 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, } int dpcm_path_get(struct snd_soc_pcm_runtime *fe, - int stream, struct snd_soc_dapm_widget_list **list_) + int stream, struct snd_soc_dapm_widget_list **list) { struct snd_soc_dai *cpu_dai = fe->cpu_dai; - struct snd_soc_dapm_widget_list *list; int paths; - list = kzalloc(sizeof(struct snd_soc_dapm_widget_list) + - sizeof(struct snd_soc_dapm_widget *), GFP_KERNEL); - if (list == NULL) - return -ENOMEM; - /* get number of valid DAI paths and their widgets */ - paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, &list); + paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list); dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths, stream ? "capture" : "playback"); - *list_ = list; return paths; } -- cgit v1.2.3 From e63bfd45aba4269811662de0954785622a2ac928 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 26 Jul 2015 19:05:00 +0200 Subject: ASoC: dapm: Add widget path iterators Add helper iterator macros for iterating over the source and sink paths of widget. This will make it easier to change the implementation later on. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 18 ++++++++++++++++++ sound/soc/intel/atom/sst-atom-controls.c | 4 ++-- sound/soc/soc-dapm.c | 30 +++++++++++++++--------------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index cadc7fc5d727..4973083d89ce 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -673,4 +673,22 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( return dapm->bias_level; } +/** + * snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths leaving a + * widget + * @w: The widget + * @p: The path iterator variable + */ +#define snd_soc_dapm_widget_for_each_sink_path(w, p) \ + list_for_each_entry(p, &w->sinks, list_source) + +/** + * snd_soc_dapm_widget_for_each_source_path - Iterates over all paths leading to + * a widget + * @w: The widget + * @p: The path iterator variable + */ +#define snd_soc_dapm_widget_for_each_source_path(w, p) \ + list_for_each_entry(p, &w->sources, list_sink) + #endif diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 31e9b9ecbb8a..1399e34add72 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -1298,7 +1298,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) dev_dbg(dai->dev, "Stream name=%s\n", dai->playback_widget->name); w = dai->playback_widget; - list_for_each_entry(p, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connected && !p->connected(w, p->sink)) continue; @@ -1317,7 +1317,7 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) dev_dbg(dai->dev, "Stream name=%s\n", dai->capture_widget->name); w = dai->capture_widget; - list_for_each_entry(p, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connected && !p->connected(w, p->sink)) continue; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 4586f95c676b..ac506cfb9ed2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -193,7 +193,7 @@ static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) list_add_tail(&w->work_list, &list); list_for_each_entry(w, &list, work_list) { - list_for_each_entry(p, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->is_supply || p->weak || !p->connect) continue; sink = p->sink; @@ -232,7 +232,7 @@ static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) list_add_tail(&w->work_list, &list); list_for_each_entry(w, &list, work_list) { - list_for_each_entry(p, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->is_supply || p->weak || !p->connect) continue; source = p->source; @@ -894,7 +894,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { /* match name */ - list_for_each_entry(path, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, path) { /* mixer/mux paths name must match control name */ if (path->name != (char *)w->kcontrol_news[i].name) continue; @@ -958,12 +958,12 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return ret; if (w->id == snd_soc_dapm_mux) { - list_for_each_entry(path, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, path) { if (path->name) dapm_kcontrol_add_path(w->kcontrols[0], path); } } else { - list_for_each_entry(path, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, path) { if (path->name) dapm_kcontrol_add_path(w->kcontrols[0], path); } @@ -1079,7 +1079,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, return widget->outputs; } - list_for_each_entry(path, &widget->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(widget, path) { DAPM_UPDATE_STAT(widget, neighbour_checks); if (path->weak || path->is_supply) @@ -1126,7 +1126,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, return widget->inputs; } - list_for_each_entry(path, &widget->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(widget, path) { DAPM_UPDATE_STAT(widget, neighbour_checks); if (path->weak || path->is_supply) @@ -1292,7 +1292,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) DAPM_UPDATE_STAT(w, power_checks); /* Check if one of our outputs is connected */ - list_for_each_entry(path, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, path) { DAPM_UPDATE_STAT(w, neighbour_checks); if (path->weak) @@ -1716,12 +1716,12 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, /* If we changed our power state perhaps our neigbours changed * also. */ - list_for_each_entry(path, &w->sources, list_sink) + snd_soc_dapm_widget_for_each_source_path(w, path) dapm_widget_set_peer_power(path->source, power, path->connect); /* Supplies can't affect their outputs, only their inputs */ if (!w->is_supply) { - list_for_each_entry(path, &w->sinks, list_source) + snd_soc_dapm_widget_for_each_sink_path(w, path) dapm_widget_set_peer_power(path->sink, power, path->connect); } @@ -1958,7 +1958,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, w->sname, w->active ? "active" : "inactive"); - list_for_each_entry(p, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connected && !p->connected(w, p->source)) continue; @@ -1968,7 +1968,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, p->name ? p->name : "static", p->source->name); } - list_for_each_entry(p, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connected && !p->connected(w, p->sink)) continue; @@ -2426,7 +2426,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) if (w->dapm->card->fully_routed) break; w->is_source = 1; - list_for_each_entry(p, &w->sources, list_sink) { + snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->source->id == snd_soc_dapm_micbias || p->source->id == snd_soc_dapm_mic || p->source->id == snd_soc_dapm_line || @@ -2441,7 +2441,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) if (w->dapm->card->fully_routed) break; w->is_sink = 1; - list_for_each_entry(p, &w->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->sink->id == snd_soc_dapm_spk || p->sink->id == snd_soc_dapm_hp || p->sink->id == snd_soc_dapm_line || @@ -2841,7 +2841,7 @@ static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n", route->source, route->sink); - list_for_each_entry(path, &source->sinks, list_source) { + snd_soc_dapm_widget_for_each_sink_path(source, path) { if (path->sink == sink) { path->weak = 1; count++; -- cgit v1.2.3 From 30abbe7727b23c6661daeea5d36be36ed7a41665 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 11 Aug 2015 21:37:59 +0200 Subject: ASoC: dapm: dapm_dai_get_connected_widgets: Fix missing mutex unlock Make sure to unlock the DAPM mutex when dapm_widget_list_create() fails. This means the function will now generate a trace_snd_soc_dapm_connected event, even if the creation of the list fails. But that was the behavior before the patch that introduced the unlock issue, so that should be fine. Fixes: 1ce43acff0c0 ("ASoC: dapm: Simplify list creation in dapm_dai_get_connected_widgets()") Reported-by: Dan Carpenter Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index ac506cfb9ed2..fa09108047c8 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1191,7 +1191,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, ret = dapm_widget_list_create(list, &widgets); if (ret) - return ret; + paths = ret; trace_snd_soc_dapm_connected(paths, stream); mutex_unlock(&card->dapm_mutex); -- cgit v1.2.3 From a3423b02cf745c1418f1f199646c450d6fc9ca4e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 11 Aug 2015 21:38:00 +0200 Subject: ASoC: dapm: Consolidate input and output path handling After the recent cleanups and generalizations of the DAPM algorithm the handling of input and output paths is now fully symmetric. This means by making some slight changes to the data structure and using arrays with one entry for each direction, rather than separate fields, it is possible to create a generic implementation that is capable of handling both input and output paths. Unfortunately this generalization significantly increases the code size on the hot path of is_connected_{input,output}_ep() and dapm_widget_invalidate_{input,output}_paths(), which has a negative impact on the overall performance. The inner loops of those functions are quite small and the generic implementation adds extra pointer arithmetic in a few places. Testing on ARM shows that the combined code size of the specialized functions is about 50% larger than the generalized function in relative numbers. But in absolute numbers its less than 200 bytes, which is still quite small. On the other hand the generalized function increases the execution time of dapm_power_one_widget() by 30%. Given that this function is one of the most often called functions of the DAPM framework the trade-off of getting better performance at expense of generating slightly larger code at seems to be worth it. To avoid this still keep two versions of these functions around, one for input and one for output. But have a generic implementation of the algorithm which gets inlined by those two versions. And then let the compiler take care of optimizing it and removing he extra instructions. This still reduces the source code size as well as the makes making changes to the implementation more straight forward since the same change does no longer need to be done in two separate places. Also on the slow paths we can use a generic implementations that handle both input and output paths. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 69 ++++++++-- sound/soc/soc-dapm.c | 350 +++++++++++++++++++++++------------------------ 2 files changed, 227 insertions(+), 192 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 4973083d89ce..5abba037d245 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -512,9 +512,18 @@ struct snd_soc_dapm_route { struct snd_soc_dapm_path { const char *name; - /* source (input) and sink (output) widgets */ - struct snd_soc_dapm_widget *source; - struct snd_soc_dapm_widget *sink; + /* + * source (input) and sink (output) widgets + * The union is for convience, since it is a lot nicer to type + * p->source, rather than p->node[SND_SOC_DAPM_DIR_IN] + */ + union { + struct { + struct snd_soc_dapm_widget *source; + struct snd_soc_dapm_widget *sink; + }; + struct snd_soc_dapm_widget *node[2]; + }; /* status */ u32 connect:1; /* source and sink widgets are connected */ @@ -525,8 +534,7 @@ struct snd_soc_dapm_path { int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); - struct list_head list_source; - struct list_head list_sink; + struct list_head list_node[2]; struct list_head list_kcontrol; struct list_head list; }; @@ -560,8 +568,7 @@ struct snd_soc_dapm_widget { unsigned char new_power:1; /* power from this run */ unsigned char power_checked:1; /* power checked this run */ unsigned char is_supply:1; /* Widget is a supply type widget */ - unsigned char is_sink:1; /* Widget is a sink type widget */ - unsigned char is_source:1; /* Widget is a source type widget */ + unsigned char is_ep:2; /* Widget is a endpoint type widget */ int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); @@ -576,16 +583,14 @@ struct snd_soc_dapm_widget { struct snd_kcontrol **kcontrols; struct snd_soc_dobj dobj; - /* widget input and outputs */ - struct list_head sources; - struct list_head sinks; + /* widget input and output edges */ + struct list_head edges[2]; /* used during DAPM updates */ struct list_head work_list; struct list_head power_list; struct list_head dirty; - int inputs; - int outputs; + int endpoints[2]; struct clk *clk; }; @@ -673,6 +678,42 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( return dapm->bias_level; } +enum snd_soc_dapm_direction { + SND_SOC_DAPM_DIR_IN, + SND_SOC_DAPM_DIR_OUT +}; + +#define SND_SOC_DAPM_DIR_TO_EP(x) BIT(x) + +#define SND_SOC_DAPM_EP_SOURCE SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_IN) +#define SND_SOC_DAPM_EP_SINK SND_SOC_DAPM_DIR_TO_EP(SND_SOC_DAPM_DIR_OUT) + +/** + * snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths in the + * specified direction of a widget + * @w: The widget + * @dir: Whether to iterate over the paths where the specified widget is the + * incoming or outgoing widgets + * @p: The path iterator variable + */ +#define snd_soc_dapm_widget_for_each_path(w, dir, p) \ + list_for_each_entry(p, &w->edges[dir], list_node[dir]) + +/** + * snd_soc_dapm_widget_for_each_sink_path_safe - Iterates over all paths in the + * specified direction of a widget + * @w: The widget + * @dir: Whether to iterate over the paths where the specified widget is the + * incoming or outgoing widgets + * @p: The path iterator variable + * @next_p: Temporary storage for the next path + * + * This function works like snd_soc_dapm_widget_for_each_sink_path, expect that + * it is safe to remove the current path from the list while iterating + */ +#define snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) \ + list_for_each_entry_safe(p, next_p, &w->edges[dir], list_node[dir]) + /** * snd_soc_dapm_widget_for_each_sink_path - Iterates over all paths leaving a * widget @@ -680,7 +721,7 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( * @p: The path iterator variable */ #define snd_soc_dapm_widget_for_each_sink_path(w, p) \ - list_for_each_entry(p, &w->sinks, list_source) + snd_soc_dapm_widget_for_each_path(w, SND_SOC_DAPM_DIR_IN, p) /** * snd_soc_dapm_widget_for_each_source_path - Iterates over all paths leading to @@ -689,6 +730,6 @@ static inline enum snd_soc_bias_level snd_soc_dapm_get_bias_level( * @p: The path iterator variable */ #define snd_soc_dapm_widget_for_each_source_path(w, p) \ - list_for_each_entry(p, &w->sources, list_sink) + snd_soc_dapm_widget_for_each_path(w, SND_SOC_DAPM_DIR_OUT, p) #endif diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index fa09108047c8..27b7fd96e819 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -47,6 +47,13 @@ #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; +#define SND_SOC_DAPM_DIR_REVERSE(x) ((x == SND_SOC_DAPM_DIR_IN) ? \ + SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN) + +#define snd_soc_dapm_for_each_direction(dir) \ + for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ + (dir)++) + static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, const char *control, @@ -167,44 +174,58 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason) } /* - * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input - * paths - * @w: The widget for which to invalidate the cached number of input paths - * - * The function resets the cached number of inputs for the specified widget and - * all widgets that can be reached via outgoing paths from the widget. - * - * This function must be called if the number of input paths for a widget might - * have changed. E.g. if the source state of a widget changes or a path is added - * or activated with the widget as the sink. + * Common implementation for dapm_widget_invalidate_input_paths() and + * dapm_widget_invalidate_output_paths(). The function is inlined since the + * combined size of the two specialized functions is only marginally larger then + * the size of the generic function and at the same time the fast path of the + * specialized functions is significantly smaller than the generic function. */ -static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) +static __always_inline void dapm_widget_invalidate_paths( + struct snd_soc_dapm_widget *w, enum snd_soc_dapm_direction dir) { - struct snd_soc_dapm_widget *sink; + enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + struct snd_soc_dapm_widget *node; struct snd_soc_dapm_path *p; LIST_HEAD(list); dapm_assert_locked(w->dapm); - if (w->inputs == -1) + if (w->endpoints[dir] == -1) return; - w->inputs = -1; list_add_tail(&w->work_list, &list); + w->endpoints[dir] = -1; list_for_each_entry(w, &list, work_list) { - snd_soc_dapm_widget_for_each_sink_path(w, p) { + snd_soc_dapm_widget_for_each_path(w, dir, p) { if (p->is_supply || p->weak || !p->connect) continue; - sink = p->sink; - if (sink->inputs != -1) { - sink->inputs = -1; - list_add_tail(&sink->work_list, &list); + node = p->node[rdir]; + if (node->endpoints[dir] != -1) { + node->endpoints[dir] = -1; + list_add_tail(&node->work_list, &list); } } } } +/* + * dapm_widget_invalidate_input_paths() - Invalidate the cached number of + * input paths + * @w: The widget for which to invalidate the cached number of input paths + * + * Resets the cached number of inputs for the specified widget and all widgets + * that can be reached via outcoming paths from the widget. + * + * This function must be called if the number of output paths for a widget might + * have changed. E.g. if the source state of a widget changes or a path is added + * or activated with the widget as the sink. + */ +static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) +{ + dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_IN); +} + /* * dapm_widget_invalidate_output_paths() - Invalidate the cached number of * output paths @@ -219,29 +240,7 @@ static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w) */ static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w) { - struct snd_soc_dapm_widget *source; - struct snd_soc_dapm_path *p; - LIST_HEAD(list); - - dapm_assert_locked(w->dapm); - - if (w->outputs == -1) - return; - - w->outputs = -1; - list_add_tail(&w->work_list, &list); - - list_for_each_entry(w, &list, work_list) { - snd_soc_dapm_widget_for_each_source_path(w, p) { - if (p->is_supply || p->weak || !p->connect) - continue; - source = p->source; - if (source->outputs != -1) { - source->outputs = -1; - list_add_tail(&source->work_list, &list); - } - } - } + dapm_widget_invalidate_paths(w, SND_SOC_DAPM_DIR_OUT); } /* @@ -270,9 +269,9 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p) * endpoints is either connected or disconnected that sum won't change, * so there is no need to re-check the path. */ - if (p->source->inputs != 0) + if (p->source->endpoints[SND_SOC_DAPM_DIR_IN] != 0) dapm_widget_invalidate_input_paths(p->sink); - if (p->sink->outputs != 0) + if (p->sink->endpoints[SND_SOC_DAPM_DIR_OUT] != 0) dapm_widget_invalidate_output_paths(p->source); } @@ -283,11 +282,11 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) mutex_lock(&card->dapm_mutex); list_for_each_entry(w, &card->widgets, list) { - if (w->is_sink || w->is_source) { + if (w->is_ep) { dapm_mark_dirty(w, "Rechecking endpoints"); - if (w->is_sink) + if (w->is_ep & SND_SOC_DAPM_EP_SINK) dapm_widget_invalidate_output_paths(w); - if (w->is_source) + if (w->is_ep & SND_SOC_DAPM_EP_SOURCE) dapm_widget_invalidate_input_paths(w); } } @@ -923,18 +922,18 @@ static int dapm_new_mixer(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; + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_path *path; - struct list_head *paths; const char *type; int ret; switch (w->id) { case snd_soc_dapm_mux: - paths = &w->sources; + dir = SND_SOC_DAPM_DIR_OUT; type = "mux"; break; case snd_soc_dapm_demux: - paths = &w->sinks; + dir = SND_SOC_DAPM_DIR_IN; type = "demux"; break; default: @@ -948,7 +947,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return -EINVAL; } - if (list_empty(paths)) { + if (list_empty(&w->edges[dir])) { dev_err(dapm->dev, "ASoC: %s %s has no paths\n", type, w->name); return -EINVAL; } @@ -957,16 +956,9 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) if (ret < 0) return ret; - if (w->id == snd_soc_dapm_mux) { - snd_soc_dapm_widget_for_each_source_path(w, path) { - if (path->name) - dapm_kcontrol_add_path(w->kcontrols[0], path); - } - } else { - snd_soc_dapm_widget_for_each_sink_path(w, path) { - if (path->name) - dapm_kcontrol_add_path(w->kcontrols[0], path); - } + snd_soc_dapm_widget_for_each_path(w, dir, path) { + if (path->name) + dapm_kcontrol_add_path(w->kcontrols[0], path); } return 0; @@ -1055,18 +1047,32 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, return 0; } +static void trace_snd_soc_dapm_path(struct snd_soc_dapm_widget *w, + enum snd_soc_dapm_direction dir, struct snd_soc_dapm_path *p) +{ + if (dir == SND_SOC_DAPM_DIR_IN) + trace_snd_soc_dapm_input_path(w, p); + else + trace_snd_soc_dapm_output_path(w, p); +} + /* - * Recursively check for a completed path to an active or physically connected - * output widget. Returns number of complete paths. + * Common implementation for is_connected_output_ep() and + * is_connected_input_ep(). The function is inlined since the combined size of + * the two specialized functions is only marginally larger then the size of the + * generic function and at the same time the fast path of the specialized + * functions is significantly smaller than the generic function. */ -static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, - struct list_head *list) +static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget, + struct list_head *list, enum snd_soc_dapm_direction dir, + int (*fn)(struct snd_soc_dapm_widget *, struct list_head *)) { + enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir); struct snd_soc_dapm_path *path; int con = 0; - if (widget->outputs >= 0) - return widget->outputs; + if (widget->endpoints[dir] >= 0) + return widget->endpoints[dir]; DAPM_UPDATE_STAT(widget, path_checks); @@ -1074,12 +1080,12 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, if (list) list_add_tail(&widget->work_list, list); - if (widget->is_sink && widget->connected) { - widget->outputs = snd_soc_dapm_suspend_check(widget); - return widget->outputs; + if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) { + widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget); + return widget->endpoints[dir]; } - snd_soc_dapm_widget_for_each_sink_path(widget, path) { + snd_soc_dapm_widget_for_each_path(widget, rdir, path) { DAPM_UPDATE_STAT(widget, neighbour_checks); if (path->weak || path->is_supply) @@ -1088,20 +1094,31 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, if (path->walking) return 1; - trace_snd_soc_dapm_output_path(widget, path); + trace_snd_soc_dapm_path(widget, dir, path); if (path->connect) { path->walking = 1; - con += is_connected_output_ep(path->sink, list); + con += fn(path->node[dir], list); path->walking = 0; } } - widget->outputs = con; + widget->endpoints[dir] = con; return con; } +/* + * 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, + struct list_head *list) +{ + return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT, + is_connected_output_ep); +} + /* * Recursively check for a completed path to an active or physically connected * input widget. Returns number of complete paths. @@ -1109,44 +1126,8 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, struct list_head *list) { - struct snd_soc_dapm_path *path; - int con = 0; - - if (widget->inputs >= 0) - return widget->inputs; - - DAPM_UPDATE_STAT(widget, path_checks); - - /* do we need to add this widget to the list ? */ - if (list) - list_add_tail(&widget->work_list, list); - - if (widget->is_source && widget->connected) { - widget->inputs = snd_soc_dapm_suspend_check(widget); - return widget->inputs; - } - - snd_soc_dapm_widget_for_each_source_path(widget, path) { - DAPM_UPDATE_STAT(widget, neighbour_checks); - - if (path->weak || path->is_supply) - continue; - - if (path->walking) - return 1; - - trace_snd_soc_dapm_input_path(widget, path); - - if (path->connect) { - path->walking = 1; - con += is_connected_input_ep(path->source, list); - path->walking = 0; - } - } - - widget->inputs = con; - - return con; + return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN, + is_connected_input_ep); } /** @@ -1177,8 +1158,8 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, * to reset the cached number of inputs and outputs. */ list_for_each_entry(w, &card->widgets, list) { - w->inputs = -1; - w->outputs = -1; + w->endpoints[SND_SOC_DAPM_DIR_IN] = -1; + w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1; } if (stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -1922,6 +1903,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, { struct snd_soc_dapm_widget *w = file->private_data; struct snd_soc_card *card = w->dapm->card; + enum snd_soc_dapm_direction dir, rdir; char *buf; int in, out; ssize_t ret; @@ -1958,25 +1940,21 @@ static ssize_t dapm_widget_power_read_file(struct file *file, w->sname, w->active ? "active" : "inactive"); - snd_soc_dapm_widget_for_each_source_path(w, p) { - if (p->connected && !p->connected(w, p->source)) - continue; + snd_soc_dapm_for_each_direction(dir) { + rdir = SND_SOC_DAPM_DIR_REVERSE(dir); + snd_soc_dapm_widget_for_each_path(w, dir, p) { + if (p->connected && !p->connected(w, p->node[rdir])) + continue; - if (p->connect) - ret += snprintf(buf + ret, PAGE_SIZE - ret, - " in \"%s\" \"%s\"\n", - p->name ? p->name : "static", - p->source->name); - } - snd_soc_dapm_widget_for_each_sink_path(w, p) { - if (p->connected && !p->connected(w, p->sink)) - continue; + if (!p->connect) + continue; - if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " out \"%s\" \"%s\"\n", + " %s \"%s\" \"%s\"\n", + (rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out", p->name ? p->name : "static", - p->sink->name); + p->node[rdir]->name); + } } mutex_unlock(&card->dapm_mutex); @@ -2276,8 +2254,8 @@ struct attribute *soc_dapm_dev_attrs[] = { static void dapm_free_path(struct snd_soc_dapm_path *path) { - list_del(&path->list_sink); - list_del(&path->list_source); + list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]); + list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]); list_del(&path->list_kcontrol); list_del(&path->list); kfree(path); @@ -2286,6 +2264,7 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) { struct snd_soc_dapm_path *p, *next_p; + enum snd_soc_dapm_direction dir; list_del(&w->list); /* @@ -2293,11 +2272,10 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w) * While removing the path, remove reference to it from both * source and sink widgets so that path is removed only once. */ - list_for_each_entry_safe(p, next_p, &w->sources, list_sink) - dapm_free_path(p); - - list_for_each_entry_safe(p, next_p, &w->sinks, list_source) - dapm_free_path(p); + snd_soc_dapm_for_each_direction(dir) { + snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) + dapm_free_path(p); + } kfree(w->kcontrols); kfree_const(w->name); @@ -2418,20 +2396,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); */ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) { + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_path *p; + unsigned int ep; switch (w->id) { case snd_soc_dapm_input: /* On a fully routed card a input is never a source */ if (w->dapm->card->fully_routed) - break; - w->is_source = 1; + return; + ep = SND_SOC_DAPM_EP_SOURCE; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->source->id == snd_soc_dapm_micbias || p->source->id == snd_soc_dapm_mic || p->source->id == snd_soc_dapm_line || p->source->id == snd_soc_dapm_output) { - w->is_source = 0; + ep = 0; break; } } @@ -2439,25 +2419,30 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w) case snd_soc_dapm_output: /* On a fully routed card a output is never a sink */ if (w->dapm->card->fully_routed) - break; - w->is_sink = 1; + return; + ep = SND_SOC_DAPM_EP_SINK; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->sink->id == snd_soc_dapm_spk || p->sink->id == snd_soc_dapm_hp || p->sink->id == snd_soc_dapm_line || p->sink->id == snd_soc_dapm_input) { - w->is_sink = 0; + ep = 0; break; } } break; case snd_soc_dapm_line: - w->is_sink = !list_empty(&w->sources); - w->is_source = !list_empty(&w->sinks); + ep = 0; + snd_soc_dapm_for_each_direction(dir) { + if (!list_empty(&w->edges[dir])) + ep |= SND_SOC_DAPM_DIR_TO_EP(dir); + } break; default: - break; + return; } + + w->is_ep = ep; } static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm, @@ -2510,6 +2495,8 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink)) { + struct snd_soc_dapm_widget *widgets[2]; + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_path *path; int ret; @@ -2542,13 +2529,14 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, if (!path) return -ENOMEM; - path->source = wsource; - path->sink = wsink; + path->node[SND_SOC_DAPM_DIR_IN] = wsource; + path->node[SND_SOC_DAPM_DIR_OUT] = wsink; + widgets[SND_SOC_DAPM_DIR_IN] = wsource; + widgets[SND_SOC_DAPM_DIR_OUT] = wsink; + path->connected = connected; INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list_kcontrol); - INIT_LIST_HEAD(&path->list_source); - INIT_LIST_HEAD(&path->list_sink); if (wsource->is_supply || wsink->is_supply) path->is_supply = 1; @@ -2586,14 +2574,13 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, } list_add(&path->list, &dapm->card->paths); - list_add(&path->list_sink, &wsink->sources); - list_add(&path->list_source, &wsource->sinks); + snd_soc_dapm_for_each_direction(dir) + list_add(&path->list_node[dir], &widgets[dir]->edges[dir]); - dapm_update_widget_flags(wsource); - dapm_update_widget_flags(wsink); - - dapm_mark_dirty(wsource, "Route added"); - dapm_mark_dirty(wsink, "Route added"); + snd_soc_dapm_for_each_direction(dir) { + dapm_update_widget_flags(widgets[dir]); + dapm_mark_dirty(widgets[dir], "Route added"); + } if (dapm->card->instantiated && path->connect) dapm_path_invalidate(path); @@ -3275,6 +3262,7 @@ struct snd_soc_dapm_widget * snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget) { + enum snd_soc_dapm_direction dir; struct snd_soc_dapm_widget *w; const char *prefix; int ret; @@ -3329,27 +3317,27 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, switch (w->id) { case snd_soc_dapm_mic: - w->is_source = 1; + w->is_ep = SND_SOC_DAPM_EP_SOURCE; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_input: if (!dapm->card->fully_routed) - w->is_source = 1; + w->is_ep = SND_SOC_DAPM_EP_SOURCE; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_spk: case snd_soc_dapm_hp: - w->is_sink = 1; + w->is_ep = SND_SOC_DAPM_EP_SINK; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_output: if (!dapm->card->fully_routed) - w->is_sink = 1; + w->is_ep = SND_SOC_DAPM_EP_SINK; w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_vmid: case snd_soc_dapm_siggen: - w->is_source = 1; + w->is_ep = SND_SOC_DAPM_EP_SOURCE; w->power_check = dapm_always_on_check_power; break; case snd_soc_dapm_mux: @@ -3383,14 +3371,14 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, } w->dapm = dapm; - INIT_LIST_HEAD(&w->sources); - INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->dirty); list_add_tail(&w->list, &dapm->card->widgets); - w->inputs = -1; - w->outputs = -1; + snd_soc_dapm_for_each_direction(dir) { + INIT_LIST_HEAD(&w->edges[dir]); + w->endpoints[dir] = -1; + } /* machine layer set ups unconnected pins and insertions */ w->connected = 1; @@ -3444,14 +3432,17 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, int ret; if (WARN_ON(!config) || - WARN_ON(list_empty(&w->sources) || list_empty(&w->sinks))) + WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || + list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) return -EINVAL; /* We only support a single source and sink, pick the first */ - source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path, - list_sink); - sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path, - list_source); + source_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_OUT], + struct snd_soc_dapm_path, + list_node[SND_SOC_DAPM_DIR_OUT]); + sink_p = list_first_entry(&w->edges[SND_SOC_DAPM_DIR_IN], + struct snd_soc_dapm_path, + list_node[SND_SOC_DAPM_DIR_IN]); source = source_p->source->priv; sink = sink_p->sink->priv; @@ -3828,6 +3819,7 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, int event) { struct snd_soc_dapm_widget *w; + unsigned int ep; if (stream == SNDRV_PCM_STREAM_PLAYBACK) w = dai->playback_widget; @@ -3837,12 +3829,22 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, if (w) { dapm_mark_dirty(w, "stream event"); + if (w->id == snd_soc_dapm_dai_in) { + ep = SND_SOC_DAPM_EP_SOURCE; + dapm_widget_invalidate_input_paths(w); + } else { + ep = SND_SOC_DAPM_EP_SINK; + dapm_widget_invalidate_output_paths(w); + } + switch (event) { case SND_SOC_DAPM_STREAM_START: w->active = 1; + w->is_ep = ep; break; case SND_SOC_DAPM_STREAM_STOP: w->active = 0; + w->is_ep = 0; break; case SND_SOC_DAPM_STREAM_SUSPEND: case SND_SOC_DAPM_STREAM_RESUME: @@ -3850,14 +3852,6 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: break; } - - if (w->id == snd_soc_dapm_dai_in) { - w->is_source = w->active; - dapm_widget_invalidate_input_paths(w); - } else { - w->is_sink = w->active; - dapm_widget_invalidate_output_paths(w); - } } } -- cgit v1.2.3 From 6e588a0d839b51bae49852b68740a25cacc91978 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 11 Aug 2015 21:38:01 +0200 Subject: ASoC: dapm: Consolidate path trace events The snd_soc_dapm_input_path and snd_soc_dapm_output_path trace events are identical except for the direction. Instead of having two events have a single one that has a field that contains the direction. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/trace/events/asoc.h | 53 +++++++++++++-------------------------------- sound/soc/soc-dapm.c | 9 -------- 2 files changed, 15 insertions(+), 47 deletions(-) diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h index 88cf39d96d0f..317a1ed2f4ac 100644 --- a/include/trace/events/asoc.h +++ b/include/trace/events/asoc.h @@ -8,6 +8,7 @@ #include #define DAPM_DIRECT "(direct)" +#define DAPM_ARROW(dir) (((dir) == SND_SOC_DAPM_DIR_OUT) ? "->" : "<-") struct snd_soc_jack; struct snd_soc_codec; @@ -152,62 +153,38 @@ TRACE_EVENT(snd_soc_dapm_walk_done, (int)__entry->path_checks, (int)__entry->neighbour_checks) ); -TRACE_EVENT(snd_soc_dapm_output_path, +TRACE_EVENT(snd_soc_dapm_path, TP_PROTO(struct snd_soc_dapm_widget *widget, + enum snd_soc_dapm_direction dir, struct snd_soc_dapm_path *path), - TP_ARGS(widget, path), + TP_ARGS(widget, dir, 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 ) + __string( pnname, path->node[dir]->name ) + __field( int, path_node ) __field( int, path_connect ) + __field( int, path_dir ) ), TP_fast_assign( __assign_str(wname, widget->name); __assign_str(pname, path->name ? path->name : DAPM_DIRECT); - __assign_str(psname, path->sink->name); + __assign_str(pnname, path->node[dir]->name); __entry->path_connect = path->connect; - __entry->path_sink = (long)path->sink; + __entry->path_node = (long)path->node[dir]; + __entry->path_dir = dir; ), - TP_printk("%c%s -> %s -> %s", - (int) __entry->path_sink && + TP_printk("%c%s %s %s %s %s", + (int) __entry->path_node && (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 = (long)path->source; - ), - - TP_printk("%c%s <- %s <- %s", - (int) __entry->path_source && - (int) __entry->path_connect ? '*' : ' ', - __get_str(wname), __get_str(pname), __get_str(psname)) + __get_str(wname), DAPM_ARROW(__entry->path_dir), + __get_str(pname), DAPM_ARROW(__entry->path_dir), + __get_str(pnname)) ); TRACE_EVENT(snd_soc_dapm_connected, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 27b7fd96e819..cf2a0696b4a7 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1047,15 +1047,6 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, return 0; } -static void trace_snd_soc_dapm_path(struct snd_soc_dapm_widget *w, - enum snd_soc_dapm_direction dir, struct snd_soc_dapm_path *p) -{ - if (dir == SND_SOC_DAPM_DIR_IN) - trace_snd_soc_dapm_input_path(w, p); - else - trace_snd_soc_dapm_output_path(w, p); -} - /* * Common implementation for is_connected_output_ep() and * is_connected_input_ep(). The function is inlined since the combined size of -- cgit v1.2.3