diff options
-rw-r--r-- | include/sound/soc-dapm.h | 1 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 161 |
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 | */ | ||
174 | static 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 | */ | ||
213 | static 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 | */ | ||
251 | static 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 | |||
162 | void dapm_mark_endpoints_dirty(struct snd_soc_card *card) | 272 | void 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; |
2271 | err: | 2400 | err: |
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 | ||