aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2014-10-25 11:42:03 -0400
committerMark Brown <broonie@kernel.org>2014-10-27 20:19:59 -0400
commit92a99ea439c4e27fc6e32eb6d51c5d091c6084bd (patch)
treeb1539da9e012f31fa47c55413535a9b4ee55708b
parente409dfbfccf9a49409197afc677a21e1c11ba015 (diff)
ASoC: dapm: Use more aggressive caching
Currently we cache the number of input and output paths going to/from a widget only within a power update sequence. But not in between power update sequences. But we know how changes to the DAPM graph affect the number of input (form a source) and output (to a sink) paths of a widget and only need to recalculate them if a operation has been performed that might have changed them. * Adding/removing or connecting/disconnecting a path means that the for the source of the path the number of output paths can change and for the sink the number of input paths can change. * Connecting/disconnecting a widget has the same effect has connecting/ disconnecting all paths of the widget. So for the widget itself the number of inputs and outputs can change, for all sinks of the widget the number of inputs can change and for all sources of the widget the number of outputs can change. * Activating/Deactivating a stream can either change the number of outputs on the sources of the widget associated with the stream or the number of inputs on the sinks. Instead of always invalidating all cached numbers of input and output paths for each power up or down sequence this patch restructures the code to only invalidate the cached numbers when a operation that might change them has been performed. This can greatly reduce the number of DAPM power checks for some very common operations. Since per DAPM operation typically only either change the number of inputs or outputs the number of path checks is reduced by at least 50%. The number of neighbor checks is also reduced about the same percentage, but since the number of neighbors encountered when walking from sink to source is not the same as when walking from source to sink the actual numbers will slightly vary from card to card (e.g. for a mixer we see 1 neighbor when walking from source to sink, but the number of inputs neighbors when walking from source to sink). Bigger improvements can be observed for widgets with multiple connected inputs and output (e.g. mixers probably being the most widespread form of this). Previously we had to re-calculate the number of inputs and outputs on all input and output paths. With this change we only have to re-calculate the number of outputs on the input path that got changed and the number of inputs on the output paths. E.g. imagine the following example: A --> B ----. v M --> N --> Z <-- S <-- R | v X Widget Z has multiple input paths, if any change was made that cause Z to be marked as dirty the power state of Z has to be re-computed. This requires to know the number of inputs and outputs of Z, which requires to know the number of inputs and outputs of all widgets on all paths from or to Z. Previously this meant re-computing all inputs and outputs of all the path going into or out of Z. With this patch in place only paths that actually have changed need to be re-computed. If the system is idle (or the part of the system affected by the changed path) the number of path checks drops to either 0 or 1, regardless of how large or complex the DAPM context is. 0 if there is no connected sink and no connected source. 1 if there is either a connected source or sink, but not both. The number of neighbor checks again will scale accordingly and will be a constant number that is the number of inputs or outputs of the widget for which we did the path check. When loading a state file or switching between different profiles typically multiple mixer and mux settings are changed, so we see the benefit of this patch multiplied for these kinds of operations. Testing with the ADAU1761 shows the following changes in DAPM stats for changing a single Mixer switch for a Mixer with 5 inputs while the DAPM context is idle. Power Path Neighbour Before: 2 12 30 After: 2 1 2 For the same switch, but with a active playback stream the stat changed are as follows. Power Path Neighbour Before: 10 20 54 After: 10 7 21 Cumulative numbers for switching the audio profile which changes 7 controls while the system is idle: Power Path Neighbour Before: 16 80 170 After: 16 7 23 Cumulative numbers for switching the audio profile which changes 7 controls while playback is active: Power Path Neighbour Before: 51 123 273 After: 51 29 109 Starting (or stopping) the playback stream: Power Path Neighbour Before: 34 34 117 After: 34 17 69 Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--include/sound/soc-dapm.h1
-rw-r--r--sound/soc/soc-dapm.c161
2 files changed, 154 insertions, 8 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 43ca1656dab4..89823cfe6f04 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -569,6 +569,7 @@ struct snd_soc_dapm_widget {
569 struct list_head sinks; 569 struct list_head sinks;
570 570
571 /* used during DAPM updates */ 571 /* used during DAPM updates */
572 struct list_head work_list;
572 struct list_head power_list; 573 struct list_head power_list;
573 struct list_head dirty; 574 struct list_head dirty;
574 int inputs; 575 int inputs;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 8e26c2bc7fdf..6bf2c9795df2 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -159,6 +159,116 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
159 } 159 }
160} 160}
161 161
162/*
163 * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
164 * paths
165 * @w: The widget for which to invalidate the cached number of input paths
166 *
167 * The function resets the cached number of inputs for the specified widget and
168 * all widgets that can be reached via outgoing paths from the widget.
169 *
170 * This function must be called if the number of input paths for a widget might
171 * have changed. E.g. if the source state of a widget changes or a path is added
172 * or activated with the widget as the sink.
173 */
174static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
175{
176 struct snd_soc_dapm_widget *sink;
177 struct snd_soc_dapm_path *p;
178 LIST_HEAD(list);
179
180 dapm_assert_locked(w->dapm);
181
182 if (w->inputs == -1)
183 return;
184
185 w->inputs = -1;
186 list_add_tail(&w->work_list, &list);
187
188 list_for_each_entry(w, &list, work_list) {
189 list_for_each_entry(p, &w->sinks, list_source) {
190 if (p->is_supply || p->weak || !p->connect)
191 continue;
192 sink = p->sink;
193 if (sink->inputs != -1) {
194 sink->inputs = -1;
195 list_add_tail(&sink->work_list, &list);
196 }
197 }
198 }
199}
200
201/*
202 * dapm_widget_invalidate_output_paths() - Invalidate the cached number of
203 * output paths
204 * @w: The widget for which to invalidate the cached number of output paths
205 *
206 * Resets the cached number of outputs for the specified widget and all widgets
207 * that can be reached via incoming paths from the widget.
208 *
209 * This function must be called if the number of output paths for a widget might
210 * have changed. E.g. if the sink state of a widget changes or a path is added
211 * or activated with the widget as the source.
212 */
213static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
214{
215 struct snd_soc_dapm_widget *source;
216 struct snd_soc_dapm_path *p;
217 LIST_HEAD(list);
218
219 dapm_assert_locked(w->dapm);
220
221 if (w->outputs == -1)
222 return;
223
224 w->outputs = -1;
225 list_add_tail(&w->work_list, &list);
226
227 list_for_each_entry(w, &list, work_list) {
228 list_for_each_entry(p, &w->sources, list_sink) {
229 if (p->is_supply || p->weak || !p->connect)
230 continue;
231 source = p->source;
232 if (source->outputs != -1) {
233 source->outputs = -1;
234 list_add_tail(&source->work_list, &list);
235 }
236 }
237 }
238}
239
240/*
241 * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
242 * for the widgets connected to a path
243 * @p: The path to invalidate
244 *
245 * Resets the cached number of inputs for the sink of the path and the cached
246 * number of outputs for the source of the path.
247 *
248 * This function must be called when a path is added, removed or the connected
249 * state changes.
250 */
251static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
252{
253 /*
254 * Weak paths or supply paths do not influence the number of input or
255 * output paths of their neighbors.
256 */
257 if (p->weak || p->is_supply)
258 return;
259
260 /*
261 * The number of connected endpoints is the sum of the number of
262 * connected endpoints of all neighbors. If a node with 0 connected
263 * endpoints is either connected or disconnected that sum won't change,
264 * so there is no need to re-check the path.
265 */
266 if (p->source->inputs != 0)
267 dapm_widget_invalidate_input_paths(p->sink);
268 if (p->sink->outputs != 0)
269 dapm_widget_invalidate_output_paths(p->source);
270}
271
162void dapm_mark_endpoints_dirty(struct snd_soc_card *card) 272void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
163{ 273{
164 struct snd_soc_dapm_widget *w; 274 struct snd_soc_dapm_widget *w;
@@ -166,8 +276,13 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
166 mutex_lock(&card->dapm_mutex); 276 mutex_lock(&card->dapm_mutex);
167 277
168 list_for_each_entry(w, &card->widgets, list) { 278 list_for_each_entry(w, &card->widgets, list) {
169 if (w->is_sink || w->is_source) 279 if (w->is_sink || w->is_source) {
170 dapm_mark_dirty(w, "Rechecking endpoints"); 280 dapm_mark_dirty(w, "Rechecking endpoints");
281 if (w->is_sink)
282 dapm_widget_invalidate_output_paths(w);
283 if (w->is_source)
284 dapm_widget_invalidate_input_paths(w);
285 }
171 } 286 }
172 287
173 mutex_unlock(&card->dapm_mutex); 288 mutex_unlock(&card->dapm_mutex);
@@ -379,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
379 list_for_each_entry(w, &card->widgets, list) { 494 list_for_each_entry(w, &card->widgets, list) {
380 w->new_power = w->power; 495 w->new_power = w->power;
381 w->power_checked = false; 496 w->power_checked = false;
382 w->inputs = -1;
383 w->outputs = -1;
384 } 497 }
385} 498}
386 499
@@ -931,10 +1044,19 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
931 struct snd_soc_dapm_widget_list **list) 1044 struct snd_soc_dapm_widget_list **list)
932{ 1045{
933 struct snd_soc_card *card = dai->card; 1046 struct snd_soc_card *card = dai->card;
1047 struct snd_soc_dapm_widget *w;
934 int paths; 1048 int paths;
935 1049
936 mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 1050 mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
937 dapm_reset(card); 1051
1052 /*
1053 * For is_connected_{output,input}_ep fully discover the graph we need
1054 * to reset the cached number of inputs and outputs.
1055 */
1056 list_for_each_entry(w, &card->widgets, list) {
1057 w->inputs = -1;
1058 w->outputs = -1;
1059 }
938 1060
939 if (stream == SNDRV_PCM_STREAM_PLAYBACK) 1061 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
940 paths = is_connected_output_ep(dai->playback_widget, list); 1062 paths = is_connected_output_ep(dai->playback_widget, list);
@@ -1846,6 +1968,7 @@ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
1846 path->connect = connect; 1968 path->connect = connect;
1847 dapm_mark_dirty(path->source, reason); 1969 dapm_mark_dirty(path->source, reason);
1848 dapm_mark_dirty(path->sink, reason); 1970 dapm_mark_dirty(path->sink, reason);
1971 dapm_path_invalidate(path);
1849} 1972}
1850 1973
1851/* test and update the power status of a mux widget */ 1974/* test and update the power status of a mux widget */
@@ -2084,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
2084 return -EINVAL; 2207 return -EINVAL;
2085 } 2208 }
2086 2209
2087 if (w->connected != status) 2210 if (w->connected != status) {
2088 dapm_mark_dirty(w, "pin configuration"); 2211 dapm_mark_dirty(w, "pin configuration");
2212 dapm_widget_invalidate_input_paths(w);
2213 dapm_widget_invalidate_output_paths(w);
2214 }
2089 2215
2090 w->connected = status; 2216 w->connected = status;
2091 if (status == 0) 2217 if (status == 0)
@@ -2267,6 +2393,9 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
2267 dapm_mark_dirty(wsource, "Route added"); 2393 dapm_mark_dirty(wsource, "Route added");
2268 dapm_mark_dirty(wsink, "Route added"); 2394 dapm_mark_dirty(wsink, "Route added");
2269 2395
2396 if (dapm->card->instantiated && path->connect)
2397 dapm_path_invalidate(path);
2398
2270 return 0; 2399 return 0;
2271err: 2400err:
2272 kfree(path); 2401 kfree(path);
@@ -2390,6 +2519,8 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
2390 2519
2391 dapm_mark_dirty(wsource, "Route removed"); 2520 dapm_mark_dirty(wsource, "Route removed");
2392 dapm_mark_dirty(wsink, "Route removed"); 2521 dapm_mark_dirty(wsink, "Route removed");
2522 if (path->connect)
2523 dapm_path_invalidate(path);
2393 2524
2394 dapm_free_path(path); 2525 dapm_free_path(path);
2395 2526
@@ -3007,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
3007 INIT_LIST_HEAD(&w->dirty); 3138 INIT_LIST_HEAD(&w->dirty);
3008 list_add(&w->list, &dapm->card->widgets); 3139 list_add(&w->list, &dapm->card->widgets);
3009 3140
3141 w->inputs = -1;
3142 w->outputs = -1;
3143
3010 /* machine layer set ups unconnected pins and insertions */ 3144 /* machine layer set ups unconnected pins and insertions */
3011 w->connected = 1; 3145 w->connected = 1;
3012 return w; 3146 return w;
@@ -3355,10 +3489,13 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
3355 break; 3489 break;
3356 } 3490 }
3357 3491
3358 if (w->id == snd_soc_dapm_dai_in) 3492 if (w->id == snd_soc_dapm_dai_in) {
3359 w->is_source = w->active; 3493 w->is_source = w->active;
3360 else 3494 dapm_widget_invalidate_input_paths(w);
3495 } else {
3361 w->is_sink = w->active; 3496 w->is_sink = w->active;
3497 dapm_widget_invalidate_output_paths(w);
3498 }
3362 } 3499 }
3363} 3500}
3364 3501
@@ -3485,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
3485 } 3622 }
3486 3623
3487 dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin); 3624 dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);
3488 w->connected = 1; 3625 if (!w->connected) {
3626 /*
3627 * w->force does not affect the number of input or output paths,
3628 * so we only have to recheck if w->connected is changed
3629 */
3630 dapm_widget_invalidate_input_paths(w);
3631 dapm_widget_invalidate_output_paths(w);
3632 w->connected = 1;
3633 }
3489 w->force = 1; 3634 w->force = 1;
3490 dapm_mark_dirty(w, "force enable"); 3635 dapm_mark_dirty(w, "force enable");
3491 3636